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

fix(r/trigger): handle dynamic evaluation schedule #539

Merged
merged 1 commit into from
Sep 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions internal/models/notification_recipients.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,6 @@ type NotificationRecipientModel struct {
Details types.List `tfsdk:"notification_details"` // NotificationRecipientDetailsModel
}

type NotificationRecipientDetailsModel struct {
PDSeverity types.String `tfsdk:"pagerduty_severity"`
}

var NotificationRecipientAttrType = map[string]attr.Type{
"id": types.StringType,
"type": types.StringType,
Expand All @@ -25,6 +21,10 @@ var NotificationRecipientAttrType = map[string]attr.Type{
}},
}

type NotificationRecipientDetailsModel struct {
PDSeverity types.String `tfsdk:"pagerduty_severity"`
}

var NotificationRecipientDetailsAttrType = map[string]attr.Type{
"pagerduty_severity": types.StringType,
}
41 changes: 28 additions & 13 deletions internal/models/triggers.go
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
package models

import "github.com/hashicorp/terraform-plugin-framework/types"
import (
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/types"
)

type TriggerResourceModel struct {
ID types.String `tfsdk:"id"`
Name types.String `tfsdk:"name"`
Dataset types.String `tfsdk:"dataset"`
Description types.String `tfsdk:"description"`
Disabled types.Bool `tfsdk:"disabled"`
QueryID types.String `tfsdk:"query_id"`
QueryJson types.String `tfsdk:"query_json"`
AlertType types.String `tfsdk:"alert_type"`
Frequency types.Int64 `tfsdk:"frequency"`
Threshold []TriggerThresholdModel `tfsdk:"threshold"`
Recipients types.Set `tfsdk:"recipient"` // NotificationRecipientModel
EvaluationSchedule []TriggerEvaluationScheduleModel `tfsdk:"evaluation_schedule"`
ID types.String `tfsdk:"id"`
Name types.String `tfsdk:"name"`
Dataset types.String `tfsdk:"dataset"`
Description types.String `tfsdk:"description"`
Disabled types.Bool `tfsdk:"disabled"`
QueryID types.String `tfsdk:"query_id"`
QueryJson types.String `tfsdk:"query_json"`
AlertType types.String `tfsdk:"alert_type"`
Frequency types.Int64 `tfsdk:"frequency"`
Threshold types.List `tfsdk:"threshold"` // TriggerThresholdModel
Recipients types.Set `tfsdk:"recipient"` // NotificationRecipientModel
EvaluationSchedule types.List `tfsdk:"evaluation_schedule"` // TriggerEvaluationScheduleModel
}

