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

Add Schema examples annotation #580

Merged
merged 13 commits into from
Jan 24, 2022
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
190 changes: 190 additions & 0 deletions pkg/cmd/template/schema_author_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -756,6 +756,196 @@ schema.yml:
files.MustNewFileFromSource(files.NewBytesSource("schema.yml", []byte(schemaYAML))),
})

assertFails(t, filesToProcess, expectedErr, opts)
})
})
t.Run("when schema/examples annotation value", func(t *testing.T) {
t.Run("is empty", func(t *testing.T) {
schemaYAML := `#@data/values-schema
---
#@schema/examples
key: val
`
expectedErr := `
Invalid schema
==============

syntax error in @schema/examples annotation
schema.yml:
|
3 | #@schema/examples
4 | key: val
|

= found: missing value in @schema/examples (by schema.yml:3)
= expected: 2-tuple containing description (string) and example value (of expected type)
`

filesToProcess := files.NewSortedFiles([]*files.File{
files.MustNewFileFromSource(files.NewBytesSource("schema.yml", []byte(schemaYAML))),
})

assertFails(t, filesToProcess, expectedErr, opts)
})
t.Run("is not a tuple", func(t *testing.T) {
schemaYAML := `#@data/values-schema
---
#@schema/examples "example value"
key: val
`
expectedErr := `
Invalid schema
==============

syntax error in @schema/examples annotation
schema.yml:
|
3 | #@schema/examples "example value"
4 | key: val
|

= found: string for @schema/examples (at schema.yml:3)
= expected: 2-tuple containing description (string) and example value (of expected type)
`

filesToProcess := files.NewSortedFiles([]*files.File{
files.MustNewFileFromSource(files.NewBytesSource("schema.yml", []byte(schemaYAML))),
})

assertFails(t, filesToProcess, expectedErr, opts)
})
t.Run("is an empty tuple", func(t *testing.T) {
schemaYAML := `#@data/values-schema
---
#@schema/examples ()
key: val
`
expectedErr := `
Invalid schema
==============

syntax error in @schema/examples annotation
schema.yml:
|
3 | #@schema/examples ()
4 | key: val
|

= found: empty tuple in @schema/examples (by schema.yml:3)
= expected: 2-tuple containing description (string) and example value (of expected type)
`

filesToProcess := files.NewSortedFiles([]*files.File{
files.MustNewFileFromSource(files.NewBytesSource("schema.yml", []byte(schemaYAML))),
})

assertFails(t, filesToProcess, expectedErr, opts)
})
t.Run("has more than two args in an example", func(t *testing.T) {
schemaYAML := `#@data/values-schema
---
#@schema/examples ("key example", "val", "value")
key: val
`
expectedErr := `
Invalid schema
==============

syntax error in @schema/examples annotation
schema.yml:
|
3 | #@schema/examples ("key example", "val", "value")
4 | key: val
|

= found: 3-tuple argument in @schema/examples (by schema.yml:3)
= expected: 2-tuple containing description (string) and example value (of expected type)
`

filesToProcess := files.NewSortedFiles([]*files.File{
files.MustNewFileFromSource(files.NewBytesSource("schema.yml", []byte(schemaYAML))),
})

assertFails(t, filesToProcess, expectedErr, opts)
})
t.Run("example description is not a string", func(t *testing.T) {
schemaYAML := `#@data/values-schema
---
#@schema/examples (3.14, 5)
key: 7.3
`
expectedErr := `
Invalid schema
==============

syntax error in @schema/examples annotation
schema.yml:
|
3 | #@schema/examples (3.14, 5)
4 | key: 7.3
|

= found: float value for @schema/examples (at schema.yml:3)
= expected: 2-tuple containing description (string) and example value (of expected type)
`

filesToProcess := files.NewSortedFiles([]*files.File{
files.MustNewFileFromSource(files.NewBytesSource("schema.yml", []byte(schemaYAML))),
})

assertFails(t, filesToProcess, expectedErr, opts)
})
t.Run("is key=value pair", func(t *testing.T) {
schemaYAML := `#@data/values-schema
---
#@schema/examples key=True
key: val
`
expectedErr := `
Invalid schema
==============

syntax error in @schema/examples annotation
schema.yml:
|
3 | #@schema/examples key=True
4 | key: val
|

= found: keyword argument in @schema/examples (by schema.yml:3)
= expected: 2-tuple containing description (string) and example value (of expected type)
`

filesToProcess := files.NewSortedFiles([]*files.File{
files.MustNewFileFromSource(files.NewBytesSource("schema.yml", []byte(schemaYAML))),
})

assertFails(t, filesToProcess, expectedErr, opts)
})
t.Run("does not match type of annotated node", func(t *testing.T) {
schemaYAML := `#@data/values-schema
---
#@schema/examples ("Zero value", 0)
enabled: false
`
expectedErr := `Invalid schema - @schema/examples has wrong type
================================================

schema.yml:
|
3 | #@schema/examples ("Zero value", 0)
4 | enabled: false
|

= found: integer (by schema.yml:3)
= expected: boolean (by schema.yml:4)
= hint: is the default value set using @schema/default?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just note: the hint is misplaced and is fixed in #582. This is a new instance of the hint and will need to be removed after that PR is merged.

`

filesToProcess := files.NewSortedFiles([]*files.File{
files.MustNewFileFromSource(files.NewBytesSource("schema.yml", []byte(schemaYAML))),
})

assertFails(t, filesToProcess, expectedErr, opts)
})
})
Expand Down
121 changes: 112 additions & 9 deletions pkg/cmd/template/schema_inspect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,7 @@ components:
})

}

