Skip to content

Commit

Permalink
feat: add minimal working example using required fields only
Browse files Browse the repository at this point in the history
  • Loading branch information
Skarlso committed May 23, 2024
1 parent bdfb254 commit b38acf4
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 18 deletions.
4 changes: 3 additions & 1 deletion pkg/create_html_output.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,16 @@ func RenderContent(w io.Writer, crdContent []byte, comments bool) error {
return fmt.Errorf("failed to unmarshal into custom resource definition: %w", err)
}
versions := make([]Version, 0)
parser := NewParser(crd.Spec.Group, crd.Spec.Names.Kind, comments, true)

for _, version := range crd.Spec.Versions {
out, err := parseCRD(version.Schema.OpenAPIV3Schema.Properties, version.Name, version.Schema.OpenAPIV3Schema.Required)
if err != nil {
return fmt.Errorf("failed to parse properties: %w", err)
}
var buffer []byte
buf := bytes.NewBuffer(buffer)
if err := ParseProperties(crd.Spec.Group, version.Name, crd.Spec.Names.Kind, version.Schema.OpenAPIV3Schema.Properties, buf, 0, false, comments); err != nil {
if err := parser.ParseProperties(version.Name, buf, version.Schema.OpenAPIV3Schema.Properties); err != nil {
return fmt.Errorf("failed to generate yaml sample: %w", err)
}
versions = append(versions, Version{
Expand Down
54 changes: 40 additions & 14 deletions pkg/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ func Generate(crd *v1beta1.CustomResourceDefinition, w io.WriteCloser, enableCom
err = errors.Join(err, cerr)
}
}()

parser := NewParser(crd.Spec.Group, crd.Spec.Names.Kind, enableComments, false)
for i, version := range crd.Spec.Versions {
if err := ParseProperties(crd.Spec.Group, version.Name, crd.Spec.Names.Kind, version.Schema.OpenAPIV3Schema.Properties, w, 0, false, enableComments); err != nil {
if err := parser.ParseProperties(version.Name, w, version.Schema.OpenAPIV3Schema.Properties); err != nil {
return fmt.Errorf("failed to parse properties: %w", err)
}

Expand All @@ -46,10 +46,29 @@ func (w *writer) write(wc io.Writer, msg string) {
_, w.err = wc.Write([]byte(msg))
}

type Parser struct {
comments bool
inArray bool
indent int
group string
kind string
onlyRequired bool
}

// NewParser creates a new parser contains most of the things that do not change over each call.
func NewParser(group, kind string, comments, requiredOnly bool) *Parser {
return &Parser{
group: group,
kind: kind,
comments: comments,
onlyRequired: requiredOnly,
}
}

// ParseProperties takes a writer and puts out any information / properties it encounters during the runs.
// It will recursively parse every "properties:" and "additionalProperties:". Using the types, it will also output
// some sample data based on those types.
func ParseProperties(group, version, kind string, properties map[string]v1beta1.JSONSchemaProps, file io.Writer, indent int, inArray, comments bool) error {
func (p *Parser) ParseProperties(version string, file io.Writer, properties map[string]v1beta1.JSONSchemaProps) error {
sortedKeys := make([]string, 0, len(properties))
for k := range properties {
sortedKeys = append(sortedKeys, k)
Expand All @@ -58,60 +77,67 @@ func ParseProperties(group, version, kind string, properties map[string]v1beta1.

w := &writer{}
for _, k := range sortedKeys {
if inArray {
if p.inArray {
w.write(file, k+":")
inArray = false
p.inArray = false
} else {
if comments && properties[k].Description != "" {
if p.comments && properties[k].Description != "" {
comment := strings.Builder{}
multiLine := strings.Split(properties[k].Description, "\n")
for _, line := range multiLine {
comment.WriteString(fmt.Sprintf("%s# %s\n", strings.Repeat(" ", indent), line))
comment.WriteString(fmt.Sprintf("%s# %s\n", strings.Repeat(" ", p.indent), line))
}

w.write(file, comment.String())
}

w.write(file, fmt.Sprintf("%s%s:", strings.Repeat(" ", indent), k))
w.write(file, fmt.Sprintf("%s%s:", strings.Repeat(" ", p.indent), k))
}
switch {
case len(properties[k].Properties) == 0 && properties[k].AdditionalProperties == nil:
if k == "apiVersion" {
w.write(file, fmt.Sprintf(" %s/%s\n", group, version))
w.write(file, fmt.Sprintf(" %s/%s\n", p.group, version))

continue
}
if k == "kind" {
w.write(file, fmt.Sprintf(" %s\n", kind))
w.write(file, fmt.Sprintf(" %s\n", p.kind))

continue
}
// If we are dealing with an array, and we have properties to parse
// we need to reparse all of them again.
var result string
if properties[k].Type == array && properties[k].Items.Schema != nil && len(properties[k].Items.Schema.Properties) > 0 {
w.write(file, fmt.Sprintf("\n%s- ", strings.Repeat(" ", indent)))
if err := ParseProperties(group, version, kind, properties[k].Items.Schema.Properties, file, indent+2, true, comments); err != nil {
w.write(file, fmt.Sprintf("\n%s- ", strings.Repeat(" ", p.indent)))
p.indent += 2
p.inArray = true
if err := p.ParseProperties(version, file, properties[k].Items.Schema.Properties); err != nil {
return err
}
p.indent -= 2
} else {
result = outputValueType(properties[k])
w.write(file, fmt.Sprintf(" %s\n", result))
}
case len(properties[k].Properties) > 0:
w.write(file, "\n")
// recursively parse all sub-properties
if err := ParseProperties(group, version, kind, properties[k].Properties, file, indent+2, false, comments); err != nil {
p.indent += 2
if err := p.ParseProperties(version, file, properties[k].Properties); err != nil {
return err
}
p.indent -= 2
case properties[k].AdditionalProperties != nil:
if len(properties[k].AdditionalProperties.Schema.Properties) == 0 {
w.write(file, " {}\n")
} else {
w.write(file, "\n")
if err := ParseProperties(group, version, kind, properties[k].AdditionalProperties.Schema.Properties, file, indent+2, false, comments); err != nil {
p.indent += 2
if err := p.ParseProperties(version, file, properties[k].AdditionalProperties.Schema.Properties); err != nil {
return err
}
p.indent -= 2
}
}
}
Expand Down
46 changes: 44 additions & 2 deletions pkg/generate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ func TestGenerate(t *testing.T) {
buffer := bytes.NewBuffer(output)

version := crd.Spec.Versions[0]
require.NoError(t, ParseProperties(crd.Spec.Group, version.Name, crd.Spec.Names.Kind, version.Schema.OpenAPIV3Schema.Properties, buffer, 0, false, false))
parser := NewParser(crd.Spec.Group, crd.Spec.Names.Kind, false, false)
require.NoError(t, parser.ParseProperties(version.Name, buffer, version.Schema.OpenAPIV3Schema.Properties))

golden, err := os.ReadFile(filepath.Join("testdata", "sample_crd_golden.yaml"))
require.NoError(t, err)
Expand All @@ -41,11 +42,52 @@ func TestGenerateWithExample(t *testing.T) {
var output []byte
buffer := bytes.NewBuffer(output)

parser := NewParser(crd.Spec.Group, crd.Spec.Names.Kind, false, false)
version := crd.Spec.Versions[0]
require.NoError(t, ParseProperties(crd.Spec.Group, version.Name, crd.Spec.Names.Kind, version.Schema.OpenAPIV3Schema.Properties, buffer, 0, false, false))
require.NoError(t, parser.ParseProperties(version.Name, buffer, version.Schema.OpenAPIV3Schema.Properties))

golden, err := os.ReadFile(filepath.Join("testdata", "sample_crd_with_example_golden.yaml"))
require.NoError(t, err)

assert.Equal(t, golden, buffer.Bytes())
}

func TestGenerateWithComments(t *testing.T) {
content, err := os.ReadFile(filepath.Join("testdata", "sample_crd.yaml"))
require.NoError(t, err)

crd := &v1beta1.CustomResourceDefinition{}
require.NoError(t, yaml.Unmarshal(content, crd))

var output []byte
buffer := bytes.NewBuffer(output)

parser := NewParser(crd.Spec.Group, crd.Spec.Names.Kind, true, false)
version := crd.Spec.Versions[0]
require.NoError(t, parser.ParseProperties(version.Name, buffer, version.Schema.OpenAPIV3Schema.Properties))

golden, err := os.ReadFile(filepath.Join("testdata", "sample_crd_with_comments_golden.yaml"))
require.NoError(t, err)

assert.Equal(t, golden, buffer.Bytes())
}

func TestGenerateMinimal(t *testing.T) {
content, err := os.ReadFile(filepath.Join("testdata", "sample_crd.yaml"))
require.NoError(t, err)

crd := &v1beta1.CustomResourceDefinition{}
require.NoError(t, yaml.Unmarshal(content, crd))

var output []byte
buffer := bytes.NewBuffer(output)

parser := NewParser(crd.Spec.Group, crd.Spec.Names.Kind, false, true)
version := crd.Spec.Versions[0]
require.NoError(t, parser.ParseProperties(version.Name, buffer, version.Schema.OpenAPIV3Schema.Properties))

golden, err := os.ReadFile(filepath.Join("testdata", "sample_crd_with_minimal_example_golden.yaml"))
require.NoError(t, err)

assert.Equal(t, golden, buffer.Bytes())
}
25 changes: 25 additions & 0 deletions pkg/testdata/sample_crd_with_comments_golden.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
apiVersion: delivery.krok.app/v1alpha1
# Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
kind: KrokCommand
metadata: {}
# KrokCommandSpec defines the desired state of KrokCommand
spec:
# CommandHasOutputToWrite if defined, it signals the underlying Job, to put its output into a generated and created secret.
commandHasOutputToWrite: true
# Dependencies defines a list of command names that this command depends on.
dependencies: ["string"]
# Enabled defines if this command can be executed or not.
enabled: true
# Image defines the image name and tag of the command example: krok-hook/slack-notification:v0.0.1
image: string
# Platforms holds all the platforms which this command supports.
platforms: ["string"]
# ReadInputFromSecret if defined, the command will take a list of key/value pairs in a secret and apply them as arguments to the command.
readInputFromSecret:
name: string
namespace: string
# Schedule of the command. example: 0 * * * * // follows cron job syntax.
schedule: string
# KrokCommandStatus defines the observed state of KrokCommand
status: {}
4 changes: 4 additions & 0 deletions pkg/testdata/sample_crd_with_minimal_example_golden.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
apiVersion: delivery.krok.app/v1alpha1
kind: KrokCommand
spec:
image: "krok-hook/slack-notification:v0.0.1"
3 changes: 2 additions & 1 deletion wasm/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,15 @@ func (h *crdView) Render() app.UI {
}

versions := make([]Version, 0)
parser := pkg.NewParser(crd.Spec.Group, crd.Spec.Names.Kind, h.comment)
for _, version := range crd.Spec.Versions {
out, err := parseCRD(version.Schema.OpenAPIV3Schema.Properties, version.Name, version.Schema.OpenAPIV3Schema.Required)
if err != nil {
return h.buildError(err)
}
var buffer []byte
buf := bytes.NewBuffer(buffer)
if err := pkg.ParseProperties(crd.Spec.Group, version.Name, crd.Spec.Names.Kind, version.Schema.OpenAPIV3Schema.Properties, buf, 0, false, h.comment); err != nil {
if err := parser.ParseProperties(version.Name, buf, version.Schema.OpenAPIV3Schema.Properties); err != nil {
return h.buildError(err)
}
versions = append(versions, Version{
Expand Down

0 comments on commit b38acf4

Please sign in to comment.