├── _vendor ├── github.com │ ├── smartystreets │ │ ├── goconvey │ │ │ ├── convey │ │ │ │ ├── reporting │ │ │ │ │ ├── reporting.goconvey │ │ │ │ │ ├── doc.go │ │ │ │ │ ├── console.go │ │ │ │ │ ├── gotest.go │ │ │ │ │ ├── dot.go │ │ │ │ │ ├── reporter.go │ │ │ │ │ ├── printer.go │ │ │ │ │ ├── story.go │ │ │ │ │ ├── problems.go │ │ │ │ │ ├── json.go │ │ │ │ │ ├── init.go │ │ │ │ │ └── statistics.go │ │ │ │ ├── convey.goconvey │ │ │ │ ├── nilReporter.go │ │ │ │ ├── gotest │ │ │ │ │ └── utils.go │ │ │ │ ├── init.go │ │ │ │ ├── discovery.go │ │ │ │ └── assertions.go │ │ │ └── LICENSE.md │ │ └── assertions │ │ │ ├── .gitignore │ │ │ ├── internal │ │ │ └── oglematchers │ │ │ │ ├── .gitignore │ │ │ │ ├── .travis.yml │ │ │ │ ├── transform_description.go │ │ │ │ ├── greater_than.go │ │ │ │ ├── greater_or_equal.go │ │ │ │ ├── less_or_equal.go │ │ │ │ ├── not.go │ │ │ │ ├── contains.go │ │ │ │ ├── README.md │ │ │ │ ├── deep_equals.go │ │ │ │ ├── any_of.go │ │ │ │ ├── matcher.go │ │ │ │ └── less_than.go │ │ │ ├── .travis.yml │ │ │ ├── filter.go │ │ │ ├── LICENSE.md │ │ │ ├── CONTRIBUTING.md │ │ │ ├── serializer.go │ │ │ ├── panic.go │ │ │ ├── doc.go │ │ │ └── type.go │ ├── zenazn │ │ └── goji │ │ │ ├── web │ │ │ └── mutil │ │ │ │ ├── mutil.go │ │ │ │ └── writer_proxy.go │ │ │ └── LICENSE │ ├── asaskevich │ │ └── govalidator │ │ │ ├── .travis.yml │ │ │ ├── wercker.yml │ │ │ ├── error.go │ │ │ ├── LICENSE │ │ │ ├── converter.go │ │ │ ├── numerics.go │ │ │ └── arrays.go │ ├── jtolds │ │ └── gls │ │ │ ├── gen_sym.go │ │ │ ├── id_pool.go │ │ │ ├── LICENSE │ │ │ ├── stack_tags.go │ │ │ ├── README.md │ │ │ └── context.go │ ├── derekdowling │ │ └── go-stdlogger │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ ├── logger.go │ │ │ └── LICENSE │ └── gopherjs │ │ └── gopherjs │ │ └── LICENSE ├── goji.io │ ├── .travis.yml │ ├── dispatch.go │ ├── pattern.go │ ├── router.go │ ├── internal │ │ ├── context.go │ │ └── internal.go │ ├── router_simple.go │ ├── LICENSE │ ├── pat │ │ ├── match.go │ │ ├── methods.go │ │ └── url.go │ ├── handle.go │ ├── mux.go │ ├── pattern │ │ └── pattern.go │ ├── middleware.go │ ├── goji.go │ ├── README.md │ └── router_trie.go └── golang.org │ └── x │ └── net │ ├── PATENTS │ └── LICENSE ├── jsh-api ├── jshapi.go ├── relationship.go ├── sender.go ├── utility.go ├── LICENSE ├── api_test.go ├── store │ └── store.go ├── mock_storage.go └── api.go ├── Godeps ├── Readme └── Godeps.json ├── client ├── jsc.go ├── delete_test.go ├── action_test.go ├── post_test.go ├── patch_test.go ├── get_test.go ├── delete.go ├── post.go ├── test_util.go ├── patch.go ├── action.go ├── get.go └── client_test.go ├── .travis.yml ├── .gitignore ├── test_util.go ├── jsh.go ├── goji2-logger ├── README.md ├── LICENSE ├── color_writer.go └── logger.go ├── relationship_test.go ├── LICENSE ├── list.go ├── response_test.go ├── relationship.go ├── link.go ├── link_test.go ├── error_test.go ├── response.go ├── list_test.go ├── parser_test.go ├── object_test.go └── parser.go /_vendor/github.com/smartystreets/goconvey/convey/reporting/reporting.goconvey: -------------------------------------------------------------------------------- 1 | #ignore 2 | -timeout=1s 3 | -------------------------------------------------------------------------------- /_vendor/github.com/smartystreets/assertions/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | Thumbs.db 3 | *.iml 4 | /.idea 5 | coverage.out 6 | -------------------------------------------------------------------------------- /_vendor/github.com/smartystreets/assertions/internal/oglematchers/.gitignore: -------------------------------------------------------------------------------- 1 | *.6 2 | 6.out 3 | _obj/ 4 | _test/ 5 | _testmain.go 6 | -------------------------------------------------------------------------------- /jsh-api/jshapi.go: -------------------------------------------------------------------------------- 1 | // Package jshapi is a http.Handler compatible wrapper that makes building JSON API 2 | // resource handlers easy. 3 | package jshapi 4 | -------------------------------------------------------------------------------- /Godeps/Readme: -------------------------------------------------------------------------------- 1 | This directory tree is generated automatically by godep. 2 | 3 | Please do not edit. 4 | 5 | See https://github.com/tools/godep for more information. 6 | -------------------------------------------------------------------------------- /_vendor/github.com/zenazn/goji/web/mutil/mutil.go: -------------------------------------------------------------------------------- 1 | // Package mutil contains various functions that are helpful when writing http 2 | // middleware. 3 | package mutil 4 | -------------------------------------------------------------------------------- /_vendor/github.com/asaskevich/govalidator/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.1 5 | - 1.2 6 | - 1.3 7 | 8 | notifications: 9 | email: 10 | - bwatas@gmail.com 11 | -------------------------------------------------------------------------------- /client/jsc.go: -------------------------------------------------------------------------------- 1 | // Package jsc (JSON Specification Client) is an http client that makes sending HTTP requests that match 2 | // the JSON Specification: http://jsonapi.org/ simple. 3 | package jsc 4 | -------------------------------------------------------------------------------- /_vendor/github.com/smartystreets/assertions/internal/oglematchers/.travis.yml: -------------------------------------------------------------------------------- 1 | # Cf. http://docs.travis-ci.com/user/getting-started/ 2 | # Cf. http://docs.travis-ci.com/user/languages/go/ 3 | 4 | language: go 5 | -------------------------------------------------------------------------------- /_vendor/github.com/smartystreets/assertions/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.x 5 | - master 6 | 7 | install: 8 | - go get -t ./... 9 | 10 | script: go test -v 11 | 12 | sudo: false 13 | -------------------------------------------------------------------------------- /_vendor/goji.io/.travis.yml: -------------------------------------------------------------------------------- 1 | go_import_path: goji.io 2 | language: go 3 | sudo: false 4 | 5 | matrix: 6 | include: 7 | - go: 1.7 8 | - go: tip 9 | 10 | script: 11 | - go test -cover -race ./... 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - tip 4 | - 1.8 5 | 6 | install: 7 | - go get github.com/smartystreets/goconvey 8 | 9 | before_script: 10 | - ln -s _vendor vendor 11 | 12 | script: 13 | - go test ./... 14 | -------------------------------------------------------------------------------- /_vendor/github.com/smartystreets/goconvey/convey/convey.goconvey: -------------------------------------------------------------------------------- 1 | #ignore 2 | -timeout=1s 3 | #-covermode=count 4 | #-coverpkg=github.com/smartystreets/goconvey/convey,github.com/smartystreets/goconvey/convey/gotest,github.com/smartystreets/goconvey/convey/reporting -------------------------------------------------------------------------------- /_vendor/github.com/smartystreets/goconvey/convey/reporting/doc.go: -------------------------------------------------------------------------------- 1 | // Package reporting contains internal functionality related 2 | // to console reporting and output. Although this package has 3 | // exported names is not intended for public consumption. See the 4 | // examples package for how to use this project. 5 | package reporting 6 | -------------------------------------------------------------------------------- /_vendor/github.com/smartystreets/goconvey/convey/reporting/console.go: -------------------------------------------------------------------------------- 1 | package reporting 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | ) 7 | 8 | type console struct{} 9 | 10 | func (self *console) Write(p []byte) (n int, err error) { 11 | return fmt.Print(string(p)) 12 | } 13 | 14 | func NewConsole() io.Writer { 15 | return new(console) 16 | } 17 | -------------------------------------------------------------------------------- /jsh-api/relationship.go: -------------------------------------------------------------------------------- 1 | package jshapi 2 | 3 | // Relationship helps define the relationship between two resources 4 | type Relationship string 5 | 6 | const ( 7 | // ToOne signifies a one to one relationship 8 | ToOne Relationship = "One-To-One" 9 | // ToMany signifies a one to many relationship 10 | ToMany Relationship = "One-To-Many" 11 | ) 12 | -------------------------------------------------------------------------------- /_vendor/github.com/asaskevich/govalidator/wercker.yml: -------------------------------------------------------------------------------- 1 | box: wercker/golang 2 | build: 3 | steps: 4 | - setup-go-workspace 5 | 6 | - script: 7 | name: go get 8 | code: | 9 | go version 10 | go get -t ./... 11 | 12 | - script: 13 | name: go test 14 | code: | 15 | go test -race ./... 16 | -------------------------------------------------------------------------------- /_vendor/github.com/jtolds/gls/gen_sym.go: -------------------------------------------------------------------------------- 1 | package gls 2 | 3 | var ( 4 | symPool = &idPool{} 5 | ) 6 | 7 | // ContextKey is a throwaway value you can use as a key to a ContextManager 8 | type ContextKey struct{ id uint } 9 | 10 | // GenSym will return a brand new, never-before-used ContextKey 11 | func GenSym() ContextKey { 12 | return ContextKey{id: symPool.Acquire()} 13 | } 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | vendor 26 | -------------------------------------------------------------------------------- /_vendor/goji.io/dispatch.go: -------------------------------------------------------------------------------- 1 | package goji 2 | 3 | import ( 4 | "net/http" 5 | 6 | "goji.io/internal" 7 | ) 8 | 9 | type dispatch struct{} 10 | 11 | func (d dispatch) ServeHTTP(w http.ResponseWriter, r *http.Request) { 12 | ctx := r.Context() 13 | h := ctx.Value(internal.Handler) 14 | if h == nil { 15 | http.NotFound(w, r) 16 | } else { 17 | h.(http.Handler).ServeHTTP(w, r) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /_vendor/github.com/derekdowling/go-stdlogger/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | -------------------------------------------------------------------------------- /_vendor/goji.io/pattern.go: -------------------------------------------------------------------------------- 1 | package goji 2 | 3 | // httpMethods is an internal interface for the HTTPMethods pattern 4 | // optimization. See the documentation on Pattern for more. 5 | type httpMethods interface { 6 | HTTPMethods() map[string]struct{} 7 | } 8 | 9 | // pathPrefix is an internal interface for the PathPrefix pattern optimization. 10 | // See the documentation on Pattern for more. 11 | type pathPrefix interface { 12 | PathPrefix() string 13 | } 14 | -------------------------------------------------------------------------------- /_vendor/github.com/asaskevich/govalidator/error.go: -------------------------------------------------------------------------------- 1 | package govalidator 2 | 3 | type Errors []error 4 | 5 | func (es Errors) Errors() []error { 6 | return es 7 | } 8 | 9 | func (es Errors) Error() string { 10 | var err string 11 | for _, e := range es { 12 | err += e.Error() + ";" 13 | } 14 | return err 15 | } 16 | 17 | type Error struct { 18 | Name string 19 | Err error 20 | } 21 | 22 | func (e Error) Error() string { 23 | return e.Name + ": " + e.Err.Error() 24 | } 25 | -------------------------------------------------------------------------------- /_vendor/goji.io/router.go: -------------------------------------------------------------------------------- 1 | package goji 2 | 3 | import ( 4 | "context" 5 | "net/http" 6 | 7 | "goji.io/internal" 8 | ) 9 | 10 | type match struct { 11 | context.Context 12 | p Pattern 13 | h http.Handler 14 | } 15 | 16 | func (m match) Value(key interface{}) interface{} { 17 | switch key { 18 | case internal.Pattern: 19 | return m.p 20 | case internal.Handler: 21 | return m.h 22 | default: 23 | return m.Context.Value(key) 24 | } 25 | } 26 | 27 | var _ context.Context = match{} 28 | -------------------------------------------------------------------------------- /test_util.go: -------------------------------------------------------------------------------- 1 | package jsh 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | "io/ioutil" 7 | "net/http" 8 | ) 9 | 10 | // CreateReadCloser is a helper function for dealing with creating HTTP requests 11 | func CreateReadCloser(data []byte) io.ReadCloser { 12 | reader := bytes.NewReader(data) 13 | return ioutil.NopCloser(reader) 14 | } 15 | 16 | func testRequest(bytes []byte) (*http.Request, error) { 17 | req, err := http.NewRequest("GET", "", CreateReadCloser(bytes)) 18 | if err != nil { 19 | return nil, err 20 | } 21 | 22 | req.Header.Set("Content-Type", ContentType) 23 | return req, nil 24 | } 25 | -------------------------------------------------------------------------------- /client/delete_test.go: -------------------------------------------------------------------------------- 1 | package jsc 2 | 3 | import ( 4 | "net/http" 5 | "net/http/httptest" 6 | "testing" 7 | 8 | . "github.com/smartystreets/goconvey/convey" 9 | ) 10 | 11 | func TestDelete(t *testing.T) { 12 | 13 | Convey("DELETE Tests", t, func() { 14 | 15 | api := testAPI() 16 | server := httptest.NewServer(api) 17 | defer server.Close() 18 | 19 | baseURL := server.URL 20 | 21 | Convey("->Delete()", func() { 22 | resp, err := Delete(baseURL, "tests", "1") 23 | 24 | So(err, ShouldBeNil) 25 | So(resp.StatusCode, ShouldEqual, http.StatusNoContent) 26 | }) 27 | }) 28 | } 29 | -------------------------------------------------------------------------------- /jsh.go: -------------------------------------------------------------------------------- 1 | // Package jsh (JSON API Specification Handler) makes it easy to parse JSON API 2 | // requests and send responses that match the JSON API Specification: http://jsonapi.org/ 3 | // from your server. 4 | // 5 | // For a request client, see: jsc: https://godoc.org/github.com/derekdowling/go-json-spec-handler/client 6 | // 7 | // For a full http.Handler API builder see jshapi: https://godoc.org/github.com/derekdowling/go-json-spec-handler/jsh-api 8 | package jsh 9 | 10 | const ( 11 | // ContentType is the data encoding of choice for HTTP Request and Response Headers 12 | ContentType = "application/vnd.api+json" 13 | ) 14 | -------------------------------------------------------------------------------- /client/action_test.go: -------------------------------------------------------------------------------- 1 | package jsc 2 | 3 | import ( 4 | "net/http" 5 | "net/http/httptest" 6 | "testing" 7 | 8 | . "github.com/smartystreets/goconvey/convey" 9 | ) 10 | 11 | func TestAction(t *testing.T) { 12 | 13 | Convey("Action Tests", t, func() { 14 | 15 | api := testAPI() 16 | server := httptest.NewServer(api) 17 | defer server.Close() 18 | 19 | baseURL := server.URL 20 | 21 | Convey("->Action()", func() { 22 | doc, resp, err := Action(baseURL, "tests", "1", "testAction") 23 | So(err, ShouldBeNil) 24 | So(resp.StatusCode, ShouldEqual, http.StatusOK) 25 | So(doc, ShouldNotBeEmpty) 26 | }) 27 | }) 28 | } 29 | -------------------------------------------------------------------------------- /_vendor/goji.io/internal/context.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | // ContextKey is a type used for Goji's context.Context keys. 4 | type ContextKey int 5 | 6 | var ( 7 | // Path is the context key used to store the path Goji uses for its 8 | // PathPrefix optimization. 9 | Path interface{} = ContextKey(0) 10 | // Pattern is the context key used to store the Pattern that Goji last 11 | // matched. 12 | Pattern interface{} = ContextKey(1) 13 | // Handler is the context key used to store the Handler that Goji last 14 | // mached (and will therefore dispatch to at the end of the middleware 15 | // stack). 16 | Handler interface{} = ContextKey(2) 17 | ) 18 | -------------------------------------------------------------------------------- /goji2-logger/README.md: -------------------------------------------------------------------------------- 1 | # goji2-logger 2 | 3 | [![GoDoc](https://godoc.org/github.com/derekdowling/goji2-logger?status.png)](https://godoc.org/github.com/derekdowling/goji2-logger) 4 | 5 | Logging Middleware For [Goji 2](http://goji.io) 6 | 7 | ```go 8 | 9 | import github.com/derekdowling/goji2-logger 10 | 11 | // works with Logrus, stdlogger, most others as well 12 | logger := New(os.Stderr, "", LstdFlags) 13 | gojilogger.SetLogger(logger) 14 | 15 | mux := goji.NewMux() 16 | mux.UseC(gojilogger.Middleware) 17 | ``` 18 | 19 | Output looks like: 20 | 21 | ```bash 22 | 2015/12/16 16:55:21 Serving PATCH "/api/foos/1" from 127.0.0.1:57639 23 | 2015/12/16 16:55:21 Returning HTTP 200 after 53.044µs 24 | ``` 25 | -------------------------------------------------------------------------------- /client/post_test.go: -------------------------------------------------------------------------------- 1 | package jsc 2 | 3 | import ( 4 | "net/http" 5 | "net/http/httptest" 6 | "testing" 7 | 8 | "github.com/derekdowling/go-json-spec-handler" 9 | . "github.com/smartystreets/goconvey/convey" 10 | ) 11 | 12 | func TestPost(t *testing.T) { 13 | 14 | attrs := map[string]string{ 15 | "foo": "bar", 16 | } 17 | 18 | api := testAPI() 19 | server := httptest.NewServer(api) 20 | defer server.Close() 21 | 22 | baseURL := server.URL 23 | 24 | Convey("Post Tests", t, func() { 25 | testObject, err := jsh.NewObject("", "tests", attrs) 26 | So(err, ShouldBeNil) 27 | 28 | _, resp, postErr := Post(baseURL, testObject) 29 | So(postErr, ShouldBeNil) 30 | So(resp.StatusCode, ShouldEqual, http.StatusCreated) 31 | }) 32 | } 33 | -------------------------------------------------------------------------------- /_vendor/github.com/smartystreets/goconvey/convey/nilReporter.go: -------------------------------------------------------------------------------- 1 | package convey 2 | 3 | import ( 4 | "github.com/smartystreets/goconvey/convey/reporting" 5 | ) 6 | 7 | type nilReporter struct{} 8 | 9 | func (self *nilReporter) BeginStory(story *reporting.StoryReport) {} 10 | func (self *nilReporter) Enter(scope *reporting.ScopeReport) {} 11 | func (self *nilReporter) Report(report *reporting.AssertionResult) {} 12 | func (self *nilReporter) Exit() {} 13 | func (self *nilReporter) EndStory() {} 14 | func (self *nilReporter) Write(p []byte) (int, error) { return len(p), nil } 15 | func newNilReporter() *nilReporter { return &nilReporter{} } 16 | -------------------------------------------------------------------------------- /_vendor/goji.io/router_simple.go: -------------------------------------------------------------------------------- 1 | // +build goji_router_simple 2 | 3 | package goji 4 | 5 | import "net/http" 6 | 7 | /* 8 | This is the simplest correct router implementation for Goji. 9 | */ 10 | 11 | type router []route 12 | 13 | type route struct { 14 | Pattern 15 | http.Handler 16 | } 17 | 18 | func (rt *router) add(p Pattern, h http.Handler) { 19 | *rt = append(*rt, route{p, h}) 20 | } 21 | 22 | func (rt *router) route(r *http.Request) *http.Request { 23 | for _, route := range *rt { 24 | if r2 := route.Match(r); r2 != nil { 25 | return r2.WithContext(&match{ 26 | Context: r2.Context(), 27 | p: route.Pattern, 28 | h: route.Handler, 29 | }) 30 | } 31 | } 32 | return r.WithContext(&match{Context: r.Context()}) 33 | } 34 | -------------------------------------------------------------------------------- /_vendor/goji.io/internal/internal.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package internal is a private package that allows Goji to expose a less 3 | confusing interface to its users. This package must not be used outside of Goji; 4 | every piece of its functionality has been exposed by one of Goji's subpackages. 5 | 6 | The problem this package solves is to allow Goji to internally agree on types 7 | and secret values between its packages without introducing import cycles. Goji 8 | needs to agree on these types and values in order to organize its public API 9 | into audience-specific subpackages (for instance, a package for pattern authors, 10 | a package for middleware authors, and a main package for routing users) without 11 | exposing implementation details in any of the packages. 12 | */ 13 | package internal 14 | -------------------------------------------------------------------------------- /_vendor/github.com/derekdowling/go-stdlogger/README.md: -------------------------------------------------------------------------------- 1 | # go-stdlogger 2 | 3 | The Go Standard Logging Interface. Plain and simple. 4 | 5 | ```go 6 | // Logger describes a logger interface that is compatible with the standard 7 | // log.Logger but also logrus and others. As not to limit which loggers can and 8 | // can't be used with the API. 9 | // 10 | // This interface is from https://godoc.org/github.com/Sirupsen/logrus#StdLogger 11 | type Logger interface { 12 | Print(...interface{}) 13 | Printf(string, ...interface{}) 14 | Println(...interface{}) 15 | 16 | Fatal(...interface{}) 17 | Fatalf(string, ...interface{}) 18 | Fatalln(...interface{}) 19 | 20 | Panic(...interface{}) 21 | Panicf(string, ...interface{}) 22 | Panicln(...interface{}) 23 | } 24 | ``` 25 | -------------------------------------------------------------------------------- /_vendor/github.com/jtolds/gls/id_pool.go: -------------------------------------------------------------------------------- 1 | package gls 2 | 3 | // though this could probably be better at keeping ids smaller, the goal of 4 | // this class is to keep a registry of the smallest unique integer ids 5 | // per-process possible 6 | 7 | import ( 8 | "sync" 9 | ) 10 | 11 | type idPool struct { 12 | mtx sync.Mutex 13 | released []uint 14 | max_id uint 15 | } 16 | 17 | func (p *idPool) Acquire() (id uint) { 18 | p.mtx.Lock() 19 | defer p.mtx.Unlock() 20 | if len(p.released) > 0 { 21 | id = p.released[len(p.released)-1] 22 | p.released = p.released[:len(p.released)-1] 23 | return id 24 | } 25 | id = p.max_id 26 | p.max_id++ 27 | return id 28 | } 29 | 30 | func (p *idPool) Release(id uint) { 31 | p.mtx.Lock() 32 | defer p.mtx.Unlock() 33 | p.released = append(p.released, id) 34 | } 35 | -------------------------------------------------------------------------------- /client/patch_test.go: -------------------------------------------------------------------------------- 1 | package jsc 2 | 3 | import ( 4 | "net/http" 5 | "net/http/httptest" 6 | "testing" 7 | 8 | "github.com/derekdowling/go-json-spec-handler" 9 | . "github.com/smartystreets/goconvey/convey" 10 | ) 11 | 12 | func TestPatch(t *testing.T) { 13 | 14 | Convey("Patch Tests", t, func() { 15 | 16 | api := testAPI() 17 | server := httptest.NewServer(api) 18 | defer server.Close() 19 | 20 | baseURL := server.URL 21 | 22 | Convey("->Patch()", func() { 23 | object, err := jsh.NewObject("2", "tests", nil) 24 | So(err, ShouldBeNil) 25 | 26 | json, resp, patchErr := Patch(baseURL, object) 27 | 28 | So(resp.StatusCode, ShouldEqual, http.StatusOK) 29 | So(patchErr, ShouldBeNil) 30 | So(json.HasErrors(), ShouldBeFalse) 31 | So(json.HasData(), ShouldBeTrue) 32 | }) 33 | }) 34 | } 35 | -------------------------------------------------------------------------------- /_vendor/github.com/smartystreets/goconvey/convey/reporting/gotest.go: -------------------------------------------------------------------------------- 1 | package reporting 2 | 3 | type gotestReporter struct{ test T } 4 | 5 | func (self *gotestReporter) BeginStory(story *StoryReport) { 6 | self.test = story.Test 7 | } 8 | 9 | func (self *gotestReporter) Enter(scope *ScopeReport) {} 10 | 11 | func (self *gotestReporter) Report(r *AssertionResult) { 12 | if !passed(r) { 13 | self.test.Fail() 14 | } 15 | } 16 | 17 | func (self *gotestReporter) Exit() {} 18 | 19 | func (self *gotestReporter) EndStory() { 20 | self.test = nil 21 | } 22 | 23 | func (self *gotestReporter) Write(content []byte) (written int, err error) { 24 | return len(content), nil // no-op 25 | } 26 | 27 | func NewGoTestReporter() *gotestReporter { 28 | return new(gotestReporter) 29 | } 30 | 31 | func passed(r *AssertionResult) bool { 32 | return r.Error == nil && r.Failure == "" 33 | } 34 | -------------------------------------------------------------------------------- /relationship_test.go: -------------------------------------------------------------------------------- 1 | package jsh 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | func TestRelationship(t *testing.T) { 10 | Convey("ResourceLinkage Tests", t, func() { 11 | Convey("->UnmarshalJSON()", func() { 12 | 13 | Convey("should handle a linkage object", func() { 14 | jObj := `{"type": "testRelationship", "id": "ID456"}` 15 | 16 | rl := ResourceLinkage{} 17 | err := rl.UnmarshalJSON([]byte(jObj)) 18 | So(err, ShouldBeNil) 19 | So(len(rl), ShouldEqual, 1) 20 | }) 21 | 22 | Convey("should handle a linkage list", func() { 23 | jList := `[ 24 | {"type": "testRelationship", "id": "ID456"}, 25 | {"type": "testRelationship", "id": "ID789"} 26 | ]` 27 | 28 | rl := ResourceLinkage{} 29 | err := rl.UnmarshalJSON([]byte(jList)) 30 | So(err, ShouldBeNil) 31 | So(len(rl), ShouldEqual, 2) 32 | }) 33 | }) 34 | }) 35 | } 36 | -------------------------------------------------------------------------------- /_vendor/github.com/derekdowling/go-stdlogger/logger.go: -------------------------------------------------------------------------------- 1 | // Package std is the missing standard logging interface that should be present within 2 | // Go. This entire package exposes a single "Logger" interface so that I don't 3 | // need to pull in a huge dependency tree everytime I want to use this 4 | // interface. 5 | package std 6 | 7 | // Logger describes a logger interface that is compatible with the standard 8 | // log.Logger but also logrus and others. As not to limit which loggers can and 9 | // can't be used with the API. 10 | // 11 | // This interface is from https://godoc.org/github.com/Sirupsen/logrus#StdLogger 12 | type Logger interface { 13 | Print(...interface{}) 14 | Printf(string, ...interface{}) 15 | Println(...interface{}) 16 | 17 | Fatal(...interface{}) 18 | Fatalf(string, ...interface{}) 19 | Fatalln(...interface{}) 20 | 21 | Panic(...interface{}) 22 | Panicf(string, ...interface{}) 23 | Panicln(...interface{}) 24 | } 25 | -------------------------------------------------------------------------------- /_vendor/github.com/smartystreets/goconvey/convey/gotest/utils.go: -------------------------------------------------------------------------------- 1 | // Package gotest contains internal functionality. Although this package 2 | // contains one or more exported names it is not intended for public 3 | // consumption. See the examples package for how to use this project. 4 | package gotest 5 | 6 | import ( 7 | "runtime" 8 | "strings" 9 | ) 10 | 11 | func ResolveExternalCaller() (file string, line int, name string) { 12 | var caller_id uintptr 13 | callers := runtime.Callers(0, callStack) 14 | 15 | for x := 0; x < callers; x++ { 16 | caller_id, file, line, _ = runtime.Caller(x) 17 | if strings.HasSuffix(file, "_test.go") || strings.HasSuffix(file, "_tests.go") { 18 | name = runtime.FuncForPC(caller_id).Name() 19 | return 20 | } 21 | } 22 | file, line, name = "", -1, "" 23 | return // panic? 24 | } 25 | 26 | const maxStackDepth = 100 // This had better be enough... 27 | 28 | var callStack []uintptr = make([]uintptr, maxStackDepth, maxStackDepth) 29 | -------------------------------------------------------------------------------- /_vendor/github.com/smartystreets/assertions/filter.go: -------------------------------------------------------------------------------- 1 | package assertions 2 | 3 | import "fmt" 4 | 5 | const ( 6 | success = "" 7 | needExactValues = "This assertion requires exactly %d comparison values (you provided %d)." 8 | needNonEmptyCollection = "This assertion requires at least 1 comparison value (you provided 0)." 9 | needFewerValues = "This assertion allows %d or fewer comparison values (you provided %d)." 10 | ) 11 | 12 | func need(needed int, expected []interface{}) string { 13 | if len(expected) != needed { 14 | return fmt.Sprintf(needExactValues, needed, len(expected)) 15 | } 16 | return success 17 | } 18 | 19 | func atLeast(minimum int, expected []interface{}) string { 20 | if len(expected) < 1 { 21 | return needNonEmptyCollection 22 | } 23 | return success 24 | } 25 | 26 | func atMost(max int, expected []interface{}) string { 27 | if len(expected) > max { 28 | return fmt.Sprintf(needFewerValues, max, len(expected)) 29 | } 30 | return success 31 | } 32 | -------------------------------------------------------------------------------- /_vendor/github.com/smartystreets/goconvey/convey/reporting/dot.go: -------------------------------------------------------------------------------- 1 | package reporting 2 | 3 | import "fmt" 4 | 5 | type dot struct{ out *Printer } 6 | 7 | func (self *dot) BeginStory(story *StoryReport) {} 8 | 9 | func (self *dot) Enter(scope *ScopeReport) {} 10 | 11 | func (self *dot) Report(report *AssertionResult) { 12 | if report.Error != nil { 13 | fmt.Print(redColor) 14 | self.out.Insert(dotError) 15 | } else if report.Failure != "" { 16 | fmt.Print(yellowColor) 17 | self.out.Insert(dotFailure) 18 | } else if report.Skipped { 19 | fmt.Print(yellowColor) 20 | self.out.Insert(dotSkip) 21 | } else { 22 | fmt.Print(greenColor) 23 | self.out.Insert(dotSuccess) 24 | } 25 | fmt.Print(resetColor) 26 | } 27 | 28 | func (self *dot) Exit() {} 29 | 30 | func (self *dot) EndStory() {} 31 | 32 | func (self *dot) Write(content []byte) (written int, err error) { 33 | return len(content), nil // no-op 34 | } 35 | 36 | func NewDotReporter(out *Printer) *dot { 37 | self := new(dot) 38 | self.out = out 39 | return self 40 | } 41 | -------------------------------------------------------------------------------- /jsh-api/sender.go: -------------------------------------------------------------------------------- 1 | package jshapi 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/derekdowling/go-json-spec-handler" 7 | "github.com/derekdowling/go-stdlogger" 8 | ) 9 | 10 | /* 11 | Sender is a function type definition that allows consumers to customize how they 12 | send and log API responses. 13 | */ 14 | type Sender func(http.ResponseWriter, *http.Request, jsh.Sendable) 15 | 16 | /* 17 | DefaultSender is the default sender that will log 5XX errors that it encounters 18 | in the process of sending a response. 19 | */ 20 | func DefaultSender(logger std.Logger) Sender { 21 | return func(w http.ResponseWriter, r *http.Request, sendable jsh.Sendable) { 22 | sendableError, isType := sendable.(jsh.ErrorType) 23 | if isType && sendableError.StatusCode() >= 500 { 24 | logger.Printf("Returning ISE: %s\n", sendableError.Error()) 25 | } 26 | 27 | sendError := jsh.Send(w, r, sendable) 28 | if sendError != nil && sendError.Status >= 500 { 29 | logger.Printf("Error sending response: %s\n", sendError.Error()) 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /jsh-api/utility.go: -------------------------------------------------------------------------------- 1 | package jshapi 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/derekdowling/go-json-spec-handler" 7 | ) 8 | 9 | const testType = "test" 10 | 11 | var testObjAttrs = map[string]string{ 12 | "foo": "bar", 13 | } 14 | 15 | // NewMockResource builds a mock API endpoint that can perform basic CRUD actions: 16 | // 17 | // GET /types 18 | // POST /types 19 | // GET /types/:id 20 | // DELETE /types/:id 21 | // PATCH /types/:id 22 | // 23 | // Will return objects and lists based upon the sampleObject that is specified here 24 | // in the constructor. 25 | func NewMockResource(resourceType string, listCount int, sampleObject interface{}) *Resource { 26 | mock := &MockStorage{ 27 | ResourceType: resourceType, 28 | ResourceAttributes: sampleObject, 29 | ListCount: listCount, 30 | } 31 | 32 | return NewCRUDResource(resourceType, mock) 33 | } 34 | 35 | func sampleObject(id string, resourceType string, sampleObject interface{}) *jsh.Object { 36 | object, err := jsh.NewObject(id, resourceType, sampleObject) 37 | if err != nil { 38 | log.Fatal(err.Error()) 39 | } 40 | 41 | return object 42 | } 43 | -------------------------------------------------------------------------------- /_vendor/github.com/jtolds/gls/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Space Monkey, Inc. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /client/get_test.go: -------------------------------------------------------------------------------- 1 | package jsc 2 | 3 | import ( 4 | "net/http" 5 | "net/http/httptest" 6 | "testing" 7 | 8 | . "github.com/smartystreets/goconvey/convey" 9 | ) 10 | 11 | func TestGet(t *testing.T) { 12 | 13 | api := testAPI() 14 | server := httptest.NewServer(api) 15 | defer server.Close() 16 | 17 | baseURL := server.URL 18 | 19 | Convey("Get Tests", t, func() { 20 | 21 | Convey("->List()", func() { 22 | 23 | Convey("should handle an object listing request", func() { 24 | json, resp, err := List(baseURL, "tests") 25 | 26 | So(err, ShouldBeNil) 27 | So(resp.StatusCode, ShouldEqual, http.StatusOK) 28 | So(json.HasErrors(), ShouldBeFalse) 29 | So(json.HasData(), ShouldBeTrue) 30 | }) 31 | }) 32 | 33 | Convey("->Fetch()", func() { 34 | 35 | Convey("should handle a specific object request", func() { 36 | json, resp, err := Fetch(baseURL, "tests", "1") 37 | 38 | So(err, ShouldBeNil) 39 | So(resp.StatusCode, ShouldEqual, http.StatusOK) 40 | So(json.HasErrors(), ShouldBeFalse) 41 | So(json.HasData(), ShouldBeTrue) 42 | So(json.First().ID, ShouldEqual, "1") 43 | }) 44 | }) 45 | }) 46 | } 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Derek Dowling 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /jsh-api/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Derek Dowling 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /goji2-logger/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Derek Dowling 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /_vendor/goji.io/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, 2016 Carl Jackson (carl@avtok.com) 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /_vendor/github.com/asaskevich/govalidator/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Alex Saskevich 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /_vendor/github.com/zenazn/goji/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, 2015 Carl Jackson (carl@avtok.com) 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /_vendor/github.com/derekdowling/go-stdlogger/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Derek Dowling 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /_vendor/goji.io/pat/match.go: -------------------------------------------------------------------------------- 1 | package pat 2 | 3 | import ( 4 | "context" 5 | "sort" 6 | 7 | "goji.io/internal" 8 | "goji.io/pattern" 9 | ) 10 | 11 | type match struct { 12 | context.Context 13 | pat *Pattern 14 | matches []string 15 | } 16 | 17 | func (m match) Value(key interface{}) interface{} { 18 | switch key { 19 | case pattern.AllVariables: 20 | var vs map[pattern.Variable]interface{} 21 | if vsi := m.Context.Value(key); vsi == nil { 22 | if len(m.pat.pats) == 0 { 23 | return nil 24 | } 25 | vs = make(map[pattern.Variable]interface{}, len(m.matches)) 26 | } else { 27 | vs = vsi.(map[pattern.Variable]interface{}) 28 | } 29 | 30 | for _, p := range m.pat.pats { 31 | vs[p.name] = m.matches[p.idx] 32 | } 33 | return vs 34 | case internal.Path: 35 | if len(m.matches) == len(m.pat.pats)+1 { 36 | return m.matches[len(m.matches)-1] 37 | } 38 | return "" 39 | } 40 | 41 | if k, ok := key.(pattern.Variable); ok { 42 | i := sort.Search(len(m.pat.pats), func(i int) bool { 43 | return m.pat.pats[i].name >= k 44 | }) 45 | if i < len(m.pat.pats) && m.pat.pats[i].name == k { 46 | return m.matches[m.pat.pats[i].idx] 47 | } 48 | } 49 | 50 | return m.Context.Value(key) 51 | } 52 | -------------------------------------------------------------------------------- /list.go: -------------------------------------------------------------------------------- 1 | package jsh 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/http" 7 | ) 8 | 9 | // List is just a wrapper around an object array that implements Sendable 10 | type List []*Object 11 | 12 | /* 13 | Validate ensures that List is JSON API compatible. 14 | */ 15 | func (list List) Validate(r *http.Request, response bool) *Error { 16 | for _, object := range list { 17 | err := object.Validate(r, response) 18 | if err != nil { 19 | return err 20 | } 21 | } 22 | 23 | return nil 24 | } 25 | 26 | /* 27 | UnmarshalJSON allows us to manually decode a list via the json.Unmarshaler 28 | interface. 29 | */ 30 | func (list *List) UnmarshalJSON(rawData []byte) error { 31 | // Create a sub-type here so when we call Unmarshal below, we don't recursively 32 | // call this function over and over 33 | type UnmarshalList List 34 | 35 | // if our "List" is a single object, modify the JSON to make it into a list 36 | // by wrapping with "[ ]" 37 | if rawData[0] == '{' { 38 | rawData = []byte(fmt.Sprintf("[%s]", rawData)) 39 | } 40 | 41 | newList := UnmarshalList{} 42 | 43 | err := json.Unmarshal(rawData, &newList) 44 | if err != nil { 45 | return err 46 | } 47 | 48 | convertedList := List(newList) 49 | *list = convertedList 50 | 51 | return nil 52 | } 53 | -------------------------------------------------------------------------------- /_vendor/github.com/asaskevich/govalidator/converter.go: -------------------------------------------------------------------------------- 1 | package govalidator 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "strconv" 7 | ) 8 | 9 | // ToString convert the input to a string. 10 | func ToString(obj interface{}) string { 11 | res := fmt.Sprintf("%v", obj) 12 | return string(res) 13 | } 14 | 15 | // ToJSON convert the input to a valid JSON string 16 | func ToJSON(obj interface{}) (string, error) { 17 | res, err := json.Marshal(obj) 18 | if err != nil { 19 | res = []byte("") 20 | } 21 | return string(res), err 22 | } 23 | 24 | // ToFloat convert the input string to a float, or 0.0 if the input is not a float. 25 | func ToFloat(str string) (float64, error) { 26 | res, err := strconv.ParseFloat(str, 64) 27 | if err != nil { 28 | res = 0.0 29 | } 30 | return res, err 31 | } 32 | 33 | // ToInt convert the input string to an integer, or 0 if the input is not an integer. 34 | func ToInt(str string) (int64, error) { 35 | res, err := strconv.ParseInt(str, 0, 64) 36 | if err != nil { 37 | res = 0 38 | } 39 | return res, err 40 | } 41 | 42 | // ToBoolean convert the input string to a boolean. 43 | func ToBoolean(str string) (bool, error) { 44 | res, err := strconv.ParseBool(str) 45 | if err != nil { 46 | res = false 47 | } 48 | return res, err 49 | } 50 | -------------------------------------------------------------------------------- /response_test.go: -------------------------------------------------------------------------------- 1 | package jsh 2 | 3 | import ( 4 | "encoding/json" 5 | "net/http" 6 | "net/http/httptest" 7 | "strconv" 8 | "testing" 9 | 10 | . "github.com/smartystreets/goconvey/convey" 11 | ) 12 | 13 | func TestSend(t *testing.T) { 14 | 15 | Convey("Send Tests", t, func() { 16 | 17 | writer := httptest.NewRecorder() 18 | request := &http.Request{} 19 | 20 | object := &Object{ 21 | ID: "1234", 22 | Type: "user", 23 | Attributes: json.RawMessage(`{"foo":"bar"}`), 24 | } 25 | 26 | Convey("Success Handlers", func() { 27 | 28 | Convey("->Send()", func() { 29 | 30 | Convey("should send a proper HTTP JSON response", func() { 31 | 32 | request.Method = "GET" 33 | 34 | err := Send(writer, request, object) 35 | So(err, ShouldBeNil) 36 | So(writer.Code, ShouldEqual, http.StatusOK) 37 | 38 | contentLength, convErr := strconv.Atoi(writer.HeaderMap.Get("Content-Length")) 39 | So(convErr, ShouldBeNil) 40 | So(contentLength, ShouldBeGreaterThan, 0) 41 | So(writer.HeaderMap.Get("Content-Type"), ShouldEqual, ContentType) 42 | }) 43 | }) 44 | }) 45 | 46 | Convey("->Ok()", func() { 47 | doc := Ok() 48 | err := SendDocument(writer, request, doc) 49 | So(err, ShouldBeNil) 50 | So(writer.Code, ShouldEqual, http.StatusOK) 51 | }) 52 | }) 53 | } 54 | -------------------------------------------------------------------------------- /_vendor/goji.io/pat/methods.go: -------------------------------------------------------------------------------- 1 | package pat 2 | 3 | /* 4 | Delete returns a Pat route that only matches the DELETE HTTP method. 5 | */ 6 | func Delete(pat string) *Pattern { 7 | return newWithMethods(pat, "DELETE") 8 | } 9 | 10 | /* 11 | Get returns a Pat route that only matches the GET and HEAD HTTP method. HEAD 12 | requests are handled transparently by net/http. 13 | */ 14 | func Get(pat string) *Pattern { 15 | return newWithMethods(pat, "GET", "HEAD") 16 | } 17 | 18 | /* 19 | Head returns a Pat route that only matches the HEAD HTTP method. 20 | */ 21 | func Head(pat string) *Pattern { 22 | return newWithMethods(pat, "HEAD") 23 | } 24 | 25 | /* 26 | Options returns a Pat route that only matches the OPTIONS HTTP method. 27 | */ 28 | func Options(pat string) *Pattern { 29 | return newWithMethods(pat, "OPTIONS") 30 | } 31 | 32 | /* 33 | Patch returns a Pat route that only matches the PATCH HTTP method. 34 | */ 35 | func Patch(pat string) *Pattern { 36 | return newWithMethods(pat, "PATCH") 37 | } 38 | 39 | /* 40 | Post returns a Pat route that only matches the POST HTTP method. 41 | */ 42 | func Post(pat string) *Pattern { 43 | return newWithMethods(pat, "POST") 44 | } 45 | 46 | /* 47 | Put returns a Pat route that only matches the PUT HTTP method. 48 | */ 49 | func Put(pat string) *Pattern { 50 | return newWithMethods(pat, "PUT") 51 | } 52 | -------------------------------------------------------------------------------- /_vendor/goji.io/handle.go: -------------------------------------------------------------------------------- 1 | package goji 2 | 3 | import "net/http" 4 | 5 | /* 6 | Handle adds a new route to the Mux. Requests that match the given Pattern will 7 | be dispatched to the given http.Handler. 8 | 9 | Routing is performed in the order in which routes are added: the first route 10 | with a matching Pattern will be used. In particular, Goji guarantees that 11 | routing is performed in a manner that is indistinguishable from the following 12 | algorithm: 13 | 14 | // Assume routes is a slice that every call to Handle appends to 15 | for _, route := range routes { 16 | // For performance, Patterns can opt out of this call to Match. 17 | // See the documentation for Pattern for more. 18 | if r2 := route.pattern.Match(r); r2 != nil { 19 | route.handler.ServeHTTP(w, r2) 20 | break 21 | } 22 | } 23 | 24 | It is not safe to concurrently register routes from multiple goroutines, or to 25 | register routes concurrently with requests. 26 | */ 27 | func (m *Mux) Handle(p Pattern, h http.Handler) { 28 | m.router.add(p, h) 29 | } 30 | 31 | /* 32 | HandleFunc adds a new route to the Mux. It is equivalent to calling Handle on a 33 | handler wrapped with http.HandlerFunc, and is provided only for convenience. 34 | */ 35 | func (m *Mux) HandleFunc(p Pattern, h func(http.ResponseWriter, *http.Request)) { 36 | m.Handle(p, http.HandlerFunc(h)) 37 | } 38 | -------------------------------------------------------------------------------- /_vendor/github.com/smartystreets/goconvey/convey/reporting/reporter.go: -------------------------------------------------------------------------------- 1 | package reporting 2 | 3 | import "io" 4 | 5 | type Reporter interface { 6 | BeginStory(story *StoryReport) 7 | Enter(scope *ScopeReport) 8 | Report(r *AssertionResult) 9 | Exit() 10 | EndStory() 11 | io.Writer 12 | } 13 | 14 | type reporters struct{ collection []Reporter } 15 | 16 | func (self *reporters) BeginStory(s *StoryReport) { self.foreach(func(r Reporter) { r.BeginStory(s) }) } 17 | func (self *reporters) Enter(s *ScopeReport) { self.foreach(func(r Reporter) { r.Enter(s) }) } 18 | func (self *reporters) Report(a *AssertionResult) { self.foreach(func(r Reporter) { r.Report(a) }) } 19 | func (self *reporters) Exit() { self.foreach(func(r Reporter) { r.Exit() }) } 20 | func (self *reporters) EndStory() { self.foreach(func(r Reporter) { r.EndStory() }) } 21 | 22 | func (self *reporters) Write(contents []byte) (written int, err error) { 23 | self.foreach(func(r Reporter) { 24 | written, err = r.Write(contents) 25 | }) 26 | return written, err 27 | } 28 | 29 | func (self *reporters) foreach(action func(Reporter)) { 30 | for _, r := range self.collection { 31 | action(r) 32 | } 33 | } 34 | 35 | func NewReporters(collection ...Reporter) *reporters { 36 | self := new(reporters) 37 | self.collection = collection 38 | return self 39 | } 40 | -------------------------------------------------------------------------------- /client/delete.go: -------------------------------------------------------------------------------- 1 | package jsc 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "net/url" 7 | 8 | "github.com/derekdowling/go-json-spec-handler" 9 | ) 10 | 11 | /* 12 | Delete allows a user to make an outbound "DELETE /resource/:id" request. 13 | 14 | resp, err := jsh.Delete("http://apiserver", "user", "2") 15 | */ 16 | func Delete(urlStr string, resourceType string, id string) (*http.Response, error) { 17 | request, err := DeleteRequest(urlStr, resourceType, id) 18 | if err != nil { 19 | return nil, err 20 | } 21 | 22 | _, response, err := Do(request, jsh.ObjectMode) 23 | if err != nil { 24 | return nil, err 25 | } 26 | 27 | return response, nil 28 | } 29 | 30 | /* 31 | DeleteRequest returns a fully formatted request for performing a JSON API DELETE. 32 | This is useful for if you need to set custom headers on the request. Otherwise 33 | just use "jsc.Delete". 34 | */ 35 | func DeleteRequest(urlStr string, resourceType string, id string) (*http.Request, error) { 36 | u, err := url.Parse(urlStr) 37 | if err != nil { 38 | return nil, jsh.ISE(fmt.Sprintf("Error parsing URL: %s", err.Error())) 39 | } 40 | 41 | setIDPath(u, resourceType, id) 42 | 43 | request, err := NewRequest("DELETE", u.String(), nil) 44 | if err != nil { 45 | return nil, jsh.ISE(fmt.Sprintf("Error creating DELETE request: %s", err.Error())) 46 | } 47 | 48 | return request, nil 49 | } 50 | -------------------------------------------------------------------------------- /_vendor/golang.org/x/net/PATENTS: -------------------------------------------------------------------------------- 1 | Additional IP Rights Grant (Patents) 2 | 3 | "This implementation" means the copyrightable works distributed by 4 | Google as part of the Go project. 5 | 6 | Google hereby grants to You a perpetual, worldwide, non-exclusive, 7 | no-charge, royalty-free, irrevocable (except as stated in this section) 8 | patent license to make, have made, use, offer to sell, sell, import, 9 | transfer and otherwise run, modify and propagate the contents of this 10 | implementation of Go, where such license applies only to those patent 11 | claims, both currently owned or controlled by Google and acquired in 12 | the future, licensable by Google that are necessarily infringed by this 13 | implementation of Go. This grant does not include claims that would be 14 | infringed only as a consequence of further modification of this 15 | implementation. If you or your agent or exclusive licensee institute or 16 | order or agree to the institution of patent litigation against any 17 | entity (including a cross-claim or counterclaim in a lawsuit) alleging 18 | that this implementation of Go or any code incorporated within this 19 | implementation of Go constitutes direct or contributory patent 20 | infringement, or inducement of patent infringement, then any patent 21 | rights granted to you under this License for this implementation of Go 22 | shall terminate as of the date such litigation is filed. 23 | -------------------------------------------------------------------------------- /_vendor/github.com/smartystreets/goconvey/LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 SmartyStreets, LLC 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | 21 | NOTE: Various optional and subordinate components carry their own licensing 22 | requirements and restrictions. Use of those components is subject to the terms 23 | and conditions outlined the respective license of each component. 24 | -------------------------------------------------------------------------------- /_vendor/goji.io/pat/url.go: -------------------------------------------------------------------------------- 1 | package pat 2 | 3 | import "net/url" 4 | 5 | // Stolen (with modifications) from net/url in the Go stdlib 6 | 7 | func ishex(c byte) bool { 8 | switch { 9 | case '0' <= c && c <= '9': 10 | return true 11 | case 'a' <= c && c <= 'f': 12 | return true 13 | case 'A' <= c && c <= 'F': 14 | return true 15 | } 16 | return false 17 | } 18 | 19 | func unhex(c byte) byte { 20 | switch { 21 | case '0' <= c && c <= '9': 22 | return c - '0' 23 | case 'a' <= c && c <= 'f': 24 | return c - 'a' + 10 25 | case 'A' <= c && c <= 'F': 26 | return c - 'A' + 10 27 | } 28 | return 0 29 | } 30 | 31 | func unescape(s string) (string, error) { 32 | // Count %, check that they're well-formed. 33 | n := 0 34 | for i := 0; i < len(s); { 35 | switch s[i] { 36 | case '%': 37 | n++ 38 | if i+2 >= len(s) || !ishex(s[i+1]) || !ishex(s[i+2]) { 39 | s = s[i:] 40 | if len(s) > 3 { 41 | s = s[:3] 42 | } 43 | return "", url.EscapeError(s) 44 | } 45 | i += 3 46 | default: 47 | i++ 48 | } 49 | } 50 | 51 | if n == 0 { 52 | return s, nil 53 | } 54 | 55 | t := make([]byte, len(s)-2*n) 56 | j := 0 57 | for i := 0; i < len(s); { 58 | switch s[i] { 59 | case '%': 60 | t[j] = unhex(s[i+1])<<4 | unhex(s[i+2]) 61 | j++ 62 | i += 3 63 | default: 64 | t[j] = s[i] 65 | j++ 66 | i++ 67 | } 68 | } 69 | return string(t), nil 70 | } 71 | -------------------------------------------------------------------------------- /client/post.go: -------------------------------------------------------------------------------- 1 | package jsc 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "net/url" 7 | 8 | "github.com/derekdowling/go-json-spec-handler" 9 | ) 10 | 11 | // Post allows a user to make an outbound POST /resources request: 12 | // 13 | // obj, _ := jsh.NewObject("123", "user", payload) 14 | // // does POST http://apiserver/user/123 15 | // json, resp, err := jsh.Post("http://apiserver", obj) 16 | func Post(baseURL string, object *jsh.Object) (*jsh.Document, *http.Response, error) { 17 | request, err := PostRequest(baseURL, object) 18 | if err != nil { 19 | return nil, nil, err 20 | } 21 | 22 | return Do(request, jsh.ObjectMode) 23 | } 24 | 25 | // PostRequest returns a fully formatted request with JSON body for performing 26 | // a JSONAPI POST. This is useful for if you need to set custom headers on the 27 | // request. Otherwise just use "jsc.Post". 28 | func PostRequest(baseURL string, object *jsh.Object) (*http.Request, error) { 29 | u, err := url.Parse(baseURL) 30 | if err != nil { 31 | return nil, fmt.Errorf("Error parsing URL: %s", err.Error()) 32 | } 33 | 34 | setPath(u, object.Type) 35 | 36 | request, err := NewRequest("POST", u.String(), nil) 37 | if err != nil { 38 | return nil, fmt.Errorf("Error building POST request: %s", err.Error()) 39 | } 40 | 41 | err = prepareBody(request, object) 42 | if err != nil { 43 | return nil, err 44 | } 45 | 46 | return request, nil 47 | } 48 | -------------------------------------------------------------------------------- /_vendor/github.com/gopherjs/gopherjs/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Richard Musiol. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 15 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 16 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 17 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 18 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 19 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 20 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /_vendor/github.com/smartystreets/assertions/LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 SmartyStreets, LLC 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | 21 | NOTE: Various optional and subordinate components carry their own licensing 22 | requirements and restrictions. Use of those components is subject to the terms 23 | and conditions outlined the respective license of each component. 24 | -------------------------------------------------------------------------------- /client/test_util.go: -------------------------------------------------------------------------------- 1 | package jsc 2 | 3 | import ( 4 | "net/http" 5 | "net/http/httptest" 6 | "net/url" 7 | 8 | "github.com/derekdowling/go-json-spec-handler" 9 | ) 10 | 11 | func mockObjectResponse(object *jsh.Object) (*http.Response, error) { 12 | url := &url.URL{Host: "tests"} 13 | setIDPath(url, object.Type, object.ID) 14 | 15 | req, reqErr := http.NewRequest("GET", url.String(), nil) 16 | if reqErr != nil { 17 | return nil, reqErr 18 | } 19 | 20 | err := object.Validate(req, false) 21 | if err != nil { 22 | return nil, err 23 | } 24 | 25 | recorder := httptest.NewRecorder() 26 | jsh.Send(recorder, req, object) 27 | return recorderToResponse(recorder), nil 28 | } 29 | 30 | func mockListResponse(list jsh.List) (*http.Response, error) { 31 | 32 | url := &url.URL{Host: "test"} 33 | setPath(url, list[0].Type) 34 | 35 | req, reqErr := http.NewRequest("GET", url.String(), nil) 36 | if reqErr != nil { 37 | return nil, reqErr 38 | } 39 | 40 | err := list.Validate(req, false) 41 | if err != nil { 42 | return nil, err 43 | } 44 | 45 | recorder := httptest.NewRecorder() 46 | jsh.Send(recorder, req, list) 47 | return recorderToResponse(recorder), nil 48 | } 49 | 50 | func recorderToResponse(recorder *httptest.ResponseRecorder) *http.Response { 51 | return &http.Response{ 52 | StatusCode: recorder.Code, 53 | Body: jsh.CreateReadCloser(recorder.Body.Bytes()), 54 | Header: recorder.Header(), 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /jsh-api/api_test.go: -------------------------------------------------------------------------------- 1 | package jshapi 2 | 3 | import ( 4 | "net/http" 5 | "net/http/httptest" 6 | "testing" 7 | 8 | "github.com/derekdowling/go-json-spec-handler" 9 | "github.com/derekdowling/go-json-spec-handler/client" 10 | . "github.com/smartystreets/goconvey/convey" 11 | ) 12 | 13 | const testResourceType = "bars" 14 | 15 | func TestAPI(t *testing.T) { 16 | 17 | Convey("API Tests", t, func() { 18 | 19 | api := New("api") 20 | 21 | So(api.prefix, ShouldEqual, "/api") 22 | 23 | testAttrs := map[string]string{ 24 | "foo": "bar", 25 | } 26 | 27 | Convey("->AddResource()", func() { 28 | resource := NewMockResource(testResourceType, 1, testAttrs) 29 | api.Add(resource) 30 | 31 | So(api.Resources[testResourceType], ShouldEqual, resource) 32 | 33 | server := httptest.NewServer(api) 34 | baseURL := server.URL + api.prefix 35 | 36 | Convey("should work with / routes", func() { 37 | _, resp, err := jsc.List(baseURL, testResourceType) 38 | 39 | So(resp.StatusCode, ShouldEqual, http.StatusOK) 40 | So(err, ShouldBeNil) 41 | }) 42 | 43 | Convey("should work with //:id routes", func() { 44 | patchObj, err := jsh.NewObject("1", testResourceType, testAttrs) 45 | So(err, ShouldBeNil) 46 | 47 | _, resp, patchErr := jsc.Patch(baseURL, patchObj) 48 | So(resp.StatusCode, ShouldEqual, http.StatusOK) 49 | So(patchErr, ShouldBeNil) 50 | }) 51 | }) 52 | }) 53 | } 54 | -------------------------------------------------------------------------------- /_vendor/github.com/smartystreets/assertions/internal/oglematchers/transform_description.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Aaron Jacobs. All Rights Reserved. 2 | // Author: aaronjjacobs@gmail.com (Aaron Jacobs) 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package oglematchers 17 | 18 | // transformDescription returns a matcher that is equivalent to the supplied 19 | // one, except that it has the supplied description instead of the one attached 20 | // to the existing matcher. 21 | func transformDescription(m Matcher, newDesc string) Matcher { 22 | return &transformDescriptionMatcher{newDesc, m} 23 | } 24 | 25 | type transformDescriptionMatcher struct { 26 | desc string 27 | wrappedMatcher Matcher 28 | } 29 | 30 | func (m *transformDescriptionMatcher) Description() string { 31 | return m.desc 32 | } 33 | 34 | func (m *transformDescriptionMatcher) Matches(c interface{}) error { 35 | return m.wrappedMatcher.Matches(c) 36 | } 37 | -------------------------------------------------------------------------------- /client/patch.go: -------------------------------------------------------------------------------- 1 | package jsc 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "net/url" 7 | 8 | "github.com/derekdowling/go-json-spec-handler" 9 | ) 10 | 11 | // Patch allows a consumer to perform a PATCH /resources/:id request 12 | // Example: 13 | // 14 | // obj, _ := jsh.NewObject("123", "user", payload) 15 | // // does PATCH /http://postap.com/api/user/123 16 | // json, resp, err := jsc.Patch("http://postap.com/api/", obj) 17 | // updatedObj := json.First() 18 | // 19 | func Patch(baseURL string, object *jsh.Object) (*jsh.Document, *http.Response, error) { 20 | request, err := PatchRequest(baseURL, object) 21 | if err != nil { 22 | return nil, nil, err 23 | } 24 | 25 | return Do(request, jsh.ObjectMode) 26 | } 27 | 28 | // PatchRequest returns a fully formatted request with JSON body for performing 29 | // a JSONAPI PATCH. This is useful for if you need to set custom headers on the 30 | // request. Otherwise just use "jsc.Patch". 31 | func PatchRequest(baseURL string, object *jsh.Object) (*http.Request, error) { 32 | u, err := url.Parse(baseURL) 33 | if err != nil { 34 | return nil, fmt.Errorf("Error parsing URL: %s", err.Error()) 35 | } 36 | 37 | setIDPath(u, object.Type, object.ID) 38 | 39 | request, err := NewRequest("PATCH", u.String(), nil) 40 | if err != nil { 41 | return nil, fmt.Errorf("Error creating PATCH request: %s", err.Error()) 42 | } 43 | 44 | err = prepareBody(request, object) 45 | if err != nil { 46 | return nil, err 47 | } 48 | 49 | return request, nil 50 | } 51 | -------------------------------------------------------------------------------- /_vendor/github.com/smartystreets/goconvey/convey/reporting/printer.go: -------------------------------------------------------------------------------- 1 | package reporting 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "strings" 7 | ) 8 | 9 | type Printer struct { 10 | out io.Writer 11 | prefix string 12 | } 13 | 14 | func (self *Printer) Println(message string, values ...interface{}) { 15 | formatted := self.format(message, values...) + newline 16 | self.out.Write([]byte(formatted)) 17 | } 18 | 19 | func (self *Printer) Print(message string, values ...interface{}) { 20 | formatted := self.format(message, values...) 21 | self.out.Write([]byte(formatted)) 22 | } 23 | 24 | func (self *Printer) Insert(text string) { 25 | self.out.Write([]byte(text)) 26 | } 27 | 28 | func (self *Printer) format(message string, values ...interface{}) string { 29 | var formatted string 30 | if len(values) == 0 { 31 | formatted = self.prefix + message 32 | } else { 33 | formatted = self.prefix + fmt.Sprintf(message, values...) 34 | } 35 | indented := strings.Replace(formatted, newline, newline+self.prefix, -1) 36 | return strings.TrimRight(indented, space) 37 | } 38 | 39 | func (self *Printer) Indent() { 40 | self.prefix += pad 41 | } 42 | 43 | func (self *Printer) Dedent() { 44 | if len(self.prefix) >= padLength { 45 | self.prefix = self.prefix[:len(self.prefix)-padLength] 46 | } 47 | } 48 | 49 | func NewPrinter(out io.Writer) *Printer { 50 | self := new(Printer) 51 | self.out = out 52 | return self 53 | } 54 | 55 | const space = " " 56 | const pad = space + space 57 | const padLength = len(pad) 58 | -------------------------------------------------------------------------------- /jsh-api/store/store.go: -------------------------------------------------------------------------------- 1 | // Package store is a collection of composable interfaces that are can be implemented 2 | // in order to build a storage driver 3 | package store 4 | 5 | import ( 6 | "context" 7 | 8 | "github.com/derekdowling/go-json-spec-handler" 9 | ) 10 | 11 | // CRUD implements all sub-storage functions 12 | type CRUD interface { 13 | Save(ctx context.Context, object *jsh.Object) (*jsh.Object, jsh.ErrorType) 14 | Get(ctx context.Context, id string) (*jsh.Object, jsh.ErrorType) 15 | List(ctx context.Context) (jsh.List, jsh.ErrorType) 16 | Update(ctx context.Context, object *jsh.Object) (*jsh.Object, jsh.ErrorType) 17 | Delete(ctx context.Context, id string) jsh.ErrorType 18 | } 19 | 20 | // Save a new resource to storage 21 | type Save func(ctx context.Context, object *jsh.Object) (*jsh.Object, jsh.ErrorType) 22 | 23 | // Get a specific instance of a resource by id from storage 24 | type Get func(ctx context.Context, id string) (*jsh.Object, jsh.ErrorType) 25 | 26 | // List all instances of a resource from storage 27 | type List func(ctx context.Context) (jsh.List, jsh.ErrorType) 28 | 29 | // Update an existing object in storage 30 | type Update func(ctx context.Context, object *jsh.Object) (*jsh.Object, jsh.ErrorType) 31 | 32 | // Delete an object from storage by id 33 | type Delete func(ctx context.Context, id string) jsh.ErrorType 34 | 35 | // ToMany retrieves a list of objects of a single resource type that are related to 36 | // the provided resource id 37 | type ToMany func(ctx context.Context, id string) (jsh.List, jsh.ErrorType) 38 | -------------------------------------------------------------------------------- /_vendor/github.com/asaskevich/govalidator/numerics.go: -------------------------------------------------------------------------------- 1 | package govalidator 2 | 3 | import "math" 4 | 5 | // Abs returns absolute value of number 6 | func Abs(value float64) float64 { 7 | return value * Sign(value) 8 | } 9 | 10 | // Sign returns signum of number: 1 in case of value > 0, -1 in case of value < 0, 0 otherwise 11 | func Sign(value float64) float64 { 12 | if value > 0 { 13 | return 1 14 | } else if value < 0 { 15 | return -1 16 | } else { 17 | return 0 18 | } 19 | } 20 | 21 | // IsNegative returns true if value < 0 22 | func IsNegative(value float64) bool { 23 | return value < 0 24 | } 25 | 26 | // IsPositive returns true if value > 0 27 | func IsPositive(value float64) bool { 28 | return value > 0 29 | } 30 | 31 | // IsNonNegative returns true if value >= 0 32 | func IsNonNegative(value float64) bool { 33 | return value >= 0 34 | } 35 | 36 | // IsNonPositive returns true if value <= 0 37 | func IsNonPositive(value float64) bool { 38 | return value <= 0 39 | } 40 | 41 | // InRange returns true if value lies between left and right border 42 | func InRange(value, left, right float64) bool { 43 | if left > right { 44 | left, right = right, left 45 | } 46 | return value >= left && value <= right 47 | } 48 | 49 | // IsWhole returns true if value is whole number 50 | func IsWhole(value float64) bool { 51 | return Abs(math.Remainder(value, 1)) == 0 52 | } 53 | 54 | // IsNatural returns true if value is natural number (positive and whole) 55 | func IsNatural(value float64) bool { 56 | return IsWhole(value) && IsPositive(value) 57 | } 58 | -------------------------------------------------------------------------------- /client/action.go: -------------------------------------------------------------------------------- 1 | package jsc 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "net/url" 7 | "strings" 8 | 9 | "github.com/derekdowling/go-json-spec-handler" 10 | ) 11 | 12 | // Action performs an outbound GET /resource/:id/action request 13 | func Action(baseURL string, resourceType string, id string, action string) (*jsh.Document, *http.Response, error) { 14 | request, err := ActionRequest(baseURL, resourceType, id, action) 15 | if err != nil { 16 | return nil, nil, err 17 | } 18 | 19 | return Do(request, jsh.ObjectMode) 20 | } 21 | 22 | /* 23 | ActionRequest returns a fully formatted JSONAPI Action (GET /resource/:id/action) request. 24 | Useful if you need to set custom headers before proceeding. Otherwise just use "jsh.Action". 25 | */ 26 | func ActionRequest(baseURL string, resourceType, id string, action string) (*http.Request, error) { 27 | if id == "" { 28 | return nil, jsh.SpecificationError("ID cannot be empty for an Action request type") 29 | } 30 | 31 | if action == "" { 32 | return nil, jsh.SpecificationError("Action specifier cannot be empty for an Action request type") 33 | } 34 | 35 | u, urlErr := url.Parse(baseURL) 36 | if urlErr != nil { 37 | return nil, jsh.ISE(fmt.Sprintf("Error parsing URL: %s", urlErr.Error())) 38 | } 39 | 40 | setIDPath(u, resourceType, id) 41 | 42 | // concat the action the end of url.Path, ensure no "/" prefix 43 | if strings.HasPrefix(action, "/") { 44 | action = action[1:] 45 | } 46 | 47 | u.Path = strings.Join([]string{u.Path, action}, "/") 48 | 49 | return NewRequest("GET", u.String(), nil) 50 | } 51 | -------------------------------------------------------------------------------- /_vendor/github.com/smartystreets/assertions/internal/oglematchers/greater_than.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Aaron Jacobs. All Rights Reserved. 2 | // Author: aaronjjacobs@gmail.com (Aaron Jacobs) 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package oglematchers 17 | 18 | import ( 19 | "fmt" 20 | "reflect" 21 | ) 22 | 23 | // GreaterThan returns a matcher that matches integer, floating point, or 24 | // strings values v such that v > x. Comparison is not defined between numeric 25 | // and string types, but is defined between all integer and floating point 26 | // types. 27 | // 28 | // x must itself be an integer, floating point, or string type; otherwise, 29 | // GreaterThan will panic. 30 | func GreaterThan(x interface{}) Matcher { 31 | desc := fmt.Sprintf("greater than %v", x) 32 | 33 | // Special case: make it clear that strings are strings. 34 | if reflect.TypeOf(x).Kind() == reflect.String { 35 | desc = fmt.Sprintf("greater than \"%s\"", x) 36 | } 37 | 38 | return transformDescription(Not(LessOrEqual(x)), desc) 39 | } 40 | -------------------------------------------------------------------------------- /_vendor/golang.org/x/net/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 The Go Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /_vendor/github.com/smartystreets/assertions/internal/oglematchers/greater_or_equal.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Aaron Jacobs. All Rights Reserved. 2 | // Author: aaronjjacobs@gmail.com (Aaron Jacobs) 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package oglematchers 17 | 18 | import ( 19 | "fmt" 20 | "reflect" 21 | ) 22 | 23 | // GreaterOrEqual returns a matcher that matches integer, floating point, or 24 | // strings values v such that v >= x. Comparison is not defined between numeric 25 | // and string types, but is defined between all integer and floating point 26 | // types. 27 | // 28 | // x must itself be an integer, floating point, or string type; otherwise, 29 | // GreaterOrEqual will panic. 30 | func GreaterOrEqual(x interface{}) Matcher { 31 | desc := fmt.Sprintf("greater than or equal to %v", x) 32 | 33 | // Special case: make it clear that strings are strings. 34 | if reflect.TypeOf(x).Kind() == reflect.String { 35 | desc = fmt.Sprintf("greater than or equal to \"%s\"", x) 36 | } 37 | 38 | return transformDescription(Not(LessThan(x)), desc) 39 | } 40 | -------------------------------------------------------------------------------- /relationship.go: -------------------------------------------------------------------------------- 1 | package jsh 2 | 3 | import ( 4 | "fmt" 5 | 6 | "encoding/json" 7 | ) 8 | 9 | // Relationship represents a reference from the resource object in which it's 10 | // defined to other resource objects. 11 | type Relationship struct { 12 | Links *Links `json:"links,omitempty"` 13 | Data ResourceLinkage `json:"data,omitempty"` 14 | Meta map[string]interface{} `json:"meta,omitempty"` 15 | } 16 | 17 | // ResourceLinkage is a typedef around a slice of resource identifiers. This 18 | // allows us to implement a custom UnmarshalJSON. 19 | type ResourceLinkage []*ResourceIdentifier 20 | 21 | // ResourceIdentifier identifies an individual resource. 22 | type ResourceIdentifier struct { 23 | Type string `json:"type" valid:"required"` 24 | ID string `json:"id" valid:"required"` 25 | } 26 | 27 | /* 28 | UnmarshalJSON allows us to manually decode a the resource linkage via the 29 | json.Unmarshaler interface. 30 | */ 31 | func (rl *ResourceLinkage) UnmarshalJSON(data []byte) error { 32 | // Create a sub-type here so when we call Unmarshal below, we don't recursively 33 | // call this function over and over 34 | type UnmarshalLinkage ResourceLinkage 35 | 36 | // if our "List" is a single object, modify the JSON to make it into a list 37 | // by wrapping with "[ ]" 38 | if data[0] == '{' { 39 | data = []byte(fmt.Sprintf("[%s]", data)) 40 | } 41 | 42 | newLinkage := UnmarshalLinkage{} 43 | 44 | err := json.Unmarshal(data, &newLinkage) 45 | if err != nil { 46 | return err 47 | } 48 | 49 | convertedLinkage := ResourceLinkage(newLinkage) 50 | *rl = convertedLinkage 51 | 52 | return nil 53 | } 54 | -------------------------------------------------------------------------------- /_vendor/github.com/smartystreets/assertions/internal/oglematchers/less_or_equal.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Aaron Jacobs. All Rights Reserved. 2 | // Author: aaronjjacobs@gmail.com (Aaron Jacobs) 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package oglematchers 17 | 18 | import ( 19 | "fmt" 20 | "reflect" 21 | ) 22 | 23 | // LessOrEqual returns a matcher that matches integer, floating point, or 24 | // strings values v such that v <= x. Comparison is not defined between numeric 25 | // and string types, but is defined between all integer and floating point 26 | // types. 27 | // 28 | // x must itself be an integer, floating point, or string type; otherwise, 29 | // LessOrEqual will panic. 30 | func LessOrEqual(x interface{}) Matcher { 31 | desc := fmt.Sprintf("less than or equal to %v", x) 32 | 33 | // Special case: make it clear that strings are strings. 34 | if reflect.TypeOf(x).Kind() == reflect.String { 35 | desc = fmt.Sprintf("less than or equal to \"%s\"", x) 36 | } 37 | 38 | // Put LessThan last so that its error messages will be used in the event of 39 | // failure. 40 | return transformDescription(AnyOf(Equals(x), LessThan(x)), desc) 41 | } 42 | -------------------------------------------------------------------------------- /_vendor/github.com/smartystreets/assertions/internal/oglematchers/not.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Aaron Jacobs. All Rights Reserved. 2 | // Author: aaronjjacobs@gmail.com (Aaron Jacobs) 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package oglematchers 17 | 18 | import ( 19 | "errors" 20 | "fmt" 21 | ) 22 | 23 | // Not returns a matcher that inverts the set of values matched by the wrapped 24 | // matcher. It does not transform the result for values for which the wrapped 25 | // matcher returns a fatal error. 26 | func Not(m Matcher) Matcher { 27 | return ¬Matcher{m} 28 | } 29 | 30 | type notMatcher struct { 31 | wrapped Matcher 32 | } 33 | 34 | func (m *notMatcher) Matches(c interface{}) (err error) { 35 | err = m.wrapped.Matches(c) 36 | 37 | // Did the wrapped matcher say yes? 38 | if err == nil { 39 | return errors.New("") 40 | } 41 | 42 | // Did the wrapped matcher return a fatal error? 43 | if _, isFatal := err.(*FatalError); isFatal { 44 | return err 45 | } 46 | 47 | // The wrapped matcher returned a non-fatal error. 48 | return nil 49 | } 50 | 51 | func (m *notMatcher) Description() string { 52 | return fmt.Sprintf("not(%s)", m.wrapped.Description()) 53 | } 54 | -------------------------------------------------------------------------------- /link.go: -------------------------------------------------------------------------------- 1 | package jsh 2 | 3 | import "encoding/json" 4 | 5 | // Links is a top-level document field 6 | type Links struct { 7 | Self *Link `json:"self,omitempty"` 8 | Related *Link `json:"related,omitempty"` 9 | } 10 | 11 | // Link is a resource link that can encode as a string or as an object 12 | // as per the JSON API specification. 13 | type Link struct { 14 | HREF string `json:"href,omitempty"` 15 | Meta map[string]interface{} `json:"meta,omitempty"` 16 | } 17 | 18 | // NewLink creates a new link encoded as a string. 19 | func NewLink(href string) *Link { 20 | return &Link{ 21 | HREF: href, 22 | } 23 | } 24 | 25 | // NewMetaLink creates a new link with metadata encoded as an object. 26 | func NewMetaLink(href string, meta map[string]interface{}) *Link { 27 | return &Link{ 28 | HREF: href, 29 | Meta: meta, 30 | } 31 | } 32 | 33 | // MarshalJSON implements the Marshaler interface for Link. 34 | func (l *Link) MarshalJSON() ([]byte, error) { 35 | if l.Meta == nil { 36 | return json.Marshal(l.HREF) 37 | } 38 | // Create a sub-type here so when we call Marshal below, we don't recursively 39 | // call this function over and over 40 | type MarshalLink Link 41 | return json.Marshal(MarshalLink(*l)) 42 | } 43 | 44 | // UnmarshalJSON implements the Unmarshaler interface for Link. 45 | func (l *Link) UnmarshalJSON(data []byte) error { 46 | var href string 47 | err := json.Unmarshal(data, &href) 48 | if err == nil { 49 | l.HREF = href 50 | return nil 51 | } 52 | // Create a sub-type here so when we call Unmarshal below, we don't recursively 53 | // call this function over and over 54 | type UnmarshalLink Link 55 | link := UnmarshalLink{} 56 | 57 | err = json.Unmarshal(data, &link) 58 | if err != nil { 59 | return err 60 | } 61 | *l = Link(link) 62 | return nil 63 | } 64 | -------------------------------------------------------------------------------- /client/get.go: -------------------------------------------------------------------------------- 1 | package jsc 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "net/url" 7 | 8 | "github.com/derekdowling/go-json-spec-handler" 9 | ) 10 | 11 | // Fetch performs an outbound GET /resourceTypes/:id request 12 | func Fetch(baseURL string, resourceType string, id string) (*jsh.Document, *http.Response, error) { 13 | request, err := FetchRequest(baseURL, resourceType, id) 14 | if err != nil { 15 | return nil, nil, err 16 | } 17 | 18 | return Do(request, jsh.ObjectMode) 19 | } 20 | 21 | /* 22 | FetchRequest returns a fully formatted JSONAPI Fetch request. Useful if you need to 23 | set custom headers before proceeding. Otherwise just use "jsh.Fetch". 24 | */ 25 | func FetchRequest(baseURL string, resourceType, id string) (*http.Request, error) { 26 | if id == "" { 27 | return nil, jsh.SpecificationError("ID cannot be empty for GetObject request type") 28 | } 29 | 30 | u, urlErr := url.Parse(baseURL) 31 | if urlErr != nil { 32 | return nil, jsh.ISE(fmt.Sprintf("Error parsing URL: %s", urlErr.Error())) 33 | } 34 | 35 | setIDPath(u, resourceType, id) 36 | 37 | return NewRequest("GET", u.String(), nil) 38 | } 39 | 40 | // List prepares an outbound GET /resourceTypes request 41 | func List(baseURL string, resourceType string) (*jsh.Document, *http.Response, error) { 42 | request, err := ListRequest(baseURL, resourceType) 43 | if err != nil { 44 | return nil, nil, err 45 | } 46 | 47 | return Do(request, jsh.ListMode) 48 | } 49 | 50 | /* 51 | ListRequest returns a fully formatted JSONAPI List request. Useful if you need to 52 | set custom headers before proceeding. Otherwise just use "jsh.List". 53 | */ 54 | func ListRequest(baseURL string, resourceType string) (*http.Request, error) { 55 | u, urlErr := url.Parse(baseURL) 56 | if urlErr != nil { 57 | return nil, jsh.ISE(fmt.Sprintf("Error parsing URL: %s", urlErr.Error())) 58 | } 59 | 60 | setPath(u, resourceType) 61 | 62 | return NewRequest("GET", u.String(), nil) 63 | } 64 | -------------------------------------------------------------------------------- /_vendor/github.com/smartystreets/assertions/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | In general, the code posted to the [SmartyStreets github organization](https://github.com/smartystreets) is created to solve specific problems at SmartyStreets that are ancillary to our core products in the address verification industry and may or may not be useful to other organizations or developers. Our reason for posting said code isn't necessarily to solicit feedback or contributions from the community but more as a showcase of some of the approaches to solving problems we have adopted. 4 | 5 | Having stated that, we do consider issues raised by other githubbers as well as contributions submitted via pull requests. When submitting such a pull request, please follow these guidelines: 6 | 7 | - _Look before you leap:_ If the changes you plan to make are significant, it's in everyone's best interest for you to discuss them with a SmartyStreets team member prior to opening a pull request. 8 | - _License and ownership:_ If modifying the `LICENSE.md` file, limit your changes to fixing typographical mistakes. Do NOT modify the actual terms in the license or the copyright by **SmartyStreets, LLC**. Code submitted to SmartyStreets projects becomes property of SmartyStreets and must be compatible with the associated license. 9 | - _Testing:_ If the code you are submitting resides in packages/modules covered by automated tests, be sure to add passing tests that cover your changes and assert expected behavior and state. Submit the additional test cases as part of your change set. 10 | - _Style:_ Match your approach to **naming** and **formatting** with the surrounding code. Basically, the code you submit shouldn't stand out. 11 | - "Naming" refers to such constructs as variables, methods, functions, classes, structs, interfaces, packages, modules, directories, files, etc... 12 | - "Formatting" refers to such constructs as whitespace, horizontal line length, vertical function length, vertical file length, indentation, curly braces, etc... 13 | -------------------------------------------------------------------------------- /_vendor/github.com/smartystreets/goconvey/convey/reporting/story.go: -------------------------------------------------------------------------------- 1 | // TODO: in order for this reporter to be completely honest 2 | // we need to retrofit to be more like the json reporter such that: 3 | // 1. it maintains ScopeResult collections, which count assertions 4 | // 2. it reports only after EndStory(), so that all tick marks 5 | // are placed near the appropriate title. 6 | // 3. Under unit test 7 | 8 | package reporting 9 | 10 | import ( 11 | "fmt" 12 | "strings" 13 | ) 14 | 15 | type story struct { 16 | out *Printer 17 | titlesById map[string]string 18 | currentKey []string 19 | } 20 | 21 | func (self *story) BeginStory(story *StoryReport) {} 22 | 23 | func (self *story) Enter(scope *ScopeReport) { 24 | self.out.Indent() 25 | 26 | self.currentKey = append(self.currentKey, scope.Title) 27 | ID := strings.Join(self.currentKey, "|") 28 | 29 | if _, found := self.titlesById[ID]; !found { 30 | self.out.Println("") 31 | self.out.Print(scope.Title) 32 | self.out.Insert(" ") 33 | self.titlesById[ID] = scope.Title 34 | } 35 | } 36 | 37 | func (self *story) Report(report *AssertionResult) { 38 | if report.Error != nil { 39 | fmt.Print(redColor) 40 | self.out.Insert(error_) 41 | } else if report.Failure != "" { 42 | fmt.Print(yellowColor) 43 | self.out.Insert(failure) 44 | } else if report.Skipped { 45 | fmt.Print(yellowColor) 46 | self.out.Insert(skip) 47 | } else { 48 | fmt.Print(greenColor) 49 | self.out.Insert(success) 50 | } 51 | fmt.Print(resetColor) 52 | } 53 | 54 | func (self *story) Exit() { 55 | self.out.Dedent() 56 | self.currentKey = self.currentKey[:len(self.currentKey)-1] 57 | } 58 | 59 | func (self *story) EndStory() { 60 | self.titlesById = make(map[string]string) 61 | self.out.Println("\n") 62 | } 63 | 64 | func (self *story) Write(content []byte) (written int, err error) { 65 | return len(content), nil // no-op 66 | } 67 | 68 | func NewStoryReporter(out *Printer) *story { 69 | self := new(story) 70 | self.out = out 71 | self.titlesById = make(map[string]string) 72 | return self 73 | } 74 | -------------------------------------------------------------------------------- /_vendor/github.com/smartystreets/assertions/internal/oglematchers/contains.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Aaron Jacobs. All Rights Reserved. 2 | // Author: aaronjjacobs@gmail.com (Aaron Jacobs) 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package oglematchers 17 | 18 | import ( 19 | "fmt" 20 | "reflect" 21 | ) 22 | 23 | // Return a matcher that matches arrays slices with at least one element that 24 | // matches the supplied argument. If the argument x is not itself a Matcher, 25 | // this is equivalent to Contains(Equals(x)). 26 | func Contains(x interface{}) Matcher { 27 | var result containsMatcher 28 | var ok bool 29 | 30 | if result.elementMatcher, ok = x.(Matcher); !ok { 31 | result.elementMatcher = DeepEquals(x) 32 | } 33 | 34 | return &result 35 | } 36 | 37 | type containsMatcher struct { 38 | elementMatcher Matcher 39 | } 40 | 41 | func (m *containsMatcher) Description() string { 42 | return fmt.Sprintf("contains: %s", m.elementMatcher.Description()) 43 | } 44 | 45 | func (m *containsMatcher) Matches(candidate interface{}) error { 46 | // The candidate must be a slice or an array. 47 | v := reflect.ValueOf(candidate) 48 | if v.Kind() != reflect.Slice && v.Kind() != reflect.Array { 49 | return NewFatalError("which is not a slice or array") 50 | } 51 | 52 | // Check each element. 53 | for i := 0; i < v.Len(); i++ { 54 | elem := v.Index(i) 55 | if matchErr := m.elementMatcher.Matches(elem.Interface()); matchErr == nil { 56 | return nil 57 | } 58 | } 59 | 60 | return fmt.Errorf("") 61 | } 62 | -------------------------------------------------------------------------------- /goji2-logger/color_writer.go: -------------------------------------------------------------------------------- 1 | package gojilogger 2 | 3 | // copy of https://github.com/zenazn/goji/blob/master/web/middleware/terminal.go 4 | 5 | import ( 6 | "bytes" 7 | "fmt" 8 | "os" 9 | ) 10 | 11 | var ( 12 | // Normal colors 13 | nBlack = []byte{'\033', '[', '3', '0', 'm'} 14 | nRed = []byte{'\033', '[', '3', '1', 'm'} 15 | nGreen = []byte{'\033', '[', '3', '2', 'm'} 16 | nYellow = []byte{'\033', '[', '3', '3', 'm'} 17 | nBlue = []byte{'\033', '[', '3', '4', 'm'} 18 | nMagenta = []byte{'\033', '[', '3', '5', 'm'} 19 | nCyan = []byte{'\033', '[', '3', '6', 'm'} 20 | nWhite = []byte{'\033', '[', '3', '7', 'm'} 21 | // Bright colors 22 | bBlack = []byte{'\033', '[', '3', '0', ';', '1', 'm'} 23 | bRed = []byte{'\033', '[', '3', '1', ';', '1', 'm'} 24 | bGreen = []byte{'\033', '[', '3', '2', ';', '1', 'm'} 25 | bYellow = []byte{'\033', '[', '3', '3', ';', '1', 'm'} 26 | bBlue = []byte{'\033', '[', '3', '4', ';', '1', 'm'} 27 | bMagenta = []byte{'\033', '[', '3', '5', ';', '1', 'm'} 28 | bCyan = []byte{'\033', '[', '3', '6', ';', '1', 'm'} 29 | bWhite = []byte{'\033', '[', '3', '7', ';', '1', 'm'} 30 | 31 | reset = []byte{'\033', '[', '0', 'm'} 32 | ) 33 | 34 | var isTTY bool 35 | 36 | func init() { 37 | // This is sort of cheating: if stdout is a character device, we assume 38 | // that means it's a TTY. Unfortunately, there are many non-TTY 39 | // character devices, but fortunately stdout is rarely set to any of 40 | // them. 41 | // 42 | // We could solve this properly by pulling in a dependency on 43 | // code.google.com/p/go.crypto/ssh/terminal, for instance, but as a 44 | // heuristic for whether to print in color or in black-and-white, I'd 45 | // really rather not. 46 | fi, err := os.Stdout.Stat() 47 | if err == nil { 48 | m := os.ModeDevice | os.ModeCharDevice 49 | isTTY = fi.Mode()&m == m 50 | } 51 | } 52 | 53 | // colorWrite 54 | func colorWrite(buf *bytes.Buffer, color []byte, s string, args ...interface{}) { 55 | if isTTY { 56 | buf.Write(color) 57 | } 58 | fmt.Fprintf(buf, s, args...) 59 | if isTTY { 60 | buf.Write(reset) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /_vendor/github.com/smartystreets/goconvey/convey/reporting/problems.go: -------------------------------------------------------------------------------- 1 | package reporting 2 | 3 | import "fmt" 4 | 5 | type problem struct { 6 | silent bool 7 | out *Printer 8 | errors []*AssertionResult 9 | failures []*AssertionResult 10 | } 11 | 12 | func (self *problem) BeginStory(story *StoryReport) {} 13 | 14 | func (self *problem) Enter(scope *ScopeReport) {} 15 | 16 | func (self *problem) Report(report *AssertionResult) { 17 | if report.Error != nil { 18 | self.errors = append(self.errors, report) 19 | } else if report.Failure != "" { 20 | self.failures = append(self.failures, report) 21 | } 22 | } 23 | 24 | func (self *problem) Exit() {} 25 | 26 | func (self *problem) EndStory() { 27 | self.show(self.showErrors, redColor) 28 | self.show(self.showFailures, yellowColor) 29 | self.prepareForNextStory() 30 | } 31 | func (self *problem) show(display func(), color string) { 32 | if !self.silent { 33 | fmt.Print(color) 34 | } 35 | display() 36 | if !self.silent { 37 | fmt.Print(resetColor) 38 | } 39 | self.out.Dedent() 40 | } 41 | func (self *problem) showErrors() { 42 | for i, e := range self.errors { 43 | if i == 0 { 44 | self.out.Println("\nErrors:\n") 45 | self.out.Indent() 46 | } 47 | self.out.Println(errorTemplate, e.File, e.Line, e.Error, e.StackTrace) 48 | } 49 | } 50 | func (self *problem) showFailures() { 51 | for i, f := range self.failures { 52 | if i == 0 { 53 | self.out.Println("\nFailures:\n") 54 | self.out.Indent() 55 | } 56 | self.out.Println(failureTemplate, f.File, f.Line, f.Failure) 57 | } 58 | } 59 | 60 | func (self *problem) Write(content []byte) (written int, err error) { 61 | return len(content), nil // no-op 62 | } 63 | 64 | func NewProblemReporter(out *Printer) *problem { 65 | self := new(problem) 66 | self.out = out 67 | self.prepareForNextStory() 68 | return self 69 | } 70 | 71 | func NewSilentProblemReporter(out *Printer) *problem { 72 | self := NewProblemReporter(out) 73 | self.silent = true 74 | return self 75 | } 76 | 77 | func (self *problem) prepareForNextStory() { 78 | self.errors = []*AssertionResult{} 79 | self.failures = []*AssertionResult{} 80 | } 81 | -------------------------------------------------------------------------------- /_vendor/github.com/smartystreets/assertions/serializer.go: -------------------------------------------------------------------------------- 1 | package assertions 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | 7 | "github.com/smartystreets/assertions/internal/go-render/render" 8 | ) 9 | 10 | type Serializer interface { 11 | serialize(expected, actual interface{}, message string) string 12 | serializeDetailed(expected, actual interface{}, message string) string 13 | } 14 | 15 | type failureSerializer struct{} 16 | 17 | func (self *failureSerializer) serializeDetailed(expected, actual interface{}, message string) string { 18 | view := FailureView{ 19 | Message: message, 20 | Expected: render.Render(expected), 21 | Actual: render.Render(actual), 22 | } 23 | serialized, _ := json.Marshal(view) 24 | return string(serialized) 25 | } 26 | 27 | func (self *failureSerializer) serialize(expected, actual interface{}, message string) string { 28 | view := FailureView{ 29 | Message: message, 30 | Expected: fmt.Sprintf("%+v", expected), 31 | Actual: fmt.Sprintf("%+v", actual), 32 | } 33 | serialized, _ := json.Marshal(view) 34 | return string(serialized) 35 | } 36 | 37 | func newSerializer() *failureSerializer { 38 | return &failureSerializer{} 39 | } 40 | 41 | /////////////////////////////////////////////////////////////////////////////// 42 | 43 | // This struct is also declared in github.com/smartystreets/goconvey/convey/reporting. 44 | // The json struct tags should be equal in both declarations. 45 | type FailureView struct { 46 | Message string `json:"Message"` 47 | Expected string `json:"Expected"` 48 | Actual string `json:"Actual"` 49 | } 50 | 51 | /////////////////////////////////////////////////////// 52 | 53 | // noopSerializer just gives back the original message. This is useful when we are using 54 | // the assertions from a context other than the GoConvey Web UI, that requires the JSON 55 | // structure provided by the failureSerializer. 56 | type noopSerializer struct{} 57 | 58 | func (self *noopSerializer) serialize(expected, actual interface{}, message string) string { 59 | return message 60 | } 61 | func (self *noopSerializer) serializeDetailed(expected, actual interface{}, message string) string { 62 | return message 63 | } 64 | -------------------------------------------------------------------------------- /_vendor/github.com/asaskevich/govalidator/arrays.go: -------------------------------------------------------------------------------- 1 | package govalidator 2 | 3 | // Iterator is the function that accepts element of slice/array and its index 4 | type Iterator func(interface{}, int) 5 | 6 | // ResultIterator is the function that accepts element of slice/array and its index and returns any result 7 | type ResultIterator func(interface{}, int) interface{} 8 | 9 | // ConditionIterator is the function that accepts element of slice/array and its index and returns boolean 10 | type ConditionIterator func(interface{}, int) bool 11 | 12 | // Each iterates over the slice and apply Iterator to every item 13 | func Each(array []interface{}, iterator Iterator) { 14 | for index, data := range array { 15 | iterator(data, index) 16 | } 17 | } 18 | 19 | // Map iterates over the slice and apply ResultIterator to every item. Returns new slice as a result. 20 | func Map(array []interface{}, iterator ResultIterator) []interface{} { 21 | var result []interface{} = make([]interface{}, len(array)) 22 | for index, data := range array { 23 | result[index] = iterator(data, index) 24 | } 25 | return result 26 | } 27 | 28 | // Find iterates over the slice and apply ConditionIterator to every item. Returns first item that meet ConditionIterator or nil otherwise. 29 | func Find(array []interface{}, iterator ConditionIterator) interface{} { 30 | for index, data := range array { 31 | if iterator(data, index) { 32 | return data 33 | } 34 | } 35 | return nil 36 | } 37 | 38 | // Filter iterates over the slice and apply ConditionIterator to every item. Returns new slice. 39 | func Filter(array []interface{}, iterator ConditionIterator) []interface{} { 40 | var result []interface{} = make([]interface{}, 0) 41 | for index, data := range array { 42 | if iterator(data, index) { 43 | result = append(result, data) 44 | } 45 | } 46 | return result 47 | } 48 | 49 | // Count iterates over the slice and apply ConditionIterator to every item. Returns count of items that meets ConditionIterator. 50 | func Count(array []interface{}, iterator ConditionIterator) int { 51 | count := 0 52 | for index, data := range array { 53 | if iterator(data, index) { 54 | count = count + 1 55 | } 56 | } 57 | return count 58 | } 59 | -------------------------------------------------------------------------------- /_vendor/github.com/smartystreets/assertions/internal/oglematchers/README.md: -------------------------------------------------------------------------------- 1 | [![GoDoc](https://godoc.org/github.com/smartystreets/assertions/internal/oglematchers?status.svg)](https://godoc.org/github.com/smartystreets/assertions/internal/oglematchers) 2 | 3 | `oglematchers` is a package for the Go programming language containing a set of 4 | matchers, useful in a testing or mocking framework, inspired by and mostly 5 | compatible with [Google Test][googletest] for C++ and 6 | [Google JS Test][google-js-test]. The package is used by the 7 | [ogletest][ogletest] testing framework and [oglemock][oglemock] mocking 8 | framework, which may be more directly useful to you, but can be generically used 9 | elsewhere as well. 10 | 11 | A "matcher" is simply an object with a `Matches` method defining a set of golang 12 | values matched by the matcher, and a `Description` method describing that set. 13 | For example, here are some matchers: 14 | 15 | ```go 16 | // Numbers 17 | Equals(17.13) 18 | LessThan(19) 19 | 20 | // Strings 21 | Equals("taco") 22 | HasSubstr("burrito") 23 | MatchesRegex("t.*o") 24 | 25 | // Combining matchers 26 | AnyOf(LessThan(17), GreaterThan(19)) 27 | ``` 28 | 29 | There are lots more; see [here][reference] for a reference. You can also add 30 | your own simply by implementing the `oglematchers.Matcher` interface. 31 | 32 | 33 | Installation 34 | ------------ 35 | 36 | First, make sure you have installed Go 1.0.2 or newer. See 37 | [here][golang-install] for instructions. 38 | 39 | Use the following command to install `oglematchers` and keep it up to date: 40 | 41 | go get -u github.com/smartystreets/assertions/internal/oglematchers 42 | 43 | 44 | Documentation 45 | ------------- 46 | 47 | See [here][reference] for documentation. Alternatively, you can install the 48 | package and then use `godoc`: 49 | 50 | godoc github.com/smartystreets/assertions/internal/oglematchers 51 | 52 | 53 | [reference]: http://godoc.org/github.com/smartystreets/assertions/internal/oglematchers 54 | [golang-install]: http://golang.org/doc/install.html 55 | [googletest]: http://code.google.com/p/googletest/ 56 | [google-js-test]: http://code.google.com/p/google-js-test/ 57 | [ogletest]: http://github.com/smartystreets/assertions/internal/ogletest 58 | [oglemock]: http://github.com/smartystreets/assertions/internal/oglemock 59 | -------------------------------------------------------------------------------- /_vendor/github.com/smartystreets/goconvey/convey/init.go: -------------------------------------------------------------------------------- 1 | package convey 2 | 3 | import ( 4 | "flag" 5 | "os" 6 | 7 | "github.com/jtolds/gls" 8 | "github.com/smartystreets/assertions" 9 | "github.com/smartystreets/goconvey/convey/reporting" 10 | ) 11 | 12 | func init() { 13 | assertions.GoConveyMode(true) 14 | 15 | declareFlags() 16 | 17 | ctxMgr = gls.NewContextManager() 18 | } 19 | 20 | func declareFlags() { 21 | flag.BoolVar(&json, "convey-json", false, "When true, emits results in JSON blocks. Default: 'false'") 22 | flag.BoolVar(&silent, "convey-silent", false, "When true, all output from GoConvey is suppressed.") 23 | flag.BoolVar(&story, "convey-story", false, "When true, emits story output, otherwise emits dot output. When not provided, this flag mirros the value of the '-test.v' flag") 24 | 25 | if noStoryFlagProvided() { 26 | story = verboseEnabled 27 | } 28 | 29 | // FYI: flag.Parse() is called from the testing package. 30 | } 31 | 32 | func noStoryFlagProvided() bool { 33 | return !story && !storyDisabled 34 | } 35 | 36 | func buildReporter() reporting.Reporter { 37 | selectReporter := os.Getenv("GOCONVEY_REPORTER") 38 | 39 | switch { 40 | case testReporter != nil: 41 | return testReporter 42 | case json || selectReporter == "json": 43 | return reporting.BuildJsonReporter() 44 | case silent || selectReporter == "silent": 45 | return reporting.BuildSilentReporter() 46 | case selectReporter == "dot": 47 | // Story is turned on when verbose is set, so we need to check for dot reporter first. 48 | return reporting.BuildDotReporter() 49 | case story || selectReporter == "story": 50 | return reporting.BuildStoryReporter() 51 | default: 52 | return reporting.BuildDotReporter() 53 | } 54 | } 55 | 56 | var ( 57 | ctxMgr *gls.ContextManager 58 | 59 | // only set by internal tests 60 | testReporter reporting.Reporter 61 | ) 62 | 63 | var ( 64 | json bool 65 | silent bool 66 | story bool 67 | 68 | verboseEnabled = flagFound("-test.v=true") 69 | storyDisabled = flagFound("-story=false") 70 | ) 71 | 72 | // flagFound parses the command line args manually for flags defined in other 73 | // packages. Like the '-v' flag from the "testing" package, for instance. 74 | func flagFound(flagValue string) bool { 75 | for _, arg := range os.Args { 76 | if arg == flagValue { 77 | return true 78 | } 79 | } 80 | return false 81 | } 82 | -------------------------------------------------------------------------------- /link_test.go: -------------------------------------------------------------------------------- 1 | package jsh 2 | 3 | import ( 4 | "encoding/json" 5 | "testing" 6 | 7 | . "github.com/smartystreets/goconvey/convey" 8 | ) 9 | 10 | func TestLink(t *testing.T) { 11 | 12 | Convey("Link Tests", t, func() { 13 | 14 | Convey("->MarshalJSON()", func() { 15 | 16 | Convey("should marshal as empty string", func() { 17 | l := NewLink("") 18 | 19 | jData, err := json.Marshal(l) 20 | So(err, ShouldBeNil) 21 | So(string(jData), ShouldEqual, `""`) 22 | }) 23 | 24 | Convey("should marshal as string when no metadata is present", func() { 25 | l := NewLink("/stringlink") 26 | 27 | jData, err := json.Marshal(&l) 28 | So(err, ShouldBeNil) 29 | So(string(jData), ShouldEqual, `"/stringlink"`) 30 | }) 31 | 32 | Convey("should marshal as an object when empty metadata is present", func() { 33 | l := NewMetaLink("/metalink", make(map[string]interface{})) 34 | 35 | jData, err := json.Marshal(&l) 36 | So(err, ShouldBeNil) 37 | So(string(jData), ShouldEqual, `{"href":"/metalink"}`) 38 | }) 39 | 40 | Convey("should marshal as an object when metadata is present", func() { 41 | meta := make(map[string]interface{}) 42 | meta["count"] = 10 43 | l := NewMetaLink("/metalink", meta) 44 | 45 | jData, err := json.Marshal(&l) 46 | So(err, ShouldBeNil) 47 | So(string(jData), ShouldEqual, `{"href":"/metalink","meta":{"count":10}}`) 48 | }) 49 | }) 50 | 51 | Convey("->UnmarshalJSON()", func() { 52 | 53 | Convey("should handle a string link", func() { 54 | jStr := `"/stringlink"` 55 | 56 | l := Link{} 57 | err := l.UnmarshalJSON([]byte(jStr)) 58 | So(err, ShouldBeNil) 59 | So(l.HREF, ShouldEqual, "/stringlink") 60 | }) 61 | 62 | Convey("should handle a meta link", func() { 63 | jMeta := `{"href": "/metalink"}` 64 | 65 | l := Link{} 66 | err := l.UnmarshalJSON([]byte(jMeta)) 67 | So(err, ShouldBeNil) 68 | So(l.HREF, ShouldEqual, "/metalink") 69 | }) 70 | 71 | Convey("should handle a meta link with metadata", func() { 72 | jMeta := `{"href": "/metalink", "meta": {"count": 10}}` 73 | 74 | l := Link{} 75 | err := l.UnmarshalJSON([]byte(jMeta)) 76 | So(err, ShouldBeNil) 77 | So(l.HREF, ShouldEqual, "/metalink") 78 | So(l.Meta, ShouldNotBeEmpty) 79 | So(l.Meta["count"], ShouldEqual, 10) 80 | }) 81 | }) 82 | }) 83 | } 84 | -------------------------------------------------------------------------------- /jsh-api/mock_storage.go: -------------------------------------------------------------------------------- 1 | package jshapi 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "strconv" 7 | 8 | "github.com/derekdowling/go-json-spec-handler" 9 | ) 10 | 11 | // MockStorage allows you to mock out APIs really easily, and is also used internally 12 | // for testing the API layer. 13 | type MockStorage struct { 14 | // ResourceType is the name of the resource you are mocking i.e. "user", "comment" 15 | ResourceType string 16 | // ResourceAttributes a sample set of attributes a resource object should have 17 | // used by GET /resources and GET /resources/:id 18 | ResourceAttributes interface{} 19 | // ListCount is the number of sample objects to return in a GET /resources request 20 | ListCount int 21 | } 22 | 23 | // Save assigns a URL of 1 to the object 24 | func (m *MockStorage) Save(ctx context.Context, object *jsh.Object) (*jsh.Object, jsh.ErrorType) { 25 | var err *jsh.Error 26 | object.ID = "1" 27 | 28 | return object, err 29 | } 30 | 31 | // Get returns a resource with ID as specified by the request 32 | func (m *MockStorage) Get(ctx context.Context, id string) (*jsh.Object, jsh.ErrorType) { 33 | var err *jsh.Error 34 | 35 | return m.SampleObject(id), err 36 | } 37 | 38 | // List returns a sample list 39 | func (m *MockStorage) List(ctx context.Context) (jsh.List, jsh.ErrorType) { 40 | var err *jsh.Error 41 | 42 | return m.SampleList(m.ListCount), err 43 | } 44 | 45 | // Update does nothing 46 | func (m *MockStorage) Update(ctx context.Context, object *jsh.Object) (*jsh.Object, jsh.ErrorType) { 47 | var err jsh.ErrorList 48 | err = nil 49 | 50 | return object, err 51 | } 52 | 53 | // Delete does nothing 54 | func (m *MockStorage) Delete(ctx context.Context, id string) jsh.ErrorType { 55 | var err *jsh.Error 56 | 57 | return err 58 | } 59 | 60 | // SampleObject builds an object based on provided resource specifications 61 | func (m *MockStorage) SampleObject(id string) *jsh.Object { 62 | object, err := jsh.NewObject(id, m.ResourceType, m.ResourceAttributes) 63 | if err != nil { 64 | log.Fatal(err.Error()) 65 | } 66 | 67 | return object 68 | } 69 | 70 | // SampleList generates a sample list of resources that can be used for/against the 71 | // mock API 72 | func (m *MockStorage) SampleList(length int) jsh.List { 73 | 74 | list := jsh.List{} 75 | 76 | for id := 1; id <= length; id++ { 77 | list = append(list, m.SampleObject(strconv.Itoa(id))) 78 | } 79 | 80 | return list 81 | } 82 | -------------------------------------------------------------------------------- /_vendor/goji.io/mux.go: -------------------------------------------------------------------------------- 1 | package goji 2 | 3 | import ( 4 | "context" 5 | "net/http" 6 | 7 | "goji.io/internal" 8 | ) 9 | 10 | /* 11 | Mux is a HTTP multiplexer / router similar to net/http.ServeMux. 12 | 13 | Muxes multiplex traffic between many http.Handlers by selecting the first 14 | applicable Pattern. They then call a common middleware stack, finally passing 15 | control to the selected http.Handler. See the documentation on the Handle 16 | function for more information about how routing is performed, the documentation 17 | on the Pattern type for more information about request matching, and the 18 | documentation for the Use method for more about middleware. 19 | 20 | Muxes cannot be configured concurrently from multiple goroutines, nor can they 21 | be configured concurrently with requests. 22 | */ 23 | type Mux struct { 24 | handler http.Handler 25 | middleware []func(http.Handler) http.Handler 26 | router router 27 | root bool 28 | } 29 | 30 | /* 31 | NewMux returns a new Mux with no configured middleware or routes. 32 | */ 33 | func NewMux() *Mux { 34 | m := SubMux() 35 | m.root = true 36 | return m 37 | } 38 | 39 | /* 40 | SubMux returns a new Mux with no configured middleware or routes, and which 41 | inherits routing information from the passed context. This is especially useful 42 | when using one Mux as a http.Handler registered to another "parent" Mux. 43 | 44 | For example, a common pattern is to organize applications in a way that mirrors 45 | the structure of its URLs: a photo-sharing site might have URLs that start with 46 | "/users/" and URLs that start with "/albums/", and might be organized using 47 | three Muxes: 48 | 49 | root := NewMux() 50 | users := SubMux() 51 | root.Handle(pat.New("/users/*"), users) 52 | albums := SubMux() 53 | root.Handle(pat.New("/albums/*"), albums) 54 | 55 | // e.g., GET /users/carl 56 | users.Handle(pat.Get("/:name"), renderProfile) 57 | // e.g., POST /albums/ 58 | albums.Handle(pat.Post("/"), newAlbum) 59 | */ 60 | func SubMux() *Mux { 61 | m := &Mux{} 62 | m.buildChain() 63 | return m 64 | } 65 | 66 | // ServeHTTP implements net/http.Handler. 67 | func (m *Mux) ServeHTTP(w http.ResponseWriter, r *http.Request) { 68 | if m.root { 69 | ctx := r.Context() 70 | ctx = context.WithValue(ctx, internal.Path, r.URL.EscapedPath()) 71 | r = r.WithContext(ctx) 72 | } 73 | r = m.router.route(r) 74 | m.handler.ServeHTTP(w, r) 75 | } 76 | 77 | var _ http.Handler = &Mux{} 78 | -------------------------------------------------------------------------------- /_vendor/goji.io/pattern/pattern.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package pattern contains utilities for Goji Pattern authors. 3 | 4 | Goji users should not import this package. Instead, use the utilities provided 5 | by your Pattern package. If you are looking for an implementation of Pattern, 6 | try Goji's pat subpackage, which contains a simple domain specific language for 7 | specifying routes. 8 | 9 | For Pattern authors, use of this subpackage is entirely optional. Nevertheless, 10 | authors who wish to take advantage of Goji's PathPrefix optimization or who wish 11 | to standardize on a few common interfaces may find this package useful. 12 | */ 13 | package pattern 14 | 15 | import ( 16 | "context" 17 | 18 | "goji.io/internal" 19 | ) 20 | 21 | /* 22 | Variable is a standard type for the names of Pattern-bound variables, e.g. 23 | variables extracted from the URL. Pass the name of a variable, cast to this 24 | type, to context.Context.Value to retrieve the value bound to that name. 25 | */ 26 | type Variable string 27 | 28 | type allVariables struct{} 29 | 30 | /* 31 | AllVariables is a standard value which, when passed to context.Context.Value, 32 | returns all variable bindings present in the context, with bindings in newer 33 | contexts overriding values deeper in the stack. The concrete type 34 | 35 | map[Variable]interface{} 36 | 37 | is used for this purpose. If no variables are bound, nil should be returned 38 | instead of an empty map. 39 | */ 40 | var AllVariables = allVariables{} 41 | 42 | /* 43 | Path returns the path that the Goji router uses to perform the PathPrefix 44 | optimization. While this function does not distinguish between the absence of a 45 | path and an empty path, Goji will automatically extract a path from the request 46 | if none is present. 47 | 48 | By convention, paths are stored in their escaped form (i.e., the value returned 49 | by net/url.URL.EscapedPath, and not URL.Path) to ensure that Patterns have as 50 | much discretion as possible (e.g., to behave differently for '/' and '%2f'). 51 | */ 52 | func Path(ctx context.Context) string { 53 | pi := ctx.Value(internal.Path) 54 | if pi == nil { 55 | return "" 56 | } 57 | return pi.(string) 58 | } 59 | 60 | /* 61 | SetPath returns a new context in which the given path is used by the Goji Router 62 | when performing the PathPrefix optimization. See Path for more information about 63 | the intended semantics of this path. 64 | */ 65 | func SetPath(ctx context.Context, path string) context.Context { 66 | return context.WithValue(ctx, internal.Path, path) 67 | } 68 | -------------------------------------------------------------------------------- /Godeps/Godeps.json: -------------------------------------------------------------------------------- 1 | { 2 | "ImportPath": "github.com/derekdowling/go-json-spec-handler", 3 | "GoVersion": "go1.8", 4 | "GodepVersion": "v79", 5 | "Packages": [ 6 | "./..." 7 | ], 8 | "Deps": [ 9 | { 10 | "ImportPath": "github.com/asaskevich/govalidator", 11 | "Comment": "v2-37-gedd46cd", 12 | "Rev": "edd46cdac249b001c7b7d88c6d43993ea875e8d8" 13 | }, 14 | { 15 | "ImportPath": "github.com/derekdowling/go-stdlogger", 16 | "Rev": "cfa80b78b82f8d241ceece0b733b1161bb6a885c" 17 | }, 18 | { 19 | "ImportPath": "github.com/gopherjs/gopherjs/js", 20 | "Rev": "dc374d32704510cb387457180ca9d5193978b555" 21 | }, 22 | { 23 | "ImportPath": "github.com/jtolds/gls", 24 | "Rev": "9a4a02dbe491bef4bab3c24fd9f3087d6c4c6690" 25 | }, 26 | { 27 | "ImportPath": "github.com/smartystreets/assertions", 28 | "Comment": "1.6.0-27-g4ea54c1", 29 | "Rev": "4ea54c1f28ad3ae597e76607dea3871fa177e263" 30 | }, 31 | { 32 | "ImportPath": "github.com/smartystreets/assertions/internal/go-render/render", 33 | "Comment": "1.6.0-27-g4ea54c1", 34 | "Rev": "4ea54c1f28ad3ae597e76607dea3871fa177e263" 35 | }, 36 | { 37 | "ImportPath": "github.com/smartystreets/assertions/internal/oglematchers", 38 | "Comment": "1.6.0-27-g4ea54c1", 39 | "Rev": "4ea54c1f28ad3ae597e76607dea3871fa177e263" 40 | }, 41 | { 42 | "ImportPath": "github.com/smartystreets/goconvey/convey", 43 | "Comment": "1.6.2-21-g9e8dc3f", 44 | "Rev": "9e8dc3f972df6c8fcc0375ef492c24d0bb204857" 45 | }, 46 | { 47 | "ImportPath": "github.com/smartystreets/goconvey/convey/gotest", 48 | "Comment": "1.6.2-21-g9e8dc3f", 49 | "Rev": "9e8dc3f972df6c8fcc0375ef492c24d0bb204857" 50 | }, 51 | { 52 | "ImportPath": "github.com/smartystreets/goconvey/convey/reporting", 53 | "Comment": "1.6.2-21-g9e8dc3f", 54 | "Rev": "9e8dc3f972df6c8fcc0375ef492c24d0bb204857" 55 | }, 56 | { 57 | "ImportPath": "github.com/zenazn/goji/web/mutil", 58 | "Comment": "v0.9.0-34-gbf843a1", 59 | "Rev": "bf843a174a08e846246b8945f8a9a853d84a256a" 60 | }, 61 | { 62 | "ImportPath": "goji.io", 63 | "Comment": "v2.0", 64 | "Rev": "0d89ff54b2c18c9c4ba530e32496aef902d3c6cd" 65 | }, 66 | { 67 | "ImportPath": "goji.io/internal", 68 | "Comment": "v2.0", 69 | "Rev": "0d89ff54b2c18c9c4ba530e32496aef902d3c6cd" 70 | }, 71 | { 72 | "ImportPath": "goji.io/pat", 73 | "Comment": "v2.0", 74 | "Rev": "0d89ff54b2c18c9c4ba530e32496aef902d3c6cd" 75 | }, 76 | { 77 | "ImportPath": "goji.io/pattern", 78 | "Comment": "v2.0", 79 | "Rev": "0d89ff54b2c18c9c4ba530e32496aef902d3c6cd" 80 | } 81 | ] 82 | } 83 | -------------------------------------------------------------------------------- /error_test.go: -------------------------------------------------------------------------------- 1 | package jsh 2 | 3 | import ( 4 | "net/http" 5 | "net/http/httptest" 6 | "strconv" 7 | "testing" 8 | 9 | . "github.com/smartystreets/goconvey/convey" 10 | ) 11 | 12 | func TestError(t *testing.T) { 13 | 14 | Convey("Error Tests", t, func() { 15 | 16 | request := &http.Request{} 17 | writer := httptest.NewRecorder() 18 | 19 | testErrorObject := &Error{ 20 | Status: http.StatusBadRequest, 21 | Title: "Fail", 22 | Detail: "So badly", 23 | } 24 | 25 | Convey("->Validate()", func() { 26 | 27 | Convey("should not fail for a valid Error", func() { 28 | err := testErrorObject.Validate(request, true) 29 | So(err, ShouldBeNil) 30 | }) 31 | 32 | Convey("422 Status Formatting", func() { 33 | 34 | testErrorObject.Status = 422 35 | 36 | Convey("should accept a properly formatted 422 error", func() { 37 | testErrorObject.Source.Pointer = "/data/attributes/test" 38 | err := testErrorObject.Validate(request, true) 39 | So(err, ShouldBeNil) 40 | }) 41 | 42 | Convey("should error if Source.Pointer isn't set", func() { 43 | err := testErrorObject.Validate(request, true) 44 | So(err, ShouldNotBeNil) 45 | }) 46 | }) 47 | 48 | Convey("should fail for an out of range HTTP error status", func() { 49 | testErrorObject.Status = http.StatusOK 50 | err := testErrorObject.Validate(request, true) 51 | So(err, ShouldNotBeNil) 52 | }) 53 | }) 54 | 55 | Convey("->Send()", func() { 56 | 57 | testError := &Error{ 58 | Status: http.StatusForbidden, 59 | Title: "Forbidden", 60 | Detail: "Can't Go Here", 61 | } 62 | 63 | Convey("should send a properly formatted JSON error", func() { 64 | err := Send(writer, request, testError) 65 | So(err, ShouldBeNil) 66 | So(writer.Code, ShouldEqual, http.StatusForbidden) 67 | 68 | contentLength, convErr := strconv.Atoi(writer.HeaderMap.Get("Content-Length")) 69 | So(convErr, ShouldBeNil) 70 | So(contentLength, ShouldBeGreaterThan, 0) 71 | So(writer.HeaderMap.Get("Content-Type"), ShouldEqual, ContentType) 72 | }) 73 | 74 | Convey("should work for an ErrorList", func() { 75 | 76 | errorList := ErrorList{testError} 77 | 78 | err := Send(writer, request, errorList) 79 | So(err, ShouldBeNil) 80 | So(writer.Code, ShouldEqual, http.StatusForbidden) 81 | 82 | contentLength, convErr := strconv.Atoi(writer.HeaderMap.Get("Content-Length")) 83 | So(convErr, ShouldBeNil) 84 | So(contentLength, ShouldBeGreaterThan, 0) 85 | So(writer.HeaderMap.Get("Content-Type"), ShouldEqual, ContentType) 86 | }) 87 | }) 88 | }) 89 | } 90 | -------------------------------------------------------------------------------- /_vendor/github.com/smartystreets/goconvey/convey/reporting/json.go: -------------------------------------------------------------------------------- 1 | // TODO: under unit test 2 | 3 | package reporting 4 | 5 | import ( 6 | "bytes" 7 | "encoding/json" 8 | "fmt" 9 | "strings" 10 | ) 11 | 12 | type JsonReporter struct { 13 | out *Printer 14 | currentKey []string 15 | current *ScopeResult 16 | index map[string]*ScopeResult 17 | scopes []*ScopeResult 18 | } 19 | 20 | func (self *JsonReporter) depth() int { return len(self.currentKey) } 21 | 22 | func (self *JsonReporter) BeginStory(story *StoryReport) {} 23 | 24 | func (self *JsonReporter) Enter(scope *ScopeReport) { 25 | self.currentKey = append(self.currentKey, scope.Title) 26 | ID := strings.Join(self.currentKey, "|") 27 | if _, found := self.index[ID]; !found { 28 | next := newScopeResult(scope.Title, self.depth(), scope.File, scope.Line) 29 | self.scopes = append(self.scopes, next) 30 | self.index[ID] = next 31 | } 32 | self.current = self.index[ID] 33 | } 34 | 35 | func (self *JsonReporter) Report(report *AssertionResult) { 36 | self.current.Assertions = append(self.current.Assertions, report) 37 | } 38 | 39 | func (self *JsonReporter) Exit() { 40 | self.currentKey = self.currentKey[:len(self.currentKey)-1] 41 | } 42 | 43 | func (self *JsonReporter) EndStory() { 44 | self.report() 45 | self.reset() 46 | } 47 | func (self *JsonReporter) report() { 48 | scopes := []string{} 49 | for _, scope := range self.scopes { 50 | serialized, err := json.Marshal(scope) 51 | if err != nil { 52 | self.out.Println(jsonMarshalFailure) 53 | panic(err) 54 | } 55 | var buffer bytes.Buffer 56 | json.Indent(&buffer, serialized, "", " ") 57 | scopes = append(scopes, buffer.String()) 58 | } 59 | self.out.Print(fmt.Sprintf("%s\n%s,\n%s\n", OpenJson, strings.Join(scopes, ","), CloseJson)) 60 | } 61 | func (self *JsonReporter) reset() { 62 | self.scopes = []*ScopeResult{} 63 | self.index = map[string]*ScopeResult{} 64 | self.currentKey = nil 65 | } 66 | 67 | func (self *JsonReporter) Write(content []byte) (written int, err error) { 68 | self.current.Output += string(content) 69 | return len(content), nil 70 | } 71 | 72 | func NewJsonReporter(out *Printer) *JsonReporter { 73 | self := new(JsonReporter) 74 | self.out = out 75 | self.reset() 76 | return self 77 | } 78 | 79 | const OpenJson = ">->->OPEN-JSON->->->" // "⌦" 80 | const CloseJson = "<-<-<-CLOSE-JSON<-<-<" // "⌫" 81 | const jsonMarshalFailure = ` 82 | 83 | GOCONVEY_JSON_MARSHALL_FAILURE: There was an error when attempting to convert test results to JSON. 84 | Please file a bug report and reference the code that caused this failure if possible. 85 | 86 | Here's the panic: 87 | 88 | ` 89 | -------------------------------------------------------------------------------- /_vendor/github.com/jtolds/gls/stack_tags.go: -------------------------------------------------------------------------------- 1 | package gls 2 | 3 | // so, basically, we're going to encode integer tags in base-16 on the stack 4 | 5 | import ( 6 | "reflect" 7 | "runtime" 8 | ) 9 | 10 | const ( 11 | bitWidth = 4 12 | ) 13 | 14 | func addStackTag(tag uint, context_call func()) { 15 | if context_call == nil { 16 | return 17 | } 18 | markS(tag, context_call) 19 | } 20 | 21 | func markS(tag uint, cb func()) { _m(tag, cb) } 22 | func mark0(tag uint, cb func()) { _m(tag, cb) } 23 | func mark1(tag uint, cb func()) { _m(tag, cb) } 24 | func mark2(tag uint, cb func()) { _m(tag, cb) } 25 | func mark3(tag uint, cb func()) { _m(tag, cb) } 26 | func mark4(tag uint, cb func()) { _m(tag, cb) } 27 | func mark5(tag uint, cb func()) { _m(tag, cb) } 28 | func mark6(tag uint, cb func()) { _m(tag, cb) } 29 | func mark7(tag uint, cb func()) { _m(tag, cb) } 30 | func mark8(tag uint, cb func()) { _m(tag, cb) } 31 | func mark9(tag uint, cb func()) { _m(tag, cb) } 32 | func markA(tag uint, cb func()) { _m(tag, cb) } 33 | func markB(tag uint, cb func()) { _m(tag, cb) } 34 | func markC(tag uint, cb func()) { _m(tag, cb) } 35 | func markD(tag uint, cb func()) { _m(tag, cb) } 36 | func markE(tag uint, cb func()) { _m(tag, cb) } 37 | func markF(tag uint, cb func()) { _m(tag, cb) } 38 | 39 | var pc_lookup = make(map[uintptr]int8, 17) 40 | var mark_lookup [16]func(uint, func()) 41 | 42 | func init() { 43 | setEntries := func(f func(uint, func()), v int8) { 44 | pc_lookup[reflect.ValueOf(f).Pointer()] = v 45 | if v >= 0 { 46 | mark_lookup[v] = f 47 | } 48 | } 49 | setEntries(markS, -0x1) 50 | setEntries(mark0, 0x0) 51 | setEntries(mark1, 0x1) 52 | setEntries(mark2, 0x2) 53 | setEntries(mark3, 0x3) 54 | setEntries(mark4, 0x4) 55 | setEntries(mark5, 0x5) 56 | setEntries(mark6, 0x6) 57 | setEntries(mark7, 0x7) 58 | setEntries(mark8, 0x8) 59 | setEntries(mark9, 0x9) 60 | setEntries(markA, 0xa) 61 | setEntries(markB, 0xb) 62 | setEntries(markC, 0xc) 63 | setEntries(markD, 0xd) 64 | setEntries(markE, 0xe) 65 | setEntries(markF, 0xf) 66 | } 67 | 68 | func _m(tag_remainder uint, cb func()) { 69 | if tag_remainder == 0 { 70 | cb() 71 | } else { 72 | mark_lookup[tag_remainder&0xf](tag_remainder>>bitWidth, cb) 73 | } 74 | } 75 | 76 | func readStackTags(stack []uintptr) (tags []uint) { 77 | var current_tag uint 78 | for _, pc := range stack { 79 | pc = runtime.FuncForPC(pc).Entry() 80 | val, ok := pc_lookup[pc] 81 | if !ok { 82 | continue 83 | } 84 | if val < 0 { 85 | tags = append(tags, current_tag) 86 | current_tag = 0 87 | continue 88 | } 89 | current_tag <<= bitWidth 90 | current_tag += uint(val) 91 | } 92 | return 93 | } 94 | -------------------------------------------------------------------------------- /_vendor/github.com/smartystreets/goconvey/convey/reporting/init.go: -------------------------------------------------------------------------------- 1 | package reporting 2 | 3 | import ( 4 | "os" 5 | "runtime" 6 | "strings" 7 | ) 8 | 9 | func init() { 10 | if !isColorableTerminal() { 11 | monochrome() 12 | } 13 | 14 | if runtime.GOOS == "windows" { 15 | success, failure, error_ = dotSuccess, dotFailure, dotError 16 | } 17 | } 18 | 19 | func BuildJsonReporter() Reporter { 20 | out := NewPrinter(NewConsole()) 21 | return NewReporters( 22 | NewGoTestReporter(), 23 | NewJsonReporter(out)) 24 | } 25 | func BuildDotReporter() Reporter { 26 | out := NewPrinter(NewConsole()) 27 | return NewReporters( 28 | NewGoTestReporter(), 29 | NewDotReporter(out), 30 | NewProblemReporter(out), 31 | consoleStatistics) 32 | } 33 | func BuildStoryReporter() Reporter { 34 | out := NewPrinter(NewConsole()) 35 | return NewReporters( 36 | NewGoTestReporter(), 37 | NewStoryReporter(out), 38 | NewProblemReporter(out), 39 | consoleStatistics) 40 | } 41 | func BuildSilentReporter() Reporter { 42 | out := NewPrinter(NewConsole()) 43 | return NewReporters( 44 | NewGoTestReporter(), 45 | NewSilentProblemReporter(out)) 46 | } 47 | 48 | var ( 49 | newline = "\n" 50 | success = "✔" 51 | failure = "✘" 52 | error_ = "🔥" 53 | skip = "⚠" 54 | dotSuccess = "." 55 | dotFailure = "x" 56 | dotError = "E" 57 | dotSkip = "S" 58 | errorTemplate = "* %s \nLine %d: - %v \n%s\n" 59 | failureTemplate = "* %s \nLine %d:\n%s\n" 60 | ) 61 | 62 | var ( 63 | greenColor = "\033[32m" 64 | yellowColor = "\033[33m" 65 | redColor = "\033[31m" 66 | resetColor = "\033[0m" 67 | ) 68 | 69 | var consoleStatistics = NewStatisticsReporter(NewPrinter(NewConsole())) 70 | 71 | func SuppressConsoleStatistics() { consoleStatistics.Suppress() } 72 | func PrintConsoleStatistics() { consoleStatistics.PrintSummary() } 73 | 74 | // QuiteMode disables all console output symbols. This is only meant to be used 75 | // for tests that are internal to goconvey where the output is distracting or 76 | // otherwise not needed in the test output. 77 | func QuietMode() { 78 | success, failure, error_, skip, dotSuccess, dotFailure, dotError, dotSkip = "", "", "", "", "", "", "", "" 79 | } 80 | 81 | func monochrome() { 82 | greenColor, yellowColor, redColor, resetColor = "", "", "", "" 83 | } 84 | 85 | func isColorableTerminal() bool { 86 | return strings.Contains(os.Getenv("TERM"), "color") 87 | } 88 | 89 | // This interface allows us to pass the *testing.T struct 90 | // throughout the internals of this tool without ever 91 | // having to import the "testing" package. 92 | type T interface { 93 | Fail() 94 | } 95 | -------------------------------------------------------------------------------- /_vendor/github.com/smartystreets/goconvey/convey/reporting/statistics.go: -------------------------------------------------------------------------------- 1 | package reporting 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | func (self *statistics) BeginStory(story *StoryReport) {} 9 | 10 | func (self *statistics) Enter(scope *ScopeReport) {} 11 | 12 | func (self *statistics) Report(report *AssertionResult) { 13 | self.Lock() 14 | defer self.Unlock() 15 | 16 | if !self.failing && report.Failure != "" { 17 | self.failing = true 18 | } 19 | if !self.erroring && report.Error != nil { 20 | self.erroring = true 21 | } 22 | if report.Skipped { 23 | self.skipped += 1 24 | } else { 25 | self.total++ 26 | } 27 | } 28 | 29 | func (self *statistics) Exit() {} 30 | 31 | func (self *statistics) EndStory() { 32 | self.Lock() 33 | defer self.Unlock() 34 | 35 | if !self.suppressed { 36 | self.printSummaryLocked() 37 | } 38 | } 39 | 40 | func (self *statistics) Suppress() { 41 | self.Lock() 42 | defer self.Unlock() 43 | self.suppressed = true 44 | } 45 | 46 | func (self *statistics) PrintSummary() { 47 | self.Lock() 48 | defer self.Unlock() 49 | self.printSummaryLocked() 50 | } 51 | 52 | func (self *statistics) printSummaryLocked() { 53 | self.reportAssertionsLocked() 54 | self.reportSkippedSectionsLocked() 55 | self.completeReportLocked() 56 | } 57 | func (self *statistics) reportAssertionsLocked() { 58 | self.decideColorLocked() 59 | self.out.Print("\n%d total %s", self.total, plural("assertion", self.total)) 60 | } 61 | func (self *statistics) decideColorLocked() { 62 | if self.failing && !self.erroring { 63 | fmt.Print(yellowColor) 64 | } else if self.erroring { 65 | fmt.Print(redColor) 66 | } else { 67 | fmt.Print(greenColor) 68 | } 69 | } 70 | func (self *statistics) reportSkippedSectionsLocked() { 71 | if self.skipped > 0 { 72 | fmt.Print(yellowColor) 73 | self.out.Print(" (one or more sections skipped)") 74 | } 75 | } 76 | func (self *statistics) completeReportLocked() { 77 | fmt.Print(resetColor) 78 | self.out.Print("\n") 79 | self.out.Print("\n") 80 | } 81 | 82 | func (self *statistics) Write(content []byte) (written int, err error) { 83 | return len(content), nil // no-op 84 | } 85 | 86 | func NewStatisticsReporter(out *Printer) *statistics { 87 | self := statistics{} 88 | self.out = out 89 | return &self 90 | } 91 | 92 | type statistics struct { 93 | sync.Mutex 94 | 95 | out *Printer 96 | total int 97 | failing bool 98 | erroring bool 99 | skipped int 100 | suppressed bool 101 | } 102 | 103 | func plural(word string, count int) string { 104 | if count == 1 { 105 | return word 106 | } 107 | return word + "s" 108 | } 109 | -------------------------------------------------------------------------------- /_vendor/goji.io/middleware.go: -------------------------------------------------------------------------------- 1 | package goji 2 | 3 | import "net/http" 4 | 5 | /* 6 | Use appends a middleware to the Mux's middleware stack. 7 | 8 | Middleware are composable pieces of functionality that augment http.Handlers. 9 | Common examples of middleware include request loggers, authentication checkers, 10 | and metrics gatherers. 11 | 12 | Middleware are evaluated in the reverse order in which they were added, but the 13 | resulting http.Handlers execute in "normal" order (i.e., the http.Handler 14 | returned by the first Middleware to be added gets called first). 15 | 16 | For instance, given middleware A, B, and C, added in that order, Goji will 17 | behave similarly to this snippet: 18 | 19 | augmentedHandler := A(B(C(yourHandler))) 20 | augmentedHandler.ServeHTTP(w, r) 21 | 22 | Assuming each of A, B, and C look something like this: 23 | 24 | func A(inner http.Handler) http.Handler { 25 | log.Print("A: called") 26 | mw := func(w http.ResponseWriter, r *http.Request) { 27 | log.Print("A: before") 28 | inner.ServeHTTP(w, r) 29 | log.Print("A: after") 30 | } 31 | return http.HandlerFunc(mw) 32 | } 33 | 34 | we'd expect to see the following in the log: 35 | 36 | C: called 37 | B: called 38 | A: called 39 | --- 40 | A: before 41 | B: before 42 | C: before 43 | yourHandler: called 44 | C: after 45 | B: after 46 | A: after 47 | 48 | Note that augmentedHandler will called many times, producing the log output 49 | below the divider, while the outer middleware functions (the log output above 50 | the divider) will only be called a handful of times at application boot. 51 | 52 | Middleware in Goji is called after routing has been performed. Therefore it is 53 | possible to examine any routing information placed into the Request context by 54 | Patterns, or to view or modify the http.Handler that will be routed to. 55 | Middleware authors should read the documentation for the "middleware" subpackage 56 | for more information about how this is done. 57 | 58 | The http.Handler returned by the given middleware must be safe for concurrent 59 | use by multiple goroutines. It is not safe to concurrently register middleware 60 | from multiple goroutines, or to register middleware concurrently with requests. 61 | */ 62 | func (m *Mux) Use(middleware func(http.Handler) http.Handler) { 63 | m.middleware = append(m.middleware, middleware) 64 | m.buildChain() 65 | } 66 | 67 | // Pre-compile a http.Handler for us to use during dispatch. Yes, this means 68 | // that adding middleware is quadratic, but it (a) happens during configuration 69 | // time, not at "runtime", and (b) n should ~always be small. 70 | func (m *Mux) buildChain() { 71 | m.handler = dispatch{} 72 | for i := len(m.middleware) - 1; i >= 0; i-- { 73 | m.handler = m.middleware[i](m.handler) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /_vendor/github.com/smartystreets/assertions/internal/oglematchers/deep_equals.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Aaron Jacobs. All Rights Reserved. 2 | // Author: aaronjjacobs@gmail.com (Aaron Jacobs) 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package oglematchers 17 | 18 | import ( 19 | "bytes" 20 | "errors" 21 | "fmt" 22 | "reflect" 23 | ) 24 | 25 | var byteSliceType reflect.Type = reflect.TypeOf([]byte{}) 26 | 27 | // DeepEquals returns a matcher that matches based on 'deep equality', as 28 | // defined by the reflect package. This matcher requires that values have 29 | // identical types to x. 30 | func DeepEquals(x interface{}) Matcher { 31 | return &deepEqualsMatcher{x} 32 | } 33 | 34 | type deepEqualsMatcher struct { 35 | x interface{} 36 | } 37 | 38 | func (m *deepEqualsMatcher) Description() string { 39 | xDesc := fmt.Sprintf("%v", m.x) 40 | xValue := reflect.ValueOf(m.x) 41 | 42 | // Special case: fmt.Sprintf presents nil slices as "[]", but 43 | // reflect.DeepEqual makes a distinction between nil and empty slices. Make 44 | // this less confusing. 45 | if xValue.Kind() == reflect.Slice && xValue.IsNil() { 46 | xDesc = "" 47 | } 48 | 49 | return fmt.Sprintf("deep equals: %s", xDesc) 50 | } 51 | 52 | func (m *deepEqualsMatcher) Matches(c interface{}) error { 53 | // Make sure the types match. 54 | ct := reflect.TypeOf(c) 55 | xt := reflect.TypeOf(m.x) 56 | 57 | if ct != xt { 58 | return NewFatalError(fmt.Sprintf("which is of type %v", ct)) 59 | } 60 | 61 | // Special case: handle byte slices more efficiently. 62 | cValue := reflect.ValueOf(c) 63 | xValue := reflect.ValueOf(m.x) 64 | 65 | if ct == byteSliceType && !cValue.IsNil() && !xValue.IsNil() { 66 | xBytes := m.x.([]byte) 67 | cBytes := c.([]byte) 68 | 69 | if bytes.Equal(cBytes, xBytes) { 70 | return nil 71 | } 72 | 73 | return errors.New("") 74 | } 75 | 76 | // Defer to the reflect package. 77 | if reflect.DeepEqual(m.x, c) { 78 | return nil 79 | } 80 | 81 | // Special case: if the comparison failed because c is the nil slice, given 82 | // an indication of this (since its value is printed as "[]"). 83 | if cValue.Kind() == reflect.Slice && cValue.IsNil() { 84 | return errors.New("which is nil") 85 | } 86 | 87 | return errors.New("") 88 | } 89 | -------------------------------------------------------------------------------- /response.go: -------------------------------------------------------------------------------- 1 | package jsh 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/http" 7 | "strconv" 8 | ) 9 | 10 | // JSONAPIVersion is version of JSON API Spec that is currently compatible: 11 | // http://jsonapi.org/format/1.1/ 12 | const JSONAPIVersion = "1.1" 13 | 14 | // Sendable implements functions that allows different response types 15 | // to produce a sendable JSON Response format 16 | type Sendable interface { 17 | Validate(r *http.Request, response bool) *Error 18 | } 19 | 20 | // Send will return a JSON payload to the requestor. If the payload response validation 21 | // fails, it will send an appropriate error to the requestor and will return the error 22 | func Send(w http.ResponseWriter, r *http.Request, payload Sendable) *Error { 23 | 24 | validationErr := payload.Validate(r, true) 25 | if validationErr != nil { 26 | 27 | // use the prepared error as the new response, unless something went horribly 28 | // wrong 29 | err := validationErr.Validate(r, true) 30 | if err != nil { 31 | http.Error(w, DefaultErrorTitle, http.StatusInternalServerError) 32 | return err 33 | } 34 | 35 | payload = validationErr 36 | } 37 | 38 | return SendDocument(w, r, Build(payload)) 39 | } 40 | 41 | /* 42 | SendDocument handles sending a fully prepared JSON Document. This is useful if you 43 | require custom validation or additional build steps before sending. 44 | 45 | SendJSON is designed to always send a response, but will also return the last 46 | error it encountered to help with debugging in the event of an Internal Server 47 | Error. 48 | */ 49 | func SendDocument(w http.ResponseWriter, r *http.Request, document *Document) *Error { 50 | 51 | validationErr := document.Validate(r, true) 52 | if validationErr != nil { 53 | prepErr := validationErr.Validate(r, true) 54 | 55 | // If we ever hit this, something seriously wrong has happened 56 | if prepErr != nil { 57 | http.Error(w, DefaultErrorTitle, http.StatusInternalServerError) 58 | return prepErr 59 | } 60 | 61 | // if we didn't error out, make this the new response 62 | document = Build(validationErr) 63 | } 64 | 65 | content, jsonErr := json.MarshalIndent(document, "", " ") 66 | if jsonErr != nil { 67 | http.Error(w, DefaultErrorTitle, http.StatusInternalServerError) 68 | return ISE(fmt.Sprintf("Unable to marshal JSON payload: %s", jsonErr.Error())) 69 | } 70 | 71 | w.Header().Add("Content-Type", ContentType) 72 | w.Header().Set("Content-Length", strconv.Itoa(len(content))) 73 | w.WriteHeader(document.Status) 74 | w.Write(content) 75 | 76 | return validationErr 77 | } 78 | 79 | // Ok makes it simple to return a 200 OK response via jsh: 80 | // 81 | // jsh.SendDocument(w, r, jsh.Ok()) 82 | func Ok() *Document { 83 | doc := New() 84 | doc.Status = http.StatusOK 85 | doc.empty = true 86 | 87 | return doc 88 | } 89 | -------------------------------------------------------------------------------- /_vendor/github.com/smartystreets/goconvey/convey/discovery.go: -------------------------------------------------------------------------------- 1 | package convey 2 | 3 | type actionSpecifier uint8 4 | 5 | const ( 6 | noSpecifier actionSpecifier = iota 7 | skipConvey 8 | focusConvey 9 | ) 10 | 11 | type suite struct { 12 | Situation string 13 | Test t 14 | Focus bool 15 | Func func(C) // nil means skipped 16 | FailMode FailureMode 17 | } 18 | 19 | func newSuite(situation string, failureMode FailureMode, f func(C), test t, specifier actionSpecifier) *suite { 20 | ret := &suite{ 21 | Situation: situation, 22 | Test: test, 23 | Func: f, 24 | FailMode: failureMode, 25 | } 26 | switch specifier { 27 | case skipConvey: 28 | ret.Func = nil 29 | case focusConvey: 30 | ret.Focus = true 31 | } 32 | return ret 33 | } 34 | 35 | func discover(items []interface{}) *suite { 36 | name, items := parseName(items) 37 | test, items := parseGoTest(items) 38 | failure, items := parseFailureMode(items) 39 | action, items := parseAction(items) 40 | specifier, items := parseSpecifier(items) 41 | 42 | if len(items) != 0 { 43 | conveyPanic(parseError) 44 | } 45 | 46 | return newSuite(name, failure, action, test, specifier) 47 | } 48 | func item(items []interface{}) interface{} { 49 | if len(items) == 0 { 50 | conveyPanic(parseError) 51 | } 52 | return items[0] 53 | } 54 | func parseName(items []interface{}) (string, []interface{}) { 55 | if name, parsed := item(items).(string); parsed { 56 | return name, items[1:] 57 | } 58 | conveyPanic(parseError) 59 | panic("never get here") 60 | } 61 | func parseGoTest(items []interface{}) (t, []interface{}) { 62 | if test, parsed := item(items).(t); parsed { 63 | return test, items[1:] 64 | } 65 | return nil, items 66 | } 67 | func parseFailureMode(items []interface{}) (FailureMode, []interface{}) { 68 | if mode, parsed := item(items).(FailureMode); parsed { 69 | return mode, items[1:] 70 | } 71 | return FailureInherits, items 72 | } 73 | func parseAction(items []interface{}) (func(C), []interface{}) { 74 | switch x := item(items).(type) { 75 | case nil: 76 | return nil, items[1:] 77 | case func(C): 78 | return x, items[1:] 79 | case func(): 80 | return func(C) { x() }, items[1:] 81 | } 82 | conveyPanic(parseError) 83 | panic("never get here") 84 | } 85 | func parseSpecifier(items []interface{}) (actionSpecifier, []interface{}) { 86 | if len(items) == 0 { 87 | return noSpecifier, items 88 | } 89 | if spec, ok := items[0].(actionSpecifier); ok { 90 | return spec, items[1:] 91 | } 92 | conveyPanic(parseError) 93 | panic("never get here") 94 | } 95 | 96 | // This interface allows us to pass the *testing.T struct 97 | // throughout the internals of this package without ever 98 | // having to import the "testing" package. 99 | type t interface { 100 | Fail() 101 | } 102 | 103 | const parseError = "You must provide a name (string), then a *testing.T (if in outermost scope), an optional FailureMode, and then an action (func())." 104 | -------------------------------------------------------------------------------- /_vendor/github.com/smartystreets/assertions/internal/oglematchers/any_of.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Aaron Jacobs. All Rights Reserved. 2 | // Author: aaronjjacobs@gmail.com (Aaron Jacobs) 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package oglematchers 17 | 18 | import ( 19 | "errors" 20 | "fmt" 21 | "reflect" 22 | "strings" 23 | ) 24 | 25 | // AnyOf accepts a set of values S and returns a matcher that follows the 26 | // algorithm below when considering a candidate c: 27 | // 28 | // 1. If there exists a value m in S such that m implements the Matcher 29 | // interface and m matches c, return true. 30 | // 31 | // 2. Otherwise, if there exists a value v in S such that v does not implement 32 | // the Matcher interface and the matcher Equals(v) matches c, return true. 33 | // 34 | // 3. Otherwise, if there is a value m in S such that m implements the Matcher 35 | // interface and m returns a fatal error for c, return that fatal error. 36 | // 37 | // 4. Otherwise, return false. 38 | // 39 | // This is akin to a logical OR operation for matchers, with non-matchers x 40 | // being treated as Equals(x). 41 | func AnyOf(vals ...interface{}) Matcher { 42 | // Get ahold of a type variable for the Matcher interface. 43 | var dummy *Matcher 44 | matcherType := reflect.TypeOf(dummy).Elem() 45 | 46 | // Create a matcher for each value, or use the value itself if it's already a 47 | // matcher. 48 | wrapped := make([]Matcher, len(vals)) 49 | for i, v := range vals { 50 | t := reflect.TypeOf(v) 51 | if t != nil && t.Implements(matcherType) { 52 | wrapped[i] = v.(Matcher) 53 | } else { 54 | wrapped[i] = Equals(v) 55 | } 56 | } 57 | 58 | return &anyOfMatcher{wrapped} 59 | } 60 | 61 | type anyOfMatcher struct { 62 | wrapped []Matcher 63 | } 64 | 65 | func (m *anyOfMatcher) Description() string { 66 | wrappedDescs := make([]string, len(m.wrapped)) 67 | for i, matcher := range m.wrapped { 68 | wrappedDescs[i] = matcher.Description() 69 | } 70 | 71 | return fmt.Sprintf("or(%s)", strings.Join(wrappedDescs, ", ")) 72 | } 73 | 74 | func (m *anyOfMatcher) Matches(c interface{}) (err error) { 75 | err = errors.New("") 76 | 77 | // Try each matcher in turn. 78 | for _, matcher := range m.wrapped { 79 | wrappedErr := matcher.Matches(c) 80 | 81 | // Return immediately if there's a match. 82 | if wrappedErr == nil { 83 | err = nil 84 | return 85 | } 86 | 87 | // Note the fatal error, if any. 88 | if _, isFatal := wrappedErr.(*FatalError); isFatal { 89 | err = wrappedErr 90 | } 91 | } 92 | 93 | return 94 | } 95 | -------------------------------------------------------------------------------- /_vendor/github.com/smartystreets/assertions/panic.go: -------------------------------------------------------------------------------- 1 | package assertions 2 | 3 | import "fmt" 4 | 5 | // ShouldPanic receives a void, niladic function and expects to recover a panic. 6 | func ShouldPanic(actual interface{}, expected ...interface{}) (message string) { 7 | if fail := need(0, expected); fail != success { 8 | return fail 9 | } 10 | 11 | action, _ := actual.(func()) 12 | 13 | if action == nil { 14 | message = shouldUseVoidNiladicFunction 15 | return 16 | } 17 | 18 | defer func() { 19 | recovered := recover() 20 | if recovered == nil { 21 | message = shouldHavePanicked 22 | } else { 23 | message = success 24 | } 25 | }() 26 | action() 27 | 28 | return 29 | } 30 | 31 | // ShouldNotPanic receives a void, niladic function and expects to execute the function without any panic. 32 | func ShouldNotPanic(actual interface{}, expected ...interface{}) (message string) { 33 | if fail := need(0, expected); fail != success { 34 | return fail 35 | } 36 | 37 | action, _ := actual.(func()) 38 | 39 | if action == nil { 40 | message = shouldUseVoidNiladicFunction 41 | return 42 | } 43 | 44 | defer func() { 45 | recovered := recover() 46 | if recovered != nil { 47 | message = fmt.Sprintf(shouldNotHavePanicked, recovered) 48 | } else { 49 | message = success 50 | } 51 | }() 52 | action() 53 | 54 | return 55 | } 56 | 57 | // ShouldPanicWith receives a void, niladic function and expects to recover a panic with the second argument as the content. 58 | func ShouldPanicWith(actual interface{}, expected ...interface{}) (message string) { 59 | if fail := need(1, expected); fail != success { 60 | return fail 61 | } 62 | 63 | action, _ := actual.(func()) 64 | 65 | if action == nil { 66 | message = shouldUseVoidNiladicFunction 67 | return 68 | } 69 | 70 | defer func() { 71 | recovered := recover() 72 | if recovered == nil { 73 | message = shouldHavePanicked 74 | } else { 75 | if equal := ShouldEqual(recovered, expected[0]); equal != success { 76 | message = serializer.serialize(expected[0], recovered, fmt.Sprintf(shouldHavePanickedWith, expected[0], recovered)) 77 | } else { 78 | message = success 79 | } 80 | } 81 | }() 82 | action() 83 | 84 | return 85 | } 86 | 87 | // ShouldNotPanicWith receives a void, niladic function and expects to recover a panic whose content differs from the second argument. 88 | func ShouldNotPanicWith(actual interface{}, expected ...interface{}) (message string) { 89 | if fail := need(1, expected); fail != success { 90 | return fail 91 | } 92 | 93 | action, _ := actual.(func()) 94 | 95 | if action == nil { 96 | message = shouldUseVoidNiladicFunction 97 | return 98 | } 99 | 100 | defer func() { 101 | recovered := recover() 102 | if recovered == nil { 103 | message = success 104 | } else { 105 | if equal := ShouldEqual(recovered, expected[0]); equal == success { 106 | message = fmt.Sprintf(shouldNotHavePanickedWith, expected[0]) 107 | } else { 108 | message = success 109 | } 110 | } 111 | }() 112 | action() 113 | 114 | return 115 | } 116 | -------------------------------------------------------------------------------- /_vendor/goji.io/goji.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package goji is a minimalistic and flexible HTTP request multiplexer. 3 | 4 | Goji itself has very few features: it is first and foremost a standard set of 5 | interfaces for writing web applications. Several subpackages are distributed 6 | with Goji to provide standard production-ready implementations of several of the 7 | interfaces, however users are also encouraged to implement the interfaces on 8 | their own, especially if their needs are unusual. 9 | */ 10 | package goji 11 | 12 | import "net/http" 13 | 14 | /* 15 | Pattern determines whether a given request matches some criteria. Goji users 16 | looking for a concrete type that implements this interface should consider 17 | Goji's "pat" sub-package, which implements a small domain specific language for 18 | HTTP routing. 19 | 20 | Patterns typically only examine a small portion of incoming requests, most 21 | commonly the HTTP method and the URL's RawPath. As an optimization, Goji can 22 | elide calls to your Pattern for requests it knows cannot match. Pattern authors 23 | who wish to take advantage of this functionality (and in some cases an 24 | asymptotic performance improvement) can augment their Pattern implementations 25 | with any of the following methods: 26 | 27 | // HTTPMethods returns a set of HTTP methods that this Pattern matches, 28 | // or nil if it's not possible to determine which HTTP methods might be 29 | // matched. Put another way, requests with HTTP methods not in the 30 | // returned set are guaranteed to never match this Pattern. 31 | HTTPMethods() map[string]struct{} 32 | 33 | // PathPrefix returns a string which all RawPaths that match this 34 | // Pattern must have as a prefix. Put another way, requests with 35 | // RawPaths that do not contain the returned string as a prefix are 36 | // guaranteed to never match this Pattern. 37 | PathPrefix() string 38 | 39 | The presence or lack of these performance improvements should be viewed as an 40 | implementation detail and are not part of Goji's API compatibility guarantee. It 41 | is the responsibility of Pattern authors to ensure that their Match function 42 | always returns correct results, even if these optimizations are not performed. 43 | 44 | All operations on Patterns must be safe for concurrent use by multiple 45 | goroutines. 46 | */ 47 | type Pattern interface { 48 | // Match examines the input Request to determine if it matches some 49 | // criteria, and if so returns a non-nil output Request. This returned 50 | // Request will be passed to the middleware stack and the final Handler. 51 | // 52 | // Patterns often extract variables from the Request, for instance from 53 | // the URL or from HTTP headers. In this case, it is common for the 54 | // Request returned from the Match function to be derived from the input 55 | // Request using the WithContext function, with a Context that contains 56 | // those variable bindings. If no variable bindings are necessary, 57 | // another common choice is to return the input Request unchanged. 58 | // 59 | // Match must not mutate the passed request if it returns nil. 60 | // Implementers are also strongly discouraged from mutating the input 61 | // Request even in the event of a match; instead, prefer making a copy. 62 | Match(*http.Request) *http.Request 63 | } 64 | -------------------------------------------------------------------------------- /jsh-api/api.go: -------------------------------------------------------------------------------- 1 | package jshapi 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | "path" 8 | "strings" 9 | 10 | "goji.io" 11 | "goji.io/pat" 12 | 13 | gojilogger "github.com/derekdowling/go-json-spec-handler/goji2-logger" 14 | "github.com/derekdowling/go-stdlogger" 15 | ) 16 | 17 | // API is used to direct HTTP requests to resources 18 | type API struct { 19 | *goji.Mux 20 | prefix string 21 | Resources map[string]*Resource 22 | Debug bool 23 | } 24 | 25 | /* 26 | SendHandler allows the customization of how API responses are sent and logged. This 27 | is used by all jshapi.Resource objects. 28 | */ 29 | var SendHandler = DefaultSender(log.New(os.Stderr, "jshapi: ", log.LstdFlags)) 30 | 31 | /* 32 | New initializes a new top level API Resource without doing any additional setup. 33 | */ 34 | func New(prefix string) *API { 35 | 36 | // ensure that our top level prefix is "/" prefixed 37 | if !strings.HasPrefix(prefix, "/") { 38 | prefix = fmt.Sprintf("/%s", prefix) 39 | } 40 | 41 | // create our new API 42 | return &API{ 43 | Mux: goji.NewMux(), 44 | prefix: prefix, 45 | Resources: map[string]*Resource{}, 46 | } 47 | } 48 | 49 | /* 50 | Default builds a new top-level API with a few out of the box additions to get people 51 | started without needing to add a lot of extra functionality. 52 | 53 | The most basic implementation is: 54 | 55 | // create a logger, the std log package works, as do most other loggers 56 | // std.Logger interface defined here: 57 | // https://github.com/derekdowling/go-stdlogger/blob/master/logger.go 58 | logger := log.New(os.Stderr, "jshapi: ", log.LstdFlags) 59 | 60 | // create the API. Specify a http://yourapi// if required 61 | api := jshapi.Default("", false, logger) 62 | api.Add(yourResource) 63 | 64 | */ 65 | func Default(prefix string, debug bool, logger std.Logger) *API { 66 | 67 | api := New(prefix) 68 | SendHandler = DefaultSender(logger) 69 | 70 | // register logger middleware 71 | gojilogger := gojilogger.New(logger, debug) 72 | api.Use(gojilogger.Middleware) 73 | 74 | return api 75 | } 76 | 77 | // Add implements mux support for a given resource which is effectively handled as: 78 | // pat.New("/(prefix/)resource.Plu*) 79 | func (a *API) Add(resource *Resource) { 80 | 81 | // track our associated resources, will enable auto-generation docs later 82 | a.Resources[resource.Type] = resource 83 | 84 | // Because of how prefix matches work: 85 | // https://godoc.org/github.com/goji/goji/pat#hdr-Prefix_Matches 86 | // We need two separate routes, 87 | // /(prefix/)resources 88 | matcher := path.Join(a.prefix, resource.Type) 89 | a.Mux.Handle(pat.New(matcher), resource) 90 | 91 | // And: 92 | // /(prefix/)resources/* 93 | idMatcher := path.Join(a.prefix, resource.Type, "*") 94 | a.Mux.Handle(pat.New(idMatcher), resource) 95 | } 96 | 97 | // RouteTree prints out all accepted routes for the API that use jshapi implemented 98 | // ways of adding routes through resources: NewCRUDResource(), .Get(), .Post, .Delete(), 99 | // .Patch(), .List(), and .NewAction() 100 | func (a *API) RouteTree() string { 101 | var routes string 102 | 103 | for _, resource := range a.Resources { 104 | routes = strings.Join([]string{routes, resource.RouteTree()}, "") 105 | } 106 | 107 | return routes 108 | } 109 | -------------------------------------------------------------------------------- /client/client_test.go: -------------------------------------------------------------------------------- 1 | package jsc 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "net/http" 7 | "net/url" 8 | "testing" 9 | 10 | "github.com/derekdowling/go-json-spec-handler" 11 | "github.com/derekdowling/go-json-spec-handler/jsh-api" 12 | . "github.com/smartystreets/goconvey/convey" 13 | ) 14 | 15 | const testURL = "https://httpbin.org" 16 | 17 | func TestClientRequest(t *testing.T) { 18 | 19 | Convey("Client Tests", t, func() { 20 | 21 | Convey("->setPath()", func() { 22 | url := &url.URL{Host: "test"} 23 | 24 | Convey("should format properly", func() { 25 | setPath(url, "tests") 26 | So(url.String(), ShouldEqual, "//test/tests") 27 | }) 28 | 29 | Convey("should respect an existing path", func() { 30 | url.Path = "admin" 31 | setPath(url, "test") 32 | So(url.String(), ShouldEqual, "//test/admin/test") 33 | }) 34 | }) 35 | 36 | Convey("->setIDPath()", func() { 37 | url := &url.URL{Host: "test"} 38 | 39 | Convey("should format properly an id url", func() { 40 | setIDPath(url, "tests", "1") 41 | So(url.String(), ShouldEqual, "//test/tests/1") 42 | }) 43 | }) 44 | 45 | }) 46 | } 47 | 48 | func TestParseResponse(t *testing.T) { 49 | 50 | Convey("ParseResponse", t, func() { 51 | 52 | response := &http.Response{ 53 | StatusCode: http.StatusNotFound, 54 | } 55 | 56 | Convey("404 response parsing should not return a 406 error", func() { 57 | doc, err := ParseResponse(response, jsh.ObjectMode) 58 | So(doc, ShouldBeNil) 59 | So(err, ShouldBeNil) 60 | }) 61 | }) 62 | } 63 | 64 | func TestResponseParsing(t *testing.T) { 65 | 66 | Convey("Response Parsing Tests", t, func() { 67 | 68 | Convey("Parse Object", func() { 69 | 70 | obj, objErr := jsh.NewObject("123", "test", map[string]string{"test": "test"}) 71 | So(objErr, ShouldBeNil) 72 | 73 | response, err := mockObjectResponse(obj) 74 | So(err, ShouldBeNil) 75 | 76 | Convey("should parse successfully", func() { 77 | doc, err := Document(response, jsh.ObjectMode) 78 | 79 | So(err, ShouldBeNil) 80 | So(doc.HasData(), ShouldBeTrue) 81 | So(doc.First().ID, ShouldEqual, "123") 82 | }) 83 | }) 84 | 85 | Convey("Parse List", func() { 86 | 87 | obj, objErr := jsh.NewObject("123", "test", map[string]string{"test": "test"}) 88 | So(objErr, ShouldBeNil) 89 | 90 | list := jsh.List{obj, obj} 91 | 92 | response, err := mockListResponse(list) 93 | So(err, ShouldBeNil) 94 | 95 | Convey("should parse successfully", func() { 96 | doc, err := Document(response, jsh.ListMode) 97 | 98 | So(err, ShouldBeNil) 99 | So(doc.HasData(), ShouldBeTrue) 100 | So(doc.First().ID, ShouldEqual, "123") 101 | }) 102 | }) 103 | }) 104 | } 105 | 106 | // not a great for this, would much rather have it in test_util, but it causes an 107 | // import cycle with jsh-api 108 | func testAPI() *jshapi.API { 109 | 110 | resource := jshapi.NewMockResource("tests", 1, nil) 111 | resource.Action("testAction", func(ctx context.Context, id string) (*jsh.Object, jsh.ErrorType) { 112 | object, err := jsh.NewObject("1", "tests", []string{"testAction"}) 113 | if err != nil { 114 | log.Fatal(err.Error()) 115 | } 116 | 117 | return object, nil 118 | }) 119 | 120 | api := jshapi.New("") 121 | api.Add(resource) 122 | 123 | return api 124 | } 125 | -------------------------------------------------------------------------------- /_vendor/goji.io/README.md: -------------------------------------------------------------------------------- 1 | Goji 2 | ==== 3 | 4 | [![GoDoc](https://godoc.org/goji.io?status.svg)](https://godoc.org/goji.io) [![Build Status](https://travis-ci.org/goji/goji.svg?branch=master)](https://travis-ci.org/goji/goji) 5 | 6 | Goji is a HTTP request multiplexer, similar to [`net/http.ServeMux`][servemux]. 7 | It compares incoming requests to a list of registered [Patterns][pattern], and 8 | dispatches to the [http.Handler][handler] that corresponds to the first matching 9 | Pattern. Goji also supports [Middleware][middleware] (composable shared 10 | functionality applied to every request) and uses the standard 11 | [`context`][context] package to store request-scoped values. 12 | 13 | [servemux]: https://golang.org/pkg/net/http/#ServeMux 14 | [pattern]: https://godoc.org/goji.io#Pattern 15 | [handler]: https://golang.org/pkg/net/http/#Handler 16 | [middleware]: https://godoc.org/goji.io#Mux.Use 17 | [context]: https://golang.org/pkg/context 18 | 19 | 20 | Quick Start 21 | ----------- 22 | 23 | ```go 24 | package main 25 | 26 | import ( 27 | "fmt" 28 | "net/http" 29 | 30 | "goji.io" 31 | "goji.io/pat" 32 | ) 33 | 34 | func hello(w http.ResponseWriter, r *http.Request) { 35 | name := pat.Param(r, "name") 36 | fmt.Fprintf(w, "Hello, %s!", name) 37 | } 38 | 39 | func main() { 40 | mux := goji.NewMux() 41 | mux.HandleFunc(pat.Get("/hello/:name"), hello) 42 | 43 | http.ListenAndServe("localhost:8000", mux) 44 | } 45 | ``` 46 | 47 | Please refer to [Goji's GoDoc Documentation][godoc] for a full API reference. 48 | 49 | [godoc]: https://godoc.org/goji.io 50 | 51 | 52 | Stability 53 | --------- 54 | 55 | Goji's API was recently updated to use the new `net/http` and `context` 56 | integration, and is therefore some of its interfaces are in a state of flux. We 57 | don't expect any further changes to the API, and expect to be able to announce 58 | API stability soon. Goji is suitable for use in production. 59 | 60 | Prior to Go 1.7, Goji promised API stability with a different API to the one 61 | that is offered today. The author broke this promise, and does not take this 62 | breach of trust lightly. While stability is obviously extremely important, the 63 | author and community have decided to follow the broader Go community in 64 | standardizing on the standard library copy of the `context` package. 65 | 66 | Users of the old API can find that familiar API on the `net-context` branch. The 67 | author promises to maintain both the `net-context` branch and `master` for the 68 | forseeable future. 69 | 70 | 71 | Community / Contributing 72 | ------------------------ 73 | 74 | Goji maintains a mailing list, [gojiberries][berries], where you should feel 75 | welcome to ask questions about the project (no matter how simple!), to announce 76 | projects or libraries built on top of Goji, or to talk about Goji more 77 | generally. Goji's author (Carl Jackson) also loves to hear from users directly 78 | at his personal email address, which is available on his GitHub profile page. 79 | 80 | Contributions to Goji are welcome, however please be advised that due to Goji's 81 | stability guarantees interface changes are unlikely to be accepted. 82 | 83 | All interactions in the Goji community will be held to the high standard of the 84 | broader Go community's [Code of Conduct][conduct]. 85 | 86 | [berries]: https://groups.google.com/forum/#!forum/gojiberries 87 | [conduct]: https://golang.org/conduct 88 | -------------------------------------------------------------------------------- /_vendor/github.com/smartystreets/goconvey/convey/assertions.go: -------------------------------------------------------------------------------- 1 | package convey 2 | 3 | import "github.com/smartystreets/assertions" 4 | 5 | var ( 6 | ShouldEqual = assertions.ShouldEqual 7 | ShouldNotEqual = assertions.ShouldNotEqual 8 | ShouldAlmostEqual = assertions.ShouldAlmostEqual 9 | ShouldNotAlmostEqual = assertions.ShouldNotAlmostEqual 10 | ShouldResemble = assertions.ShouldResemble 11 | ShouldNotResemble = assertions.ShouldNotResemble 12 | ShouldPointTo = assertions.ShouldPointTo 13 | ShouldNotPointTo = assertions.ShouldNotPointTo 14 | ShouldBeNil = assertions.ShouldBeNil 15 | ShouldNotBeNil = assertions.ShouldNotBeNil 16 | ShouldBeTrue = assertions.ShouldBeTrue 17 | ShouldBeFalse = assertions.ShouldBeFalse 18 | ShouldBeZeroValue = assertions.ShouldBeZeroValue 19 | 20 | ShouldBeGreaterThan = assertions.ShouldBeGreaterThan 21 | ShouldBeGreaterThanOrEqualTo = assertions.ShouldBeGreaterThanOrEqualTo 22 | ShouldBeLessThan = assertions.ShouldBeLessThan 23 | ShouldBeLessThanOrEqualTo = assertions.ShouldBeLessThanOrEqualTo 24 | ShouldBeBetween = assertions.ShouldBeBetween 25 | ShouldNotBeBetween = assertions.ShouldNotBeBetween 26 | ShouldBeBetweenOrEqual = assertions.ShouldBeBetweenOrEqual 27 | ShouldNotBeBetweenOrEqual = assertions.ShouldNotBeBetweenOrEqual 28 | 29 | ShouldContain = assertions.ShouldContain 30 | ShouldNotContain = assertions.ShouldNotContain 31 | ShouldContainKey = assertions.ShouldContainKey 32 | ShouldNotContainKey = assertions.ShouldNotContainKey 33 | ShouldBeIn = assertions.ShouldBeIn 34 | ShouldNotBeIn = assertions.ShouldNotBeIn 35 | ShouldBeEmpty = assertions.ShouldBeEmpty 36 | ShouldNotBeEmpty = assertions.ShouldNotBeEmpty 37 | ShouldHaveLength = assertions.ShouldHaveLength 38 | 39 | ShouldStartWith = assertions.ShouldStartWith 40 | ShouldNotStartWith = assertions.ShouldNotStartWith 41 | ShouldEndWith = assertions.ShouldEndWith 42 | ShouldNotEndWith = assertions.ShouldNotEndWith 43 | ShouldBeBlank = assertions.ShouldBeBlank 44 | ShouldNotBeBlank = assertions.ShouldNotBeBlank 45 | ShouldContainSubstring = assertions.ShouldContainSubstring 46 | ShouldNotContainSubstring = assertions.ShouldNotContainSubstring 47 | 48 | ShouldPanic = assertions.ShouldPanic 49 | ShouldNotPanic = assertions.ShouldNotPanic 50 | ShouldPanicWith = assertions.ShouldPanicWith 51 | ShouldNotPanicWith = assertions.ShouldNotPanicWith 52 | 53 | ShouldHaveSameTypeAs = assertions.ShouldHaveSameTypeAs 54 | ShouldNotHaveSameTypeAs = assertions.ShouldNotHaveSameTypeAs 55 | ShouldImplement = assertions.ShouldImplement 56 | ShouldNotImplement = assertions.ShouldNotImplement 57 | 58 | ShouldHappenBefore = assertions.ShouldHappenBefore 59 | ShouldHappenOnOrBefore = assertions.ShouldHappenOnOrBefore 60 | ShouldHappenAfter = assertions.ShouldHappenAfter 61 | ShouldHappenOnOrAfter = assertions.ShouldHappenOnOrAfter 62 | ShouldHappenBetween = assertions.ShouldHappenBetween 63 | ShouldHappenOnOrBetween = assertions.ShouldHappenOnOrBetween 64 | ShouldNotHappenOnOrBetween = assertions.ShouldNotHappenOnOrBetween 65 | ShouldHappenWithin = assertions.ShouldHappenWithin 66 | ShouldNotHappenWithin = assertions.ShouldNotHappenWithin 67 | ShouldBeChronological = assertions.ShouldBeChronological 68 | 69 | ShouldBeError = assertions.ShouldBeError 70 | ) 71 | -------------------------------------------------------------------------------- /list_test.go: -------------------------------------------------------------------------------- 1 | package jsh 2 | 3 | import ( 4 | "encoding/json" 5 | "net/http" 6 | "net/http/httptest" 7 | "strconv" 8 | "testing" 9 | 10 | . "github.com/smartystreets/goconvey/convey" 11 | ) 12 | 13 | func TestList(t *testing.T) { 14 | 15 | Convey("List Tests", t, func() { 16 | 17 | testObject := &Object{ 18 | ID: "ID123", 19 | Type: "testConversion", 20 | Attributes: json.RawMessage(`{"foo":"bar"}`), 21 | } 22 | 23 | testList := List{testObject} 24 | req := &http.Request{Method: "GET"} 25 | 26 | Convey("->Validate()", func() { 27 | err := testList.Validate(req, true) 28 | So(err, ShouldBeNil) 29 | }) 30 | 31 | Convey("->Send(list)", func() { 32 | 33 | Convey("should send a properly formatted List response", func() { 34 | 35 | writer := httptest.NewRecorder() 36 | err := Send(writer, req, testList) 37 | So(err, ShouldBeNil) 38 | So(writer.Code, ShouldEqual, http.StatusOK) 39 | 40 | contentLength, convErr := strconv.Atoi(writer.HeaderMap.Get("Content-Length")) 41 | So(convErr, ShouldBeNil) 42 | So(contentLength, ShouldBeGreaterThan, 0) 43 | So(writer.HeaderMap.Get("Content-Type"), ShouldEqual, ContentType) 44 | 45 | req, reqErr := testRequest(writer.Body.Bytes()) 46 | So(reqErr, ShouldBeNil) 47 | 48 | responseList, parseErr := ParseList(req) 49 | So(parseErr, ShouldBeNil) 50 | So(len(responseList), ShouldEqual, 1) 51 | }) 52 | }) 53 | 54 | Convey("->Send(empty list)", func() { 55 | 56 | Convey("should send a properly formatted empty List response", func() { 57 | 58 | writer := httptest.NewRecorder() 59 | err := Send(writer, req, List([]*Object{})) 60 | So(err, ShouldBeNil) 61 | So(writer.Code, ShouldEqual, http.StatusOK) 62 | 63 | contentLength, convErr := strconv.Atoi(writer.HeaderMap.Get("Content-Length")) 64 | So(convErr, ShouldBeNil) 65 | So(contentLength, ShouldBeGreaterThan, 0) 66 | So(writer.HeaderMap.Get("Content-Type"), ShouldEqual, ContentType) 67 | 68 | req, reqErr := testRequest(writer.Body.Bytes()) 69 | So(reqErr, ShouldBeNil) 70 | 71 | responseList, parseErr := ParseList(req) 72 | So(parseErr, ShouldBeNil) 73 | So(len(responseList), ShouldEqual, 0) 74 | }) 75 | }) 76 | 77 | Convey("->Send(nil list)", func() { 78 | 79 | Convey("should fail with a 500 ISE", func() { 80 | 81 | writer := httptest.NewRecorder() 82 | var list []*Object 83 | err := Send(writer, req, List(list)) 84 | So(err, ShouldNotBeNil) 85 | So(writer.Code, ShouldEqual, http.StatusInternalServerError) 86 | }) 87 | }) 88 | 89 | Convey("->UnmarshalJSON()", func() { 90 | 91 | Convey("should handle a data object", func() { 92 | jObj := `{"data": {"type": "user", "id": "sweetID123", "attributes": {"ID":"123"}}}` 93 | 94 | l := List{} 95 | err := l.UnmarshalJSON([]byte(jObj)) 96 | So(err, ShouldBeNil) 97 | So(l, ShouldNotBeEmpty) 98 | }) 99 | 100 | Convey("should handle a data list", func() { 101 | jList := `{"data": [{"type": "user", "id": "sweetID123", "attributes": {"ID":"123"}}]}` 102 | 103 | l := List{} 104 | err := l.UnmarshalJSON([]byte(jList)) 105 | So(err, ShouldBeNil) 106 | So(l, ShouldNotBeEmpty) 107 | }) 108 | 109 | Convey("should handle an empty array", func() { 110 | jObj := `{"data": []}` 111 | 112 | l := List{} 113 | err := l.UnmarshalJSON([]byte(jObj)) 114 | So(err, ShouldBeNil) 115 | So(l, ShouldNotBeNil) 116 | }) 117 | }) 118 | 119 | Convey("->MarshalJSON()", func() { 120 | 121 | Convey("should preserve an empty list", func() { 122 | list := List{} 123 | 124 | jData, err := json.Marshal(list) 125 | So(err, ShouldBeNil) 126 | 127 | So(string(jData), ShouldEqual, "[]") 128 | }) 129 | }) 130 | }) 131 | } 132 | -------------------------------------------------------------------------------- /_vendor/github.com/jtolds/gls/README.md: -------------------------------------------------------------------------------- 1 | gls 2 | === 3 | 4 | Goroutine local storage 5 | 6 | ### IMPORTANT NOTE ### 7 | 8 | It is my duty to point you to https://blog.golang.org/context, which is how 9 | Google solves all of the problems you'd perhaps consider using this package 10 | for at scale. 11 | 12 | One downside to Google's approach is that *all* of your functions must have 13 | a new first argument, but after clearing that hurdle everything else is much 14 | better. 15 | 16 | If you aren't interested in this warning, read on. 17 | 18 | ### Huhwaht? Why? ### 19 | 20 | Every so often, a thread shows up on the 21 | [golang-nuts](https://groups.google.com/d/forum/golang-nuts) asking for some 22 | form of goroutine-local-storage, or some kind of goroutine id, or some kind of 23 | context. There are a few valid use cases for goroutine-local-storage, one of 24 | the most prominent being log line context. One poster was interested in being 25 | able to log an HTTP request context id in every log line in the same goroutine 26 | as the incoming HTTP request, without having to change every library and 27 | function call he was interested in logging. 28 | 29 | This would be pretty useful. Provided that you could get some kind of 30 | goroutine-local-storage, you could call 31 | [log.SetOutput](http://golang.org/pkg/log/#SetOutput) with your own logging 32 | writer that checks goroutine-local-storage for some context information and 33 | adds that context to your log lines. 34 | 35 | But alas, Andrew Gerrand's typically diplomatic answer to the question of 36 | goroutine-local variables was: 37 | 38 | > We wouldn't even be having this discussion if thread local storage wasn't 39 | > useful. But every feature comes at a cost, and in my opinion the cost of 40 | > threadlocals far outweighs their benefits. They're just not a good fit for 41 | > Go. 42 | 43 | So, yeah, that makes sense. That's a pretty good reason for why the language 44 | won't support a specific and (relatively) unuseful feature that requires some 45 | runtime changes, just for the sake of a little bit of log improvement. 46 | 47 | But does Go require runtime changes? 48 | 49 | ### How it works ### 50 | 51 | Go has pretty fantastic introspective and reflective features, but one thing Go 52 | doesn't give you is any kind of access to the stack pointer, or frame pointer, 53 | or goroutine id, or anything contextual about your current stack. It gives you 54 | access to your list of callers, but only along with program counters, which are 55 | fixed at compile time. 56 | 57 | But it does give you the stack. 58 | 59 | So, we define 16 special functions and embed base-16 tags into the stack using 60 | the call order of those 16 functions. Then, we can read our tags back out of 61 | the stack looking at the callers list. 62 | 63 | We then use these tags as an index into a traditional map for implementing 64 | this library. 65 | 66 | ### What are people saying? ### 67 | 68 | "Wow, that's horrifying." 69 | 70 | "This is the most terrible thing I have seen in a very long time." 71 | 72 | "Where is it getting a context from? Is this serializing all the requests? 73 | What the heck is the client being bound to? What are these tags? Why does he 74 | need callers? Oh god no. No no no." 75 | 76 | ### Docs ### 77 | 78 | Please see the docs at http://godoc.org/github.com/jtolds/gls 79 | 80 | ### Related ### 81 | 82 | If you're okay relying on the string format of the current runtime stacktrace 83 | including a unique goroutine id (not guaranteed by the spec or anything, but 84 | very unlikely to change within a Go release), you might be able to squeeze 85 | out a bit more performance by using this similar library, inspired by some 86 | code Brad Fitzpatrick wrote for debugging his HTTP/2 library: 87 | https://github.com/tylerb/gls (in contrast, jtolds/gls doesn't require 88 | any knowledge of the string format of the runtime stacktrace, which 89 | probably adds unnecessary overhead). 90 | -------------------------------------------------------------------------------- /_vendor/github.com/smartystreets/assertions/internal/oglematchers/matcher.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Aaron Jacobs. All Rights Reserved. 2 | // Author: aaronjjacobs@gmail.com (Aaron Jacobs) 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | // Package oglematchers provides a set of matchers useful in a testing or 17 | // mocking framework. These matchers are inspired by and mostly compatible with 18 | // Google Test for C++ and Google JS Test. 19 | // 20 | // This package is used by github.com/smartystreets/assertions/internal/ogletest and 21 | // github.com/smartystreets/assertions/internal/oglemock, which may be more directly useful if you're not 22 | // writing your own testing package or defining your own matchers. 23 | package oglematchers 24 | 25 | // A Matcher is some predicate implicitly defining a set of values that it 26 | // matches. For example, GreaterThan(17) matches all numeric values greater 27 | // than 17, and HasSubstr("taco") matches all strings with the substring 28 | // "taco". 29 | // 30 | // Matchers are typically exposed to tests via constructor functions like 31 | // HasSubstr. In order to implement such a function you can either define your 32 | // own matcher type or use NewMatcher. 33 | type Matcher interface { 34 | // Check whether the supplied value belongs to the the set defined by the 35 | // matcher. Return a non-nil error if and only if it does not. 36 | // 37 | // The error describes why the value doesn't match. The error text is a 38 | // relative clause that is suitable for being placed after the value. For 39 | // example, a predicate that matches strings with a particular substring may, 40 | // when presented with a numerical value, return the following error text: 41 | // 42 | // "which is not a string" 43 | // 44 | // Then the failure message may look like: 45 | // 46 | // Expected: has substring "taco" 47 | // Actual: 17, which is not a string 48 | // 49 | // If the error is self-apparent based on the description of the matcher, the 50 | // error text may be empty (but the error still non-nil). For example: 51 | // 52 | // Expected: 17 53 | // Actual: 19 54 | // 55 | // If you are implementing a new matcher, see also the documentation on 56 | // FatalError. 57 | Matches(candidate interface{}) error 58 | 59 | // Description returns a string describing the property that values matching 60 | // this matcher have, as a verb phrase where the subject is the value. For 61 | // example, "is greather than 17" or "has substring "taco"". 62 | Description() string 63 | } 64 | 65 | // FatalError is an implementation of the error interface that may be returned 66 | // from matchers, indicating the error should be propagated. Returning a 67 | // *FatalError indicates that the matcher doesn't process values of the 68 | // supplied type, or otherwise doesn't know how to handle the value. 69 | // 70 | // For example, if GreaterThan(17) returned false for the value "taco" without 71 | // a fatal error, then Not(GreaterThan(17)) would return true. This is 72 | // technically correct, but is surprising and may mask failures where the wrong 73 | // sort of matcher is accidentally used. Instead, GreaterThan(17) can return a 74 | // fatal error, which will be propagated by Not(). 75 | type FatalError struct { 76 | errorText string 77 | } 78 | 79 | // NewFatalError creates a FatalError struct with the supplied error text. 80 | func NewFatalError(s string) *FatalError { 81 | return &FatalError{s} 82 | } 83 | 84 | func (e *FatalError) Error() string { 85 | return e.errorText 86 | } 87 | -------------------------------------------------------------------------------- /goji2-logger/logger.go: -------------------------------------------------------------------------------- 1 | package gojilogger 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "log" 7 | "net/http" 8 | "net/url" 9 | "os" 10 | "time" 11 | 12 | "goji.io/pattern" 13 | 14 | "github.com/derekdowling/go-stdlogger" 15 | "github.com/zenazn/goji/web/mutil" 16 | ) 17 | 18 | const ( 19 | // FastResponse is anything under this duration 20 | FastResponse = 500 * time.Millisecond 21 | // AcceptableResponse is anything under this duration 22 | AcceptableResponse = 5 * time.Second 23 | ) 24 | 25 | // Logger contains instance state for a goji2logger to avoid configuration 26 | // collisions if this middleware is used in multiple places 27 | type Logger struct { 28 | // Logger is a https://github.com/derekdowling/go-stdlogger 29 | Logger std.Logger 30 | // Debug will increase verbosity of logging information, and causes Query params not 31 | // to be omitted. Do NOT use in production otherwise you risk logging sensitive 32 | // information. 33 | Debug bool 34 | } 35 | 36 | // New creates a new goji2logger instance 37 | func New(logger std.Logger, debug bool) *Logger { 38 | l := &Logger{ 39 | Debug: debug, 40 | Logger: logger, 41 | } 42 | 43 | if l.Logger == nil { 44 | l.Logger = log.New(os.Stderr, "", log.LstdFlags) 45 | } 46 | 47 | return l 48 | } 49 | 50 | // Middleware logs the start and end of each request, along 51 | // with some useful data about what was requested, what the response status was, 52 | // and how long it took to return. When standard output is a TTY, Logger will 53 | // print in color, otherwise it will print in black and white. 54 | // 55 | // Use like so with Goji2: 56 | // gLogger := gojilogger.New(nil, false) 57 | // yourGoji.UseC(gLogger.Middleware) 58 | func (l *Logger) Middleware(next http.Handler) http.Handler { 59 | middleware := func(w http.ResponseWriter, r *http.Request) { 60 | l.printRequest(r) 61 | 62 | // WrapWriter lets us peek at ResponseWriter outputs 63 | lw := mutil.WrapWriter(w) 64 | 65 | startTime := time.Now() 66 | next.ServeHTTP(lw, r) 67 | 68 | if lw.Status() == 0 { 69 | lw.WriteHeader(http.StatusOK) 70 | } 71 | 72 | finishTime := time.Now() 73 | 74 | l.printResponse(lw, finishTime.Sub(startTime)) 75 | } 76 | 77 | return http.HandlerFunc(middleware) 78 | } 79 | 80 | func (l *Logger) printRequest(r *http.Request) { 81 | var buf bytes.Buffer 82 | 83 | if l.Debug { 84 | buf.WriteString("[DEBUG]") 85 | } 86 | 87 | buf.WriteString("Serving route: ") 88 | 89 | // Goji routing details 90 | colorWrite(&buf, bGreen, "%s", pattern.Path(r.Context())) 91 | 92 | // Server details 93 | buf.WriteString(fmt.Sprintf(" from %s ", r.RemoteAddr)) 94 | 95 | // Request details 96 | buf.WriteString("for ") 97 | colorWrite(&buf, bMagenta, "%s ", r.Method) 98 | 99 | urlStr := r.URL.String() 100 | 101 | // if not in debug mode, remove Query params from logging as not to include any 102 | // sensitive information inadvertantly into user's logs 103 | if !l.Debug && r.URL.RawQuery != "" { 104 | tempURL := &url.URL{} 105 | *tempURL = *r.URL 106 | tempURL.RawQuery = "" 107 | urlStr = tempURL.String() 108 | } 109 | 110 | colorWrite(&buf, bBlue, "%q", urlStr) 111 | l.Logger.Print(buf.String()) 112 | } 113 | 114 | func (l *Logger) printResponse(w mutil.WriterProxy, delta time.Duration) { 115 | var buf bytes.Buffer 116 | 117 | buf.WriteString("Returning HTTP ") 118 | 119 | status := w.Status() 120 | if status < 200 { 121 | colorWrite(&buf, bBlue, "%03d", status) 122 | } else if status < 300 { 123 | colorWrite(&buf, bGreen, "%03d", status) 124 | } else if status < 400 { 125 | colorWrite(&buf, bCyan, "%03d", status) 126 | } else if status < 500 { 127 | colorWrite(&buf, bYellow, "%03d", status) 128 | } else { 129 | colorWrite(&buf, bRed, "%03d", status) 130 | } 131 | 132 | buf.WriteString(" in ") 133 | 134 | if delta < FastResponse { 135 | colorWrite(&buf, nGreen, "%s", delta.String()) 136 | } else if delta < AcceptableResponse { 137 | colorWrite(&buf, nYellow, "%s", delta.String()) 138 | } else { 139 | colorWrite(&buf, nRed, "%s", delta.String()) 140 | } 141 | 142 | l.Logger.Print(buf.String()) 143 | } 144 | -------------------------------------------------------------------------------- /parser_test.go: -------------------------------------------------------------------------------- 1 | package jsh 2 | 3 | import ( 4 | "encoding/json" 5 | "net/http" 6 | "testing" 7 | 8 | . "github.com/smartystreets/goconvey/convey" 9 | ) 10 | 11 | func TestParsing(t *testing.T) { 12 | 13 | Convey("Parse Tests", t, func() { 14 | 15 | Convey("->validateHeaders()", func() { 16 | req, reqErr := http.NewRequest("GET", "", nil) 17 | So(reqErr, ShouldBeNil) 18 | req.Header.Set("Content-Type", "jpeg") 19 | 20 | err := validateHeaders(req.Header) 21 | So(err, ShouldNotBeNil) 22 | So(err.Status, ShouldEqual, http.StatusNotAcceptable) 23 | }) 24 | 25 | Convey("->ParseObject()", func() { 26 | 27 | Convey("should parse a valid object", func() { 28 | 29 | objectJSON := `{ 30 | "data": { 31 | "type": "user", 32 | "id": "sweetID123", 33 | "attributes": {"ID":"123"}, 34 | "relationships": { 35 | "company": { 36 | "data": { "type": "company", "id": "companyID123" } 37 | }, 38 | "comments": { 39 | "data": [ 40 | { "type": "comments", "id": "commentID123" }, 41 | { "type": "comments", "id": "commentID456" } 42 | ] 43 | } 44 | } 45 | } 46 | }` 47 | req, reqErr := testRequest([]byte(objectJSON)) 48 | So(reqErr, ShouldBeNil) 49 | 50 | object, err := ParseObject(req) 51 | 52 | So(err, ShouldBeNil) 53 | So(object, ShouldNotBeEmpty) 54 | So(object.Type, ShouldEqual, "user") 55 | So(object.ID, ShouldEqual, "sweetID123") 56 | So(object.Attributes, ShouldResemble, json.RawMessage(`{"ID":"123"}`)) 57 | So(object.Relationships["company"], ShouldResemble, &Relationship{Data: ResourceLinkage{&ResourceIdentifier{Type: "company", ID: "companyID123"}}}) 58 | So(object.Relationships["comments"], ShouldResemble, &Relationship{Data: ResourceLinkage{{Type: "comments", ID: "commentID123"}, {Type: "comments", ID: "commentID456"}}}) 59 | }) 60 | 61 | Convey("should reject an object with missing attributes", func() { 62 | objectJSON := `{"data": {"id": "sweetID123", "attributes": {"ID":"123"}}}` 63 | 64 | req, reqErr := testRequest([]byte(objectJSON)) 65 | So(reqErr, ShouldBeNil) 66 | 67 | _, err := ParseObject(req) 68 | So(err, ShouldNotBeNil) 69 | So(err.Status, ShouldEqual, 422) 70 | So(err.Source.Pointer, ShouldEqual, "/data/attributes/type") 71 | }) 72 | 73 | Convey("should accept empty ID only for POST", func() { 74 | objectJSON := `{"data": {"id": "", "type":"test", "attributes": {"ID":"123"}}}` 75 | req, reqErr := testRequest([]byte(objectJSON)) 76 | So(reqErr, ShouldBeNil) 77 | 78 | Convey("POST test", func() { 79 | req.Method = "POST" 80 | _, err := ParseObject(req) 81 | So(err, ShouldBeNil) 82 | }) 83 | 84 | Convey("PATCH test", func() { 85 | req.Method = "PATCH" 86 | _, err := ParseObject(req) 87 | So(err, ShouldNotBeNil) 88 | }) 89 | }) 90 | }) 91 | 92 | Convey("->ParseList()", func() { 93 | 94 | Convey("should parse a valid list", func() { 95 | 96 | listJSON := 97 | `{"data": [ 98 | {"type": "user", "id": "sweetID123", "attributes": {"ID":"123"}}, 99 | {"type": "user", "id": "sweetID456", "attributes": {"ID":"456"}} 100 | ]}` 101 | req, reqErr := testRequest([]byte(listJSON)) 102 | So(reqErr, ShouldBeNil) 103 | 104 | list, err := ParseList(req) 105 | So(err, ShouldBeNil) 106 | So(len(list), ShouldEqual, 2) 107 | 108 | object := list[1] 109 | So(object.Type, ShouldEqual, "user") 110 | So(object.ID, ShouldEqual, "sweetID456") 111 | So(object.Attributes, ShouldResemble, json.RawMessage(`{"ID":"456"}`)) 112 | }) 113 | 114 | Convey("should error for an invalid list", func() { 115 | listJSON := 116 | `{"data": [ 117 | {"type": "user", "id": "sweetID123", "attributes": {"ID":"123"}}, 118 | {"type": "user", "attributes": {"ID":"456"}} 119 | ]}` 120 | 121 | req, reqErr := testRequest([]byte(listJSON)) 122 | So(reqErr, ShouldBeNil) 123 | 124 | _, err := ParseList(req) 125 | So(err, ShouldNotBeNil) 126 | So(err.Status, ShouldEqual, 422) 127 | So(err.Source.Pointer, ShouldEqual, "/data/attributes/id") 128 | }) 129 | }) 130 | }) 131 | } 132 | -------------------------------------------------------------------------------- /_vendor/github.com/zenazn/goji/web/mutil/writer_proxy.go: -------------------------------------------------------------------------------- 1 | package mutil 2 | 3 | import ( 4 | "bufio" 5 | "io" 6 | "net" 7 | "net/http" 8 | ) 9 | 10 | // WriterProxy is a proxy around an http.ResponseWriter that allows you to hook 11 | // into various parts of the response process. 12 | type WriterProxy interface { 13 | http.ResponseWriter 14 | // Status returns the HTTP status of the request, or 0 if one has not 15 | // yet been sent. 16 | Status() int 17 | // BytesWritten returns the total number of bytes sent to the client. 18 | BytesWritten() int 19 | // Tee causes the response body to be written to the given io.Writer in 20 | // addition to proxying the writes through. Only one io.Writer can be 21 | // tee'd to at once: setting a second one will overwrite the first. 22 | // Writes will be sent to the proxy before being written to this 23 | // io.Writer. It is illegal for the tee'd writer to be modified 24 | // concurrently with writes. 25 | Tee(io.Writer) 26 | // Unwrap returns the original proxied target. 27 | Unwrap() http.ResponseWriter 28 | } 29 | 30 | // WrapWriter wraps an http.ResponseWriter, returning a proxy that allows you to 31 | // hook into various parts of the response process. 32 | func WrapWriter(w http.ResponseWriter) WriterProxy { 33 | _, cn := w.(http.CloseNotifier) 34 | _, fl := w.(http.Flusher) 35 | _, hj := w.(http.Hijacker) 36 | _, rf := w.(io.ReaderFrom) 37 | 38 | bw := basicWriter{ResponseWriter: w} 39 | if cn && fl && hj && rf { 40 | return &fancyWriter{bw} 41 | } 42 | if fl { 43 | return &flushWriter{bw} 44 | } 45 | return &bw 46 | } 47 | 48 | // basicWriter wraps a http.ResponseWriter that implements the minimal 49 | // http.ResponseWriter interface. 50 | type basicWriter struct { 51 | http.ResponseWriter 52 | wroteHeader bool 53 | code int 54 | bytes int 55 | tee io.Writer 56 | } 57 | 58 | func (b *basicWriter) WriteHeader(code int) { 59 | if !b.wroteHeader { 60 | b.code = code 61 | b.wroteHeader = true 62 | b.ResponseWriter.WriteHeader(code) 63 | } 64 | } 65 | func (b *basicWriter) Write(buf []byte) (int, error) { 66 | b.WriteHeader(http.StatusOK) 67 | n, err := b.ResponseWriter.Write(buf) 68 | if b.tee != nil { 69 | _, err2 := b.tee.Write(buf[:n]) 70 | // Prefer errors generated by the proxied writer. 71 | if err == nil { 72 | err = err2 73 | } 74 | } 75 | b.bytes += n 76 | return n, err 77 | } 78 | func (b *basicWriter) maybeWriteHeader() { 79 | if !b.wroteHeader { 80 | b.WriteHeader(http.StatusOK) 81 | } 82 | } 83 | func (b *basicWriter) Status() int { 84 | return b.code 85 | } 86 | func (b *basicWriter) BytesWritten() int { 87 | return b.bytes 88 | } 89 | func (b *basicWriter) Tee(w io.Writer) { 90 | b.tee = w 91 | } 92 | func (b *basicWriter) Unwrap() http.ResponseWriter { 93 | return b.ResponseWriter 94 | } 95 | 96 | // fancyWriter is a writer that additionally satisfies http.CloseNotifier, 97 | // http.Flusher, http.Hijacker, and io.ReaderFrom. It exists for the common case 98 | // of wrapping the http.ResponseWriter that package http gives you, in order to 99 | // make the proxied object support the full method set of the proxied object. 100 | type fancyWriter struct { 101 | basicWriter 102 | } 103 | 104 | func (f *fancyWriter) CloseNotify() <-chan bool { 105 | cn := f.basicWriter.ResponseWriter.(http.CloseNotifier) 106 | return cn.CloseNotify() 107 | } 108 | func (f *fancyWriter) Flush() { 109 | fl := f.basicWriter.ResponseWriter.(http.Flusher) 110 | fl.Flush() 111 | } 112 | func (f *fancyWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { 113 | hj := f.basicWriter.ResponseWriter.(http.Hijacker) 114 | return hj.Hijack() 115 | } 116 | func (f *fancyWriter) ReadFrom(r io.Reader) (int64, error) { 117 | if f.basicWriter.tee != nil { 118 | return io.Copy(&f.basicWriter, r) 119 | } 120 | rf := f.basicWriter.ResponseWriter.(io.ReaderFrom) 121 | f.basicWriter.maybeWriteHeader() 122 | return rf.ReadFrom(r) 123 | } 124 | 125 | var _ http.CloseNotifier = &fancyWriter{} 126 | var _ http.Flusher = &fancyWriter{} 127 | var _ http.Hijacker = &fancyWriter{} 128 | var _ io.ReaderFrom = &fancyWriter{} 129 | 130 | type flushWriter struct { 131 | basicWriter 132 | } 133 | 134 | func (f *flushWriter) Flush() { 135 | fl := f.basicWriter.ResponseWriter.(http.Flusher) 136 | fl.Flush() 137 | } 138 | 139 | var _ http.Flusher = &flushWriter{} 140 | -------------------------------------------------------------------------------- /_vendor/github.com/smartystreets/assertions/internal/oglematchers/less_than.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 Aaron Jacobs. All Rights Reserved. 2 | // Author: aaronjjacobs@gmail.com (Aaron Jacobs) 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package oglematchers 17 | 18 | import ( 19 | "errors" 20 | "fmt" 21 | "math" 22 | "reflect" 23 | ) 24 | 25 | // LessThan returns a matcher that matches integer, floating point, or strings 26 | // values v such that v < x. Comparison is not defined between numeric and 27 | // string types, but is defined between all integer and floating point types. 28 | // 29 | // x must itself be an integer, floating point, or string type; otherwise, 30 | // LessThan will panic. 31 | func LessThan(x interface{}) Matcher { 32 | v := reflect.ValueOf(x) 33 | kind := v.Kind() 34 | 35 | switch { 36 | case isInteger(v): 37 | case isFloat(v): 38 | case kind == reflect.String: 39 | 40 | default: 41 | panic(fmt.Sprintf("LessThan: unexpected kind %v", kind)) 42 | } 43 | 44 | return &lessThanMatcher{v} 45 | } 46 | 47 | type lessThanMatcher struct { 48 | limit reflect.Value 49 | } 50 | 51 | func (m *lessThanMatcher) Description() string { 52 | // Special case: make it clear that strings are strings. 53 | if m.limit.Kind() == reflect.String { 54 | return fmt.Sprintf("less than \"%s\"", m.limit.String()) 55 | } 56 | 57 | return fmt.Sprintf("less than %v", m.limit.Interface()) 58 | } 59 | 60 | func compareIntegers(v1, v2 reflect.Value) (err error) { 61 | err = errors.New("") 62 | 63 | switch { 64 | case isSignedInteger(v1) && isSignedInteger(v2): 65 | if v1.Int() < v2.Int() { 66 | err = nil 67 | } 68 | return 69 | 70 | case isSignedInteger(v1) && isUnsignedInteger(v2): 71 | if v1.Int() < 0 || uint64(v1.Int()) < v2.Uint() { 72 | err = nil 73 | } 74 | return 75 | 76 | case isUnsignedInteger(v1) && isSignedInteger(v2): 77 | if v1.Uint() <= math.MaxInt64 && int64(v1.Uint()) < v2.Int() { 78 | err = nil 79 | } 80 | return 81 | 82 | case isUnsignedInteger(v1) && isUnsignedInteger(v2): 83 | if v1.Uint() < v2.Uint() { 84 | err = nil 85 | } 86 | return 87 | } 88 | 89 | panic(fmt.Sprintf("compareIntegers: %v %v", v1, v2)) 90 | } 91 | 92 | func getFloat(v reflect.Value) float64 { 93 | switch { 94 | case isSignedInteger(v): 95 | return float64(v.Int()) 96 | 97 | case isUnsignedInteger(v): 98 | return float64(v.Uint()) 99 | 100 | case isFloat(v): 101 | return v.Float() 102 | } 103 | 104 | panic(fmt.Sprintf("getFloat: %v", v)) 105 | } 106 | 107 | func (m *lessThanMatcher) Matches(c interface{}) (err error) { 108 | v1 := reflect.ValueOf(c) 109 | v2 := m.limit 110 | 111 | err = errors.New("") 112 | 113 | // Handle strings as a special case. 114 | if v1.Kind() == reflect.String && v2.Kind() == reflect.String { 115 | if v1.String() < v2.String() { 116 | err = nil 117 | } 118 | return 119 | } 120 | 121 | // If we get here, we require that we are dealing with integers or floats. 122 | v1Legal := isInteger(v1) || isFloat(v1) 123 | v2Legal := isInteger(v2) || isFloat(v2) 124 | if !v1Legal || !v2Legal { 125 | err = NewFatalError("which is not comparable") 126 | return 127 | } 128 | 129 | // Handle the various comparison cases. 130 | switch { 131 | // Both integers 132 | case isInteger(v1) && isInteger(v2): 133 | return compareIntegers(v1, v2) 134 | 135 | // At least one float32 136 | case v1.Kind() == reflect.Float32 || v2.Kind() == reflect.Float32: 137 | if float32(getFloat(v1)) < float32(getFloat(v2)) { 138 | err = nil 139 | } 140 | return 141 | 142 | // At least one float64 143 | case v1.Kind() == reflect.Float64 || v2.Kind() == reflect.Float64: 144 | if getFloat(v1) < getFloat(v2) { 145 | err = nil 146 | } 147 | return 148 | } 149 | 150 | // We shouldn't get here. 151 | panic(fmt.Sprintf("lessThanMatcher.Matches: Shouldn't get here: %v %v", v1, v2)) 152 | } 153 | -------------------------------------------------------------------------------- /_vendor/github.com/smartystreets/assertions/doc.go: -------------------------------------------------------------------------------- 1 | // Package assertions contains the implementations for all assertions which 2 | // are referenced in goconvey's `convey` package 3 | // (github.com/smartystreets/goconvey/convey) and gunit (github.com/smartystreets/gunit) 4 | // for use with the So(...) method. 5 | // They can also be used in traditional Go test functions and even in 6 | // applications. 7 | // 8 | // Many of the assertions lean heavily on work done by Aaron Jacobs in his excellent oglematchers library. 9 | // (https://github.com/jacobsa/oglematchers) 10 | // The ShouldResemble assertion leans heavily on work done by Daniel Jacques in his very helpful go-render library. 11 | // (https://github.com/luci/go-render) 12 | package assertions 13 | 14 | import ( 15 | "fmt" 16 | "runtime" 17 | ) 18 | 19 | // By default we use a no-op serializer. The actual Serializer provides a JSON 20 | // representation of failure results on selected assertions so the goconvey 21 | // web UI can display a convenient diff. 22 | var serializer Serializer = new(noopSerializer) 23 | 24 | // GoConveyMode provides control over JSON serialization of failures. When 25 | // using the assertions in this package from the convey package JSON results 26 | // are very helpful and can be rendered in a DIFF view. In that case, this function 27 | // will be called with a true value to enable the JSON serialization. By default, 28 | // the assertions in this package will not serializer a JSON result, making 29 | // standalone usage more convenient. 30 | func GoConveyMode(yes bool) { 31 | if yes { 32 | serializer = newSerializer() 33 | } else { 34 | serializer = new(noopSerializer) 35 | } 36 | } 37 | 38 | type testingT interface { 39 | Error(args ...interface{}) 40 | } 41 | 42 | type Assertion struct { 43 | t testingT 44 | failed bool 45 | } 46 | 47 | // New swallows the *testing.T struct and prints failed assertions using t.Error. 48 | // Example: assertions.New(t).So(1, should.Equal, 1) 49 | func New(t testingT) *Assertion { 50 | return &Assertion{t: t} 51 | } 52 | 53 | // Failed reports whether any calls to So (on this Assertion instance) have failed. 54 | func (this *Assertion) Failed() bool { 55 | return this.failed 56 | } 57 | 58 | // So calls the standalone So function and additionally, calls t.Error in failure scenarios. 59 | func (this *Assertion) So(actual interface{}, assert assertion, expected ...interface{}) bool { 60 | ok, result := So(actual, assert, expected...) 61 | if !ok { 62 | this.failed = true 63 | _, file, line, _ := runtime.Caller(1) 64 | this.t.Error(fmt.Sprintf("\n%s:%d\n%s", file, line, result)) 65 | } 66 | return ok 67 | } 68 | 69 | // So is a convenience function (as opposed to an inconvenience function?) 70 | // for running assertions on arbitrary arguments in any context, be it for testing or even 71 | // application logging. It allows you to perform assertion-like behavior (and get nicely 72 | // formatted messages detailing discrepancies) but without the program blowing up or panicking. 73 | // All that is required is to import this package and call `So` with one of the assertions 74 | // exported by this package as the second parameter. 75 | // The first return parameter is a boolean indicating if the assertion was true. The second 76 | // return parameter is the well-formatted message showing why an assertion was incorrect, or 77 | // blank if the assertion was correct. 78 | // 79 | // Example: 80 | // 81 | // if ok, message := So(x, ShouldBeGreaterThan, y); !ok { 82 | // log.Println(message) 83 | // } 84 | // 85 | func So(actual interface{}, assert assertion, expected ...interface{}) (bool, string) { 86 | if result := so(actual, assert, expected...); len(result) == 0 { 87 | return true, result 88 | } else { 89 | return false, result 90 | } 91 | } 92 | 93 | // so is like So, except that it only returns the string message, which is blank if the 94 | // assertion passed. Used to facilitate testing. 95 | func so(actual interface{}, assert func(interface{}, ...interface{}) string, expected ...interface{}) string { 96 | return assert(actual, expected...) 97 | } 98 | 99 | // assertion is an alias for a function with a signature that the So() 100 | // function can handle. Any future or custom assertions should conform to this 101 | // method signature. The return value should be an empty string if the assertion 102 | // passes and a well-formed failure message if not. 103 | type assertion func(actual interface{}, expected ...interface{}) string 104 | 105 | //////////////////////////////////////////////////////////////////////////// 106 | -------------------------------------------------------------------------------- /_vendor/github.com/smartystreets/assertions/type.go: -------------------------------------------------------------------------------- 1 | package assertions 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | ) 7 | 8 | // ShouldHaveSameTypeAs receives exactly two parameters and compares their underlying types for equality. 9 | func ShouldHaveSameTypeAs(actual interface{}, expected ...interface{}) string { 10 | if fail := need(1, expected); fail != success { 11 | return fail 12 | } 13 | 14 | first := reflect.TypeOf(actual) 15 | second := reflect.TypeOf(expected[0]) 16 | 17 | if equal := ShouldEqual(first, second); equal != success { 18 | return serializer.serialize(second, first, fmt.Sprintf(shouldHaveBeenA, actual, second, first)) 19 | } 20 | return success 21 | } 22 | 23 | // ShouldNotHaveSameTypeAs receives exactly two parameters and compares their underlying types for inequality. 24 | func ShouldNotHaveSameTypeAs(actual interface{}, expected ...interface{}) string { 25 | if fail := need(1, expected); fail != success { 26 | return fail 27 | } 28 | 29 | first := reflect.TypeOf(actual) 30 | second := reflect.TypeOf(expected[0]) 31 | 32 | if equal := ShouldEqual(first, second); equal == success { 33 | return fmt.Sprintf(shouldNotHaveBeenA, actual, second) 34 | } 35 | return success 36 | } 37 | 38 | // ShouldImplement receives exactly two parameters and ensures 39 | // that the first implements the interface type of the second. 40 | func ShouldImplement(actual interface{}, expectedList ...interface{}) string { 41 | if fail := need(1, expectedList); fail != success { 42 | return fail 43 | } 44 | 45 | expected := expectedList[0] 46 | if fail := ShouldBeNil(expected); fail != success { 47 | return shouldCompareWithInterfacePointer 48 | } 49 | 50 | if fail := ShouldNotBeNil(actual); fail != success { 51 | return shouldNotBeNilActual 52 | } 53 | 54 | var actualType reflect.Type 55 | if reflect.TypeOf(actual).Kind() != reflect.Ptr { 56 | actualType = reflect.PtrTo(reflect.TypeOf(actual)) 57 | } else { 58 | actualType = reflect.TypeOf(actual) 59 | } 60 | 61 | expectedType := reflect.TypeOf(expected) 62 | if fail := ShouldNotBeNil(expectedType); fail != success { 63 | return shouldCompareWithInterfacePointer 64 | } 65 | 66 | expectedInterface := expectedType.Elem() 67 | 68 | if !actualType.Implements(expectedInterface) { 69 | return fmt.Sprintf(shouldHaveImplemented, expectedInterface, actualType) 70 | } 71 | return success 72 | } 73 | 74 | // ShouldNotImplement receives exactly two parameters and ensures 75 | // that the first does NOT implement the interface type of the second. 76 | func ShouldNotImplement(actual interface{}, expectedList ...interface{}) string { 77 | if fail := need(1, expectedList); fail != success { 78 | return fail 79 | } 80 | 81 | expected := expectedList[0] 82 | if fail := ShouldBeNil(expected); fail != success { 83 | return shouldCompareWithInterfacePointer 84 | } 85 | 86 | if fail := ShouldNotBeNil(actual); fail != success { 87 | return shouldNotBeNilActual 88 | } 89 | 90 | var actualType reflect.Type 91 | if reflect.TypeOf(actual).Kind() != reflect.Ptr { 92 | actualType = reflect.PtrTo(reflect.TypeOf(actual)) 93 | } else { 94 | actualType = reflect.TypeOf(actual) 95 | } 96 | 97 | expectedType := reflect.TypeOf(expected) 98 | if fail := ShouldNotBeNil(expectedType); fail != success { 99 | return shouldCompareWithInterfacePointer 100 | } 101 | 102 | expectedInterface := expectedType.Elem() 103 | 104 | if actualType.Implements(expectedInterface) { 105 | return fmt.Sprintf(shouldNotHaveImplemented, actualType, expectedInterface) 106 | } 107 | return success 108 | } 109 | 110 | // ShouldBeError asserts that the first argument implements the error interface. 111 | // It also compares the first argument against the second argument if provided 112 | // (which must be an error message string or another error value). 113 | func ShouldBeError(actual interface{}, expected ...interface{}) string { 114 | if fail := atMost(1, expected); fail != success { 115 | return fail 116 | } 117 | 118 | if !isError(actual) { 119 | return fmt.Sprintf(shouldBeError, reflect.TypeOf(actual)) 120 | } 121 | 122 | if len(expected) == 0 { 123 | return success 124 | } 125 | 126 | if expected := expected[0]; !isString(expected) && !isError(expected) { 127 | return fmt.Sprintf(shouldBeErrorInvalidComparisonValue, reflect.TypeOf(expected)) 128 | } 129 | return ShouldEqual(fmt.Sprint(actual), fmt.Sprint(expected[0])) 130 | } 131 | 132 | func isString(value interface{}) bool { _, ok := value.(string); return ok } 133 | func isError(value interface{}) bool { _, ok := value.(error); return ok } 134 | -------------------------------------------------------------------------------- /_vendor/goji.io/router_trie.go: -------------------------------------------------------------------------------- 1 | // +build !goji_router_simple 2 | 3 | package goji 4 | 5 | import ( 6 | "net/http" 7 | "sort" 8 | "strings" 9 | 10 | "goji.io/internal" 11 | ) 12 | 13 | type router struct { 14 | routes []route 15 | methods map[string]*trieNode 16 | wildcard trieNode 17 | } 18 | 19 | type route struct { 20 | Pattern 21 | http.Handler 22 | } 23 | 24 | type child struct { 25 | prefix string 26 | node *trieNode 27 | } 28 | 29 | type trieNode struct { 30 | routes []int 31 | children []child 32 | } 33 | 34 | func (rt *router) add(p Pattern, h http.Handler) { 35 | i := len(rt.routes) 36 | rt.routes = append(rt.routes, route{p, h}) 37 | 38 | var prefix string 39 | if pp, ok := p.(pathPrefix); ok { 40 | prefix = pp.PathPrefix() 41 | } 42 | 43 | var methods map[string]struct{} 44 | if hm, ok := p.(httpMethods); ok { 45 | methods = hm.HTTPMethods() 46 | } 47 | if methods == nil { 48 | rt.wildcard.add(prefix, i) 49 | for _, sub := range rt.methods { 50 | sub.add(prefix, i) 51 | } 52 | } else { 53 | if rt.methods == nil { 54 | rt.methods = make(map[string]*trieNode) 55 | } 56 | 57 | for method := range methods { 58 | if _, ok := rt.methods[method]; !ok { 59 | rt.methods[method] = rt.wildcard.clone() 60 | } 61 | rt.methods[method].add(prefix, i) 62 | } 63 | } 64 | } 65 | 66 | func (rt *router) route(r *http.Request) *http.Request { 67 | tn := &rt.wildcard 68 | if tn2, ok := rt.methods[r.Method]; ok { 69 | tn = tn2 70 | } 71 | 72 | ctx := r.Context() 73 | path := ctx.Value(internal.Path).(string) 74 | for path != "" { 75 | i := sort.Search(len(tn.children), func(i int) bool { 76 | return path[0] <= tn.children[i].prefix[0] 77 | }) 78 | if i == len(tn.children) || !strings.HasPrefix(path, tn.children[i].prefix) { 79 | break 80 | } 81 | 82 | path = path[len(tn.children[i].prefix):] 83 | tn = tn.children[i].node 84 | } 85 | for _, i := range tn.routes { 86 | if r2 := rt.routes[i].Match(r); r2 != nil { 87 | return r2.WithContext(&match{ 88 | Context: r2.Context(), 89 | p: rt.routes[i].Pattern, 90 | h: rt.routes[i].Handler, 91 | }) 92 | } 93 | } 94 | return r.WithContext(&match{Context: ctx}) 95 | } 96 | 97 | // We can be a teensy bit more efficient here: we're maintaining a sorted list, 98 | // so we know exactly where to insert the new element. But since that involves 99 | // more bookkeeping and makes the code messier, let's cross that bridge when we 100 | // come to it. 101 | type byPrefix []child 102 | 103 | func (b byPrefix) Len() int { 104 | return len(b) 105 | } 106 | func (b byPrefix) Less(i, j int) bool { 107 | return b[i].prefix < b[j].prefix 108 | } 109 | func (b byPrefix) Swap(i, j int) { 110 | b[i], b[j] = b[j], b[i] 111 | } 112 | 113 | func longestPrefix(a, b string) string { 114 | mlen := len(a) 115 | if len(b) < mlen { 116 | mlen = len(b) 117 | } 118 | for i := 0; i < mlen; i++ { 119 | if a[i] != b[i] { 120 | return a[:i] 121 | } 122 | } 123 | return a[:mlen] 124 | } 125 | 126 | func (tn *trieNode) add(prefix string, idx int) { 127 | if len(prefix) == 0 { 128 | tn.routes = append(tn.routes, idx) 129 | for i := range tn.children { 130 | tn.children[i].node.add(prefix, idx) 131 | } 132 | return 133 | } 134 | 135 | ch := prefix[0] 136 | i := sort.Search(len(tn.children), func(i int) bool { 137 | return ch <= tn.children[i].prefix[0] 138 | }) 139 | 140 | if i == len(tn.children) || ch != tn.children[i].prefix[0] { 141 | routes := append([]int(nil), tn.routes...) 142 | tn.children = append(tn.children, child{ 143 | prefix: prefix, 144 | node: &trieNode{routes: append(routes, idx)}, 145 | }) 146 | } else { 147 | lp := longestPrefix(prefix, tn.children[i].prefix) 148 | 149 | if tn.children[i].prefix == lp { 150 | tn.children[i].node.add(prefix[len(lp):], idx) 151 | return 152 | } 153 | 154 | split := new(trieNode) 155 | split.children = []child{ 156 | {tn.children[i].prefix[len(lp):], tn.children[i].node}, 157 | } 158 | split.routes = append([]int(nil), tn.routes...) 159 | split.add(prefix[len(lp):], idx) 160 | 161 | tn.children[i].prefix = lp 162 | tn.children[i].node = split 163 | } 164 | 165 | sort.Sort(byPrefix(tn.children)) 166 | } 167 | 168 | func (tn *trieNode) clone() *trieNode { 169 | clone := new(trieNode) 170 | clone.routes = append(clone.routes, tn.routes...) 171 | clone.children = append(clone.children, tn.children...) 172 | for i := range clone.children { 173 | clone.children[i].node = tn.children[i].node.clone() 174 | } 175 | return clone 176 | } 177 | -------------------------------------------------------------------------------- /object_test.go: -------------------------------------------------------------------------------- 1 | package jsh 2 | 3 | import ( 4 | "encoding/json" 5 | "net/http" 6 | "net/http/httptest" 7 | "testing" 8 | 9 | . "github.com/smartystreets/goconvey/convey" 10 | ) 11 | 12 | func TestObject(t *testing.T) { 13 | 14 | Convey("Object Tests", t, func() { 15 | 16 | testObject := &Object{ 17 | ID: "ID123", 18 | Type: "testObject", 19 | Attributes: json.RawMessage(`{"foo":"bar"}`), 20 | } 21 | 22 | request := &http.Request{} 23 | 24 | Convey("->NewObject()", func() { 25 | 26 | Convey("should create a new object with populated attrs", func() { 27 | attrs := struct { 28 | Foo string `json:"foo"` 29 | }{"bar"} 30 | 31 | newObj, err := NewObject(testObject.ID, testObject.Type, attrs) 32 | So(err, ShouldBeNil) 33 | So(newObj.Attributes, ShouldNotBeEmpty) 34 | }) 35 | }) 36 | 37 | Convey("->Unmarshal()", func() { 38 | 39 | Convey("non-govalidator structs", func() { 40 | 41 | testConversion := struct { 42 | ID string 43 | Foo string `json:"foo"` 44 | }{} 45 | 46 | Convey("Should successfully populate a valid struct", func() { 47 | err := testObject.Unmarshal("testObject", &testConversion) 48 | So(err, ShouldBeNil) 49 | So(testConversion.Foo, ShouldEqual, "bar") 50 | }) 51 | 52 | Convey("Should reject a non-matching type", func() { 53 | err := testObject.Unmarshal("badType", &testConversion) 54 | So(err, ShouldNotBeNil) 55 | }) 56 | 57 | }) 58 | 59 | Convey("govalidator struct unmarshals", func() { 60 | 61 | Convey("should not error if input validates properly", func() { 62 | testValidation := struct { 63 | Foo string `json:"foo" valid:"alphanum"` 64 | }{} 65 | 66 | err := testObject.Unmarshal("testObject", &testValidation) 67 | So(err, ShouldBeNil) 68 | So(testValidation.Foo, ShouldEqual, "bar") 69 | }) 70 | 71 | Convey("should return a 422 Error correctly for a validation failure", func() { 72 | testValidation := struct { 73 | Foo string `valid:"ipv4,required" json:"foo"` 74 | }{} 75 | 76 | err := testObject.Unmarshal("testObject", &testValidation) 77 | So(err, ShouldNotBeNil) 78 | So(err[0].Source.Pointer, ShouldEqual, "/data/attributes/foo") 79 | }) 80 | 81 | Convey("should return a 422 Error correctly for multiple validation failures", func() { 82 | 83 | testManyObject := &Object{ 84 | ID: "ID123", 85 | Type: "testObject", 86 | Attributes: json.RawMessage(`{"foo":"bar", "baz":"4567"}`), 87 | } 88 | 89 | testManyValidations := struct { 90 | Foo string `valid:"ipv4,required" json:"foo"` 91 | Baz string `valid:"alpha,required" json:"baz"` 92 | }{} 93 | 94 | err := testManyObject.Unmarshal("testObject", &testManyValidations) 95 | So(err, ShouldNotBeNil) 96 | 97 | So(err[0].Source.Pointer, ShouldEqual, "/data/attributes/foo") 98 | So(err[1].Source.Pointer, ShouldEqual, "/data/attributes/baz") 99 | }) 100 | }) 101 | }) 102 | 103 | Convey("->Marshal()", func() { 104 | 105 | Convey("should properly update attributes", func() { 106 | attrs := map[string]string{"foo": "baz"} 107 | err := testObject.Marshal(attrs) 108 | So(err, ShouldBeNil) 109 | 110 | raw, jsonErr := json.MarshalIndent(attrs, "", " ") 111 | So(jsonErr, ShouldBeNil) 112 | So(string(testObject.Attributes), ShouldEqual, string(raw)) 113 | }) 114 | }) 115 | 116 | Convey("->JSON()", func() { 117 | 118 | Convey("should handle a POST response correctly", func() { 119 | request.Method = "POST" 120 | err := testObject.Validate(request, true) 121 | So(err, ShouldBeNil) 122 | So(testObject.Status, ShouldEqual, http.StatusCreated) 123 | }) 124 | 125 | Convey("should handle a GET response correctly", func() { 126 | request.Method = "GET" 127 | err := testObject.Validate(request, true) 128 | So(err, ShouldBeNil) 129 | So(testObject.Status, ShouldEqual, http.StatusOK) 130 | }) 131 | 132 | Convey("should handle a PATCH response correctly", func() { 133 | request.Method = "PATCH" 134 | err := testObject.Validate(request, true) 135 | So(err, ShouldBeNil) 136 | So(testObject.Status, ShouldEqual, http.StatusOK) 137 | }) 138 | 139 | Convey("should return a formatted Error for an unsupported method Type", func() { 140 | request.Method = "PUT" 141 | err := testObject.Validate(request, true) 142 | So(err, ShouldNotBeNil) 143 | So(err.Status, ShouldEqual, http.StatusNotAcceptable) 144 | }) 145 | }) 146 | 147 | Convey("->Send(Object)", func() { 148 | request.Method = "POST" 149 | writer := httptest.NewRecorder() 150 | err := Send(writer, request, testObject) 151 | So(err, ShouldBeNil) 152 | So(writer.Code, ShouldEqual, http.StatusCreated) 153 | }) 154 | }) 155 | } 156 | -------------------------------------------------------------------------------- /parser.go: -------------------------------------------------------------------------------- 1 | package jsh 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io" 7 | "log" 8 | "net/http" 9 | ) 10 | 11 | /* 12 | ParseObject validates the HTTP request and returns a JSON object for a given 13 | io.ReadCloser containing a raw JSON payload. Here's an example of how to use it 14 | as part of your full flow. 15 | 16 | func Handler(w http.ResponseWriter, r *http.Request) { 17 | obj, error := jsh.ParseObject(r) 18 | if error != nil { 19 | // log your error 20 | err := jsh.Send(w, r, error) 21 | return 22 | } 23 | 24 | yourType := &YourType{} 25 | 26 | err := object.Unmarshal("yourtype", &yourType) 27 | if err != nil { 28 | err := jsh.Send(w, r, err) 29 | return 30 | } 31 | 32 | yourType.ID = obj.ID 33 | // do business logic 34 | 35 | err := object.Marshal(yourType) 36 | if err != nil { 37 | // log error 38 | err := jsh.Send(w, r, err) 39 | return 40 | } 41 | 42 | err := jsh.Send(w, r, object) 43 | } 44 | */ 45 | func ParseObject(r *http.Request) (*Object, *Error) { 46 | document, err := ParseDoc(r, ObjectMode) 47 | if err != nil { 48 | return nil, err 49 | } 50 | 51 | if !document.HasData() { 52 | return nil, nil 53 | } 54 | 55 | object := document.First() 56 | if r.Method != "POST" && object.ID == "" { 57 | return nil, InputError("Missing mandatory object attribute", "id") 58 | } 59 | 60 | return object, nil 61 | } 62 | 63 | /* 64 | ParseList validates the HTTP request and returns a resulting list of objects 65 | parsed from the request Body. Use just like ParseObject. 66 | */ 67 | func ParseList(r *http.Request) (List, *Error) { 68 | document, err := ParseDoc(r, ListMode) 69 | if err != nil { 70 | return nil, err 71 | } 72 | 73 | return document.Data, nil 74 | } 75 | 76 | // MaxContentLength is 10MB 77 | // https://github.com/golang/go/blob/abb3c0618b658a41bf91a087f1737412e93ff6d9/src/pkg/net/http/request.go#L617 78 | const MaxContentLength int64 = 10 << 20 79 | 80 | /* 81 | ParseDoc parses and returns a top level jsh.Document. In most cases, using 82 | "ParseList" or "ParseObject" is preferable. 83 | */ 84 | func ParseDoc(r *http.Request, mode DocumentMode) (*Document, *Error) { 85 | return NewParser(r).Document(r.Body, mode) 86 | } 87 | 88 | // Parser is an abstraction layer that helps to support parsing JSON payload from 89 | // many types of sources, and allows other libraries to leverage this if desired. 90 | type Parser struct { 91 | Method string 92 | Headers http.Header 93 | } 94 | 95 | // NewParser creates a parser from an http.Request 96 | func NewParser(request *http.Request) *Parser { 97 | return &Parser{ 98 | Method: request.Method, 99 | Headers: request.Header, 100 | } 101 | } 102 | 103 | /* 104 | Document returns a single JSON data object from the parser. In the process it will 105 | also validate any data objects against the JSON API. 106 | */ 107 | func (p *Parser) Document(payload io.ReadCloser, mode DocumentMode) (*Document, *Error) { 108 | defer closeReader(payload) 109 | 110 | err := validateHeaders(p.Headers) 111 | if err != nil { 112 | return nil, err 113 | } 114 | 115 | document := &Document{ 116 | Data: List{}, 117 | Mode: mode, 118 | } 119 | 120 | decodeErr := json.NewDecoder(io.LimitReader(payload, MaxContentLength)).Decode(document) 121 | if decodeErr != nil { 122 | return nil, ISE(fmt.Sprintf("Error parsing JSON Document: %s", decodeErr.Error())) 123 | } 124 | 125 | // If the document has data, validate against specification 126 | if document.HasData() { 127 | for _, object := range document.Data { 128 | 129 | // TODO: currently this doesn't really do any user input 130 | // validation since it is validating against the jsh 131 | // "Object" type. Figure out how to options pass the 132 | // corressponding user object struct in to enable this 133 | // without making the API super clumsy. 134 | inputErr := validateInput(object) 135 | if inputErr != nil { 136 | return nil, inputErr[0] 137 | } 138 | 139 | // if we have a list, then all resource objects should have IDs, will 140 | // cross the bridge of bulk creation if and when there is a use case 141 | if len(document.Data) > 1 && object.ID == "" { 142 | return nil, InputError("Object without ID present in list", "id") 143 | } 144 | } 145 | } 146 | 147 | return document, nil 148 | } 149 | 150 | /* 151 | closeReader is a deferal helper function for closing a reader and logging any errors that might occur after the fact. 152 | */ 153 | func closeReader(reader io.ReadCloser) { 154 | err := reader.Close() 155 | if err != nil { 156 | log.Println("Unable to close request Body") 157 | } 158 | } 159 | 160 | func validateHeaders(headers http.Header) *Error { 161 | 162 | reqContentType := headers.Get("Content-Type") 163 | if reqContentType != ContentType { 164 | return SpecificationError(fmt.Sprintf( 165 | "Expected Content-Type header to be %s, got: %s", 166 | ContentType, 167 | reqContentType, 168 | )) 169 | } 170 | 171 | return nil 172 | } 173 | -------------------------------------------------------------------------------- /_vendor/github.com/jtolds/gls/context.go: -------------------------------------------------------------------------------- 1 | // Package gls implements goroutine-local storage. 2 | package gls 3 | 4 | import ( 5 | "runtime" 6 | "sync" 7 | ) 8 | 9 | const ( 10 | maxCallers = 64 11 | ) 12 | 13 | var ( 14 | stackTagPool = &idPool{} 15 | mgrRegistry = make(map[*ContextManager]bool) 16 | mgrRegistryMtx sync.RWMutex 17 | ) 18 | 19 | // Values is simply a map of key types to value types. Used by SetValues to 20 | // set multiple values at once. 21 | type Values map[interface{}]interface{} 22 | 23 | func currentStack(skip int) []uintptr { 24 | stack := make([]uintptr, maxCallers) 25 | return stack[:runtime.Callers(2+skip, stack)] 26 | } 27 | 28 | // ContextManager is the main entrypoint for interacting with 29 | // Goroutine-local-storage. You can have multiple independent ContextManagers 30 | // at any given time. ContextManagers are usually declared globally for a given 31 | // class of context variables. You should use NewContextManager for 32 | // construction. 33 | type ContextManager struct { 34 | mtx sync.RWMutex 35 | values map[uint]Values 36 | } 37 | 38 | // NewContextManager returns a brand new ContextManager. It also registers the 39 | // new ContextManager in the ContextManager registry which is used by the Go 40 | // method. ContextManagers are typically defined globally at package scope. 41 | func NewContextManager() *ContextManager { 42 | mgr := &ContextManager{values: make(map[uint]Values)} 43 | mgrRegistryMtx.Lock() 44 | defer mgrRegistryMtx.Unlock() 45 | mgrRegistry[mgr] = true 46 | return mgr 47 | } 48 | 49 | // Unregister removes a ContextManager from the global registry, used by the 50 | // Go method. Only intended for use when you're completely done with a 51 | // ContextManager. Use of Unregister at all is rare. 52 | func (m *ContextManager) Unregister() { 53 | mgrRegistryMtx.Lock() 54 | defer mgrRegistryMtx.Unlock() 55 | delete(mgrRegistry, m) 56 | } 57 | 58 | // SetValues takes a collection of values and a function to call for those 59 | // values to be set in. Anything further down the stack will have the set 60 | // values available through GetValue. SetValues will add new values or replace 61 | // existing values of the same key and will not mutate or change values for 62 | // previous stack frames. 63 | // SetValues is slow (makes a copy of all current and new values for the new 64 | // gls-context) in order to reduce the amount of lookups GetValue requires. 65 | func (m *ContextManager) SetValues(new_values Values, context_call func()) { 66 | if len(new_values) == 0 { 67 | context_call() 68 | return 69 | } 70 | 71 | tags := readStackTags(currentStack(1)) 72 | 73 | m.mtx.Lock() 74 | values := new_values 75 | for _, tag := range tags { 76 | if existing_values, ok := m.values[tag]; ok { 77 | // oh, we found existing values, let's make a copy 78 | values = make(Values, len(existing_values)+len(new_values)) 79 | for key, val := range existing_values { 80 | values[key] = val 81 | } 82 | for key, val := range new_values { 83 | values[key] = val 84 | } 85 | break 86 | } 87 | } 88 | new_tag := stackTagPool.Acquire() 89 | m.values[new_tag] = values 90 | m.mtx.Unlock() 91 | defer func() { 92 | m.mtx.Lock() 93 | delete(m.values, new_tag) 94 | m.mtx.Unlock() 95 | stackTagPool.Release(new_tag) 96 | }() 97 | 98 | addStackTag(new_tag, context_call) 99 | } 100 | 101 | // GetValue will return a previously set value, provided that the value was set 102 | // by SetValues somewhere higher up the stack. If the value is not found, ok 103 | // will be false. 104 | func (m *ContextManager) GetValue(key interface{}) (value interface{}, ok bool) { 105 | 106 | tags := readStackTags(currentStack(1)) 107 | m.mtx.RLock() 108 | defer m.mtx.RUnlock() 109 | for _, tag := range tags { 110 | if values, ok := m.values[tag]; ok { 111 | value, ok := values[key] 112 | return value, ok 113 | } 114 | } 115 | return "", false 116 | } 117 | 118 | func (m *ContextManager) getValues() Values { 119 | tags := readStackTags(currentStack(2)) 120 | m.mtx.RLock() 121 | defer m.mtx.RUnlock() 122 | for _, tag := range tags { 123 | if values, ok := m.values[tag]; ok { 124 | return values 125 | } 126 | } 127 | return nil 128 | } 129 | 130 | // Go preserves ContextManager values and Goroutine-local-storage across new 131 | // goroutine invocations. The Go method makes a copy of all existing values on 132 | // all registered context managers and makes sure they are still set after 133 | // kicking off the provided function in a new goroutine. If you don't use this 134 | // Go method instead of the standard 'go' keyword, you will lose values in 135 | // ContextManagers, as goroutines have brand new stacks. 136 | func Go(cb func()) { 137 | mgrRegistryMtx.RLock() 138 | defer mgrRegistryMtx.RUnlock() 139 | 140 | for mgr, _ := range mgrRegistry { 141 | values := mgr.getValues() 142 | if len(values) > 0 { 143 | mgr_copy := mgr 144 | cb_copy := cb 145 | cb = func() { mgr_copy.SetValues(values, cb_copy) } 146 | } 147 | } 148 | 149 | go cb() 150 | } 151 | --------------------------------------------------------------------------------