Skip to content
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

Implement Configure Public Client #170

Merged
14 changes: 14 additions & 0 deletions apiv2/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"net/url"
"strings"

"github.com/mittwald/goharbor-client/v5/apiv2/pkg/clients/configure"
"github.com/mittwald/goharbor-client/v5/apiv2/pkg/clients/ping"
"github.com/mittwald/goharbor-client/v5/apiv2/pkg/clients/statistic"

Expand Down Expand Up @@ -49,6 +50,7 @@ const v2URLSuffix string = "/v2.0"
type Client interface {
auditlog.Client
artifact.Client
configure.Client
gc.Client
health.Client
label.Client
Expand All @@ -73,6 +75,7 @@ type Client interface {
type RESTClient struct {
auditlog *auditlog.RESTClient
artifact *artifact.RESTClient
configure *configure.RESTClient
gc *gc.RESTClient
health *health.RESTClient
label *label.RESTClient
Expand Down Expand Up @@ -103,6 +106,7 @@ func NewRESTClient(v2Client *v2client.Harbor, opts *config.Options, authInfo run
return &RESTClient{
auditlog: auditlog.NewClient(v2Client, opts, authInfo),
artifact: artifact.NewClient(v2Client, opts, authInfo),
configure: configure.NewClient(v2Client, opts, authInfo),
gc: gc.NewClient(v2Client, opts, authInfo),
health: health.NewClient(v2Client, opts, authInfo),
label: label.NewClient(v2Client, opts, authInfo),
Expand Down Expand Up @@ -213,6 +217,16 @@ func (c *RESTClient) RemoveLabel(ctx context.Context, projectName, repositoryNam
return c.artifact.RemoveLabel(ctx, projectName, repositoryName, reference, id)
}

// Configure Client

func (c *RESTClient) GetConfig(ctx context.Context) (*modelv2.ConfigurationsResponse, error) {
return c.configure.GetConfigs(ctx)
}

func (c *RESTClient) UpdateConfigs(ctx context.Context, cfg *modelv2.Configurations) error {
return c.configure.UpdateConfigs(ctx, cfg)
}

// GC Client

func (c *RESTClient) NewGarbageCollection(ctx context.Context, gcSchedule *modelv2.Schedule) error {
Expand Down
64 changes: 64 additions & 0 deletions apiv2/pkg/clients/configure/configure.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package configure

import (
"context"

"github.com/go-openapi/runtime"

v2client "github.com/mittwald/goharbor-client/v5/apiv2/internal/api/client"
"github.com/mittwald/goharbor-client/v5/apiv2/internal/api/client/configure"
"github.com/mittwald/goharbor-client/v5/apiv2/model"
"github.com/mittwald/goharbor-client/v5/apiv2/pkg/config"
)

// RESTClient is a subclient for handling project related actions.
type RESTClient struct {
// Options contains optional configuration when making API calls.
Options *config.Options

// The new client of the harbor v2 API
V2Client *v2client.Harbor

// AuthInfo contains the auth information that is provided on API calls.
AuthInfo runtime.ClientAuthInfoWriter
}

func NewClient(v2Client *v2client.Harbor, opts *config.Options, authInfo runtime.ClientAuthInfoWriter) *RESTClient {
return &RESTClient{
Options: opts,
V2Client: v2Client,
AuthInfo: authInfo,
}
}

type Client interface {
GetConfigs(ctx context.Context) (*model.ConfigurationsResponse, error)
UpdateConfigs(ctx context.Context, newConfiguration *model.Configurations) error
}

// GetConfigs returns a system configurations object.
func (c *RESTClient) GetConfigs(ctx context.Context) (*model.ConfigurationsResponse, error) {
params := &configure.GetConfigurationsParams{
Context: ctx,
}

params.WithTimeout(c.Options.Timeout)

resp, err := c.V2Client.Configure.GetConfigurations(params, c.AuthInfo)
if err != nil {
return nil, handleSwaggerConfigurationsErrors(err)
}

return resp.Payload, nil
}

// UpdateConfigs modifying system configurations that only provides for admin users.
func (c *RESTClient) UpdateConfigs(ctx context.Context, cfg *model.Configurations) error {
params := &configure.UpdateConfigurationsParams{
Configurations: cfg,
Context: ctx,
}
params.WithTimeout(c.Options.Timeout)
_, err := c.V2Client.Configure.UpdateConfigurations(params, c.AuthInfo)
return handleSwaggerConfigurationsErrors(err)
}
27 changes: 27 additions & 0 deletions apiv2/pkg/clients/configure/configure_errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package configure

import (
"net/http"

"github.com/go-openapi/runtime"

"github.com/mittwald/goharbor-client/v5/apiv2/pkg/errors"
)

// handleSwaggerConfigurationsErrors takes a swagger generated error as input,
// which usually does not contain any form of error message,
// and outputs a new error with a proper message.
func handleSwaggerConfigurationsErrors(in error) error {
t, ok := in.(*runtime.APIError)
if ok {
switch t.Code {
case http.StatusUnauthorized:
return &errors.ErrConfigureUnauthorized{}
case http.StatusForbidden:
return &errors.ErrConfigureNoPermission{}
case http.StatusInternalServerError:
return &errors.ErrConfigureInternalServerError{}
}
}
return nil
}
32 changes: 32 additions & 0 deletions apiv2/pkg/clients/configure/configure_integration_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//go:build integration

package configure

import (
"context"
"testing"

modelv2 "github.com/mittwald/goharbor-client/v5/apiv2/model"
clienttesting "github.com/mittwald/goharbor-client/v5/apiv2/pkg/testing"

"github.com/stretchr/testify/require"
)

func TestAPIGetConfig(t *testing.T) {
ctx := context.Background()
c := NewClient(clienttesting.V2SwaggerClient, clienttesting.DefaultOpts, clienttesting.AuthInfo)

resp, err := c.GetConfigs(ctx)
require.NoError(t, err)

require.Equal(t, "db_auth", *&resp.AuthMode.Value)
}

func TestAPIUpdateConfigs(t *testing.T) {
authMode := "db_auth"
ctx := context.Background()
c := NewClient(clienttesting.V2SwaggerClient, clienttesting.DefaultOpts, clienttesting.AuthInfo)

err := c.UpdateConfigs(ctx, &modelv2.Configurations{AuthMode: &authMode})
require.NoError(t, err)
}
69 changes: 69 additions & 0 deletions apiv2/pkg/clients/configure/configure_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
//go:build !integration

package configure

import (
"context"
"testing"

"github.com/mittwald/goharbor-client/v5/apiv2/internal/api/client/configure"
"github.com/mittwald/goharbor-client/v5/apiv2/mocks"
"github.com/mittwald/goharbor-client/v5/apiv2/model"
clienttesting "github.com/mittwald/goharbor-client/v5/apiv2/pkg/testing"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
)

var (
authMode = "oidc"
OIDCName = "example"
exampleConfig = &model.Configurations{
AuthMode: &authMode,
OIDCName: &OIDCName,
}
ctx = context.Background()
)

func APIandMockClientsForTests() (*RESTClient, *clienttesting.MockClients) {
desiredMockClients := &clienttesting.MockClients{
Configure: mocks.MockConfigureClientService{},
}

v2Client := clienttesting.BuildV2ClientWithMocks(desiredMockClients)

cl := NewClient(v2Client, clienttesting.DefaultOpts, clienttesting.AuthInfo)

return cl, desiredMockClients
}

func TestRESTClient_GetConfigurations(t *testing.T) {
apiClient, mockClient := APIandMockClientsForTests()

params := &configure.GetConfigurationsParams{
Context: ctx,
}
params.WithTimeout(apiClient.Options.Timeout)

mockClient.Configure.On("GetConfigurations", params, mock.AnythingOfType("runtime.ClientAuthInfoWriterFunc")).Return(&configure.GetConfigurationsOK{}, nil)

_, err := apiClient.GetConfigs(ctx)

require.NoError(t, err)
mockClient.Configure.AssertExpectations(t)
}

func TestRESTClient_UpdateConfigs(t *testing.T) {
apiClient, mockClient := APIandMockClientsForTests()

params := &configure.UpdateConfigurationsParams{
Configurations: exampleConfig,
Context: ctx,
}
params.WithTimeout(apiClient.Options.Timeout)

mockClient.Configure.On("UpdateConfigurations", params, mock.AnythingOfType("runtime.ClientAuthInfoWriterFunc")).Return(&configure.UpdateConfigurationsOK{}, nil)

err := apiClient.UpdateConfigs(ctx, exampleConfig)
require.NoError(t, err)
mockClient.Configure.AssertExpectations(t)
}
38 changes: 38 additions & 0 deletions apiv2/pkg/errors/configure_errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package errors

const (
// ErrConfigureUnauthorizedMsg is the error message for ErrConfigureUnauthorized error.
ErrConfigureUnauthorizedMsg = "unauthorized"

// ErrConfigureNoPermissionMsg is the error message for ErrConfigureNoPermission error.
ErrConfigureNoPermissionMsg = "user does not have permission of admin role"

// ErrConfigureInternalServerErrorMsg is the error message for ErrConfigureInternalServerError error.
ErrConfigureInternalServerErrorMsg = "unexpected internal errors"
)

type (
// ErrConfigureInternalServerError describes server-side internal errors.
ErrConfigureInternalServerError struct{}

// ErrConfigureNoPermission describes a request error without permission.
ErrConfigureNoPermission struct{}

// ErrConfigureUnauthorized describes an unauthorized request.
ErrConfigureUnauthorized struct{}
)

// Error returns the error message.
func (e *ErrConfigureUnauthorized) Error() string {
return ErrConfigureUnauthorizedMsg
}

// Error returns the error message.
func (e *ErrConfigureNoPermission) Error() string {
return ErrConfigureNoPermissionMsg
}

// Error returns the error message.
func (e *ErrConfigureInternalServerError) Error() string {
return ErrConfigureInternalServerErrorMsg
}
Loading