Skip to content

[cmd/mdatagen] Generate required fields validation for go config structs - #14768

Merged
dmitryax merged 8 commits into
open-telemetry:mainfrom
jkoronaAtCisco:go_validators_mdatagen
Mar 24, 2026
Merged

[cmd/mdatagen] Generate required fields validation for go config structs#14768
dmitryax merged 8 commits into
open-telemetry:mainfrom
jkoronaAtCisco:go_validators_mdatagen

Conversation

@jkoronaAtCisco

@jkoronaAtCisco jkoronaAtCisco commented Mar 13, 2026

Copy link
Copy Markdown
Member

Description

This PR adds automatic generation of Validate() methods for Go config structs produced by mdatagen, based on the JSON Schema config metadata.

When a config field is marked required in the config schema, the generated Validate() method emits the appropriate nil/empty check, handling all field kinds:

  • IsPointer → nil check
  • IsOptional (configoptional) → .HasValue() check
  • string → empty string check
  • array → len() == 0 check
  • object (map) → nil or empty map check

Validation is also propagated recursively — if a field is an embedded struct, map, or slice whose element type itself has required fields, the generated Validate() calls the child type's Validate() method. Internal $ref and allOf references are resolved through internal $defs and validated the same way.

Example

Given a metadata.yaml with:

config:
  type: object
  required: [targets]
  properties:
    targets:
      type: array
      items:
        type: object
        required: [url]
        properties:
          url:
            type: string

The generated code produces:

func (c *TargetsItem) Validate() error {
  var err error

  if c.URL == "" {
      err = errors.Join(err, errors.New("URL is required"))
  }

  return err
}

func (c *Config) Validate() error {
  var err error

  if len(c.Targets) == 0 {
      err = errors.Join(err, errors.New("Targets is required"))
  }
  for _, value := range c.Targets {
      err = errors.Join(err, value.Validate())
  }

  return err
}

Link to tracking issue

#14563

Testing

  1. Unit tests
  2. Added use cases to samplescraper and samplereceiver

@codspeed-hq

codspeed-hq Bot commented Mar 13, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

⚠️ Unknown Walltime execution environment detected

Using the Walltime instrument on standard Hosted Runners will lead to inconsistent data.

For the most accurate results, we recommend using CodSpeed Macro Runners: bare-metal machines fine-tuned for performance measurement consistency.

✅ 7 untouched benchmarks
⏩ 76 skipped benchmarks1


Comparing jkoronaAtCisco:go_validators_mdatagen (3dce9ee) with main (16844a7)

Open in CodSpeed

Footnotes

  1. 76 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@jkoronaAtCisco
jkoronaAtCisco marked this pull request as ready for review March 13, 2026 21:44
@jkoronaAtCisco
jkoronaAtCisco requested review from a team and dmitryax as code owners March 13, 2026 21:44
@codecov

codecov Bot commented Mar 13, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 54.68750% with 29 lines in your changes missing coverage. Please review.
✅ Project coverage is 91.33%. Comparing base (16844a7) to head (3dce9ee).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
cmd/mdatagen/internal/cfggen/generation.go 74.46% 9 Missing and 3 partials ⚠️
...datagen/internal/samplescraper/generated_config.go 0.00% 12 Missing ⚠️
...atagen/internal/samplereceiver/generated_config.go 0.00% 5 Missing ⚠️

❌ Your patch status has failed because the patch coverage (54.68%) is below the target coverage (95.00%). You can increase the patch coverage or adjust the target coverage.

Additional details and impacted files
@@            Coverage Diff             @@
##             main   #14768      +/-   ##
==========================================
- Coverage   91.38%   91.33%   -0.05%     
==========================================
  Files         695      697       +2     
  Lines       44405    44456      +51     
==========================================
+ Hits        40579    40604      +25     
- Misses       2690     2714      +24     
- Partials     1136     1138       +2     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@dmitryax

Copy link
Copy Markdown
Member

IsOptional (configoptional) → .HasValue() check

This is not needed. optional and required are contradictory

@jkoronaAtCisco

Copy link
Copy Markdown
Member Author

IsOptional (configoptional) → .HasValue() check

This is not needed. optional and required are contradictory

Makes sense. I removed it.

Comment thread cmd/mdatagen/internal/samplereceiver/generated_config.go Outdated
@dmitryax
dmitryax added this pull request to the merge queue Mar 24, 2026
Merged via the queue into open-telemetry:main with commit c985f1f Mar 24, 2026
66 of 67 checks passed
@jkoronaAtCisco
jkoronaAtCisco deleted the go_validators_mdatagen branch March 24, 2026 11:26
blakerouse pushed a commit to blakerouse/opentelemetry-collector that referenced this pull request May 13, 2026
…cts (open-telemetry#14768)

<!--Ex. Fixing a bug - Describe the bug and how this fixes the issue.
Ex. Adding a feature - Explain what this achieves.-->
#### Description
This PR adds automatic generation of Validate() methods for Go config
structs produced by mdatagen, based on the JSON Schema config metadata.

When a config field is marked required in the config schema, the
generated Validate() method emits the appropriate nil/empty check,
handling all field kinds:

  - IsPointer → nil check
  - IsOptional (configoptional) → .HasValue() check
  - string → empty string check
  - array → len() == 0 check
  - object (map) → nil or empty map check

Validation is also propagated recursively — if a field is an embedded
struct, map, or slice whose element type itself has required fields, the
generated Validate() calls the child type's Validate() method. Internal
$ref and allOf references are resolved through internal $defs and
validated the same way.

#### Example

Given a metadata.yaml with:
```yaml
config:
  type: object
  required: [targets]
  properties:
    targets:
      type: array
      items:
        type: object
        required: [url]
        properties:
          url:
            type: string
```
The generated code produces:
```go
func (c *TargetsItem) Validate() error {
  var err error

  if c.URL == "" {
      err = errors.Join(err, errors.New("URL is required"))
  }

  return err
}

func (c *Config) Validate() error {
  var err error

  if len(c.Targets) == 0 {
      err = errors.Join(err, errors.New("Targets is required"))
  }
  for _, value := range c.Targets {
      err = errors.Join(err, value.Validate())
  }

  return err
}
```

<!-- Issue number if applicable -->
#### Link to tracking issue
open-telemetry#14563

<!--Describe what testing was performed and which tests were added.-->
#### Testing

1. Unit tests
2. Added use cases to samplescraper and samplereceiver
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants