Skip to content

Commit 9186d3d

Browse files
authored
feat: add stringvalidator HTTPCode (#55)
1 parent 3aefe39 commit 9186d3d

File tree

6 files changed

+446
-1
lines changed

6 files changed

+446
-1
lines changed

.changelog/55.txt

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:feature
2+
`stringvalidator` - Add new network validator `HTTPCode` to validate with settings the http code range allowed (Ex: 2xx).
3+
```

docs/stringvalidator/cases.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ func (r *xResource) Schema(ctx context.Context, _ resource.SchemaRequest, resp *
3232
Optional: true,
3333
MarkdownDescription: "Username for ...",
3434
Validators: []validator.String{
35-
fstringvalidator.IsNetwork([]fstringvalidator.CasesValidatorType{
35+
fstringvalidator.Cases([]fstringvalidator.CasesValidatorType{
3636
fstringvalidator.CasesDisallowUpper,
3737
fstringvalidator.CasesDisallowSpace,
3838
}, true)

docs/stringvalidator/httpcode.md

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
---
2+
hide:
3+
- navigation
4+
---
5+
# `Cases`
6+
7+
!!! quote inline end "Released in v1.15.0"
8+
9+
This validator is used to check if the string contains a valid http status code.
10+
A parameter can be passed to allow a category of status codes.
11+
The following categories are available:
12+
13+
* `1xx` - Informational responses
14+
* `2xx` - Successful responses
15+
* `3xx` - Redirection messages
16+
* `4xx` - Client error responses
17+
* `5xx` - Server error responses
18+
19+
## How to use it
20+
21+
The following example will check if the string does not contain any uppercase characters and does not contain any space characters.
22+
23+
```go
24+
// Schema defines the schema for the resource.
25+
func (r *xResource) Schema(ctx context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
26+
resp.Schema = schema.Schema{
27+
(...)
28+
"status_code": schema.StringAttribute{
29+
Optional: true,
30+
MarkdownDescription: "Allowed HTTP status code",
31+
Validators: []validator.String{
32+
fstringvalidator.HTTPCode(stringvalidator.HTTPCodeParams{
33+
Allow1xx: false,
34+
Allow2xx: true,
35+
Allow3xx: true,
36+
Allow4xx: false,
37+
Allow5xx: false,
38+
})
39+
},
40+
},
41+
```
42+
43+
In this example, the validator will check if the string is a valid HTTP status code and will allow only 2xx and 3xx status codes.

docs/stringvalidator/index.md

+1
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,4 @@ import (
4141
### Special
4242

4343
- [`Not`](not.md) - This validator is used to negate the result of another validator.
44+
- [`HTTPCode`](httpcode.md) - This validator is used to check if the string contains a valid http status code.

stringvalidator/http_code.go

+155
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
/*
2+
* SPDX-FileCopyrightText: Copyright (c) 2025 Orange
3+
* SPDX-License-Identifier: Mozilla Public License 2.0
4+
*
5+
* This software is distributed under the MPL-2.0 license.
6+
* the text of which is available at https://www.mozilla.org/en-US/MPL/2.0/
7+
* or see the "LICENSE" file for more details.
8+
*/
9+
10+
package stringvalidator
11+
12+
import (
13+
"context"
14+
"fmt"
15+
"net/http"
16+
"strconv"
17+
18+
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
19+
)
20+
21+
var _ validator.String = httpCode{}
22+
23+
type httpCode struct {
24+
allowedRanges []httpCodeParam
25+
}
26+
27+
// Description describes the validation in plain text formatting.
28+
func (validator httpCode) Description(_ context.Context) string {
29+
if len(validator.allowedRanges) == 0 {
30+
return ""
31+
}
32+
33+
description := ""
34+
35+
if len(validator.allowedRanges) == 1 {
36+
description += fmt.Sprintf("The allowed HTTP status code pattern is %s", validator.allowedRanges[0].format)
37+
} else {
38+
description += "The following HTTP status codes patterns are allowed: "
39+
for _, r := range validator.allowedRanges {
40+
description += fmt.Sprintf("%s, ", r.format)
41+
}
42+
description = description[:len(description)-2] // remove the last comma and space
43+
}
44+
45+
return description
46+
}
47+
48+
// MarkdownDescription describes the validation in Markdown formatting.
49+
func (validator httpCode) MarkdownDescription(ctx context.Context) string {
50+
if len(validator.allowedRanges) == 0 {
51+
return ""
52+
}
53+
54+
description := ""
55+
56+
if len(validator.allowedRanges) == 1 {
57+
description += fmt.Sprintf("The allowed HTTP status code pattern is `%s`", validator.allowedRanges[0].format)
58+
} else {
59+
description += "The following HTTP status codes patterns are allowed: "
60+
for _, r := range validator.allowedRanges {
61+
description += fmt.Sprintf("`%s`, ", r.format)
62+
}
63+
description = description[:len(description)-2] // remove the last comma and space
64+
}
65+
return description
66+
}
67+
68+
// Validate performs the validation.
69+
func (validator httpCode) ValidateString(
70+
_ context.Context,
71+
request validator.StringRequest,
72+
response *validator.StringResponse,
73+
) {
74+
if request.ConfigValue.IsNull() || request.ConfigValue.IsUnknown() {
75+
return
76+
}
77+
78+
// Check if the value is a valid HTTP status code in the allowed ranges
79+
httpCode := request.ConfigValue.ValueString()
80+
81+
// convert the string to an integer
82+
code, err := strconv.Atoi(httpCode)
83+
if err != nil {
84+
response.Diagnostics.AddError(
85+
"Invalid HTTP code",
86+
fmt.Sprintf("The value %s is not a valid HTTP status code", httpCode),
87+
)
88+
return
89+
}
90+
91+
// if the status text is empty, it means the code is not valid
92+
if v := http.StatusText(code); v == "" {
93+
response.Diagnostics.AddError(
94+
"Invalid HTTP code",
95+
fmt.Sprintf("The value %s is not a valid HTTP status code defined by the HTTP RFC9110", httpCode),
96+
)
97+
return
98+
}
99+
100+
// Check if the code is in the allowed ranges
101+
for _, r := range validator.allowedRanges {
102+
if code >= r.start && code <= r.end {
103+
return
104+
}
105+
}
106+
107+
response.Diagnostics.AddError(
108+
"Invalid HTTP code",
109+
fmt.Sprintf("The value %s is not a valid HTTP status code in the allowed ranges", httpCode),
110+
)
111+
}
112+
113+
type HTTPCodeParams struct {
114+
Allow1xx bool
115+
Allow2xx bool
116+
Allow3xx bool
117+
Allow4xx bool
118+
Allow5xx bool
119+
}
120+
121+
type httpCodeParam struct {
122+
format string
123+
start int
124+
end int
125+
}
126+
127+
// HTTPCode validates that a string represents a valid HTTP status code.
128+
//
129+
// Parameters:
130+
// - settings: HTTPCodeParams containing the configuration for the validator.
131+
//
132+
// Returns:
133+
// - validator.String: A validator that checks if the string is a valid HTTP status code.
134+
func HTTPCode(settings HTTPCodeParams) validator.String {
135+
return &httpCode{
136+
allowedRanges: func() (ranges []httpCodeParam) {
137+
if settings.Allow1xx {
138+
ranges = append(ranges, httpCodeParam{start: 100, end: 199, format: "1xx"})
139+
}
140+
if settings.Allow2xx {
141+
ranges = append(ranges, httpCodeParam{start: 200, end: 299, format: "2xx"})
142+
}
143+
if settings.Allow3xx {
144+
ranges = append(ranges, httpCodeParam{start: 300, end: 399, format: "3xx"})
145+
}
146+
if settings.Allow4xx {
147+
ranges = append(ranges, httpCodeParam{start: 400, end: 499, format: "4xx"})
148+
}
149+
if settings.Allow5xx {
150+
ranges = append(ranges, httpCodeParam{start: 500, end: 599, format: "5xx"})
151+
}
152+
return ranges
153+
}(),
154+
}
155+
}

0 commit comments

Comments
 (0)