Skip to content
Permalink
Browse files
switch to using Kubernetes jsonpath package
  • Loading branch information
geomacy committed Aug 14, 2019
1 parent 9a5fa7d commit 496e632b3704883bc7381b34519744481dad631e
Showing 32 changed files with 2,353 additions and 3,050 deletions.

Some generated files are not rendered by default. Learn more.

@@ -16,8 +16,8 @@
# under the License.
#
[[constraint]]
name = "github.com/NodePrime/jsonpath"
source = "git@github.com:johnmccabe/jsonpath.git"
name = "k8s.io/client-go"
version = "12.0.0"

[[constraint]]
name = "github.com/urfave/cli"
@@ -38,7 +38,7 @@ var appConfig = configDefaults{
Name: os.Args[0],
HelpName: os.Args[0],
Usage: "A Brooklyn command line client application",
Version: "1.0.0-SNAPSHOT", // BROOKLYN_VERSION
Version: "1.0.0-SNAPSHOT", // BROOKLYN_VERSION
}

func NewApp(baseName string, cmdRunner command_runner.Runner, metadatas ...command_metadata.CommandMetadata) (app *cli.App) {
@@ -58,14 +58,18 @@ func NewApp(baseName string, cmdRunner command_runner.Runner, metadatas ...comma
},
cli.StringFlag{
Name: "json, j",
Usage: "Render value as json with json path selector as described at https://github.com/NodePrime/jsonpath. (Experimental, not supported on all commands at present) ",
Usage: "Render value as json with json path selector. (Experimental, not supported on all commands at present) ",
},
cli.BoolFlag {
Name: "verbose",
cli.BoolFlag{
Name: "raw-output, r",
Usage: "Used with --json; if result is a string, write it without quotes",
},
cli.BoolFlag{
Name: "verbose",
Usage: "Print HTTP requests and responses",
},
cli.BoolFlag {
Name: "vverbose",
cli.BoolFlag{
Name: "vverbose",
Usage: "Print HTTP requests and responses and include body data",
},
}
@@ -58,14 +58,18 @@ func assertItemSummaryFields(t *testing.T, expected models.CatalogItemSummary, a
assert.Equal(t, expected.Config[0].Pinned, actual.Config[0].Pinned, "Config[0].Pinned")
}

func testDisplay(t *testing.T, fn func(c *cli.Context) error, testPath string) string {
func testInApp(t *testing.T, fn func(c *cli.Context) error, args ...string) string {

testApp := cli.NewApp()
testApp.Flags = []cli.Flag{
cli.StringFlag{
Name: "json, j",
Usage: "Render value as json with json path selector as described at https://github.com/NodePrime/jsonpath. (Experimental, not supported on all commands at present) ",
},
cli.BoolFlag{
Name: "raw-output, r",
Usage: "Used with --json; if result is a string, write it without quotes",
},
}
var out string
var err error
@@ -75,19 +79,24 @@ func testDisplay(t *testing.T, fn func(c *cli.Context) error, testPath string) s
})
}

err = testApp.Run([]string{"", "--json", testPath})
// prepend `br` so args are the same as in the CLI
args = append([]string{"br"}, args...)
runErr := testApp.Run(args)
if runErr != nil {
t.Fatalf("Error from Run: %s\n", runErr)
}
if err != nil {
t.Fatalf("Error from Run: %s\n", err)
t.Fatalf("Error from display: %s\n", err)
}

return out
}