type TriggerThresholdModel struct {
Expand All @@ -23,8 +26,20 @@ type TriggerThresholdModel struct {
ExceededLimit types.Int64 `tfsdk:"exceeded_limit"`
}

var TriggerThresholdAttrType = map[string]attr.Type{
"op": types.StringType,
"value": types.Float64Type,
"exceeded_limit": types.Int64Type,
}

type TriggerEvaluationScheduleModel struct {
DaysOfWeek []types.String `tfsdk:"days_of_week"`
StartTime types.String `tfsdk:"start_time"`
EndTime types.String `tfsdk:"end_time"`
}

var TriggerEvaluationScheduleAttrType = map[string]attr.Type{
"days_of_week": types.ListType{ElemType: types.StringType},
"start_time": types.StringType,
"end_time": types.StringType,
}
1 change: 0 additions & 1 deletion internal/provider/notification_recipients.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,6 @@ func notificationRecipientDetailsToList(ctx context.Context, details *client.Not
diags.Append(d...)
result, d = types.ListValueFrom(ctx, types.ObjectType{AttrTypes: models.NotificationRecipientDetailsAttrType}, []attr.Value{objVal})
diags.Append(d...)

} else {
result = types.ListNull(types.ObjectType{AttrTypes: models.NotificationRecipientDetailsAttrType})
}
Expand Down
166 changes: 114 additions & 52 deletions internal/provider/trigger_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
"github.com/hashicorp/terraform-plugin-framework-validators/int64validator"
"github.com/hashicorp/terraform-plugin-framework-validators/listvalidator"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
Expand Down Expand Up @@ -241,10 +243,13 @@ func (r *triggerResource) Create(ctx context.Context, req resource.CreateRequest
Description: plan.Description.ValueString(),
Disabled: plan.Disabled.ValueBool(),
AlertType: client.TriggerAlertType(plan.AlertType.ValueString()),
Threshold: expandTriggerThreshold(plan.Threshold),
Threshold: expandTriggerThreshold(ctx, plan.Threshold, &resp.Diagnostics),
Frequency: int(plan.Frequency.ValueInt64()),
Recipients: expandNotificationRecipients(ctx, plan.Recipients, &resp.Diagnostics),
EvaluationSchedule: expandTriggerEvaluationSchedule(plan.EvaluationSchedule),
EvaluationSchedule: expandTriggerEvaluationSchedule(ctx, plan.EvaluationSchedule, &resp.Diagnostics),
}
if resp.Diagnostics.HasError() {
return
}

specifiedByID := !plan.QueryID.IsNull()
Expand All @@ -265,7 +270,7 @@ func (r *triggerResource) Create(ctx context.Context, req resource.CreateRequest
newTrigger.Query = &q
}

if plan.EvaluationSchedule != nil {
if !plan.EvaluationSchedule.IsNull() {
newTrigger.EvaluationScheduleType = client.TriggerEvaluationScheduleWindow
}

Expand All @@ -281,9 +286,9 @@ func (r *triggerResource) Create(ctx context.Context, req resource.CreateRequest
state.Description = types.StringValue(trigger.Description)
state.Disabled = types.BoolValue(trigger.Disabled)
state.AlertType = types.StringValue(string(trigger.AlertType))
state.Threshold = flattenTriggerThreshold(trigger.Threshold)
state.Threshold = flattenTriggerThreshold(ctx, trigger.Threshold, &resp.Diagnostics)
state.Frequency = types.Int64Value(int64(trigger.Frequency))
state.EvaluationSchedule = flattenTriggerEvaluationSchedule(trigger)
state.EvaluationSchedule = flattenTriggerEvaluationSchedule(ctx, trigger.EvaluationSchedule, &resp.Diagnostics)
// we created them as authored so to avoid matching type-target or ID we can just use the same value
state.Recipients = config.Recipients

Expand Down Expand Up @@ -343,9 +348,9 @@ func (r *triggerResource) Read(ctx context.Context, req resource.ReadRequest, re
state.Description = types.StringValue(trigger.Description)
state.Disabled = types.BoolValue(trigger.Disabled)
state.AlertType = types.StringValue(string(trigger.AlertType))
state.Threshold = flattenTriggerThreshold(trigger.Threshold)
state.Threshold = flattenTriggerThreshold(ctx, trigger.Threshold, &resp.Diagnostics)
state.Frequency = types.Int64Value(int64(trigger.Frequency))
state.EvaluationSchedule = flattenTriggerEvaluationSchedule(trigger)
state.EvaluationSchedule = flattenTriggerEvaluationSchedule(ctx, trigger.EvaluationSchedule, &resp.Diagnostics)
state.Recipients = reconcileReadNotificationRecipientState(ctx, trigger.Recipients, state.Recipients, &resp.Diagnostics)

specifiedByID := !state.QueryID.IsNull()
Expand Down Expand Up @@ -385,9 +390,12 @@ func (r *triggerResource) Update(ctx context.Context, req resource.UpdateRequest
Disabled: plan.Disabled.ValueBool(),
AlertType: client.TriggerAlertType(plan.AlertType.ValueString()),
Frequency: int(plan.Frequency.ValueInt64()),
Threshold: expandTriggerThreshold(plan.Threshold),
Threshold: expandTriggerThreshold(ctx, plan.Threshold, &resp.Diagnostics),
Recipients: expandNotificationRecipients(ctx, plan.Recipients, &resp.Diagnostics),
EvaluationSchedule: expandTriggerEvaluationSchedule(plan.EvaluationSchedule),
EvaluationSchedule: expandTriggerEvaluationSchedule(ctx, plan.EvaluationSchedule, &resp.Diagnostics),
}
if resp.Diagnostics.HasError() {
return
}

specifiedByID := !plan.QueryID.IsNull()
Expand Down Expand Up @@ -432,8 +440,8 @@ func (r *triggerResource) Update(ctx context.Context, req resource.UpdateRequest
state.Disabled = types.BoolValue(trigger.Disabled)
state.AlertType = types.StringValue(string(trigger.AlertType))
state.Frequency = types.Int64Value(int64(trigger.Frequency))
state.Threshold = flattenTriggerThreshold(trigger.Threshold)
state.EvaluationSchedule = flattenTriggerEvaluationSchedule(trigger)
state.Threshold = flattenTriggerThreshold(ctx, trigger.Threshold, &resp.Diagnostics)
state.EvaluationSchedule = flattenTriggerEvaluationSchedule(ctx, trigger.EvaluationSchedule, &resp.Diagnostics)
// we created them as authored so to avoid matching type-target or ID we can just use the same value
state.Recipients = config.Recipients

Expand Down Expand Up @@ -497,11 +505,13 @@ func (r *triggerResource) ImportState(ctx context.Context, req resource.ImportSt
}

resp.Diagnostics.Append(resp.State.Set(ctx, &models.TriggerResourceModel{
ID: types.StringValue(id),
Dataset: types.StringValue(dataset),
QueryID: types.StringNull(),
QueryJson: types.StringUnknown(), // favor QueryJSON on import
Recipients: types.SetUnknown(types.ObjectType{AttrTypes: models.NotificationRecipientAttrType}),
ID: types.StringValue(id),
Dataset: types.StringValue(dataset),
QueryID: types.StringNull(),
QueryJson: types.StringUnknown(), // favor QueryJSON on import
Recipients: types.SetUnknown(types.ObjectType{AttrTypes: models.NotificationRecipientAttrType}),
Threshold: types.ListUnknown(types.ObjectType{AttrTypes: models.TriggerThresholdAttrType}),
EvaluationSchedule: types.ListUnknown(types.ObjectType{AttrTypes: models.TriggerEvaluationScheduleAttrType}),
})...)
}

Expand Down Expand Up @@ -600,8 +610,18 @@ func (r *triggerResource) ValidateConfig(ctx context.Context, req resource.Valid
}
}

func expandTriggerThreshold(t []models.TriggerThresholdModel) *client.TriggerThreshold {
if len(t) != 1 {
func expandTriggerThreshold(
ctx context.Context,
l types.List,
diags *diag.Diagnostics,
) *client.TriggerThreshold {
if l.IsNull() || l.IsUnknown() {
return nil
}

var t []models.TriggerThresholdModel
diags.Append(l.ElementsAs(ctx, &t, false)...)
if diags.HasError() {
return nil
}

Expand All @@ -612,48 +632,90 @@ func expandTriggerThreshold(t []models.TriggerThresholdModel) *client.TriggerThr
}
}

func flattenTriggerThreshold(t *client.TriggerThreshold) []models.TriggerThresholdModel {
return []models.TriggerThresholdModel{{
Op: types.StringValue(string(t.Op)),
Value: types.Float64Value(t.Value),
ExceededLimit: types.Int64Value(int64(t.ExceededLimit)),
}}
func flattenTriggerThreshold(
ctx context.Context,
t *client.TriggerThreshold,
diags *diag.Diagnostics,
) types.List {
if t == nil {
return types.ListNull(types.ObjectType{AttrTypes: models.TriggerThresholdAttrType})
}

thresholdObj, d := types.ObjectValue(models.TriggerThresholdAttrType, map[string]attr.Value{
"op": types.StringValue(string(t.Op)),
"value": types.Float64Value(t.Value),
"exceeded_limit": types.Int64Value(int64(t.ExceededLimit)),
})
diags.Append(d...)

result, d := types.ListValueFrom(
ctx,
types.ObjectType{AttrTypes: models.TriggerThresholdAttrType},
[]attr.Value{thresholdObj},
)
diags.Append(d...)

return result
}

func expandTriggerEvaluationSchedule(s []models.TriggerEvaluationScheduleModel) *client.TriggerEvaluationSchedule {
if s != nil {
days := make([]string, len(s[0].DaysOfWeek))
for i, d := range s[0].DaysOfWeek {
days[i] = d.ValueString()
}
func expandTriggerEvaluationSchedule(
ctx context.Context,
l types.List,
diags *diag.Diagnostics,
) *client.TriggerEvaluationSchedule {
if l.IsNull() || l.IsUnknown() {
return nil
}

return &client.TriggerEvaluationSchedule{
Window: client.TriggerEvaluationWindow{
StartTime: s[0].StartTime.ValueString(),
EndTime: s[0].EndTime.ValueString(),
DaysOfWeek: days,
},
}
var s []models.TriggerEvaluationScheduleModel
diags.Append(l.ElementsAs(ctx, &s, false)...)
if diags.HasError() {
return nil
}

days := make([]string, len(s[0].DaysOfWeek))
for i, d := range s[0].DaysOfWeek {
days[i] = d.ValueString()
}

return nil
return &client.TriggerEvaluationSchedule{
Window: client.TriggerEvaluationWindow{
StartTime: s[0].StartTime.ValueString(),
EndTime: s[0].EndTime.ValueString(),
DaysOfWeek: days,
},
}
}

func flattenTriggerEvaluationSchedule(t *client.Trigger) []models.TriggerEvaluationScheduleModel {
if t.EvaluationScheduleType == client.TriggerEvaluationScheduleWindow {
days := make([]basetypes.StringValue, len(t.EvaluationSchedule.Window.DaysOfWeek))
for i, d := range t.EvaluationSchedule.Window.DaysOfWeek {
days[i] = types.StringValue(d)
}
func flattenTriggerEvaluationSchedule(
ctx context.Context,
w *client.TriggerEvaluationSchedule,
diags *diag.Diagnostics,
) types.List {
if w == nil {
return types.ListNull(types.ObjectType{AttrTypes: models.TriggerEvaluationScheduleAttrType})
}

return []models.TriggerEvaluationScheduleModel{
{
StartTime: types.StringValue(t.EvaluationSchedule.Window.StartTime),
EndTime: types.StringValue(t.EvaluationSchedule.Window.EndTime),
DaysOfWeek: days,
},
}
days := make([]basetypes.StringValue, len(w.Window.DaysOfWeek))
for i, d := range w.Window.DaysOfWeek {
days[i] = types.StringValue(d)
}
daysObj, d := types.ListValueFrom(ctx, types.StringType, days)
diags.Append(d...)

scheduleObj, d := types.ObjectValue(models.TriggerEvaluationScheduleAttrType, map[string]attr.Value{
"days_of_week": daysObj,
"start_time": types.StringValue(w.Window.StartTime),
"end_time": types.StringValue(w.Window.EndTime),
})
diags.Append(d...)

result, d := types.ListValueFrom(
ctx,
types.ObjectType{AttrTypes: models.TriggerEvaluationScheduleAttrType},
[]attr.Value{scheduleObj},
)
diags.Append(d...)

return nil
return result
}
Loading