feat: aggregate defaults and values

- Output merged defaults and values
- Add "check" command
This commit is contained in:
2022-09-27 22:20:44 +02:00
committed by Bornholm
parent 53b2bba28b
commit f4b3d8f532
26 changed files with 3229 additions and 65 deletions

49
internal/command/check.go Normal file
View File

@ -0,0 +1,49 @@
package command
import (
"fmt"
"github.com/pkg/errors"
"github.com/santhosh-tekuri/jsonschema/v5"
_ "github.com/santhosh-tekuri/jsonschema/v5/httploader"
"github.com/urfave/cli/v2"
)
func Check() *cli.Command {
flags := []cli.Flag{}
flags = append(flags, commonFlags()...)
return &cli.Command{
Name: "check",
Usage: "Check values with the given schema",
Flags: flags,
Action: func(ctx *cli.Context) error {
schema, err := loadSchema(ctx)
if err != nil {
return errors.Wrap(err, "could not load schema")
}
_, values, err := loadData(ctx)
if err != nil {
return errors.Wrap(err, "could not load data")
}
if err := schema.Validate(values); err != nil {
if _, ok := err.(*jsonschema.ValidationError); ok {
fmt.Printf("%#v\n", err)
return errors.New("invalid values")
}
return errors.Wrap(err, "could not validate values")
}
if err := outputValues(ctx, values); err != nil {
return errors.Wrap(err, "could not output updated values")
}
return nil
},
}
}

View File

@ -0,0 +1,87 @@
package command
import (
"flag"
"testing"
"github.com/pkg/errors"
"github.com/urfave/cli/v2"
)
type ExpectFunc func(t *testing.T, cmd *cli.Command, err error)
type checkCommandTestCase struct {
Name string
SchemaFile string
DefaultFile string
ValuesFile string
Expect ExpectFunc
}
var checkCommandTestCases = []checkCommandTestCase{
{
Name: "ok",
SchemaFile: "file://testdata/check/schema.json",
DefaultFile: "file://testdata/check/defaults.json",
ValuesFile: "file://testdata/check/values-ok.json",
Expect: expectNoError,
},
{
Name: "nok",
SchemaFile: "file://testdata/check/schema.json",
DefaultFile: "file://testdata/check/defaults.json",
ValuesFile: "file://testdata/check/values-nok.json",
Expect: expectError,
},
}
func TestCheck(t *testing.T) {
t.Parallel()
for _, tc := range checkCommandTestCases {
func(tc *checkCommandTestCase) {
t.Run(tc.Name, func(t *testing.T) {
t.Parallel()
flags := flag.NewFlagSet("", flag.ExitOnError)
cmd := Check()
for _, f := range cmd.Flags {
if err := f.Apply(flags); err != nil {
t.Fatal(errors.WithStack(err))
}
}
err := flags.Parse([]string{
"check",
"--schema", tc.SchemaFile,
"--defaults", tc.DefaultFile,
"--values", tc.ValuesFile,
"--output", "null://local?format=json",
})
if err != nil {
t.Fatal(errors.WithStack(err))
}
app := cli.NewApp()
ctx := cli.NewContext(app, flags, nil)
err = cmd.Run(ctx)
tc.Expect(t, cmd, err)
})
}(&tc)
}
}
func expectNoError(t *testing.T, cmd *cli.Command, err error) {
if err != nil {
t.Error(errors.Wrap(err, "the command result in an unexpected error"))
}
}
func expectError(t *testing.T, cmd *cli.Command, err error) {
if err == nil {
t.Error(errors.New("an error should have been returned"))
}
}

View File

@ -5,6 +5,7 @@ import (
"io"
"net/url"
"os"
"reflect"
encjson "encoding/json"
@ -20,6 +21,7 @@ import (
"forge.cadoles.com/wpetit/formidable/internal/data/updater/null"
"forge.cadoles.com/wpetit/formidable/internal/data/updater/stdout"
"forge.cadoles.com/wpetit/formidable/internal/def"
"forge.cadoles.com/wpetit/formidable/internal/merge"
"github.com/pkg/errors"
"github.com/santhosh-tekuri/jsonschema/v5"
"github.com/urfave/cli/v2"
@ -113,6 +115,46 @@ func loadDefaults(ctx *cli.Context) (interface{}, error) {
return defaults, nil
}
func loadData(ctx *cli.Context) (defaults interface{}, values interface{}, err error) {
values, err = loadValues(ctx)
if err != nil {
return nil, nil, errors.Wrap(err, "could not load values")
}
defaults, err = loadDefaults(ctx)
if err != nil {
return nil, nil, errors.Wrap(err, "could not load defaults")
}
merged, err := getMatchingZeroValue(values)
if err != nil {
return nil, nil, errors.WithStack(err)
}
if defaults != nil {
if err := merge.Merge(&merged, defaults, values); err != nil {
return nil, nil, errors.Wrap(err, "could not merge values")
}
values = merged
}
return defaults, values, nil
}
func getMatchingZeroValue(values interface{}) (interface{}, error) {
valuesKind := reflect.TypeOf(values).Kind()
switch valuesKind {
case reflect.Map:
return make(map[string]interface{}, 0), nil
case reflect.Slice:
return make([]interface{}, 0), nil
default:
return nil, errors.Errorf("unexpected type '%T'", values)
}
}
func loadSchema(ctx *cli.Context) (*jsonschema.Schema, error) {
schemaFlag := ctx.String("schema")

View File

@ -55,14 +55,9 @@ func Edit() *cli.Command {
return errors.Wrap(err, "could not load schema")
}
values, err := loadValues(ctx)
defaults, values, err := loadData(ctx)
if err != nil {
return errors.Wrap(err, "could not load values")
}
defaults, err := loadDefaults(ctx)
if err != nil {
return errors.Wrap(err, "could not load defaults")
return errors.Wrap(err, "could not load data")
}
srvCtx, srvCancel := context.WithCancel(ctx.Context)

View File

@ -26,9 +26,9 @@ func Get() *cli.Command {
return errors.Wrap(err, "could not load schema")
}
values, err := loadValues(ctx)
_, values, err := loadData(ctx)
if err != nil {
return errors.Wrap(err, "could not load values")
return errors.Wrap(err, "could not load data")
}
if err := schema.Validate(values); err != nil {

View File

@ -8,5 +8,6 @@ func Root() []*cli.Command {
Set(),
Get(),
Delete(),
Check(),
}
}

View File

@ -33,9 +33,9 @@ func Set() *cli.Command {
return errors.Wrap(err, "could not load schema")
}
values, err := loadValues(ctx)
_, values, err := loadData(ctx)
if err != nil {
return errors.Wrap(err, "could not load values")
return errors.Wrap(err, "could not load data")
}
rawPointer := ctx.Args().Get(0)

View File

@ -0,0 +1,5 @@
{
"foo": {
"bar": "test"
}
}

View File

@ -0,0 +1,17 @@
{
"$schema": "http://json-schema.org/draft-07/schema",
"type": "object",
"properties": {
"foo": {
"type": "object",
"properties": {
"bar": {
"type": "string"
}
},
"required": ["bar"]
}
},
"required": ["foo"],
"additionalProperties": true
}

View File

@ -0,0 +1,5 @@
{
"foo": {
"bar": false
}
}

View File

@ -0,0 +1,3 @@
{
"test": 1
}