make client interface

This commit is contained in:
Mike Mason
2023-07-18 13:36:41 +00:00
parent 05762f5d75
commit f828546cfe
10 changed files with 72 additions and 46 deletions

View File

@@ -10,41 +10,46 @@ import (
"go.equinixmetal.net/infra9-metal-bridge/internal/metal/providers"
)
// Client is the Equinix Metal API Client struct.
type Client struct {
// Client is the Equinix Metal Client Interface.
type Client interface {
providers.Provider
}
// client is the Equinix Metal Client struct.
type client struct {
logger *zap.Logger
provider providers.Provider
}
// GetOrganizationDetails fetches the organization id provided with its memberships.
func (c *Client) GetOrganizationDetails(ctx context.Context, id gidx.PrefixedID) (*models.OrganizationDetails, error) {
func (c *client) GetOrganizationDetails(ctx context.Context, id gidx.PrefixedID) (*models.OrganizationDetails, error) {
return c.provider.GetOrganizationDetails(ctx, id)
}
// GetProjectDetails fetchs the provided project id with membership information.
func (c *Client) GetProjectDetails(ctx context.Context, id gidx.PrefixedID) (*models.ProjectDetails, error) {
func (c *client) GetProjectDetails(ctx context.Context, id gidx.PrefixedID) (*models.ProjectDetails, error) {
return c.provider.GetProjectDetails(ctx, id)
}
// GetUserDetails fetches the provided user id.
func (c *Client) GetUserDetails(ctx context.Context, id gidx.PrefixedID) (*models.UserDetails, error) {
func (c *client) GetUserDetails(ctx context.Context, id gidx.PrefixedID) (*models.UserDetails, error) {
return c.provider.GetUserDetails(ctx, id)
}
// GetUserOrganizationRole returns the role for the user in the organization.
func (c *Client) GetUserOrganizationRole(ctx context.Context, userID, orgID gidx.PrefixedID) (string, error) {
func (c *client) GetUserOrganizationRole(ctx context.Context, userID, orgID gidx.PrefixedID) (string, error) {
return c.provider.GetUserOrganizationRole(ctx, userID, orgID)
}
// GetUserProjectRole returns the role for the user in the project.
func (c *Client) GetUserProjectRole(ctx context.Context, userID, projID gidx.PrefixedID) (string, error) {
func (c *client) GetUserProjectRole(ctx context.Context, userID, projID gidx.PrefixedID) (string, error) {
return c.provider.GetUserProjectRole(ctx, userID, projID)
}
// New creates a new Client.
func New(options ...Option) (*Client, error) {
client := new(Client)
// New creates a new Equinix Metal Client.
func New(options ...Option) (Client, error) {
client := new(client)
for _, opt := range options {
if err := opt(client); err != nil {

View File

@@ -9,11 +9,11 @@ import (
)
// Option is a Client configuration Option definition.
type Option func(c *Client) error
type Option func(c *client) error
// WithProvider sets the provider on the client.
func WithProvider(provider providers.Provider) Option {
return func(c *Client) error {
return func(c *client) error {
c.provider = provider
return nil
@@ -22,7 +22,7 @@ func WithProvider(provider providers.Provider) Option {
// WithLogger sets the logger for the client.
func WithLogger(logger *zap.Logger) Option {
return func(c *Client) error {
return func(c *client) error {
c.logger = logger
return nil
@@ -31,7 +31,7 @@ func WithLogger(logger *zap.Logger) Option {
// WithConfig applies all configurations defined in the config.
func WithConfig(config Config) Option {
return func(c *Client) error {
return func(c *client) error {
var options []Option
if config.EMGQL.Populated() {

View File

@@ -26,7 +26,7 @@ type roleAssignmentData struct {
}
// AssignRole assigns the provided member ID to the given role ID.
func (c *Client) AssignRole(ctx context.Context, roleID gidx.PrefixedID, memberID gidx.PrefixedID) error {
func (c *client) AssignRole(ctx context.Context, roleID gidx.PrefixedID, memberID gidx.PrefixedID) error {
path := fmt.Sprintf("/api/v1/roles/%s/assignments", roleID.String())
body, err := encodeJSON(RoleAssign{
@@ -50,7 +50,7 @@ func (c *Client) AssignRole(ctx context.Context, roleID gidx.PrefixedID, memberI
}
// UnassignRole removes the provided member ID from the given role ID.
func (c *Client) UnassignRole(ctx context.Context, roleID gidx.PrefixedID, memberID gidx.PrefixedID) error {
func (c *client) UnassignRole(ctx context.Context, roleID gidx.PrefixedID, memberID gidx.PrefixedID) error {
path := fmt.Sprintf("/api/v1/roles/%s/assignments", roleID.String())
body, err := encodeJSON(RoleAssign{
@@ -74,7 +74,7 @@ func (c *Client) UnassignRole(ctx context.Context, roleID gidx.PrefixedID, membe
}
// ListRoleAssignments lists all assignments for the given role.
func (c *Client) ListRoleAssignments(ctx context.Context, roleID gidx.PrefixedID) ([]gidx.PrefixedID, error) {
func (c *client) ListRoleAssignments(ctx context.Context, roleID gidx.PrefixedID) ([]gidx.PrefixedID, error) {
path := fmt.Sprintf("/api/v1/roles/%s/assignments", roleID.String())
var response roleAssignmentData
@@ -98,7 +98,7 @@ func (c *Client) ListRoleAssignments(ctx context.Context, roleID gidx.PrefixedID
}
// RoleHasAssignment gets the assignments for the given role and check for the provided member id.
func (c *Client) RoleHasAssignment(ctx context.Context, roleID gidx.PrefixedID, memberID gidx.PrefixedID) (bool, error) {
func (c *client) RoleHasAssignment(ctx context.Context, roleID gidx.PrefixedID, memberID gidx.PrefixedID) (bool, error) {
assignments, err := c.ListRoleAssignments(ctx, roleID)
if err != nil {
return false, err

View File

@@ -12,21 +12,37 @@ import (
"time"
"github.com/labstack/echo/v4"
"go.infratographer.com/x/gidx"
"go.uber.org/zap"
)
const (
defaultPermissionsURL = "https://permissions-api.hollow-a.sv15.metalkube.net"
DefaultHTTPClientTimeout = 5 * time.Second
defaultHTTPClientTimeout = 5 * time.Second
)
// DefaultHTTPClient is the default HTTP client for the Permissions Client.
var DefaultHTTPClient = &http.Client{
Timeout: DefaultHTTPClientTimeout,
Timeout: defaultHTTPClientTimeout,
}
// Client is the permissions client.
type Client struct {
// Client defines the Permissions API client interface.
type Client interface {
AssignRole(ctx context.Context, roleID gidx.PrefixedID, memberID gidx.PrefixedID) error
CreateRole(ctx context.Context, resourceID gidx.PrefixedID, actions []string) (gidx.PrefixedID, error)
DeleteResourceRelationship(ctx context.Context, resourceID gidx.PrefixedID, relation string, relatedResourceID gidx.PrefixedID) error
DeleteRole(ctx context.Context, roleID gidx.PrefixedID) error
FindResourceRoleByActions(ctx context.Context, resourceID gidx.PrefixedID, actions []string) (ResourceRole, error)
ListResourceRelationships(ctx context.Context, resourceID gidx.PrefixedID, relatedResourceType string) ([]ResourceRelationship, error)
ListResourceRoles(ctx context.Context, resourceID gidx.PrefixedID) (ResourceRoles, error)
ListRoleAssignments(ctx context.Context, roleID gidx.PrefixedID) ([]gidx.PrefixedID, error)
RoleHasAssignment(ctx context.Context, roleID gidx.PrefixedID, memberID gidx.PrefixedID) (bool, error)
UnassignRole(ctx context.Context, roleID gidx.PrefixedID, memberID gidx.PrefixedID) error
}
// client is the permissions client.
type client struct {
logger *zap.SugaredLogger
httpClient *http.Client
@@ -39,7 +55,7 @@ type Client struct {
// Do executes the provided request.
// If the out value is provided, the response will attempt to be json decoded.
func (c *Client) Do(req *http.Request, out any) (*http.Response, error) {
func (c *client) Do(req *http.Request, out any) (*http.Response, error) {
if c.token != "" {
req.Header.Set(echo.HeaderAuthorization, "Bearer "+c.token)
}
@@ -63,7 +79,7 @@ func (c *Client) Do(req *http.Request, out any) (*http.Response, error) {
}
// DoRequest creates a new request from the provided parameters and executes the request.
func (c *Client) DoRequest(ctx context.Context, method, path string, body io.Reader, out any) (*http.Response, error) {
func (c *client) DoRequest(ctx context.Context, method, path string, body io.Reader, out any) (*http.Response, error) {
path = strings.TrimPrefix(path, c.baseURL.Path)
pathURL, err := url.Parse(path)
@@ -109,8 +125,8 @@ func encodeJSON(v any) (*bytes.Buffer, error) {
}
// NewClient creats a new permissions client.
func NewClient(token string, options ...Option) (*Client, error) {
client := &Client{
func NewClient(token string, options ...Option) (Client, error) {
client := &client{
logger: zap.NewNop().Sugar(),
httpClient: DefaultHTTPClient,
token: token,

View File

@@ -29,7 +29,7 @@ func MustViperFlags(v *viper.Viper, flags *pflag.FlagSet) {
// WithConfig applies all configurations defined in the config.
func WithConfig(config Config) Option {
return func(c *Client) error {
return func(c *client) error {
var options []Option
if config.BaseURL != "" {
@@ -52,7 +52,7 @@ func WithConfig(config Config) Option {
// WithBaseURL updates the baseurl used by the client.
func WithBaseURL(baseURL string) Option {
return func(c *Client) error {
return func(c *client) error {
u, err := url.Parse(baseURL)
if err != nil {
return fmt.Errorf("failed to parse emapi base url %s: %w", baseURL, err)
@@ -66,7 +66,7 @@ func WithBaseURL(baseURL string) Option {
// WithBearerToken sets the bearer token to authenticate the request with.
func WithBearerToken(token string) Option {
return func(c *Client) error {
return func(c *client) error {
c.token = token
return nil

View File

@@ -7,11 +7,11 @@ import (
)
// Option is a client configuration option definition.
type Option func(*Client) error
type Option func(*client) error
// WithLogger sets the logger for the client.
func WithLogger(logger *zap.SugaredLogger) Option {
return func(c *Client) error {
return func(c *client) error {
c.logger = logger
return nil
@@ -19,9 +19,9 @@ func WithLogger(logger *zap.SugaredLogger) Option {
}
// WithHTTPClient sets the http client to be used by the client.
func WithHTTPClient(client *http.Client) Option {
return func(c *Client) error {
c.httpClient = client
func WithHTTPClient(httpClient *http.Client) Option {
return func(c *client) error {
c.httpClient = httpClient
return nil
}

View File

@@ -34,7 +34,7 @@ type ResourceRelationshipDeleteResponse struct {
}
// DeleteResourceRelationship deletes the provided resources relationship to the given subject id.
func (c *Client) DeleteResourceRelationship(ctx context.Context, resourceID gidx.PrefixedID, relation string, relatedResourceID gidx.PrefixedID) error {
func (c *client) DeleteResourceRelationship(ctx context.Context, resourceID gidx.PrefixedID, relation string, relatedResourceID gidx.PrefixedID) error {
path := fmt.Sprintf("/api/v1/resources/%s/relationships", resourceID.String())
body, err := encodeJSON(ResourceRelationshipRequest{
@@ -61,7 +61,7 @@ func (c *Client) DeleteResourceRelationship(ctx context.Context, resourceID gidx
// ListResourceRelationships returns resources related to the given id.
// If relatedResourceType is not provied, relations to subjects are returned.
// If relatedResourceType is provided, relations to the given resource are returned which match the given type.
func (c *Client) ListResourceRelationships(ctx context.Context, resourceID gidx.PrefixedID, relatedResourceType string) ([]ResourceRelationship, error) {
func (c *client) ListResourceRelationships(ctx context.Context, resourceID gidx.PrefixedID, relatedResourceType string) ([]ResourceRelationship, error) {
query := url.Values{
"resourceType": []string{relatedResourceType},
}

View File

@@ -34,7 +34,7 @@ type ResourceRole struct {
}
// CreateRole creates a role on the given resource id with the provided actions.
func (c *Client) CreateRole(ctx context.Context, resourceID gidx.PrefixedID, actions []string) (gidx.PrefixedID, error) {
func (c *client) CreateRole(ctx context.Context, resourceID gidx.PrefixedID, actions []string) (gidx.PrefixedID, error) {
path := fmt.Sprintf("/api/v1/resources/%s/roles", resourceID.String())
body, err := encodeJSON(ResourceRoleCreate{
@@ -59,7 +59,7 @@ func (c *Client) CreateRole(ctx context.Context, resourceID gidx.PrefixedID, act
}
// DeleteRole deletes the provided role.
func (c *Client) DeleteRole(ctx context.Context, roleID gidx.PrefixedID) error {
func (c *client) DeleteRole(ctx context.Context, roleID gidx.PrefixedID) error {
path := fmt.Sprintf("/api/v1/roles/%s", roleID.String())
var response ResourceRoleDeleteResponse
@@ -76,7 +76,7 @@ func (c *Client) DeleteRole(ctx context.Context, roleID gidx.PrefixedID) error {
}
// ListResourceRoles fetches all roles assigned to the provided resource.
func (c *Client) ListResourceRoles(ctx context.Context, resourceID gidx.PrefixedID) (ResourceRoles, error) {
func (c *client) ListResourceRoles(ctx context.Context, resourceID gidx.PrefixedID) (ResourceRoles, error) {
path := fmt.Sprintf("/api/v1/resources/%s/roles", resourceID.String())
var response struct {
@@ -91,7 +91,7 @@ func (c *Client) ListResourceRoles(ctx context.Context, resourceID gidx.Prefixed
}
// FindResourceRoleByActions fetches roles assigned to the provided resource and finds the first role where the actions match the provided actions.
func (c *Client) FindResourceRoleByActions(ctx context.Context, resourceID gidx.PrefixedID, actions []string) (ResourceRole, error) {
func (c *client) FindResourceRoleByActions(ctx context.Context, resourceID gidx.PrefixedID, actions []string) (ResourceRole, error) {
roles, err := c.ListResourceRoles(ctx, resourceID)
if err != nil {
return ResourceRole{}, err

View File

@@ -21,7 +21,7 @@ func WithLogger(logger *zap.SugaredLogger) Option {
}
// WithMetalClient sets the Equinix Metal client used by the service.
func WithMetalClient(client *metal.Client) Option {
func WithMetalClient(client metal.Client) Option {
return func(s *service) error {
s.metal = client
@@ -30,7 +30,7 @@ func WithMetalClient(client *metal.Client) Option {
}
// WithPermissionsClient sets the permissions client used by the service.
func WithPermissionsClient(client *permissions.Client) Option {
func WithPermissionsClient(client permissions.Client) Option {
return func(s *service) error {
s.perms = client

View File

@@ -78,13 +78,18 @@ type Service interface {
IsAssignableResource(id gidx.PrefixedID) bool
}
// EventPublisher defines the required methods to publish events.
type EventPublisher interface {
PublishChange(ctx context.Context, subjectType string, change events.ChangeMessage) error
}
var _ Service = &service{}
type service struct {
logger *zap.SugaredLogger
publisher *events.Publisher
metal *metal.Client
perms *permissions.Client
publisher EventPublisher
metal metal.Client
perms permissions.Client
idPrefixMap map[string]ObjectType
rootResource prefixedID
@@ -100,7 +105,7 @@ func (r prefixedID) PrefixedID() gidx.PrefixedID {
}
// New creates a new service.
func New(publisher *events.Publisher, metal *metal.Client, perms *permissions.Client, options ...Option) (Service, error) {
func New(publisher EventPublisher, metal metal.Client, perms permissions.Client, options ...Option) (Service, error) {
svc := &service{
publisher: publisher,
metal: metal,