Skip to content

Commit 3333bf9

Browse files
committed
enabling IsURL validator to operate on lists or sets of strings as well as just strings
1 parent 5564ae5 commit 3333bf9

File tree

1 file changed

+57
-16
lines changed

1 file changed

+57
-16
lines changed

validation/validators.go

Lines changed: 57 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"net/url"
99
"os"
1010
"regexp"
11+
"strconv"
1112
"strings"
1213
"time"
1314

@@ -362,29 +363,69 @@ func Compare(op CompareOp, target interface{}, meta ...interface{}) Generic {
362363
}
363364

364365
// TestIsURL asserts that the provided value can be parsed by url.Parse()
365-
func TestIsURL() TestFunc {
366+
func TestIsURL(requireScheme string, requirePort int) TestFunc {
366367
return func(ctx context.Context, req GenericRequest, resp *GenericResponse) {
367-
if _, err := url.Parse(conv.AttributeValueToString(req.ConfigValue)); err != nil {
368-
resp.Diagnostics.AddAttributeError(
369-
req.Path,
370-
"Value is not parseable as URL",
371-
fmt.Sprintf("Value is not parseable as url.URL: %v", err),
372-
)
368+
requireScheme := requireScheme
369+
requirePort := strconv.Itoa(requirePort)
370+
371+
validateURL := func(v string) {
372+
if purl, err := url.Parse(v); err != nil {
373+
resp.Diagnostics.AddAttributeError(
374+
req.Path,
375+
"Value is not parseable as URL",
376+
fmt.Sprintf("Value is not parseable as url.URL: %v", err),
377+
)
378+
} else {
379+
if requireScheme != "" && purl.Scheme != requireScheme {
380+
resp.Diagnostics.AddAttributeError(
381+
req.Path,
382+
"URL scheme mismatch",
383+
fmt.Sprintf("Defined scheme %q does not match required %q", purl.Scheme, requireScheme),
384+
)
385+
}
386+
if requirePort != "" && purl.Port() != requirePort {
387+
resp.Diagnostics.AddAttributeError(
388+
req.Path,
389+
"URL port mismatch",
390+
fmt.Sprintf("Defined port %q does not match required %q", purl.Port(), requirePort),
391+
)
392+
}
393+
}
394+
}
395+
396+
if lv, ok := req.ConfigValue.(types.List); ok {
397+
for _, v := range lv.Elements() {
398+
validateURL(conv.AttributeValueToString(v))
399+
}
400+
} else if sv, ok := req.ConfigValue.(types.Set); ok {
401+
for _, v := range sv.Elements() {
402+
validateURL(conv.AttributeValueToString(v))
403+
}
404+
} else if mv, ok := req.ConfigValue.(types.Map); ok {
405+
for _, v := range mv.Elements() {
406+
validateURL(conv.AttributeValueToString(v))
407+
}
408+
} else {
409+
validateURL(conv.AttributeValueToString(req.ConfigValue))
373410
}
374411
}
375412
}
376413

377-
var isURLValidator = NewGenericValidator(GenericConfig{
378-
Description: "Tests if provided value is parseable as url.URL",
379-
MarkdownDescription: "Tests if provided value is parseable as url.URL",
380-
TestFunc: TestIsURL(),
381-
SkipWhenNull: true,
382-
SkipWhenUnknown: true,
383-
})
414+
// IsURLWith returns a validator that asserts a given attribute's value(s) are parseable as an URL, and that it / they
415+
// have a specific scheme and / or port
416+
func IsURLWith(requiredScheme string, requiredPort int) Generic {
417+
return NewGenericValidator(GenericConfig{
418+
Description: "Tests if provided value is parseable as url.URL",
419+
MarkdownDescription: "Tests if provided value is parseable as url.URL",
420+
TestFunc: TestIsURL(requiredScheme, requiredPort),
421+
SkipWhenNull: true,
422+
SkipWhenUnknown: true,
423+
})
424+
}
384425

385-
// IsURL returns a validator that asserts a given attribute value is parseable as a URL
426+
// IsURL returns a validator that asserts a given attribute's value(s) are parseable as an URL
386427
func IsURL() Generic {
387-
return isURLValidator
428+
return IsURLWith("", 0)
388429
}
389430

390431
// TestIsDurationString asserts that a given attribute's value is a valid time.Duration string

0 commit comments

Comments
 (0)