-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
SCALRCORE-31246: Add client for run schedule rule
- Loading branch information
Showing
4 changed files
with
395 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
package scalr | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"fmt" | ||
"net/url" | ||
) | ||
|
||
// Compile-time proof of interface implementation. | ||
var _ RunScheduleRules = (*runScheduleRules)(nil) | ||
|
||
// RunScheduleRules describes all the run schedule rule related methods that the Scalr API supports. | ||
type RunScheduleRules interface { | ||
List(ctx context.Context, options RunScheduleRuleListOptions) (*RunScheduleRulesList, error) | ||
Create(ctx context.Context, options RunScheduleRuleCreateOptions) (*RunScheduleRule, error) | ||
Read(ctx context.Context, ruleID string) (*RunScheduleRule, error) | ||
Delete(ctx context.Context, ruleID string) error | ||
Update(ctx context.Context, ruleID string, options RunScheduleRuleUpdateOptions) (*RunScheduleRule, error) | ||
} | ||
|
||
// runScheduleRules implements RunScheduleRules. | ||
type runScheduleRules struct { | ||
client *Client | ||
} | ||
|
||
// RunScheduleRulesList represents a list of run schedule rules. | ||
type RunScheduleRulesList struct { | ||
*Pagination | ||
Items []*RunScheduleRule | ||
} | ||
|
||
// ScheduleMode represents the run-type that will be scheduled. | ||
type ScheduleMode string | ||
|
||
const ( | ||
ApplyMode ScheduleMode = "apply" | ||
DestroyMode ScheduleMode = "destroy" | ||
RefreshMode ScheduleMode = "refresh" | ||
) | ||
|
||
// RunScheduleRule represents a Scalr run schedule rule. | ||
type RunScheduleRule struct { | ||
ID string `jsonapi:"primary,run-schedule-rules"` | ||
Schedule string `jsonapi:"attr,schedule"` | ||
ScheduleMode ScheduleMode `jsonapi:"attr,schedule-mode"` | ||
|
||
Workspace *Workspace `jsonapi:"relation,workspace,omitempty"` | ||
} | ||
|
||
// RunScheduleRuleListOptions represents the options for listing run schedule rules. | ||
type RunScheduleRuleListOptions struct { | ||
ListOptions | ||
Workspace string `url:"filter[workspace],omitempty"` | ||
Include string `url:"include,omitempty"` | ||
} | ||
|
||
// List all run schedule rules in the workspace. | ||
func (s *runScheduleRules) List(ctx context.Context, options RunScheduleRuleListOptions) (*RunScheduleRulesList, error) { | ||
req, err := s.client.newRequest("GET", "run-schedule-rules", &options) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
runScheduleRulesList := &RunScheduleRulesList{} | ||
err = s.client.do(ctx, req, runScheduleRulesList) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return runScheduleRulesList, nil | ||
} | ||
|
||
// RunScheduleRuleCreateOptions represents the options for creating a new run schedule rule. | ||
type RunScheduleRuleCreateOptions struct { | ||
ID string `jsonapi:"primary,run-schedule-rules"` | ||
Schedule string `jsonapi:"attr,schedule"` | ||
ScheduleMode ScheduleMode `jsonapi:"attr,schedule-mode"` | ||
|
||
Workspace *Workspace `jsonapi:"relation,workspace,omitempty"` | ||
} | ||
|
||
// Create is used to create a new run schedule rule. | ||
func (s *runScheduleRules) Create(ctx context.Context, options RunScheduleRuleCreateOptions) (*RunScheduleRule, error) { | ||
options.ID = "" | ||
|
||
urlPath := fmt.Sprintf("run-schedule-rules") | ||
req, err := s.client.newRequest("POST", urlPath, &options) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
rule := &RunScheduleRule{} | ||
err = s.client.do(ctx, req, rule) | ||
|
||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return rule, nil | ||
} | ||
|
||
// Read a run schedule rule by ID. | ||
func (s *runScheduleRules) Read(ctx context.Context, ruleID string) (*RunScheduleRule, error) { | ||
if !validStringID(&ruleID) { | ||
return nil, errors.New("invalid value for run schedule rule ID") | ||
} | ||
|
||
urlPath := fmt.Sprintf("run-schedule-rules/%s", url.QueryEscape(ruleID)) | ||
|
||
options := struct { | ||
Include string `url:"include"` | ||
}{ | ||
Include: "workspace", | ||
} | ||
|
||
req, err := s.client.newRequest("GET", urlPath, options) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
rule := &RunScheduleRule{} | ||
err = s.client.do(ctx, req, rule) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return rule, nil | ||
} | ||
|
||
// RunScheduleRuleUpdateOptions represents the options for updating a run schedule rule. | ||
type RunScheduleRuleUpdateOptions struct { | ||
ID string `jsonapi:"primary,run-schedule-rules"` | ||
Schedule string `jsonapi:"attr,schedule"` | ||
ScheduleMode ScheduleMode `jsonapi:"attr,schedule-mode"` | ||
} | ||
|
||
// Update an existing run schedule rule. | ||
func (s *runScheduleRules) Update(ctx context.Context, ruleID string, options RunScheduleRuleUpdateOptions) (*RunScheduleRule, error) { | ||
if !validStringID(&ruleID) { | ||
return nil, errors.New("invalid value for run schedule rule ID") | ||
} | ||
|
||
urlPath := fmt.Sprintf("run-schedule-rules/%s", url.QueryEscape(ruleID)) | ||
|
||
req, err := s.client.newRequest("PATCH", urlPath, &options) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
rule := &RunScheduleRule{} | ||
err = s.client.do(ctx, req, rule) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return rule, nil | ||
} | ||
|
||
// Delete deletes a run schedule rule by its ID. | ||
func (s *runScheduleRules) Delete(ctx context.Context, ruleID string) error { | ||
if !validStringID(&ruleID) { | ||
return errors.New("invalid value for run schedule rule ID") | ||
} | ||
|
||
urlPath := fmt.Sprintf("run-schedule-rules/%s", url.QueryEscape(ruleID)) | ||
req, err := s.client.newRequest("DELETE", urlPath, nil) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return s.client.do(ctx, req, nil) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,198 @@ | ||
package scalr | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
"testing" | ||
) | ||
|
||
func TestRunScheduleRulesList(t *testing.T) { | ||
client := testClient(t) | ||
ctx := context.Background() | ||
environment, environmentCleanup := createEnvironment(t, client) | ||
defer environmentCleanup() | ||
workspace, workspaceCleanup := createWorkspace(t, client, environment) | ||
defer workspaceCleanup() | ||
|
||
scheduleRuleA, scheduleRuleACleanup := createRunScheduleRule(t, client, workspace, ApplyMode) | ||
defer scheduleRuleACleanup() | ||
scheduleRuleB, scheduleRuleBCleanup := createRunScheduleRule(t, client, workspace, DestroyMode) | ||
defer scheduleRuleBCleanup() | ||
|
||
t.Run("without include", func(t *testing.T) { | ||
ruleList, err := client.RunScheduleRules.List(ctx, RunScheduleRuleListOptions{ | ||
Workspace: workspace.ID, | ||
}) | ||
require.NoError(t, err) | ||
assert.Equal(t, 2, ruleList.TotalCount) | ||
|
||
ruleIDs := make([]string, len(ruleList.Items)) | ||
for i, rule := range ruleList.Items { | ||
ruleIDs[i] = rule.ID | ||
} | ||
assert.Contains(t, ruleIDs, scheduleRuleA.ID) | ||
assert.Contains(t, ruleIDs, scheduleRuleB.ID) | ||
}) | ||
|
||
t.Run("with include", func(t *testing.T) { | ||
ruleList, err := client.RunScheduleRules.List(ctx, RunScheduleRuleListOptions{ | ||
Workspace: workspace.ID, | ||
Include: "workspace", | ||
}) | ||
require.NoError(t, err) | ||
assert.Equal(t, 2, ruleList.TotalCount) | ||
for _, rule := range ruleList.Items { | ||
assert.NotEqual(t, rule.Workspace, nil) | ||
} | ||
}) | ||
} | ||
|
||
func TestRunScheduleRulesCreate(t *testing.T) { | ||
client := testClient(t) | ||
ctx := context.Background() | ||
environment, environmentCleanup := createEnvironment(t, client) | ||
defer environmentCleanup() | ||
workspace, workspaceCleanup := createWorkspace(t, client, environment) | ||
defer workspaceCleanup() | ||
|
||
t.Run("with valid options", func(t *testing.T) { | ||
options := RunScheduleRuleCreateOptions{ | ||
Schedule: "0 0 * * *", | ||
ScheduleMode: ApplyMode, | ||
Workspace: workspace, | ||
} | ||
rule, err := client.RunScheduleRules.Create(ctx, options) | ||
require.NoError(t, err) | ||
|
||
// Get a refreshed view from the API. | ||
refreshed, err := client.RunScheduleRules.Read(ctx, rule.ID) | ||
require.NoError(t, err) | ||
|
||
for _, item := range []*RunScheduleRule{ | ||
rule, | ||
refreshed, | ||
} { | ||
assert.NotEmpty(t, item.ID) | ||
} | ||
err = client.RunScheduleRules.Delete(ctx, rule.ID) | ||
require.NoError(t, err) | ||
}) | ||
|
||
t.Run("when rule type collision", func(t *testing.T) { | ||
options := RunScheduleRuleCreateOptions{ | ||
Schedule: "0 0 * * *", | ||
ScheduleMode: ApplyMode, | ||
Workspace: workspace, | ||
} | ||
rule, err := client.RunScheduleRules.Create(ctx, options) | ||
require.NoError(t, err) | ||
|
||
_, err = client.RunScheduleRules.Create(ctx, options) | ||
require.Error(t, err) | ||
|
||
err = client.RunScheduleRules.Delete(ctx, rule.ID) | ||
require.NoError(t, err) | ||
}) | ||
} | ||
|
||
func TestRunScheduleRulesRead(t *testing.T) { | ||
client := testClient(t) | ||
ctx := context.Background() | ||
environment, environmentCleanup := createEnvironment(t, client) | ||
defer environmentCleanup() | ||
workspace, workspaceCleanup := createWorkspace(t, client, environment) | ||
defer workspaceCleanup() | ||
|
||
ruleTest, ruleTestCleanup := createRunScheduleRule(t, client, workspace, ApplyMode) | ||
defer ruleTestCleanup() | ||
|
||
t.Run("by ID when the rule exists", func(t *testing.T) { | ||
rule, err := client.RunScheduleRules.Read(ctx, ruleTest.ID) | ||
require.NoError(t, err) | ||
assert.Equal(t, ruleTest.ID, rule.ID) | ||
}) | ||
|
||
t.Run("by ID when the rule does not exist", func(t *testing.T) { | ||
rule, err := client.RunScheduleRules.Read(ctx, "rule-nonexisting") | ||
assert.Nil(t, rule) | ||
assert.Error(t, err) | ||
}) | ||
|
||
t.Run("by ID without a valid rule ID", func(t *testing.T) { | ||
rule, err := client.RunScheduleRules.Read(ctx, badIdentifier) | ||
assert.Nil(t, rule) | ||
assert.EqualError(t, err, "invalid value for run schedule rule ID") | ||
}) | ||
} | ||
|
||
func TestRunScheduleRulesUpdate(t *testing.T) { | ||
client := testClient(t) | ||
ctx := context.Background() | ||
environment, environmentCleanup := createEnvironment(t, client) | ||
defer environmentCleanup() | ||
workspace, workspaceCleanup := createWorkspace(t, client, environment) | ||
defer workspaceCleanup() | ||
|
||
scheduleRuleA, scheduleRuleACleanup := createRunScheduleRule(t, client, workspace, ApplyMode) | ||
defer scheduleRuleACleanup() | ||
_, scheduleRuleBCleanup := createRunScheduleRule(t, client, workspace, RefreshMode) | ||
defer scheduleRuleBCleanup() | ||
|
||
t.Run("with valid options", func(t *testing.T) { | ||
options := RunScheduleRuleUpdateOptions{ | ||
Schedule: "* * * * *", | ||
ScheduleMode: DestroyMode, | ||
} | ||
|
||
rule, err := client.RunScheduleRules.Update(ctx, scheduleRuleA.ID, options) | ||
require.NoError(t, err) | ||
|
||
// Get a refreshed view from the API. | ||
refreshed, err := client.RunScheduleRules.Read(ctx, rule.ID) | ||
require.NoError(t, err) | ||
|
||
for _, item := range []*RunScheduleRule{ | ||
rule, | ||
refreshed, | ||
} { | ||
assert.Equal(t, options.Schedule, item.Schedule) | ||
assert.Equal(t, options.ScheduleMode, item.ScheduleMode) | ||
} | ||
}) | ||
|
||
t.Run("with mode collision", func(t *testing.T) { | ||
rule, err := client.RunScheduleRules.Update(ctx, scheduleRuleA.ID, RunScheduleRuleUpdateOptions{ | ||
Schedule: "* * * * *", | ||
ScheduleMode: RefreshMode, | ||
}) | ||
assert.Nil(t, rule) | ||
assert.Error(t, err) | ||
}) | ||
} | ||
|
||
func TestRunScheduleRulesDelete(t *testing.T) { | ||
client := testClient(t) | ||
ctx := context.Background() | ||
environment, environmentCleanup := createEnvironment(t, client) | ||
defer environmentCleanup() | ||
workspace, workspaceCleanup := createWorkspace(t, client, environment) | ||
defer workspaceCleanup() | ||
|
||
scheduleRule, _ := createRunScheduleRule(t, client, workspace, ApplyMode) | ||
|
||
t.Run("with valid options", func(t *testing.T) { | ||
err := client.RunScheduleRules.Delete(ctx, scheduleRule.ID) | ||
require.NoError(t, err) | ||
|
||
_, err = client.RunScheduleRules.Read(ctx, scheduleRule.ID) | ||
assert.Equal( | ||
t, | ||
ResourceNotFoundError{ | ||
Message: fmt.Sprintf("RunScheduleRule with ID '%s' not found or user unauthorized.", scheduleRule.ID), | ||
}.Error(), | ||
err.Error(), | ||
) | ||
}) | ||
} |
Oops, something went wrong.