func TestCatalogItemSummaryDisplay(t *testing.T) {
expected := testCatalogEntitySummary()
displayOutput := testDisplay(t, func(c *cli.Context) error {
displayOutput := testInApp(t, func(c *cli.Context) error {
return expected.Display(c)
}, "$")
}, "--raw-output", "--json", "$")
var actual models.CatalogItemSummary
actualBytes := []byte(displayOutput)
unmarshalErr := json.Unmarshal(actualBytes, &actual)
@@ -99,9 +108,9 @@ func TestCatalogItemSummaryDisplay(t *testing.T) {
func TestCatalogEntitySummaryDisplay(t *testing.T) {

expected := testCatalogEntitySummary()
displayOutput := testDisplay(t, func(c *cli.Context) error {
displayOutput := testInApp(t, func(c *cli.Context) error {
return expected.Display(c)
}, "$")
}, "--raw-output", "--json", "$")

var actual models.CatalogEntitySummary
actualBytes := []byte(displayOutput)
@@ -112,6 +121,36 @@ func TestCatalogEntitySummaryDisplay(t *testing.T) {
assert.Equal(t, expected.IconUrl, actual.IconUrl, "iconUrl")
}

func TestPathsRaw(t *testing.T) {
type pathTest struct {
testPath string
expected string
}

testObject := testCatalogEntitySummary()

tests := []pathTest{
{"$.name", testObject.Name},
{"$.id", testObject.Id},
{"$.version", testObject.Version},
{"$.javaType", testObject.JavaType},
{"$.iconUrl", testObject.IconUrl},
{"$.config[0].name", testObject.Config[0].Name},
{"$.config[0].pinned", fmt.Sprintf("%t", testObject.Config[0].Pinned)},
}

for _, test := range tests {
actual := testInApp(t, func(c *cli.Context) error {
return testObject.Display(c)
}, "--raw-output", "--json", test.testPath)
assert.Equal(t, test.expected, actual, fmt.Sprintf("path %s", test.testPath))
}
}

func q(s string) string {
return fmt.Sprintf(`"%s"`, s)
}

func TestPaths(t *testing.T) {
type pathTest struct {
testPath string
@@ -121,14 +160,18 @@ func TestPaths(t *testing.T) {
testObject := testCatalogEntitySummary()

tests := []pathTest{
{"$.name+", testObject.Name},
{"$.id+", testObject.Id},
{"$.name", q(testObject.Name)},
{"$.id", q(testObject.Id)},
{"$.version", q(testObject.Version)},
{"$.javaType", q(testObject.JavaType)},
{"$.iconUrl", q(testObject.IconUrl)},
{"$.config[0].name", q(testObject.Config[0].Name)},
}

for _, test := range tests {
actual := testDisplay(t, func(c *cli.Context) error {
actual := testInApp(t, func(c *cli.Context) error {
return testObject.Display(c)
}, test.testPath)
}, "--json", test.testPath)
assert.Equal(t, test.expected, actual, fmt.Sprintf("path %s", test.testPath))
}
}
@@ -22,10 +22,14 @@ import (
"bytes"
"encoding/json"
"fmt"
"github.com/NodePrime/jsonpath"
"github.com/apache/brooklyn-client/cli/terminal"
"github.com/urfave/cli"
"io"
"k8s.io/client-go/util/jsonpath"
"os"
"reflect"
"strconv"
"strings"
)

type IdentityDetails struct {
@@ -73,7 +77,8 @@ func createTableWithIdentityDetails(item IdentityDetails) terminal.Table {
func (summary *CatalogItemSummary) Display(c *cli.Context) (err error) {

if json := c.GlobalString("json"); json != "" {
displayAsJson(summary, json)
raw := c.GlobalBool("raw-output")
displayAsJson(summary, json, raw)
} else {
summary.displayAsTable()
}
@@ -83,9 +88,16 @@ func (summary *CatalogItemSummary) Display(c *cli.Context) (err error) {
func (summary *CatalogEntitySummary) Display(c *cli.Context) (err error) {

if json := c.GlobalString("json"); json != "" {
displayAsJson(summary, json)
raw := c.GlobalBool("raw-output")
err := displayAsJson(summary, json, raw)
if err != nil {
return fmt.Errorf("display error: %s\n", err)
}
} else {
summary.displayAsTable()
err := summary.displayAsTable()
if err != nil {
return fmt.Errorf("display error: %s\n", err)
}
}
return err
}
@@ -156,35 +168,50 @@ func (summary *CatalogEntitySummary) displayAsTable() (err error) {
return err
}

func displayAsJson(v interface{}, jsonPath string) (err error) {
marshalled, err := json.Marshal(v)
if err != nil {
return err
}
// a convenient special case
if "$" == jsonPath {
fmt.Printf("%s", string(marshalled))
return nil
func resultsBackToJson(wr io.Writer, values []reflect.Value, raw bool) error {
for i, r := range values {
object := r.Interface()

jsonText, err := json.Marshal(object)
if err != nil {
return fmt.Errorf("error converting object to JSON: %s", err)
}

if r.Kind() == reflect.String && raw {
stringWithQuotes := string(jsonText)
trimLeft := strings.TrimPrefix(stringWithQuotes, `"`)
withoutQuotes := strings.TrimSuffix(trimLeft, `"`)
jsonText = []byte(withoutQuotes)
}

if i != len(values)-1 {
jsonText = append(jsonText, ' ')
}
if _, err := wr.Write(jsonText); err != nil {
return err
}
}
return nil
}

paths, err := jsonpath.ParsePaths(jsonPath)
func displayAsJson(v interface{}, displayPath string, raw bool) (err error) {
j := jsonpath.New("displayer")
j.AllowMissingKeys(true)

// wrap the path with k8s.io's conventional {} braces for convenience
err = j.Parse(fmt.Sprintf("{%s}", displayPath))
if err != nil {
return err
return fmt.Errorf("could not parse JSONPath expression (%s)", err)
}
eval, err := jsonpath.EvalPathsInBytes(marshalled, paths)

allResults, err := j.FindResults(v)
if err != nil {
return err
return fmt.Errorf("error evaluating JSONPath expression: %s", err)
}
for {
if result, ok := eval.Next(); ok {
pretty := result.Pretty(false) // true -> show keys in pretty string
fmt.Print(pretty)
} else {
break
for ix := range allResults {
if err = resultsBackToJson(os.Stdout, allResults[ix], raw); err != nil {
return err
}
}
if eval.Error != nil {
return eval.Error
}
return nil
}

This file was deleted.

This file was deleted.

This file was deleted.

0 comments on commit 496e632

Please sign in to comment.