Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/add custom headers to audit trail #563

Merged
merged 9 commits into from
Aug 14, 2024
3 changes: 2 additions & 1 deletion docs/resources/audit_trail_webhook.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,11 @@ resource "spacelift_audit_trail_webhook" "example" {

- `enabled` (Boolean) `enabled` determines whether the webhook is enabled. If it is not, Spacelift will not send any requests to the endpoint.
- `endpoint` (String) `endpoint` is the URL to which Spacelift will send POST requests about audit events.
- `secret` (String, Sensitive) `secret` is a secret that Spacelift will send with the request
- `secret` (String, Sensitive) `secret` is a secret that Spacelift will send with the request.

### Optional

- `custom_headers` (Map of String) `custom_headers` is a Map of key-value strings, that will be passed as headers with audit trail requests.
- `include_runs` (Boolean) `include_runs` determines whether the webhook should include information about the run that triggered the event.

### Read-Only
Expand Down
6 changes: 6 additions & 0 deletions spacelift/internal/structs/audit_trail_webhook.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
package structs

type AuditTrailWebhook struct {
AuditTrailWebhookRead
// CustomHeaders is used only for create/update, API doesn't return them back.
CustomHeaders StringMap `graphql:"customHeaders"`
}

type AuditTrailWebhookRead struct {
Enabled bool `graphql:"enabled"`
Endpoint string `graphql:"endpoint"`
IncludeRuns bool `graphql:"includeRuns"`
Expand Down
21 changes: 17 additions & 4 deletions spacelift/internal/structs/audit_trail_webhook_input.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,21 @@ package structs
import "github.com/shurcooL/graphql"

type AuditTrailWebhookInput struct {
Enabled graphql.Boolean `json:"enabled"`
Endpoint graphql.String `json:"endpoint"`
IncludeRuns graphql.Boolean `json:"includeRuns"`
Secret graphql.String `json:"secret"`
Enabled graphql.Boolean `json:"enabled"`
Endpoint graphql.String `json:"endpoint"`
IncludeRuns graphql.Boolean `json:"includeRuns"`
Secret graphql.String `json:"secret"`
CustomHeaders *StringMap `json:"customHeaders"`
}

type StringMap struct {
Entries []KeyValuePair `json:"entries"`
}

func (m StringMap) ToStdMap() map[string]string {
mapped := make(map[string]string)
for _, kv := range m.Entries {
mapped[kv.Key] = kv.Value
}
return mapped
}
33 changes: 20 additions & 13 deletions spacelift/resource_audit_trail_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,22 +47,28 @@ func resourceAuditTrailWebhook() *schema.Resource {
Type: schema.TypeString,
Required: true,
Sensitive: true,
Description: "`secret` is a secret that Spacelift will send with the request",
Description: "`secret` is a secret that Spacelift will send with the request.",
},
"custom_headers": {
Type: schema.TypeMap,
Optional: true,
Description: "`custom_headers` is a Map of key-value strings, that will be passed as headers with audit trail requests.",
},
},
}
}

