From 84b8d9c0e180583b20397550fab94b85f0ac7539 Mon Sep 17 00:00:00 2001 From: Matthew Rose Date: Sat, 9 Oct 2021 16:30:02 +1000 Subject: [PATCH] feat: add initial parser package includes parsing functionality for name/description, inputs and outputs as well as relevant tests --- go.mod | 7 +- go.sum | 1 + pkg/parser/parser.go | 109 ++++++++++++++++++++++ pkg/parser/parser_test.go | 63 +++++++++++++ pkg/parser/testdata/inputs.yaml | 11 +++ pkg/parser/testdata/name_description.yaml | 2 + pkg/parser/testdata/outputs.yaml | 7 ++ pkg/types/composite_action.go | 16 ++++ pkg/types/input.go | 6 +- pkg/types/output.go | 4 +- 10 files changed, 220 insertions(+), 6 deletions(-) create mode 100644 pkg/parser/parser.go create mode 100644 pkg/parser/parser_test.go create mode 100644 pkg/parser/testdata/inputs.yaml create mode 100644 pkg/parser/testdata/name_description.yaml create mode 100644 pkg/parser/testdata/outputs.yaml diff --git a/go.mod b/go.mod index ec1e5a2..58eba2e 100644 --- a/go.mod +++ b/go.mod @@ -3,17 +3,22 @@ module github.com/matty-rose/gha-docs go 1.17 require ( + github.com/mitchellh/mapstructure v1.4.2 + github.com/pkg/errors v0.8.1 github.com/spf13/cobra v1.2.1 github.com/spf13/viper v1.9.0 + github.com/stretchr/testify v1.7.0 + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b ) require ( + github.com/davecgh/go-spew v1.1.1 // indirect github.com/fsnotify/fsnotify v1.5.1 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/magiconair/properties v1.8.5 // indirect - github.com/mitchellh/mapstructure v1.4.2 // indirect github.com/pelletier/go-toml v1.9.4 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/spf13/afero v1.6.0 // indirect github.com/spf13/cast v1.4.1 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect diff --git a/go.sum b/go.sum index f21ab1f..071f56b 100644 --- a/go.sum +++ b/go.sum @@ -236,6 +236,7 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM= github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= diff --git a/pkg/parser/parser.go b/pkg/parser/parser.go new file mode 100644 index 0000000..8f6d3c3 --- /dev/null +++ b/pkg/parser/parser.go @@ -0,0 +1,109 @@ +/* +Copyright © 2021 Matt Rose + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +package parser + +import ( + "fmt" + "io/ioutil" + + "github.com/matty-rose/gha-docs/pkg/types" + "github.com/mitchellh/mapstructure" + "github.com/pkg/errors" + "gopkg.in/yaml.v3" +) + +func Parse(filename string) (*types.CompositeAction, error) { + file, err := ioutil.ReadFile(filename) + if err != nil { + return nil, errors.Wrap(err, "couldn't read given yaml file") + } + + data := make(map[interface{}]interface{}) + + err = yaml.Unmarshal(file, &data) + if err != nil { + return nil, errors.Wrap(err, "failed unmarshalling yaml data") + } + + var action types.CompositeAction + + parseMetadata(&action, data) + + err = parseInputs(&action, data) + if err != nil { + return nil, err + } + + err = parseOutputs(&action, data) + if err != nil { + return nil, err + } + + return &action, nil +} + +func parseMetadata(action *types.CompositeAction, data map[interface{}]interface{}) { + action.SetName(data["name"].(string)) + action.SetDescription(data["description"].(string)) +} + +func parseInputs(action *types.CompositeAction, data map[interface{}]interface{}) error { + inputs, ok := data["inputs"].(map[string]interface{}) + if !ok { + // TODO: Replace with logrus + fmt.Println("no inputs found") + } + + for name, input := range inputs { + inp := types.Input{Name: name} + + err := mapstructure.Decode(input, &inp) + if err != nil { + return errors.Wrap(err, "failed parsing action input into struct") + } + + action.AddInput(inp) + } + + return nil +} + +func parseOutputs(action *types.CompositeAction, data map[interface{}]interface{}) error { + outputs, ok := data["outputs"].(map[string]interface{}) + if !ok { + // TODO: Replace with logrus + fmt.Println("no outputs found") + } + + for name, output := range outputs { + out := types.Output{Name: name} + + err := mapstructure.Decode(output, &out) + if err != nil { + return errors.Wrap(err, "failed parsing action output into struct") + } + + action.AddOutput(out) + } + + return nil +} diff --git a/pkg/parser/parser_test.go b/pkg/parser/parser_test.go new file mode 100644 index 0000000..bff52be --- /dev/null +++ b/pkg/parser/parser_test.go @@ -0,0 +1,63 @@ +/* +Copyright © 2021 Matt Rose + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +package parser_test + +import ( + "testing" + + "github.com/matty-rose/gha-docs/pkg/parser" + "github.com/stretchr/testify/assert" +) + +func TestParseNameDescription(t *testing.T) { + assertion := assert.New(t) + + action, err := parser.Parse("./testdata/name_description.yaml") + if err != nil { + t.Fatal(err) + } + + assertion.Equal("test", action.Name) + assertion.Equal("test", action.Description) +} + +func TestParseInputs(t *testing.T) { + assertion := assert.New(t) + + action, err := parser.Parse("./testdata/inputs.yaml") + if err != nil { + t.Fatal(err) + } + + assertion.Len(action.Inputs, 2) +} + +func TestParseOutputs(t *testing.T) { + assertion := assert.New(t) + + action, err := parser.Parse("./testdata/outputs.yaml") + if err != nil { + t.Fatal(err) + } + + assertion.Len(action.Outputs, 1) +} diff --git a/pkg/parser/testdata/inputs.yaml b/pkg/parser/testdata/inputs.yaml new file mode 100644 index 0000000..4084003 --- /dev/null +++ b/pkg/parser/testdata/inputs.yaml @@ -0,0 +1,11 @@ +name: "test" +description: "test" + +inputs: + input-a: + description: "hello" + required: false + default: "a" + input-b: + description: "hello" + required: true diff --git a/pkg/parser/testdata/name_description.yaml b/pkg/parser/testdata/name_description.yaml new file mode 100644 index 0000000..d3bdd65 --- /dev/null +++ b/pkg/parser/testdata/name_description.yaml @@ -0,0 +1,2 @@ +name: "test" +description: "test" diff --git a/pkg/parser/testdata/outputs.yaml b/pkg/parser/testdata/outputs.yaml new file mode 100644 index 0000000..a6f9dca --- /dev/null +++ b/pkg/parser/testdata/outputs.yaml @@ -0,0 +1,7 @@ +name: "test" +description: "test" + +outputs: + output-a: + description: "an output" + value: "b" diff --git a/pkg/types/composite_action.go b/pkg/types/composite_action.go index 3c4cc80..d35a693 100644 --- a/pkg/types/composite_action.go +++ b/pkg/types/composite_action.go @@ -29,3 +29,19 @@ type CompositeAction struct { Outputs []Output ThirdPartyActions []ThirdPartyAction } + +func (c *CompositeAction) SetName(name string) { + c.Name = name +} + +func (c *CompositeAction) SetDescription(description string) { + c.Description = description +} + +func (c *CompositeAction) AddInput(input Input) { + c.Inputs = append(c.Inputs, input) +} + +func (c *CompositeAction) AddOutput(output Output) { + c.Outputs = append(c.Outputs, output) +} diff --git a/pkg/types/input.go b/pkg/types/input.go index 0bd7b17..985da08 100644 --- a/pkg/types/input.go +++ b/pkg/types/input.go @@ -24,7 +24,7 @@ package types // Input represents a single input to a composite action. type Input struct { Name string - Description string - Required bool - Default string + Description string `mapstructure:"description"` + Required bool `mapstructure:"required"` + Default string `mapstructure:"default"` } diff --git a/pkg/types/output.go b/pkg/types/output.go index 8f1e358..deb9805 100644 --- a/pkg/types/output.go +++ b/pkg/types/output.go @@ -24,6 +24,6 @@ package types // Output represents a single output of a composite action. type Output struct { Name string - Description string - Value string + Description string `mapstructure:"description"` + Value string `mapstructure:"value"` }