Skip to content

Commit

Permalink
fix: add PR-related fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
yuryfirebolt committed Aug 1, 2022
1 parent 3cb1d41 commit 1af2617
Show file tree
Hide file tree
Showing 12 changed files with 97 additions and 86 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,9 @@ func main() {


### DSN (Data source name)
All information for the connection should be specifying using the DSN string. The firebolt dsn string has the following format:
All information for the connection should be specified using the DSN string. The firebolt dsn string has the following format:
```
firebolt://username:password@database[/engine_name]?account_name=account_name
firebolt://username:password@database[/engine_name][?account_name=account_name]
```

- **username** - the email address you use to log in to Firebolt.
Expand Down
6 changes: 2 additions & 4 deletions auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@ package fireboltgosdk

import (
"encoding/json"
"log"
)

// AuthenticationResponse definition of the authentication response
type AuthenticationResponse struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
Expand All @@ -16,7 +14,7 @@ type AuthenticationResponse struct {

// Authenticate sends an authentication request, and returns a newly constructed client object
func Authenticate(username, password string) (*Client, error) {
log.Printf("Start authentication into '%s' using '%s'", HostNameURL, LoginUrl)
infolog.Printf("Start authentication into '%s' using '%s'", HostNameURL, LoginUrl)

values := map[string]string{"username": username, "password": password}
jsonData, _ := json.Marshal(values)
Expand All @@ -32,6 +30,6 @@ func Authenticate(username, password string) (*Client, error) {
return nil, ConstructNestedError("failed to unmarshal authentication response with error", err)
}

log.Printf("Authentication was successful")
infolog.Printf("Authentication was successful")
return &Client{AccessToken: authResp.AccessToken}, nil
}
52 changes: 21 additions & 31 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"errors"
"fmt"
"io/ioutil"
"log"
"net/http"
"strings"
)
Expand All @@ -17,13 +16,12 @@ type Client struct {

// GetAccountIdByName returns account ID based on account name
func (c *Client) GetAccountIdByName(accountName string) (string, error) {
log.Printf("get account id by name: %s", accountName)
infolog.Printf("get account id by name: %s", accountName)
type AccountIdByNameResponse struct {
AccountId string `json:"account_id"`
}

params := make(map[string]string)
params["account_name"] = accountName
params := map[string]string{"account_name": accountName}

response, err := request(c.AccessToken, "GET", HostNameURL+AccountIdByNameURL, params, "")
if err != nil {
Expand All @@ -39,7 +37,7 @@ func (c *Client) GetAccountIdByName(accountName string) (string, error) {

// GetEngineIdByName returns engineId based on engineName and accountId
func (c *Client) GetEngineIdByName(engineName string, accountId string) (string, error) {
log.Printf("get engine id by name '%s' and account id '%s'", engineName, accountId)
infolog.Printf("get engine id by name '%s' and account id '%s'", engineName, accountId)

type EngineIdByNameInnerResponse struct {
AccountId string `json:"account_id"`
Expand All @@ -49,9 +47,7 @@ func (c *Client) GetEngineIdByName(engineName string, accountId string) (string,
EngineId EngineIdByNameInnerResponse `json:"engine_id"`
}

params := make(map[string]string)
params["engine_name"] = engineName

params := map[string]string{"engine_name": engineName}
response, err := request(c.AccessToken, "GET", fmt.Sprintf(HostNameURL+EngineIdByNameURL, accountId), params, "")
if err != nil {
return "", ConstructNestedError("error during getting engine id by name request", err)
Expand All @@ -66,7 +62,7 @@ func (c *Client) GetEngineIdByName(engineName string, accountId string) (string,

// GetEngineUrlById returns engine url based on engineId and accountId
func (c *Client) GetEngineUrlById(engineId string, accountId string) (string, error) {
log.Printf("get engine url by id '%s' and account id '%s'", engineId, accountId)
infolog.Printf("get engine url by id '%s' and account id '%s'", engineId, accountId)

type EngineResponse struct {
Endpoint string `json:"endpoint"`
Expand All @@ -75,8 +71,7 @@ func (c *Client) GetEngineUrlById(engineId string, accountId string) (string, er
Engine EngineResponse `json:"engine"`
}

params := make(map[string]string)
response, err := request(c.AccessToken, "GET", fmt.Sprintf(HostNameURL+EngineByIdURL, accountId, engineId), params, "")
response, err := request(c.AccessToken, "GET", fmt.Sprintf(HostNameURL+EngineByIdURL, accountId, engineId), make(map[string]string), "")
if err != nil {
return "", ConstructNestedError("error during getting engine url by id request", err)
}
Expand All @@ -85,7 +80,7 @@ func (c *Client) GetEngineUrlById(engineId string, accountId string) (string, er
if err = json.Unmarshal(response, &engineByIdResponse); err != nil {
return "", ConstructNestedError("error during unmarshalling engine url by id response", errors.New(string(response)))
}
return fmt.Sprintf("https://%s", engineByIdResponse.Engine.Endpoint), nil
return makeCanonicalUrl(engineByIdResponse.Engine.Endpoint), nil
}

// GetDefaultAccount returns an id of the default account
Expand All @@ -98,8 +93,7 @@ func (c *Client) GetDefaultAccountId() (string, error) {
Account AccountResponse `json:"account"`
}

params := make(map[string]string)
response, err := request(c.AccessToken, "GET", fmt.Sprintf(HostNameURL+DefaultAccountURL), params, "")
response, err := request(c.AccessToken, "GET", fmt.Sprintf(HostNameURL+DefaultAccountURL), make(map[string]string), "")

if err != nil {
return "", ConstructNestedError("error during getting default account id request", err)
Expand All @@ -115,7 +109,7 @@ func (c *Client) GetDefaultAccountId() (string, error) {

// GetEngineUrlByName return engine URL based on engineName and accountName
func (c *Client) GetEngineUrlByName(engineName string, accountId string) (string, error) {
log.Printf("get engine url by name '%s' and account id '%s'", engineName, accountId)
infolog.Printf("get engine url by name '%s' and account id '%s'", engineName, accountId)

engineId, err := c.GetEngineIdByName(engineName, accountId)
if err != nil {
Expand All @@ -132,14 +126,13 @@ func (c *Client) GetEngineUrlByName(engineName string, accountId string) (string

// GetEngineUrlByDatabase return URL of the default engine based on databaseName and accountName
func (c *Client) GetEngineUrlByDatabase(databaseName string, accountId string) (string, error) {
log.Printf("get engine url by database name '%s' and account name '%s'", databaseName, accountId)
infolog.Printf("get engine url by database name '%s' and account name '%s'", databaseName, accountId)

type EngineUrlByDatabaseResponse struct {
EngineUrl string `json:"engine_url"`
}

params := make(map[string]string)
params["database_name"] = databaseName
params := map[string]string{"database_name": databaseName}
response, err := request(c.AccessToken, "GET", fmt.Sprintf(HostNameURL+EngineUrlByDatabaseNameURL, accountId), params, "")
if err != nil {
return "", ConstructNestedError("error during getting engine url by database request", err)
Expand All @@ -153,12 +146,10 @@ func (c *Client) GetEngineUrlByDatabase(databaseName string, accountId string) (
}

// Query sends a query to the engine URL and populates queryResponse, if query was successful
func (c *Client) Query(engineUrl, databaseName, query string, setStatements *map[string]string, queryResponse *QueryResponse) error {
log.Printf("Query engine '%s' with '%s'", engineUrl, query)
func (c *Client) Query(engineUrl, databaseName, query string, setStatements *map[string]string) (*QueryResponse, error) {
infolog.Printf("Query engine '%s' with '%s'", engineUrl, query)

params := make(map[string]string)
params["database"] = databaseName
params["output_format"] = "FB_JSONCompactLimited"
params := map[string]string{"database": databaseName, "output_format": "JSONCompact"}
if setStatements != nil {
for setKey, setValue := range *setStatements {
params[setKey] = setValue
Expand All @@ -167,15 +158,16 @@ func (c *Client) Query(engineUrl, databaseName, query string, setStatements *map

response, err := request(c.AccessToken, "POST", engineUrl, params, query)
if err != nil {
return ConstructNestedError("error during query request", err)
return nil, ConstructNestedError("error during query request", err)
}

var queryResponse QueryResponse
if err = json.Unmarshal(response, &queryResponse); err != nil {
return ConstructNestedError("wrong response", errors.New(string(response)))
return nil, ConstructNestedError("wrong response", errors.New(string(response)))
}

log.Printf("Query was successful")
return nil
infolog.Printf("Query was successful")
return &queryResponse, nil
}

// makeCanonicalUrl checks whether url starts with https:// and if not prepends it
Expand Down Expand Up @@ -214,8 +206,6 @@ func checkErrorResponse(response []byte) error {
func request(accessToken string, method string, url string, params map[string]string, bodyStr string) ([]byte, error) {
req, _ := http.NewRequest(method, makeCanonicalUrl(url), strings.NewReader(bodyStr))

req.Header.Add("Content-Type", "application/x-www-form-urlencoded")

if len(accessToken) > 0 {
var bearer = "Bearer " + accessToken
req.Header.Add("Authorization", bearer)
Expand All @@ -230,15 +220,15 @@ func request(accessToken string, method string, url string, params map[string]st
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
log.Println(err)
infolog.Println(err)
return nil, ConstructNestedError("error during a request execution", err)
}

defer resp.Body.Close()

body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Println(err)
infolog.Println(err)
return nil, ConstructNestedError("error during reading a request response", err)
}

Expand Down
17 changes: 10 additions & 7 deletions client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,10 @@ func TestGetEngineUrlByName(t *testing.T) {
if makeCanonicalUrl(engineUrl) != makeCanonicalUrl(engineUrlMock) {
t.Errorf("Returned engine url is not equal to a mocked engine url %s != %s", engineUrl, engineUrlMock)
}
if _, err = clientMock.GetEngineUrlByName("not_existing_engine", accountNameMock); err == nil {
if res, err := clientMock.GetEngineUrlByName("not_existing_engine", accountNameMock); err == nil || res != "" {
t.Errorf("GetEngineUrlByName didn't return an error with not existing engine")
}
if _, err = clientMock.GetEngineUrlByName(engineNameMock, "not_existing_account"); err == nil {
if res, err := clientMock.GetEngineUrlByName(engineNameMock, "not_existing_account"); err == nil || res != "" {
t.Errorf("GetEngineUrlByName didn't return an error with not existing account")
}
}
Expand Down Expand Up @@ -93,25 +93,28 @@ func TestGetEngineUrlByDatabase(t *testing.T) {
func TestQuery(t *testing.T) {
markIntegrationTest(t)

var queryResponse QueryResponse
if err := clientMock.Query(engineUrlMock, databaseMock, "SELECT 1", nil, &queryResponse); err != nil {
queryResponse, err := clientMock.Query(engineUrlMock, databaseMock, "SELECT 1", nil)
if err != nil {
t.Errorf("Query returned an error: %v", err)
}
if queryResponse.Rows != 1 {
t.Errorf("Query response has an invalid number of rows %d != %d", queryResponse.Rows, 1)
}

if queryResponse.Data[0][0].(float64) != 1 {
t.Errorf("queryResponse data is not correct")
}
}

// TestQuery with set statements
func TestQuerySetStatements(t *testing.T) {
markIntegrationTest(t)

query := "SELECT * FROM information_schema.tables"
var queryResponse QueryResponse
if err := clientMock.Query(engineUrlMock, databaseMock, query, &map[string]string{"use_standard_sql": "1"}, &queryResponse); err != nil {
if _, err := clientMock.Query(engineUrlMock, databaseMock, query, &map[string]string{"use_standard_sql": "1"}); err != nil {
t.Errorf("Query returned an error: %v", err)
}
if err := clientMock.Query(engineUrlMock, databaseMock, query, &map[string]string{"use_standard_sql": "0"}, &queryResponse); err == nil {
if _, err := clientMock.Query(engineUrlMock, databaseMock, query, &map[string]string{"use_standard_sql": "0"}); err == nil {
t.Errorf("Query didn't return an error, but should")
}
}
Expand Down
28 changes: 9 additions & 19 deletions connection.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"database/sql/driver"
"errors"
"fmt"
)

type fireboltConnection struct {
Expand Down Expand Up @@ -32,48 +33,37 @@ func (c *fireboltConnection) Close() error {

// Begin is not implemented, as firebolt doesn't support transactions
func (c *fireboltConnection) Begin() (driver.Tx, error) {
panic("Transactions are not implemented in firebolt")
return nil, fmt.Errorf("Transactions are not implemented in firebolt")
}

// ExecContext sends the query to the engine and returns empty fireboltResult
func (c *fireboltConnection) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) {
if len(args) != 0 {
panic("Prepared statements are not implemented")
}
if processSetStatement(c, query) {
return &FireboltResult{}, nil
}

var queryResponse QueryResponse
if err := c.client.Query(c.engineUrl, c.databaseName, query, &c.setStatements, &queryResponse); err != nil {
return nil, ConstructNestedError("error during query execution", err)
}

return &FireboltResult{}, nil
_, err := c.QueryContext(ctx, query, args)
return &FireboltResult{}, err
}

// QueryContext sends the query to the engine and returns fireboltRows
func (c *fireboltConnection) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) {
if len(args) != 0 {
panic("Prepared statements are not implemented")
return nil, fmt.Errorf("Prepared statements are not implemented")
}
if processSetStatement(c, query) {
return &fireboltRows{QueryResponse{}, 0}, nil
}

var queryResponse QueryResponse
if err := c.client.Query(c.engineUrl, c.databaseName, query, &c.setStatements, &queryResponse); err != nil {
queryResponse, err := c.client.Query(c.engineUrl, c.databaseName, query, &c.setStatements)
if err != nil {
return nil, ConstructNestedError("error during query execution", err)
}

return &fireboltRows{queryResponse, 0}, nil
return &fireboltRows{*queryResponse, 0}, nil
}

// processSetStatement is an internal function for checking whether query is a valid set statement
// and updating set statement map of the fireboltConnection
func processSetStatement(c *fireboltConnection, query string) bool {
if setKey, setValue, err := parseSetStatement(query); err == nil {
if nil == c.client.Query(c.engineUrl, c.databaseName, "SELECT 1", &map[string]string{setKey: setValue}, &QueryResponse{}) {
if _, err := c.client.Query(c.engineUrl, c.databaseName, "SELECT 1", &map[string]string{setKey: setValue}); err == nil {
c.setStatements[setKey] = setValue
return true
}
Expand Down
13 changes: 5 additions & 8 deletions driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,14 @@ package fireboltgosdk
import (
"database/sql"
"database/sql/driver"
"io/ioutil"
"log"
)

type FireboltDriver struct {
}

// Open parses the dsn string, and if correct tries to establish a connection
func (d FireboltDriver) Open(dsn string) (driver.Conn, error) {
log.Println("Opening firebolt driver")
infolog.Println("Opening firebolt driver")

// parsing dsn string to get configuration settings
settings, err := ParseDSNString(dsn)
Expand All @@ -21,7 +19,7 @@ func (d FireboltDriver) Open(dsn string) (driver.Conn, error) {
}

// authenticating and getting access token
log.Println("dsn parsed correctly, trying to authenticate")
infolog.Println("dsn parsed correctly, trying to authenticate")
client, err := Authenticate(settings.username, settings.password)
if err != nil {
return nil, ConstructNestedError("error during authentication", err)
Expand All @@ -30,7 +28,7 @@ func (d FireboltDriver) Open(dsn string) (driver.Conn, error) {
// getting accountId, either default, or by specified accountName
var accountId string
if settings.accountName == "" {
log.Println("account name not specified, trying to get a default account id")
infolog.Println("account name not specified, trying to get a default account id")
accountId, err = client.GetDefaultAccountId()
} else {
accountId, err = client.GetAccountIdByName(settings.accountName)
Expand All @@ -45,19 +43,18 @@ func (d FireboltDriver) Open(dsn string) (driver.Conn, error) {
if settings.engineName != "" {
engineUrl, err = client.GetEngineUrlByName(settings.engineName, accountId)
} else {
log.Println("engine name not set, trying to get a default engine")
infolog.Println("engine name not set, trying to get a default engine")
engineUrl, err = client.GetEngineUrlByDatabase(settings.database, accountId)
}
if err != nil {
return nil, ConstructNestedError("error during getting engine url", err)
}

log.Printf("firebolt connection is created")
infolog.Printf("firebolt connection is created")
return &fireboltConnection{client, settings.database, engineUrl, map[string]string{}}, nil
}

// init registers a firebolt driver
func init() {
log.SetOutput(ioutil.Discard)
sql.Register("firebolt", &FireboltDriver{})
}
Loading

0 comments on commit 1af2617

Please sign in to comment.