func TestSchemaInspect_annotation_adds_key(t *testing.T) {
t.Run("when description provided by @schema/desc", func(t *testing.T) {
opts := cmdtpl.NewOptions()
Expand Down Expand Up @@ -578,40 +579,40 @@ paths: {}
components:
schemas:
dataValues:
description: Network configuration values
type: object
additionalProperties: false
description: Network configuration values
properties:
db_conn:
type: array
description: List of database connections
type: array
items:
description: A network entry
type: object
additionalProperties: false
description: A network entry
properties:
hostname:
description: The hostname
type: string
default: ""
description: The hostname
port:
description: Port should be between 49152 through 65535
type: integer
default: 0
description: Port should be between 49152 through 65535
timeout:
description: Timeout in minutes
type: number
default: 1
format: float
description: Timeout in minutes
any_key:
description: Any type is allowed
nullable: true
default: thing
description: Any type is allowed
null_key:
description: When not provided, the default is null
type: string
default: null
nullable: true
description: When not provided, the default is null
default: []
`

Expand Down Expand Up @@ -685,16 +686,118 @@ components:
nullable: true
default: thing
null_key:
title: When not provided, the default is null
type: string
default: null
title: When not provided, the default is null
nullable: true
default: []
`
filesToProcess := files.NewSortedFiles([]*files.File{
files.MustNewFileFromSource(files.NewBytesSource("schema.yml", []byte(schemaYAML))),
})

assertSucceedsDocSet(t, filesToProcess, expected, opts)
})
t.Run("when examples are provided by @schema/examples", func(t *testing.T) {
opts := cmdtpl.NewOptions()
opts.DataValuesFlags.InspectSchema = true
opts.RegularFilesSourceOpts.OutputType.Types = []string{"openapi-v3"}

schemaYAML := `#@data/values-schema
#@schema/examples ("schema example description", {"db_conn": [{"hostname": "localhost", "port": 8080, "timeout": 4.2, "any_key": "anything", "null_key": None}]})
---
#@schema/examples ("db_conn example description", [{"hostname": "localhost", "port": 8080, "timeout": 4.2, "any_key": "anything", "null_key": None}])
db_conn:
#@schema/examples ("db_conn array example description", {"hostname": "localhost", "port": 8080, "timeout": 4.2, "any_key": "anything", "null_key": "not null"})
-
#@schema/examples ("hostname example description", "localhost")
#@schema/desc "The hostname"
hostname: ""
#@schema/examples ("",8080)
port: 0
#@schema/examples ("timeout example description", 4.2), ("another timeout ex desc", 5)
timeout: 1.0
#@schema/examples ("any_key example description", "anything")
#@schema/type any=True
any_key: thing
#@schema/examples ("null_key example description", None)
#@schema/nullable
null_key: ""
`
expected := `openapi: 3.0.0
info:
version: 0.1.0
title: Schema for data values, generated by ytt
paths: {}
components:
schemas:
dataValues:
x-example-description: schema example description
example:
db_conn:
- hostname: localhost
port: 8080
timeout: 4.2
any_key: anything
null_key: null
type: object
additionalProperties: false
properties:
db_conn:
x-example-description: db_conn example description
example:
- hostname: localhost
port: 8080
timeout: 4.2
any_key: anything
null_key: null
type: array
items:
x-example-description: db_conn array example description
example:
hostname: localhost
port: 8080
timeout: 4.2
any_key: anything
null_key: not null
type: object
additionalProperties: false
properties:
hostname:
description: The hostname
x-example-description: hostname example description
example: localhost
type: string
default: ""
port:
x-example-description: ""
example: 8080
type: integer
default: 0
timeout:
x-example-description: timeout example description
example: 4.2
type: number
default: 1
format: float
any_key:
x-example-description: any_key example description
example: anything
nullable: true
default: thing
null_key:
x-example-description: null_key example description
example: null
type: string
default: null
nullable: true
default: []
`

filesToProcess := files.NewSortedFiles([]*files.File{
files.MustNewFileFromSource(files.NewBytesSource("schema.yml", []byte(schemaYAML))),
})

assertSucceedsDocSet(t, filesToProcess, expected, opts)
})
}
Expand Down