Skip to content
This repository was archived by the owner on Jan 15, 2024. It is now read-only.

Commit 50d2d63

Browse files
Jguervtorosyan
andauthored
Add service account methods (#87)
* add service account token methods * solve linting issues * Add service account routes Co-authored-by: Vardan Torosyan <vardants@gmail.com>
1 parent f5da2d4 commit 50d2d63

File tree

4 files changed

+320
-4
lines changed

4 files changed

+320
-4
lines changed

api_key_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import (
77
)
88

99
const (
10-
createAPIKeyJSON = `{"name":"key-name", "key":"mock-api-key"}`
11-
deleteAPIKeyJSON = `{"message":"API key deleted"}`
10+
createAPIKeyJSON = `{"name":"key-name", "key":"mock-api-key"}` //#nosec
11+
deleteAPIKeyJSON = `{"message":"API key deleted"}` //#nosec
1212

1313
getAPIKeysJSON = `[
1414
{
@@ -22,7 +22,7 @@ const (
2222
"role": "Admin",
2323
"expiration": "2021-10-30T10:52:03+03:00"
2424
}
25-
]`
25+
]` //#nosec
2626
)
2727

2828
func TestCreateAPIKey(t *testing.T) {

playlist_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ func TestPlaylistCreateAndUpdate(t *testing.T) {
4545
Name: "my playlist",
4646
Interval: "5m",
4747
Items: []PlaylistItem{
48-
PlaylistItem{},
48+
{},
4949
},
5050
}
5151

service_account.go

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
package gapi
2+
3+
import (
4+
"bytes"
5+
"encoding/json"
6+
"fmt"
7+
"net/http"
8+
"time"
9+
)
10+
11+
// CreateServiceAccountTokenRequest represents the request body for creating a new service account token.
12+
type CreateServiceAccountTokenRequest struct {
13+
Name string `json:"name"`
14+
ServiceAccountID int64 `json:"-"`
15+
SecondsToLive int64 `json:"secondsToLive,omitempty"`
16+
}
17+
18+
// CreateServiceAccountRequest is the request body for creating a new service account.
19+
type CreateServiceAccountRequest struct {
20+
Name string `json:"name"`
21+
}
22+
23+
// UpdateServiceAccountRequest is the request body for modifying a service account.
24+
type UpdateServiceAccountRequest struct {
25+
Name string `json:"name,omitempty"`
26+
Role string `json:"role,omitempty"`
27+
IsDisabled *bool `json:"isDisabled,omitempty"`
28+
}
29+
30+
// ServiceAccountDTO represents a Grafana service account.
31+
type ServiceAccountDTO struct {
32+
ID int64 `json:"id"`
33+
Name string `json:"name"`
34+
Login string `json:"login"`
35+
OrgID int64 `json:"orgId"`
36+
IsDisabled bool `json:"isDisabled"`
37+
Role string `json:"role"`
38+
Tokens int64 `json:"tokens"`
39+
AvatarURL string `json:"avatarUrl"`
40+
}
41+
42+
// CreateServiceAccountTokenResponse represents the response
43+
// from the Grafana API when creating a service account token.
44+
type CreateServiceAccountTokenResponse struct {
45+
ID int64 `json:"id"`
46+
Name string `json:"name"`
47+
Key string `json:"key"`
48+
}
49+
50+
// GetServiceAccountTokensResponse represents a Grafana service account token.
51+
type GetServiceAccountTokensResponse struct {
52+
ID int64 `json:"id"`
53+
Name string `json:"name"`
54+
Created time.Time `json:"created,omitempty"`
55+
Expiration *time.Time `json:"expiration,omitempty"`
56+
SecondsUntilExpiration *float64 `json:"secondsUntilExpiration,omitempty"`
57+
HasExpired bool `json:"hasExpired,omitempty"`
58+
}
59+
60+
// DeleteServiceAccountResponse represents the response from deleting a service account
61+
// or a service account token.
62+
type DeleteServiceAccountResponse struct {
63+
Message string `json:"message"`
64+
}
65+
66+
// CreateServiceAccount creates a new Grafana service account.
67+
func (c *Client) CreateServiceAccount(request CreateServiceAccountRequest) (*ServiceAccountDTO, error) {
68+
response := ServiceAccountDTO{}
69+
70+
data, err := json.Marshal(request)
71+
if err != nil {
72+
return nil, err
73+
}
74+
75+
err = c.request(http.MethodPost, "/api/serviceaccounts/", nil, bytes.NewBuffer(data), &response)
76+
return &response, err
77+
}
78+
79+
// CreateServiceAccountToken creates a new Grafana service account token.
80+
func (c *Client) CreateServiceAccountToken(request CreateServiceAccountTokenRequest) (*CreateServiceAccountTokenResponse, error) {
81+
response := CreateServiceAccountTokenResponse{}
82+
83+
data, err := json.Marshal(request)
84+
if err != nil {
85+
return nil, err
86+
}
87+
88+
err = c.request(http.MethodPost,
89+
fmt.Sprintf("/api/serviceaccounts/%d/tokens", request.ServiceAccountID),
90+
nil, bytes.NewBuffer(data), &response)
91+
return &response, err
92+
}
93+
94+
// UpdateServiceAccount updates a specific serviceAccountID
95+
func (c *Client) UpdateServiceAccount(serviceAccountID int64, request UpdateServiceAccountRequest) (*ServiceAccountDTO, error) {
96+
response := ServiceAccountDTO{}
97+
98+
data, err := json.Marshal(request)
99+
if err != nil {
100+
return nil, err
101+
}
102+
103+
err = c.request(http.MethodPatch,
104+
fmt.Sprintf("/api/serviceaccounts/%d", serviceAccountID),
105+
nil, bytes.NewBuffer(data), &response)
106+
return &response, err
107+
}
108+
109+
// GetServiceAccounts retrieves a list of all service accounts for the organization.
110+
func (c *Client) GetServiceAccounts() ([]ServiceAccountDTO, error) {
111+
response := make([]ServiceAccountDTO, 0)
112+
113+
err := c.request(http.MethodGet, "/api/serviceaccounts/search", nil, nil, &response)
114+
return response, err
115+
}
116+
117+
// GetServiceAccountTokens retrieves a list of all service account tokens for a specific service account.
118+
func (c *Client) GetServiceAccountTokens(serviceAccountID int64) ([]GetServiceAccountTokensResponse, error) {
119+
response := make([]GetServiceAccountTokensResponse, 0)
120+
121+
err := c.request(http.MethodGet,
122+
fmt.Sprintf("/api/serviceaccounts/%d/tokens", serviceAccountID),
123+
nil, nil, &response)
124+
return response, err
125+
}
126+
127+
// DeleteServiceAccount deletes the Grafana service account with the specified ID.
128+
func (c *Client) DeleteServiceAccount(serviceAccountID int64) (*DeleteServiceAccountResponse, error) {
129+
response := DeleteServiceAccountResponse{}
130+
131+
path := fmt.Sprintf("/api/serviceaccounts/%d", serviceAccountID)
132+
err := c.request(http.MethodDelete, path, nil, nil, &response)
133+
return &response, err
134+
}
135+
136+
// DeleteServiceAccountToken deletes the Grafana service account token with the specified ID.
137+
func (c *Client) DeleteServiceAccountToken(serviceAccountID, tokenID int64) (*DeleteServiceAccountResponse, error) {
138+
response := DeleteServiceAccountResponse{}
139+
140+
path := fmt.Sprintf("/api/serviceaccounts/%d/tokens/%d", serviceAccountID, tokenID)
141+
err := c.request(http.MethodDelete, path, nil, nil, &response)
142+
return &response, err
143+
}

service_account_test.go

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
package gapi
2+
3+
import (
4+
"net/http"
5+
"testing"
6+
7+
"github.com/gobs/pretty"
8+
)
9+
10+
const (
11+
serviceAccountJSON = `{
12+
"id": 8,
13+
"name": "newSA",
14+
"login": "sa-newsa",
15+
"orgId": 1,
16+
"isDisabled": false,
17+
"role": "",
18+
"tokens": 0,
19+
"avatarUrl": ""
20+
}`
21+
searchServiceAccountsJSON = `[
22+
{
23+
"id": 8,
24+
"name": "newSA",
25+
"login": "sa-newsa",
26+
"orgId": 1,
27+
"isDisabled": false,
28+
"role": "",
29+
"tokens": 1,
30+
"avatarUrl": "/avatar/0e94f33c929884a5163d953582f27fec"
31+
},
32+
{
33+
"id": 9,
34+
"name": "newnewSA",
35+
"login": "sa-newnewsa",
36+
"orgId": 1,
37+
"isDisabled": true,
38+
"role": "Admin",
39+
"tokens": 2,
40+
"avatarUrl": "/avatar/0e29f33c929824a5163d953582e83abe"
41+
}
42+
]`
43+
createServiceAccountTokenJSON = `{"name":"key-name", "key":"mock-api-key"}` //#nosec
44+
deleteServiceAccountTokenJSON = `{"message":"Service account token deleted"}` //#nosec
45+
deleteServiceAccountJSON = `{"message":"service account deleted"}`
46+
47+
getServiceAccountTokensJSON = `[
48+
{
49+
"id": 4,
50+
"name": "testToken",
51+
"created": "2022-06-15T15:19:00+02:00",
52+
"expiration": "2022-06-15T16:17:20+02:00",
53+
"secondsUntilExpiration": 3412.443626017,
54+
"hasExpired": false
55+
},
56+
{
57+
"id": 1,
58+
"name": "testToken2",
59+
"created": "2022-01-15T15:19:00+02:00",
60+
"expiration": "2022-02-15T16:17:20+02:00",
61+
"secondsUntilExpiration": 0,
62+
"hasExpired": true
63+
},
64+
{
65+
"id": 6,
66+
"name": "testTokenzx",
67+
"created": "2022-06-15T15:39:54+02:00",
68+
"expiration": null,
69+
"secondsUntilExpiration": 0,
70+
"hasExpired": false
71+
}
72+
]` //#nosec
73+
)
74+
75+
func TestCreateServiceAccountToken(t *testing.T) {
76+
server, client := gapiTestTools(t, http.StatusOK, createServiceAccountTokenJSON)
77+
defer server.Close()
78+
79+
req := CreateServiceAccountTokenRequest{
80+
Name: "key-name",
81+
SecondsToLive: 0,
82+
}
83+
84+
res, err := client.CreateServiceAccountToken(req)
85+
if err != nil {
86+
t.Error(err)
87+
}
88+
89+
t.Log(pretty.PrettyFormat(res))
90+
}
91+
92+
func TestCreateServiceAccount(t *testing.T) {
93+
server, client := gapiTestTools(t, http.StatusOK, serviceAccountJSON)
94+
defer server.Close()
95+
96+
req := CreateServiceAccountRequest{
97+
Name: "newSA",
98+
}
99+
100+
res, err := client.CreateServiceAccount(req)
101+
if err != nil {
102+
t.Error(err)
103+
}
104+
105+
t.Log(pretty.PrettyFormat(res))
106+
}
107+
108+
func TestUpdateServiceAccount(t *testing.T) {
109+
server, client := gapiTestTools(t, http.StatusOK, serviceAccountJSON)
110+
defer server.Close()
111+
112+
isDisabled := false
113+
req := UpdateServiceAccountRequest{
114+
Name: "",
115+
Role: "Admin",
116+
IsDisabled: &isDisabled,
117+
}
118+
119+
res, err := client.UpdateServiceAccount(5, req)
120+
if err != nil {
121+
t.Error(err)
122+
}
123+
124+
t.Log(pretty.PrettyFormat(res))
125+
}
126+
127+
func TestDeleteServiceAccount(t *testing.T) {
128+
server, client := gapiTestTools(t, http.StatusOK, deleteServiceAccountJSON)
129+
defer server.Close()
130+
131+
res, err := client.DeleteServiceAccount(int64(1))
132+
if err != nil {
133+
t.Error(err)
134+
}
135+
136+
t.Log(pretty.PrettyFormat(res))
137+
}
138+
139+
func TestDeleteServiceAccountToken(t *testing.T) {
140+
server, client := gapiTestTools(t, http.StatusOK, deleteServiceAccountTokenJSON)
141+
defer server.Close()
142+
143+
res, err := client.DeleteServiceAccountToken(int64(1), int64(1))
144+
if err != nil {
145+
t.Error(err)
146+
}
147+
148+
t.Log(pretty.PrettyFormat(res))
149+
}
150+
151+
func TestGetServiceAccounts(t *testing.T) {
152+
server, client := gapiTestTools(t, http.StatusOK, searchServiceAccountsJSON)
153+
defer server.Close()
154+
155+
res, err := client.GetServiceAccounts()
156+
if err != nil {
157+
t.Error(err)
158+
}
159+
160+
t.Log(pretty.PrettyFormat(res))
161+
}
162+
163+
func TestGetServiceAccountTokens(t *testing.T) {
164+
server, client := gapiTestTools(t, http.StatusOK, getServiceAccountTokensJSON)
165+
defer server.Close()
166+
167+
res, err := client.GetServiceAccountTokens(5)
168+
if err != nil {
169+
t.Error(err)
170+
}
171+
172+
t.Log(pretty.PrettyFormat(res))
173+
}

0 commit comments

Comments
 (0)