Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions pkg/platformhubpolicies/platform_hub_policy.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package platformhubpolicies

import (
"sync"

"github.com/go-playground/validator/v10"
"github.com/go-playground/validator/v10/non-standard/validators"
)
Expand Down Expand Up @@ -107,11 +109,22 @@ func (p *PlatformHubPolicy) SetViolationAction(violationAction string) {

// Validate checks the state of the policy and returns an error if invalid.
func (p *PlatformHubPolicy) Validate() error {
validate, err := getValidator()
if err != nil {
return err
}

return validate.Struct(p)
}

var getValidator = sync.OnceValues(buildValidator)

func buildValidator() (*validator.Validate, error) {
v := validator.New()
err := v.RegisterValidation("notblank", validators.NotBlank)
if err != nil {
return err
return nil, err
}

return v.Struct(p)
return v, nil
}
84 changes: 83 additions & 1 deletion pkg/platformhubpolicies/platform_hub_policy_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/newclient"
)

const template = "/api/platformhub/{gitRef}/policies"
const template = "/api/platformhub/{gitRef}/policies{/slug}{?skip,take,partialName}"

// Add creates a new Platform Hub policy.
func Add(client newclient.Client, policy *PlatformHubPolicy, commitMessage string) (*PlatformHubPolicy, error) {
Expand All @@ -29,6 +29,66 @@ func Add(client newclient.Client, policy *PlatformHubPolicy, commitMessage strin
return createdPolicy, nil
}

// PoliciesQuery represents query parameters for listing policies.
type PoliciesQuery struct {
GitRef string `uri:"gitRef" json:"gitRef"`
PartialName string `uri:"partialName,omitempty" json:"partialName,omitempty"`
Skip int `uri:"skip,omitempty" json:"skip,omitempty"`
Take int `uri:"take,omitempty" json:"take,omitempty"`
}

// PoliciesResponse represents the response from listing Platform Hub policies.
type PoliciesResponse struct {
Policies []PlatformHubPolicy `json:"Policies"`
ItemsPerPage int `json:"ItemsPerPage"`
FilteredItemsCount int `json:"FilteredItemsCount"`
TotalItemsCount int `json:"TotalItemsCount"`
}

// Get returns a collection of Platform Hub policies based on the provided query.
func Get(client newclient.Client, query PoliciesQuery) (*PoliciesResponse, error) {
path, pathError := client.URITemplateCache().Expand(template, query)
if pathError != nil {
return nil, pathError
}

response, responseError := newclient.Get[PoliciesResponse](client.HttpSession(), path)
if responseError != nil {
return nil, responseError
}

return response, nil
}

// GetBySlug returns the Platform Hub policy that matches given GitRef and Slug.
func GetBySlug(client newclient.Client, gitRef string, slug string) (*PlatformHubPolicy, error) {
path, pathError := client.URITemplateCache().Expand(template, map[string]any{"gitRef": gitRef, "slug": slug})
if pathError != nil {
return nil, pathError
}

policy, err := newclient.Get[PlatformHubPolicy](client.HttpSession(), path)
if err != nil {
return nil, err
}

return policy, nil
}

// Update modifies an existing Platform Hub policy.
func Update(client newclient.Client, policy *PlatformHubPolicy, commitMessage string) (*PlatformHubPolicy, error) {
command, path, err := buildUpdateCommand(client, policy, commitMessage)
if err != nil {
return nil, err
}

updatedPolicy, err := newclient.Put[PlatformHubPolicy](client.HttpSession(), path, command)
if err != nil {
return nil, err
}
return updatedPolicy, nil
}

func buildAddCommand(client newclient.Client, policy *PlatformHubPolicy, commitMessage string) (*platformHubPolicyUpsertCommand, string, error) {
if policy == nil {
return nil, "", internal.CreateRequiredParameterIsEmptyOrNilError("policy")
Expand All @@ -51,6 +111,28 @@ func buildAddCommand(client newclient.Client, policy *PlatformHubPolicy, commitM
return &command, path, nil
}

func buildUpdateCommand(client newclient.Client, policy *PlatformHubPolicy, commitMessage string) (*platformHubPolicyUpsertCommand, string, error) {
if policy == nil {
return nil, "", internal.CreateRequiredParameterIsEmptyOrNilError("policy")
}

if validationError := policy.Validate(); validationError != nil {
return nil, "", internal.CreateValidationFailureError("Update", validationError)
}

path, pathError := client.URITemplateCache().Expand(template, map[string]any{"gitRef": policy.GitRef, "slug": policy.Slug})
if pathError != nil {
return nil, "", pathError
}

command := platformHubPolicyUpsertCommand{
ChangeDescription: commitMessage,
PlatformHubPolicy: *policy,
}

return &command, path, nil
}

type platformHubPolicyUpsertCommand struct {
ChangeDescription string `json:"ChangeDescription,omitempty"`

Expand Down
80 changes: 65 additions & 15 deletions pkg/platformhubpolicies/platform_hub_policy_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,25 @@ import (

func TestPlatformHubPolicyService_BuildAddCommand(t *testing.T) {
var client = newclient.NewClient(&newclient.HttpSession{})
policy := newPolicyBuilder().GitRef("refs/heads/test/branch")

// Policy is nil - should fail
nilCommand, nilPath, commandNilError := buildAddCommand(client, nil, "commit nil command")
require.ErrorContains(t, commandNilError, "policy")
require.Nil(t, nilCommand)
require.Empty(t, nilPath)

// Invalid policy
invalidPolicy := &PlatformHubPolicy{Name: "Invalid", GitRef: ""}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if this should be a separate test

invalidCommand, invalidPath, invalidCommandError := buildAddCommand(client, invalidPolicy, "commit invalid command")
require.Error(t, invalidCommandError)
require.Nil(t, invalidCommand)
require.Empty(t, invalidPath)

// Valid policy
policy := newPolicyBuilder().
GitRef("refs/heads/test/branch").
Description("Testing policy").
ViolationReason("Missing manual intervention")
urlEncodedGitRef := "refs%2Fheads%2Ftest%2Fbranch"
commitMessage := "Create new policy"

Expand All @@ -25,28 +43,60 @@ func TestPlatformHubPolicyService_BuildAddCommand(t *testing.T) {

require.Equal(t, fmt.Sprintf("/api/platformhub/%s/policies", urlEncodedGitRef), path)

require.NotNil(t, command)
require.Equal(t, policy.name, command.GetName())
require.Equal(t, policy.gitRef, command.GetGitRef())
require.Equal(t, policy.slug, command.GetSlug())
require.Equal(t, policy.scopeRego, command.GetScopeRego())
require.Equal(t, policy.conditionsRego, command.GetConditionsRego())
require.Equal(t, policy.violationAction, command.GetViolationAction())
require.Equal(t, commitMessage, command.ChangeDescription)
testAssertUpsertCommand(t, command, policy, commitMessage)
testAssertUpsertCommandMarshalJSON(t, command, policy, commitMessage)
}

func TestPlatformHubPolicyService_AddCommandMarshalJSON(t *testing.T) {
func TestPlatformHubPolicyService_BuildUpdateCommand(t *testing.T) {
var client = newclient.NewClient(&newclient.HttpSession{})
policy := newPolicyBuilder().GitRef("main").Description("Testing policy").ViolationReason("Missing manual intervention")
commitMessage := "Add new policy as JSON"

// Policy is nil - should fail
nilCommand, nilPath, commandNilError := buildUpdateCommand(client, nil, "commit nil command")
require.ErrorContains(t, commandNilError, "policy")
require.Nil(t, nilCommand)
require.Empty(t, nilPath)

// Invalid policy
invalidPolicy := &PlatformHubPolicy{Name: "Invalid", GitRef: "main", ViolationAction: ""}
invalidCommand, invalidPath, invalidCommandError := buildUpdateCommand(client, invalidPolicy, "commit invalid command")
require.Error(t, invalidCommandError)
require.Nil(t, invalidCommand)
require.Empty(t, invalidPath)

// Valid policy
policy := newPolicyBuilder().Name("Valid Policy").
GitRef("refs/heads/main").
Slug("valid_policy").
Description("Ok").
ViolationReason("None")
urlEncodedGitRef := "refs%2Fheads%2Fmain"
commitMessage := "Update valid policy"

newPolicy, policyError := policy.Create()
require.NoError(t, policyError)

command, _, commandError := buildAddCommand(client, newPolicy, commitMessage)
// Act
command, path, commandError := buildUpdateCommand(client, newPolicy, commitMessage)
require.NoError(t, commandError)

// Act
require.Equal(t, fmt.Sprintf("/api/platformhub/%s/policies/%s", urlEncodedGitRef, policy.slug), path)

testAssertUpsertCommand(t, command, policy, commitMessage)
testAssertUpsertCommandMarshalJSON(t, command, policy, commitMessage)
}

func testAssertUpsertCommand(t *testing.T, command *platformHubPolicyUpsertCommand, expected *policyBuilder, expectedCommit string) {
require.NotNil(t, command)
require.Equal(t, expected.name, command.GetName())
require.Equal(t, expected.gitRef, command.GetGitRef())
require.Equal(t, expected.slug, command.GetSlug())
require.Equal(t, expected.scopeRego, command.GetScopeRego())
require.Equal(t, expected.conditionsRego, command.GetConditionsRego())
require.Equal(t, expected.violationAction, command.GetViolationAction())
require.Equal(t, expectedCommit, command.ChangeDescription)
}

func testAssertUpsertCommandMarshalJSON(t *testing.T, command *platformHubPolicyUpsertCommand, expected *policyBuilder, expectedCommit string) {
jsonCommand, jsonError := json.Marshal(command)
require.NoError(t, jsonError)
require.NotNil(t, jsonCommand)
Expand All @@ -61,7 +111,7 @@ func TestPlatformHubPolicyService_AddCommandMarshalJSON(t *testing.T) {
"ConditionsRego": "%s",
"ViolationReason": "%s",
"ViolationAction": "%s"
}`, commitMessage, policy.name, policy.gitRef, policy.slug, policy.description, policy.scopeRego, policy.conditionsRego, policy.violationReason, policy.violationAction)
}`, expectedCommit, expected.name, expected.gitRef, expected.slug, expected.description, expected.scopeRego, expected.conditionsRego, expected.violationReason, expected.violationAction)

jsonassert.New(t).Assertf(expectedJson, string(jsonCommand))
}
96 changes: 96 additions & 0 deletions pkg/platformhubpolicies/platform_hub_policy_version_service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package platformhubpolicies

import (
"time"

"github.com/OctopusDeploy/go-octopusdeploy/v2/pkg/newclient"
)

// PlatformHubPolicyVersion represents an immutable published version of a Platform Hub policy.
type PlatformHubPolicyVersion struct {
ID string `json:"Id"`
Slug string `json:"Slug"`
Version string `json:"Version"`
PublishedDate time.Time `json:"PublishedDate"`
GitRef string `json:"GitRef"`
GitCommit string `json:"GitCommit"`
Name string `json:"Name"`
Description string `json:"Description,omitempty"`
ViolationReason string `json:"ViolationReason,omitempty"`
ViolationAction string `json:"ViolationAction"`
RegoScope string `json:"RegoScope"`
RegoConditions string `json:"RegoConditions"`
IsActive bool `json:"IsActive"`
}

// Publish publishes a Platform Hub policy version.
func Publish(client newclient.Client, gitRef string, slug string, version string) (PlatformHubPolicyVersion, error) {
path, pathError := client.URITemplateCache().Expand("/api/platformhub/{gitRef}/policies/{slug}/publish", map[string]any{"gitRef": gitRef, "slug": slug})
if pathError != nil {
return PlatformHubPolicyVersion{}, pathError
}

body := struct {
Version string `json:"Version"`
}{Version: version}

publishedVersion, err := newclient.Post[PlatformHubPolicyVersion](client.HttpSession(), path, body)
if err != nil {
return PlatformHubPolicyVersion{}, err
}

return *publishedVersion, nil
}

// VersionsQuery represents query parameters for listing policy versions.
type VersionsQuery struct {
Slug string `uri:"slug"`
Skip int `uri:"skip,omitempty"`
Take int `uri:"take,omitempty"`
}

// GetVersions returns published versions of a Platform Hub policy.
func GetVersions(client newclient.Client, query VersionsQuery) ([]PlatformHubPolicyVersion, error) {
path, pathError := client.URITemplateCache().Expand("/api/platformhub/policies/{slug}/versions{?skip,take}", query)
if pathError != nil {
return nil, pathError
}

versions, err := newclient.Get[[]PlatformHubPolicyVersion](client.HttpSession(), path)
if err != nil {
return nil, err
}

return *versions, nil
}

// ActivateVersion activates a published Platform Hub policy version.
func ActivateVersion(client newclient.Client, version PlatformHubPolicyVersion) (PlatformHubPolicyVersion, error) {
return modifyVersionStatus(client, version, true)
}

// DeactivateVersion deactivates a published Platform Hub policy version.
func DeactivateVersion(client newclient.Client, version PlatformHubPolicyVersion) (PlatformHubPolicyVersion, error) {
return modifyVersionStatus(client, version, false)
}

func modifyVersionStatus(client newclient.Client, version PlatformHubPolicyVersion, isActive bool) (PlatformHubPolicyVersion, error) {
path, pathError := client.URITemplateCache().Expand("/api/platformhub/policies/{slug}/versions/{version}/modify-status", map[string]any{
"slug": version.Slug,
"version": version.Version,
})
if pathError != nil {
return version, pathError
}

body := struct {
IsActive bool `json:"IsActive"`
}{IsActive: isActive}

modifiedVersion, err := newclient.Post[PlatformHubPolicyVersion](client.HttpSession(), path, body)
if err != nil {
return version, err
}

return *modifiedVersion, nil
}
Loading