Skip to content

Commit 6892869

Browse files
authored
Add new attributes to OnCall outgoing webhook (#1147)
* add new fields to ResourceOutgoingWebhook * bump amixr-api-go-client to v0.0.11 * go generate * tweak test * refactor IntegrationFilter create/update * use OnCallClient.Webhooks for datasource
1 parent d5aed61 commit 6892869

File tree

6 files changed

+147
-33
lines changed

6 files changed

+147
-33
lines changed

docs/resources/oncall_outgoing_webhook.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,16 @@ resource "grafana_oncall_outgoing_webhook" "test-acc-outgoing_webhook" {
3232

3333
- `authorization_header` (String, Sensitive) The auth data of the webhook. Used in Authorization header instead of user/password auth.
3434
- `data` (String) The data of the webhook.
35-
- `forward_whole_payload` (Boolean) Forwards whole payload of the alert to the webhook's url as POST data.
35+
- `forward_whole_payload` (Boolean) Toggle to send the entire webhook payload instead of using the values in the Data field.
36+
- `headers` (String) Headers to add to the outgoing webhook request.
37+
- `http_method` (String) The HTTP method used in the request made by the outgoing webhook. Defaults to `POST`.
38+
- `integration_filter` (List of String) Restricts the outgoing webhook to only trigger if the event came from a selected integration. If no integrations are selected the outgoing webhook will trigger for any integration.
39+
- `is_webhook_enabled` (Boolean) Controls whether the outgoing webhook will trigger or is ignored. The default is `true`.
3640
- `password` (String, Sensitive) The auth data of the webhook. Used for Basic authentication
3741
- `team_id` (String) The ID of the OnCall team. To get one, create a team in Grafana, and navigate to the OnCall plugin (to sync the team with OnCall). You can then get the ID using the `grafana_oncall_team` datasource.
38-
- `user` (String) The auth data of the webhook. Used for Basic authentication.
42+
- `trigger_template` (String) A template used to dynamically determine whether the webhook should execute based on the content of the payload.
43+
- `trigger_type` (String) The type of event that will cause this outgoing webhook to execute. The types of triggers are: `escalation`, `alert group created`, `acknowledge`, `resolve`, `silence`, `unsilence`, `unresolve`, `unacknowledge`. Defaults to `escalation`.
44+
- `user` (String) Username to use when making the outgoing webhook request.
3945

4046
### Read-Only
4147

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ toolchain go1.21.1
77
require (
88
github.com/Masterminds/semver/v3 v3.2.1
99
github.com/go-openapi/strfmt v0.21.7
10-
github.com/grafana/amixr-api-go-client v0.0.10
10+
github.com/grafana/amixr-api-go-client v0.0.11
1111
github.com/grafana/grafana-api-golang-client v0.26.0
1212
github.com/grafana/grafana-openapi-client-go v0.0.0-20231031181526-6f78415901a3
1313
github.com/grafana/machine-learning-go-client v0.5.0

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,8 @@ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
142142
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
143143
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
144144
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
145-
github.com/grafana/amixr-api-go-client v0.0.10 h1:L2Wc1aETiG7ORqmB+XSCBJdncHM/V0Nq2pJ6m/lV5xg=
146-
github.com/grafana/amixr-api-go-client v0.0.10/go.mod h1:N6x26XUrM5zGtK5zL5vNJnAn2JFMxLFPPLTw/6pDkFE=
145+
github.com/grafana/amixr-api-go-client v0.0.11 h1:jlE+5t0tRuCtjbpM81j70Dr2J4eCySuWyNGdfLMGdhE=
146+
github.com/grafana/amixr-api-go-client v0.0.11/go.mod h1:N6x26XUrM5zGtK5zL5vNJnAn2JFMxLFPPLTw/6pDkFE=
147147
github.com/grafana/grafana-api-golang-client v0.25.0 h1:jDxnR0U5xgIwKzE+IliZJvjMUUTQxGq+c1s+3M46flI=
148148
github.com/grafana/grafana-api-golang-client v0.25.0/go.mod h1:24W29gPe9yl0/3A9X624TPkAOR8DpHno490cPwnkv8E=
149149
github.com/grafana/grafana-api-golang-client v0.26.0 h1:Eu2YsfUezYngy8ifvmLybgluIcn/2IS9u1xkzuYstEM=

internal/resources/oncall/data_source_outgoing_webhook.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,23 +28,23 @@ func DataSourceOutgoingWebhook() *schema.Resource {
2828

2929
func DataSourceOutgoingWebhookRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
3030
client := m.(*common.Client).OnCallClient
31-
options := &onCallAPI.ListCustomActionOptions{}
31+
options := &onCallAPI.ListWebhookOptions{}
3232
name := d.Get("name").(string)
3333

3434
options.Name = name
3535

36-
outgoingWebhookResponse, _, err := client.CustomActions.ListCustomActions(options)
36+
outgoingWebhookResponse, _, err := client.Webhooks.ListWebhooks(options)
3737
if err != nil {
3838
return diag.FromErr(err)
3939
}
4040

41-
if len(outgoingWebhookResponse.CustomActions) == 0 {
41+
if len(outgoingWebhookResponse.Webhooks) == 0 {
4242
return diag.Errorf("couldn't find an outgoing webhook matching: %s", options.Name)
43-
} else if len(outgoingWebhookResponse.CustomActions) != 1 {
43+
} else if len(outgoingWebhookResponse.Webhooks) != 1 {
4444
return diag.Errorf("more than one outgoing webhook found matching: %s", options.Name)
4545
}
4646

47-
outgoingWebhook := outgoingWebhookResponse.CustomActions[0]
47+
outgoingWebhook := outgoingWebhookResponse.Webhooks[0]
4848

4949
d.SetId(outgoingWebhook.ID)
5050
d.Set("name", outgoingWebhook.Name)

internal/resources/oncall/resource_outgoing_webhook.go

Lines changed: 123 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ func ResourceOutgoingWebhook() *schema.Resource {
4848
"user": {
4949
Type: schema.TypeString,
5050
Optional: true,
51-
Description: "The auth data of the webhook. Used for Basic authentication.",
51+
Description: "Username to use when making the outgoing webhook request.",
5252
},
5353
"password": {
5454
Type: schema.TypeString,
@@ -65,7 +65,40 @@ func ResourceOutgoingWebhook() *schema.Resource {
6565
"forward_whole_payload": {
6666
Type: schema.TypeBool,
6767
Optional: true,
68-
Description: "Forwards whole payload of the alert to the webhook's url as POST data.",
68+
Description: "Toggle to send the entire webhook payload instead of using the values in the Data field.",
69+
},
70+
"trigger_type": {
71+
Type: schema.TypeString,
72+
Optional: true,
73+
Description: "The type of event that will cause this outgoing webhook to execute. The types of triggers are: `escalation`, `alert group created`, `acknowledge`, `resolve`, `silence`, `unsilence`, `unresolve`, `unacknowledge`.",
74+
Default: "escalation",
75+
},
76+
"http_method": {
77+
Type: schema.TypeString,
78+
Optional: true,
79+
Description: "The HTTP method used in the request made by the outgoing webhook.",
80+
Default: "POST",
81+
},
82+
"trigger_template": {
83+
Type: schema.TypeString,
84+
Optional: true,
85+
Description: "A template used to dynamically determine whether the webhook should execute based on the content of the payload.",
86+
},
87+
"headers": {
88+
Type: schema.TypeString,
89+
Optional: true,
90+
Description: "Headers to add to the outgoing webhook request.",
91+
},
92+
"integration_filter": {
93+
Type: schema.TypeList,
94+
Elem: &schema.Schema{Type: schema.TypeString},
95+
Optional: true,
96+
Description: "Restricts the outgoing webhook to only trigger if the event came from a selected integration. If no integrations are selected the outgoing webhook will trigger for any integration.",
97+
},
98+
"is_webhook_enabled": {
99+
Type: schema.TypeBool,
100+
Optional: true,
101+
Description: "Controls whether the outgoing webhook will trigger or is ignored. The default is `true`.",
69102
},
70103
},
71104
}
@@ -79,11 +112,11 @@ func ResourceOutgoingWebhookCreate(ctx context.Context, d *schema.ResourceData,
79112
url := d.Get("url").(string)
80113
forwardWholePayload := d.Get("forward_whole_payload").(bool)
81114

82-
createOptions := &onCallAPI.CreateCustomActionOptions{
83-
Name: name,
84-
TeamId: teamID,
85-
Url: url,
86-
ForwardWholePayload: forwardWholePayload,
115+
createOptions := &onCallAPI.CreateWebhookOptions{
116+
Name: name,
117+
Team: teamID,
118+
Url: url,
119+
ForwardAll: forwardWholePayload,
87120
}
88121

89122
data, dataOk := d.GetOk("data")
@@ -94,7 +127,7 @@ func ResourceOutgoingWebhookCreate(ctx context.Context, d *schema.ResourceData,
94127
user, userOk := d.GetOk("user")
95128
if userOk {
96129
u := user.(string)
97-
createOptions.User = &u
130+
createOptions.Username = &u
98131
}
99132

100133
password, passwordOk := d.GetOk("password")
@@ -108,7 +141,39 @@ func ResourceOutgoingWebhookCreate(ctx context.Context, d *schema.ResourceData,
108141
createOptions.AuthorizationHeader = &a
109142
}
110143

111-
outgoingWebhook, _, err := client.CustomActions.CreateCustomAction(createOptions)
144+
triggerType, triggerTypeOk := d.GetOk("trigger_type")
145+
if triggerTypeOk {
146+
createOptions.TriggerType = triggerType.(string)
147+
}
148+
149+
httpMethod, httpMethodOk := d.GetOk("http_method")
150+
if httpMethodOk {
151+
createOptions.HttpMethod = httpMethod.(string)
152+
}
153+
154+
triggerTemplate, triggerTemplateOk := d.GetOk("trigger_template")
155+
if triggerTemplateOk {
156+
t := triggerTemplate.(string)
157+
createOptions.TriggerTemplate = &t
158+
}
159+
160+
headers, headersOk := d.GetOk("headers")
161+
if headersOk {
162+
h := headers.(string)
163+
createOptions.Headers = &h
164+
}
165+
166+
integrationFilter, integrationFilterOk := d.GetOk("integration_filter")
167+
if integrationFilterOk {
168+
f := integrationFilter.([]interface{})
169+
integrationFilterSlice := make([]string, len(f))
170+
for i := range f {
171+
integrationFilterSlice[i] = f[i].(string)
172+
}
173+
createOptions.IntegrationFilter = &integrationFilterSlice
174+
}
175+
176+
outgoingWebhook, _, err := client.Webhooks.CreateWebhook(createOptions)
112177
if err != nil {
113178
return diag.FromErr(err)
114179
}
@@ -121,7 +186,7 @@ func ResourceOutgoingWebhookCreate(ctx context.Context, d *schema.ResourceData,
121186
func ResourceOutgoingWebhookRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
122187
client := m.(*common.Client).OnCallClient
123188

124-
outgoingWebhook, r, err := client.CustomActions.GetCustomAction(d.Id(), &onCallAPI.GetCustomActionOptions{})
189+
outgoingWebhook, r, err := client.Webhooks.GetWebhook(d.Id(), &onCallAPI.GetWebhookOptions{})
125190
if err != nil {
126191
if r != nil && r.StatusCode == http.StatusNotFound {
127192
log.Printf("[WARN] removing outgoingWebhook %s from state because it no longer exists", d.Get("name").(string))
@@ -132,11 +197,16 @@ func ResourceOutgoingWebhookRead(ctx context.Context, d *schema.ResourceData, m
132197
}
133198

134199
d.Set("name", outgoingWebhook.Name)
135-
d.Set("team_id", outgoingWebhook.TeamId)
200+
d.Set("team_id", outgoingWebhook.Team)
136201
d.Set("url", outgoingWebhook.Url)
137202
d.Set("data", outgoingWebhook.Data)
138-
d.Set("user", outgoingWebhook.User)
139-
d.Set("forward_whole_payload", outgoingWebhook.ForwardWholePayload)
203+
d.Set("user", outgoingWebhook.Username)
204+
d.Set("forward_whole_payload", outgoingWebhook.ForwardAll)
205+
d.Set("trigger_type", outgoingWebhook.TriggerType)
206+
d.Set("http_method", outgoingWebhook.HttpMethod)
207+
d.Set("trigger_template", outgoingWebhook.TriggerTemplate)
208+
d.Set("headers", outgoingWebhook.Headers)
209+
d.Set("integration_filter", outgoingWebhook.IntegrationFilter)
140210

141211
return nil
142212
}
@@ -149,11 +219,11 @@ func ResourceOutgoingWebhookUpdate(ctx context.Context, d *schema.ResourceData,
149219
url := d.Get("url").(string)
150220
forwardWholePayload := d.Get("forward_whole_payload").(bool)
151221

152-
updateOptions := &onCallAPI.UpdateCustomActionOptions{
153-
Name: name,
154-
TeamId: teamID,
155-
Url: url,
156-
ForwardWholePayload: forwardWholePayload,
222+
updateOptions := &onCallAPI.UpdateWebhookOptions{
223+
Name: name,
224+
Team: teamID,
225+
Url: url,
226+
ForwardAll: forwardWholePayload,
157227
}
158228

159229
data, dataOk := d.GetOk("data")
@@ -164,7 +234,7 @@ func ResourceOutgoingWebhookUpdate(ctx context.Context, d *schema.ResourceData,
164234
user, userOk := d.GetOk("user")
165235
if userOk {
166236
u := user.(string)
167-
updateOptions.User = &u
237+
updateOptions.Username = &u
168238
}
169239

170240
password, passwordOk := d.GetOk("password")
@@ -178,7 +248,39 @@ func ResourceOutgoingWebhookUpdate(ctx context.Context, d *schema.ResourceData,
178248
updateOptions.AuthorizationHeader = &a
179249
}
180250

181-
outgoingWebhook, _, err := client.CustomActions.UpdateCustomAction(d.Id(), updateOptions)
251+
triggerType, triggerTypeOk := d.GetOk("trigger_type")
252+
if triggerTypeOk {
253+
updateOptions.TriggerType = triggerType.(string)
254+
}
255+
256+
httpMethod, httpMethodOk := d.GetOk("http_method")
257+
if httpMethodOk {
258+
updateOptions.HttpMethod = httpMethod.(string)
259+
}
260+
261+
triggerTemplate, triggerTemplateOk := d.GetOk("trigger_template")
262+
if triggerTemplateOk {
263+
t := triggerTemplate.(string)
264+
updateOptions.TriggerTemplate = &t
265+
}
266+
267+
headers, headersOk := d.GetOk("headers")
268+
if headersOk {
269+
h := headers.(string)
270+
updateOptions.Headers = &h
271+
}
272+
273+
integrationFilter, integrationFilterOk := d.GetOk("integration_filter")
274+
if integrationFilterOk {
275+
f := integrationFilter.([]interface{})
276+
integrationFilterSlice := make([]string, len(f))
277+
for i := range f {
278+
integrationFilterSlice[i] = f[i].(string)
279+
}
280+
updateOptions.IntegrationFilter = &integrationFilterSlice
281+
}
282+
283+
outgoingWebhook, _, err := client.Webhooks.UpdateWebhook(d.Id(), updateOptions)
182284
if err != nil {
183285
return diag.FromErr(err)
184286
}
@@ -190,7 +292,7 @@ func ResourceOutgoingWebhookUpdate(ctx context.Context, d *schema.ResourceData,
190292
func ResourceOutgoingWebhookDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
191293
client := m.(*common.Client).OnCallClient
192294

193-
_, err := client.CustomActions.DeleteCustomAction(d.Id(), &onCallAPI.DeleteCustomActionOptions{})
295+
_, err := client.Webhooks.DeleteWebhook(d.Id(), &onCallAPI.DeleteWebhookOptions{})
194296
if err != nil {
195297
return diag.FromErr(err)
196298
}

internal/resources/oncall/resource_outgoing_webhook_test.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ func testAccCheckOnCallOutgoingWebhookResourceDestroy(s *terraform.State) error
3838
continue
3939
}
4040

41-
if _, _, err := client.CustomActions.GetCustomAction(r.Primary.ID, &onCallAPI.GetCustomActionOptions{}); err == nil {
41+
if _, _, err := client.Webhooks.GetWebhook(r.Primary.ID, &onCallAPI.GetWebhookOptions{}); err == nil {
4242
return fmt.Errorf("OutgoingWebhook still exists")
4343
}
4444
}
@@ -55,6 +55,12 @@ resource "grafana_oncall_outgoing_webhook" "test-acc-outgoing_webhook" {
5555
password = "test"
5656
authorization_header = "Authorization"
5757
forward_whole_payload = false
58+
trigger_type = "escalation"
59+
http_method = "POST"
60+
trigger_template = "123"
61+
headers = jsonencode({ "test" = "test123" })
62+
integration_filter = []
63+
is_webhook_enabled = true
5864
}
5965
`, webhookName)
6066
}
@@ -71,7 +77,7 @@ func testAccCheckOnCallOutgoingWebhookResourceExists(name string) resource.TestC
7177

7278
client := testutils.Provider.Meta().(*common.Client).OnCallClient
7379

74-
found, _, err := client.CustomActions.GetCustomAction(rs.Primary.ID, &onCallAPI.GetCustomActionOptions{})
80+
found, _, err := client.Webhooks.GetWebhook(rs.Primary.ID, &onCallAPI.GetWebhookOptions{})
7581
if err != nil {
7682
return err
7783
}

0 commit comments

Comments
 (0)