Skip to content

Commit

Permalink
finalize and add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
parkedwards committed Nov 21, 2024
1 parent 18dd114 commit 929b484
Show file tree
Hide file tree
Showing 5 changed files with 390 additions and 79 deletions.
7 changes: 6 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
module github.com/prefecthq/terraform-provider-prefect

go 1.22.0
go 1.22.7

toolchain go1.22.9

require (
Expand All @@ -15,6 +16,8 @@ require (
github.com/hashicorp/terraform-plugin-go v0.25.0
github.com/hashicorp/terraform-plugin-log v0.9.0
github.com/hashicorp/terraform-plugin-testing v1.10.0
github.com/stretchr/testify v1.9.0
k8s.io/utils v0.0.0-20241104163129-6fe5fd82f078
)

require (
Expand All @@ -30,6 +33,7 @@ require (
github.com/bgentry/speakeasy v0.1.0 // indirect
github.com/bmatcuk/doublestar/v4 v4.7.1 // indirect
github.com/cloudflare/circl v1.3.7 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fatih/color v1.16.0 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/go-cmp v0.6.0 // indirect
Expand Down Expand Up @@ -63,6 +67,7 @@ require (
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/oklog/run v1.0.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/posener/complete v1.2.3 // indirect
github.com/shopspring/decimal v1.3.1 // indirect
github.com/spf13/cast v1.5.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -289,3 +289,5 @@ gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
k8s.io/utils v0.0.0-20241104163129-6fe5fd82f078 h1:jGnCPejIetjiy2gqaJ5V0NLwTpF4wbQ6cZIItJCSHno=
k8s.io/utils v0.0.0-20241104163129-6fe5fd82f078/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
195 changes: 119 additions & 76 deletions internal/api/automations.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,113 +2,156 @@ package api

import (
"context"
"encoding/json"
"fmt"

"github.com/google/uuid"
)

// AutomationsClient is a client for working with automations.
type AutomationsClient interface {
Get(ctx context.Context, id uuid.UUID) (*Automation, error)
Create(ctx context.Context, data AutomationCreate) (*Automation, error)
Update(ctx context.Context, id uuid.UUID, data AutomationUpdate) error
Create(ctx context.Context, data AutomationUpsert) (*Automation, error)
Update(ctx context.Context, id uuid.UUID, data AutomationUpsert) error
Delete(ctx context.Context, id uuid.UUID) error
}

type TriggerTypes interface {
// No methods needed - this is just for type union
// Automation represents an automation response.
type Automation struct {
BaseModel
AutomationUpsert
AccountID uuid.UUID `json:"account_id"`
WorkspaceID uuid.UUID `json:"workspace_id"`
}
type AutomationUpsert struct {
Name string `json:"name"`
Description string `json:"description"`
Enabled bool `json:"enabled"`
Trigger Trigger `json:"trigger"`
Actions []Action `json:"actions"`
ActionsOnTrigger []Action `json:"actions_on_trigger"`
ActionsOnResolve []Action `json:"actions_on_resolve"`
}

type TriggerBase struct {
type Trigger struct {
Type string `json:"type"`
ID string `json:"id"`
}

type EventTrigger struct {
TriggerBase
After []string `json:"after"`
Expect []string `json:"expect"`
ForEach []string `json:"for_each"`
Posture string `json:"posture"`
Threshold int `json:"threshold"`
Within int `json:"within"`
// For EventTrigger
Match *ResourceSpecification `json:"match,omitempty"`
MatchRelated *ResourceSpecification `json:"match_related,omitempty"`
Posture *string `json:"posture,omitempty"`
After []string `json:"after,omitempty"`
Expect []string `json:"expect,omitempty"`
ForEach []string `json:"for_each,omitempty"`
Threshold *int `json:"threshold,omitempty"`
Within *string `json:"within,omitempty"` // Duration string
// For MetricTrigger
Metric *MetricTriggerQuery `json:"metric,omitempty"`
// For CompoundTrigger
Triggers []Trigger `json:"triggers,omitempty"`
Require interface{} `json:"require,omitempty"` // int or string ("any"/"all")
}

// Ensure EventTrigger implements TriggerTypes.
var _ TriggerTypes = (*EventTrigger)(nil)
type MetricTriggerQuery struct {
Name string `json:"name"`
Threshold float64 `json:"threshold"`
Operator string `json:"operator"` // "<", "<=", ">", ">="
Range int `json:"range"`
FiringFor int `json:"firing_for"`
}

type MetricTriggerOperator string
// ResourceSpecification is a composite type that returns a map
// where the keys are strings and the values
// can be either (1) a string or (2) a list of strings.
//
// ex:
//
// {
// "resource_type": "aws_s3_bucket",
// "tags": ["tag1", "tag2"]
// }
//
// This is used for the `match` and `match_related` fields.
type ResourceSpecification map[string]StringOrSlice

type StringOrSlice struct {
String string
StringList []string
IsList bool
}

const (
LT MetricTriggerOperator = "<"
LTE MetricTriggerOperator = "<="
GT MetricTriggerOperator = ">"
GTE MetricTriggerOperator = ">="
)
// For marshalling a ResourceSpecification to JSON.
func (s StringOrSlice) MarshalJSON() ([]byte, error) {
var val interface{}
val = s.StringList
if !s.IsList {
val = s.String
}

type PrefectMetric string
bytes, err := json.Marshal(val)
if err != nil {
return nil, fmt.Errorf("marshal string or slice: %w", err)
}

type MetricTriggerQuery struct {
Name PrefectMetric `json:"name"`
Threshold float64 `json:"threshold"`
Operator MetricTriggerOperator `json:"operator"`
Range int `json:"range"` // duration in seconds, min 300
FiringFor int `json:"firing_for"` // duration in seconds, min 300
return bytes, nil
}

type MetricTrigger struct {
TriggerBase
Posture string `json:"posture"`
Metric MetricTriggerQuery `json:"metric"`
}
// For unmarshalling a ResourceSpecification from JSON.
func (s *StringOrSlice) UnmarshalJSON(data []byte) error {
// Try as string first
var str string
if err := json.Unmarshal(data, &str); err == nil {
s.String = str
s.IsList = false

// Ensure MetricTrigger implements TriggerTypes.
var _ TriggerTypes = (*MetricTrigger)(nil)
return nil
}

type CompoundTrigger struct {
TriggerBase
Triggers []TriggerTypes `json:"triggers"`
Require interface{} `json:"require"` // int or "any"/"all"
Within *int `json:"within,omitempty"`
}
// Try as string slice
var strList []string
if err := json.Unmarshal(data, &strList); err == nil {
s.StringList = strList
s.IsList = true

var _ TriggerTypes = (*CompoundTrigger)(nil)
return nil
}

type SequenceTrigger struct {
TriggerBase
Triggers []TriggerTypes `json:"triggers"`
Within *int `json:"within,omitempty"`
return fmt.Errorf("ResourceSpecification must be string or string array")
}

// Ensure SequenceTrigger implements TriggerTypes.
var _ TriggerTypes = (*SequenceTrigger)(nil)
type Action struct {
// On all actions
Type string `json:"type"`

type Action struct{}
// On WorkPoolAction, WorkQueueAction, DeploymentAction, and AutomationAction
Source *string `json:"source,omitempty"`

type AutomationCore struct {
Name string `json:"name"`
Description string `json:"description"`
Enabled bool `json:"enabled"`
Trigger TriggerTypes `json:"trigger"`
Actions []Action `json:"actions"`
ActionsOnTrigger []Action `json:"actions_on_trigger"`
ActionsOnResolve []Action `json:"actions_on_resolve"`
}
// DeploymentAction fields
DeploymentID *uuid.UUID `json:"deployment_id,omitempty"`

// Automation represents an automation response.
type Automation struct {
BaseModel
AutomationCore
AccountID uuid.UUID `json:"account"`
WorkspaceID uuid.UUID `json:"workspace"`
}
// WorkPoolAction fields
WorkPoolID *uuid.UUID `json:"work_pool_id,omitempty"`

// AutomationCreate is the payload for creating automations.
type AutomationCreate struct {
AutomationCore
OwnerResource *string `json:"owner_resource"`
}
// WorkQueueAction fields
WorkQueueID *uuid.UUID `json:"work_queue_id,omitempty"`

// AutomationAction fields
AutomationID *uuid.UUID `json:"automation_id,omitempty"`

// RunDeployment fields
Parameters map[string]interface{} `json:"parameters,omitempty"`
JobVariables map[string]interface{} `json:"job_variables,omitempty"`

// ChangeFlowRunState fields
Name *string `json:"name,omitempty"`
State *string `json:"state,omitempty"`
Message *string `json:"message,omitempty"`

// Webhook fields
BlockDocumentID *uuid.UUID `json:"block_document_id,omitempty"`
Payload *string `json:"payload,omitempty"`

// AutomationUpdate is the payload for updating automations.
type AutomationUpdate struct {
AutomationCore
// Notification fields
Subject *string `json:"subject,omitempty"`
Body *string `json:"body,omitempty"`
}
Loading

0 comments on commit 929b484

Please sign in to comment.