diff --git a/Makefile b/Makefile index 8a57b10..f282bca 100644 --- a/Makefile +++ b/Makefile @@ -18,30 +18,52 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -.PHONY: all clean test gobin build upx +.PHONY: all clean test verify build version := $(shell git describe --tags --abbrev=0 2>/dev/null || (git rev-parse HEAD | cut -c-8)) sources := $(wildcard cmd/ytbx/*.go internal/cmd/*.go pkg/v1/ytbx/*.go) -all: test +all: clean verify test build clean: - GO111MODULE=on go clean -i -r -cache - rm -rf binaries + @GO111MODULE=on go clean -cache $(shell go list ./...) + @rm -rf binaries -test: - GO111MODULE=on ginkgo -r --randomizeAllSpecs --randomizeSuites --failOnPending --trace --race --nodes=2 --compilers=2 +verify: + @GO111MODULE=on go mod download + @GO111MODULE=on go mod verify -gobin: - GO111MODULE=on go build -ldflags='-s -w -extldflags "-static"' -o ${GOPATH}/bin/ytbx cmd/ytbx/main.go - -build: binaries/ytbx-linux-amd64 binaries/ytbx-darwin-amd64 binaries/ytbx-windows-amd64 +test: $(sources) + @GO111MODULE=on ginkgo -r \ + -randomizeAllSpecs \ + -randomizeSuites \ + -failOnPending \ + -trace \ + -race \ + -nodes=2 \ + -compilers=2 binaries/ytbx-linux-amd64: $(sources) - GO111MODULE=on CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -tags netgo -ldflags='-s -w -extldflags "-static" -X github.com/homeport/ytbx/internal/cmd.version=$(version)' -o binaries/ytbx-linux-amd64 cmd/ytbx/main.go + @GO111MODULE=on CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \ + -tags netgo \ + -ldflags='-s -w -extldflags "-static" -X github.com/homeport/ytbx/internal/cmd.version=$(version)' \ + -o binaries/ytbx-linux-amd64 \ + cmd/ytbx/main.go binaries/ytbx-darwin-amd64: $(sources) - GO111MODULE=on CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -tags netgo -ldflags='-s -w -extldflags "-static" -X github.com/homeport/ytbx/internal/cmd.version=$(version)' -o binaries/ytbx-darwin-amd64 cmd/ytbx/main.go + @GO111MODULE=on CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build \ + -tags netgo \ + -ldflags='-s -w -extldflags "-static" -X github.com/homeport/ytbx/internal/cmd.version=$(version)' \ + -o binaries/ytbx-darwin-amd64 \ + cmd/ytbx/main.go + +binaries/ytbx-windows-amd64.exe: $(sources) + @GO111MODULE=on CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build \ + -tags netgo \ + -ldflags='-s -w -extldflags "-static" -X github.com/homeport/ytbx/internal/cmd.version=$(version)' \ + -o binaries/ytbx-windows-amd64.exe \ + cmd/ytbx/main.go -binaries/ytbx-windows-amd64: $(sources) - GO111MODULE=on CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -tags netgo -ldflags='-s -w -extldflags "-static" -X github.com/homeport/ytbx/internal/cmd.version=$(version)' -o binaries/ytbx-windows-amd64 cmd/ytbx/main.go +build: binaries/ytbx-linux-amd64 binaries/ytbx-darwin-amd64 binaries/ytbx-windows-amd64.exe + @/bin/sh -c "echo '\n\033[1mSHA sum of compiled binaries:\033[0m'" + @shasum -a256 binaries/ytbx-linux-amd64 binaries/ytbx-darwin-amd64 binaries/ytbx-windows-amd64.exe diff --git a/go.mod b/go.mod index ae368b7..ab0c41b 100644 --- a/go.mod +++ b/go.mod @@ -2,8 +2,8 @@ module github.com/homeport/ytbx require ( github.com/BurntSushi/toml v0.3.1 - github.com/gorilla/mux v1.7.0 - github.com/homeport/gonvenience v1.7.0 + github.com/gorilla/mux v1.7.1 + github.com/homeport/gonvenience v1.7.6 github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/onsi/ginkgo v1.7.0 github.com/onsi/gomega v1.4.3 diff --git a/go.sum b/go.sum index 001298b..74b666c 100644 --- a/go.sum +++ b/go.sum @@ -2,10 +2,10 @@ github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/gorilla/mux v1.7.0 h1:tOSd0UKHQd6urX6ApfOn4XdBMY6Sh1MfxV3kmaazO+U= -github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/homeport/gonvenience v1.7.0 h1:w41b0ehw45R6OR6NxgHKrNdfsQV8QN6kX7P8xNlD7GQ= -github.com/homeport/gonvenience v1.7.0/go.mod h1:G2NH1mGKb2RtQ/xy7Axv5Tnnwzq4yE6NoYyINd1Lvuk= +github.com/gorilla/mux v1.7.1 h1:Dw4jY2nghMMRsh1ol8dv1axHkDwMQK2DHerMNJsIpJU= +github.com/gorilla/mux v1.7.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/homeport/gonvenience v1.7.6 h1:XJIykzrxOLZkOYwk2mj61YnWbIM+47XWrYJ3BSOMbAg= +github.com/homeport/gonvenience v1.7.6/go.mod h1:G2NH1mGKb2RtQ/xy7Axv5Tnnwzq4yE6NoYyINd1Lvuk= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= @@ -43,6 +43,7 @@ golang.org/x/sys v0.0.0-20181128092732-4ed8d59d0b35/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= gopkg.in/DATA-DOG/go-sqlmock.v1 v1.3.0/go.mod h1:OdE7CF6DbADk7lN8LIKRzRJTTZXIjtWgA5THM5lhBAw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= diff --git a/internal/cmd/get.go b/internal/cmd/get.go index a36a07e..bd9ffdf 100644 --- a/internal/cmd/get.go +++ b/internal/cmd/get.go @@ -23,8 +23,8 @@ package cmd import ( "fmt" - "github.com/homeport/ytbx/pkg/v1/ytbx" "github.com/homeport/gonvenience/pkg/v1/neat" + "github.com/homeport/ytbx/pkg/v1/ytbx" "github.com/spf13/cobra" ) diff --git a/internal/cmd/restructure.go b/internal/cmd/restructure.go new file mode 100644 index 0000000..ad97d84 --- /dev/null +++ b/internal/cmd/restructure.go @@ -0,0 +1,125 @@ +// Copyright © 2018 The Homeport Team +// +// 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 cmd + +import ( + "bufio" + "bytes" + "fmt" + "io/ioutil" + "os" + + "github.com/homeport/gonvenience/pkg/v1/bunt" + "github.com/homeport/gonvenience/pkg/v1/neat" + "github.com/homeport/ytbx/pkg/v1/ytbx" + "github.com/spf13/cobra" + "gopkg.in/yaml.v2" +) + +var inplace bool + +// restructureCmd represents the paths command +var restructureCmd = &cobra.Command{ + Use: "restructure ", + Args: cobra.ExactArgs(1), + Short: "Restructure the order of keys", + Long: renderLongDescription(), + RunE: func(cmd *cobra.Command, args []string) error { + location := args[0] + + input, err := ytbx.LoadFile(location) + if err != nil { + return err + } + + for i := range input.Documents { + input.Documents[i] = ytbx.RestructureObject(input.Documents[i]) + } + + if inplace { + info, err := os.Stat(location) + if err != nil { + return err + } + + var buf bytes.Buffer + writer := bufio.NewWriter(&buf) + for _, document := range input.Documents { + out, err := yaml.Marshal(document) + if err != nil { + return err + } + + fmt.Fprint(writer, "---\n", string(out)) + } + + writer.Flush() + ioutil.WriteFile(location, buf.Bytes(), info.Mode()) + + } else { + for _, document := range input.Documents { + out, err := neat.ToYAMLString(document) + if err != nil { + return err + } + + bunt.Println("DimGray{*---*}") + fmt.Print(out) + fmt.Println() + } + } + + return nil + }, +} + +func init() { + rootCmd.AddCommand(restructureCmd) + + restructureCmd.Flags().SortFlags = false + restructureCmd.PersistentFlags().SortFlags = false + + restructureCmd.PersistentFlags().BoolVarP(&inplace, "in-place", "i", false, "overwrite input file with output of this command") + restructureCmd.PersistentFlags().BoolVarP(&ytbx.DisableRemainingKeySort, "disable-remaining-key-sort", "s", false, "disables that all unknown keys are sorted to improve the readability") +} + +func renderLongDescription() string { + var data yaml.MapSlice + yaml.Unmarshal([]byte(`--- +releases: +- sha1: 5ab3b7e685ca18a47d0b4a16d0e3b60832b0a393 + name: binary-buildpack + version: 1.0.32 + url: https://bosh.io/d/github.com/cloudfoundry/binary-buildpack-release?v=1.0.32 +`), &data) + + before, _ := neat.ToYAMLString(data) + after, _ := neat.ToYAMLString(ytbx.RestructureObject(data)) + + return bunt.Sprintf(`Restructure the order of keys in YAML maps + +Example: +%s + +Result: +%s +`, before, after) +} diff --git a/internal/cmd/root.go b/internal/cmd/root.go index c5f113e..8c9d524 100644 --- a/internal/cmd/root.go +++ b/internal/cmd/root.go @@ -26,9 +26,9 @@ import ( "github.com/spf13/cobra" - "github.com/homeport/ytbx/pkg/v1/ytbx" "github.com/homeport/gonvenience/pkg/v1/bunt" "github.com/homeport/gonvenience/pkg/v1/neat" + "github.com/homeport/ytbx/pkg/v1/ytbx" ) // rootCmd represents the base command when called without any subcommands diff --git a/pkg/v1/ytbx/input_test.go b/pkg/v1/ytbx/input_test.go index c285b03..19ea0dd 100644 --- a/pkg/v1/ytbx/input_test.go +++ b/pkg/v1/ytbx/input_test.go @@ -27,8 +27,8 @@ import ( "net/http/httptest" "os" - . "github.com/homeport/ytbx/pkg/v1/ytbx" . "github.com/gorilla/mux" + . "github.com/homeport/ytbx/pkg/v1/ytbx" yaml "gopkg.in/yaml.v2" . "github.com/onsi/ginkgo" diff --git a/pkg/v1/ytbx/list_functions.go b/pkg/v1/ytbx/list_functions.go index 909897a..750fb5a 100644 --- a/pkg/v1/ytbx/list_functions.go +++ b/pkg/v1/ytbx/list_functions.go @@ -21,6 +21,8 @@ package ytbx import ( + "fmt" + yaml "gopkg.in/yaml.v2" ) @@ -51,6 +53,22 @@ func GetIdentifierFromNamedList(list []interface{}) string { return "" } +// ListStringKeys returns a list of the keys of the YAML MapSlice (map). Only string keys are supported. Other types will result in an error. +func ListStringKeys(mapslice yaml.MapSlice) ([]string, error) { + keys := make([]string, len(mapslice)) + for i, mapitem := range mapslice { + switch mapitem.Key.(type) { + case string: + keys[i] = mapitem.Key.(string) + + default: + return nil, fmt.Errorf("provided mapslice mapitem contains non-string key: %#v", mapitem.Key) + } + } + + return keys, nil +} + // getEntryFromNamedList returns the entry that is identified by the identifier key and a name, for example: `name: one` where name is the identifier key and one the name. Function will return nil with bool false if there is no such entry. func getEntryFromNamedList(list []interface{}, identifier string, name interface{}) (interface{}, bool) { for _, listEntry := range list { diff --git a/pkg/v1/ytbx/restructure.go b/pkg/v1/ytbx/restructure.go new file mode 100644 index 0000000..b729bd2 --- /dev/null +++ b/pkg/v1/ytbx/restructure.go @@ -0,0 +1,180 @@ +// Copyright © 2019 The Homeport Team +// +// 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 ytbx + +import ( + "encoding/json" + "sort" + + yaml "gopkg.in/yaml.v2" +) + +// DisableRemainingKeySort disables that that during restructuring of map keys, +// all unknown keys are also sorted in such a way that it ideally improves the +// readability. +var DisableRemainingKeySort = false + +var knownKeyOrders = [][]string{ + {"name", "director_uuid", "releases", "instance_groups", "networks", "resource_pools", "compilation"}, + {"name", "url", "version", "sha1"}, + + // Concourse (https://concourse-ci.org/pipelines.html, https://concourse-ci.org/steps.html, https://concourse-ci.org/resources.html) + {"jobs", "resources", "resource_types"}, + {"name", "type", "source"}, + {"get"}, + {"put"}, + {"task"}, + + // SUSE SCF role manifest (https://github.com/SUSE/scf/blob/develop/container-host-files/etc/scf/config/role-manifest.yml) + {"releases", "instance_groups", "configuration", "variables"}, + {"auth", "templates"}, + + // Universal default #1 ... name should always be first + {"name"}, + + // Universal default #2 ... key should always be first + {"key"}, + + // Universal default #3 ... id should always be first + {"id"}, +} + +func lookupMap(list []string) map[string]int { + result := make(map[string]int, len(list)) + for idx, entry := range list { + result[entry] = idx + } + + return result +} + +func countCommonKeys(keys []string, list []string) int { + counter, lookup := 0, lookupMap(keys) + for _, key := range list { + if _, ok := lookup[key]; ok { + counter++ + } + } + + return counter +} + +func commonKeys(setA []string, setB []string) []string { + result, lookup := []string{}, lookupMap(setB) + for _, entry := range setA { + if _, ok := lookup[entry]; ok { + result = append(result, entry) + } + } + + return result +} + +func reorderMapsliceKeys(input yaml.MapSlice, keys []string) yaml.MapSlice { + // Create list with all remaining keys: those that are part of the input + // YAML MapSlice, but not listed in the keys list + remainingKeys, lookup := []string{}, lookupMap(keys) + for _, mapitem := range input { + key := mapitem.Key.(string) + if _, ok := lookup[key]; !ok { + remainingKeys = append(remainingKeys, key) + } + } + + // Sort remaining keys by sorting long and possibly hard to read structure + // to the end of the map + if !DisableRemainingKeySort { + sort.Slice(remainingKeys, func(i, j int) bool { + valI, _ := getValueByKey(input, remainingKeys[i]) + valJ, _ := getValueByKey(input, remainingKeys[j]) + marI, _ := json.Marshal(valI) + marJ, _ := json.Marshal(valJ) + return len(marI) < len(marJ) + }) + } + + // Rebuild a new YAML MapSlice key by key by using first the keys from the + // reorder list and then all remaining keys + result := yaml.MapSlice{} + for _, key := range append(keys, remainingKeys...) { + // Ignore the error field here since we know what keys there are + value, _ := getValueByKey(input, key) + result = append(result, yaml.MapItem{ + Key: key, + Value: value, + }) + } + + return result +} + +func getSuitableReorderFunction(keys []string) func(yaml.MapSlice) yaml.MapSlice { + topCandidateIdx, topCandidateHits := -1, -1 + for idx, candidate := range knownKeyOrders { + if count := countCommonKeys(keys, candidate); count > topCandidateHits { + topCandidateIdx = idx + topCandidateHits = count + } + } + + if topCandidateIdx >= 0 { + return func(input yaml.MapSlice) yaml.MapSlice { + return reorderMapsliceKeys(input, commonKeys(knownKeyOrders[topCandidateIdx], keys)) + } + } + + return nil +} + +// RestructureObject takes an object and traverses down any sub elements such as list entries or map values to recursively call restructure itself. On YAML MapSlices (maps), it will use a look-up mechanism to decide if the order of key in that map needs to be rearranged to meet some known established human order. +func RestructureObject(obj interface{}) interface{} { + switch val := obj.(type) { + case yaml.MapSlice: + // Restructure the YAML MapSlice keys + if keys, err := ListStringKeys(val); err == nil { + if fn := getSuitableReorderFunction(keys); fn != nil { + val = fn(val) + } + } + + // Restructure the values of the respective keys of this YAML MapSlice + for idx := range val { + val[idx].Value = RestructureObject(val[idx].Value) + } + + return val + + case []interface{}: + for i := range val { + val[i] = RestructureObject(val[i]) + } + return val + + case []yaml.MapSlice: + for i := range val { + val[i] = RestructureObject(val[i]).(yaml.MapSlice) + } + return val + + default: + return obj + } +} diff --git a/pkg/v1/ytbx/restructure_test.go b/pkg/v1/ytbx/restructure_test.go new file mode 100644 index 0000000..79eb0a8 --- /dev/null +++ b/pkg/v1/ytbx/restructure_test.go @@ -0,0 +1,76 @@ +// Copyright © 2019 The Homeport Team +// +// 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 ytbx_test + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + . "github.com/homeport/ytbx/pkg/v1/ytbx" + yaml "gopkg.in/yaml.v2" +) + +var _ = Describe("Restructure order of map keys", func() { + Context("YAML MapSlice key reorderings of the MapSlice itself", func() { + It("should restructure Concourse root level keys", func() { + input := yml("{ groups: [], jobs: [], resources: [], resource_types: [] }") + output := RestructureObject(input).(yaml.MapSlice) + + keys, err := ListStringKeys(output) + Expect(err).To(BeNil()) + Expect(keys).To(BeEquivalentTo([]string{"jobs", "resources", "resource_types", "groups"})) + }) + + It("should restructure Concourse resource and resource_type keys", func() { + input := yml("{ source: {}, name: {}, type: {}, privileged: {} }") + output := RestructureObject(input).(yaml.MapSlice) + + keys, err := ListStringKeys(output) + Expect(err).To(BeNil()) + Expect(keys).To(BeEquivalentTo([]string{"name", "type", "source", "privileged"})) + }) + }) + + Context("YAML MapSlice key reorderings of the MapSlice values", func() { + It("should restructure Concourse resource keys as part as part of a MapSlice value", func() { + input := yml("{ resources: [ { privileged: false, source: { branch: foo, paths: [] }, name: myname, type: mytype } ] }") + output := RestructureObject(input).(yaml.MapSlice) + + value := output[0].Value.([]interface{}) + obj := value[0].(yaml.MapSlice) + + keys, err := ListStringKeys(obj) + Expect(err).To(BeNil()) + Expect(keys).To(BeEquivalentTo([]string{"name", "type", "source", "privileged"})) + }) + }) + + Context("Restructure code tries to rearrange even unknown keys", func() { + It("should reorder map keys in a somehow more readable way", func() { + input := yml(`{"list":["one","two","three"], "some":{"deep":{"structure":{"where":{"you":{"loose":{"focus":{"one":1,"two":2}}}}}}}, "name":"here", "release":"this"}`) + output := RestructureObject(input).(yaml.MapSlice) + + keys, err := ListStringKeys(output) + Expect(err).To(BeNil()) + Expect(keys).To(BeEquivalentTo([]string{"name", "release", "list", "some"})) + }) + }) +}) diff --git a/pkg/v1/ytbx/ytbx_suite_test.go b/pkg/v1/ytbx/ytbx_suite_test.go index 41f1b73..8aeb485 100644 --- a/pkg/v1/ytbx/ytbx_suite_test.go +++ b/pkg/v1/ytbx/ytbx_suite_test.go @@ -28,9 +28,9 @@ import ( "runtime" "testing" - "github.com/homeport/ytbx/pkg/v1/ytbx" "github.com/homeport/gonvenience/pkg/v1/bunt" "github.com/homeport/gonvenience/pkg/v1/neat" + "github.com/homeport/ytbx/pkg/v1/ytbx" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega"