Skip to content

Commit

Permalink
Merge pull request #70 from hlubek/feature-robot-accounts
Browse files Browse the repository at this point in the history
Add methods to list, create, update and delete robot accounts
  • Loading branch information
elenz97 authored Jan 11, 2021
2 parents 4028717 + f9ad44e commit 0729e3a
Show file tree
Hide file tree
Showing 3 changed files with 247 additions and 4 deletions.
25 changes: 23 additions & 2 deletions apiv2/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ package apiv2

import (
"context"
modelv2 "github.com/mittwald/goharbor-client/v3/apiv2/model"
"github.com/mittwald/goharbor-client/v3/apiv2/retention"
"net/url"
"strings"

modelv2 "github.com/mittwald/goharbor-client/v3/apiv2/model"
"github.com/mittwald/goharbor-client/v3/apiv2/retention"

"github.com/go-openapi/runtime"
runtimeclient "github.com/go-openapi/runtime/client"
"github.com/go-openapi/strfmt"
Expand Down Expand Up @@ -175,6 +176,26 @@ func (c *RESTClient) DeleteProjectMetadataValue(ctx context.Context, p *modelv2.
return c.project.DeleteProjectMetadataValue(ctx, p, key)
}

// ListProjectRobots wraps the ListProjectRobots method of the project sub-package.
func (c *RESTClient) ListProjectRobots(ctx context.Context, p *modelv2.Project) ([]*model.RobotAccount, error) {
return c.project.ListProjectRobots(ctx, p)
}

// AddProjectRobot wraps the AddProjectRobot method of the project sub-package.
func (c *RESTClient) AddProjectRobot(ctx context.Context, p *modelv2.Project, robot *model.RobotAccountCreate) (string, error) {
return c.project.AddProjectRobot(ctx, p, robot)
}

// UpdateProjectRobot wraps the UpdateProjectRobot method of the project sub-package.
func (c *RESTClient) UpdateProjectRobot(ctx context.Context, p *modelv2.Project, robotID int, robot *model.RobotAccountUpdate) error {
return c.project.UpdateProjectRobot(ctx, p, robotID, robot)
}

// DeleteProjectRobot wraps the DeleteProjectRobot method of the project sub-package.
func (c *RESTClient) DeleteProjectRobot(ctx context.Context, p *modelv2.Project, robotID int) error {
return c.project.DeleteProjectRobot(ctx, p, robotID)
}

// Registry Client

// NewRegistry wraps the NewRegistry method of the registry sub-package.
Expand Down
77 changes: 77 additions & 0 deletions apiv2/project/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package project
import (
"context"
"errors"

projectapi "github.com/mittwald/goharbor-client/v3/apiv2/internal/api/client/project"

modelv2 "github.com/mittwald/goharbor-client/v3/apiv2/model"
Expand Down Expand Up @@ -534,6 +535,82 @@ func (c *RESTClient) DeleteProjectMetadataValue(ctx context.Context, p *modelv2.
return handleSwaggerProjectErrors(err)
}

// ListProjectRobots returns a list of all robot accounts in project p.
func (c *RESTClient) ListProjectRobots(ctx context.Context, p *modelv2.Project) ([]*model.RobotAccount, error) {
if p == nil {
return nil, &ErrProjectNotProvided{}
}

resp, err := c.LegacyClient.Products.GetProjectsProjectIDRobots(
&products.GetProjectsProjectIDRobotsParams{
ProjectID: int64(p.ProjectID),
Context: ctx,
}, c.AuthInfo)
if err != nil {
return nil, handleSwaggerProjectErrors(err)
}

return resp.Payload, nil
}

// AddProjectRobot adds a robot account to project p and returns the token.
func (c *RESTClient) AddProjectRobot(ctx context.Context, p *modelv2.Project, robot *model.RobotAccountCreate) (string, error) {
if p == nil {
return "", &ErrProjectNotProvided{}
}

resp, err := c.LegacyClient.Products.PostProjectsProjectIDRobots(
&products.PostProjectsProjectIDRobotsParams{
Robot: robot,
ProjectID: int64(p.ProjectID),
Context: ctx,
}, c.AuthInfo)
if err != nil {
return "", handleSwaggerProjectErrors(err)
}

return resp.Payload.Token, nil
}

// UpdateProjectRobot updates a robot account in project p.
func (c *RESTClient) UpdateProjectRobot(ctx context.Context, p *modelv2.Project, robotID int, robot *model.RobotAccountUpdate) error {
if p == nil {
return &ErrProjectNotProvided{}
}

_, err := c.LegacyClient.Products.PutProjectsProjectIDRobotsRobotID(
&products.PutProjectsProjectIDRobotsRobotIDParams{
ProjectID: int64(p.ProjectID),
Robot: robot,
RobotID: int64(robotID),
Context: ctx,
}, c.AuthInfo)
if err != nil {
return handleSwaggerProjectErrors(err)
}

return nil
}

// DeleteProjectRobot deletes a robot account from project p.
func (c *RESTClient) DeleteProjectRobot(ctx context.Context, p *modelv2.Project, robotID int) error {
if p == nil {
return &ErrProjectNotProvided{}
}

_, err := c.LegacyClient.Products.DeleteProjectsProjectIDRobotsRobotID(
&products.DeleteProjectsProjectIDRobotsRobotIDParams{
ProjectID: int64(p.ProjectID),
RobotID: int64(robotID),
Context: ctx,
}, c.AuthInfo)
if err != nil {
return handleSwaggerProjectErrors(err)
}

return nil
}

// projectExists returns true, if p matches a project on server side.
// Returns false, if not found.
// Returns an error in case of communication problems.
Expand Down
149 changes: 147 additions & 2 deletions apiv2/project/project_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ package project

import (
"context"
projectapi "github.com/mittwald/goharbor-client/v3/apiv2/internal/api/client/project"
modelv2 "github.com/mittwald/goharbor-client/v3/apiv2/model"
"fmt"
"net/http"
"testing"

projectapi "github.com/mittwald/goharbor-client/v3/apiv2/internal/api/client/project"
modelv2 "github.com/mittwald/goharbor-client/v3/apiv2/model"

"github.com/go-openapi/runtime"
v2client "github.com/mittwald/goharbor-client/v3/apiv2/internal/api/client"

Expand Down Expand Up @@ -1884,6 +1886,149 @@ func TestRESTClient_DeleteProjectMetadataValue(t *testing.T) {
l.AssertExpectations(t)
}

func TestRESTClient_ListProjectRobots(t *testing.T) {
p := &mocks.MockProductsClientService{}

legacyClient := BuildLegacyClientWithMock(p)
v2Client := BuildProjectClientWithMocks(nil)

cl := NewClient(legacyClient, v2Client, authInfo)

ctx := context.Background()

expectedRobots := []*model.RobotAccount{
&model.RobotAccount{
Description: "some robot account",
Disabled: false,
ID: 42,
Name: "robot$account",
ProjectID: exampleProjectID,
},
}

params := &products.GetProjectsProjectIDRobotsParams{
ProjectID: exampleProjectID,
Context: ctx,
}

p.On("GetProjectsProjectIDRobots", params, mock.AnythingOfType("runtime.ClientAuthInfoWriterFunc")).
Return(&products.GetProjectsProjectIDRobotsOK{Payload: expectedRobots}, nil)

robots, err := cl.ListProjectRobots(ctx, exampleProject)

assert.NoError(t, err)

assert.Equal(t, expectedRobots, robots)

p.AssertExpectations(t)
}

func TestRESTClient_AddProjectRobot(t *testing.T) {
p := &mocks.MockProductsClientService{}

legacyClient := BuildLegacyClientWithMock(p)
v2Client := BuildProjectClientWithMocks(nil)

cl := NewClient(legacyClient, v2Client, authInfo)

ctx := context.Background()

newRobot := &model.RobotAccountCreate{
Access: []*model.RobotAccountAccess{
{
Action: "push",
Resource: fmt.Sprintf("/project/%d/repository", exampleProjectID),
},
},
Description: "some robot account",
ExpiresAt: 0,
Name: "my-account",
}

params := &products.PostProjectsProjectIDRobotsParams{
ProjectID: exampleProjectID,
Robot: newRobot,
Context: ctx,
}

expectedPayload := &model.RobotAccountPostRep{
Name: "robot$my-account",
Token: "very-secret-token-here",
}

p.On("PostProjectsProjectIDRobots", params, mock.AnythingOfType("runtime.ClientAuthInfoWriterFunc")).
Return(&products.PostProjectsProjectIDRobotsCreated{Payload: expectedPayload}, nil)

token, err := cl.AddProjectRobot(ctx, exampleProject, newRobot)

assert.NoError(t, err)

assert.Equal(t, expectedPayload.Token, token)

p.AssertExpectations(t)
}

func TestRESTClient_UpdateProjectRobot(t *testing.T) {
p := &mocks.MockProductsClientService{}

legacyClient := BuildLegacyClientWithMock(p)
v2Client := BuildProjectClientWithMocks(nil)

cl := NewClient(legacyClient, v2Client, authInfo)

ctx := context.Background()

const exampleRobotID = 42

updateRobot := &model.RobotAccountUpdate{
Disabled: true,
}

params := &products.PutProjectsProjectIDRobotsRobotIDParams{
ProjectID: exampleProjectID,
RobotID: exampleRobotID,
Robot: updateRobot,
Context: ctx,
}

p.On("PutProjectsProjectIDRobotsRobotID", params, mock.AnythingOfType("runtime.ClientAuthInfoWriterFunc")).
Return(&products.PutProjectsProjectIDRobotsRobotIDOK{}, nil)

err := cl.UpdateProjectRobot(ctx, exampleProject, exampleRobotID, updateRobot)

assert.NoError(t, err)

p.AssertExpectations(t)
}

func TestRESTClient_DeleteProjectRobot(t *testing.T) {
p := &mocks.MockProductsClientService{}

legacyClient := BuildLegacyClientWithMock(p)
v2Client := BuildProjectClientWithMocks(nil)

cl := NewClient(legacyClient, v2Client, authInfo)

ctx := context.Background()

const exampleRobotID = 42

params := &products.DeleteProjectsProjectIDRobotsRobotIDParams{
ProjectID: exampleProjectID,
RobotID: exampleRobotID,
Context: ctx,
}

p.On("DeleteProjectsProjectIDRobotsRobotID", params, mock.AnythingOfType("runtime.ClientAuthInfoWriterFunc")).
Return(&products.DeleteProjectsProjectIDRobotsRobotIDOK{}, nil)

err := cl.DeleteProjectRobot(ctx, exampleProject, exampleRobotID)

assert.NoError(t, err)

p.AssertExpectations(t)
}

func TestErrProjectNameNotProvided_Error(t *testing.T) {
var e ErrProjectNameNotProvided

Expand Down

0 comments on commit 0729e3a

Please sign in to comment.