// Package client is an HTTP client for the Queryer. package client import ( "bytes" "context" "encoding/json" "fmt" "io" "net/http" featurebase "github.com/featurebasedb/featurebase/v3" "github.com/featurebasedb/featurebase/v3/dax" queryerhttp "github.com/featurebasedb/featurebase/v3/dax/queryer/http" "github.com/featurebasedb/featurebase/v3/errors" "github.com/featurebasedb/featurebase/v3/logger" ) const ( defaultScheme = "http" ) // Client is an HTTP client that operates on the Controller endpoints exposed by // the main Controller service. type Client struct { address dax.Address logger logger.Logger } // New returns a new instance of Client. func New(address dax.Address, logger logger.Logger) *Client { return &Client{ address: address, logger: logger, } } // Health returns true if the client address returns status OK at its /health // endpoint. func (c *Client) Health() bool { url := fmt.Sprintf("%s/health", c.address.WithScheme(defaultScheme)) if resp, err := http.Get(url); err != nil { return false } else if resp.StatusCode != http.StatusOK { return false } return true } func (c *Client) QuerySQL(ctx context.Context, qdbid dax.QualifiedDatabaseID, sql string) (*featurebase.WireQueryResponse, error) { url := fmt.Sprintf("%s/sql", c.address.WithScheme(defaultScheme)) req := &queryerhttp.SQLRequest{ OrganizationID: qdbid.OrganizationID, DatabaseID: qdbid.DatabaseID, SQL: sql, } // Encode the request. postBody, err := json.Marshal(req) if err != nil { return nil, errors.Wrap(err, "marshalling post request") } responseBody := bytes.NewBuffer(postBody) // Post the request. c.logger.Debugf("POST query sql request: url: %s", url) resp, err := http.Post(url, "application/json", responseBody) if err != nil { return nil, errors.Wrap(err, "posting query sql request") } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { b, _ := io.ReadAll(resp.Body) return nil, errors.Errorf("status code: %d: %s", resp.StatusCode, b) } var wireResp *featurebase.WireQueryResponse if err := json.NewDecoder(resp.Body).Decode(&wireResp); err != nil { return nil, errors.Wrap(err, "reading response body") } return wireResp, nil } func (c *Client) QueryPQL(ctx context.Context, qdbid dax.QualifiedDatabaseID, table dax.TableName, pql string) (*featurebase.WireQueryResponse, error) { url := fmt.Sprintf("%s/query", c.address.WithScheme(defaultScheme)) req := &queryerhttp.QueryRequest{ OrganizationID: qdbid.OrganizationID, DatabaseID: qdbid.DatabaseID, Table: table, PQL: pql, } // Encode the request. postBody, err := json.Marshal(req) if err != nil { return nil, errors.Wrap(err, "marshalling post request") } responseBody := bytes.NewBuffer(postBody) // Post the request. c.logger.Debugf("POST query pql request: url: %s", url) resp, err := http.Post(url, "application/json", responseBody) if err != nil { return nil, errors.Wrap(err, "posting query pql request") } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { b, _ := io.ReadAll(resp.Body) return nil, errors.Errorf("status code: %d: %s", resp.StatusCode, b) } var wireResp *featurebase.WireQueryResponse if err := json.NewDecoder(resp.Body).Decode(&wireResp); err != nil { return nil, errors.Wrap(err, "reading response body") } return wireResp, nil }