mirror of
https://github.com/hanzoai/telemetry.git
synced 2026-10-30 20:01:49 +00:00
#### Description Add combiner logic for the schemas. Note there are many missing things here like preserving and merging `$defs` and rewrite `$ref`. I will do this as part of following PRs. This PR will allow to continue with the work related to `ocb` and improvements to generate a good schema. <!-- Issue number if applicable --> #### Link to tracking issue Updates #15010 Signed-off-by: Israel Blancas <[email protected]>
137 lines
8.5 KiB
Go
137 lines
8.5 KiB
Go
// Copyright The OpenTelemetry Authors
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package schemagen // import "go.opentelemetry.io/collector/internal/schemagen"
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"go.opentelemetry.io/collector/confmap"
|
|
)
|
|
|
|
// ConfigMetadata represents a JSON schema object, draft 2020-12 (limited), with additional custom fields.
|
|
type ConfigMetadata struct {
|
|
Schema string `mapstructure:"$schema,omitempty" json:"$schema,omitempty" yaml:"$schema,omitempty"`
|
|
ID string `mapstructure:"$id,omitempty" json:"$id,omitempty" yaml:"$id,omitempty"`
|
|
Title string `mapstructure:"title,omitempty" json:"title,omitempty" yaml:"title,omitempty"`
|
|
Description string `mapstructure:"description,omitempty" json:"description,omitempty" yaml:"description,omitempty"`
|
|
Comment string `mapstructure:"$comment,omitempty" json:"$comment,omitempty" yaml:"$comment,omitempty"`
|
|
Type string `mapstructure:"type,omitempty" json:"type,omitempty" yaml:"type,omitempty"`
|
|
Ref string `mapstructure:"$ref,omitempty" json:"-" yaml:"$ref,omitempty"`
|
|
Default any `mapstructure:"default,omitempty" json:"default,omitempty" yaml:"default,omitempty"`
|
|
Examples []any `mapstructure:"examples,omitempty" json:"examples,omitempty" yaml:"examples,omitempty"`
|
|
Deprecated bool `mapstructure:"deprecated,omitempty" json:"deprecated,omitempty" yaml:"deprecated,omitempty"`
|
|
Enum []any `mapstructure:"enum,omitempty" json:"enum,omitempty" yaml:"enum,omitempty"`
|
|
Const any `mapstructure:"const,omitempty" json:"const,omitempty" yaml:"const,omitempty"`
|
|
AllOf []*ConfigMetadata `mapstructure:"allOf,omitempty" json:"allOf,omitempty" yaml:"allOf,omitempty"`
|
|
Properties map[string]*ConfigMetadata `mapstructure:"properties,omitempty" json:"properties,omitempty" yaml:"properties,omitempty"`
|
|
AdditionalProperties *ConfigMetadata `mapstructure:"additionalProperties,omitempty" json:"additionalProperties,omitempty" yaml:"additionalProperties,omitempty"`
|
|
PatternProperties map[string]*ConfigMetadata `mapstructure:"patternProperties,omitempty" json:"-" yaml:"patternProperties,omitempty"`
|
|
Required []string `mapstructure:"required,omitempty" json:"required,omitempty" yaml:"required,omitempty"`
|
|
MinProperties *int `mapstructure:"minProperties,omitempty" json:"minProperties,omitempty" yaml:"minProperties,omitempty"`
|
|
MaxProperties *int `mapstructure:"maxProperties,omitempty" json:"maxProperties,omitempty" yaml:"maxProperties,omitempty"`
|
|
Items *ConfigMetadata `mapstructure:"items,omitempty" json:"items,omitempty" yaml:"items,omitempty"`
|
|
MinItems *int `mapstructure:"minItems,omitempty" json:"minItems,omitempty" yaml:"minItems,omitempty"`
|
|
MaxItems *int `mapstructure:"maxItems,omitempty" json:"maxItems,omitempty" yaml:"maxItems,omitempty"`
|
|
UniqueItems bool `mapstructure:"uniqueItems,omitempty" json:"uniqueItems,omitempty" yaml:"uniqueItems,omitempty"`
|
|
MaxLength *int `mapstructure:"maxLength,omitempty" json:"maxLength,omitempty" yaml:"maxLength,omitempty"`
|
|
MinLength *int `mapstructure:"minLength,omitempty" json:"minLength,omitempty" yaml:"minLength,omitempty"`
|
|
Pattern string `mapstructure:"pattern,omitempty" json:"pattern,omitempty" yaml:"pattern,omitempty"`
|
|
Format string `mapstructure:"format,omitempty" json:"format,omitempty" yaml:"format,omitempty"`
|
|
ContentMediaType string `mapstructure:"contentMediaType,omitempty" json:"contentMediaType,omitempty" yaml:"contentMediaType,omitempty"`
|
|
ContentEncoding string `mapstructure:"contentEncoding,omitempty" json:"contentEncoding,omitempty" yaml:"contentEncoding,omitempty"`
|
|
ContentSchema *ConfigMetadata `mapstructure:"contentSchema,omitempty" json:"contentSchema,omitempty" yaml:"contentSchema,omitempty"`
|
|
MultipleOf *float64 `mapstructure:"multipleOf,omitempty" json:"multipleOf,omitempty" yaml:"multipleOf,omitempty"`
|
|
Maximum *float64 `mapstructure:"maximum,omitempty" json:"maximum,omitempty" yaml:"maximum,omitempty"`
|
|
ExclusiveMaximum *float64 `mapstructure:"exclusiveMaximum,omitempty" json:"exclusiveMaximum,omitempty" yaml:"exclusiveMaximum,omitempty"`
|
|
Minimum *float64 `mapstructure:"minimum,omitempty" json:"minimum,omitempty" yaml:"minimum,omitempty"`
|
|
ExclusiveMinimum *float64 `mapstructure:"exclusiveMinimum,omitempty" json:"exclusiveMinimum,omitempty" yaml:"exclusiveMinimum,omitempty"`
|
|
Defs map[string]*ConfigMetadata `mapstructure:"$defs,omitempty" json:"$defs,omitempty" yaml:"$defs,omitempty"`
|
|
// Additional custom fields
|
|
GoStruct GoStructConfig `mapstructure:"go_struct,omitempty" json:"-" yaml:"go_struct,omitempty"`
|
|
GoType string `mapstructure:"x-customType,omitempty" json:"-" yaml:"x-customType,omitempty"`
|
|
IsPointer bool `mapstructure:"x-pointer,omitempty" json:"-" yaml:"x-pointer,omitempty"`
|
|
IsOptional bool `mapstructure:"x-optional,omitempty" json:"-" yaml:"x-optional,omitempty"`
|
|
Embed bool `mapstructure:"embed,omitempty" json:"-" yaml:"embed,omitempty"`
|
|
// internal
|
|
ResolvedFrom string `mapstructure:"-" json:"-" yaml:"-"`
|
|
EmbeddedName string `mapstructure:"-" json:"-" yaml:"-"`
|
|
AdditionalPropertiesAllowed *bool `mapstructure:"-" json:"-" yaml:"-"`
|
|
InternalOnly bool `mapstructure:"-" json:"-" yaml:"-"`
|
|
}
|
|
|
|
type Metadata struct {
|
|
Config *ConfigMetadata `mapstructure:"config,omitempty" json:"config,omitempty" yaml:"config,omitempty"`
|
|
ExportedConfigs map[string]*ConfigMetadata `mapstructure:"exported_configs,omitempty" json:"exported_configs,omitempty" yaml:"exported_configs,omitempty"`
|
|
}
|
|
|
|
type GoStructConfig struct {
|
|
CustomValidator *CustomValidatorConfig `mapstructure:"custom_validator" json:"-" yaml:"custom_validator,omitempty"`
|
|
Anonymous bool `mapstructure:"anonymous" json:"-" yaml:"anonymous,omitempty"`
|
|
IgnoreDefault bool `mapstructure:"ignore_default" json:"-" yaml:"ignore_default,omitempty"`
|
|
}
|
|
|
|
type CustomValidatorConfig struct {
|
|
Name string `mapstructure:"name,omitempty" json:"-" yaml:"name,omitempty"`
|
|
}
|
|
|
|
func (g *GoStructConfig) Unmarshal(parser *confmap.Conf) error {
|
|
type goStructConfig GoStructConfig
|
|
if err := parser.Unmarshal((*goStructConfig)(g), confmap.WithIgnoreUnused()); err != nil {
|
|
return err
|
|
}
|
|
if !parser.IsSet("custom_validator") || g.CustomValidator != nil {
|
|
return nil
|
|
}
|
|
sub, err := parser.Sub("custom_validator")
|
|
if err != nil {
|
|
return fmt.Errorf("invalid custom_validator: %w", err)
|
|
}
|
|
g.CustomValidator = &CustomValidatorConfig{}
|
|
return sub.Unmarshal(g.CustomValidator)
|
|
}
|
|
|
|
func (md *ConfigMetadata) ToJSON() ([]byte, error) {
|
|
return json.MarshalIndent(md, "", " ")
|
|
}
|
|
|
|
func (md *ConfigMetadata) MarshalJSON() ([]byte, error) {
|
|
type alias ConfigMetadata
|
|
|
|
if len(md.PatternProperties) == 0 && md.AdditionalPropertiesAllowed == nil {
|
|
return json.Marshal((*alias)(md))
|
|
}
|
|
|
|
type withSpecialFields struct {
|
|
*alias
|
|
AdditionalProperties any `json:"additionalProperties,omitempty"`
|
|
PatternProperties map[string]*ConfigMetadata `json:"patternProperties,omitempty"`
|
|
}
|
|
|
|
out := withSpecialFields{
|
|
alias: (*alias)(md),
|
|
PatternProperties: md.PatternProperties,
|
|
}
|
|
if md.AdditionalPropertiesAllowed != nil {
|
|
out.AdditionalProperties = *md.AdditionalPropertiesAllowed
|
|
}
|
|
|
|
return json.Marshal(out)
|
|
}
|
|
|
|
func (md *ConfigMetadata) Validate() error {
|
|
var errs error
|
|
|
|
hasDefs := len(md.Defs) > 0
|
|
hasConfigFields := len(md.Properties) > 0 || len(md.AllOf) > 0
|
|
if md.Type != "object" && (md.Type != "" || !hasDefs || hasConfigFields) {
|
|
errs = errors.Join(errs, fmt.Errorf("config type must be \"object\", got %q", md.Type))
|
|
}
|
|
if !hasDefs && !hasConfigFields {
|
|
errs = errors.Join(errs, errors.New("config must not be empty"))
|
|
}
|
|
return errs
|
|
}
|