Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Client readme #1881

Merged
merged 14 commits into from
Mar 9, 2015
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
- [#1877](https://github.com/influxdb/influxdb/pull/1877): Broker clients track broker leader
- [#1862](https://github.com/influxdb/influxdb/pull/1862): Fix memory leak in `httpd.serveWait`. Thanks @mountkin
- [#1868](https://github.com/influxdb/influxdb/pull/1868): Use `BatchPoints` for `client.Write` method. Thanks @vladlopes, @georgmu, @d2g, @evanphx, @akolosov.
- [#1881](https://github.com/influxdb/influxdb/pull/1881): Update documentation for `client` package. Misc library tweaks.

## v0.9.0-rc9 [2015-03-06]

Expand Down
7 changes: 6 additions & 1 deletion client/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
InfluxDB Go Client Library
============

# TODO Add links to the godoc and examples when they are written
Please refer to [http://godoc.org/github.com/influxdb/influxdb/client](http://godoc.org/github.com/influxdb/influxdb/client)
for documentation.

# Currently Used:

You can see a use of the client libray in the [InfluxDB CLI](https://github.com/influxdb/influxdb/blob/master/cmd/influx/main.go).
46 changes: 35 additions & 11 deletions client/influxdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,24 @@ import (
"github.com/influxdb/influxdb/influxql"
)

// Query is used to send a command to the server. Both Command and Database are required.
type Query struct {
Command string
Database string
}

// Config is used to specify what server to connect to.
// URL: The URL of the server connecting to.
// Username/Password are optional. They will be passed via basic auth if provided.
// UserAgent: If not provided, will default "InfluxDBClient",
type Config struct {
URL url.URL
Username string
Password string
UserAgent string
}

// Client is used to make calls to the server.
type Client struct {
url url.URL
username string
Expand All @@ -32,6 +38,7 @@ type Client struct {
userAgent string
}

// NewClient will instantiate and return a connected client to issue commands to the server.
func NewClient(c Config) (*Client, error) {
client := Client{
url: c.URL,
Expand All @@ -40,13 +47,20 @@ func NewClient(c Config) (*Client, error) {
httpClient: &http.Client{},
userAgent: c.UserAgent,
}
if client.userAgent == "" {
client.userAgent = "InfluxDBClient"
}
return &client, nil
}

// Query sends a command to the server and returns the Results
func (c *Client) Query(q Query) (*Results, error) {
u := c.url

u.Path = "query"
if c.username != "" {
u.User = url.UserPassword(c.username, c.password)
}
values := u.Query()
values.Set("q", q.Command)
values.Set("db", q.Database)
Expand All @@ -56,9 +70,7 @@ func (c *Client) Query(q Query) (*Results, error) {
if err != nil {
return nil, err
}
if c.userAgent != "" {
req.Header.Set("User-Agent", c.userAgent)
}
req.Header.Set("User-Agent", c.userAgent)
resp, err := c.httpClient.Do(req)
if err != nil {
return nil, err
Expand All @@ -75,6 +87,9 @@ func (c *Client) Query(q Query) (*Results, error) {
return &results, nil
}

// Write takes BatchPoints and allows for writing of multiple points with defaults
// If successful, error is nil and Results is nil
// If an error occurs, Results may contain additional information if populated.
func (c *Client) Write(bp BatchPoints) (*Results, error) {
c.url.Path = "write"

Expand All @@ -88,9 +103,7 @@ func (c *Client) Write(bp BatchPoints) (*Results, error) {
return nil, err
}
req.Header.Set("Content-Type", "application/json")
if c.userAgent != "" {
req.Header.Set("User-Agent", c.userAgent)
}
req.Header.Set("User-Agent", c.userAgent)
resp, err := c.httpClient.Do(req)
if err != nil {
return nil, err
Expand All @@ -109,9 +122,11 @@ func (c *Client) Write(bp BatchPoints) (*Results, error) {
return &results, results.Error()
}

return &results, nil
return nil, nil
}

// Ping will check to see if the server is up
// Ping returns how long the requeset took, the version of the server it connected to, and an error if one occured.
func (c *Client) Ping() (time.Duration, string, error) {
now := time.Now()
u := c.url
Expand All @@ -121,9 +136,7 @@ func (c *Client) Ping() (time.Duration, string, error) {
if err != nil {
return 0, "", err
}
if c.userAgent != "" {
req.Header.Set("User-Agent", c.userAgent)
}
req.Header.Set("User-Agent", c.userAgent)
resp, err := c.httpClient.Do(req)
if err != nil {
return 0, "", err
Expand Down Expand Up @@ -183,7 +196,8 @@ type Results struct {
Err error
}

func (r Results) MarshalJSON() ([]byte, error) {
// MarshalJSON encodes the result into JSON.
func (r *Results) MarshalJSON() ([]byte, error) {
// Define a struct that outputs "error" as a string.
var o struct {
Results []Result `json:"results,omitempty"`
Expand Down Expand Up @@ -234,6 +248,9 @@ func (a Results) Error() error {
}

// Point defines the fields that will be written to the database
// Name, Timestamp, and Fields are required
// Precision can be specified if the timestamp is in epoch format (integer).
// Valid values for Precision are n, u, ms, s, m, and h
type Point struct {
Name string
Tags map[string]string
Expand Down Expand Up @@ -341,6 +358,12 @@ func normalizeFields(fields map[string]interface{}) map[string]interface{} {
}

// BatchPoints is used to send batched data in a single write.
// Database and Points are required
// If no retention policy is specified, it will use the databases default retention policy.
// If tags are specified, they will be "merged" with all points. If a point already has that tag, it is ignored.
// If timestamp is specified, it will be applied to any point with an empty timestamp.
// Precision can be specified if the timestamp is in epoch format (integer).
// Valid values for Precision are n, u, ms, s, m, and h
type BatchPoints struct {
Points []Point `json:"points,omitempty"`
Database string `json:"database,omitempty"`
Expand Down Expand Up @@ -409,6 +432,7 @@ func (bp *BatchPoints) UnmarshalJSON(b []byte) error {

// utility functions

// Addr provides the current url as a string of the server the client is connected to.
func (c *Client) Addr() string {
return c.url.String()
}
Expand Down
9 changes: 4 additions & 5 deletions client/influxdb_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"net/http"
"net/http/httptest"
"net/url"
"strings"
"testing"
"time"

Expand Down Expand Up @@ -134,8 +135,6 @@ func TestClient_UserAgent(t *testing.T) {
t.Fatalf("unexpected error. expected %v, actual %v", nil, err)
}

defaultUserAgent := receivedUserAgent

tests := []struct {
name string
userAgent string
Expand All @@ -144,7 +143,7 @@ func TestClient_UserAgent(t *testing.T) {
{
name: "Empty user agent",
userAgent: "",
expected: defaultUserAgent,
expected: "InfluxDBClient",
},
{
name: "Custom user agent",
Expand All @@ -167,7 +166,7 @@ func TestClient_UserAgent(t *testing.T) {
if err != nil {
t.Fatalf("unexpected error. expected %v, actual %v", nil, err)
}
if receivedUserAgent != test.expected {
if !strings.HasPrefix(receivedUserAgent, test.expected) {
t.Fatalf("Unexpected user agent. expected %v, actual %v", test.expected, receivedUserAgent)
}

Expand All @@ -177,7 +176,7 @@ func TestClient_UserAgent(t *testing.T) {
if err != nil {
t.Fatalf("unexpected error. expected %v, actual %v", nil, err)
}
if receivedUserAgent != test.expected {
if !strings.HasPrefix(receivedUserAgent, test.expected) {
t.Fatalf("Unexpected user agent. expected %v, actual %v", test.expected, receivedUserAgent)
}

Expand Down
6 changes: 3 additions & 3 deletions cmd/influxd/server_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -690,7 +690,7 @@ func TestClientLibrary(t *testing.T) {
},
{
name: "no points",
writeExpected: `{}`,
writeExpected: `null`,
bp: client.BatchPoints{Database: "mydb"},
},
{
Expand All @@ -701,7 +701,7 @@ func TestClientLibrary(t *testing.T) {
{Name: "cpu", Fields: map[string]interface{}{"value": 1.1}, Timestamp: now},
},
},
writeExpected: `{}`,
writeExpected: `null`,
query: client.Query{Command: `select * from "mydb"."myrp".cpu`},
queryExpected: fmt.Sprintf(`{"results":[{"series":[{"name":"cpu","columns":["time","value"],"values":[["%s",1.1]]}]}]}`, now.Format(time.RFC3339Nano)),
},
Expand All @@ -726,7 +726,7 @@ func TestClientLibrary(t *testing.T) {
}

if test.query.Command != "" {
time.Sleep(50 * time.Millisecond)
time.Sleep(100 * time.Millisecond)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you find you needed to do this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, 50 was sometimes coming up short. 100 always worked. Until we get our wait endpoint back up to par this feels like the best we can do.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, it's just a stop-gap anyway so cool.

queryResult, err := c.Query(test.query)
if test.queryErr != errToString(err) {
t.Errorf("unexpected error. expected: %s, got %v", test.queryErr, err)
Expand Down