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
✨ Add registry provider client and integration tests #404
Changes from all commits
5cb09b9
ece36e4
88b4b94
8c72683
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,243 @@ | ||||||||||||||||||
package tfe | ||||||||||||||||||
|
||||||||||||||||||
import ( | ||||||||||||||||||
"context" | ||||||||||||||||||
"errors" | ||||||||||||||||||
"fmt" | ||||||||||||||||||
"net/url" | ||||||||||||||||||
) | ||||||||||||||||||
|
||||||||||||||||||
// Compile-time proof of interface implementation. | ||||||||||||||||||
var _ RegistryProviders = (*registryProviders)(nil) | ||||||||||||||||||
|
||||||||||||||||||
// RegistryProviders describes all the registry provider related methods that the Terraform | ||||||||||||||||||
// Enterprise API supports. | ||||||||||||||||||
// | ||||||||||||||||||
// TFE API docs: https://www.terraform.io/docs/cloud/api/providers.html | ||||||||||||||||||
type RegistryProviders interface { | ||||||||||||||||||
// List all the providers within an organization. | ||||||||||||||||||
List(ctx context.Context, organization string, options *RegistryProviderListOptions) (*RegistryProviderList, error) | ||||||||||||||||||
|
||||||||||||||||||
// Create a registry provider | ||||||||||||||||||
Create(ctx context.Context, organization string, options RegistryProviderCreateOptions) (*RegistryProvider, error) | ||||||||||||||||||
|
||||||||||||||||||
// Read a registry provider | ||||||||||||||||||
Read(ctx context.Context, organization string, registryName RegistryName, namespace string, name string, options *RegistryProviderReadOptions) (*RegistryProvider, error) | ||||||||||||||||||
|
||||||||||||||||||
// Delete a registry provider | ||||||||||||||||||
Delete(ctx context.Context, organization string, registryName RegistryName, namespace string, name string) error | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
// registryProviders implements RegistryProviders. | ||||||||||||||||||
type registryProviders struct { | ||||||||||||||||||
client *Client | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
// RegistryName represents which registry is being targeted | ||||||||||||||||||
type RegistryName string | ||||||||||||||||||
|
||||||||||||||||||
// List of available registry names | ||||||||||||||||||
const ( | ||||||||||||||||||
PrivateRegistry RegistryName = "private" | ||||||||||||||||||
PublicRegistry RegistryName = "public" | ||||||||||||||||||
) | ||||||||||||||||||
|
||||||||||||||||||
// RegistryProvider represents a registry provider | ||||||||||||||||||
type RegistryProvider struct { | ||||||||||||||||||
ID string `jsonapi:"primary,registry-providers"` | ||||||||||||||||||
Namespace string `jsonapi:"attr,namespace"` | ||||||||||||||||||
Name string `jsonapi:"attr,name"` | ||||||||||||||||||
RegistryName RegistryName `jsonapi:"attr,registry-name"` | ||||||||||||||||||
Permissions RegistryProviderPermissions `jsonapi:"attr,permissions"` | ||||||||||||||||||
CreatedAt string `jsonapi:"attr,created-at"` | ||||||||||||||||||
UpdatedAt string `jsonapi:"attr,updated-at"` | ||||||||||||||||||
|
||||||||||||||||||
// Relations | ||||||||||||||||||
Organization *Organization `jsonapi:"relation,organization"` | ||||||||||||||||||
RegistryProviderVersions []RegistryProviderVersion `jsonapi:"relation,registry-provider-version"` | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
type RegistryProviderPermissions struct { | ||||||||||||||||||
CanDelete bool `jsonapi:"attr,can-delete"` | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
type RegistryProviderListOptions struct { | ||||||||||||||||||
ListOptions | ||||||||||||||||||
// A query string to filter by registry_name | ||||||||||||||||||
RegistryName *RegistryName `url:"filter[registry_name],omitempty"` | ||||||||||||||||||
// A query string to filter by organization | ||||||||||||||||||
OrganizationName *string `url:"filter[organization_name],omitempty"` | ||||||||||||||||||
// A query string to do a fuzzy search | ||||||||||||||||||
Search *string `url:"q,omitempty"` | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
type RegistryProviderList struct { | ||||||||||||||||||
*Pagination | ||||||||||||||||||
Items []*RegistryProvider | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
func (o RegistryProviderListOptions) valid() error { | ||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||||||||||||||
return nil | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
func (r *registryProviders) List(ctx context.Context, organization string, options *RegistryProviderListOptions) (*RegistryProviderList, error) { | ||||||||||||||||||
|
||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. unnecessary whitespace ✏️ |
||||||||||||||||||
if !validStringID(&organization) { | ||||||||||||||||||
return nil, ErrInvalidOrg | ||||||||||||||||||
} | ||||||||||||||||||
if options != nil { | ||||||||||||||||||
if err := options.valid(); err != nil { | ||||||||||||||||||
return nil, err | ||||||||||||||||||
} | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
u := fmt.Sprintf("organizations/%s/registry-providers", url.QueryEscape(organization)) | ||||||||||||||||||
req, err := r.client.newRequest("GET", u, options) | ||||||||||||||||||
if err != nil { | ||||||||||||||||||
return nil, err | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
pl := &RegistryProviderList{} | ||||||||||||||||||
err = r.client.do(ctx, req, pl) | ||||||||||||||||||
if err != nil { | ||||||||||||||||||
return nil, err | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
return pl, nil | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
// RegistryProviderCreateOptions is used when creating a registry provider | ||||||||||||||||||
type RegistryProviderCreateOptions struct { | ||||||||||||||||||
// Type is a public field utilized by JSON:API to | ||||||||||||||||||
// set the resource type via the field tag. | ||||||||||||||||||
// It is not a user-defined value and does not need to be set. | ||||||||||||||||||
// https://jsonapi.org/format/#crud-creating | ||||||||||||||||||
Type string `jsonapi:"primary,registry-providers"` | ||||||||||||||||||
|
||||||||||||||||||
Namespace *string `jsonapi:"attr,namespace"` | ||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These fields should not be pointers unless the field is a struct type or the API accepts a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In PR #313 they are not pointers. |
||||||||||||||||||
Name *string `jsonapi:"attr,name"` | ||||||||||||||||||
RegistryName *RegistryName `jsonapi:"attr,registry-name"` | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
func (o RegistryProviderCreateOptions) valid() error { | ||||||||||||||||||
if !validString(o.Name) { | ||||||||||||||||||
return ErrRequiredName | ||||||||||||||||||
} | ||||||||||||||||||
if !validStringID(o.Name) { | ||||||||||||||||||
return ErrInvalidName | ||||||||||||||||||
} | ||||||||||||||||||
if !validString(o.Namespace) { | ||||||||||||||||||
return errors.New("namespace is required") | ||||||||||||||||||
} | ||||||||||||||||||
if !validStringID(o.Namespace) { | ||||||||||||||||||
return errors.New("invalid value for namespace") | ||||||||||||||||||
} | ||||||||||||||||||
if !validString((*string)(o.RegistryName)) { | ||||||||||||||||||
return errors.New("registry-name is required") | ||||||||||||||||||
} | ||||||||||||||||||
return nil | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
func (r *registryProviders) Create(ctx context.Context, organization string, options RegistryProviderCreateOptions) (*RegistryProvider, error) { | ||||||||||||||||||
if !validStringID(&organization) { | ||||||||||||||||||
return nil, ErrInvalidOrg | ||||||||||||||||||
} | ||||||||||||||||||
if err := options.valid(); err != nil { | ||||||||||||||||||
return nil, err | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
u := fmt.Sprintf( | ||||||||||||||||||
"organizations/%s/registry-providers", | ||||||||||||||||||
url.QueryEscape(organization), | ||||||||||||||||||
) | ||||||||||||||||||
req, err := r.client.newRequest("POST", u, &options) | ||||||||||||||||||
if err != nil { | ||||||||||||||||||
return nil, err | ||||||||||||||||||
} | ||||||||||||||||||
prv := &RegistryProvider{} | ||||||||||||||||||
err = r.client.do(ctx, req, prv) | ||||||||||||||||||
if err != nil { | ||||||||||||||||||
return nil, err | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
return prv, nil | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
type RegistryProviderReadOptions struct { | ||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Empty struct? 👻 |
||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
func (r *registryProviders) Read(ctx context.Context, organization string, registryName RegistryName, namespace string, name string, options *RegistryProviderReadOptions) (*RegistryProvider, error) { | ||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Whenever I see a long method signature like that I wonder if we can use a struct to keep the signature concise? An example: Lines 75 to 82 in 11a4eb9
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is refactored in the next PR. Wondering if it's simpler to go with the large PR that has everything? |
||||||||||||||||||
if !validStringID(&organization) { | ||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. All these There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This has been refactored. |
||||||||||||||||||
return nil, ErrInvalidOrg | ||||||||||||||||||
} | ||||||||||||||||||
if !validString(&name) { | ||||||||||||||||||
return nil, ErrRequiredName | ||||||||||||||||||
} | ||||||||||||||||||
if !validStringID(&name) { | ||||||||||||||||||
return nil, ErrInvalidName | ||||||||||||||||||
} | ||||||||||||||||||
if !validString(&namespace) { | ||||||||||||||||||
return nil, errors.New("namespace is required") | ||||||||||||||||||
} | ||||||||||||||||||
if !validStringID(&namespace) { | ||||||||||||||||||
return nil, errors.New("invalid value for namespace") | ||||||||||||||||||
} | ||||||||||||||||||
if !validString((*string)(®istryName)) { | ||||||||||||||||||
return nil, errors.New("registry-name is required") | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
u := fmt.Sprintf( | ||||||||||||||||||
"organizations/%s/registry-providers/%s/%s/%s", | ||||||||||||||||||
url.QueryEscape(organization), | ||||||||||||||||||
url.QueryEscape(string(registryName)), | ||||||||||||||||||
url.QueryEscape(namespace), | ||||||||||||||||||
url.QueryEscape(name), | ||||||||||||||||||
) | ||||||||||||||||||
req, err := r.client.newRequest("GET", u, options) | ||||||||||||||||||
if err != nil { | ||||||||||||||||||
return nil, err | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
prv := &RegistryProvider{} | ||||||||||||||||||
err = r.client.do(ctx, req, prv) | ||||||||||||||||||
if err != nil { | ||||||||||||||||||
return nil, err | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
return prv, nil | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
func (r *registryProviders) Delete(ctx context.Context, organization string, registryName RegistryName, namespace string, name string) error { | ||||||||||||||||||
if !validStringID(&organization) { | ||||||||||||||||||
return ErrInvalidOrg | ||||||||||||||||||
} | ||||||||||||||||||
if !validString(&name) { | ||||||||||||||||||
return ErrRequiredName | ||||||||||||||||||
} | ||||||||||||||||||
if !validStringID(&name) { | ||||||||||||||||||
return ErrInvalidName | ||||||||||||||||||
} | ||||||||||||||||||
if !validString(&namespace) { | ||||||||||||||||||
return errors.New("namespace is required") | ||||||||||||||||||
} | ||||||||||||||||||
if !validStringID(&namespace) { | ||||||||||||||||||
return errors.New("invalid value for namespace") | ||||||||||||||||||
} | ||||||||||||||||||
if !validString((*string)(®istryName)) { | ||||||||||||||||||
return errors.New("registry-name is required") | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
u := fmt.Sprintf( | ||||||||||||||||||
"organizations/%s/registry-providers/%s/%s/%s", | ||||||||||||||||||
url.QueryEscape(organization), | ||||||||||||||||||
url.QueryEscape(string(registryName)), | ||||||||||||||||||
url.QueryEscape(namespace), | ||||||||||||||||||
url.QueryEscape(name), | ||||||||||||||||||
) | ||||||||||||||||||
req, err := r.client.newRequest("DELETE", u, nil) | ||||||||||||||||||
if err != nil { | ||||||||||||||||||
return err | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
return r.client.do(ctx, req, nil) | ||||||||||||||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We follow the doc convention:
// Optional: A query string to filter by registry_name
even though the field being a pointer is implying that it's optional.