From 787e2c3d02e0079e3399a44ab69a5485864f100f Mon Sep 17 00:00:00 2001 From: Christopher Talib Date: Fri, 7 Feb 2020 17:38:43 +0100 Subject: [PATCH] Creating balboa package --- balboa/client.go | 37 ++++++++++++++++ balboa/main.go | 104 ++++++++++++++++++++++++++++++++++++++++++++ balboa/main_test.go | 16 +++++++ balboa/queries.go | 31 +++++++++++++ balboa/types.go | 24 ++++++++++ 5 files changed, 212 insertions(+) create mode 100644 balboa/client.go create mode 100644 balboa/main.go create mode 100644 balboa/main_test.go create mode 100644 balboa/queries.go create mode 100644 balboa/types.go diff --git a/balboa/client.go b/balboa/client.go new file mode 100644 index 0000000..b69df57 --- /dev/null +++ b/balboa/client.go @@ -0,0 +1,37 @@ +package balboa + +import ( + "fmt" + + "github.com/sirupsen/logrus" +) + +// Client for Balboa instance. +type Client struct { + addr string +} + +// NewClient for Balboa located at address. +func NewClient(address string) (c *Client) { + c = &Client{ + addr: address, + } + + return +} + +// GetClient returns the cached session to Balboa. If no cached session +// is available, or when it is not valid any longer, a new session is +// created. +func GetClient() (*Client, error) { + logrus.Debugf("Getting new Balboa Client") + // TODO: refactor it in env variable and then in config file + addr := "http://127.0.0.1:8030" + if addr == "" { + return nil, fmt.Errorf("missing Balboa configuration") + } + + client := NewClient(addr) + + return client, nil +} diff --git a/balboa/main.go b/balboa/main.go new file mode 100644 index 0000000..ad7774d --- /dev/null +++ b/balboa/main.go @@ -0,0 +1,104 @@ +package balboa + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "time" + + "gitlab.dcso.lolcat/go/golistic/xapi" + "gitlab.dcso.lolcat/go/golistic/xhttp" +) + +const ( + headerFieldContentType = "Content-type" + mimeTypeJSON = "application/json" + graphqlPath = "/query" +) + +// post GraphQL query +func (c *Client) post(ctx context.Context, query *xapi.GraphQLQuery, dest *xapi.GraphQLPayload) error { + gqlClient := xapi.NewGraphQLClient(c.addr+graphqlPath, xhttp.OptionTimeout(2*time.Minute)) + err := gqlClient.AddMiddleware(func(ctx context.Context, r *http.Request) error { + r.Header.Add(headerFieldContentType, mimeTypeJSON) + return nil + }) + if err != nil { + return err + } + + if err = gqlClient.Exec(ctx, query, dest); err != nil { + return err + } + + return nil +} + +// Ping Balboa to see whether it is both available and that we can connect +// and query it. +func (c *Client) Ping(ctx context.Context) (err error) { + resp := xapi.GraphQLPayload{} + query := &xapi.GraphQLQuery{ + Query: queryTest, + } + + if err = c.post(ctx, query, &resp); err != nil { + return err + } + + return +} + +// GetAllEntries retrieves all entries for a Balboa query +func (c *Client) GetAllEntries(ctx context.Context, rrname string, rrtype string, rdata string, limit int32) ([]Entries, error) { + resp := xapi.GraphQLPayload{} + query := &xapi.GraphQLQuery{ + Query: queryAllEntries, + Variables: map[string]interface{}{ + "rrname": rrname, + "rrtype": rrtype, + "rdata": rdata, + "limit": 100, + }, + } + + if err := c.post(ctx, query, &resp); err != nil { + return nil, err + } + + l := EntriesList{} + if err := json.Unmarshal(resp.Data, &l); err != nil { + return nil, fmt.Errorf("json decode: %v", err) + } + return l.EntriesListItem, nil +} + +// GetEntriesBySensor returns Balboa entries by sensorID +func (c *Client) GetEntriesBySensor(ctx context.Context, rrname string, rrtype string, rdata string, limit int32, sensorID string) ([]Entries, error) { + if limit == 0 { + limit = 100 + } + resp := xapi.GraphQLPayload{} + query := &xapi.GraphQLQuery{ + Query: queryAllEntries, + Variables: map[string]interface{}{ + "rrname": rrname, + "rrtype": rrtype, + "rdata": rdata, + "sensorID": sensorID, + "limit": limit, + }, + } + + if err := c.post(ctx, query, &resp); err != nil { + return nil, err + } + + l := EntriesList{} + if err := json.Unmarshal(resp.Data, &l); err != nil { + return nil, fmt.Errorf("json decode: %v", err) + } + + return l.EntriesListItem, nil +} diff --git a/balboa/main_test.go b/balboa/main_test.go new file mode 100644 index 0000000..34aeec3 --- /dev/null +++ b/balboa/main_test.go @@ -0,0 +1,16 @@ +package balboa + +import ( + "os" + "testing" +) + +var ( + testExitCode = 0 +) + +func TestMain(m *testing.M) { + defer func() { os.Exit(testExitCode) }() + + testExitCode = m.Run() +} diff --git a/balboa/queries.go b/balboa/queries.go new file mode 100644 index 0000000..f07cefb --- /dev/null +++ b/balboa/queries.go @@ -0,0 +1,31 @@ +package balboa + +const ( + queryTest = ` + { + e1:entries(rrname:"example.com", limit:1) { + rrname + rrtype + rdata + sensor_id + time_first_rfc3339 + time_last_rfc3339 + count + }, + } + + ` + queryAllEntries = ` + query ($rrname: String!, $limit: Int!, $rrtype: RRType!, $rdata: String!, $sensorID: String!) { + entries(rrname: $rrname, rrtype: $rrtype, rdata: $rdata, limit: $limit, sensor_id: $sensorID) { + rrname + rrtype + rdata + sensor_id + time_first_rfc3339 + time_last_rfc3339 + count + } + } + ` +) diff --git a/balboa/types.go b/balboa/types.go new file mode 100644 index 0000000..3767bc9 --- /dev/null +++ b/balboa/types.go @@ -0,0 +1,24 @@ +package balboa + +import "time" + +// GraphqlError represent graphql errors. +type GraphqlError struct { + Message string `json:"message"` +} + +// Entries represent Entries structure fetched from Balboa +type Entries struct { + Rrname string `json:"rrname"` + Rrtype string `json:"rrtype"` + Rdata string `json:"rdata"` + SensorID string `json:"sensor_id"` + TimeFirstRFC3339 time.Time `json:"time_first_rfc3339"` + TimeLastRFC3339 time.Time `json:"time_last_rfc3339"` + Count int32 `json:"count"` +} + +// EntriesList is the glue between EntriesResponse and []Entries +type EntriesList struct { + EntriesListItem []Entries `json:"entries"` +}