|
8 | 8 | "net/url" |
9 | 9 | "os" |
10 | 10 | "regexp" |
| 11 | + "strconv" |
11 | 12 | "strings" |
12 | 13 | "time" |
13 | 14 |
|
@@ -362,29 +363,69 @@ func Compare(op CompareOp, target interface{}, meta ...interface{}) Generic { |
362 | 363 | } |
363 | 364 |
|
364 | 365 | // TestIsURL asserts that the provided value can be parsed by url.Parse() |
365 | | -func TestIsURL() TestFunc { |
| 366 | +func TestIsURL(requireScheme string, requirePort int) TestFunc { |
366 | 367 | 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)) |
373 | 410 | } |
374 | 411 | } |
375 | 412 | } |
376 | 413 |
|
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 | +} |
384 | 425 |
|
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 |
386 | 427 | func IsURL() Generic { |
387 | | - return isURLValidator |
| 428 | + return IsURLWith("", 0) |
388 | 429 | } |
389 | 430 |
|
390 | 431 | // TestIsDurationString asserts that a given attribute's value is a valid time.Duration string |
|
0 commit comments