Skip to content

Commit

Permalink
Fix JSONReader integration with client
Browse files Browse the repository at this point in the history
This patch updates the esapi.newRequest() function to handle body passed as esutil.JSONReader properly.

It makes the esutil.JSONReader exported, and renames the constructor function to esutil.NewJSONReader().

It also adds proper integration tests for esutil.JSONReader.
  • Loading branch information
karmi committed May 10, 2019
1 parent 04312bb commit 82fc17d
Show file tree
Hide file tree
Showing 6 changed files with 146 additions and 14 deletions.
55 changes: 55 additions & 0 deletions _examples/encoding/jsonreader.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package main

import (
"log"

"github.com/elastic/go-elasticsearch/v8"
"github.com/elastic/go-elasticsearch/v8/esapi"
"github.com/elastic/go-elasticsearch/v8/esutil"
)

func init() {
log.SetFlags(0)
}

func main() {
var (
res *esapi.Response
err error
)

es, err := elasticsearch.NewDefaultClient()
if err != nil {
log.Fatalf("Error creating the client: %s", err)
}

doc := struct {
Title string `json:"title"`
}{Title: "Test"}

res, err = es.Index("test", esutil.NewJSONReader(&doc), es.Index.WithRefresh("true"))
if err != nil {
log.Fatalf("Error getting response: %s", err)
}

log.Println(res)

query := map[string]interface{}{
"query": map[string]interface{}{
"match": map[string]interface{}{
"title": "test",
},
},
}

res, err = es.Search(
es.Search.WithIndex("test"),
es.Search.WithBody(esutil.NewJSONReader(&query)),
es.Search.WithPretty(),
)
if err != nil {
log.Fatalf("Error getting response: %s", err)
}

log.Println(res)
}
2 changes: 2 additions & 0 deletions esapi/esapi.request.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ func newRequest(method, path string, body io.Reader) (*http.Request, error) {
case *strings.Reader:
r.Body = ioutil.NopCloser(body)
r.ContentLength = int64(b.Len())
default:
r.Body = ioutil.NopCloser(body)
}
}

Expand Down
21 changes: 14 additions & 7 deletions esutil/json_reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import (
"io"
)

