-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #9 from stevenferrer/feature/config-api
Implement config API client
- Loading branch information
Showing
15 changed files
with
1,013 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
package config | ||
|
||
import ( | ||
"bytes" | ||
"context" | ||
"encoding/json" | ||
"fmt" | ||
"net/http" | ||
"net/url" | ||
"strings" | ||
"time" | ||
|
||
"github.com/pkg/errors" | ||
) | ||
|
||
// Client is a config API client | ||
type Client interface { | ||
GetConfig(ctx context.Context, collection string) (*Response, error) | ||
SendCommands(ctx context.Context, collection string, commands ...Commander) error | ||
} | ||
|
||
type client struct { | ||
host string | ||
port int | ||
proto string | ||
httpClient *http.Client | ||
} | ||
|
||
// New is a factory for config client | ||
func New(host string, port int) Client { | ||
proto := "http" | ||
return &client{ | ||
host: host, | ||
port: port, | ||
proto: proto, | ||
httpClient: &http.Client{ | ||
Timeout: time.Second * 60, | ||
}, | ||
} | ||
} | ||
|
||
// NewWithHTTPClient is a factory for config client | ||
func NewWithHTTPClient(host string, port int, httpClient *http.Client) Client { | ||
proto := "http" | ||
return &client{ | ||
host: host, | ||
port: port, | ||
proto: proto, | ||
httpClient: httpClient, | ||
} | ||
} | ||
|
||
func (c *client) GetConfig(ctx context.Context, collection string) (*Response, error) { | ||
theURL, err := c.buildURL(collection) | ||
if err != nil { | ||
return nil, errors.Wrap(err, "build url") | ||
} | ||
|
||
httpReq, err := http.NewRequestWithContext(ctx, http.MethodGet, theURL.String(), nil) | ||
if err != nil { | ||
return nil, errors.Wrap(err, "new http request") | ||
} | ||
|
||
return c.do(httpReq) | ||
} | ||
|
||
func (c *client) SendCommands(ctx context.Context, collection string, commands ...Commander) error { | ||
if len(commands) == 0 { | ||
return nil | ||
} | ||
|
||
theURL, err := c.buildURL(collection) | ||
if err != nil { | ||
return errors.Wrap(err, "build url") | ||
} | ||
|
||
// build commands | ||
commandStrs := []string{} | ||
for _, command := range commands { | ||
commandStr, err := command.Command() | ||
if err != nil { | ||
return errors.Wrap(err, "build commad") | ||
} | ||
|
||
commandStrs = append(commandStrs, commandStr) | ||
} | ||
|
||
// send commands to solr | ||
requestBody := "{" + strings.Join(commandStrs, ",") + "}" | ||
|
||
return c.sendCommands(ctx, theURL.String(), []byte(requestBody)) | ||
} | ||
|
||
func (c *client) buildURL(collection string) (*url.URL, error) { | ||
u, err := url.Parse(fmt.Sprintf("%s://%s:%d/solr/%s/config", | ||
c.proto, c.host, c.port, collection)) | ||
if err != nil { | ||
return nil, errors.Wrap(err, "parse url") | ||
} | ||
|
||
return u, nil | ||
} | ||
|
||
func (c *client) sendCommands(ctx context.Context, urlStr string, body []byte) error { | ||
httpReq, err := http.NewRequestWithContext(ctx, | ||
http.MethodPost, urlStr, bytes.NewReader(body)) | ||
if err != nil { | ||
return errors.Wrap(err, "new http request") | ||
} | ||
|
||
_, err = c.do(httpReq) | ||
if err != nil { | ||
return errors.Wrap(err, "send commands") | ||
} | ||
|
||
return err | ||
} | ||
|
||
func (c *client) do(httpReq *http.Request) (*Response, error) { | ||
httpReq.Header.Add("content-type", "application/json") | ||
httpResp, err := c.httpClient.Do(httpReq) | ||
if err != nil { | ||
return nil, errors.Wrap(err, "http do request") | ||
} | ||
|
||
var resp Response | ||
err = json.NewDecoder(httpResp.Body).Decode(&resp) | ||
if err != nil { | ||
return nil, errors.Wrap(err, "decode response") | ||
} | ||
|
||
if httpResp.StatusCode > http.StatusOK { | ||
return nil, resp.Error | ||
} | ||
|
||
return &resp, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
package config_test | ||
|
||
import ( | ||
"context" | ||
"net/http" | ||
"testing" | ||
"time" | ||
|
||
"github.com/dnaeon/go-vcr/recorder" | ||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
|
||
solrconfig "github.com/stevenferrer/solr-go/config" | ||
) | ||
|
||
func TestClient(t *testing.T) { | ||
ctx := context.Background() | ||
collection := "gettingstarted" | ||
host := "localhost" | ||
port := 8983 | ||
timeout := time.Second * 6 | ||
|
||
// only for covering | ||
_ = solrconfig.New(host, port) | ||
|
||
t.Run("retrieve config", func(t *testing.T) { | ||
rec, err := recorder.New("fixtures/retrieve-config") | ||
require.NoError(t, err) | ||
defer rec.Stop() | ||
|
||
configClient := solrconfig.NewWithHTTPClient(host, port, &http.Client{ | ||
Timeout: timeout, | ||
Transport: rec, | ||
}) | ||
|
||
resp, err := configClient.GetConfig(ctx, collection) | ||
require.NoError(t, err) | ||
|
||
assert.NotNil(t, resp) | ||
}) | ||
|
||
t.Run("send commands", func(t *testing.T) { | ||
t.Run("ok", func(t *testing.T) { | ||
rec, err := recorder.New("fixtures/send-commands-ok") | ||
require.NoError(t, err) | ||
defer rec.Stop() | ||
|
||
configClient := solrconfig.NewWithHTTPClient(host, port, &http.Client{ | ||
Timeout: timeout, | ||
Transport: rec, | ||
}) | ||
|
||
setUpdateHandlerAutoCommit := solrconfig.NewSetPropCommand( | ||
"updateHandler.autoCommit.maxTime", 15000) | ||
|
||
addSuggestComponent := solrconfig.NewComponentCommand( | ||
solrconfig.AddSearchComponent, | ||
map[string]interface{}{ | ||
"name": "suggest", | ||
"class": "solr.SuggestComponent", | ||
"suggester": map[string]string{ | ||
"name": "mySuggester", | ||
"lookupImpl": "FuzzyLookupFactory", | ||
"dictionaryImpl": "DocumentDictionaryFactory", | ||
"field": "_text_", | ||
"suggestAnalyzerFieldType": "text_general", | ||
}, | ||
}, | ||
) | ||
|
||
addSuggestHandler := solrconfig.NewComponentCommand( | ||
solrconfig.AddRequestHandler, | ||
map[string]interface{}{ | ||
"name": "/suggest", | ||
"class": "solr.SearchHandler", | ||
"startup": "lazy", | ||
"defaults": map[string]interface{}{ | ||
"suggest": true, | ||
"suggest.count": 10, | ||
"suggest.dictionary": "mySuggester", | ||
}, | ||
"components": []string{"suggest"}, | ||
}, | ||
) | ||
|
||
err = configClient.SendCommands(ctx, collection, | ||
setUpdateHandlerAutoCommit, | ||
addSuggestComponent, | ||
addSuggestHandler, | ||
) | ||
assert.NoError(t, err) | ||
}) | ||
|
||
t.Run("error", func(t *testing.T) { | ||
rec, err := recorder.New("fixtures/send-commands-error") | ||
require.NoError(t, err) | ||
defer rec.Stop() | ||
|
||
configClient := solrconfig.NewWithHTTPClient(host, port, &http.Client{ | ||
Timeout: timeout, | ||
Transport: rec, | ||
}) | ||
|
||
addSuggestComponent := solrconfig.NewComponentCommand( | ||
solrconfig.AddSearchComponent, | ||
map[string]interface{}{ | ||
"name": "suggest", | ||
"class": "solr.SuggestComponent", | ||
"suggester": map[string]string{ | ||
"name": "mySuggester", | ||
"lookupImpl": "FuzzyLookupFactory-BLAH-BLAH", | ||
"dictionaryImpl": "DocumentDictionaryFactory-BLAH-BLAH", | ||
"field": "_text_", | ||
"suggestAnalyzerFieldType": "text_general", | ||
}, | ||
}, | ||
) | ||
|
||
err = configClient.SendCommands(ctx, collection, addSuggestComponent) | ||
assert.Error(t, err) | ||
}) | ||
}) | ||
|
||
} |
Oops, something went wrong.