@@ -18,7 +18,7 @@ import (
1818var alertRuleSpecType = types.ObjectType {
1919 AttrTypes : map [string ]attr.Type {
2020 "title" : types .StringType ,
21- "expressions" : types .DynamicType ,
21+ "expressions" : types.MapType { ElemType : types . StringType } ,
2222 "paused" : types .BoolType ,
2323 "trigger" : ruleTriggerType ,
2424 "no_data_state" : types .StringType ,
@@ -35,7 +35,7 @@ var alertRuleSpecType = types.ObjectType{
3535
3636type AlertRuleSpecModel struct {
3737 Title types.String `tfsdk:"title"`
38- Expressions types.Dynamic `tfsdk:"expressions"`
38+ Expressions types.Map `tfsdk:"expressions"`
3939 Paused types.Bool `tfsdk:"paused"`
4040 Trigger types.Object `tfsdk:"trigger"`
4141 NoDataState types.String `tfsdk:"no_data_state"`
@@ -98,12 +98,10 @@ Manages Grafana Alert Rules.
9898 Required : true ,
9999 Description : "The title of the alert rule." ,
100100 },
101- "expressions" : schema.DynamicAttribute {
101+ "expressions" : schema.MapAttribute {
102102 Required : true ,
103- Description : "A sequence of stages that describe the contents of the rule." ,
104- Validators : []validator.Dynamic {
105- ExpressionsDynamicValidator {},
106- },
103+ ElementType : types .StringType ,
104+ Description : "A sequence of stages that describe the contents of the rule. Each value is a JSON string representing an expression object." ,
107105 },
108106 "paused" : schema.BoolAttribute {
109107 Optional : true ,
@@ -282,18 +280,28 @@ func parseAlertRuleExpressions(ctx context.Context, data *AlertRuleSpecModel, sp
282280 return nil
283281 }
284282
285- expressionsMap , diags := ParseExpressionsFromDynamic (ctx , data .Expressions )
286- if diags .HasError () {
287- return diags
288- }
289-
283+ // Parse map[string]string where each string is a JSON-encoded expression
290284 spec .Expressions = make (map [string ]v0alpha1.AlertRuleExpression )
291- for ref , obj := range expressionsMap {
292- exprData , diags := parseAlertRuleExpressionModel (ctx , obj )
293- if diags .HasError () {
294- return diags
285+ for ref , val := range data .Expressions .Elements () {
286+ strVal , ok := val .(types.String )
287+ if ! ok || strVal .IsNull () || strVal .IsUnknown () {
288+ continue
289+ }
290+
291+ // Parse JSON string to expression data
292+ var exprJSON map [string ]interface {}
293+ if err := json .Unmarshal ([]byte (strVal .ValueString ()), & exprJSON ); err != nil {
294+ return diag.Diagnostics {
295+ diag .NewErrorDiagnostic ("Failed to parse expression JSON" , err .Error ()),
296+ }
295297 }
296- spec .Expressions [ref ] = exprData
298+
299+ // Convert JSON to expression object
300+ exprObj , d := convertJSONToAlertRuleExpression (ctx , exprJSON )
301+ if d .HasError () {
302+ return d
303+ }
304+ spec .Expressions [ref ] = exprObj
297305 }
298306 return nil
299307}
@@ -424,25 +432,26 @@ func saveAlertRuleSpec(ctx context.Context, src *v0alpha1.AlertRule, dst *Resour
424432 values ["panel_ref" ] = types .DynamicNull ()
425433 }
426434 if len (src .Spec .Expressions ) > 0 {
427- // Convert expressions to a map of objects for the dynamic type
435+ // Convert expressions to map[string]string where each string is JSON
428436 expressionsMap := make (map [string ]attr.Value )
429437 for ref , expr := range src .Spec .Expressions {
430- // Use the conversion function to parse JSON strings back to HCL objects
431- exprObj , d := ConvertAPIExpressionToTerraform (ctx , expr , ruleExpressionType .AttrTypes )
432- if d .HasError () {
433- return d
438+ // Marshal expression to JSON
439+ jsonBytes , err := json .Marshal (expr )
440+ if err != nil {
441+ return diag.Diagnostics {
442+ diag .NewErrorDiagnostic ("Failed to marshal expression to JSON" , err .Error ()),
443+ }
434444 }
435- expressionsMap [ref ] = exprObj
445+ expressionsMap [ref ] = types . StringValue ( string ( jsonBytes ))
436446 }
437- // Use shared conversion function
438- dynamicValue , d := ConvertExpressionsMapToDynamic (ctx , expressionsMap )
447+ mapValue , d := types .MapValue (types .StringType , expressionsMap )
439448 if d .HasError () {
440449 return d
441450 }
442- values ["expressions" ] = dynamicValue
451+ values ["expressions" ] = mapValue
443452 } else {
444453 // Set to null if no expressions
445- values ["expressions" ] = types .DynamicNull ( )
454+ values ["expressions" ] = types .MapNull ( types . StringType )
446455 }
447456
448457 spec , d := types .ObjectValue (alertRuleSpecType .AttrTypes , values )
@@ -559,65 +568,40 @@ func parsePanelRef(ctx context.Context, src types.Dynamic) (v0alpha1.AlertRuleV0
559568 }, diag.Diagnostics {}
560569}
561570
562- func parseAlertRuleRelativeTimeRange (ctx context.Context , src types.Object ) (v0alpha1.AlertRuleRelativeTimeRange , diag.Diagnostics ) {
563- var data RelativeTimeRangeModel
564- if diag := src .As (ctx , & data , basetypes.ObjectAsOptions {
565- UnhandledNullAsEmpty : true ,
566- UnhandledUnknownAsEmpty : true ,
567- }); diag .HasError () {
568- return v0alpha1.AlertRuleRelativeTimeRange {}, diag
569- }
570-
571- return v0alpha1.AlertRuleRelativeTimeRange {
572- From : v0alpha1 .AlertRulePromDurationWMillis (data .From .ValueString ()),
573- To : v0alpha1 .AlertRulePromDurationWMillis (data .To .ValueString ()),
574- }, diag.Diagnostics {}
575- }
576-
577- func parseAlertRuleExpressionModel (ctx context.Context , src types.Object ) (v0alpha1.AlertRuleExpression , diag.Diagnostics ) {
578- var srcExpr RuleExpressionModel
579- if diag := src .As (ctx , & srcExpr , basetypes.ObjectAsOptions {
580- UnhandledNullAsEmpty : true ,
581- UnhandledUnknownAsEmpty : true ,
582- }); diag .HasError () {
583- return v0alpha1.AlertRuleExpression {}, diag
584- }
585-
571+ // convertJSONToAlertRuleExpression converts a JSON map to an AlertRuleExpression
572+ func convertJSONToAlertRuleExpression (ctx context.Context , exprJSON map [string ]interface {}) (v0alpha1.AlertRuleExpression , diag.Diagnostics ) {
586573 dstExpr := v0alpha1.AlertRuleExpression {}
587574
588- // Model should be a map/object for the API, not a JSON string
589- // Parse the JSON string back to a map
590- if ! srcExpr .Model .IsNull () && ! srcExpr .Model .IsUnknown () {
591- modelStr := srcExpr .Model .ValueString ()
592- var modelMap map [string ]interface {}
593- if err := json .Unmarshal ([]byte (modelStr ), & modelMap ); err != nil {
594- return v0alpha1.AlertRuleExpression {}, diag.Diagnostics {
595- diag .NewErrorDiagnostic ("Failed to parse model JSON" , err .Error ()),
596- }
597- }
598- dstExpr .Model = modelMap
575+ // Extract model
576+ if model , ok := exprJSON ["model" ].(map [string ]interface {}); ok {
577+ dstExpr .Model = model
599578 }
600579
601- // Handle relative time range if present
602- if ! srcExpr .RelativeTimeRange .IsNull () && ! srcExpr .RelativeTimeRange .IsUnknown () {
603- relativeTimeRange , diags := parseAlertRuleRelativeTimeRange (ctx , srcExpr .RelativeTimeRange )
604- if diags .HasError () {
605- return v0alpha1.AlertRuleExpression {}, diags
606- }
607- dstExpr .RelativeTimeRange = & v0alpha1.AlertRuleRelativeTimeRange {
608- From : relativeTimeRange .From ,
609- To : relativeTimeRange .To ,
610- }
580+ // Extract query_type
581+ if queryType , ok := exprJSON ["query_type" ].(string ); ok && queryType != "" {
582+ dstExpr .QueryType = util .Ptr (queryType )
611583 }
612584
613- if srcExpr .QueryType .ValueString () != "" {
614- dstExpr .QueryType = util .Ptr (srcExpr .QueryType .ValueString ())
585+ // Extract datasource_uid
586+ if datasourceUID , ok := exprJSON ["datasource_uid" ].(string ); ok && datasourceUID != "" {
587+ dstExpr .DatasourceUID = util .Ptr (v0alpha1 .AlertRuleDatasourceUID (datasourceUID ))
615588 }
616- if srcExpr .DatasourceUID .ValueString () != "" {
617- dstExpr .DatasourceUID = util .Ptr (v0alpha1 .AlertRuleDatasourceUID (srcExpr .DatasourceUID .ValueString ()))
589+
590+ // Extract source
591+ if source , ok := exprJSON ["source" ].(bool ); ok {
592+ dstExpr .Source = util .Ptr (source )
618593 }
619- if ! srcExpr .Source .IsNull () && ! srcExpr .Source .IsUnknown () {
620- dstExpr .Source = util .Ptr (srcExpr .Source .ValueBool ())
594+
595+ // Extract relative_time_range
596+ if relTimeRange , ok := exprJSON ["relative_time_range" ].(map [string ]interface {}); ok {
597+ from , _ := relTimeRange ["from" ].(string )
598+ to , _ := relTimeRange ["to" ].(string )
599+ if from != "" || to != "" {
600+ dstExpr .RelativeTimeRange = & v0alpha1.AlertRuleRelativeTimeRange {
601+ From : v0alpha1 .AlertRulePromDurationWMillis (from ),
602+ To : v0alpha1 .AlertRulePromDurationWMillis (to ),
603+ }
604+ }
621605 }
622606
623607 return dstExpr , diag.Diagnostics {}
0 commit comments