// JSONReader is an utility function which encodes v into JSON and returns it as a reader.
// NewJSONReader encodes v into JSON and returns it as an io.Reader.
//
func JSONReader(v interface{}) io.Reader {
return &jsonReader{val: v, buf: nil}
func NewJSONReader(v interface{}) io.Reader {
return &JSONReader{val: v, buf: nil}
}

// JSONEncoder defines the interface for custom JSON encoders.
Expand All @@ -18,15 +18,20 @@ type JSONEncoder interface {
EncodeJSON(io.Writer) error
}

type jsonReader struct {
// JSONReader represents a reader which takes an interface value,
// encodes it into JSON, and wraps it in an io.Reader.
//
type JSONReader struct {
val interface{}
buf interface {
io.ReadWriter
io.WriterTo
}
}

func (r *jsonReader) Read(p []byte) (int, error) {
// Read implements the io.Reader interface.
//
func (r *JSONReader) Read(p []byte) (int, error) {
if r.buf == nil {
r.buf = new(bytes.Buffer)
if err := r.encode(r.buf); err != nil {
Expand All @@ -37,13 +42,15 @@ func (r *jsonReader) Read(p []byte) (int, error) {
return r.buf.Read(p)
}

func (r *jsonReader) WriteTo(w io.Writer) (int64, error) {
// WriteTo implements the io.WriterTo interface.
//
func (r *JSONReader) WriteTo(w io.Writer) (int64, error) {
cw := countingWriter{Writer: w}
err := r.encode(&cw)
return int64(cw.n), err
}

func (r *jsonReader) encode(w io.Writer) error {
func (r *JSONReader) encode(w io.Writer) error {
var err error

if e, ok := r.val.(JSONEncoder); ok {
Expand Down
6 changes: 3 additions & 3 deletions esutil/json_reader_benchmark_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func BenchmarkJSONReader(b *testing.B) {
b.ResetTimer()

for i := 0; i < b.N; i++ {
out, _ := ioutil.ReadAll(esutil.JSONReader(map[string]string{"foo": "bar"}))
out, _ := ioutil.ReadAll(esutil.NewJSONReader(map[string]string{"foo": "bar"}))
if string(out) != `{"foo":"bar"}`+"\n" {
b.Fatalf("Unexpected output: %q", out)
}
Expand All @@ -65,7 +65,7 @@ func BenchmarkJSONReader(b *testing.B) {

var buf bytes.Buffer
for i := 0; i < b.N; i++ {
io.Copy(&buf, esutil.JSONReader(map[string]string{"foo": "bar"}))
io.Copy(&buf, esutil.NewJSONReader(map[string]string{"foo": "bar"}))
if buf.String() != `{"foo":"bar"}`+"\n" {
b.Fatalf("Unexpected output: %q", buf.String())
}
Expand All @@ -77,7 +77,7 @@ func BenchmarkJSONReader(b *testing.B) {
b.ResetTimer()

for i := 0; i < b.N; i++ {
out, _ := ioutil.ReadAll(esutil.JSONReader(Foo{Bar: "baz"}))
out, _ := ioutil.ReadAll(esutil.NewJSONReader(Foo{Bar: "baz"}))
if string(out) != `{"bar":"BAZ"}`+"\n" {
b.Fatalf("Unexpected output: %q", out)
}
Expand Down
68 changes: 68 additions & 0 deletions esutil/json_reader_integration_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// +build integration

package esutil_test

import (
"strings"
"testing"

"github.com/elastic/go-elasticsearch/v8"
"github.com/elastic/go-elasticsearch/v8/esapi"
"github.com/elastic/go-elasticsearch/v8/esutil"
)

func TestJSONReaderIntegration(t *testing.T) {
t.Run("Index and search", func(t *testing.T) {
var (
res *esapi.Response
err error
)

es, err := elasticsearch.NewDefaultClient()
if err != nil {
t.Fatalf("Error creating the client: %s\n", err)
}

es.Indices.Delete([]string{"test"})

doc := struct {
Title string `json:"title"`
}{Title: "Foo Bar"}

res, err = es.Index("test", esutil.NewJSONReader(&doc), es.Index.WithRefresh("true"))
if err != nil {
t.Fatalf("Error getting response: %s", err)
}
defer res.Body.Close()

if res.IsError() {
t.Fatalf("Error response: %s", res.String())
}

query := map[string]interface{}{
"query": map[string]interface{}{
"match": map[string]interface{}{
"title": "foo",
},
},
}

res, err = es.Search(
es.Search.WithIndex("test"),
es.Search.WithBody(esutil.NewJSONReader(&query)),
es.Search.WithPretty(),
)
if err != nil {
t.Fatalf("Error getting response: %s", err)
}
defer res.Body.Close()

if res.IsError() {
t.Errorf("Error response: %s", res)
}

if !strings.Contains(res.String(), "Foo Bar") {
t.Errorf("Unexpected response: %s", res)
}
})
}
8 changes: 4 additions & 4 deletions esutil/json_reader_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,22 +31,22 @@ func (f Foo) EncodeJSON(w io.Writer) error {

func TestJSONReader(t *testing.T) {
t.Run("Default", func(t *testing.T) {
out, _ := ioutil.ReadAll(JSONReader(map[string]string{"foo": "bar"}))
out, _ := ioutil.ReadAll(NewJSONReader(map[string]string{"foo": "bar"}))
if string(out) != `{"foo":"bar"}`+"\n" {
t.Fatalf("Unexpected output: %s", out)
}
})

t.Run("Custom", func(t *testing.T) {
out, _ := ioutil.ReadAll(JSONReader(Foo{Bar: "baz"}))
out, _ := ioutil.ReadAll(NewJSONReader(Foo{Bar: "baz"}))
if string(out) != `{"bar":"BAZ"}`+"\n" {
t.Fatalf("Unexpected output: %s", out)
}
})

t.Run("WriteTo", func(t *testing.T) {
b := bytes.NewBuffer([]byte{})
r := jsonReader{val: map[string]string{"foo": "bar"}}
r := JSONReader{val: map[string]string{"foo": "bar"}}
r.WriteTo(b)
if b.String() != `{"foo":"bar"}`+"\n" {
t.Fatalf("Unexpected output: %s", b.String())
Expand All @@ -55,7 +55,7 @@ func TestJSONReader(t *testing.T) {

t.Run("Read error", func(t *testing.T) {
b := []byte{}
r := jsonReader{val: map[string]string{"foo": "bar"}, buf: errReader{}}
r := JSONReader{val: map[string]string{"foo": "bar"}, buf: errReader{}}
_, err := r.Read(b)
if err == nil {
t.Fatalf("Expected error, got: %#v", err)
Expand Down

0 comments on commit 82fc17d

Please sign in to comment.