/
value_maps_are.go
116 lines (92 loc) · 3.93 KB
/
value_maps_are.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
package setvalidator
import (
"context"
"fmt"
"strings"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
)
// ValueMapsAre returns an validator which ensures that any configured
// Map values passes each Map validator.
//
// Null (unconfigured) and unknown (known after apply) values are skipped.
func ValueMapsAre(elementValidators ...validator.Map) validator.Set {
return valueMapsAreValidator{
elementValidators: elementValidators,
}
}
var _ validator.Set = valueMapsAreValidator{}
// valueMapsAreValidator validates that each set member validates against each of the value validators.
type valueMapsAreValidator struct {
elementValidators []validator.Map
}
// Description describes the validation in plain text formatting.
func (v valueMapsAreValidator) Description(ctx context.Context) string {
var descriptions []string
for _, elementValidator := range v.elementValidators {
descriptions = append(descriptions, elementValidator.Description(ctx))
}
return fmt.Sprintf("element value must satisfy all validations: %s", strings.Join(descriptions, " + "))
}
// MarkdownDescription describes the validation in Markdown formatting.
func (v valueMapsAreValidator) MarkdownDescription(ctx context.Context) string {
return v.Description(ctx)
}
// ValidateSet performs the validation.
func (v valueMapsAreValidator) ValidateSet(ctx context.Context, req validator.SetRequest, resp *validator.SetResponse) {
if req.ConfigValue.IsNull() || req.ConfigValue.IsUnknown() {
return
}
_, ok := req.ConfigValue.ElementType(ctx).(basetypes.MapTypable)
if !ok {
resp.Diagnostics.AddAttributeError(
req.Path,
"Invalid Validator for Element Type",
"While performing schema-based validation, an unexpected error occurred. "+
"The attribute declares a Map values validator, however its values do not implement types.MapType or the types.MapTypable interface for custom Map types. "+
"Use the appropriate values validator that matches the element type. "+
"This is always an issue with the provider and should be reported to the provider developers.\n\n"+
fmt.Sprintf("Path: %s\n", req.Path.String())+
fmt.Sprintf("Element Type: %T\n", req.ConfigValue.ElementType(ctx)),
)
return
}
for _, element := range req.ConfigValue.Elements() {
elementPath := req.Path.AtSetValue(element)
elementValuable, ok := element.(basetypes.MapValuable)
// The check above should have prevented this, but raise an error
// instead of a type assertion panic or skipping the element. Any issue
// here likely indicates something wrong in the framework itself.
if !ok {
resp.Diagnostics.AddAttributeError(
req.Path,
"Invalid Validator for Element Value",
"While performing schema-based validation, an unexpected error occurred. "+
"The attribute declares a Map values validator, however its values do not implement types.MapType or the types.MapTypable interface for custom Map types. "+
"This is likely an issue with terraform-plugin-framework and should be reported to the provider developers.\n\n"+
fmt.Sprintf("Path: %s\n", req.Path.String())+
fmt.Sprintf("Element Type: %T\n", req.ConfigValue.ElementType(ctx))+
fmt.Sprintf("Element Value Type: %T\n", element),
)
return
}
elementValue, diags := elementValuable.ToMapValue(ctx)
resp.Diagnostics.Append(diags...)
// Only return early if the new diagnostics indicate an issue since
// it likely will be the same for all elements.
if diags.HasError() {
return
}
elementReq := validator.MapRequest{
Path: elementPath,
PathExpression: elementPath.Expression(),
ConfigValue: elementValue,
Config: req.Config,
}
for _, elementValidator := range v.elementValidators {
elementResp := &validator.MapResponse{}
elementValidator.ValidateMap(ctx, elementReq, elementResp)
resp.Diagnostics.Append(elementResp.Diagnostics...)
}
}
}