func resourceAuditTrailWebhookCreate(ctx context.Context, data *schema.ResourceData, i interface{}) diag.Diagnostics {
var mutation struct {
AuditTrailWebhook *structs.AuditTrailWebhook `graphql:"auditTrailSetWebhook(input: $input)"`
AuditTrailWebhook *structs.AuditTrailWebhookRead `graphql:"auditTrailSetWebhook(input: $input)"`
}
variables := map[string]interface{}{
"input": structs.AuditTrailWebhookInput{
Enabled: toBool(data.Get("enabled")),
Endpoint: toString(data.Get("endpoint")),
IncludeRuns: toBool(data.Get("include_runs")),
Secret: toString(data.Get("secret")),
Enabled: toBool(data.Get("enabled")),
Endpoint: toString(data.Get("endpoint")),
IncludeRuns: toBool(data.Get("include_runs")),
Secret: toString(data.Get("secret")),
CustomHeaders: toOptionalStringMap(data.Get("custom_headers")),
},
}
if err := i.(*internal.Client).Mutate(ctx, "AuditTrailWebhookCreate", &mutation, variables); err != nil {
Expand All @@ -76,7 +82,7 @@ func resourceAuditTrailWebhookCreate(ctx context.Context, data *schema.ResourceD

func resourceAuditTrailWebhookRead(ctx context.Context, data *schema.ResourceData, i interface{}) diag.Diagnostics {
var query struct {
AuditTrailWebhook *structs.AuditTrailWebhook `graphql:"auditTrailWebhook"`
AuditTrailWebhook *structs.AuditTrailWebhookRead `graphql:"auditTrailWebhook"`
}
if err := i.(*internal.Client).Query(ctx, "AuditTrailWebhookRead", &query, nil); err != nil {
return diag.Errorf("could not query for audit trail webhook: %v", internal.FromSpaceliftError(err))
Expand All @@ -97,14 +103,15 @@ func resourceAuditTrailWebhookRead(ctx context.Context, data *schema.ResourceDat

func resourceAuditTrailWebhookUpdate(ctx context.Context, data *schema.ResourceData, i interface{}) diag.Diagnostics {
var mutation struct {
AuditTrailWebhook *structs.AuditTrailWebhook `graphql:"auditTrailSetWebhook(input: $input)"`
AuditTrailWebhook *structs.AuditTrailWebhookRead `graphql:"auditTrailSetWebhook(input: $input)"`
}
variables := map[string]interface{}{
"input": structs.AuditTrailWebhookInput{
Enabled: toBool(data.Get("enabled")),
Endpoint: toString(data.Get("endpoint")),
IncludeRuns: toBool(data.Get("include_runs")),
Secret: toString(data.Get("secret")),
Enabled: toBool(data.Get("enabled")),
Endpoint: toString(data.Get("endpoint")),
IncludeRuns: toBool(data.Get("include_runs")),
Secret: toString(data.Get("secret")),
CustomHeaders: toOptionalStringMap(data.Get("custom_headers")),
},
}
if err := i.(*internal.Client).Mutate(ctx, "AuditTrailWebhookUpdate", &mutation, variables); err != nil {
Expand All @@ -116,7 +123,7 @@ func resourceAuditTrailWebhookUpdate(ctx context.Context, data *schema.ResourceD

func resourceAuditTrailWebhookDelete(ctx context.Context, data *schema.ResourceData, i interface{}) diag.Diagnostics {
var mutation struct {
AuditTrailWebhook *structs.AuditTrailWebhook `graphql:"auditTrailDeleteWebhook"`
AuditTrailWebhook *structs.AuditTrailWebhookRead `graphql:"auditTrailDeleteWebhook"`
}
if err := i.(*internal.Client).Mutate(ctx, "AuditTrailWebhookDelete", &mutation, nil); err != nil {
return diag.Errorf("could not delete audit trail webhook: %v", internal.FromSpaceliftError(err))
Expand Down
29 changes: 28 additions & 1 deletion spacelift/resource_audit_trail_webhook_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
. "github.com/spacelift-io/terraform-provider-spacelift/spacelift/internal/testhelpers"
)

var auditTrailWebhookSimple = `
const auditTrailWebhookSimple = `
resource "spacelift_audit_trail_webhook" "test" {
enabled = true
endpoint = "%s"
Expand All @@ -19,6 +19,19 @@ resource "spacelift_audit_trail_webhook" "test" {
}
`

const auditTrailWebhookCustomHeaders = `
resource "spacelift_audit_trail_webhook" "test" {
enabled = true
endpoint = "%s"
include_runs = true
secret = "secret"
custom_headers = {
"X-Some-Header" = "some-value"
"X-Some-Header-2" = "some-value-2"
}
}
`

func Test_resourceAuditTrailWebhook(t *testing.T) {
const resourceName = "spacelift_audit_trail_webhook.test"

Expand All @@ -39,6 +52,19 @@ func Test_resourceAuditTrailWebhook(t *testing.T) {
ImportState: true,
ImportStateVerify: true,
},
{
Config: fmt.Sprintf(auditTrailWebhookCustomHeaders, "https://example.com"),
Check: Resource(
resourceName,
Attribute("enabled", Equals("true")),
Attribute("endpoint", Equals("https://example.com")),
Attribute("include_runs", Equals("true")),
Attribute("secret", Equals("secret")),
Attribute("custom_headers.%", Equals("2")),
Attribute("custom_headers.X-Some-Header", Equals("some-value")),
Attribute("custom_headers.X-Some-Header-2", Equals("some-value-2")),
),
},
})
})

Expand All @@ -50,4 +76,5 @@ func Test_resourceAuditTrailWebhook(t *testing.T) {
},
})
})

}
19 changes: 19 additions & 0 deletions spacelift/type_conversions.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package spacelift
import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/shurcooL/graphql"
"github.com/spacelift-io/terraform-provider-spacelift/spacelift/internal/structs"
)

func toBool(input interface{}) graphql.Boolean {
Expand All @@ -25,6 +26,10 @@ func toString(input interface{}) graphql.String {
return graphql.String(input.(string))
}

func toMap(input interface{}) map[string]interface{} {
return input.(map[string]interface{})
}

func toOptionalInt(input interface{}) *graphql.Int {
v := graphql.Int(input.(int))
return graphql.NewInt(v)
Expand All @@ -45,3 +50,17 @@ func toOptionalStringList(input interface{}) *[]graphql.String {

return nil
}

func toOptionalStringMap(input interface{}) *structs.StringMap {
var customHeaders structs.StringMap
for k, v := range toMap(input) {
customHeaders.Entries = append(customHeaders.Entries, structs.KeyValuePair{
Key: k,
Value: v.(string),
})
}
if len(customHeaders.Entries) == 0 {
return nil
}
return &customHeaders
}
Loading