Skip to content

Commit

Permalink
feature: add additionalProperties
Browse files Browse the repository at this point in the history
Signed-off-by: Aleksandar Stojanov <[email protected]>
  • Loading branch information
losisin committed Jun 3, 2024
1 parent b6e2392 commit 28c13d4
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 32 deletions.
50 changes: 49 additions & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
JSON schema is partially implemented in this tool. It uses line comments to add annotations for the schema because head comments are frequently used by humans and tools like helm-docs. Multiple annotations can be added to a single line sepparated by semi-colon. For example:

```yaml
nameOverride: "myapp" # @schema maxLength:10;patter:^[a-z]+$
nameOverride: "myapp" # @schema maxLength:10;pattern:^[a-z]+$
```
This will generate following schema:
Expand Down Expand Up @@ -37,6 +37,8 @@ The following annotations are supported:
* [minProperties](#minproperties)
* [maxProperties](#maxproperties)
* [required](#required)
* [patternProperties](#patternproperties)
* [additionalProperties](#additionalproperties)
* [Meta-Data Annotations](#meta-data-annotations)
* [title and description](#title-and-description)
* [default](#default)
Expand Down Expand Up @@ -369,6 +371,52 @@ image:
}
```

### patternProperties

JSON string added "AS IS" to the node. [documentation](https://json-schema.org/understanding-json-schema/reference/object#patternProperties)

```yaml
image: # @schema patternProperties: {"^[a-z]$": {"type": "string"}}
repository: "nginx"
```
```json
"image": {
"patternProperties": {
"^[a-z]$": {
"type": "string"
}
},
"properties": {
"repository": {
"type": "string"
}
},
"type": "object"
}
```

### additionalProperties

Boolean. [documentation](https://json-schema.org/understanding-json-schema/reference/object#additionalproperties)

```yaml
image: # @schema additionalProperties: false
repository: "nginx"
```
```json
"image": {
"additionalProperties": false,
"properties": {
"repository": {
"type": "string"
}
},
"type": "object"
}
```

## Meta-Data Annotations

### title and description
Expand Down
6 changes: 6 additions & 0 deletions pkg/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ func mergeSchemas(dest, src *Schema) *Schema {
if src.Default != nil {
dest.Default = src.Default
}
if src.AdditionalProperties != nil {
dest.AdditionalProperties = src.AdditionalProperties
}

// Merge 'enum' field (assuming that maintaining order doesn't matter)
dest.Enum = append(dest.Enum, src.Enum...)
Expand Down Expand Up @@ -167,6 +170,9 @@ func convertSchemaToMapRec(schema *Schema, visited map[uintptr]bool) (map[string
if schema.Default != nil {
schemaMap["default"] = schema.Default
}
if schema.AdditionalProperties != nil {
schemaMap["additionalProperties"] = *schema.AdditionalProperties
}

// Arrays
if len(schema.Required) > 0 {
Expand Down
19 changes: 12 additions & 7 deletions pkg/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ func float64Ptr(f float64) *float64 {
return &f
}

func boolPtr(b bool) *bool {
return &b
}

// schemasEqual is a helper function to compare two Schema objects.
func schemasEqual(a, b *Schema) bool {
if a == nil || b == nil {
Expand Down Expand Up @@ -151,9 +155,9 @@ func TestMergeSchemas(t *testing.T) {
},
{
name: "object properties",
dest: &Schema{Type: "object", MinProperties: uint64Ptr(1), MaxProperties: uint64Ptr(10), PatternProperties: map[string]*Schema{"^.$": {Type: "string"}}},
src: &Schema{Type: "object", MinProperties: uint64Ptr(1), MaxProperties: uint64Ptr(10), PatternProperties: map[string]*Schema{"^.$": {Type: "string"}}},
want: &Schema{Type: "object", MinProperties: uint64Ptr(1), MaxProperties: uint64Ptr(10), PatternProperties: map[string]*Schema{"^.$": {Type: "string"}}},
dest: &Schema{Type: "object", MinProperties: uint64Ptr(1), MaxProperties: uint64Ptr(10), PatternProperties: map[string]*Schema{"^.$": {Type: "string"}}, AdditionalProperties: boolPtr(false)},
src: &Schema{Type: "object", MinProperties: uint64Ptr(1), MaxProperties: uint64Ptr(10), PatternProperties: map[string]*Schema{"^.$": {Type: "string"}}, AdditionalProperties: boolPtr(false)},
want: &Schema{Type: "object", MinProperties: uint64Ptr(1), MaxProperties: uint64Ptr(10), PatternProperties: map[string]*Schema{"^.$": {Type: "string"}}, AdditionalProperties: boolPtr(false)},
},
{
name: "meta-data properties",
Expand Down Expand Up @@ -214,7 +218,7 @@ func TestConvertSchemaToMap(t *testing.T) {
name: "with nested items",
schema: &Schema{
Type: "array",
Items: &Schema{Type: "string", MinLength: uint64Ptr(1), MaxLength: uint64Ptr(10)},
Items: &Schema{Type: "string", MinLength: uint64Ptr(1), MaxLength: uint64Ptr(10), AdditionalProperties: boolPtr(false)},
MinItems: uint64Ptr(1),
MaxItems: uint64Ptr(2),
UniqueItems: true,
Expand All @@ -223,9 +227,10 @@ func TestConvertSchemaToMap(t *testing.T) {
want: map[string]interface{}{
"type": "array",
"items": map[string]interface{}{
"type": "string",
"minLength": uint64(1),
"maxLength": uint64(10),
"type": "string",
"minLength": uint64(1),
"maxLength": uint64(10),
"additionalProperties": false,
},
"minItems": uint64(1),
"maxItems": uint64(2),
Expand Down
47 changes: 26 additions & 21 deletions pkg/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,28 @@ import (
)

type Schema struct {
Type interface{} `json:"type,omitempty"`
Enum []any `json:"enum,omitempty"`
MultipleOf *float64 `json:"multipleOf,omitempty"`
Maximum *float64 `json:"maximum,omitempty"`
Minimum *float64 `json:"minimum,omitempty"`
MaxLength *uint64 `json:"maxLength,omitempty"`
MinLength *uint64 `json:"minLength,omitempty"`
Pattern string `json:"pattern,omitempty"`
MaxItems *uint64 `json:"maxItems,omitempty"`
MinItems *uint64 `json:"minItems,omitempty"`
UniqueItems bool `json:"uniqueItems,omitempty"`
MaxProperties *uint64 `json:"maxProperties,omitempty"`
MinProperties *uint64 `json:"minProperties,omitempty"`
PatternProperties interface{} `json:"patternProperties,omitempty"`
Required []string `json:"required,omitempty"`
Items *Schema `json:"items,omitempty"`
Properties map[string]*Schema `json:"properties,omitempty"`
Title string `json:"title,omitempty"`
Description string `json:"description,omitempty"`
ReadOnly bool `json:"readOnly,omitempty"`
Default interface{} `json:"default,omitempty"`
Type interface{} `json:"type,omitempty"`
Enum []any `json:"enum,omitempty"`
MultipleOf *float64 `json:"multipleOf,omitempty"`
Maximum *float64 `json:"maximum,omitempty"`
Minimum *float64 `json:"minimum,omitempty"`
MaxLength *uint64 `json:"maxLength,omitempty"`
MinLength *uint64 `json:"minLength,omitempty"`
Pattern string `json:"pattern,omitempty"`
MaxItems *uint64 `json:"maxItems,omitempty"`
MinItems *uint64 `json:"minItems,omitempty"`
UniqueItems bool `json:"uniqueItems,omitempty"`
MaxProperties *uint64 `json:"maxProperties,omitempty"`
MinProperties *uint64 `json:"minProperties,omitempty"`
PatternProperties interface{} `json:"patternProperties,omitempty"`
Required []string `json:"required,omitempty"`
Items *Schema `json:"items,omitempty"`
Properties map[string]*Schema `json:"properties,omitempty"`
Title string `json:"title,omitempty"`
Description string `json:"description,omitempty"`
ReadOnly bool `json:"readOnly,omitempty"`
Default interface{} `json:"default,omitempty"`
AdditionalProperties *bool `json:"additionalProperties"`
}

func getKind(value string) string {
Expand Down Expand Up @@ -185,6 +186,10 @@ func processComment(schema *Schema, comment string) (isRequired bool) {
schema.Items = &Schema{
Type: value,
}
case "additionalProperties":
if v, err := strconv.ParseBool(value); err == nil {
schema.AdditionalProperties = &v
}
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/schema_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -304,8 +304,8 @@ func TestProcessComment(t *testing.T) {
{
name: "Set object",
schema: &Schema{},
comment: "# @schema minProperties:1;maxProperties:10",
expectedSchema: &Schema{MinProperties: uint64Ptr(1), MaxProperties: uint64Ptr(10)},
comment: "# @schema minProperties:1;maxProperties:10;additionalProperties:false",
expectedSchema: &Schema{MinProperties: uint64Ptr(1), MaxProperties: uint64Ptr(10), AdditionalProperties: boolPtr(false)},
expectedRequired: false,
},
{
Expand Down
1 change: 1 addition & 0 deletions testdata/full.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"affinity": {
"properties": {
"nodeAffinity": {
"additionalProperties": false,
"maxProperties": 2,
"minProperties": 1,
"properties": {
Expand Down
2 changes: 1 addition & 1 deletion testdata/full.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ tolerations: # @schema minItems: 1 ; maxItems: 10 ; uniqueItems: true

# Objects
affinity:
nodeAffinity: # @schema minProperties: 1 ; maxProperties: 2
nodeAffinity: # @schema minProperties: 1 ; maxProperties: 2 ; additionalProperties: false
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
Expand Down

0 comments on commit 28c13d4

Please sign in to comment.