From 98631b5faeeb2b09dd44b3981cabafce4734423e Mon Sep 17 00:00:00 2001 From: Charalampos Mitsakis Date: Wed, 25 May 2022 17:07:47 +0300 Subject: [PATCH] feature: support CBOR encoding --- cmd/bolt-ui/commands/main.go | 8 ++++++ go.mod | 5 ++++ go.sum | 19 ++++++++++++++ internal/config/config.go | 1 + internal/wire/wire_gen.go | 2 +- ports/http/dto.go | 48 ++++++++++++++++++++++++++++++------ ports/http/handler.go | 6 +++-- 7 files changed, 79 insertions(+), 10 deletions(-) diff --git a/cmd/bolt-ui/commands/main.go b/cmd/bolt-ui/commands/main.go index 14e4dcc..9e064c1 100644 --- a/cmd/bolt-ui/commands/main.go +++ b/cmd/bolt-ui/commands/main.go @@ -27,6 +27,7 @@ const ( nameInsecureCORS = "insecure-cors" nameInsecureToken = "insecure-token" nameInsecureTLS = "insecure-tls" + nameEncodingCBOR = "encoding-cbor" ) var MainCmd = guinea.Command{ @@ -64,6 +65,12 @@ var MainCmd = guinea.Command{ Default: false, Description: "Disables serving using TLS", }, + { + Name: nameEncodingCBOR, + Type: guinea.Bool, + Default: false, + Description: "Try to parse values as CBOR encoded", + }, }, ShortDescription: "a web user interface for the Bolt database", Description: ` @@ -110,6 +117,7 @@ func newConfig(c guinea.Context) (*config.Config, error) { InsecureCORS: c.Options[nameInsecureCORS].Bool(), InsecureToken: c.Options[nameInsecureToken].Bool(), InsecureTLS: c.Options[nameInsecureTLS].Bool(), + EncodingCBOR: c.Options[nameEncodingCBOR].Bool(), } if !conf.InsecureToken { diff --git a/go.mod b/go.mod index 7d1ea71..63ed599 100644 --- a/go.mod +++ b/go.mod @@ -2,10 +2,12 @@ module github.com/boreq/bolt-ui require ( github.com/NYTimes/gziphandler v1.1.0 + github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d github.com/boreq/errors v0.1.0 github.com/boreq/guinea v0.0.0-20190218203212-75c10cec45e9 github.com/boreq/rest v0.1.0 github.com/davecgh/go-spew v1.1.1 // indirect + github.com/fxamacker/cbor/v2 v2.4.0 github.com/go-stack/stack v1.8.0 // indirect github.com/google/wire v0.5.0 github.com/inconshreveable/log15 v0.0.0-20180818164646-67afb5ed74ec @@ -14,8 +16,11 @@ require ( github.com/mattn/go-colorable v0.1.2 // indirect github.com/oklog/ulid/v2 v2.0.2 github.com/pkg/errors v0.8.1 + github.com/polydawn/refmt v0.0.0-20201211092308-30ac6d18308e github.com/rs/cors v1.6.0 + github.com/smartystreets/goconvey v1.7.2 // indirect github.com/stretchr/testify v1.7.0 + github.com/warpfork/go-wish v0.0.0-20200122115046-b9ea61034e4a // indirect go.etcd.io/bbolt v1.3.3 golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 // indirect gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect diff --git a/go.sum b/go.sum index 9a1a08b..f78a716 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/NYTimes/gziphandler v1.1.0 h1:wkMjq4kSz11Zer+ncYWNBQDlj9Y5RLloY/Tb8yOj6gA= github.com/NYTimes/gziphandler v1.1.0/go.mod h1:EwmLXLwj3Rvq6vawd3hKEPUcQRyz2CDE1bov6dy8HNQ= +github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= +github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= github.com/boreq/errors v0.1.0 h1:aJIXv9JnyR5KtxFpQ8/AiblH3nfYmr1e1yoTze/5A1k= github.com/boreq/errors v0.1.0/go.mod h1:B3dsXzhYvfgUXp7ViU/moPYM4PojgQ9MiQ21uvY6qqQ= github.com/boreq/guinea v0.0.0-20190218203212-75c10cec45e9 h1:CgYlE4U2Piu/oRDi97UFgjjS7W2ODdPu0BVUV4LJ0sM= @@ -9,14 +11,20 @@ github.com/boreq/rest v0.1.0/go.mod h1:Ckfx0qLDdPbS081820aWkkqvwhlrbv0SDu8UBDY4k github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88= +github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/wire v0.5.0 h1:I7ELFeVBr3yfPIcc8+MWvrjk+3VjbcSzoXm3JVa+jD8= github.com/google/wire v0.5.0/go.mod h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1EoU= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/inconshreveable/log15 v0.0.0-20180818164646-67afb5ed74ec h1:CGkYB1Q7DSsH/ku+to+foV4agt2F2miquaLUgF6L178= github.com/inconshreveable/log15 v0.0.0-20180818164646-67afb5ed74ec/go.mod h1:cOaXtrgN4ScfRrD9Bre7U1thNq5RtJ8ZoP4iXVGRj6o= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0 h1:TDTW5Yz1mjftljbcKqRcrYhd4XeOoI98t+9HbQbYf7g= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= @@ -35,13 +43,23 @@ 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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/polydawn/refmt v0.0.0-20201211092308-30ac6d18308e h1:ZOcivgkkFRnjfoTcGsDq3UQYiBmekwLA+qg0OjyB/ls= +github.com/polydawn/refmt v0.0.0-20201211092308-30ac6d18308e/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o= github.com/rs/cors v1.6.0 h1:G9tHG9lebljV9mfp9SNPDL36nCDxmo3zTlAf1YgvzmI= github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs= +github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= +github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg= +github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/warpfork/go-wish v0.0.0-20200122115046-b9ea61034e4a h1:G++j5e0OC488te356JvdhaM8YS6nMsjLAYF7JxCv07w= +github.com/warpfork/go-wish v0.0.0-20200122115046-b9ea61034e4a/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -51,6 +69,7 @@ golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 h1:siQdpVirKtzPhKl3lZWozZraCFObP8S1v6PRp0bLrtU= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190422233926-fe54fb35175b/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= diff --git a/internal/config/config.go b/internal/config/config.go index d822f66..c76365f 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -11,4 +11,5 @@ type Config struct { InsecureCORS bool InsecureToken bool InsecureTLS bool + EncodingCBOR bool } diff --git a/internal/wire/wire_gen.go b/internal/wire/wire_gen.go index 3ecb2d7..557ecb8 100644 --- a/internal/wire/wire_gen.go +++ b/internal/wire/wire_gen.go @@ -60,7 +60,7 @@ func BuildService(conf *config.Config) (*service.Service, error) { Browse: browseHandler, } tokenAuthProvider := http.NewTokenAuthProvider(conf) - handler, err := http.NewHandler(applicationApplication, tokenAuthProvider) + handler, err := http.NewHandler(applicationApplication, tokenAuthProvider, conf.EncodingCBOR) if err != nil { return nil, err } diff --git a/ports/http/dto.go b/ports/http/dto.go index d485625..fc72ded 100644 --- a/ports/http/dto.go +++ b/ports/http/dto.go @@ -1,11 +1,19 @@ package http import ( + "bufio" + "bytes" "encoding/hex" "encoding/json" + "fmt" "unicode" + "github.com/acarl005/stripansi" "github.com/boreq/bolt-ui/application" + "github.com/fxamacker/cbor/v2" + refmtcbor "github.com/polydawn/refmt/cbor" + refmtpretty "github.com/polydawn/refmt/pretty" + refmtshared "github.com/polydawn/refmt/shared" ) type Tree struct { @@ -29,10 +37,10 @@ type Entry struct { Value *Value `json:"value,omitempty"` } -func toTree(tree application.Tree) Tree { +func toTree(tree application.Tree, encodingCBOR bool) Tree { return Tree{ toKeys(tree.Path), - toEntries(tree.Entries), + toEntries(tree.Entries, encodingCBOR), } } @@ -44,19 +52,19 @@ func toKeys(keys []application.Key) []Key { return result } -func toEntries(entries []application.Entry) []Entry { +func toEntries(entries []application.Entry, encodingCBOR bool) []Entry { result := make([]Entry, 0) for _, entry := range entries { - result = append(result, toEntry(entry)) + result = append(result, toEntry(entry, encodingCBOR)) } return result } -func toEntry(entry application.Entry) Entry { +func toEntry(entry application.Entry, encodingCBOR bool) Entry { return Entry{ Bucket: entry.Bucket, Key: toKey(entry.Key), - Value: toValue(entry.Value), + Value: toValue(entry.Value, encodingCBOR), } } @@ -74,7 +82,7 @@ func toKey(key application.Key) Key { return result } -func toValue(value application.Value) *Value { +func toValue(value application.Value, encodingCBOR bool) *Value { if value.IsEmpty() { return nil } @@ -85,6 +93,15 @@ func toValue(value application.Value) *Value { Hex: hex.EncodeToString(b), } + if encodingCBOR { + if err := cbor.Valid(b); err == nil { + t, err := cborToText(b) + if err == nil && isDisplayableString(t) { + result.Str = t + return result + } + } + } if canDisplayAsString(b) { result.Str = string(b) } @@ -107,3 +124,20 @@ func isDisplayableString(str string) bool { } return true } + +func cborToText(dataCBOR []byte) (string, error) { + var buf bytes.Buffer + bufWriter := bufio.NewWriter(&buf) + err := refmtshared.TokenPump{ + refmtcbor.NewDecoder(refmtcbor.DecodeOptions{}, bytes.NewReader(dataCBOR)), + refmtpretty.NewEncoder(bufWriter), + }.Run() + if err != nil { + return "", fmt.Errorf("shared.TokenPump.Run() failed: %s", err) + } + err = bufWriter.Flush() + if err != nil { + return "", fmt.Errorf("bufWriter.Flush() failed: %s", err) + } + return stripansi.Strip(buf.String()), nil +} diff --git a/ports/http/handler.go b/ports/http/handler.go index 79c695a..3a3a5f2 100644 --- a/ports/http/handler.go +++ b/ports/http/handler.go @@ -18,14 +18,16 @@ type Handler struct { authProvider AuthProvider router *httprouter.Router log logging.Logger + encodingCBOR bool } -func NewHandler(app *application.Application, authProvider AuthProvider) (*Handler, error) { +func NewHandler(app *application.Application, authProvider AuthProvider, encodingCBOR bool) (*Handler, error) { h := &Handler{ app: app, authProvider: authProvider, router: httprouter.New(), log: logging.New("ports/http.Handler"), + encodingCBOR: encodingCBOR, } h.router.HandlerFunc(http.MethodGet, "/api/browse/*path", rest.Wrap(h.browse)) @@ -104,7 +106,7 @@ func (h *Handler) browse(r *http.Request) rest.RestResponse { } return rest.NewResponse( - toTree(tree), + toTree(tree, h.encodingCBOR), ) }