Skip to content

Commit

Permalink
Merge pull request #91 from dadav/feat/add_minmax
Browse files Browse the repository at this point in the history
Feat/add minmax
  • Loading branch information
dadav authored Dec 7, 2024
2 parents e6d6416 + 21ff86a commit c4dd6e5
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 18 deletions.
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,8 @@ foo: bar
| [`$ref`](#ref) | Accepts an URI to a valid `jsonschema`. Extend the schema for the current key | Takes an URI (or relative file) |
| [`minLength`](#minlength) | Minimum string length. | Takes an `integer`. Must be smaller or equal than `maxLength` (if used) |
| [`maxLength`](#maxlength) | Maximum string length. | Takes an `integer`. Must be greater or equal than `minLength` (if used) |
| [`minItems`](#minItems) | Minimum length of an array. | Takes an `integer`. Must be smaller or equal than `maxItems` (if used) |
| [`maxItems`](#maxItems) | Maximum length of an array. | Takes an `integer`. Must be greater or equal than `minItems` (if used) |

## Validation & completion

Expand Down Expand Up @@ -716,6 +718,31 @@ The value must be an integer greater than zero and defines the maximum length of
namespace: foo
```

#### `minItems`

The value must be an integer greater than zero and defines the minimum length of an array value.

```yaml
# @schema
# minItems: 1
# @schema
namespace:
- foo
```

#### `maxItems`

The value must be an integer greater than zero and defines the maximum length of an array value.

```yaml
# @schema
# maxItems: 2
# @schema
namespace:
- foo
- bar
```

#### `$ref`

The value must be an URI or relative file.
Expand Down
53 changes: 35 additions & 18 deletions pkg/schema/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"fmt"
"io"
"os"
"reflect"
"regexp"
"slices"
"strconv"
Expand Down Expand Up @@ -247,6 +248,8 @@ type Schema struct {
CustomAnnotations map[string]interface{} `yaml:"-" json:",omitempty"`
MinLength *int `yaml:"minLength,omitempty" json:"minLength,omitempty"`
MaxLength *int `yaml:"maxLength,omitempty" json:"maxLength,omitempty"`
MinItems *int `yaml:"minItems,omitempty" json:"minItems,omitempty"`
MaxItems *int `yaml:"maxItems,omitempty" json:"maxItems,omitempty"`
}

func NewSchema(schemaType string) *Schema {
Expand All @@ -260,6 +263,17 @@ func NewSchema(schemaType string) *Schema {
}
}

func (s Schema) getJsonKeys() []string {
result := []string{}
t := reflect.TypeOf(s)

for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
result = append(result, field.Tag.Get("json"))
}
return result
}

// UnmarshalYAML custom unmarshal method
func (s *Schema) UnmarshalYAML(node *yaml.Node) error {
// Create an alias type to avoid recursion
Expand All @@ -276,32 +290,27 @@ func (s *Schema) UnmarshalYAML(node *yaml.Node) error {
// Initialize CustomAnnotations map
alias.CustomAnnotations = make(map[string]interface{})

knownKeys := s.getJsonKeys()

// Iterate through all node fields
for i := 0; i < len(node.Content)-1; i += 2 {
keyNode := node.Content[i]
valueNode := node.Content[i+1]
key := keyNode.Value

// Check if the key is a known field
switch key {
case "additionalProperties", "default", "then", "patternProperties", "properties",
"if", "minimum", "multipleOf", "exclusiveMaximum", "items", "exclusiveMinimum",
"maximum", "else", "pattern", "const", "$ref", "$schema", "$id", "format",
"description", "title", "type", "anyOf", "allOf", "oneOf", "requiredProperties",
"examples", "enum", "deprecated", "required", "not":
// Skip known fields
if slices.Contains(knownKeys, key) {
continue
default:
// Unmarshal unknown fields into the CustomAnnotations map
if !strings.HasPrefix(key, CustomAnnotationPrefix) {
continue
}
var value interface{}
if err := valueNode.Decode(&value); err != nil {
return err
}
alias.CustomAnnotations[key] = value
}

// Unmarshal unknown fields into the CustomAnnotations map
if !strings.HasPrefix(key, CustomAnnotationPrefix) {
continue
}
var value interface{}
if err := valueNode.Decode(&value); err != nil {
return err
}
alias.CustomAnnotations[key] = value
}

// Copy alias to the main struct
Expand Down Expand Up @@ -413,6 +422,14 @@ func (s Schema) Validate() error {
return fmt.Errorf("cant use items if type is %s. Use type=array", s.Type)
}

if (s.MinItems != nil || s.MaxItems != nil) && !s.Type.IsEmpty() && !s.Type.Matches("array") {
return fmt.Errorf("cant use minItems or maxItems if type is %s. Use type=array", s.Type)
}

if (s.MinItems != nil && s.MaxItems != nil) && *s.MaxItems < *s.MinItems {
return errors.New("minItems cant be greater than maxItems")
}

if s.Const != nil && !s.Type.IsEmpty() {
return errors.New("if your are using const, you can't use type")
}
Expand Down
24 changes: 24 additions & 0 deletions pkg/schema/schema_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,30 @@ func TestValidate(t *testing.T) {
# @schema`,
expectedValid: true,
},
{
comment: `
# @schema
# minItems: 1
# maxItems: 2
# @schema`,
expectedValid: true,
},
{
comment: `
# @schema
# minItems: 2
# maxItems: 1
# @schema`,
expectedValid: false,
},
{
comment: `
# @schema
# type: string
# minItems: 1
# @schema`,
expectedValid: false,
},
}

for _, test := range tests {
Expand Down

0 comments on commit c4dd6e5

Please sign in to comment.