|
| 1 | +// Copyright Mondoo, Inc. 2024, 2026 |
| 2 | +// SPDX-License-Identifier: BUSL-1.1 |
| 3 | + |
| 4 | +package provider |
| 5 | + |
| 6 | +import ( |
| 7 | + "strings" |
| 8 | + |
| 9 | + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" |
| 10 | + "github.com/hashicorp/terraform-plugin-framework/resource/schema" |
| 11 | + "github.com/hashicorp/terraform-plugin-framework/schema/validator" |
| 12 | + "github.com/hashicorp/terraform-plugin-framework/types" |
| 13 | + mondoov1 "go.mondoo.com/mondoo-go" |
| 14 | +) |
| 15 | + |
| 16 | +// AssetRoutingConditionModel is the Terraform model for a single routing condition. |
| 17 | +// Shared between mondoo_asset_routing_table and mondoo_asset_routing_rule. |
| 18 | +type AssetRoutingConditionModel struct { |
| 19 | + Field types.String `tfsdk:"field"` |
| 20 | + Operator types.String `tfsdk:"operator"` |
| 21 | + Values types.List `tfsdk:"values"` |
| 22 | + Key types.String `tfsdk:"key"` |
| 23 | +} |
| 24 | + |
| 25 | +// assetRoutingConditionSchemaBlock returns a ListNestedBlock for routing conditions, |
| 26 | +// reusable by both the table and rule resources. |
| 27 | +func assetRoutingConditionSchemaBlock() schema.ListNestedBlock { |
| 28 | + return schema.ListNestedBlock{ |
| 29 | + MarkdownDescription: "Conditions that must all match for this rule to apply (AND logic). If empty, the rule matches all assets (catch-all).", |
| 30 | + NestedObject: schema.NestedBlockObject{ |
| 31 | + Attributes: map[string]schema.Attribute{ |
| 32 | + "field": schema.StringAttribute{ |
| 33 | + MarkdownDescription: "The field to match on. Valid values: `HOSTNAME`, `PLATFORM`, `LABEL`.", |
| 34 | + Required: true, |
| 35 | + Validators: []validator.String{ |
| 36 | + stringvalidator.OneOf("HOSTNAME", "PLATFORM", "LABEL"), |
| 37 | + }, |
| 38 | + }, |
| 39 | + "operator": schema.StringAttribute{ |
| 40 | + MarkdownDescription: "The comparison operator. Valid values: `EQUAL`, `NOT_EQUAL`, `CONTAINS`, `MATCHES`.", |
| 41 | + Required: true, |
| 42 | + Validators: []validator.String{ |
| 43 | + stringvalidator.OneOf("EQUAL", "NOT_EQUAL", "CONTAINS", "MATCHES"), |
| 44 | + }, |
| 45 | + }, |
| 46 | + "values": schema.ListAttribute{ |
| 47 | + MarkdownDescription: "List of values to match against. A condition matches if the field matches any of the listed values (OR logic).", |
| 48 | + Required: true, |
| 49 | + ElementType: types.StringType, |
| 50 | + }, |
| 51 | + "key": schema.StringAttribute{ |
| 52 | + MarkdownDescription: "The label key to match on. Required when `field` is `LABEL`.", |
| 53 | + Optional: true, |
| 54 | + }, |
| 55 | + }, |
| 56 | + }, |
| 57 | + } |
| 58 | +} |
| 59 | + |
| 60 | +// conditionsFromModel converts Terraform condition models to GraphQL input types. |
| 61 | +func conditionsFromModel(conditions []AssetRoutingConditionModel) []AssetRoutingConditionInput { |
| 62 | + result := make([]AssetRoutingConditionInput, len(conditions)) |
| 63 | + for i, c := range conditions { |
| 64 | + values := make([]mondoov1.String, 0) |
| 65 | + if !c.Values.IsNull() && !c.Values.IsUnknown() { |
| 66 | + for _, v := range c.Values.Elements() { |
| 67 | + if sv, ok := v.(types.String); ok { |
| 68 | + values = append(values, mondoov1.String(sv.ValueString())) |
| 69 | + } |
| 70 | + } |
| 71 | + } |
| 72 | + |
| 73 | + input := AssetRoutingConditionInput{ |
| 74 | + Field: AssetRoutingConditionField(c.Field.ValueString()), |
| 75 | + Operator: AssetRoutingConditionOperator(c.Operator.ValueString()), |
| 76 | + Values: values, |
| 77 | + } |
| 78 | + if !c.Key.IsNull() && !c.Key.IsUnknown() && c.Key.ValueString() != "" { |
| 79 | + key := mondoov1.String(c.Key.ValueString()) |
| 80 | + input.Key = &key |
| 81 | + } |
| 82 | + result[i] = input |
| 83 | + } |
| 84 | + return result |
| 85 | +} |
| 86 | + |
| 87 | +func isNotFoundError(err error) bool { |
| 88 | + return err != nil && strings.Contains(err.Error(), "code = NotFound") |
| 89 | +} |
| 90 | + |
| 91 | +// conditionsToModel converts GraphQL condition payloads to Terraform models. |
| 92 | +func conditionsToModel(conditions []AssetRoutingConditionPayload) []AssetRoutingConditionModel { |
| 93 | + result := make([]AssetRoutingConditionModel, len(conditions)) |
| 94 | + for i, c := range conditions { |
| 95 | + values := make([]string, len(c.Values)) |
| 96 | + copy(values, c.Values) |
| 97 | + |
| 98 | + model := AssetRoutingConditionModel{ |
| 99 | + Field: types.StringValue(c.Field), |
| 100 | + Operator: types.StringValue(c.Operator), |
| 101 | + Values: ConvertListValue(values), |
| 102 | + } |
| 103 | + if c.Key != "" { |
| 104 | + model.Key = types.StringValue(c.Key) |
| 105 | + } else { |
| 106 | + model.Key = types.StringNull() |
| 107 | + } |
| 108 | + result[i] = model |
| 109 | + } |
| 110 | + return result |
| 111 | +} |
0 commit comments