Skip to content

Commit 5de93b9

Browse files
authored
[cmd/mdatagen] Implement numeric validation in generated Go config code (#14806) (#15270)
<!--Ex. Fixing a bug - Describe the bug and how this fixes the issue. Ex. Adding a feature - Explain what this achieves.--> Implements support for the JSON Schema numeric validation keywords 'minimum', 'maximum', 'exclusiveMaximum' and 'exclusiveMinimum' in mdatagen config generation pipeline. <!-- Issue number if applicable --> Fixes #14806 <!--Describe what testing was performed and which tests were added.--> * Added unit tests in generation_test.go * The samplescraper golden file (`generated_config.go`) is updated to reflect the new generated output.
1 parent 522187d commit 5de93b9

9 files changed

Lines changed: 452 additions & 10 deletions

File tree

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Use this changelog template to create an entry for release notes.
2+
3+
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
4+
change_type: enhancement
5+
6+
# The name of the component, or a single word describing the area of concern, (e.g. receiver/otlp)
7+
component: cmd/mdatagen
8+
9+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
10+
note: Handle numeric validators in generated config structs
11+
12+
# One or more tracking issues or pull requests related to the change
13+
issues: [14806]
14+
15+
# (Optional) One or more lines of additional information to render under the primary note.
16+
# These lines will be padded with 2 spaces and then inserted directly into the document.
17+
# Use pipe (|) for multiline entries.
18+
subtext: Supported validators include `minimum`, `maximum`, `exclusiveMinimum` and `exclusiveMaximum`.
19+
20+
# Optional: The change log or logs in which this entry should be included
21+
# e.g. '[user]' or '[user, api]'
22+
# Include 'user' if the change is relevant to end users
23+
# Include 'api' if there is a change to a library API
24+
# Default: '[user]'
25+
change_logs: [api]

cmd/mdatagen/internal/cfggen/generation.go

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -441,14 +441,19 @@ func ExtractValidators(md *ConfigMetadata) []Validator {
441441
}
442442

443443
type ValidationRules struct {
444-
MaxLength *int
445-
MinLength *int
446-
Pattern *string
447-
Required bool
444+
MaxLength *int
445+
MinLength *int
446+
Pattern *string
447+
Required bool
448+
Minimum *float64
449+
Maximum *float64
450+
ExclusiveMinimum *float64
451+
ExclusiveMaximum *float64
448452
}
449453

450454
func (vr *ValidationRules) HasValueRule() bool {
451-
return vr.MaxLength != nil || vr.MinLength != nil || vr.Pattern != nil
455+
return vr.MaxLength != nil || vr.MinLength != nil || vr.Pattern != nil ||
456+
vr.Minimum != nil || vr.Maximum != nil || vr.ExclusiveMinimum != nil || vr.ExclusiveMaximum != nil
452457
}
453458

454459
func (vr *ValidationRules) Enabled() bool {
@@ -468,8 +473,12 @@ func collectValidators(md *ConfigMetadata, validators *[]Validator) {
468473
for _, propName := range slices.Sorted(maps.Keys(md.Properties)) {
469474
prop := md.Properties[propName]
470475
rules := ValidationRules{
471-
MaxLength: prop.MaxLength,
472-
MinLength: prop.MinLength,
476+
MaxLength: prop.MaxLength,
477+
MinLength: prop.MinLength,
478+
Minimum: prop.Minimum,
479+
Maximum: prop.Maximum,
480+
ExclusiveMinimum: prop.ExclusiveMinimum,
481+
ExclusiveMaximum: prop.ExclusiveMaximum,
473482
}
474483

475484
rules.Required = slices.Contains(md.Required, propName)

cmd/mdatagen/internal/cfggen/generation_test.go

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1799,6 +1799,150 @@ func TestExtractValidators_StringValidators(t *testing.T) {
17991799
}
18001800
}
18011801

1802+
func TestExtractValidators_NumericValidators(t *testing.T) {
1803+
minVal := 0.0
1804+
maxVal := 100.0
1805+
exclMinVal := 5.0
1806+
exclMaxVal := 10.0
1807+
1808+
tests := []struct {
1809+
name string
1810+
metadata *ConfigMetadata
1811+
expected []Validator
1812+
}{
1813+
{
1814+
name: "minimum only",
1815+
metadata: &ConfigMetadata{
1816+
Type: "object",
1817+
Properties: map[string]*ConfigMetadata{
1818+
"count": {Type: "number", Minimum: &minVal},
1819+
},
1820+
},
1821+
expected: []Validator{
1822+
{
1823+
FieldName: "count",
1824+
FieldType: "number",
1825+
Rules: ValidationRules{Minimum: &minVal},
1826+
},
1827+
},
1828+
},
1829+
{
1830+
name: "maximum only",
1831+
metadata: &ConfigMetadata{
1832+
Type: "object",
1833+
Properties: map[string]*ConfigMetadata{
1834+
"count": {Type: "number", Maximum: &maxVal},
1835+
},
1836+
},
1837+
expected: []Validator{
1838+
{
1839+
FieldName: "count",
1840+
FieldType: "number",
1841+
Rules: ValidationRules{Maximum: &maxVal},
1842+
},
1843+
},
1844+
},
1845+
{
1846+
name: "exclusiveMinimum only",
1847+
metadata: &ConfigMetadata{
1848+
Type: "object",
1849+
Properties: map[string]*ConfigMetadata{
1850+
"score": {Type: "number", ExclusiveMinimum: &exclMinVal},
1851+
},
1852+
},
1853+
expected: []Validator{
1854+
{
1855+
FieldName: "score",
1856+
FieldType: "number",
1857+
Rules: ValidationRules{ExclusiveMinimum: &exclMinVal},
1858+
},
1859+
},
1860+
},
1861+
{
1862+
name: "exclusiveMaximum only",
1863+
metadata: &ConfigMetadata{
1864+
Type: "object",
1865+
Properties: map[string]*ConfigMetadata{
1866+
"score": {Type: "number", ExclusiveMaximum: &exclMaxVal},
1867+
},
1868+
},
1869+
expected: []Validator{
1870+
{
1871+
FieldName: "score",
1872+
FieldType: "number",
1873+
Rules: ValidationRules{ExclusiveMaximum: &exclMaxVal},
1874+
},
1875+
},
1876+
},
1877+
{
1878+
name: "all numeric validators",
1879+
metadata: &ConfigMetadata{
1880+
Type: "object",
1881+
Properties: map[string]*ConfigMetadata{
1882+
"value": {Type: "number", Minimum: &minVal, Maximum: &maxVal, ExclusiveMinimum: &exclMinVal, ExclusiveMaximum: &exclMaxVal},
1883+
},
1884+
},
1885+
expected: []Validator{
1886+
{
1887+
FieldName: "value",
1888+
FieldType: "number",
1889+
Rules: ValidationRules{Minimum: &minVal, Maximum: &maxVal, ExclusiveMinimum: &exclMinVal, ExclusiveMaximum: &exclMaxVal},
1890+
},
1891+
},
1892+
},
1893+
{
1894+
name: "integer type with numeric validators",
1895+
metadata: &ConfigMetadata{
1896+
Type: "object",
1897+
Properties: map[string]*ConfigMetadata{
1898+
"retry_count": {Type: "integer", Minimum: &minVal, Maximum: &maxVal},
1899+
},
1900+
},
1901+
expected: []Validator{
1902+
{
1903+
FieldName: "retry_count",
1904+
FieldType: "integer",
1905+
Rules: ValidationRules{Minimum: &minVal, Maximum: &maxVal},
1906+
},
1907+
},
1908+
},
1909+
{
1910+
name: "nil numeric validators produces no validator",
1911+
metadata: &ConfigMetadata{
1912+
Type: "object",
1913+
Properties: map[string]*ConfigMetadata{
1914+
"value": {Type: "number"},
1915+
},
1916+
},
1917+
expected: []Validator{},
1918+
},
1919+
{
1920+
name: "required combined with numeric validators",
1921+
metadata: &ConfigMetadata{
1922+
Type: "object",
1923+
Required: []string{"threshold"},
1924+
Properties: map[string]*ConfigMetadata{
1925+
"threshold": {Type: "number", Minimum: &minVal, Maximum: &maxVal},
1926+
},
1927+
},
1928+
expected: []Validator{
1929+
{
1930+
FieldName: "threshold",
1931+
FieldType: "number",
1932+
Rules: ValidationRules{Required: true, Minimum: &minVal, Maximum: &maxVal},
1933+
},
1934+
},
1935+
},
1936+
}
1937+
1938+
for _, tt := range tests {
1939+
t.Run(tt.name, func(t *testing.T) {
1940+
result := ExtractValidators(tt.metadata)
1941+
require.Equal(t, tt.expected, result)
1942+
})
1943+
}
1944+
}
1945+
18021946
func TestExtractValidators_InternalRefFromDefs_NoValidators(t *testing.T) {
18031947
md := &ConfigMetadata{
18041948
Ref: "plain_config",
@@ -1974,6 +2118,36 @@ func TestValidationRules_HasValueRule(t *testing.T) {
19742118
rules: ValidationRules{},
19752119
expected: false,
19762120
},
2121+
{
2122+
name: "minimum",
2123+
rules: ValidationRules{Minimum: Ptr(0.0)},
2124+
expected: true,
2125+
},
2126+
{
2127+
name: "maximum",
2128+
rules: ValidationRules{Maximum: Ptr(100.0)},
2129+
expected: true,
2130+
},
2131+
{
2132+
name: "exclusiveMinimum",
2133+
rules: ValidationRules{ExclusiveMinimum: Ptr(5.0)},
2134+
expected: true,
2135+
},
2136+
{
2137+
name: "exclusiveMaximum",
2138+
rules: ValidationRules{ExclusiveMaximum: Ptr(10.0)},
2139+
expected: true,
2140+
},
2141+
{
2142+
name: "all numeric validators",
2143+
rules: ValidationRules{Minimum: Ptr(0.0), Maximum: Ptr(100.0), ExclusiveMinimum: Ptr(5.0), ExclusiveMaximum: Ptr(10.0)},
2144+
expected: true,
2145+
},
2146+
{
2147+
name: "empty numeric validators",
2148+
rules: ValidationRules{},
2149+
expected: false,
2150+
},
19772151
}
19782152

19792153
for _, tt := range tests {

cmd/mdatagen/internal/samplescraper/config.schema.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -621,6 +621,20 @@
621621
"additionalProperties": {
622622
"type": "string"
623623
}
624+
},
625+
"retry_count": {
626+
"description": "Number of retry attempts for failed scrapes.",
627+
"type": "integer",
628+
"default": 3,
629+
"maximum": 10,
630+
"minimum": 0
631+
},
632+
"timeout_seconds": {
633+
"description": "Timeout in seconds for each scrape request.",
634+
"type": "number",
635+
"default": 5,
636+
"exclusiveMaximum": 30,
637+
"exclusiveMinimum": 0
624638
}
625639
},
626640
"required": [

cmd/mdatagen/internal/samplescraper/generated_config.go

Lines changed: 22 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cmd/mdatagen/internal/samplescraper/generated_config_test.go

Lines changed: 28 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cmd/mdatagen/internal/samplescraper/metadata.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,18 @@ config:
6868
default:
6969
option1: value1
7070
option2: value2
71+
retry_count:
72+
description: Number of retry attempts for failed scrapes.
73+
type: integer
74+
default: 3
75+
minimum: 0
76+
maximum: 10
77+
timeout_seconds:
78+
description: Timeout in seconds for each scrape request.
79+
type: number
80+
default: 5.0
81+
exclusiveMinimum: 0
82+
exclusiveMaximum: 30
7183
required: [labels]
7284
default:
7385
- {} # one item with default values

0 commit comments

Comments
 (0)