diff --git a/client/internal/meta/operation/monitorv2.graphql b/client/internal/meta/operation/monitorv2.graphql index f5e0276f..2bbd23ac 100644 --- a/client/internal/meta/operation/monitorv2.graphql +++ b/client/internal/meta/operation/monitorv2.graphql @@ -154,6 +154,10 @@ fragment MonitorV2SearchResult on MonitorV2SearchResult { fragment MonitorV2ActionRule on MonitorV2ActionRule { actionID levels + # @genqlient(flatten: true) + conditions { + ...MonitorV2ComparisonExpression + } sendEndNotifications sendRemindersInterval # @genqlient(flatten: true) @@ -175,6 +179,25 @@ fragment MonitorV2ActionDefinition on MonitorV2ActionDefinition { } } +fragment MonitorV2ComparisonExpression on MonitorV2ComparisonExpression { + # @genqlient(flatten: true) + compareTerms { + ...MonitorV2ComparisonTerm + } + # subExpressions/operator are not used by the UI so we won't support them here yet either +} + +fragment MonitorV2ComparisonTerm on MonitorV2ComparisonTerm { + # @genqlient(flatten: true) + comparison { + ...MonitorV2Comparison + } + # @genqlient(flatten: true) + column { + ...MonitorV2Column + } +} + # @genqlient(for: "MonitorV2Input.iconUrl", omitempty: true) # @genqlient(for: "MonitorV2Input.description", omitempty: true) # @genqlient(for: "MonitorV2Input.managedById", omitempty: true) diff --git a/client/meta/genqlient.generated.go b/client/meta/genqlient.generated.go index ef901552..1d10e585 100644 --- a/client/meta/genqlient.generated.go +++ b/client/meta/genqlient.generated.go @@ -4663,6 +4663,8 @@ type MonitorV2ActionRule struct { // Dispatch this action when the alarm matches any of the provided levels // AND'd with any of the optional conditions. Levels []MonitorV2AlarmLevel `json:"levels"` + // Conditions are additional comparisons to apply (AND'd with levels) to decide if an alert applies. + Conditions *MonitorV2ComparisonExpression `json:"conditions"` // Send notifications when the condition ends. // note: At this time, this only happens on the AlarmEnded event. SendEndNotifications *bool `json:"sendEndNotifications"` @@ -4679,6 +4681,9 @@ func (v *MonitorV2ActionRule) GetActionID() string { return v.ActionID } // GetLevels returns MonitorV2ActionRule.Levels, and is useful for accessing the field via an interface. func (v *MonitorV2ActionRule) GetLevels() []MonitorV2AlarmLevel { return v.Levels } +// GetConditions returns MonitorV2ActionRule.Conditions, and is useful for accessing the field via an interface. +func (v *MonitorV2ActionRule) GetConditions() *MonitorV2ComparisonExpression { return v.Conditions } + // GetSendEndNotifications returns MonitorV2ActionRule.SendEndNotifications, and is useful for accessing the field via an interface. func (v *MonitorV2ActionRule) GetSendEndNotifications() *bool { return v.SendEndNotifications } @@ -4845,6 +4850,16 @@ func (v *MonitorV2Comparison) GetCompareFn() MonitorV2ComparisonFunction { retur // GetCompareValue returns MonitorV2Comparison.CompareValue, and is useful for accessing the field via an interface. func (v *MonitorV2Comparison) GetCompareValue() PrimitiveValue { return v.CompareValue } +// MonitorV2ComparisonExpression includes the GraphQL fields of MonitorV2ComparisonExpression requested by the fragment MonitorV2ComparisonExpression. +type MonitorV2ComparisonExpression struct { + CompareTerms []MonitorV2ComparisonTerm `json:"compareTerms"` +} + +// GetCompareTerms returns MonitorV2ComparisonExpression.CompareTerms, and is useful for accessing the field via an interface. +func (v *MonitorV2ComparisonExpression) GetCompareTerms() []MonitorV2ComparisonTerm { + return v.CompareTerms +} + type MonitorV2ComparisonExpressionInput struct { CompareTerms []MonitorV2ComparisonTermInput `json:"compareTerms"` SubExpressions []MonitorV2ComparisonExpressionInput `json:"subExpressions"` @@ -4892,6 +4907,20 @@ func (v *MonitorV2ComparisonInput) GetCompareFn() MonitorV2ComparisonFunction { // GetCompareValue returns MonitorV2ComparisonInput.CompareValue, and is useful for accessing the field via an interface. func (v *MonitorV2ComparisonInput) GetCompareValue() PrimitiveValueInput { return v.CompareValue } +// MonitorV2ComparisonTerm includes the GraphQL fields of MonitorV2ComparisonTerm requested by the fragment MonitorV2ComparisonTerm. +type MonitorV2ComparisonTerm struct { + // Comparison describes the binary operator and the right-side value to compare. + Comparison MonitorV2Comparison `json:"comparison"` + // Column indicates the comparison left-side value comes from the column indicated here. + Column MonitorV2Column `json:"column"` +} + +// GetComparison returns MonitorV2ComparisonTerm.Comparison, and is useful for accessing the field via an interface. +func (v *MonitorV2ComparisonTerm) GetComparison() MonitorV2Comparison { return v.Comparison } + +// GetColumn returns MonitorV2ComparisonTerm.Column, and is useful for accessing the field via an interface. +func (v *MonitorV2ComparisonTerm) GetColumn() MonitorV2Column { return v.Column } + type MonitorV2ComparisonTermInput struct { Comparison MonitorV2ComparisonInput `json:"comparison"` Column MonitorV2ColumnInput `json:"column"` @@ -13339,6 +13368,9 @@ fragment MonitorV2Definition on MonitorV2Definition { fragment MonitorV2ActionRule on MonitorV2ActionRule { actionID levels + conditions { + ... MonitorV2ComparisonExpression + } sendEndNotifications sendRemindersInterval definition { @@ -13386,6 +13418,11 @@ fragment MonitorV2Scheduling on MonitorV2Scheduling { ... MonitorV2TransformSchedule } } +fragment MonitorV2ComparisonExpression on MonitorV2ComparisonExpression { + compareTerms { + ... MonitorV2ComparisonTerm + } +} fragment MonitorV2ActionDefinition on MonitorV2ActionDefinition { inline type @@ -13436,6 +13473,14 @@ fragment MonitorV2IntervalSchedule on MonitorV2IntervalSchedule { fragment MonitorV2TransformSchedule on MonitorV2TransformSchedule { freshnessGoal } +fragment MonitorV2ComparisonTerm on MonitorV2ComparisonTerm { + comparison { + ... MonitorV2Comparison + } + column { + ... MonitorV2Column + } +} fragment MonitorV2EmailAction on MonitorV2EmailAction { users addresses @@ -16812,6 +16857,9 @@ fragment MonitorV2Definition on MonitorV2Definition { fragment MonitorV2ActionRule on MonitorV2ActionRule { actionID levels + conditions { + ... MonitorV2ComparisonExpression + } sendEndNotifications sendRemindersInterval definition { @@ -16859,6 +16907,11 @@ fragment MonitorV2Scheduling on MonitorV2Scheduling { ... MonitorV2TransformSchedule } } +fragment MonitorV2ComparisonExpression on MonitorV2ComparisonExpression { + compareTerms { + ... MonitorV2ComparisonTerm + } +} fragment MonitorV2ActionDefinition on MonitorV2ActionDefinition { inline type @@ -16909,6 +16962,14 @@ fragment MonitorV2IntervalSchedule on MonitorV2IntervalSchedule { fragment MonitorV2TransformSchedule on MonitorV2TransformSchedule { freshnessGoal } +fragment MonitorV2ComparisonTerm on MonitorV2ComparisonTerm { + comparison { + ... MonitorV2Comparison + } + column { + ... MonitorV2Column + } +} fragment MonitorV2EmailAction on MonitorV2EmailAction { users addresses @@ -18512,6 +18573,9 @@ fragment MonitorV2Definition on MonitorV2Definition { fragment MonitorV2ActionRule on MonitorV2ActionRule { actionID levels + conditions { + ... MonitorV2ComparisonExpression + } sendEndNotifications sendRemindersInterval definition { @@ -18559,6 +18623,11 @@ fragment MonitorV2Scheduling on MonitorV2Scheduling { ... MonitorV2TransformSchedule } } +fragment MonitorV2ComparisonExpression on MonitorV2ComparisonExpression { + compareTerms { + ... MonitorV2ComparisonTerm + } +} fragment MonitorV2ActionDefinition on MonitorV2ActionDefinition { inline type @@ -18609,6 +18678,14 @@ fragment MonitorV2IntervalSchedule on MonitorV2IntervalSchedule { fragment MonitorV2TransformSchedule on MonitorV2TransformSchedule { freshnessGoal } +fragment MonitorV2ComparisonTerm on MonitorV2ComparisonTerm { + comparison { + ... MonitorV2Comparison + } + column { + ... MonitorV2Column + } +} fragment MonitorV2EmailAction on MonitorV2EmailAction { users addresses @@ -19183,6 +19260,9 @@ fragment MonitorV2Definition on MonitorV2Definition { fragment MonitorV2ActionRule on MonitorV2ActionRule { actionID levels + conditions { + ... MonitorV2ComparisonExpression + } sendEndNotifications sendRemindersInterval definition { @@ -19230,6 +19310,11 @@ fragment MonitorV2Scheduling on MonitorV2Scheduling { ... MonitorV2TransformSchedule } } +fragment MonitorV2ComparisonExpression on MonitorV2ComparisonExpression { + compareTerms { + ... MonitorV2ComparisonTerm + } +} fragment MonitorV2ActionDefinition on MonitorV2ActionDefinition { inline type @@ -19280,6 +19365,14 @@ fragment MonitorV2IntervalSchedule on MonitorV2IntervalSchedule { fragment MonitorV2TransformSchedule on MonitorV2TransformSchedule { freshnessGoal } +fragment MonitorV2ComparisonTerm on MonitorV2ComparisonTerm { + comparison { + ... MonitorV2Comparison + } + column { + ... MonitorV2Column + } +} fragment MonitorV2EmailAction on MonitorV2EmailAction { users addresses @@ -19408,6 +19501,9 @@ fragment MonitorV2Definition on MonitorV2Definition { fragment MonitorV2ActionRule on MonitorV2ActionRule { actionID levels + conditions { + ... MonitorV2ComparisonExpression + } sendEndNotifications sendRemindersInterval definition { @@ -19455,6 +19551,11 @@ fragment MonitorV2Scheduling on MonitorV2Scheduling { ... MonitorV2TransformSchedule } } +fragment MonitorV2ComparisonExpression on MonitorV2ComparisonExpression { + compareTerms { + ... MonitorV2ComparisonTerm + } +} fragment MonitorV2ActionDefinition on MonitorV2ActionDefinition { inline type @@ -19505,6 +19606,14 @@ fragment MonitorV2IntervalSchedule on MonitorV2IntervalSchedule { fragment MonitorV2TransformSchedule on MonitorV2TransformSchedule { freshnessGoal } +fragment MonitorV2ComparisonTerm on MonitorV2ComparisonTerm { + comparison { + ... MonitorV2Comparison + } + column { + ... MonitorV2Column + } +} fragment MonitorV2EmailAction on MonitorV2EmailAction { users addresses @@ -21101,6 +21210,9 @@ fragment MonitorV2Definition on MonitorV2Definition { fragment MonitorV2ActionRule on MonitorV2ActionRule { actionID levels + conditions { + ... MonitorV2ComparisonExpression + } sendEndNotifications sendRemindersInterval definition { @@ -21148,6 +21260,11 @@ fragment MonitorV2Scheduling on MonitorV2Scheduling { ... MonitorV2TransformSchedule } } +fragment MonitorV2ComparisonExpression on MonitorV2ComparisonExpression { + compareTerms { + ... MonitorV2ComparisonTerm + } +} fragment MonitorV2ActionDefinition on MonitorV2ActionDefinition { inline type @@ -21198,6 +21315,14 @@ fragment MonitorV2IntervalSchedule on MonitorV2IntervalSchedule { fragment MonitorV2TransformSchedule on MonitorV2TransformSchedule { freshnessGoal } +fragment MonitorV2ComparisonTerm on MonitorV2ComparisonTerm { + comparison { + ... MonitorV2Comparison + } + column { + ... MonitorV2Column + } +} fragment MonitorV2EmailAction on MonitorV2EmailAction { users addresses diff --git a/docs/data-sources/monitor_v2.md b/docs/data-sources/monitor_v2.md index 4292d4a7..044a5215 100644 --- a/docs/data-sources/monitor_v2.md +++ b/docs/data-sources/monitor_v2.md @@ -3,7 +3,6 @@ page_title: "observe_monitor_v2 Data Source - terraform-provider-observe" subcategory: "" description: |- - NOTE: This feature is still in development. It is not meant for customer use yet. Monitors provide a configurable way to alert when conditions about incoming data are matched. These alerts can optionally also be forwarded to notification receivers like email and webhooks using shared or single-monitor actions to configure the @@ -12,8 +11,6 @@ description: |- # observe_monitor_v2 (Data Source) -NOTE: This feature is still in development. It is not meant for customer use yet. - Monitors provide a configurable way to alert when conditions about incoming data are matched. These alerts can optionally also be forwarded to notification receivers like email and webhooks using shared or single-monitor actions to configure the diff --git a/docs/resources/monitor_v2.md b/docs/resources/monitor_v2.md index d705ff18..c2a59709 100644 --- a/docs/resources/monitor_v2.md +++ b/docs/resources/monitor_v2.md @@ -3,7 +3,6 @@ page_title: "observe_monitor_v2 Resource - terraform-provider-observe" subcategory: "" description: |- - NOTE: This feature is still in development. It is not meant for customer use yet. Monitors provide a configurable way to alert when conditions about incoming data are matched. These alerts can optionally also be forwarded to notification receivers like email and webhooks using shared or single-monitor actions to configure the @@ -11,8 +10,6 @@ description: |- --- # observe_monitor_v2 -NOTE: This feature is still in development. It is not meant for customer use yet. - Monitors provide a configurable way to alert when conditions about incoming data are matched. These alerts can optionally also be forwarded to notification receivers like email and webhooks using shared or single-monitor actions to configure the @@ -319,6 +316,7 @@ a stage preceding the last stage. The last stage is an output stage by default. Optional: - `action` (Block List, Max: 1) This value should be used for creating inline private actions. (see [below for nested schema](#nestedblock--actions--action)) +- `conditions` (Block List, Max: 1) Optional conditions that can be AND'd with levels to match the action. (see [below for nested schema](#nestedblock--actions--conditions)) - `levels` (List of String) The alarm level(s) at which this monitor should trigger this shared action. - `oid` (String) The OID of this shared action. This should be used for existing shared actions. - `send_end_notifications` (Boolean) If true, notifications will be sent if the monitor stops triggering. @@ -377,6 +375,69 @@ Required: + +### Nested Schema for `actions.conditions` + +Required: + +- `compare_terms` (Block List, Min: 1) (see [below for nested schema](#nestedblock--actions--conditions--compare_terms)) + + +### Nested Schema for `actions.conditions.compare_terms` + +Required: + +- `column` (Block List, Min: 1) The column (left-side) value to evaluate (see [below for nested schema](#nestedblock--actions--conditions--compare_terms--column)) +- `comparison` (Block List, Min: 1) The comparison operation and right-side value to evaluate (see [below for nested schema](#nestedblock--actions--conditions--compare_terms--comparison)) + + +### Nested Schema for `actions.conditions.compare_terms.column` + +Optional: + +- `column_path` (Block List, Max: 1) Specifies how the user wants to group by a specific column name or a JSON object column that has a path. (see [below for nested schema](#nestedblock--actions--conditions--compare_terms--column--column_path)) +- `link_column` (Block List, Max: 1) Identifies a link-type column created by connecting two different datasets' columns (primary sources & destination sources). (see [below for nested schema](#nestedblock--actions--conditions--compare_terms--column--link_column)) + + +### Nested Schema for `actions.conditions.compare_terms.column.column_path` + +Required: + +- `name` (String) The name of the column. + +Optional: + +- `path` (String) The path of the path, if the name refers to a column with a JSON object. + + + +### Nested Schema for `actions.conditions.compare_terms.column.link_column` + +Required: + +- `name` (String) The name of the link column. + + + + +### Nested Schema for `actions.conditions.compare_terms.comparison` + +Required: + +- `compare_fn` (String) the type of comparison (greater, less, equal, etc.) + +Optional: + +- `value_bool` (List of Boolean) list of size <=1 consisting of a boolean value. +- `value_duration` (List of Number) list of size <=1 consisting of a duration value. +- `value_float64` (List of Number) list of size <=1 consisting of a float value. +- `value_int64` (List of Number) list of size <=1 consisting of an integer value. +- `value_string` (List of String) list of size <=1 consisting of a string value. +- `value_timestamp` (List of String) list of size <=1 consisting of a timestamp value. + + + + ### Nested Schema for `groupings` diff --git a/observe/descriptions/monitorv2.yaml b/observe/descriptions/monitorv2.yaml index 3dbb2314..545c463c 100644 --- a/observe/descriptions/monitorv2.yaml +++ b/observe/descriptions/monitorv2.yaml @@ -1,6 +1,4 @@ description: | - NOTE: This feature is still in development. It is not meant for customer use yet. - Monitors provide a configurable way to alert when conditions about incoming data are matched. These alerts can optionally also be forwarded to notification receivers like email and webhooks using shared or single-monitor actions to configure the @@ -105,6 +103,16 @@ schema: This value should be used for creating inline private actions. levels: | The alarm level(s) at which this monitor should trigger this shared action. + conditions: + description: | + Optional conditions that can be AND'd with levels to match the action. + compare_terms: + description: | + The column and value expression to consider (implied AND) + comparison: + The comparison operation and right-side value to evaluate + column: + The column (left-side) value to evaluate send_end_notifications: | If true, notifications will be sent if the monitor stops triggering. send_reminders_interval: | diff --git a/observe/resource_monitor_v2.go b/observe/resource_monitor_v2.go index c1192010..a2f08f45 100644 --- a/observe/resource_monitor_v2.go +++ b/observe/resource_monitor_v2.go @@ -299,6 +299,43 @@ func resourceMonitorV2() *schema.Resource { }, Description: descriptions.Get("monitorv2", "schema", "actions", "levels"), }, + "conditions": { // MonitorV2ComparisonExpression + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Description: descriptions.Get("monitorv2", "schema", "actions", "conditions", "description"), + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + // note: subExpressions will be interesting to support. The UI currently does + // not support it, so we don't here either. + // When we add support for sub_expressions, it compare_terms will be ExactlyOneOf with that. + "compare_terms": { // [MonitorV2ComparisonTerm!] + Type: schema.TypeList, + Required: true, + MinItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "comparison": { // [MonitorV2Comparison!]! + Type: schema.TypeList, + Required: true, + MinItems: 1, + Elem: monitorV2ComparisonResource(), + Description: descriptions.Get("monitorv2", "schema", "actions", "conditions", "compare_terms", "comparison"), + }, + "column": { // [MonitorV2Column!]! + Type: schema.TypeList, + Required: true, + MinItems: 1, + Elem: monitorV2ColumnResource(), + Description: descriptions.Get("monitorv2", "schema", "actions", "conditions", "compare_terms", "column"), + }, + }, + }, + }, + // note: operator is an implied AND for now until the UI supports OR + }, + }, + }, "send_end_notifications": { // Boolean Type: schema.TypeBool, Optional: true, @@ -681,8 +718,33 @@ func monitorV2FlattenActionRule(ctx context.Context, client *observe.Client, gql if gqlActionRule.SendRemindersInterval != nil { rules["send_reminders_interval"] = gqlActionRule.SendRemindersInterval.String() } + if gqlActionRule.Conditions != nil && len(gqlActionRule.Conditions.CompareTerms) != 0 { + rules["conditions"] = []any{monitorV2FlattenComparisonExpression(gqlActionRule.Conditions)} + } return rules } +func monitorV2FlattenComparisonExpression(expr *gql.MonitorV2ComparisonExpression) map[string]interface{} { + // For now, we only support a single level expression with terms. Sub-expressions and operator are not yet + // supported because our UI doesn't yet support them. + if len(expr.CompareTerms) == 0 { + return nil + } + + terms := make([]interface{}, len(expr.CompareTerms)) + + for i, term := range expr.CompareTerms { + terms[i] = monitorV2FlattenComparisonTerm(term) + } + + return map[string]interface{}{"compare_terms": terms} +} + +func monitorV2FlattenComparisonTerm(term gql.MonitorV2ComparisonTerm) map[string]interface{} { + return map[string]interface{}{ + "comparison": []any{monitorV2FlattenComparison(term.Comparison)}, + "column": monitorV2FlattenColumn(term.Column), + } +} func monitorV2FlattenCountRule(gqlCount gql.MonitorV2CountRule) []interface{} { countRule := map[string]interface{}{} @@ -1281,6 +1343,8 @@ func newMonitorV2ActionAndRelation(path string, data *schema.ResourceData) (*gql var result gql.MonitorV2ActionAndRelationInput actionPath := fmt.Sprintf("%saction.0", path) + conditionsPath := fmt.Sprintf("%sconditions.0", path) + if _, ok := data.GetOk(actionPath); ok { if actInput, err := newMonitorV2ActionInput(fmt.Sprintf("%s.", actionPath), data); err != nil { return nil, err @@ -1314,5 +1378,40 @@ func newMonitorV2ActionAndRelation(path string, data *schema.ResourceData) (*gql result.SendRemindersInterval = interval } + if _, ok := data.GetOk(conditionsPath); ok { + if exprInput, err := newMonitorV2ComparisonExpressionInput(fmt.Sprintf("%s.", conditionsPath), data); err != nil { + return nil, err + } else { + result.Conditions = exprInput + } + } + return &result, nil } + +func newMonitorV2ComparisonExpressionInput(path string, data *schema.ResourceData) (input *gql.MonitorV2ComparisonExpressionInput, diags diag.Diagnostics) { + input = &gql.MonitorV2ComparisonExpressionInput{ + // AND is implied until the UI supports OR + Operator: gql.MonitorV2BooleanOperatorAnd, + } + + for i := range data.Get(fmt.Sprintf("%scompare_terms", path)).([]interface{}) { + condPath := fmt.Sprintf("%scompare_terms.%d.", path, i) + + term := gql.MonitorV2ComparisonTermInput{} + if c, d := newMonitorV2ComparisonInput(fmt.Sprintf("%scomparison.0.", condPath), data); d.HasError() { + return nil, d + } else { + term.Comparison = *c + } + if c, d := newMonitorV2ColumnInput(fmt.Sprintf("%scolumn.0.", condPath), data); d.HasError() { + return nil, d + } else { + term.Column = *c + } + + input.CompareTerms = append(input.CompareTerms, term) + } + + return +} diff --git a/observe/resource_monitor_v2_test.go b/observe/resource_monitor_v2_test.go index 19268139..f5df198b 100644 --- a/observe/resource_monitor_v2_test.go +++ b/observe/resource_monitor_v2_test.go @@ -282,6 +282,19 @@ func TestAccObserveMonitorV2MultipleActionsEmailViaOneShot(t *testing.T) { description = "an interesting description 2" } levels = ["informational"] + conditions { + compare_terms { + comparison { + compare_fn = "equal" + value_string = ["test"] + } + column { + column_path { + name = "description" + } + } + } + } send_end_notifications = false send_reminders_interval = "20m" } @@ -302,6 +315,9 @@ func TestAccObserveMonitorV2MultipleActionsEmailViaOneShot(t *testing.T) { resource.TestCheckResourceAttr("observe_monitor_v2.first", "actions.0.action.0.type", "email"), resource.TestCheckResourceAttr("observe_monitor_v2.first", "actions.0.send_reminders_interval", "10m0s"), resource.TestCheckResourceAttr("observe_monitor_v2.first", "actions.1.send_reminders_interval", "20m0s"), + resource.TestCheckResourceAttr("observe_monitor_v2.first", "actions.1.conditions.0.compare_terms.0.comparison.0.compare_fn", "equal"), + resource.TestCheckResourceAttr("observe_monitor_v2.first", "actions.1.conditions.0.compare_terms.0.comparison.0.value_string.0", "test"), + resource.TestCheckResourceAttr("observe_monitor_v2.first", "actions.1.conditions.0.compare_terms.0.column.0.column_path.0.name", "description"), ), }, // now test update @@ -374,6 +390,19 @@ func TestAccObserveMonitorV2MultipleActionsEmailViaOneShot(t *testing.T) { description = "an interesting description 2" } levels = ["informational"] + conditions { + compare_terms { + comparison { + compare_fn = "equal" + value_string = ["test"] + } + column { + column_path { + name = "description" + } + } + } + } send_end_notifications = false send_reminders_interval = "22m" }