From e88eb552f109959f8668dc02edd14a664f304ef1 Mon Sep 17 00:00:00 2001 From: supercharlesliu Date: Tue, 27 Nov 2018 11:32:35 +0800 Subject: [PATCH 01/10] Add Team CRUD API --- team.go | 208 +++++++++++++++++++++++++++++++++++++++++++++++++++ team_test.go | 90 ++++++++++++++++++++++ 2 files changed, 298 insertions(+) create mode 100644 team.go create mode 100644 team_test.go diff --git a/team.go b/team.go new file mode 100644 index 0000000..b4022fc --- /dev/null +++ b/team.go @@ -0,0 +1,208 @@ +package gapi + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io/ioutil" +) + +type Team struct { + Id int64 `json:"id,omitempty"` + OrgId int64 `json:"orgId,omitempty"` + Name string `json:"name,omitempty"` + Email string `json:"email,omitempty"` + AvatarUrl string `json:"avatarUrl,omitempty"` + MemberCount int64 `json:"memberCount,omitempty"` +} + +type TeamMember struct { + UserId int64 `json:"userId,omitempty"` + TeamId int64 `json:"teamId,omitempty"` + OrgId int64 `json:"orgId,omitempty"` + Login string `json:"login,omitempty"` + Email string `json:"email,omitempty"` + AvatarUrl string `json:"avatarUrl,omitempty"` +} + +type TeamSearchResponse struct { + TotalCount int64 `json:"totalCount,omitempty"` + Teams []*Team `json:"teams,omitempty"` + Page int64 `json:"page,omitempty"` + PerPage int64 `json:"perPage,omitempty"` +} + +type CreateTeamResponse struct { + Id int64 `json:"teamId"` +} + +func (c *Client) Team(id int64) (Team, error) { + team := Team{} + req, err := c.newRequest("GET", fmt.Sprintf("/api/teams/%d", id), nil, nil) + if err != nil { + return team, err + } + resp, err := c.Do(req) + if err != nil { + return team, err + } + if resp.StatusCode != 200 { + return team, errors.New(resp.Status) + } + data, err := ioutil.ReadAll(resp.Body) + if err != nil { + return team, err + } + err = json.Unmarshal(data, &team) + return team, err +} + +func (c *Client) Teams() ([]*Team, error) { + var list []*Team + req, err := c.newRequest("GET", "/api/teams/search", nil, nil) + if err != nil { + return list, err + } + resp, err := c.Do(req) + if err != nil { + return list, err + } + if resp.StatusCode != 200 { + return list, errors.New(resp.Status) + } + + data, err := ioutil.ReadAll(resp.Body) + if err != nil { + return list, err + } + var r TeamSearchResponse + err = json.Unmarshal(data, &r) + return r.Teams, err +} + +func (c *Client) NewTeam(name string) (Team, error) { + team := Team{ + Name: name, + } + data, err := json.Marshal(team) + req, err := c.newRequest("POST", "/api/teams", nil, bytes.NewBuffer(data)) + if err != nil { + return team, err + } + resp, err := c.Do(req) + if err != nil { + return team, err + } + if resp.StatusCode != 200 { + data, _ = ioutil.ReadAll(resp.Body) + return team, fmt.Errorf("status: %s body: %s", resp.Status, data) + } + data, err = ioutil.ReadAll(resp.Body) + if err != nil { + return team, err + } + var r CreateTeamResponse + err = json.Unmarshal(data, &r) + if err != nil { + return team, err + } + team.Id = r.Id + return team, err +} + +func (c *Client) UpdateTeam(id string, name string) error { + dataMap := map[string]string{ + "name": name, + } + data, err := json.Marshal(dataMap) + req, err := c.newRequest("PUT", fmt.Sprintf("/api/teams/%s", id), 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) DeleteTeam(id string) error { + req, err := c.newRequest("DELETE", fmt.Sprintf("/api/teams/%s", id), 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 +} + +func (c *Client) AddTeamMember(id string, userID int64) error { + dataMap := map[string]interface{}{ + "userId": userID, + } + data, err := json.Marshal(dataMap) + if err != nil { + return err + } + req, err := c.newRequest("POST", fmt.Sprintf("/api/teams/%s/members", id), 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) RemoveTeamMember(id string, userID int64) error { + req, err := c.newRequest("DELETE", fmt.Sprintf("/api/teams/%s/members/%d", id, 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 +} + +func (c *Client) TeamMembers(id string) ([]*TeamMember, error) { + req, err := c.newRequest("GET", fmt.Sprintf("/api/teams/%s/members", id), nil, nil) + if err != nil { + return nil, err + } + resp, err := c.Do(req) + if err != nil { + return nil, err + } + if resp.StatusCode != 200 { + return nil, errors.New(resp.Status) + } + + data, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + var list []*TeamMember + err = json.Unmarshal(data, &list) + return list, err +} diff --git a/team_test.go b/team_test.go new file mode 100644 index 0000000..8b3c332 --- /dev/null +++ b/team_test.go @@ -0,0 +1,90 @@ +package gapi + +import ( + "testing" + + "github.com/gobs/pretty" +) + +const ( + creatTeamJSON = `{"message":"Team created","teamId":2}` + getTeamJSON = `{ + "id": 1, + "orgId": 1, + "name": "MyTestTeam", + "email": "", + "created": "2017-12-15T10:40:45+01:00", + "updated": "2017-12-15T10:40:45+01:00" + }` + updateTeamJSON = `{"message":"Team updated"}` + deleteTeamJSON = `{"message":"Team deleted"}` + addTeamMemberJSON = `{"message":"Member added to Team"}` +) + +func TestNewTeam(t *testing.T) { + server, client := gapiTestTools(200, creatTeamJSON) + defer server.Close() + + team, err := client.NewTeam("test") + if err != nil { + t.Error(err) + } + + t.Log(pretty.PrettyFormat(team)) + + if team.Id != 2 { + t.Error("Create Team Error.") + } +} + +func TestGetTeam(t *testing.T) { + server, client := gapiTestTools(200, getTeamJSON) + defer server.Close() + + resp, err := client.Team(1) + if err != nil { + t.Error(err) + } + + t.Log(pretty.PrettyFormat(resp)) + + team := Team{ + Id: 1, + OrgId: 1, + Email: "", + Name: "MyTestTeam", + } + if resp != team { + t.Error("Not correctly parsing returned team.") + } +} + +func TestUpdateTeam(t *testing.T) { + server, client := gapiTestTools(200, updateTeamJSON) + defer server.Close() + + err := client.UpdateTeam("1", "test team 2") + if err != nil { + t.Error(err) + } +} + +func TestDeleteTeam(t *testing.T) { + server, client := gapiTestTools(200, deleteTeamJSON) + defer server.Close() + + err := client.DeleteTeam("1") + if err != nil { + t.Error(err) + } +} + +func TestAddTeamMember(t *testing.T) { + server, client := gapiTestTools(200, addTeamMemberJSON) + defer server.Close() + + err := client.AddTeamMember("1", 1) + if err != nil { + t.Error(err) + } +} From d3e68b428326b38ed7ff81768b5cf05a0594be55 Mon Sep 17 00:00:00 2001 From: supercharlesliu Date: Tue, 27 Nov 2018 11:33:38 +0800 Subject: [PATCH 02/10] Add User Get & Update API --- user.go | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/user.go b/user.go index 66f5634..32eafcf 100644 --- a/user.go +++ b/user.go @@ -1,8 +1,10 @@ package gapi import ( + "bytes" "encoding/json" "errors" + "fmt" "io/ioutil" "net/url" ) @@ -16,6 +18,12 @@ type User struct { IsAdmin bool `json:"isAdmin,omitempty"` } +type UserUpdate struct { + Email string `json:"email,omitempty"` + Name string `json:"name,omitempty"` + Login string `json:"login,omitempty"` +} + func (c *Client) Users() ([]User, error) { users := make([]User, 0) req, err := c.newRequest("GET", "/api/users", nil, nil) @@ -40,6 +48,45 @@ func (c *Client) Users() ([]User, error) { return users, err } +func (c *Client) User(userId int64) (User, error) { + var user User + req, err := c.newRequest("GET", fmt.Sprintf("/api/users/%d", userId), nil, 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 + } + + err = json.Unmarshal(data, &user) + return user, err +} + +func (c *Client) UpdateUser(userId int64, u UserUpdate) error { + data, err := json.Marshal(u) + req, err := c.newRequest("PUT", fmt.Sprintf("/api/users/%d", userId), nil, bytes.NewBuffer(data)) + if err != nil { + return err + } + resp, err := c.Do(req) + if err != nil { + return err + } + if resp.StatusCode != 200 { + data, _ = ioutil.ReadAll(resp.Body) + return fmt.Errorf("status: %s body: %s", resp.Status, data) + } + return nil +} + func (c *Client) UserByEmail(email string) (User, error) { user := User{} query := url.Values{} From b8113e3b952fec13362e48f4198853b781d2f10b Mon Sep 17 00:00:00 2001 From: supercharlesliu Date: Tue, 27 Nov 2018 11:39:45 +0800 Subject: [PATCH 03/10] Add tests for User Get & Update APIs --- user_test.go | 44 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/user_test.go b/user_test.go index dee1739..d874654 100644 --- a/user_test.go +++ b/user_test.go @@ -1,12 +1,15 @@ package gapi import ( - "github.com/gobs/pretty" "testing" + + "github.com/gobs/pretty" ) const ( getUsersJSON = `[{"id":1,"name":"","login":"admin","email":"admin@localhost","avatarUrl":"/avatar/46d229b033af06a191ff2267bca9ae56","isAdmin":true,"lastSeenAt":"2018-06-28T14:42:24Z","lastSeenAtAge":"\u003c 1m"}]` + getUserJSON = `{"id": 1, "email": "user@mygraf.com", "name": "admin", "login": "admin", "theme": "light", "orgId": 1, "isGrafanaAdmin": true}` + updateUserJSON = `{"message":"User updated"}` getUserByEmailJSON = `{"id":1,"email":"admin@localhost","name":"","login":"admin","theme":"","orgId":1,"isGrafanaAdmin":true}` ) @@ -34,6 +37,45 @@ func TestUsers(t *testing.T) { } } +func TestGetUser(t *testing.T) { + server, client := gapiTestTools(200, getUserJSON) + defer server.Close() + + resp, err := client.User(1) + if err != nil { + t.Error(err) + } + + t.Log(pretty.PrettyFormat(resp)) + + user := User{ + Id: 1, + Email: "user@mygraf.com", + Name: "admin", + Login: "admin", + IsAdmin: true, + } + if resp.Id != user.Id { + t.Error("Not correctly parsing returned user.") + } +} + +func TestUpdateUser(t *testing.T) { + server, client := gapiTestTools(200, updateUserJSON) + defer server.Close() + + userUpdate := UserUpdate{ + Email: "admin@localhost", + Name: "", + Login: "admin", + } + + err := client.UpdateUser(1, userUpdate) + if err != nil { + t.Error(err) + } +} + func TestUserByEmail(t *testing.T) { server, client := gapiTestTools(200, getUserByEmailJSON) defer server.Close() From 573ec74dba545c4fb4fe7d851e896517b7277b9a Mon Sep 17 00:00:00 2001 From: supercharlesliu Date: Tue, 27 Nov 2018 11:48:04 +0800 Subject: [PATCH 04/10] Add foler permission GET & Update api --- folder.go | 48 ++++++++++++++++++++++++++++++++ folder_test.go | 75 +++++++++++++++++++++++++++++++++++++++++++++++++- permission.go | 54 ++++++++++++++++++++++++++++++++++++ 3 files changed, 176 insertions(+), 1 deletion(-) create mode 100644 permission.go diff --git a/folder.go b/folder.go index 55613c6..2379157 100644 --- a/folder.go +++ b/folder.go @@ -14,6 +14,13 @@ type Folder struct { Title string `json:"title"` } +type FolderPermission struct { + Role string `json:"role,omitempty"` + TeamId int64 `json:"teamId,omitempty"` + UserId int64 `json:"userId,omitempty"` + Permission PermissionType `json:"permission,omitempty"` +} + func (c *Client) Folders() ([]Folder, error) { folders := make([]Folder, 0) @@ -119,3 +126,44 @@ func (c *Client) DeleteFolder(id string) error { } return err } + +func (c *Client) GetFolderPermission(id string) ([]*FolderPermission, error) { + var permissionList []*FolderPermission + req, err := c.newRequest("GET", fmt.Sprintf("/api/folders/%s/permissions", id), nil, nil) + if err != nil { + return permissionList, err + } + resp, err := c.Do(req) + if err != nil { + return permissionList, err + } + if resp.StatusCode != 200 { + return permissionList, errors.New(resp.Status) + } + + data, err := ioutil.ReadAll(resp.Body) + if err != nil { + return permissionList, err + } + err = json.Unmarshal(data, &permissionList) + return permissionList, err +} + +func (c *Client) UpdateFolderPermission(id string, list []*FolderPermission) error { + dataMap := map[string]interface{}{ + "items": list, + } + data, err := json.Marshal(dataMap) + req, err := c.newRequest("POST", fmt.Sprintf("/api/folders/%s/permissions", id), 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 nil +} diff --git a/folder_test.go b/folder_test.go index f02f313..196fc96 100644 --- a/folder_test.go +++ b/folder_test.go @@ -1,8 +1,9 @@ package gapi import ( - "github.com/gobs/pretty" "testing" + + "github.com/gobs/pretty" ) const ( @@ -81,6 +82,49 @@ const ( "message":"Folder deleted" } ` + getFolderPermissionJSON = ` + [ + { + "id": 1, + "folderId": -1, + "created": "2017-06-20T02:00:00+02:00", + "updated": "2017-06-20T02:00:00+02:00", + "userId": 0, + "userLogin": "", + "userEmail": "", + "teamId": 0, + "team": "", + "role": "Viewer", + "permission": 1, + "permissionName": "View", + "uid": "nErXDvCkzz", + "title": "", + "slug": "", + "isFolder": false, + "url": "" + }, + { + "id": 2, + "dashboardId": -1, + "created": "2017-06-20T02:00:00+02:00", + "updated": "2017-06-20T02:00:00+02:00", + "userId": 0, + "userLogin": "", + "userEmail": "", + "teamId": 0, + "team": "", + "role": "Editor", + "permission": 2, + "permissionName": "Edit", + "uid": "", + "title": "", + "slug": "", + "isFolder": false, + "url": "" + } +] + ` + updateFolderPermissionJSON = `{"message":"Folder permissions updated"}` ) func TestFolders(t *testing.T) { @@ -154,3 +198,32 @@ func TestDeleteFolder(t *testing.T) { t.Error(err) } } + +func TestGetFolderPermission(t *testing.T) { + server, client := gapiTestTools(200, getFolderPermissionJSON) + defer server.Close() + + permList, err := client.GetFolderPermission("nErXDvCkzz") + if err != nil { + t.Error(err) + } + + if len(permList) == 0 { + t.Errorf("Error Response") + } +} + +func TestUpdateFolderPermission(t *testing.T) { + server, client := gapiTestTools(200, updateFolderPermissionJSON) + defer server.Close() + + list := make([]*FolderPermission, 0) + list = append(list, &FolderPermission{ + Role: "Viewer", + Permission: PermissionView, + }) + err := client.UpdateFolderPermission("nErXDvCkzz", list) + if err != nil { + t.Error(err) + } +} diff --git a/permission.go b/permission.go new file mode 100644 index 0000000..ac6cc44 --- /dev/null +++ b/permission.go @@ -0,0 +1,54 @@ +package gapi + +import ( + "errors" + "fmt" + "strconv" +) + +type PermissionType int8 + +const ( + None PermissionType = 0 + PermissionView PermissionType = 1 + PermissionEdit PermissionType = 2 + PermissionAdmin PermissionType = 4 +) + +func NewPermissionType(p int) (PermissionType, error) { + switch p { + case 1: + return PermissionView, nil + case 2: + return PermissionEdit, nil + case 4: + return PermissionAdmin, nil + } + return None, errors.New(fmt.Sprintf("Unknow permission: %d", p)) +} + +func (p *PermissionType) Value() int8 { + switch *p { + case PermissionView: + return 1 + case PermissionEdit: + return 2 + case PermissionAdmin: + return 4 + } + return 0 +} + +func (p *PermissionType) String() string { + return strconv.FormatInt(int64(p.Value()), 10) +} + +type Permission struct { + Id int64 `json:"id,omitempty"` + FolderUid string `json:"folderUid,omitempty"` + UserId int64 `json:"userId,omitempty"` + TeamId int64 `json:"teamId,omitempty"` + Role string `json:"role,omitempty"` + Permission int `json:"permission,omitempty"` + IsFolder bool `json:"isFolder,omitempty"` +} From dbed9b661acab40e0f5061e32346a27eb0ae24b4 Mon Sep 17 00:00:00 2001 From: supercharlesliu Date: Tue, 27 Nov 2018 11:51:51 +0800 Subject: [PATCH 05/10] Add Datasources API --- datasource.go | 24 ++++++++++++++++++++++++ datasource_test.go | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/datasource.go b/datasource.go index a5b40de..0f22b25 100644 --- a/datasource.go +++ b/datasource.go @@ -122,6 +122,30 @@ func (c *Client) DataSource(id int64) (*DataSource, error) { return result, err } +func (c *Client) DataSources() ([]*DataSource, error) { + req, err := c.newRequest("GET", "/api/datasources", nil, nil) + if err != nil { + return nil, err + } + + resp, err := c.Do(req) + if err != nil { + return nil, err + } + if resp.StatusCode != 200 { + return nil, errors.New(resp.Status) + } + + data, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + var result []*DataSource + err = json.Unmarshal(data, &result) + return result, err +} + func (c *Client) DeleteDataSource(id int64) error { path := fmt.Sprintf("/api/datasources/%d", id) req, err := c.newRequest("DELETE", path, nil, nil) diff --git a/datasource_test.go b/datasource_test.go index c2ee1b7..cddd303 100644 --- a/datasource_test.go +++ b/datasource_test.go @@ -12,6 +12,26 @@ import ( const ( createdDataSourceJSON = `{"id":1,"message":"Datasource added", "name": "test_datasource"}` + getDataSourcesJSON = ` + [ + { + "id":1, + "orgId":1, + "name":"datasource_elastic", + "type":"elasticsearch", + "access":"proxy", + "url":"http://mydatasource.com", + "password":"", + "user":"", + "database":"grafana-dash", + "basicAuth":false, + "basicAuthUser":"", + "basicAuthPassword":"", + "isDefault":false, + "jsonData":null + } + ] + ` ) func gapiTestTools(code int, body string) (*httptest.Server, *Client) { @@ -73,3 +93,16 @@ func TestNewDataSource(t *testing.T) { t.Error("datasource creation response should return the created datasource ID") } } + +func TestDataSources(t *testing.T) { + server, client := gapiTestTools(200, getDataSourcesJSON) + defer server.Close() + + list, err := client.DataSources() + if err != nil { + t.Error(err) + } + if len(list) == 0 { + t.Error("Datasources parse error") + } +} From 9c8633cebd131608795816d2351cffb4823f1053 Mon Sep 17 00:00:00 2001 From: supercharlesliu Date: Tue, 27 Nov 2018 11:53:16 +0800 Subject: [PATCH 06/10] Add OrgPreferences Get & Update APIs --- orgs.go | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/orgs.go b/orgs.go index 2ef4e2d..9b64ebf 100644 --- a/orgs.go +++ b/orgs.go @@ -13,6 +13,13 @@ type Org struct { Name string `json:"name"` } +type OrgPreferences struct { + Theme string `json:"theme,omitempty"` + HomeDashboardUid string `json:"homeDashboardUid,omitempty"` + HomeDashboardId int64 `json:"homeDashboardId,omitempty"` + Timezone string `json:"timezone,omitempty"` +} + func (c *Client) Orgs() ([]Org, error) { orgs := make([]Org, 0) @@ -128,6 +135,62 @@ func (c *Client) UpdateOrg(id int64, name string) error { return err } +func (c *Client) GetOrgPreferences() (*OrgPreferences, error) { + var prefs *OrgPreferences + req, err := c.newRequest("GET", "/api/org/preferences", nil, nil) + if err != nil { + return prefs, err + } + resp, err := c.Do(req) + if err != nil { + return prefs, err + } + if resp.StatusCode != 200 { + return prefs, errors.New(resp.Status) + } + data, err := ioutil.ReadAll(resp.Body) + if err != nil { + return prefs, err + } + err = json.Unmarshal(data, &prefs) + if err != nil { + return prefs, err + } + + if prefs.HomeDashboardId != 0 { + uid, err := c.GetDashboardUidById(prefs.HomeDashboardId) + if err != nil { + return prefs, err + } + prefs.HomeDashboardUid = uid + } + return prefs, err +} + +func (c *Client) UpdateOrgPreferences(prefs *OrgPreferences) error { + if prefs.HomeDashboardUid != "" { + id, err := c.GetDashboardIdByUid(prefs.HomeDashboardUid) + if err != nil { + return err + } + prefs.HomeDashboardId = id + } + + data, err := json.Marshal(prefs) + req, err := c.newRequest("PUT", "/api/org/preferences", 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 nil +} + func (c *Client) DeleteOrg(id int64) error { req, err := c.newRequest("DELETE", fmt.Sprintf("/api/orgs/%d", id), nil, nil) if err != nil { From 2a14e14addfecd23db1d41b6d70e1b2c3717f40b Mon Sep 17 00:00:00 2001 From: supercharlesliu Date: Tue, 27 Nov 2018 11:58:56 +0800 Subject: [PATCH 07/10] Add tests for GetOrgPreferences & UpdateOrgPreferences --- orgs_test.go | 43 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/orgs_test.go b/orgs_test.go index ca81ab4..9a6c31b 100644 --- a/orgs_test.go +++ b/orgs_test.go @@ -1,16 +1,19 @@ package gapi import ( - "github.com/gobs/pretty" "testing" + + "github.com/gobs/pretty" ) 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"}` + 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"}` + getOrgPerfsJSON = `{"theme": "","homeDashboardId":0,"timezone":"utc"}` + updateOrgPerfsJSON = `{"message":"Preferences updated"}` ) func TestOrgs(t *testing.T) { @@ -101,3 +104,31 @@ func TestDeleteOrg(t *testing.T) { t.Error(err) } } + +func TestGetOrgPreferences(t *testing.T) { + server, client := gapiTestTools(200, getOrgPerfsJSON) + defer server.Close() + + prefs, err := client.GetOrgPreferences() + if err != nil { + t.Error(err) + } + if prefs == nil { + t.Errorf("Org Preferences should not be nil") + } +} + +func TestUpdateOrgPreferences(t *testing.T) { + server, client := gapiTestTools(200, updateOrgPerfsJSON) + defer server.Close() + + prefs := &OrgPreferences{ + Theme: "light", + HomeDashboardId: 0, + Timezone: "utc", + } + err := client.UpdateOrgPreferences(prefs) + if err != nil { + t.Error(err) + } +} From 826fe8803939f24b31750c94e609cd5c76301a42 Mon Sep 17 00:00:00 2001 From: supercharlesliu Date: Tue, 27 Nov 2018 12:07:02 +0800 Subject: [PATCH 08/10] Add DashboardsByFolder & DashboardByUid * Add DashboardsByFolder to search dashborads in a folder * Add DashboardByUid to support get dashboard by uid * Add GetDashboardIdByUid, GetDashboardUidById to support id <=> uid --- dashboard.go | 134 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) diff --git a/dashboard.go b/dashboard.go index 2d6418f..fddc233 100644 --- a/dashboard.go +++ b/dashboard.go @@ -7,12 +7,16 @@ import ( "fmt" "io/ioutil" "log" + "net/url" "os" + "strconv" + "strings" ) type DashboardMeta struct { IsStarred bool `json:"isStarred"` Slug string `json:"slug"` + Uid string `json:"uid"` Folder int64 `json:"folderId"` } @@ -24,6 +28,30 @@ type DashboardSaveResponse struct { Version int64 `json:"version"` } +const ( + SearchTypeFolder = "dash-folder" + SearchTypeDashboard = "dash-db" +) + +type SearchResultItem struct { + Id int64 + Uid string + Title string + Url string + Type string + Uri string +} + +func (item *SearchResultItem) IsFolder() bool { + return item.Type == SearchTypeFolder +} +func (item *SearchResultItem) Slug() string { + if item.IsFolder() { + return "" + } + return strings.Replace(item.Uri, "db/", "", 1) +} + type Dashboard struct { Meta DashboardMeta `json:"meta"` Model map[string]interface{} `json:"dashboard"` @@ -31,6 +59,16 @@ type Dashboard struct { Overwrite bool `json:overwrite` } +type DashboardData struct { + Id int64 `json:"id"` + Uid string `json:"uid"` + Title string `json:"title"` +} +type DashboardVersion struct { + Id int64 `json:"id"` + Data *DashboardData `json:"data"` +} + // Deprecated: use NewDashboard instead func (c *Client) SaveDashboard(model map[string]interface{}, overwrite bool) (*DashboardSaveResponse, error) { wrapper := map[string]interface{}{ @@ -119,6 +157,65 @@ func (c *Client) Dashboard(slug string) (*Dashboard, error) { if os.Getenv("GF_LOG") != "" { log.Printf("got back dashboard response %s", data) } + result.Meta.Uid = result.Model["uid"].(string) + return result, err +} + +func (c *Client) DashboardByUid(uid string) (*Dashboard, error) { + path := fmt.Sprintf("/api/dashboards/uid/%s", uid) + req, err := c.newRequest("GET", path, nil, nil) + if err != nil { + return nil, err + } + + resp, err := c.Do(req) + if err != nil { + return nil, err + } + if resp.StatusCode != 200 { + return nil, errors.New(resp.Status) + } + + data, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + result := &Dashboard{} + err = json.Unmarshal(data, &result) + result.Folder = result.Meta.Folder + if os.Getenv("GF_LOG") != "" { + log.Printf("got back dashboard response %s", data) + } + result.Meta.Uid = result.Model["uid"].(string) + return result, err +} + +func (c *Client) DashboardsByFolder(folderId int64) ([]*SearchResultItem, error) { + values := url.Values{} + values.Add("folderIds", strconv.Itoa(int(folderId))) + values.Add("type", "dash-db") + + req, err := c.newRequest("GET", "/api/search", values, nil) + if err != nil { + return nil, err + } + + resp, err := c.Do(req) + if err != nil { + return nil, err + } + if resp.StatusCode != 200 { + return nil, errors.New(resp.Status) + } + + data, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + var result []*SearchResultItem + err = json.Unmarshal(data, &result) return result, err } @@ -139,3 +236,40 @@ func (c *Client) DeleteDashboard(slug string) error { return nil } + +// Add this api to transform id to uid +func (c *Client) GetDashboardUidById(id int64) (string, error) { + path := fmt.Sprintf("/api/dashboards/id/%d/versions/1", id) + req, err := c.newRequest("GET", path, 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) + } + + data, err := ioutil.ReadAll(resp.Body) + if err != nil { + return "", err + } + + var result DashboardVersion + err = json.Unmarshal(data, &result) + if err != nil { + return "", err + } + return result.Data.Uid, err +} + +func (c *Client) GetDashboardIdByUid(uid string) (int64, error) { + d, err := c.DashboardByUid(uid) + if err != nil { + return 0, err + } + return int64(d.Model["id"].(float64)), nil +} From b1bdedfec7eedcc323400c9fcd574e29bd4c69ec Mon Sep 17 00:00:00 2001 From: supercharlesliu Date: Tue, 27 Nov 2018 17:06:58 +0800 Subject: [PATCH 09/10] Add GetDashboardVersions API --- dashboard.go | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/dashboard.go b/dashboard.go index fddc233..6e874fd 100644 --- a/dashboard.go +++ b/dashboard.go @@ -64,6 +64,10 @@ type DashboardData struct { Uid string `json:"uid"` Title string `json:"title"` } +type DashboardVersionItem struct { + Id int64 `json:"id"` + Version int64 `json:"version"` +} type DashboardVersion struct { Id int64 `json:"id"` Data *DashboardData `json:"data"` @@ -237,9 +241,41 @@ func (c *Client) DeleteDashboard(slug string) error { return nil } +func (c *Client) GetDashboardVersions(id int64) ([]*DashboardVersionItem, error) { + var list []*DashboardVersionItem + path := fmt.Sprintf("/api/dashboards/id/%d/versions/", id) + req, err := c.newRequest("GET", path, nil, nil) + if err != nil { + return list, err + } + + resp, err := c.Do(req) + if err != nil { + return list, err + } + if resp.StatusCode != 200 { + return list, errors.New(resp.Status) + } + + data, err := ioutil.ReadAll(resp.Body) + if err != nil { + return list, err + } + + err = json.Unmarshal(data, &list) + if err != nil { + return list, err + } + return list, err +} + // Add this api to transform id to uid func (c *Client) GetDashboardUidById(id int64) (string, error) { - path := fmt.Sprintf("/api/dashboards/id/%d/versions/1", id) + versions, err := c.GetDashboardVersions(id) + if err != nil { + return "", err + } + path := fmt.Sprintf("/api/dashboards/id/%d/versions/%d", id, versions[0].Version) req, err := c.newRequest("GET", path, nil, nil) if err != nil { return "", err From 3f9b9f5c1abb8bd2b05f33d459d7b82a8a36b507 Mon Sep 17 00:00:00 2001 From: supercharlesliu Date: Tue, 27 Nov 2018 18:13:26 +0800 Subject: [PATCH 10/10] Fix permission json Marshal and Unmarshal --- permission.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/permission.go b/permission.go index ac6cc44..267fce4 100644 --- a/permission.go +++ b/permission.go @@ -1,6 +1,7 @@ package gapi import ( + "encoding/json" "errors" "fmt" "strconv" @@ -39,6 +40,20 @@ func (p *PermissionType) Value() int8 { return 0 } +func (d *PermissionType) UnmarshalJSON(b []byte) error { + var s int64 + if err := json.Unmarshal(b, &s); err != nil { + return err + } + var err error + *d, err = NewPermissionType(int(s)) + return err +} + +func (d *PermissionType) MarshalJSON() ([]byte, error) { + return []byte(d.String()), nil +} + func (p *PermissionType) String() string { return strconv.FormatInt(int64(p.Value()), 10) }