Skip to content

Create function for "Add SSH Key" #178

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion accounts.go
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,25 @@ func (s *AccountsService) GetSSHKey(ctx context.Context, accountID, sshKeyID str
return v, resp, err
}

// AddSSHKey adds an SSH key to a user's account.
// Gerrit API docs: https://gerrit-review.googlesource.com/Documentation/rest-api-accounts.html#add-ssh-key
func (s *AccountsService) AddSSHKey(ctx context.Context, accountID string, sshKey string) (*SSHKeyInfo, *Response, error) {
u := fmt.Sprintf("accounts/%s/sshkeys", accountID)

req, err := s.client.NewRawPostRequest(ctx, u, sshKey)
if err != nil {
return nil, nil, err
}

var keyInfo SSHKeyInfo
resp, err := s.client.Do(req, &keyInfo)
if err != nil {
return nil, resp, err
}

return &keyInfo, resp, err
}

// ListGPGKeys returns the GPG keys of an account.
//
// Gerrit API docs: https://gerrit-review.googlesource.com/Documentation/rest-api-accounts.html#list-gpg-keys
Expand Down Expand Up @@ -957,6 +976,5 @@ func (s *AccountsService) UnstarChange(ctx context.Context, accountID, changeID

/*
Missing Account Endpoints:
Add SSH Key
Get Avatar
*/
71 changes: 71 additions & 0 deletions accounts_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package gerrit_test

import (
"context"
"fmt"
"io"
"net/http"
"strings"
"testing"
)

// TestAddSSHKey tests the addition of an SSH key to an account.
func TestAddSSHKey(t *testing.T) {
setup()
defer teardown()

testMux.HandleFunc("/accounts/self/sshkeys", func(w http.ResponseWriter, r *http.Request) {
// Ensure the request method is POST
if r.Method != http.MethodPost {
t.Errorf("Expected POST request, got %s", r.Method)
}

// Ensure Content-Type is text/plain
if r.Header.Get("Content-Type") != "text/plain" {
t.Errorf("Expected Content-Type 'text/plain', got %s", r.Header.Get("Content-Type"))
}

// Read body and validate SSH key
expectedSSHKey := "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA0T...YImydZAw== john.doe@example.com"
body, _ := io.ReadAll(r.Body)
receivedSSHKey := strings.TrimSpace(string(body))

if receivedSSHKey != expectedSSHKey {
t.Errorf("Expected SSH key '%s', but received '%s'", expectedSSHKey, receivedSSHKey)
}

// Mock successful JSON response
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
w.WriteHeader(http.StatusOK)
fmt.Fprint(w, `{
"seq": 2,
"ssh_public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA0T...YImydZAw== john.doe@example.com",
"encoded_key": "AAAAB3NzaC1yc2EAAAABIwAAAQEA0T...YImydZAw==",
"algorithm": "ssh-rsa",
"comment": "john.doe@example.com",
"valid": true
}`)
})

ctx := context.Background()
sshKey := "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA0T...YImydZAw== john.doe@example.com"

// Use testClient.Accounts instead of undefined Accounts variable
keyInfo, _, err := testClient.Accounts.AddSSHKey(ctx, "self", sshKey)
if err != nil {
t.Fatalf("AddSSHKey returned error: %v", err)
}

// Verify SSH key information in the response
if keyInfo.SSHPublicKey != sshKey {
t.Errorf("Expected SSH key '%s', got '%s'", sshKey, keyInfo.SSHPublicKey)
}

if keyInfo.Valid != true {
t.Errorf("Expected key validity to be true, got false")
}

if keyInfo.Comment != "john.doe@example.com" {
t.Errorf("Expected comment 'john.doe@example.com', got '%s'", keyInfo.Comment)
}
}
28 changes: 28 additions & 0 deletions gerrit.go
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,34 @@ func (c *Client) NewRawPutRequest(ctx context.Context, urlStr string, body strin
return req, nil
}

// NewRawPostRequest creates a raw POST request and makes no attempt to encode
// or marshal the body. Just passes it straight through.
func (c *Client) NewRawPostRequest(ctx context.Context, urlStr string, body string) (*http.Request, error) {
// Build URL for request
u, err := c.buildURLForRequest(urlStr)
if err != nil {
return nil, err
}

buf := bytes.NewBuffer([]byte(body))
req, err := http.NewRequestWithContext(ctx, "POST", u, buf)
if err != nil {
return nil, err
}

// Apply Authentication
if err := c.addAuthentication(ctx, req); err != nil {
return nil, err
}

// Request compact JSON
// See https://gerrit-review.googlesource.com/Documentation/rest-api.html#output
req.Header.Add("Accept", "application/json")
req.Header.Add("Content-Type", "text/plain")

return req, nil
}

// Call is a combine function for Client.NewRequest and Client.Do.
//
// Most API methods are quite the same.
Expand Down
32 changes: 32 additions & 0 deletions gerrit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,38 @@ func TestNewRawPutRequest(t *testing.T) {
}
}

func TestNewRawPostRequest(t *testing.T) {
ctx := context.Background()
c, err := gerrit.NewClient(ctx, testGerritInstanceURL, nil)
if err != nil {
t.Errorf("An error occured. Expected nil. Got %+v.", err)
}

inURL, outURL := "/foo", testGerritInstanceURL+"foo"
req, _ := c.NewRawPostRequest(ctx, inURL, "test raw POST contents")

// Test that relative URL was expanded
if got, want := req.URL.String(), outURL; got != want {
t.Errorf("NewRequest(%q) URL is %v, want %v", inURL, got, want)
}

// Test that HTTP method is POST
if got, want := req.Method, http.MethodPost; got != want {
t.Errorf("NewRawPostRequest method is %v, want %v", got, want)
}

// Test that request body is passed correctly
body, _ := io.ReadAll(req.Body)
if got, want := string(body), "test raw POST contents"; got != want {
t.Errorf("NewRequest Body is %v, want %v", got, want)
}

// Test that Content-Type is set correctly
if got, want := req.Header.Get("Content-Type"), "text/plain"; got != want {
t.Errorf("NewRawPostRequest Content-Type is %v, want %v", got, want)
}
}

func testURLParseError(t *testing.T, err error) {
if err == nil {
t.Errorf("Expected error to be returned")
Expand Down