-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(cli): command
token revoke
to revoke personal access tokens (#33)
- Loading branch information
1 parent
c446507
commit 0272afb
Showing
8 changed files
with
213 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package revoke | ||
|
||
import ( | ||
"github.com/spf13/cobra" | ||
"numerous.com/cli/cmd/errorhandling" | ||
"numerous.com/cli/internal/gql" | ||
"numerous.com/cli/internal/token" | ||
) | ||
|
||
var id string | ||
|
||
var Cmd = &cobra.Command{ | ||
Use: "revoke", | ||
Short: "Revoke a personal access token.", | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
err := Revoke(cmd.Context(), token.NewService(gql.NewClient()), id) | ||
return errorhandling.ErrorAlreadyPrinted(err) | ||
}, | ||
} | ||
|
||
func init() { | ||
flags := Cmd.Flags() | ||
flags.StringVarP(&id, "id", "", "", "The id of the personal access token.") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package revoke | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/stretchr/testify/mock" | ||
"numerous.com/cli/internal/token" | ||
) | ||
|
||
var _ TokenRevoker = &MockTokenRevoker{} | ||
|
||
type MockTokenRevoker struct{ mock.Mock } | ||
|
||
func (m *MockTokenRevoker) Revoke(ctx context.Context, id string) (token.RevokeTokenOutput, error) { | ||
args := m.Called(ctx, id) | ||
return args.Get(0).(token.RevokeTokenOutput), args.Error(1) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package revoke | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
|
||
"numerous.com/cli/cmd/output" | ||
"numerous.com/cli/internal/token" | ||
) | ||
|
||
var ErrMissingTokenID = errors.New("missing token id argument") | ||
|
||
type TokenRevoker interface { | ||
Revoke(ctx context.Context, id string) (token.RevokeTokenOutput, error) | ||
} | ||
|
||
func Revoke(ctx context.Context, revoker TokenRevoker, id string) error { | ||
if id == "" { | ||
output.PrintError("Missing token id argument.", "") | ||
return ErrMissingTokenID | ||
} | ||
|
||
out, err := revoker.Revoke(ctx, id) | ||
|
||
if err == nil { | ||
output.PrintlnOK("Revoked personal access token %q", out.Name) | ||
} else { | ||
output.PrintUnknownError(err) | ||
} | ||
|
||
return err | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package revoke | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/mock" | ||
"numerous.com/cli/internal/token" | ||
) | ||
|
||
func TestRevoke(t *testing.T) { | ||
id := "test-token-id" | ||
name := "token name" | ||
description := "token description" | ||
testErr := errors.New("test error") | ||
|
||
t.Run("revokes and returns expected name and description", func(t *testing.T) { | ||
revoker := MockTokenRevoker{} | ||
revoker.On("Revoke", mock.Anything, id).Return(token.RevokeTokenOutput{Name: name, Description: description}, nil) | ||
|
||
err := Revoke(context.TODO(), &revoker, id) | ||
|
||
assert.NoError(t, err) | ||
revoker.AssertExpectations(t) | ||
}) | ||
|
||
t.Run("passes on error", func(t *testing.T) { | ||
for _, expectedError := range []error{ | ||
token.ErrAccessDenied, | ||
testErr, | ||
} { | ||
t.Run(expectedError.Error(), func(t *testing.T) { | ||
revoker := MockTokenRevoker{} | ||
revoker.On("Revoke", mock.Anything, id).Return(token.RevokeTokenOutput{}, expectedError) | ||
|
||
err := Revoke(context.TODO(), &revoker, id) | ||
|
||
assert.ErrorIs(t, err, expectedError) | ||
}) | ||
} | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package token | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/hasura/go-graphql-client" | ||
) | ||
|
||
type RevokeTokenOutput struct { | ||
Name string | ||
Description string | ||
} | ||
|
||
type personalAccessTokenRevokeResponse struct { | ||
PersonalAccessTokenRevoke struct { | ||
Name string | ||
Description string | ||
} `graphql:"personalAccessTokenRevoke(id: $id)"` | ||
} | ||
|
||
func (s *Service) Revoke(ctx context.Context, id string) (RevokeTokenOutput, error) { | ||
var resp personalAccessTokenRevokeResponse | ||
|
||
if err := s.client.Mutate(ctx, &resp, map[string]interface{}{"id": graphql.ID(id)}); err != nil { | ||
return RevokeTokenOutput{}, ConvertErrors(err) | ||
} else { | ||
result := resp.PersonalAccessTokenRevoke | ||
return RevokeTokenOutput{ | ||
Name: result.Name, | ||
Description: result.Description, | ||
}, nil | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
package token | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/mock" | ||
"github.com/stretchr/testify/require" | ||
"numerous.com/cli/internal/test" | ||
) | ||
|
||
func TestRevoke(t *testing.T) { | ||
t.Run("given access denied then it returns access denied error", func(t *testing.T) { | ||
doer := test.MockDoer{} | ||
c := test.CreateTestGQLClient(t, &doer) | ||
s := NewService(c) | ||
respBody := ` | ||
{ | ||
"errors": [{ | ||
"message": "access denied", | ||
"location": [{"line": 1, "column": 1}], | ||
"path": ["personalAccessTokenRevoke"] | ||
}] | ||
}` | ||
resp := test.JSONResponse(respBody) | ||
doer.On("Do", mock.Anything).Return(resp, nil) | ||
|
||
actual, err := s.Revoke(context.TODO(), "some-token-id") | ||
|
||
assert.Empty(t, actual) | ||
assert.ErrorIs(t, err, ErrAccessDenied) | ||
}) | ||
|
||
t.Run("returns expected revoked token", func(t *testing.T) { | ||
doer := test.MockDoer{} | ||
c := test.CreateTestGQLClient(t, &doer) | ||
s := NewService(c) | ||
respBody := ` | ||
{ | ||
"data": { | ||
"personalAccessTokenRevoke": { | ||
"name": "token name", | ||
"description": "token description" | ||
} | ||
} | ||
}` | ||
resp := test.JSONResponse(respBody) | ||
doer.On("Do", mock.Anything).Return(resp, nil) | ||
|
||
actual, err := s.Revoke(context.TODO(), "some-token-id") | ||
|
||
require.NoError(t, err) | ||
expected := RevokeTokenOutput{Name: "token name", Description: "token description"} | ||
assert.Equal(t, expected, actual) | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters