From 21f5298e0b69fd1f7dac16304e5ad56051f4fb6e Mon Sep 17 00:00:00 2001 From: Aleksandar Stojanov Date: Mon, 3 Jun 2024 18:39:40 +0200 Subject: [PATCH 1/6] feature: add schemaRoot CLI flags for json root object Signed-off-by: Aleksandar Stojanov --- pkg/cmd.go | 5 +++++ pkg/cmd_test.go | 3 +++ pkg/generator.go | 9 ++++++--- pkg/generator_test.go | 5 +++++ pkg/parser.go | 6 ++++++ pkg/parser_test.go | 8 +++++--- pkg/schema.go | 3 +++ pkg/schema_test.go | 4 ++-- pkg/utils.go | 11 ++++++++++- testdata/full.schema.json | 1 + testdata/full.yaml | 2 +- 11 files changed, 47 insertions(+), 10 deletions(-) diff --git a/pkg/cmd.go b/pkg/cmd.go index 34ebaa6..f8bf802 100644 --- a/pkg/cmd.go +++ b/pkg/cmd.go @@ -18,6 +18,11 @@ func ParseFlags(progname string, args []string) (config *Config, output string, flags.IntVar(&conf.draft, "draft", 2020, "Draft version (4, 6, 7, 2019, or 2020)") flags.IntVar(&conf.indent, "indent", 4, "Indentation spaces (even number)") + // Nested SchemaRoot flags + flags.StringVar(&conf.SchemaRoot.ID, "schemaRoot.id", "", "JSON schema ID") + flags.StringVar(&conf.SchemaRoot.Title, "schemaRoot.title", "", "JSON schema title") + flags.StringVar(&conf.SchemaRoot.Description, "schemaRoot.description", "", "JSON schema description") + err = flags.Parse(args) if err != nil { fmt.Println("Usage: helm schema [-input STR] [-draft INT] [-output STR]") diff --git a/pkg/cmd_test.go b/pkg/cmd_test.go index 2656887..1f8ce5c 100644 --- a/pkg/cmd_test.go +++ b/pkg/cmd_test.go @@ -23,6 +23,9 @@ func TestParseFlagsPass(t *testing.T) { {[]string{"-input", "values.yaml", "-output", "my.schema.json", "-draft", "2019"}, Config{input: multiStringFlag{"values.yaml"}, outputPath: "my.schema.json", draft: 2019, indent: 4, args: []string{}}}, + + {[]string{"-input", "values.yaml", "-schemaRoot.id", "http://example.com/schema", "-schemaRoot.title", "MySchema", "-schemaRoot.description", "My schema description"}, + Config{input: multiStringFlag{"values.yaml"}, outputPath: "values.schema.json", draft: 2020, indent: 4, SchemaRoot: SchemaRoot{ID: "http://example.com/schema", Title: "MySchema", Description: "My schema description"}, args: []string{}}}, } for _, tt := range tests { diff --git a/pkg/generator.go b/pkg/generator.go index 2a9ff80..16555e8 100644 --- a/pkg/generator.go +++ b/pkg/generator.go @@ -62,9 +62,12 @@ 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, + Title: config.SchemaRoot.Title, + Description: config.SchemaRoot.Description, + ID: config.SchemaRoot.ID, } // Merge with existing data diff --git a/pkg/generator_test.go b/pkg/generator_test.go index d0733d3..0d0decc 100644 --- a/pkg/generator_test.go +++ b/pkg/generator_test.go @@ -18,6 +18,11 @@ func TestGenerateJsonSchema(t *testing.T) { outputPath: "../testdata/output.json", draft: 2020, indent: 4, + SchemaRoot: SchemaRoot{ + ID: "", + Title: "", + Description: "", + }, } err := GenerateJsonSchema(config) diff --git a/pkg/parser.go b/pkg/parser.go index 41bced7..3a4aaeb 100644 --- a/pkg/parser.go +++ b/pkg/parser.go @@ -68,6 +68,9 @@ func mergeSchemas(dest, src *Schema) *Schema { if src.AdditionalProperties != nil { dest.AdditionalProperties = src.AdditionalProperties } + if src.ID != "" { + dest.ID = src.ID + } // Merge 'enum' field (assuming that maintaining order doesn't matter) dest.Enum = append(dest.Enum, src.Enum...) @@ -173,6 +176,9 @@ func convertSchemaToMapRec(schema *Schema, visited map[uintptr]bool) (map[string if schema.AdditionalProperties != nil { schemaMap["additionalProperties"] = *schema.AdditionalProperties } + if schema.ID != "" { + schemaMap["$id"] = schema.ID + } // Arrays if len(schema.Required) > 0 { diff --git a/pkg/parser_test.go b/pkg/parser_test.go index e7b2e72..ee202ce 100644 --- a/pkg/parser_test.go +++ b/pkg/parser_test.go @@ -161,9 +161,9 @@ func TestMergeSchemas(t *testing.T) { }, { name: "meta-data properties", - dest: &Schema{Type: "object", Title: "My Title", Description: "My description", ReadOnly: true, Default: "default value"}, - src: &Schema{Type: "object", Title: "My Title", Description: "My description", ReadOnly: true, Default: "default value"}, - want: &Schema{Type: "object", Title: "My Title", Description: "My description", ReadOnly: true, Default: "default value"}, + dest: &Schema{Type: "object", Title: "My Title", Description: "My description", ReadOnly: true, Default: "default value", ID: "http://example.com/schema"}, + src: &Schema{Type: "object", Title: "My Title", Description: "My description", ReadOnly: true, Default: "default value", ID: "http://example.com/schema"}, + want: &Schema{Type: "object", Title: "My Title", Description: "My description", ReadOnly: true, Default: "default value", ID: "http://example.com/schema"}, }, } @@ -201,6 +201,7 @@ func TestConvertSchemaToMap(t *testing.T) { }, }, Required: []string{"foo"}, + ID: "http://example.com/schema", }, want: map[string]interface{}{ "type": "object", @@ -212,6 +213,7 @@ func TestConvertSchemaToMap(t *testing.T) { }, }, "required": []string{"foo"}, + "$id": "http://example.com/schema", }, }, { diff --git a/pkg/schema.go b/pkg/schema.go index 324d8cc..516b0e8 100644 --- a/pkg/schema.go +++ b/pkg/schema.go @@ -32,6 +32,7 @@ type Schema struct { ReadOnly bool `json:"readOnly,omitempty"` Default interface{} `json:"default,omitempty"` AdditionalProperties *bool `json:"additionalProperties"` + ID string `json:"$id,omitempty"` } func getKind(value string) string { @@ -190,6 +191,8 @@ func processComment(schema *Schema, comment string) (isRequired bool) { if v, err := strconv.ParseBool(value); err == nil { schema.AdditionalProperties = &v } + case "$id": + schema.ID = value } } } diff --git a/pkg/schema_test.go b/pkg/schema_test.go index c4421d1..a647bd6 100644 --- a/pkg/schema_test.go +++ b/pkg/schema_test.go @@ -304,8 +304,8 @@ func TestProcessComment(t *testing.T) { { name: "Set object", schema: &Schema{}, - comment: "# @schema minProperties:1;maxProperties:10;additionalProperties:false", - expectedSchema: &Schema{MinProperties: uint64Ptr(1), MaxProperties: uint64Ptr(10), AdditionalProperties: boolPtr(false)}, + comment: "# @schema minProperties:1;maxProperties:10;additionalProperties:false;$id:https://example.com/schema", + expectedSchema: &Schema{MinProperties: uint64Ptr(1), MaxProperties: uint64Ptr(10), AdditionalProperties: boolPtr(false), ID: "https://example.com/schema"}, expectedRequired: false, }, { diff --git a/pkg/utils.go b/pkg/utils.go index 7767528..696079b 100644 --- a/pkg/utils.go +++ b/pkg/utils.go @@ -2,6 +2,13 @@ package pkg import "strings" +// SchemaRoot struct defines root object of schema +type SchemaRoot struct { + ID string + Title string + Description string +} + // Save values of parsed flags in Config type Config struct { input multiStringFlag @@ -9,10 +16,12 @@ type Config struct { draft int indent int + SchemaRoot SchemaRoot + args []string } -// Define a custom flag type to accept multiple yamlFiles +// Define a custom flag type to accept multiple yaml files type multiStringFlag []string func (m *multiStringFlag) String() string { diff --git a/testdata/full.schema.json b/testdata/full.schema.json index 17a8bec..b036ff1 100644 --- a/testdata/full.schema.json +++ b/testdata/full.schema.json @@ -12,6 +12,7 @@ "items": { "properties": { "preference": { + "$id": "https://example.com/schema.json", "patternProperties": { "^[a-z]$": { "type": "string" diff --git a/testdata/full.yaml b/testdata/full.yaml index 52b8d9a..986af13 100644 --- a/testdata/full.yaml +++ b/testdata/full.yaml @@ -39,7 +39,7 @@ affinity: - antarctica-west1 preferredDuringSchedulingIgnoredDuringExecution: - weight: 1 - preference: # @schema patternProperties: {"^[a-z]$": {"type": "string"}} + preference: # @schema patternProperties: {"^[a-z]$": {"type": "string"}} ; $id: https://example.com/schema.json matchExpressions: - key: another-node-label-key operator: In From d4bbc89ef1ad16acfaaec155bce4ddec4cff29f7 Mon Sep 17 00:00:00 2001 From: Aleksandar Stojanov Date: Mon, 3 Jun 2024 18:40:47 +0200 Subject: [PATCH 2/6] add docs Signed-off-by: Aleksandar Stojanov --- README.md | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c22f77e..4571168 100644 --- a/README.md +++ b/README.md @@ -95,9 +95,15 @@ usage: helm schema [-input STR] [-draft INT] [-output STR] -indent int Indentation spaces (even number) (default 4) -input value - Multiple yamlFiles as inputs (comma-separated) + Multiple yaml files as inputs (comma-separated) -output string Output file path (default "values.schema.json") + -schemaRoot.description string + JSON schema description + -schemaRoot.id string + JSON schema ID + -schemaRoot.title string + JSON schema title ``` ### Basic @@ -207,6 +213,49 @@ Output will be something like this: } ``` +Adding ID, title and description to the schema: + +`basic.yaml` + +```yaml +image: + repository: nginx + tag: latest + pullPolicy: Always +``` + +```bash +$ helm schema -input basic.yaml -schemaRoot.id "https://example.com/schema" -schemaRoot.title "My schema" -schemaRoot.description "This is my schema" +``` + +Generated schema will be: + +```json +{ + "$id": "https://example.com/schema", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "This is my schema", + "properties": { + "image": { + "properties": { + "pullPolicy": { + "type": "string" + }, + "repository": { + "type": "string" + }, + "tag": { + "type": "string" + } + }, + "type": "object" + } + }, + "title": "My schema", + "type": "object" +} +``` + ## Issues, Features, Feedback Your input matters. Feel free to open [issues](https://github.com/losisin/helm-values-schema-json/issues) for bugs, feature requests, or any feedback you may have. Check if a similar issue exists before creating a new one, and please use clear titles and explanations to help understand your point better. Your thoughts help me improve this project! From b89d0ff0a7156839c757de4e16e2e45aa49129b5 Mon Sep 17 00:00:00 2001 From: Aleksandar Stojanov Date: Mon, 3 Jun 2024 18:57:22 +0200 Subject: [PATCH 3/6] clear doc headings Signed-off-by: Aleksandar Stojanov --- README.md | 4 ++++ docs/README.md | 33 ++++++++++++++++++++++++++++++--- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 4571168..646ed45 100644 --- a/README.md +++ b/README.md @@ -118,6 +118,8 @@ This will read `values.yaml`, set draft version to `2020-12` and save outpout to ### Extended +#### Multiple values files + Merge multiple values files, set json-schema draft version explicitly and save output to `my.schema.json`: `values_1.yaml` @@ -213,6 +215,8 @@ Output will be something like this: } ``` +#### Root JSON object properties + Adding ID, title and description to the schema: `basic.yaml` diff --git a/docs/README.md b/docs/README.md index 4cb37d9..db5a791 100644 --- a/docs/README.md +++ b/docs/README.md @@ -39,6 +39,7 @@ The following annotations are supported: * [required](#required) * [patternProperties](#patternproperties) * [additionalProperties](#additionalproperties) + * [$id](#$id) * [Meta-Data Annotations](#meta-data-annotations) * [title and description](#title-and-description) * [default](#default) @@ -417,6 +418,35 @@ image: # @schema additionalProperties: false } ``` +### $id + +String. [section 8.2.1](https://datatracker.ietf.org/doc/html/draft-bhutton-json-schema-00#section-8.2.1) + +```yaml +image: # @schema $id: https://example.com/schema.json + repository: nginx + tag: latest + pullPolicy: Always +``` + +```json +"image": { + "$id": "https://example.com/schema.json", + "properties": { + "pullPolicy": { + "type": "string" + }, + "repository": { + "type": "string" + }, + "tag": { + "type": "string" + } + }, + "type": "object" +} +``` + ## Meta-Data Annotations ### title and description @@ -427,9 +457,6 @@ String. [section 9.1](https://json-schema.org/draft/2020-12/json-schema-validati fullnameOverride: bar # @schema title: My title ; description: My description ``` -```json -``` - ```json "fullnameOverride": { "title": "My title", From 9e785bae9079dc2e4fc2f45cb5c21127c88ff7c5 Mon Sep 17 00:00:00 2001 From: Aleksandar Stojanov Date: Mon, 3 Jun 2024 18:58:21 +0200 Subject: [PATCH 4/6] fix typo in docs Signed-off-by: Aleksandar Stojanov --- docs/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/README.md b/docs/README.md index db5a791..45d7821 100644 --- a/docs/README.md +++ b/docs/README.md @@ -39,7 +39,7 @@ The following annotations are supported: * [required](#required) * [patternProperties](#patternproperties) * [additionalProperties](#additionalproperties) - * [$id](#$id) + * [$id](#id) * [Meta-Data Annotations](#meta-data-annotations) * [title and description](#title-and-description) * [default](#default) From 9aea121ecd44aab89f302fc3e49e63348dd97f8f Mon Sep 17 00:00:00 2001 From: Aleksandar Stojanov Date: Mon, 3 Jun 2024 19:00:29 +0200 Subject: [PATCH 5/6] fix some links Signed-off-by: Aleksandar Stojanov --- docs/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/README.md b/docs/README.md index 45d7821..942c0c6 100644 --- a/docs/README.md +++ b/docs/README.md @@ -374,7 +374,7 @@ image: ### patternProperties -JSON string added "AS IS" to the node. [documentation](https://json-schema.org/understanding-json-schema/reference/object#patternProperties) +JSON string added "AS IS" to the node. [section 10.3.2.2](https://datatracker.ietf.org/doc/html/draft-bhutton-json-schema-00#section-10.3.2.2) ```yaml image: # @schema patternProperties: {"^[a-z]$": {"type": "string"}} @@ -399,7 +399,7 @@ image: # @schema patternProperties: {"^[a-z]$": {"type": "string"}} ### additionalProperties -Boolean. [documentation](https://json-schema.org/understanding-json-schema/reference/object#additionalproperties) +Boolean. [section 10.3.2.3](https://datatracker.ietf.org/doc/html/draft-bhutton-json-schema-00#section-10.3.2.3) ```yaml image: # @schema additionalProperties: false From 7061ff6629c580e1b4eabbbb44f47c2a2b31b9a6 Mon Sep 17 00:00:00 2001 From: Aleksandar Stojanov Date: Tue, 4 Jun 2024 12:04:11 +0200 Subject: [PATCH 6/6] fix typo Signed-off-by: Aleksandar Stojanov --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c56ff6c..f32a3d1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,7 +4,7 @@ repos: hooks: - id: helm-schema args: - # Single or multiple yamlFiles as inputs (comma-separated) + # Single or multiple yaml files as inputs (comma-separated) - --input=values.yaml # Output file path (default "values.schema.json") - --output=values.schema.json