diff --git a/pkg/platformhubaccounts/platform_hub_account.go b/pkg/platformhubaccounts/platform_hub_account.go new file mode 100644 index 00000000..52a17c75 --- /dev/null +++ b/pkg/platformhubaccounts/platform_hub_account.go @@ -0,0 +1,77 @@ +package platformhubaccounts + +import ( + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/resources" + validation "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/validation" + "github.com/go-playground/validator/v10" + "github.com/go-playground/validator/v10/non-standard/validators" +) + +// IPlatformHubAccount defines the interface for Platform Hub accounts. +type IPlatformHubAccount interface { + GetAccountType() PlatformHubAccountType + GetDescription() string + SetDescription(string) + + resources.IHasName + resources.IResource +} + +// platformHubAccount is the embedded struct used for all Platform Hub accounts. +type platformHubAccount struct { + AccountType PlatformHubAccountType `json:"AccountType" validate:"required"` + Description string `json:"Description,omitempty"` + Name string `json:"Name" validate:"required,notblank"` + + resources.Resource +} + +// newPlatformHubAccount creates and initializes a Platform Hub account. +func newPlatformHubAccount(name string, accountType PlatformHubAccountType) *platformHubAccount { + return &platformHubAccount{ + AccountType: accountType, + Name: name, + Resource: *resources.NewResource(), + } +} + +// GetAccountType returns the type of this account. +func (a *platformHubAccount) GetAccountType() PlatformHubAccountType { + return a.AccountType +} + +// GetDescription returns the description of the account. +func (a *platformHubAccount) GetDescription() string { + return a.Description +} + +// GetName returns the name of the account. +func (a *platformHubAccount) GetName() string { + return a.Name +} + +// SetDescription sets the description of the account. +func (a *platformHubAccount) SetDescription(description string) { + a.Description = description +} + +// SetName sets the name of the account. +func (a *platformHubAccount) SetName(name string) { + a.Name = name +} + +// Validate checks the state of the account and returns an error if invalid. +func (a *platformHubAccount) Validate() error { + v := validator.New() + err := v.RegisterValidation("notblank", validators.NotBlank) + if err != nil { + return err + } + err = v.RegisterValidation("notall", validation.NotAll) + if err != nil { + return err + } + return v.Struct(a) +} + +var _ IPlatformHubAccount = &platformHubAccount{} diff --git a/pkg/platformhubaccounts/platform_hub_account_resource.go b/pkg/platformhubaccounts/platform_hub_account_resource.go new file mode 100644 index 00000000..ac91121e --- /dev/null +++ b/pkg/platformhubaccounts/platform_hub_account_resource.go @@ -0,0 +1,117 @@ +package platformhubaccounts + +import ( + "github.com/OctopusDeploy/go-octopusdeploy/v2/internal" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/core" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/resources" +) + +// PlatformHubAccountResource represents details for all Platform Hub accounts. +type PlatformHubAccountResource struct { + AccountType PlatformHubAccountType `json:"AccountType" validate:"required,oneof=AmazonWebServicesAccount"` + Description string `json:"Description,omitempty"` + Name string `json:"Name" validate:"required,notall"` + AccessKey string `json:"AccessKey,omitempty"` + SecretKey *core.SensitiveValue `json:"SecretKey,omitempty"` + + resources.Resource +} + +// NewPlatformHubAccountResource creates and initializes a Platform Hub account resource. +func NewPlatformHubAccountResource(name string, accountType PlatformHubAccountType) *PlatformHubAccountResource { + return &PlatformHubAccountResource{ + AccountType: accountType, + Name: name, + Resource: *resources.NewResource(), + } +} + +// GetAccountType returns the type of this account resource. +func (r *PlatformHubAccountResource) GetAccountType() PlatformHubAccountType { + return r.AccountType +} + +// GetDescription returns the description of this account resource. +func (r *PlatformHubAccountResource) GetDescription() string { + return r.Description +} + +// GetName returns the name of this account resource. +func (r *PlatformHubAccountResource) GetName() string { + return r.Name +} + +// SetDescription sets the description of the account resource. +func (r *PlatformHubAccountResource) SetDescription(description string) { + r.Description = description +} + +// SetName sets the name of this account resource. +func (r *PlatformHubAccountResource) SetName(name string) { + r.Name = name +} + +// ToPlatformHubAccount converts a PlatformHubAccountResource to the appropriate concrete account type. +func (r *PlatformHubAccountResource) ToPlatformHubAccount() (IPlatformHubAccount, error) { + if r == nil { + return nil, internal.CreateInvalidParameterError("ToPlatformHubAccount", "PlatformHubAccountResource") + } + + if err := r.Validate(); err != nil { + return nil, err + } + + var account IPlatformHubAccount + + switch r.AccountType { + case AccountTypePlatformHubAwsAccount: + awsAccount, err := NewPlatformHubAwsAccount(r.GetName(), r.AccessKey, r.SecretKey) + if err != nil { + return nil, err + } + account = awsAccount + default: + return nil, internal.CreateInvalidParameterError("ToPlatformHubAccount", "AccountType") + } + + account.SetDescription(r.Description) + account.SetID(r.GetID()) + account.SetLinks(r.GetLinks()) + account.SetModifiedBy(r.GetModifiedBy()) + account.SetModifiedOn(r.GetModifiedOn()) + + return account, nil +} + +// ToPlatformHubAccountResource converts a concrete account type to PlatformHubAccountResource. +func ToPlatformHubAccountResource(account IPlatformHubAccount) (*PlatformHubAccountResource, error) { + if account == nil { + return nil, internal.CreateInvalidParameterError("ToPlatformHubAccountResource", "PlatformHubAccount") + } + + // conversion unnecessary if input account is *PlatformHubAccountResource + if v, ok := account.(*PlatformHubAccountResource); ok { + return v, nil + } + + resource := NewPlatformHubAccountResource(account.GetName(), account.GetAccountType()) + + switch resource.AccountType { + case AccountTypePlatformHubAwsAccount: + awsAccount := account.(*PlatformHubAwsAccount) + resource.AccessKey = awsAccount.AccessKey + resource.SecretKey = awsAccount.SecretKey + default: + return nil, internal.CreateInvalidParameterError("ToPlatformHubAccountResource", "AccountType") + } + + resource.SetDescription(account.GetDescription()) + resource.SetID(account.GetID()) + resource.SetLinks(account.GetLinks()) + resource.SetModifiedBy(account.GetModifiedBy()) + resource.SetModifiedOn(account.GetModifiedOn()) + + return resource, nil +} + +var _ IPlatformHubAccount = &PlatformHubAccountResource{} diff --git a/pkg/platformhubaccounts/platform_hub_account_resource_test.go b/pkg/platformhubaccounts/platform_hub_account_resource_test.go new file mode 100644 index 00000000..8d01c078 --- /dev/null +++ b/pkg/platformhubaccounts/platform_hub_account_resource_test.go @@ -0,0 +1,106 @@ +package platformhubaccounts + +import ( + "testing" + + "github.com/OctopusDeploy/go-octopusdeploy/v2/internal" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/core" + "github.com/stretchr/testify/require" +) + +func TestToPlatformHubAccount(t *testing.T) { + // Test with nil resource + var accountResource *PlatformHubAccountResource + account, err := accountResource.ToPlatformHubAccount() + require.Nil(t, account) + require.Error(t, err) + + // Test with empty name and account type + accountResource = NewPlatformHubAccountResource("", "") + account, err = accountResource.ToPlatformHubAccount() + require.Nil(t, account) + require.Error(t, err) + + // Test with name but empty account type + accountResource = NewPlatformHubAccountResource(internal.GetRandomName(), "") + account, err = accountResource.ToPlatformHubAccount() + require.Nil(t, account) + require.Error(t, err) + + // Test with unknown account type (default case) + accountResource = NewPlatformHubAccountResource(internal.GetRandomName(), PlatformHubAccountType("UnknownAccountType")) + account, err = accountResource.ToPlatformHubAccount() + require.Nil(t, account) + require.Error(t, err) + + // Test with valid AWS account + accessKey := internal.GetRandomName() + name := internal.GetRandomName() + secretKey := core.NewSensitiveValue(internal.GetRandomName()) + + accountResource = NewPlatformHubAccountResource(name, AccountTypePlatformHubAwsAccount) + accountResource.AccessKey = accessKey + accountResource.SecretKey = secretKey + accountResource.Description = "Test description" + accountResource.ID = "test-id" + + account, err = accountResource.ToPlatformHubAccount() + require.NotNil(t, account) + require.NoError(t, err) + require.Equal(t, AccountTypePlatformHubAwsAccount, account.GetAccountType()) + require.Equal(t, name, account.GetName()) + require.Equal(t, "Test description", account.GetDescription()) + require.Equal(t, "test-id", account.GetID()) + + // Verify it's an AWS account + awsAccount, ok := account.(*PlatformHubAwsAccount) + require.True(t, ok) + require.Equal(t, accessKey, awsAccount.AccessKey) + require.Equal(t, secretKey, awsAccount.SecretKey) +} + +func TestToPlatformHubAccountResource(t *testing.T) { + // Test with nil account + accountResource, err := ToPlatformHubAccountResource(nil) + require.Nil(t, accountResource) + require.Error(t, err) + + // Test with valid AWS account + accessKey := internal.GetRandomName() + name := internal.GetRandomName() + secretKey := core.NewSensitiveValue(internal.GetRandomName()) + + awsAccount, err := NewPlatformHubAwsAccount(name, accessKey, secretKey) + require.NoError(t, err) + require.NotNil(t, awsAccount) + + awsAccount.Description = "Test description" + awsAccount.ID = "test-id" + + accountResource, err = ToPlatformHubAccountResource(awsAccount) + require.NotNil(t, accountResource) + require.NoError(t, err) + require.Equal(t, AccountTypePlatformHubAwsAccount, accountResource.GetAccountType()) + require.Equal(t, name, accountResource.GetName()) + require.Equal(t, "Test description", accountResource.GetDescription()) + require.Equal(t, "test-id", accountResource.GetID()) + require.Equal(t, accessKey, accountResource.AccessKey) + require.Equal(t, secretKey, accountResource.SecretKey) + + // Test that converting a resource returns it as-is + accountResource2, err := ToPlatformHubAccountResource(accountResource) + require.NotNil(t, accountResource2) + require.NoError(t, err) + require.Equal(t, accountResource, accountResource2) +} + +func TestToPlatformHubAccountResourceUnknownType(t *testing.T) { + // Create a resource with an unknown account type + name := internal.GetRandomName() + accountResource := NewPlatformHubAccountResource(name, PlatformHubAccountType("UnknownAccountType")) + + // This should fail when trying to convert back to account + account, err := accountResource.ToPlatformHubAccount() + require.Nil(t, account) + require.Error(t, err) +} diff --git a/pkg/platformhubaccounts/platform_hub_account_service.go b/pkg/platformhubaccounts/platform_hub_account_service.go new file mode 100644 index 00000000..c5f5613b --- /dev/null +++ b/pkg/platformhubaccounts/platform_hub_account_service.go @@ -0,0 +1,107 @@ +package platformhubaccounts + +import ( + "github.com/OctopusDeploy/go-octopusdeploy/v2/internal" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/newclient" +) + +const template = "/api/platformhub/accounts{/id}" + +// Add creates a new Platform Hub account. +func Add(client newclient.Client, platformHubAccount IPlatformHubAccount) (IPlatformHubAccount, error) { + if platformHubAccount == nil { + return nil, internal.CreateRequiredParameterIsEmptyOrNilError("platformHubAccount") + } + + if platformHubAccount.GetName() == "" { + return nil, internal.CreateRequiredParameterIsEmptyOrNilError("platformHubAccount.Name") + } + + path, err := client.URITemplateCache().Expand(template, map[string]any{}) + if err != nil { + return nil, err + } + + res, err := newclient.Post[PlatformHubAccountResource](client.HttpSession(), path, platformHubAccount) + if err != nil { + return nil, err + } + + if res.ID == "" { + return nil, internal.CreateRequiredParameterIsEmptyOrNilError("platformHubAccountID") + } + + //create only returns the id of the new resource, so we need to get the full object + return GetByID(client, res.ID) +} + +// GetByID returns the Platform Hub account that matches the input ID. +func GetByID(client newclient.Client, id string) (IPlatformHubAccount, error) { + if id == "" { + return nil, internal.CreateRequiredParameterIsEmptyOrNilError("id") + } + + path, err := client.URITemplateCache().Expand(template, map[string]any{"id": id}) + if err != nil { + return nil, err + } + + // Get the resource and convert to the appropriate concrete type + resource, err := newclient.Get[PlatformHubAccountResource](client.HttpSession(), path) + if err != nil { + return nil, err + } + + return resource.ToPlatformHubAccount() +} + +// Update modifies a Platform Hub account. +func Update(client newclient.Client, platformHubAccount IPlatformHubAccount) (IPlatformHubAccount, error) { + if platformHubAccount == nil { + return nil, internal.CreateRequiredParameterIsEmptyOrNilError("platformHubAccount") + } + + if platformHubAccount.GetID() == "" { + return nil, internal.CreateRequiredParameterIsEmptyOrNilError("platformHubAccount.ID") + } + + if platformHubAccount.GetName() == "" { + return nil, internal.CreateRequiredParameterIsEmptyOrNilError("platformHubAccount.Name") + } + + accountResource, err := ToPlatformHubAccountResource(platformHubAccount) + if err != nil { + return nil, err + } + + path, err := client.URITemplateCache().Expand(template, map[string]any{"id": platformHubAccount.GetID()}) + if err != nil { + return nil, err + } + + _, err = newclient.Put[PlatformHubAccountResource](client.HttpSession(), path, accountResource) + if err != nil { + return nil, err + } + + //modify doesn't return the updated object, so we return the input object after a successful update + return platformHubAccount, nil +} + +// Delete removes a Platform Hub account with the specified ID. +func Delete(client newclient.Client, id string) error { + if id == "" { + return internal.CreateRequiredParameterIsEmptyOrNilError("id") + } + + path, err := client.URITemplateCache().Expand(template, map[string]any{"id": id}) + if err != nil { + return err + } + + err = newclient.Delete(client.HttpSession(), path) + if err != nil { + return err + } + return nil +} diff --git a/pkg/platformhubaccounts/platform_hub_account_types.go b/pkg/platformhubaccounts/platform_hub_account_types.go new file mode 100644 index 00000000..7481e58c --- /dev/null +++ b/pkg/platformhubaccounts/platform_hub_account_types.go @@ -0,0 +1,7 @@ +package platformhubaccounts + +type PlatformHubAccountType string + +const ( + AccountTypePlatformHubAwsAccount = PlatformHubAccountType("AmazonWebServicesAccount") +) diff --git a/pkg/platformhubaccounts/platform_hub_aws_account.go b/pkg/platformhubaccounts/platform_hub_aws_account.go new file mode 100644 index 00000000..d0942725 --- /dev/null +++ b/pkg/platformhubaccounts/platform_hub_aws_account.go @@ -0,0 +1,61 @@ +package platformhubaccounts + +import ( + "github.com/OctopusDeploy/go-octopusdeploy/v2/internal" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/constants" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/core" + validation "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/validation" + "github.com/go-playground/validator/v10" + "github.com/go-playground/validator/v10/non-standard/validators" +) + +// PlatformHubAwsAccount represents a Platform Hub AWS account. +type PlatformHubAwsAccount struct { + AccessKey string `json:"AccessKey" validate:"required"` + SecretKey *core.SensitiveValue `json:"SecretKey" validate:"required"` + + platformHubAccount +} + +// NewPlatformHubAwsAccount initializes and returns a Platform Hub AWS account with a name, access key, and secret key. +func NewPlatformHubAwsAccount(name string, accessKey string, secretKey *core.SensitiveValue) (*PlatformHubAwsAccount, error) { + if internal.IsEmpty(name) { + return nil, internal.CreateRequiredParameterIsEmptyOrNilError(constants.ParameterName) + } + + if internal.IsEmpty(accessKey) { + return nil, internal.CreateRequiredParameterIsEmptyOrNilError(constants.ParameterAccessKey) + } + + if secretKey == nil { + return nil, internal.CreateRequiredParameterIsEmptyOrNilError(constants.ParameterSecretKey) + } + + account := PlatformHubAwsAccount{ + AccessKey: accessKey, + SecretKey: secretKey, + platformHubAccount: *newPlatformHubAccount(name, AccountTypePlatformHubAwsAccount), + } + + // validate to ensure that all expectations are met + err := account.Validate() + if err != nil { + return nil, err + } + + return &account, nil +} + +// Validate checks the state of this account and returns an error if invalid. +func (a *PlatformHubAwsAccount) Validate() error { + v := validator.New() + err := v.RegisterValidation("notblank", validators.NotBlank) + if err != nil { + return err + } + err = v.RegisterValidation("notall", validation.NotAll) + if err != nil { + return err + } + return v.Struct(a) +} diff --git a/pkg/platformhubaccounts/platform_hub_aws_account_test.go b/pkg/platformhubaccounts/platform_hub_aws_account_test.go new file mode 100644 index 00000000..dea8b84a --- /dev/null +++ b/pkg/platformhubaccounts/platform_hub_aws_account_test.go @@ -0,0 +1,117 @@ +package platformhubaccounts + +import ( + "encoding/json" + "fmt" + "testing" + "time" + + "github.com/OctopusDeploy/go-octopusdeploy/v2/internal" + "github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/core" + "github.com/kinbiko/jsonassert" + "github.com/stretchr/testify/require" +) + +func TestPlatformHubAwsAccountNew(t *testing.T) { + accessKey := internal.GetRandomName() + accountType := AccountTypePlatformHubAwsAccount + description := "" + name := internal.GetRandomName() + secretKey := core.NewSensitiveValue(internal.GetRandomName()) + + account, err := NewPlatformHubAwsAccount(name, accessKey, secretKey) + + require.NotNil(t, account) + require.NoError(t, err) + require.NoError(t, account.Validate()) + + // resource + require.Equal(t, "", account.ID) + require.Equal(t, "", account.ModifiedBy) + require.Nil(t, account.ModifiedOn) + require.NotNil(t, account.Links) + + // IResource + require.Equal(t, "", account.GetID()) + require.Equal(t, "", account.GetModifiedBy()) + require.Nil(t, account.GetModifiedOn()) + require.NotNil(t, account.GetLinks()) + + // IPlatformHubAccount + require.Equal(t, accountType, account.GetAccountType()) + require.Equal(t, description, account.GetDescription()) + require.Equal(t, name, account.GetName()) + + // PlatformHubAwsAccount + require.Equal(t, accessKey, account.AccessKey) + require.Equal(t, secretKey, account.SecretKey) +} + +func TestPlatformHubAwsAccountMarshalJSON(t *testing.T) { + accessKey := internal.GetRandomName() + name := internal.GetRandomName() + secretKey := core.NewSensitiveValue(internal.GetRandomName()) + + secretKeyAsJSON, err := json.Marshal(secretKey) + require.NoError(t, err) + require.NotNil(t, secretKeyAsJSON) + + expectedJson := fmt.Sprintf(`{ + "AccessKey": "%s", + "AccountType": "AmazonWebServicesAccount", + "Name": "%s", + "SecretKey": %s + }`, accessKey, name, secretKeyAsJSON) + + account, err := NewPlatformHubAwsAccount(name, accessKey, secretKey) + require.NoError(t, err) + require.NotNil(t, account) + + accountAsJSON, err := json.Marshal(account) + require.NoError(t, err) + require.NotNil(t, accountAsJSON) + + jsonassert.New(t).Assertf(expectedJson, string(accountAsJSON)) +} + +func TestPlatformHubAwsAccountNewWithConfigs(t *testing.T) { + accessKey := internal.GetRandomName() + accountType := AccountTypePlatformHubAwsAccount + id := internal.GetRandomName() + modifiedBy := internal.GetRandomName() + modifiedOn := time.Now() + name := internal.GetRandomName() + description := "Description for " + name + " (OK to Delete)" + secretKey := core.NewSensitiveValue(internal.GetRandomName()) + + account, err := NewPlatformHubAwsAccount(name, accessKey, secretKey) + require.NoError(t, err) + require.NotNil(t, account) + require.NoError(t, account.Validate()) + + account.Description = description + account.ID = id + account.ModifiedBy = modifiedBy + account.ModifiedOn = &modifiedOn + + // resource + require.Equal(t, id, account.ID) + require.Equal(t, modifiedBy, account.ModifiedBy) + require.Equal(t, &modifiedOn, account.ModifiedOn) + require.NotNil(t, account.Links) + + // IResource + require.Equal(t, id, account.GetID()) + require.Equal(t, modifiedBy, account.GetModifiedBy()) + require.Equal(t, &modifiedOn, account.GetModifiedOn()) + require.NotNil(t, account.GetLinks()) + + // IPlatformHubAccount + require.Equal(t, accountType, account.GetAccountType()) + require.Equal(t, description, account.GetDescription()) + require.Equal(t, name, account.GetName()) + + // PlatformHubAwsAccount + require.Equal(t, accessKey, account.AccessKey) + require.Equal(t, secretKey, account.SecretKey) +}