Skip to content

Commit 199d5fa

Browse files
committed
add iso8601 validation
1 parent 78afc77 commit 199d5fa

File tree

3 files changed

+105
-0
lines changed

3 files changed

+105
-0
lines changed

internal/kibana/maintenance_window/schema.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ func (r *MaintenanceWindowResource) Schema(_ context.Context, _ resource.SchemaR
5757
"start": schema.StringAttribute{
5858
Description: "The start date and time of the schedule, provided in ISO 8601 format and set to the UTC timezone. For example: `2025-03-12T12:00:00.000Z`.",
5959
Required: true,
60+
Validators: []validator.String{
61+
validation_utils.StringIsISO8601{},
62+
},
6063
},
6164
"duration": schema.StringAttribute{
6265
Description: "The duration of the schedule. It allows values in `<integer><unit>` format. `<unit>` is one of `d`, `h`, `m`, or `s` for hours, minutes, seconds. For example: `1d`, `5h`, `30m`, `5000s`.",
@@ -77,6 +80,9 @@ func (r *MaintenanceWindowResource) Schema(_ context.Context, _ resource.SchemaR
7780
"end": schema.StringAttribute{
7881
Description: "The end date and time of the schedule, provided in ISO 8601 format and set to the UTC timezone. For example: `2025-03-12T12:00:00.000Z`.",
7982
Optional: true,
83+
Validators: []validator.String{
84+
validation_utils.StringIsISO8601{},
85+
},
8086
},
8187
"every": schema.StringAttribute{
8288
Description: "The duration of the schedule. It allows values in `<integer><unit>` format. `<unit>` is one of `d`, `h`, `m`, or `s` for hours, minutes, seconds. For example: `1d`, `5h`, `30m`, `5000s`.",

internal/kibana/utils/validators.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,3 +108,33 @@ func StringIsAlertingDurationSDKV2() schema.SchemaValidateFunc {
108108
r := regexp.MustCompile(alertingDurationPattern)
109109
return validation.StringMatch(r, "string is not a valid Alerting duration in seconds (s), minutes (m), hours (h), or days (d)")
110110
}
111+
112+
func StringMatchesISO8601Regex(s string) (matched bool, err error) {
113+
pattern := `(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))`
114+
return regexp.MatchString(pattern, s)
115+
}
116+
117+
type StringIsISO8601 struct{}
118+
119+
func (s StringIsISO8601) Description(_ context.Context) string {
120+
return "a valid ISO8601 date and time formatted string"
121+
}
122+
123+
func (s StringIsISO8601) MarkdownDescription(ctx context.Context) string {
124+
return s.Description(ctx)
125+
}
126+
127+
func (s StringIsISO8601) ValidateString(_ context.Context, req validator.StringRequest, resp *validator.StringResponse) {
128+
if req.ConfigValue.IsNull() || req.ConfigValue.IsUnknown() {
129+
return
130+
}
131+
132+
if matched, err := StringMatchesISO8601Regex(req.ConfigValue.ValueString()); err != nil || !matched {
133+
resp.Diagnostics.AddAttributeError(
134+
req.Path,
135+
"expected value to be a valid ISO8601 string",
136+
fmt.Sprintf("This value must be a valid ISO8601 date and time formatted string %s", err),
137+
)
138+
return
139+
}
140+
}

internal/kibana/utils/validators_test.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,75 @@ func TestStringMatchesAlertingDuration(t *testing.T) {
4040
}
4141
}
4242

43+
func TestStringMatchesISO8601(t *testing.T) {
44+
t.Parallel()
45+
46+
tests := []struct {
47+
name string
48+
date string
49+
matched bool
50+
}{
51+
{
52+
name: "valid complete date 1",
53+
date: "1994-11-05T13:15:30Z",
54+
matched: true,
55+
},
56+
{
57+
name: "valid complete date 2",
58+
date: "1997-07-04T19:20+01:00",
59+
matched: true,
60+
},
61+
{
62+
name: "valid complete date 3",
63+
date: "1994-11-05T08:15:30-05:00",
64+
matched: true,
65+
},
66+
{
67+
name: "valid complete date plus hours, minutes and seconds",
68+
date: "1997-07-16T19:20:30+01:00",
69+
matched: true,
70+
},
71+
{
72+
name: "valid complete date plus hours, minutes, seconds and a decimal fraction of a second",
73+
date: "1997-07-16T19:20:30.45+01:00",
74+
matched: true,
75+
}, {
76+
name: "invalid year",
77+
date: "1997",
78+
matched: false,
79+
},
80+
{
81+
name: "invalid year and month",
82+
date: "1997-07",
83+
matched: false,
84+
},
85+
{
86+
name: "invalid complete date",
87+
date: "1997-07-04",
88+
matched: false,
89+
},
90+
{
91+
name: "invalid hours and minutes",
92+
date: "1997-40-04T30:220+01:00",
93+
matched: false,
94+
},
95+
{
96+
name: "invalid seconds",
97+
date: "1997-07-16T19:20:80+01:00",
98+
matched: false,
99+
},
100+
}
101+
102+
for _, tt := range tests {
103+
t.Run(tt.name, func(t *testing.T) {
104+
matched, _ := StringMatchesISO8601Regex(tt.date)
105+
if !reflect.DeepEqual(matched, tt.matched) {
106+
t.Errorf("StringMatchesISO8601Regex() failed match = %v, want %v", matched, tt.matched)
107+
}
108+
})
109+
}
110+
}
111+
43112
func TestStringMatchesOnWeekDay(t *testing.T) {
44113
t.Parallel()
45114

0 commit comments

Comments
 (0)