diff --git a/admin.go b/admin.go index 3da1ae9..1caf0c3 100644 --- a/admin.go +++ b/admin.go @@ -6,32 +6,38 @@ import ( "errors" "fmt" "io/ioutil" - - "github.com/grafana/grafana/pkg/api/dtos" ) -func (c *Client) CreateUserForm(settings dtos.AdminCreateUserForm) error { - data, err := json.Marshal(settings) - req, err := c.newRequest("POST", "/api/admin/users", bytes.NewBuffer(data)) +func (c *Client) CreateUser(user User) (int64, error) { + id := int64(0) + data, err := json.Marshal(user) + req, err := c.newRequest("POST", "/api/admin/users", nil, bytes.NewBuffer(data)) if err != nil { - return err + return id, err } resp, err := c.Do(req) if err != nil { - return err + return id, err + } + if resp.StatusCode != 200 { + return id, errors.New(resp.Status) } data, err = ioutil.ReadAll(resp.Body) if err != nil { - return err + return id, err } - if resp.StatusCode != 200 { - return errors.New(resp.Status) + created := struct { + Id int64 `json:"id"` + }{} + err = json.Unmarshal(data, &created) + if err != nil { + return id, err } - return err + return created.Id, err } func (c *Client) DeleteUser(id int64) error { - req, err := c.newRequest("DELETE", fmt.Sprintf("/api/admin/users/%d", id), nil) + req, err := c.newRequest("DELETE", fmt.Sprintf("/api/admin/users/%d", id), nil, nil) if err != nil { return err } diff --git a/admin_test.go b/admin_test.go new file mode 100644 index 0000000..5a836b8 --- /dev/null +++ b/admin_test.go @@ -0,0 +1,39 @@ +package gapi + +import ( + "testing" +) + +const ( + createUserJSON = `{"id":1,"message":"User created"}` + deleteUserJSON = `{"message":"User deleted"}` +) + +func TestCreateUser(t *testing.T) { + server, client := gapiTestTools(200, createUserJSON) + defer server.Close() + user := User{ + Email: "admin@localhost", + Login: "admin", + Name: "Administrator", + Password: "password", + } + resp, err := client.CreateUser(user) + if err != nil { + t.Error(err) + } + + if resp != 1 { + t.Error("Not correctly parsing returned user message.") + } +} + +func TestDeleteUser(t *testing.T) { + server, client := gapiTestTools(200, deleteUserJSON) + defer server.Close() + + err := client.DeleteUser(int64(1)) + if err != nil { + t.Error(err) + } +} diff --git a/alertnotification.go b/alertnotification.go index 39a32b9..bd99db9 100644 --- a/alertnotification.go +++ b/alertnotification.go @@ -9,16 +9,16 @@ import ( ) type AlertNotification struct { - Id int64 `json:"id,omitempty"` - Name string `json:"name"` - Type string `json:"type"` - IsDefault bool `json:"isDefault"` - Settings interface{} `json:"settings"` + Id int64 `json:"id,omitempty"` + Name string `json:"name"` + Type string `json:"type"` + IsDefault bool `json:"isDefault"` + Settings interface{} `json:"settings"` } func (c *Client) AlertNotification(id int64) (*AlertNotification, error) { path := fmt.Sprintf("/api/alert-notifications/%d", id) - req, err := c.newRequest("GET", path, nil) + req, err := c.newRequest("GET", path, nil, nil) if err != nil { return nil, err } @@ -46,7 +46,7 @@ func (c *Client) NewAlertNotification(a *AlertNotification) (int64, error) { if err != nil { return 0, err } - req, err := c.newRequest("POST", "/api/alert-notifications", bytes.NewBuffer(data)) + req, err := c.newRequest("POST", "/api/alert-notifications", nil, bytes.NewBuffer(data)) if err != nil { return 0, err } @@ -77,7 +77,7 @@ func (c *Client) UpdateAlertNotification(a *AlertNotification) error { if err != nil { return err } - req, err := c.newRequest("PUT", path, bytes.NewBuffer(data)) + req, err := c.newRequest("PUT", path, nil, bytes.NewBuffer(data)) if err != nil { return err } @@ -95,7 +95,7 @@ func (c *Client) UpdateAlertNotification(a *AlertNotification) error { func (c *Client) DeleteAlertNotification(id int64) error { path := fmt.Sprintf("/api/alert-notifications/%d", id) - req, err := c.newRequest("DELETE", path, nil) + req, err := c.newRequest("DELETE", path, nil, nil) if err != nil { return err } diff --git a/client.go b/client.go index d92ba88..1ab6884 100644 --- a/client.go +++ b/client.go @@ -39,9 +39,10 @@ func New(auth, baseURL string) (*Client, error) { }, nil } -func (c *Client) newRequest(method, requestPath string, body io.Reader) (*http.Request, error) { +func (c *Client) newRequest(method, requestPath string, query url.Values, body io.Reader) (*http.Request, error) { url := c.baseURL url.Path = path.Join(url.Path, requestPath) + url.RawQuery = query.Encode() req, err := http.NewRequest(method, url.String(), body) if err != nil { return req, err diff --git a/dashboard.go b/dashboard.go index ca45dc9..8e4d6c4 100644 --- a/dashboard.go +++ b/dashboard.go @@ -33,7 +33,7 @@ func (c *Client) SaveDashboard(model map[string]interface{}, overwrite bool) (*D if err != nil { return nil, err } - req, err := c.newRequest("POST", "/api/dashboards/db", bytes.NewBuffer(data)) + req, err := c.newRequest("POST", "/api/dashboards/db", nil, bytes.NewBuffer(data)) if err != nil { return nil, err } @@ -58,7 +58,7 @@ func (c *Client) SaveDashboard(model map[string]interface{}, overwrite bool) (*D func (c *Client) Dashboard(slug string) (*Dashboard, error) { path := fmt.Sprintf("/api/dashboards/db/%s", slug) - req, err := c.newRequest("GET", path, nil) + req, err := c.newRequest("GET", path, nil, nil) if err != nil { return nil, err } @@ -83,7 +83,7 @@ func (c *Client) Dashboard(slug string) (*Dashboard, error) { func (c *Client) DeleteDashboard(slug string) error { path := fmt.Sprintf("/api/dashboards/db/%s", slug) - req, err := c.newRequest("DELETE", path, nil) + req, err := c.newRequest("DELETE", path, nil, nil) if err != nil { return err } diff --git a/datasource.go b/datasource.go index 02d9257..5e2d014 100644 --- a/datasource.go +++ b/datasource.go @@ -32,9 +32,10 @@ type DataSource struct { // JSONData is a representation of the datasource `jsonData` property type JSONData struct { - AssumeRoleArn string `json:"assumeRoleArn,omitempty"` - AuthType string `json:"authType,omitempty"` - DefaultRegion string `json:"defaultRegion,omitempty"` + AssumeRoleArn string `json:"assumeRoleArn,omitempty"` + AuthType string `json:"authType,omitempty"` + CustomMetricsNamespaces string `json:"customMetricsNamespaces,omitempty"` + DefaultRegion string `json:"defaultRegion,omitempty"` } // SecureJSONData is a representation of the datasource `secureJsonData` property @@ -48,7 +49,7 @@ func (c *Client) NewDataSource(s *DataSource) (int64, error) { if err != nil { return 0, err } - req, err := c.newRequest("POST", "/api/datasources", bytes.NewBuffer(data)) + req, err := c.newRequest("POST", "/api/datasources", nil, bytes.NewBuffer(data)) if err != nil { return 0, err } @@ -79,7 +80,7 @@ func (c *Client) UpdateDataSource(s *DataSource) error { if err != nil { return err } - req, err := c.newRequest("PUT", path, bytes.NewBuffer(data)) + req, err := c.newRequest("PUT", path, nil, bytes.NewBuffer(data)) if err != nil { return err } @@ -97,7 +98,7 @@ func (c *Client) UpdateDataSource(s *DataSource) error { func (c *Client) DataSource(id int64) (*DataSource, error) { path := fmt.Sprintf("/api/datasources/%d", id) - req, err := c.newRequest("GET", path, nil) + req, err := c.newRequest("GET", path, nil, nil) if err != nil { return nil, err } @@ -122,7 +123,7 @@ func (c *Client) DataSource(id int64) (*DataSource, error) { func (c *Client) DeleteDataSource(id int64) error { path := fmt.Sprintf("/api/datasources/%d", id) - req, err := c.newRequest("DELETE", path, nil) + req, err := c.newRequest("DELETE", path, nil, nil) if err != nil { return err } diff --git a/datasource_test.go b/datasource_test.go index baf1e17..a182ea2 100644 --- a/datasource_test.go +++ b/datasource_test.go @@ -10,6 +10,10 @@ import ( "github.com/gobs/pretty" ) +const ( + createdDataSourceJSON = `{"id":1,"message":"Datasource added", "name": "test_datasource"}` +) + func gapiTestTools(code int, body string) (*httptest.Server, *Client) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(code) @@ -46,9 +50,10 @@ func TestNewDataSource(t *testing.T) { Access: "access", IsDefault: true, JSONData: JSONData{ - AssumeRoleArn: "arn:aws:iam::123:role/some-role", - AuthType: "keys", - DefaultRegion: "us-east-1", + AssumeRoleArn: "arn:aws:iam::123:role/some-role", + AuthType: "keys", + CustomMetricsNamespaces: "SomeNamespace", + DefaultRegion: "us-east-1", }, SecureJSONData: SecureJSONData{ AccessKey: "123", diff --git a/grafana_fixtures_test.go b/grafana_fixtures_test.go deleted file mode 100644 index 26510cc..0000000 --- a/grafana_fixtures_test.go +++ /dev/null @@ -1,5 +0,0 @@ -package gapi - -const ( - createdDataSourceJSON = `{"id":1,"message":"Datasource added", "name": "test_datasource"}` -) diff --git a/org_users.go b/org_users.go new file mode 100644 index 0000000..69d8e0d --- /dev/null +++ b/org_users.go @@ -0,0 +1,95 @@ +package gapi + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io/ioutil" +) + +type OrgUser struct { + OrgId int64 `json:"orgId"` + UserId int64 `json:"userId"` + Email string `json:"email"` + Login string `json:"login"` + Role string `json:"role"` +} + +func (c *Client) OrgUsers(orgId int64) ([]OrgUser, error) { + users := make([]OrgUser, 0) + req, err := c.newRequest("GET", fmt.Sprintf("/api/orgs/%d/users", orgId), nil, nil) + if err != nil { + return users, err + } + resp, err := c.Do(req) + if err != nil { + return users, err + } + if resp.StatusCode != 200 { + return users, errors.New(resp.Status) + } + data, err := ioutil.ReadAll(resp.Body) + if err != nil { + return users, err + } + err = json.Unmarshal(data, &users) + if err != nil { + return users, err + } + return users, err +} + +func (c *Client) AddOrgUser(orgId int64, user, role string) error { + dataMap := map[string]string{ + "loginOrEmail": user, + "role": role, + } + data, err := json.Marshal(dataMap) + req, err := c.newRequest("POST", fmt.Sprintf("/api/orgs/%d/users", orgId), nil, bytes.NewBuffer(data)) + if err != nil { + return err + } + resp, err := c.Do(req) + if err != nil { + return err + } + if resp.StatusCode != 200 { + return errors.New(resp.Status) + } + return err +} + +func (c *Client) UpdateOrgUser(orgId, userId int64, role string) error { + dataMap := map[string]string{ + "role": role, + } + data, err := json.Marshal(dataMap) + req, err := c.newRequest("PATCH", fmt.Sprintf("/api/orgs/%d/users/%d", orgId, userId), nil, bytes.NewBuffer(data)) + if err != nil { + return err + } + resp, err := c.Do(req) + if err != nil { + return err + } + if resp.StatusCode != 200 { + return errors.New(resp.Status) + } + return err +} + +func (c *Client) RemoveOrgUser(orgId, userId int64) error { + req, err := c.newRequest("DELETE", fmt.Sprintf("/api/orgs/%d/users/%d", orgId, userId), nil, nil) + if err != nil { + return err + } + resp, err := c.Do(req) + if err != nil { + return err + } + if resp.StatusCode != 200 { + return errors.New(resp.Status) + } + return err +} diff --git a/org_users_test.go b/org_users_test.go new file mode 100644 index 0000000..b03a9c2 --- /dev/null +++ b/org_users_test.go @@ -0,0 +1,74 @@ +package gapi + +import ( + "github.com/gobs/pretty" + "testing" +) + +const ( + getOrgUsersJSON = `[{"orgId":1,"userId":1,"email":"admin@localhost","avatarUrl":"/avatar/46d229b033af06a191ff2267bca9ae56","login":"admin","role":"Admin","lastSeenAt":"2018-06-28T14:16:11Z","lastSeenAtAge":"\u003c 1m"}]` + addOrgUserJSON = `{"message":"User added to organization"}` + updateOrgUserJSON = `{"message":"Organization user updated"}` + removeOrgUserJSON = `{"message":"User removed from organization"}` +) + +func TestOrgUsers(t *testing.T) { + server, client := gapiTestTools(200, getOrgUsersJSON) + defer server.Close() + + org := int64(1) + resp, err := client.OrgUsers(org) + if err != nil { + t.Error(err) + } + + t.Log(pretty.PrettyFormat(resp)) + + user := OrgUser{ + OrgId: 1, + UserId: 1, + Email: "admin@localhost", + Login: "admin", + Role: "Admin", + } + + if resp[0] != user { + t.Error("Not correctly parsing returned organization users.") + } +} + +func TestAddOrgUser(t *testing.T) { + server, client := gapiTestTools(200, addOrgUserJSON) + defer server.Close() + + orgId, user, role := int64(1), "admin@localhost", "Admin" + + err := client.AddOrgUser(orgId, user, role) + if err != nil { + t.Error(err) + } +} + +func TestUpdateOrgUser(t *testing.T) { + server, client := gapiTestTools(200, updateOrgUserJSON) + defer server.Close() + + orgId, userId, role := int64(1), int64(1), "Editor" + + err := client.UpdateOrgUser(orgId, userId, role) + if err != nil { + t.Error(err) + } +} + +func TestRemoveOrgUser(t *testing.T) { + server, client := gapiTestTools(200, removeOrgUserJSON) + defer server.Close() + + orgId, userId := int64(1), int64(1) + + err := client.RemoveOrgUser(orgId, userId) + if err != nil { + t.Error(err) + } +} diff --git a/orgs.go b/orgs.go index 5a38732..2ef4e2d 100644 --- a/orgs.go +++ b/orgs.go @@ -9,14 +9,14 @@ import ( ) type Org struct { - Id int64 - Name string + Id int64 `json:"id"` + Name string `json:"name"` } func (c *Client) Orgs() ([]Org, error) { orgs := make([]Org, 0) - req, err := c.newRequest("GET", "/api/orgs/", nil) + req, err := c.newRequest("GET", "/api/orgs/", nil, nil) if err != nil { return orgs, err } @@ -35,12 +35,86 @@ func (c *Client) Orgs() ([]Org, error) { return orgs, err } -func (c *Client) NewOrg(name string) error { - settings := map[string]string{ +func (c *Client) OrgByName(name string) (Org, error) { + org := Org{} + req, err := c.newRequest("GET", fmt.Sprintf("/api/orgs/name/%s", name), nil, nil) + if err != nil { + return org, err + } + resp, err := c.Do(req) + if err != nil { + return org, err + } + if resp.StatusCode != 200 { + return org, errors.New(resp.Status) + } + data, err := ioutil.ReadAll(resp.Body) + if err != nil { + return org, err + } + err = json.Unmarshal(data, &org) + return org, err +} + +func (c *Client) Org(id int64) (Org, error) { + org := Org{} + req, err := c.newRequest("GET", fmt.Sprintf("/api/orgs/%d", id), nil, nil) + if err != nil { + return org, err + } + resp, err := c.Do(req) + if err != nil { + return org, err + } + if resp.StatusCode != 200 { + return org, errors.New(resp.Status) + } + data, err := ioutil.ReadAll(resp.Body) + if err != nil { + return org, err + } + err = json.Unmarshal(data, &org) + return org, err +} + +func (c *Client) NewOrg(name string) (int64, error) { + dataMap := map[string]string{ + "name": name, + } + data, err := json.Marshal(dataMap) + id := int64(0) + req, err := c.newRequest("POST", "/api/orgs", nil, bytes.NewBuffer(data)) + if err != nil { + return id, err + } + resp, err := c.Do(req) + if err != nil { + return id, err + } + if resp.StatusCode != 200 { + return id, errors.New(resp.Status) + } + data, err = ioutil.ReadAll(resp.Body) + if err != nil { + return id, err + } + tmp := struct { + Id int64 `json:"orgId"` + }{} + err = json.Unmarshal(data, &tmp) + if err != nil { + return id, err + } + id = tmp.Id + return id, err +} + +func (c *Client) UpdateOrg(id int64, name string) error { + dataMap := map[string]string{ "name": name, } - data, err := json.Marshal(settings) - req, err := c.newRequest("POST", "/api/orgs", bytes.NewBuffer(data)) + data, err := json.Marshal(dataMap) + req, err := c.newRequest("PUT", fmt.Sprintf("/api/orgs/%d", id), nil, bytes.NewBuffer(data)) if err != nil { return err } @@ -55,7 +129,7 @@ func (c *Client) NewOrg(name string) error { } func (c *Client) DeleteOrg(id int64) error { - req, err := c.newRequest("DELETE", fmt.Sprintf("/api/orgs/%d", id), nil) + req, err := c.newRequest("DELETE", fmt.Sprintf("/api/orgs/%d", id), nil, nil) if err != nil { return err } diff --git a/orgs_test.go b/orgs_test.go new file mode 100644 index 0000000..ca81ab4 --- /dev/null +++ b/orgs_test.go @@ -0,0 +1,103 @@ +package gapi + +import ( + "github.com/gobs/pretty" + "testing" +) + +const ( + getOrgsJSON = `[{"id":1,"name":"Main Org."},{"id":2,"name":"Test Org."}]` + getOrgJSON = `{"id":1,"name":"Main Org.","address":{"address1":"","address2":"","city":"","zipCode":"","state":"","country":""}}` + createdOrgJSON = `{"message":"Organization created","orgId":1}` + updatedOrgJSON = `{"message":"Organization updated"}` + deletedOrgJSON = `{"message":"Organization deleted"}` +) + +func TestOrgs(t *testing.T) { + server, client := gapiTestTools(200, getOrgsJSON) + defer server.Close() + + orgs, err := client.Orgs() + if err != nil { + t.Error(err) + } + + t.Log(pretty.PrettyFormat(orgs)) + + if len(orgs) != 2 { + t.Error("Length of returned orgs should be 2") + } + if orgs[0].Id != 1 || orgs[0].Name != "Main Org." { + t.Error("Not correctly parsing returned organizations.") + } +} + +func TestOrgByName(t *testing.T) { + server, client := gapiTestTools(200, getOrgJSON) + defer server.Close() + + org := "Main Org." + resp, err := client.OrgByName(org) + if err != nil { + t.Error(err) + } + + t.Log(pretty.PrettyFormat(resp)) + + if resp.Id != 1 || resp.Name != org { + t.Error("Not correctly parsing returned organization.") + } +} + +func TestOrg(t *testing.T) { + server, client := gapiTestTools(200, getOrgJSON) + defer server.Close() + + org := int64(1) + resp, err := client.Org(org) + if err != nil { + t.Error(err) + } + + t.Log(pretty.PrettyFormat(resp)) + + if resp.Id != org || resp.Name != "Main Org." { + t.Error("Not correctly parsing returned organization.") + } +} + +func TestNewOrg(t *testing.T) { + server, client := gapiTestTools(200, createdOrgJSON) + defer server.Close() + + resp, err := client.NewOrg("test-org") + if err != nil { + t.Error(err) + } + + t.Log(pretty.PrettyFormat(resp)) + + if resp != 1 { + t.Error("Not correctly parsing returned creation message.") + } +} + +func TestUpdateOrg(t *testing.T) { + server, client := gapiTestTools(200, updatedOrgJSON) + defer server.Close() + + err := client.UpdateOrg(int64(1), "test-org") + if err != nil { + t.Error(err) + } +} + +func TestDeleteOrg(t *testing.T) { + server, client := gapiTestTools(200, deletedOrgJSON) + defer server.Close() + + err := client.DeleteOrg(int64(1)) + if err != nil { + t.Error(err) + } +} diff --git a/user.go b/user.go index 13a6e6f..66f5634 100644 --- a/user.go +++ b/user.go @@ -4,19 +4,21 @@ import ( "encoding/json" "errors" "io/ioutil" + "net/url" ) type User struct { - Id int64 - Email string - Name string - Login string - IsAdmin bool + Id int64 `json:"id,omitempty"` + Email string `json:"email,omitempty"` + Name string `json:"name,omitempty"` + Login string `json:"login,omitempty"` + Password string `json:"password,omitempty"` + IsAdmin bool `json:"isAdmin,omitempty"` } func (c *Client) Users() ([]User, error) { users := make([]User, 0) - req, err := c.newRequest("GET", "/api/users", nil) + req, err := c.newRequest("GET", "/api/users", nil, nil) if err != nil { return users, err } @@ -37,3 +39,38 @@ func (c *Client) Users() ([]User, error) { } return users, err } + +func (c *Client) UserByEmail(email string) (User, error) { + user := User{} + query := url.Values{} + query.Add("loginOrEmail", email) + req, err := c.newRequest("GET", "/api/users/lookup", query, nil) + if err != nil { + return user, err + } + resp, err := c.Do(req) + if err != nil { + return user, err + } + if resp.StatusCode != 200 { + return user, errors.New(resp.Status) + } + data, err := ioutil.ReadAll(resp.Body) + if err != nil { + return user, err + } + tmp := struct { + Id int64 `json:"id,omitempty"` + Email string `json:"email,omitempty"` + Name string `json:"name,omitempty"` + Login string `json:"login,omitempty"` + Password string `json:"password,omitempty"` + IsAdmin bool `json:"isGrafanaAdmin,omitempty"` + }{} + err = json.Unmarshal(data, &tmp) + if err != nil { + return user, err + } + user = User(tmp) + return user, err +} diff --git a/user_test.go b/user_test.go new file mode 100644 index 0000000..dee1739 --- /dev/null +++ b/user_test.go @@ -0,0 +1,58 @@ +package gapi + +import ( + "github.com/gobs/pretty" + "testing" +) + +const ( + getUsersJSON = `[{"id":1,"name":"","login":"admin","email":"admin@localhost","avatarUrl":"/avatar/46d229b033af06a191ff2267bca9ae56","isAdmin":true,"lastSeenAt":"2018-06-28T14:42:24Z","lastSeenAtAge":"\u003c 1m"}]` + getUserByEmailJSON = `{"id":1,"email":"admin@localhost","name":"","login":"admin","theme":"","orgId":1,"isGrafanaAdmin":true}` +) + +func TestUsers(t *testing.T) { + server, client := gapiTestTools(200, getUsersJSON) + defer server.Close() + + resp, err := client.Users() + if err != nil { + t.Error(err) + } + + t.Log(pretty.PrettyFormat(resp)) + + user := User{ + Id: 1, + Email: "admin@localhost", + Name: "", + Login: "admin", + IsAdmin: true, + } + + if len(resp) != 1 || resp[0] != user { + t.Error("Not correctly parsing returned users.") + } +} + +func TestUserByEmail(t *testing.T) { + server, client := gapiTestTools(200, getUserByEmailJSON) + defer server.Close() + + resp, err := client.UserByEmail("admin@localhost") + if err != nil { + t.Error(err) + } + + t.Log(pretty.PrettyFormat(resp)) + + user := User{ + Id: 1, + Email: "admin@localhost", + Name: "", + Login: "admin", + IsAdmin: true, + } + if resp != user { + t.Error("Not correctly parsing returned user.") + } +}