Remove common/http
This commit is contained in:
parent
f2a8e95248
commit
797bcdd9e0
153
common/http.go
153
common/http.go
|
@ -1,153 +0,0 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"gopkg.in/go-playground/validator.v9"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type ErrorResponse struct {
|
||||
Success bool `json:"success,omitempty"`
|
||||
|
||||
// Err is the error message if Success is false
|
||||
Err string `json:"error,omitempty"`
|
||||
|
||||
// Code is set if Success is false
|
||||
Code int `json:"code,omitempty"`
|
||||
}
|
||||
|
||||
// ErrorWithCode makes an ErrorResponse with the
|
||||
// provided err's Error() content, and status code.
|
||||
// It panics if err is nil.
|
||||
func ErrorWithCode(err error, code int) *ErrorResponse {
|
||||
return &ErrorResponse{
|
||||
Err: err.Error(),
|
||||
Code: code,
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that ErrorResponse implements error
|
||||
var _ error = (*ErrorResponse)(nil)
|
||||
|
||||
func (er *ErrorResponse) Error() string {
|
||||
return er.Err
|
||||
}
|
||||
|
||||
// Ensure that ErrorResponse implements httpCoder
|
||||
var _ httpCoder = (*ErrorResponse)(nil)
|
||||
|
||||
func (er *ErrorResponse) HTTPCode() int {
|
||||
return er.Code
|
||||
}
|
||||
|
||||
var errNilBody = errors.Errorf("expecting a non-nil body")
|
||||
|
||||
// FparseJSON unmarshals into save, the body of the provided reader.
|
||||
// Since it uses json.Unmarshal, save must be of a pointer type
|
||||
// or compatible with json.Unmarshal.
|
||||
func FparseJSON(r io.Reader, save interface{}) error {
|
||||
if r == nil {
|
||||
return errors.Wrap(errNilBody, "Reader")
|
||||
}
|
||||
|
||||
dec := json.NewDecoder(r)
|
||||
if err := dec.Decode(save); err != nil {
|
||||
return errors.Wrap(err, "Decode/Unmarshal")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ParseRequestJSON unmarshals into save, the body of the
|
||||
// request. It closes the body of the request after parsing.
|
||||
// Since it uses json.Unmarshal, save must be of a pointer type
|
||||
// or compatible with json.Unmarshal.
|
||||
func ParseRequestJSON(r *http.Request, save interface{}) error {
|
||||
if r == nil || r.Body == nil {
|
||||
return errNilBody
|
||||
}
|
||||
defer r.Body.Close()
|
||||
|
||||
return FparseJSON(r.Body, save)
|
||||
}
|
||||
|
||||
// ParseRequestAndValidateJSON unmarshals into save, the body of the
|
||||
// request and invokes a validator on the saved content. To ensure
|
||||
// validation, make sure to set tags "validate" on your struct as
|
||||
// per https://godoc.org/gopkg.in/go-playground/validator.v9.
|
||||
// It closes the body of the request after parsing.
|
||||
// Since it uses json.Unmarshal, save must be of a pointer type
|
||||
// or compatible with json.Unmarshal.
|
||||
func ParseRequestAndValidateJSON(r *http.Request, save interface{}) error {
|
||||
if r == nil || r.Body == nil {
|
||||
return errNilBody
|
||||
}
|
||||
defer r.Body.Close()
|
||||
|
||||
return FparseAndValidateJSON(r.Body, save)
|
||||
}
|
||||
|
||||
// FparseAndValidateJSON like FparseJSON unmarshals into save,
|
||||
// the body of the provided reader. However, it invokes the validator
|
||||
// to check the set validators on your struct fields as per
|
||||
// per https://godoc.org/gopkg.in/go-playground/validator.v9.
|
||||
// Since it uses json.Unmarshal, save must be of a pointer type
|
||||
// or compatible with json.Unmarshal.
|
||||
func FparseAndValidateJSON(r io.Reader, save interface{}) error {
|
||||
if err := FparseJSON(r, save); err != nil {
|
||||
return err
|
||||
}
|
||||
return validate(save)
|
||||
}
|
||||
|
||||
var theValidator = validator.New()
|
||||
|
||||
func validate(obj interface{}) error {
|
||||
return errors.Wrap(theValidator.Struct(obj), "Validate")
|
||||
}
|
||||
|
||||
// WriteSuccess JSON marshals the content provided, to an HTTP
|
||||
// response, setting the provided status code and setting header
|
||||
// "Content-Type" to "application/json".
|
||||
func WriteSuccess(w http.ResponseWriter, data interface{}) {
|
||||
WriteCode(w, data, 200)
|
||||
}
|
||||
|
||||
// WriteCode JSON marshals content, to an HTTP response,
|
||||
// setting the provided status code, and setting header
|
||||
// "Content-Type" to "application/json". If JSON marshalling fails
|
||||
// with an error, WriteCode instead writes out the error invoking
|
||||
// WriteError.
|
||||
func WriteCode(w http.ResponseWriter, out interface{}, code int) {
|
||||
blob, err := json.MarshalIndent(out, "", " ")
|
||||
if err != nil {
|
||||
WriteError(w, err)
|
||||
} else {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(code)
|
||||
w.Write(blob)
|
||||
}
|
||||
}
|
||||
|
||||
type httpCoder interface {
|
||||
HTTPCode() int
|
||||
}
|
||||
|
||||
// WriteError is a convenience function to write out an
|
||||
// error to an http.ResponseWriter, to send out an error
|
||||
// that's structured as JSON i.e the form
|
||||
// {"error": sss, "code": ddd}
|
||||
// If err implements the interface HTTPCode() int,
|
||||
// it will use that status code otherwise, it will
|
||||
// set code to be http.StatusBadRequest
|
||||
func WriteError(w http.ResponseWriter, err error) {
|
||||
code := http.StatusBadRequest
|
||||
if httpC, ok := err.(httpCoder); ok {
|
||||
code = httpC.HTTPCode()
|
||||
}
|
||||
|
||||
WriteCode(w, ErrorWithCode(err, code), code)
|
||||
}
|
|
@ -1,250 +0,0 @@
|
|||
package common_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/tendermint/tmlibs/common"
|
||||
)
|
||||
|
||||
func TestWriteSuccess(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
common.WriteSuccess(w, "foo")
|
||||
assert.Equal(t, w.Code, 200, "should get a 200")
|
||||
}
|
||||
|
||||
var blankErrResponse = new(common.ErrorResponse)
|
||||
|
||||
func TestWriteError(t *testing.T) {
|
||||
tests := [...]struct {
|
||||
msg string
|
||||
code int
|
||||
}{
|
||||
0: {
|
||||
msg: "this is a message",
|
||||
code: 419,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
w := httptest.NewRecorder()
|
||||
msg := tt.msg
|
||||
|
||||
// First check without a defined code, should send back a 400
|
||||
common.WriteError(w, errors.New(msg))
|
||||
assert.Equal(t, w.Code, http.StatusBadRequest, "#%d: should get a 400", i)
|
||||
blob, err := ioutil.ReadAll(w.Body)
|
||||
if err != nil {
|
||||
assert.Fail(t, "expecting a successful ioutil.ReadAll", "#%d", i)
|
||||
continue
|
||||
}
|
||||
|
||||
recv := new(common.ErrorResponse)
|
||||
if err := json.Unmarshal(blob, recv); err != nil {
|
||||
assert.Fail(t, "expecting a successful json.Unmarshal", "#%d", i)
|
||||
continue
|
||||
}
|
||||
|
||||
assert.Equal(t, reflect.DeepEqual(recv, blankErrResponse), false, "expecting a non-blank error response")
|
||||
|
||||
// Now test with an error that's .HTTPCode() int conforming
|
||||
|
||||
// Reset w
|
||||
w = httptest.NewRecorder()
|
||||
|
||||
common.WriteError(w, common.ErrorWithCode(errors.New("foo"), tt.code))
|
||||
assert.Equal(t, w.Code, tt.code, "case #%d", i)
|
||||
}
|
||||
}
|
||||
|
||||
type marshalFailer struct{}
|
||||
|
||||
var errFooFailed = errors.New("foo failed here")
|
||||
|
||||
func (mf *marshalFailer) MarshalJSON() ([]byte, error) {
|
||||
return nil, errFooFailed
|
||||
}
|
||||
|
||||
func TestWriteCode(t *testing.T) {
|
||||
codes := [...]int{
|
||||
0: http.StatusOK,
|
||||
1: http.StatusBadRequest,
|
||||
2: http.StatusUnauthorized,
|
||||
3: http.StatusInternalServerError,
|
||||
}
|
||||
|
||||
for i, code := range codes {
|
||||
w := httptest.NewRecorder()
|
||||
common.WriteCode(w, "foo", code)
|
||||
assert.Equal(t, w.Code, code, "#%d", i)
|
||||
|
||||
// Then for the failed JSON marshaling
|
||||
w = httptest.NewRecorder()
|
||||
common.WriteCode(w, &marshalFailer{}, code)
|
||||
wantCode := http.StatusBadRequest
|
||||
assert.Equal(t, w.Code, wantCode, "#%d", i)
|
||||
assert.True(t, strings.Contains(w.Body.String(), errFooFailed.Error()),
|
||||
"#%d: expected %q in the error message", i, errFooFailed)
|
||||
}
|
||||
}
|
||||
|
||||
type saver struct {
|
||||
Foo int `json:"foo" validate:"min=10"`
|
||||
Bar string `json:"bar"`
|
||||
}
|
||||
|
||||
type rcloser struct {
|
||||
closeOnce sync.Once
|
||||
body *bytes.Buffer
|
||||
closeChan chan bool
|
||||
}
|
||||
|
||||
var errAlreadyClosed = errors.New("already closed")
|
||||
|
||||
func (rc *rcloser) Close() error {
|
||||
var err = errAlreadyClosed
|
||||
rc.closeOnce.Do(func() {
|
||||
err = nil
|
||||
rc.closeChan <- true
|
||||
close(rc.closeChan)
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (rc *rcloser) Read(b []byte) (int, error) {
|
||||
return rc.body.Read(b)
|
||||
}
|
||||
|
||||
var _ io.ReadCloser = (*rcloser)(nil)
|
||||
|
||||
func makeReq(strBody string) (*http.Request, <-chan bool) {
|
||||
closeChan := make(chan bool, 1)
|
||||
buf := new(bytes.Buffer)
|
||||
buf.Write([]byte(strBody))
|
||||
req := &http.Request{
|
||||
Header: make(http.Header),
|
||||
Body: &rcloser{body: buf, closeChan: closeChan},
|
||||
}
|
||||
return req, closeChan
|
||||
}
|
||||
|
||||
func TestParseRequestJSON(t *testing.T) {
|
||||
tests := [...]struct {
|
||||
body string
|
||||
wantErr bool
|
||||
useNil bool
|
||||
}{
|
||||
0: {wantErr: true, body: ``},
|
||||
1: {body: `{}`},
|
||||
2: {body: `{"foo": 2}`}, // Not that the validate tags don't matter here since we are just parsing
|
||||
3: {body: `{"foo": "abcd"}`, wantErr: true},
|
||||
4: {useNil: true, wantErr: true},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
req, closeChan := makeReq(tt.body)
|
||||
if tt.useNil {
|
||||
req.Body = nil
|
||||
}
|
||||
sav := new(saver)
|
||||
err := common.ParseRequestJSON(req, sav)
|
||||
if tt.wantErr {
|
||||
assert.NotEqual(t, err, nil, "#%d: want non-nil error", i)
|
||||
continue
|
||||
}
|
||||
assert.Equal(t, err, nil, "#%d: want nil error", i)
|
||||
wasClosed := <-closeChan
|
||||
assert.Equal(t, wasClosed, true, "#%d: should have invoked close", i)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFparseJSON(t *testing.T) {
|
||||
r1 := strings.NewReader(`{"foo": 1}`)
|
||||
sav := new(saver)
|
||||
require.Equal(t, common.FparseJSON(r1, sav), nil, "expecting successful parsing")
|
||||
r2 := strings.NewReader(`{"bar": "blockchain"}`)
|
||||
require.Equal(t, common.FparseJSON(r2, sav), nil, "expecting successful parsing")
|
||||
require.Equal(t, reflect.DeepEqual(sav, &saver{Foo: 1, Bar: "blockchain"}), true, "should have parsed both")
|
||||
|
||||
// Now with a nil body
|
||||
require.NotEqual(t, nil, common.FparseJSON(nil, sav), "expecting a nil error report")
|
||||
}
|
||||
|
||||
func TestFparseAndValidateJSON(t *testing.T) {
|
||||
r1 := strings.NewReader(`{"foo": 1}`)
|
||||
sav := new(saver)
|
||||
require.NotEqual(t, common.FparseAndValidateJSON(r1, sav), nil, "expecting validation to fail")
|
||||
r1 = strings.NewReader(`{"foo": 100}`)
|
||||
require.Equal(t, common.FparseJSON(r1, sav), nil, "expecting successful parsing")
|
||||
r2 := strings.NewReader(`{"bar": "blockchain"}`)
|
||||
require.Equal(t, common.FparseAndValidateJSON(r2, sav), nil, "expecting successful parsing")
|
||||
require.Equal(t, reflect.DeepEqual(sav, &saver{Foo: 100, Bar: "blockchain"}), true, "should have parsed both")
|
||||
|
||||
// Now with a nil body
|
||||
require.NotEqual(t, nil, common.FparseJSON(nil, sav), "expecting a nil error report")
|
||||
}
|
||||
|
||||
var blankSaver = new(saver)
|
||||
|
||||
func TestParseAndValidateRequestJSON(t *testing.T) {
|
||||
tests := [...]struct {
|
||||
body string
|
||||
wantErr bool
|
||||
useNil bool
|
||||
}{
|
||||
0: {wantErr: true, body: ``},
|
||||
1: {body: `{}`, wantErr: true}, // Here it should fail since Foo doesn't meet the minimum value
|
||||
2: {body: `{"foo": 2}`, wantErr: true}, // Here validation should fail
|
||||
3: {body: `{"foo": "abcd"}`, wantErr: true},
|
||||
4: {useNil: true, wantErr: true},
|
||||
5: {body: `{"foo": 100}`}, // Must succeed
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
req, closeChan := makeReq(tt.body)
|
||||
if tt.useNil {
|
||||
req.Body = nil
|
||||
}
|
||||
sav := new(saver)
|
||||
err := common.ParseRequestAndValidateJSON(req, sav)
|
||||
if tt.wantErr {
|
||||
assert.NotEqual(t, err, nil, "#%d: want non-nil error", i)
|
||||
continue
|
||||
}
|
||||
|
||||
assert.Equal(t, err, nil, "#%d: want nil error", i)
|
||||
assert.False(t, reflect.DeepEqual(blankSaver, sav), "#%d: expecting a set saver", i)
|
||||
|
||||
wasClosed := <-closeChan
|
||||
assert.Equal(t, wasClosed, true, "#%d: should have invoked close", i)
|
||||
}
|
||||
}
|
||||
|
||||
func TestErrorWithCode(t *testing.T) {
|
||||
tests := [...]struct {
|
||||
code int
|
||||
err error
|
||||
}{
|
||||
0: {code: 500, err: errors.New("funky")},
|
||||
1: {code: 406, err: errors.New("purist")},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
errRes := common.ErrorWithCode(tt.err, tt.code)
|
||||
assert.Equal(t, errRes.Error(), tt.err.Error(), "#%d: expecting the error values to be equal", i)
|
||||
assert.Equal(t, errRes.Code, tt.code, "expecting the same status code", i)
|
||||
assert.Equal(t, errRes.HTTPCode(), tt.code, "expecting the same status code", i)
|
||||
}
|
||||
}
|
|
@ -1,24 +1,18 @@
|
|||
hash: 6efda1f3891a7211fc3dc1499c0079267868ced9739b781928af8e225420f867
|
||||
updated: 2017-12-19T20:38:52.947516911-08:00
|
||||
hash: 325b2f9c7e84696f88fa88126a22eb1e1e91c2be5f60402d17bfaad6713b33c2
|
||||
updated: 2017-12-25T17:45:52.357002873-08:00
|
||||
imports:
|
||||
- name: github.com/fsnotify/fsnotify
|
||||
version: 4da3e2cfbabc9f751898f250b49f2439785783a1
|
||||
- name: github.com/go-kit/kit
|
||||
version: e2b298466b32c7cd5579a9b9b07e968fc9d9452c
|
||||
version: e3b2152e0063c5f05efea89ecbe297852af2a92d
|
||||
subpackages:
|
||||
- log
|
||||
- log/level
|
||||
- log/term
|
||||
- name: github.com/go-logfmt/logfmt
|
||||
version: 390ab7935ee28ec6b286364bba9b4dd6410cb3d5
|
||||
- name: github.com/go-playground/locales
|
||||
version: e4cbcb5d0652150d40ad0646651076b6bd2be4f6
|
||||
subpackages:
|
||||
- currency
|
||||
- name: github.com/go-playground/universal-translator
|
||||
version: 71201497bace774495daed26a3874fd339e0b538
|
||||
- name: github.com/go-stack/stack
|
||||
version: 817915b46b97fd7bb80e8ab6b69f01a53ac3eebf
|
||||
version: 259ab82a6cad3992b4e21ff5cac294ccb06474bc
|
||||
- name: github.com/golang/snappy
|
||||
version: 553a641470496b2327abcac10b36396bd98e45c9
|
||||
- name: github.com/hashicorp/hcl
|
||||
|
@ -51,7 +45,7 @@ imports:
|
|||
- name: github.com/pelletier/go-toml
|
||||
version: 13d49d4606eb801b8f01ae542b4afc4c6ee3d84a
|
||||
- name: github.com/pkg/errors
|
||||
version: 645ef00459ed84a119197bfb8d8205042c6df63d
|
||||
version: f15c970de5b76fac0b59abb32d62c17cc7bed265
|
||||
- name: github.com/spf13/afero
|
||||
version: 5660eeed305fe5f69c8fc6cf899132a459a97064
|
||||
subpackages:
|
||||
|
@ -63,11 +57,11 @@ imports:
|
|||
- name: github.com/spf13/jwalterweatherman
|
||||
version: 12bd96e66386c1960ab0f74ced1362f66f552f7b
|
||||
- name: github.com/spf13/pflag
|
||||
version: 97afa5e7ca8a08a383cb259e06636b5e2cc7897f
|
||||
version: 4c012f6dcd9546820e378d0bdda4d8fc772cdfea
|
||||
- name: github.com/spf13/viper
|
||||
version: 8ef37cbca71638bf32f3d5e194117d4cb46da163
|
||||
- name: github.com/syndtr/goleveldb
|
||||
version: b89cc31ef7977104127d34c1bd31ebd1a9db2199
|
||||
version: adf24ef3f94bd13ec4163060b21a5678f22b429b
|
||||
subpackages:
|
||||
- leveldb
|
||||
- leveldb/cache
|
||||
|
@ -82,7 +76,7 @@ imports:
|
|||
- leveldb/table
|
||||
- leveldb/util
|
||||
- name: github.com/tendermint/go-wire
|
||||
version: 27be46e25124ddf775e23317a83647ce62a93f6b
|
||||
version: 5ab49b4c6ad674da6b81442911cf713ef0afb544
|
||||
subpackages:
|
||||
- data
|
||||
- data/base58
|
||||
|
@ -91,7 +85,7 @@ imports:
|
|||
subpackages:
|
||||
- term
|
||||
- name: golang.org/x/crypto
|
||||
version: edd5e9b0879d13ee6970a50153d85b8fec9f7686
|
||||
version: 94eea52f7b742c7cbe0b03b22f0c4c8631ece122
|
||||
subpackages:
|
||||
- ripemd160
|
||||
- name: golang.org/x/sys
|
||||
|
@ -99,12 +93,10 @@ imports:
|
|||
subpackages:
|
||||
- unix
|
||||
- name: golang.org/x/text
|
||||
version: c01e4764d870b77f8abe5096ee19ad20d80e8075
|
||||
version: 75cc3cad82b5f47d3fb229ddda8c5167da14f294
|
||||
subpackages:
|
||||
- transform
|
||||
- unicode/norm
|
||||
- name: gopkg.in/go-playground/validator.v9
|
||||
version: 1304298bf10d085adec514b076772a79c9cadb6b
|
||||
- name: gopkg.in/yaml.v2
|
||||
version: eb3733d160e74a9c7e442f435eb3bea458e1d19f
|
||||
testImports:
|
||||
|
|
|
@ -23,7 +23,6 @@ import:
|
|||
- package: golang.org/x/crypto
|
||||
subpackages:
|
||||
- ripemd160
|
||||
- package: gopkg.in/go-playground/validator.v9
|
||||
testImport:
|
||||
- package: github.com/stretchr/testify
|
||||
version: ^1.1.4
|
||||
|
|
Loading…
Reference in New Issue