Repository: xanzy/go-gitlab
Pull request #1434 (2022-03-26 17:07:46)

Support GET endpoints to retrieve single GAT and PAT
I've recently implemented this in GitLab and is available since 14.10:

* https://gitlab.com/gitlab-org/gitlab/-/merge_requests/82714


Patch:
diff --git a/group_access_tokens.go b/group_access_tokens.go
index 41532bf..0f57c37 100644
--- a/group_access_tokens.go
+++ b/group_access_tokens.go
@@ -30,7 +30,7 @@ type GroupAccessTokensService struct {
 	client *Client
 }
 
-// GroupAccessToken represents a GitLab Group Access Token.
+// GroupAccessToken represents a GitLab group access token.
 //
 // GitLab API docs: https://docs.gitlab.com/ee/api/group_access_tokens.html
 type GroupAccessToken struct {
@@ -57,8 +57,7 @@ func (v GroupAccessToken) String() string {
 // https://docs.gitlab.com/ee/api/group_access_tokens.html#list-group-access-tokens
 type ListGroupAccessTokensOptions ListOptions
 
-// ListGroupAccessTokens gets a list of all Group Access Tokens in a
-// group.
+// ListGroupAccessTokens gets a list of all group access tokens in a group.
 //
 // GitLab API docs:
 // https://docs.gitlab.com/ee/api/group_access_tokens.html#list-group-access-tokens
@@ -83,6 +82,31 @@ func (s *GroupAccessTokensService) ListGroupAccessTokens(gid interface{}, opt *L
 	return gats, resp, err
 }
 
+// GetGroupAccessToken gets a single group access tokens in a group.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/group_access_tokens.html#get-a-group-access-token
+func (s *GroupAccessTokensService) GetGroupAccessToken(gid interface{}, id int, options ...RequestOptionFunc) (*GroupAccessToken, *Response, error) {
+	groups, err := parseID(gid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("groups/%s/access_tokens/%d", PathEscape(groups), id)
+
+	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	gat := new(GroupAccessToken)
+	resp, err := s.client.Do(req, &gat)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return gat, resp, err
+}
+
 // CreateGroupAccessTokenOptions represents the available CreateVariable()
 // options.
 //
@@ -95,7 +119,7 @@ type CreateGroupAccessTokenOptions struct {
 	ExpiresAt   *ISOTime          `url:"expires_at,omitempty" json:"expires_at,omitempty"`
 }
 
-// CreateGroupAccessToken creates a new Group Access Token.
+// CreateGroupAccessToken creates a new group access token.
 //
 // GitLab API docs:
 // https://docs.gitlab.com/ee/api/group_access_tokens.html#create-a-group-access-token
@@ -120,11 +144,11 @@ func (s *GroupAccessTokensService) CreateGroupAccessToken(gid interface{}, opt *
 	return pat, resp, err
 }
 
-// DeleteGroupAccessToken deletes a Group Access Token.
+// RevokeGroupAccessToken revokes a group access token.
 //
 // GitLab API docs:
 // https://docs.gitlab.com/ee/api/group_access_tokens.html#revoke-a-group-access-token
-func (s *GroupAccessTokensService) DeleteGroupAccessToken(gid interface{}, id int, options ...RequestOptionFunc) (*Response, error) {
+func (s *GroupAccessTokensService) RevokeGroupAccessToken(gid interface{}, id int, options ...RequestOptionFunc) (*Response, error) {
 	groups, err := parseID(gid)
 	if err != nil {
 		return nil, err
diff --git a/project_access_tokens.go b/project_access_tokens.go
index 2821b45..349718f 100644
--- a/project_access_tokens.go
+++ b/project_access_tokens.go
@@ -30,7 +30,7 @@ type ProjectAccessTokensService struct {
 	client *Client
 }
 
-// ProjectAccessToken represents a GitLab Project Access Token.
+// ProjectAccessToken represents a GitLab project access token.
 //
 // GitLab API docs: https://docs.gitlab.com/ee/api/resource_access_tokens.html
 type ProjectAccessToken struct {
@@ -57,7 +57,7 @@ func (v ProjectAccessToken) String() string {
 // https://docs.gitlab.com/ee/api/resource_access_tokens.html#list-project-access-tokens
 type ListProjectAccessTokensOptions ListOptions
 
-// ListProjectAccessTokens gets a list of all Project Access Tokens in a
+// ListProjectAccessTokens gets a list of all project access tokens in a
 // project.
 //
 // GitLab API docs:
@@ -83,6 +83,31 @@ func (s *ProjectAccessTokensService) ListProjectAccessTokens(pid interface{}, op
 	return pats, resp, err
 }
 
+// GetProjectAccessToken gets a single project access tokens in a project.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/project_access_tokens.html#get-a-project-access-token
+func (s *ProjectAccessTokensService) GetProjectAccessToken(pid interface{}, id int, options ...RequestOptionFunc) (*ProjectAccessToken, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/access_tokens/%d", PathEscape(project), id)
+
+	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	pat := new(ProjectAccessToken)
+	resp, err := s.client.Do(req, &pat)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return pat, resp, err
+}
+
 // CreateProjectAccessTokenOptions represents the available CreateVariable()
 // options.
 //
@@ -120,11 +145,11 @@ func (s *ProjectAccessTokensService) CreateProjectAccessToken(pid interface{}, o
 	return pat, resp, err
 }
 
-// DeleteProjectAccessToken deletes a Project Access Token.
+// RevokeProjectAccessToken revokes a project access token.
 //
 // GitLab API docs:
 // https://docs.gitlab.com/ee/api/resource_access_tokens.html#revoke-a-project-access-token
-func (s *ProjectAccessTokensService) DeleteProjectAccessToken(pid interface{}, id int, options ...RequestOptionFunc) (*Response, error) {
+func (s *ProjectAccessTokensService) RevokeProjectAccessToken(pid interface{}, id int, options ...RequestOptionFunc) (*Response, error) {
 	project, err := parseID(pid)
 	if err != nil {
 		return nil, err


Test patch:
diff --git a/group_access_tokens_test.go b/group_access_tokens_test.go
index 8b9be4f..8443142 100644
--- a/group_access_tokens_test.go
+++ b/group_access_tokens_test.go
@@ -74,6 +74,41 @@ func TestListGroupAccessTokens(t *testing.T) {
 	}
 }
 
+func TestGetGroupAccessToken(t *testing.T) {
+	mux, server, client := setup(t)
+	defer teardown(server)
+
+	mux.HandleFunc("/api/v4/groups/1/access_tokens/1", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, http.MethodGet)
+		mustWriteHTTPResponse(t, w, "testdata/get_group_access_token.json")
+	})
+
+	groupAccessToken, _, err := client.GroupAccessTokens.GetGroupAccessToken(1, 1)
+	if err != nil {
+		t.Errorf("GroupAccessTokens.GetGroupAccessToken returned error: %v", err)
+	}
+
+	createdAt, err := time.Parse(time.RFC3339, "2021-03-09T21:11:47.271Z")
+	if err != nil {
+		t.Errorf("GroupAccessTokens.GetGroupAccessToken returned error: %v", err)
+	}
+
+	want := &GroupAccessToken{
+		ID:          1,
+		UserID:      2453,
+		Name:        "token 10",
+		Scopes:      []string{"api", "read_api", "read_repository", "write_repository"},
+		CreatedAt:   &createdAt,
+		Active:      true,
+		Revoked:     false,
+		AccessLevel: AccessLevelValue(40),
+	}
+
+	if !reflect.DeepEqual(want, groupAccessToken) {
+		t.Errorf("GroupAccessTokens.GetGroupAccessToken returned %+v, want %+v", groupAccessToken, want)
+	}
+}
+
 func TestCreateGroupAccessToken(t *testing.T) {
 	mux, server, client := setup(t)
 	defer teardown(server)
@@ -110,7 +145,7 @@ func TestCreateGroupAccessToken(t *testing.T) {
 	}
 }
 
-func TestDeleteGroupAccessToken(t *testing.T) {
+func TestRevokeGroupAccessToken(t *testing.T) {
 	mux, server, client := setup(t)
 	defer teardown(server)
 
@@ -118,8 +153,8 @@ func TestDeleteGroupAccessToken(t *testing.T) {
 		testMethod(t, r, http.MethodDelete)
 	})
 
-	_, err := client.GroupAccessTokens.DeleteGroupAccessToken("1", 1234)
+	_, err := client.GroupAccessTokens.RevokeGroupAccessToken("1", 1234)
 	if err != nil {
-		t.Errorf("GroupAccessTokens.DeleteGroupAccessToken returned error: %v", err)
+		t.Errorf("GroupAccessTokens.RevokeGroupAccessToken returned error: %v", err)
 	}
 }
diff --git a/project_access_tokens_test.go b/project_access_tokens_test.go
index 6630fef..cea7215 100644
--- a/project_access_tokens_test.go
+++ b/project_access_tokens_test.go
@@ -74,6 +74,41 @@ func TestListProjectAccessTokens(t *testing.T) {
 	}
 }
 
+func TestGetProjectAccessToken(t *testing.T) {
+	mux, server, client := setup(t)
+	defer teardown(server)
+
+	mux.HandleFunc("/api/v4/projects/1/access_tokens/1", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, http.MethodGet)
+		mustWriteHTTPResponse(t, w, "testdata/get_project_access_token.json")
+	})
+
+	projectAccessToken, _, err := client.ProjectAccessTokens.GetProjectAccessToken(1, 1)
+	if err != nil {
+		t.Errorf("ProjectAccessTokens.GetProjectAccessToken returned error: %v", err)
+	}
+
+	createdAt, err := time.Parse(time.RFC3339, "2021-03-09T21:11:47.271Z")
+	if err != nil {
+		t.Errorf("ProjectAccessTokens.GetProjectAccessToken returned error: %v", err)
+	}
+
+	want := &ProjectAccessToken{
+		ID:          1,
+		UserID:      2453,
+		Name:        "token 10",
+		Scopes:      []string{"api", "read_api", "read_repository", "write_repository"},
+		CreatedAt:   &createdAt,
+		Active:      true,
+		Revoked:     false,
+		AccessLevel: AccessLevelValue(40),
+	}
+
+	if !reflect.DeepEqual(want, projectAccessToken) {
+		t.Errorf("ProjectAccessTokens.GetProjectAccessToken returned %+v, want %+v", projectAccessToken, want)
+	}
+}
+
 func TestCreateProjectAccessToken(t *testing.T) {
 	mux, server, client := setup(t)
 	defer teardown(server)
@@ -110,7 +145,7 @@ func TestCreateProjectAccessToken(t *testing.T) {
 	}
 }
 
-func TestDeleteProjectAccessToken(t *testing.T) {
+func TestRevokeProjectAccessToken(t *testing.T) {
 	mux, server, client := setup(t)
 	defer teardown(server)
 
@@ -118,8 +153,8 @@ func TestDeleteProjectAccessToken(t *testing.T) {
 		testMethod(t, r, http.MethodDelete)
 	})
 
-	_, err := client.ProjectAccessTokens.DeleteProjectAccessToken("1", 1234)
+	_, err := client.ProjectAccessTokens.RevokeProjectAccessToken("1", 1234)
 	if err != nil {
-		t.Errorf("ProjectAccessTokens.DeleteProjectAccessToken returned error: %v", err)
+		t.Errorf("ProjectAccessTokens.RevokeProjectAccessToken returned error: %v", err)
 	}
 }
diff --git a/testdata/get_group_access_token.json b/testdata/get_group_access_token.json
new file mode 100644
index 0000000..f99a55b
--- /dev/null
+++ b/testdata/get_group_access_token.json
@@ -0,0 +1,11 @@
+{
+  "id": 1,
+  "name": "token 10",
+  "revoked": false,
+  "created_at": "2021-03-09T21:11:47.271Z",
+  "scopes": ["api", "read_api", "read_repository", "write_repository"],
+  "user_id": 2453,
+  "active": true,
+  "expires_at": null,
+  "access_level": 40
+}
diff --git a/testdata/get_project_access_token.json b/testdata/get_project_access_token.json
new file mode 100644
index 0000000..f99a55b
--- /dev/null
+++ b/testdata/get_project_access_token.json
@@ -0,0 +1,11 @@
+{
+  "id": 1,
+  "name": "token 10",
+  "revoked": false,
+  "created_at": "2021-03-09T21:11:47.271Z",
+  "scopes": ["api", "read_api", "read_repository", "write_repository"],
+  "user_id": 2453,
+  "active": true,
+  "expires_at": null,
+  "access_level": 40
+}