Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

modeldecoder/generator: enable additionalProperties subschema #4464

Merged
merged 1 commit into from
Nov 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/spec/v2/metricset.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
"additionalProperties": false,
"patternProperties": {
"^[^*\"]*$": {
"type": [
"null",
"object"
],
"properties": {
"value": {
"description": "Value holds the value of a single metric sample.",
Expand Down
6 changes: 1 addition & 5 deletions model/modeldecoder/generator/jsonschema.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,10 +121,6 @@ func (g *JSONSchemaGenerator) generate(st structType, key string, prop *property
default:
switch t := f.Type().Underlying().(type) {
case *types.Map:
if _, ok := tags[tagPatternKeys]; !ok && name == "" {
// ignore the field in case no json name and no patternProperties are given
continue
}
nestedProp := property{Properties: make(map[string]*property)}
if err = generateJSONPropertyMap(&info, prop, &childProp, &nestedProp); err != nil {
break
Expand Down Expand Up @@ -240,7 +236,7 @@ type property struct {
Type *propertyType `json:"type,omitempty"`
// AdditionalProperties should default to `true` and be set to `false`
// in case PatternProperties are set
AdditionalProperties *bool `json:"additionalProperties,omitempty"`
AdditionalProperties interface{} `json:"additionalProperties,omitempty"`
PatternProperties map[string]*property `json:"patternProperties,omitempty"`
Properties map[string]*property `json:"properties,omitempty"`
Items *property `json:"items,omitempty"`
Expand Down
80 changes: 36 additions & 44 deletions model/modeldecoder/generator/map.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,19 +159,23 @@ func generateJSONPropertyMap(info *fieldInfo, parent *property, child *property,
child.Type.add(TypeNameObject)
patternName, isPatternProp := info.tags[tagPatternKeys]
delete(info.tags, tagPatternKeys)
if !isPatternProp && name == "" {
// the object (map) can be places as a property with a defined key,
// or as a patternProperty with a defined key pattern
// if both are missing the field cannot be added
return fmt.Errorf("invalid combination: either json name or tag %s must be given", tagPatternKeys)
}
if !isPatternProp {
// if no pattern property is given, the child map will be nested directly as
// property inside the parent property, identified by it's json name
// e.g. {"parent":{"properties":{"jsonNameXY":{..}}}}

nestedParent := child
if name == "" {
// The map does not have a json name defined, in which case it is nested directly
// inside the parent object's patternProperties/additionalProperties
// e.g. {"parent":{"patternProperties":{"patternXY":{..}}}}
*nested = *child
parent.Properties[name] = nested
nestedParent = parent
} else {
// The map does have a json name defined, in which case it is nested as
// patternProperties/additionalProperties inside an object, which itself is nested
// inside the parent property, identified by its json name
// e.g. {"parent":{"properties":{"jsonNameXY":{"patternProperties":{"patternXY":{..}}}}}}
parent.Properties[name] = child
}

haveValueSchema := len(info.tags) > 0
if maxLen, ok := info.tags[tagMaxLengthVals]; ok {
nested.MaxLength = json.Number(maxLen)
delete(info.tags, tagMaxLengthVals)
Expand All @@ -183,42 +187,30 @@ func generateJSONPropertyMap(info *fieldInfo, parent *property, child *property,
}
delete(info.tags, tagInputTypesVals)
nested.Type = &propertyType{names: names}
}
if !isPatternProp {
// nothing more to do when no key pattern is given
return nil
}
pattern, ok := info.parsed.patternVariables[patternName]
if !ok {
return fmt.Errorf("unhandled %s tag value %s", tagPatternKeys, pattern)
}
// for map key patterns two options are supported:
// - the map does not have a json name defined, in which case it is nested directly
// inside the parent object's patternProperty
// e.g. {"parent":{"patternProperties":{"patternXY":{..}}}}
// - the map does have a json name defined, in which case it is nested as
// patternProperty inside an object, which itself is nested
// inside the parent property, identified by it's json name
// e.g. {"parent":{"properties":{"jsonNameXY":{"patternProperties":{"patternXY":{..}}}}}}
if name == "" {
if parent.PatternProperties == nil {
parent.PatternProperties = make(map[string]*property)
}
} else {
valueType := info.field.Type().Underlying().(*types.Map).Elem()
typeName, ok := propertyTypes[valueType.String()]
if !ok {
typeName = TypeNameObject
if !types.IsInterface(valueType) {
haveValueSchema = true
typeName, ok := propertyTypes[valueType.String()]
if !ok {
typeName = TypeNameObject
}
nested.Type = &propertyType{names: []propertyTypeName{typeName}}
}
nested.Type = &propertyType{names: []propertyTypeName{typeName}}
parent.PatternProperties[pattern] = nested
parent.AdditionalProperties = new(bool)
return nil
}
if child.PatternProperties == nil {
child.PatternProperties = make(map[string]*property)

if isPatternProp {
pattern, ok := info.parsed.patternVariables[patternName]
if !ok {
return fmt.Errorf("unhandled %s tag value %s", tagPatternKeys, pattern)
}
if nestedParent.PatternProperties == nil {
nestedParent.PatternProperties = make(map[string]*property)
}
nestedParent.PatternProperties[pattern] = nested
nestedParent.AdditionalProperties = false
} else if haveValueSchema {
nestedParent.AdditionalProperties = nested
}
child.PatternProperties[pattern] = nested
child.AdditionalProperties = new(bool)
parent.Properties[name] = child
return nil
}