diff --git a/README.md b/README.md index 37350c8..a888122 100644 --- a/README.md +++ b/README.md @@ -93,6 +93,8 @@ usage: helm schema [-input STR] [-draft INT] [-output STR] Draft version (4, 6, 7, 2019, or 2020) (default 2020) -input value Multiple yamlFiles as inputs (comma-separated) + -noAdditionalProperties + Set this flag to disallow additional properties -output string Output file path (default "values.schema.json") ``` diff --git a/pkg/cmd.go b/pkg/cmd.go index 40ba500..1b426f5 100644 --- a/pkg/cmd.go +++ b/pkg/cmd.go @@ -16,6 +16,7 @@ func ParseFlags(progname string, args []string) (config *Config, output string, flags.Var(&conf.input, "input", "Multiple yaml files as inputs (comma-separated)") flags.StringVar(&conf.outputPath, "output", "values.schema.json", "Output file path") flags.IntVar(&conf.draft, "draft", 2020, "Draft version (4, 6, 7, 2019, or 2020)") + flags.BoolVar(&conf.noAdditionalProperties, "noAdditionalProperties", false, "Set this flag to disallow additional properties") err = flags.Parse(args) if err != nil { diff --git a/pkg/generator.go b/pkg/generator.go index 31a5b99..06e9337 100644 --- a/pkg/generator.go +++ b/pkg/generator.go @@ -52,9 +52,10 @@ func GenerateJsonSchema(config *Config) error { // Create a temporary Schema to merge from the nodes tempSchema := &Schema{ - Type: "object", - Properties: properties, - Required: required, + Type: "object", + Properties: properties, + Required: required, + AdditionalProperties: &config.noAdditionalProperties, } // Merge with existing data @@ -63,7 +64,7 @@ func GenerateJsonSchema(config *Config) error { } // Convert merged Schema into a JSON Schema compliant map - jsonSchemaMap, err := convertSchemaToMap(mergedSchema) + jsonSchemaMap, err := convertSchemaToMap(mergedSchema, config.noAdditionalProperties) if err != nil { return err } diff --git a/pkg/parser.go b/pkg/parser.go index 815bdc0..edbdcee 100644 --- a/pkg/parser.go +++ b/pkg/parser.go @@ -85,11 +85,11 @@ func mergeSchemas(dest, src *Schema) *Schema { return dest } -func convertSchemaToMap(schema *Schema) (map[string]interface{}, error) { - return convertSchemaToMapRec(schema, make(map[uintptr]bool)) +func convertSchemaToMap(schema *Schema, noAdditionalProperties bool) (map[string]interface{}, error) { + return convertSchemaToMapRec(schema, make(map[uintptr]bool), noAdditionalProperties) } -func convertSchemaToMapRec(schema *Schema, visited map[uintptr]bool) (map[string]interface{}, error) { +func convertSchemaToMapRec(schema *Schema, visited map[uintptr]bool, noAdditionalProperties bool) (map[string]interface{}, error) { if schema == nil { return nil, nil } @@ -106,6 +106,11 @@ func convertSchemaToMapRec(schema *Schema, visited map[uintptr]bool) (map[string schemaMap := make(map[string]interface{}) + // Apply additionalProperties: false if noAdditionalProperties is true + if noAdditionalProperties && schema.Type == "object" { + schemaMap["additionalProperties"] = false + } + // Scalars if schema.Type != "" { schemaMap["type"] = schema.Type @@ -160,7 +165,7 @@ func convertSchemaToMapRec(schema *Schema, visited map[uintptr]bool) (map[string // Nested Schemas if schema.Items != nil { - itemsMap, err := convertSchemaToMapRec(schema.Items, visited) + itemsMap, err := convertSchemaToMapRec(schema.Items, visited, noAdditionalProperties) if err != nil { return nil, err } @@ -169,7 +174,7 @@ func convertSchemaToMapRec(schema *Schema, visited map[uintptr]bool) (map[string if schema.Properties != nil { propertiesMap := make(map[string]interface{}) for propName, propSchema := range schema.Properties { - propMap, err := convertSchemaToMapRec(propSchema, visited) + propMap, err := convertSchemaToMapRec(propSchema, visited, noAdditionalProperties) if err != nil { return nil, err } diff --git a/pkg/parser_test.go b/pkg/parser_test.go index cbc9a27..d4e2481 100644 --- a/pkg/parser_test.go +++ b/pkg/parser_test.go @@ -257,7 +257,7 @@ func TestConvertSchemaToMap(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := convertSchemaToMap(tt.schema) + got, err := convertSchemaToMap(tt.schema, false) if (err != nil) != tt.wantErr { t.Errorf("convertSchemaToMap() error = %v, wantErr %v", err, tt.wantErr) return @@ -304,7 +304,7 @@ func TestConvertSchemaToMapFail(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - _, err := convertSchemaToMap(tc.schema) + _, err := convertSchemaToMap(tc.schema, false) tc.expectedErr(t, err) }) } diff --git a/pkg/schema.go b/pkg/schema.go index 20cb4a5..f1f9870 100644 --- a/pkg/schema.go +++ b/pkg/schema.go @@ -9,24 +9,25 @@ 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"` - Required []string `json:"required,omitempty"` - Items *Schema `json:"items,omitempty"` - Properties map[string]*Schema `json:"properties,omitempty"` - Title string `json:"title,omitempty"` - ReadOnly bool `json:"readOnly,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"` + Required []string `json:"required,omitempty"` + Items *Schema `json:"items,omitempty"` + Properties map[string]*Schema `json:"properties,omitempty"` + Title string `json:"title,omitempty"` + ReadOnly bool `json:"readOnly,omitempty"` + AdditionalProperties *bool `json:"additionalProperties,omitempty"` } func getKind(value string) string { diff --git a/pkg/utils.go b/pkg/utils.go index d4341f5..c5debb2 100644 --- a/pkg/utils.go +++ b/pkg/utils.go @@ -4,9 +4,10 @@ import "strings" // Save values of parsed flags in Config type Config struct { - input multiStringFlag - outputPath string - draft int + input multiStringFlag + outputPath string + draft int + noAdditionalProperties bool args []string }