Skip to content

Commit

Permalink
Merge pull request #67 from silinternational/develop
Browse files Browse the repository at this point in the history
Release 3.5.0 - New library functions for Run Triggers
  • Loading branch information
briskt authored Jun 20, 2023
2 parents 7a04682 + c4f5f6a commit e3f66d8
Show file tree
Hide file tree
Showing 7 changed files with 191 additions and 4 deletions.
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,19 @@ require (
github.com/manifoldco/promptui v0.9.0
github.com/spf13/cobra v1.6.1
github.com/spf13/viper v1.15.0
github.com/stretchr/testify v1.8.1
)

require (
github.com/chzyer/readline v1.5.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/pelletier/go-toml/v2 v2.0.7 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/spf13/afero v1.9.5 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
Expand Down
6 changes: 3 additions & 3 deletions lib/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,15 @@ func buildRunPayload(message, workspaceID string) string {

_, err := data.Object("data")
if err != nil {
return "error"
return "unable to create run payload:" + err.Error()
}

if _, err = data.SetP(message, "data.attributes.message"); err != nil {
return "unable to process attribute for update:" + err.Error()
return "unable to process message for run payload:" + err.Error()
}

if _, err = data.SetP(workspaceID, "data.relationships.workspace.data.id"); err != nil {
return "unable to process attribute for update:" + err.Error()
return "unable to process workspace ID for run payload:" + err.Error()
}

return data.String()
Expand Down
2 changes: 1 addition & 1 deletion lib/run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ import (
func Test_buildRunPayload(t *testing.T) {
got := buildRunPayload("my message", "ws_id")
if got != `{"data":{"attributes":{"message":"my message"},"relationships":{"workspace":{"data":{"id":"ws_id"}}}}}` {
t.Fatalf("did not get expected result, got %q", got)
t.Fatalf("did not get expected result, got %s", got)
}
}
112 changes: 112 additions & 0 deletions lib/runtrigger.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package lib

import (
"fmt"
"io"
"net/http"
"time"

"github.com/Jeffail/gabs/v2"
)

type RunTriggerConfig struct {
WorkspaceID string
SourceWorkspaceID string
}

func CreateRunTrigger(config RunTriggerConfig) error {
u := NewTfcUrl("/workspaces/" + config.WorkspaceID + "/run-triggers")
payload := buildRunTriggerPayload(config.SourceWorkspaceID)
_ = callAPI(http.MethodPost, u.String(), payload, nil)
return nil
}

func buildRunTriggerPayload(sourceWorkspaceID string) string {
data := gabs.New()
_, err := data.Object("data")
if err != nil {
return "unable to create run trigger payload:" + err.Error()
}

workspaceObject := gabs.Wrap(map[string]any{
"type": "workspaces",
"id": sourceWorkspaceID,
})
if _, err = data.SetP(workspaceObject, "data.relationships.sourceable.data"); err != nil {
return "unable to complete run trigger payload:" + err.Error()
}

return data.String()
}

type FindRunTriggerConfig struct {
SourceWorkspaceID string
WorkspaceID string
}

// FindRunTrigger searches all the run triggers inbound to the given WorkspaceID. If a run trigger is configured for
// the given SourceWorkspaceID, that trigger is returned. Otherwise, nil is returned.
func FindRunTrigger(config FindRunTriggerConfig) (*RunTrigger, error) {
triggers, err := ListRunTriggers(ListRunTriggerConfig{
WorkspaceID: config.WorkspaceID,
Type: "inbound",
})
if err != nil {
return nil, fmt.Errorf("failed to list run triggers: %w", err)
}
for _, t := range triggers {
if t.SourceID == config.SourceWorkspaceID {
return &t, nil
}
}
return nil, nil
}

type RunTrigger struct {
CreatedAt time.Time
SourceName string
SourceID string
WorkspaceName string
WorkspaceID string
}

type ListRunTriggerConfig struct {
WorkspaceID string
Type string // must be either "inbound" or "outbound"
}

// ListRunTriggers returns a list of run triggers configured for the given workspace
// https://developer.hashicorp.com/terraform/cloud-docs/api-docs/run-triggers#list-run-triggers
func ListRunTriggers(config ListRunTriggerConfig) ([]RunTrigger, error) {
u := NewTfcUrl("/workspaces/" + config.WorkspaceID + "/run-triggers")
u.SetParam(paramFilterRunTriggerType, config.Type)

resp := callAPI(http.MethodGet, u.String(), "", nil)
triggers, err := parseRunTriggerListResponse(resp.Body)
if err != nil {
return nil, err
}
return triggers, nil
}

func parseRunTriggerListResponse(r io.Reader) ([]RunTrigger, error) {
parsed, err := gabs.ParseJSONBuffer(r)
if err != nil {
return nil, fmt.Errorf("failed to parse response data: %w", err)
}

attributes := parsed.Search("data", "*").Children()
triggers := make([]RunTrigger, len(attributes))
for i, attr := range attributes {
trigger := RunTrigger{
SourceID: attr.Path("relationships.sourceable.data.id").Data().(string),
SourceName: attr.Path("attributes.sourceable-name").Data().(string),
WorkspaceID: attr.Path("relationships.workspace.data.id").Data().(string),
WorkspaceName: attr.Path("attributes.workspace-name").Data().(string),
}
createdAt, _ := time.Parse(time.RFC3339, attr.Path("attributes.created-at").Data().(string))
trigger.CreatedAt = createdAt
triggers[i] = trigger
}
return triggers, nil
}
55 changes: 55 additions & 0 deletions lib/runtrigger_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package lib

import (
"bytes"
"testing"
"time"

"github.com/stretchr/testify/require"
)

func Test_buildRunTriggerPayload(t *testing.T) {
got := buildRunTriggerPayload("ws_id")
if got != `{"data":{"relationships":{"sourceable":{"data":{"id":"ws_id","type":"workspaces"}}}}}` {
t.Fatalf("did not get expected result, got %s", got)
}
}

func Test_parseRunTriggerListResponse(t *testing.T) {
r := bytes.NewReader([]byte(listTriggerSampleBody))
triggers, err := parseRunTriggerListResponse(r)
require.NoError(t, err)
require.Equal(t, triggers[0].WorkspaceID, "ws-abcdefghijklmnop")
require.Equal(t, triggers[0].SourceID, "ws-qrstuvwxyzABCDEF")
require.Equal(t, triggers[0].WorkspaceName, "a-workspace-name")
require.Equal(t, triggers[0].SourceName, "source-ws-1")
require.Equal(t, triggers[0].CreatedAt, time.Date(2023, 6, 20, 8, 56, 50, 996e6, time.UTC))
}

const listTriggerSampleBody = `{
"data": [
{
"id": "rt-abcdefghijklmnop",
"type": "run-triggers",
"attributes": {
"workspace-name": "a-workspace-name",
"sourceable-name": "source-ws-1",
"created-at": "2023-06-20T08:56:50.996Z"
},
"relationships": {
"workspace": {
"data": {
"id": "ws-abcdefghijklmnop",
"type": "workspaces"
}
},
"sourceable": {
"data": {
"id": "ws-qrstuvwxyzABCDEF",
"type": "workspaces"
}
}
}
}
]
}`
2 changes: 2 additions & 0 deletions lib/url.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@ const (
paramFilterWorkspaceName = "filter[workspace][name]"
paramPageSize = "page[size]"
paramPageNumber = "page[number]"
paramFilterRunTriggerType = "filter[run-trigger][type]"
paramSearchName = "search[name]"
)

type TfcUrl struct {
url.URL
}

// NewTfcUrl creates a url.URL object for the Terraform Cloud API.
func NewTfcUrl(path string) TfcUrl {
newURL, _ := url.Parse(baseURL + path)
v := url.Values{}
Expand Down
15 changes: 15 additions & 0 deletions lib/url_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package lib

import (
"testing"

"github.com/stretchr/testify/require"
)

func TestNewTfcUrl(t *testing.T) {
orgs := NewTfcUrl("/organizations")
require.Equal(t, baseURL+"/organizations", orgs.String())

withQuery := NewTfcUrl("/organizations?q=foo")
require.Equal(t, baseURL+"/organizations", withQuery.String())
}

0 comments on commit e3f66d8

Please sign in to comment.