From d1ccbc3e17fcec0a740ef96e99b352ce6d8974e6 Mon Sep 17 00:00:00 2001 From: Denis Arh Date: Mon, 16 Aug 2021 22:49:27 +0200 Subject: [PATCH] Fix encoding on save & re-encoding on fetch --- pkg/actionlog/types.go | 108 +++++++++++++++++------------------- pkg/actionlog/types_test.go | 14 ++++- system/rest/actionlog.go | 25 ++++++++- 3 files changed, 87 insertions(+), 60 deletions(-) diff --git a/pkg/actionlog/types.go b/pkg/actionlog/types.go index f20f2d3b1..0ae28cfa6 100644 --- a/pkg/actionlog/types.go +++ b/pkg/actionlog/types.go @@ -1,6 +1,7 @@ package actionlog import ( + "strconv" "time" ) @@ -95,13 +96,6 @@ func (m Meta) Set(key string, in interface{}, omitempty bool) { return } - if !omitempty { - // Nothing special, - // assign value and quit - m[key] = in - return - } - // for the rest, we need to determine what kind of if str, is := in.(string); is { @@ -125,62 +119,64 @@ func (m Meta) Set(key string, in interface{}, omitempty bool) { return } - // cast to (int|uint|float)64 - num := func(n interface{}) interface{} { - switch n := n.(type) { - case int: - return int64(n) - case int8: - return int64(n) - case int16: - return int64(n) - case int32: - return int64(n) + { + // properly encode big numbers + // before storing them as JSON + + num := func(n interface{}) interface{} { + switch n := n.(type) { + case int: + return int64(n) + case int8: + return int64(n) + case int16: + return int64(n) + case int32: + return int64(n) + case int64: + return int64(n) + case uint: + return uint64(n) + case uintptr: + return uint64(n) + case uint8: + return uint64(n) + case uint16: + return uint64(n) + case uint32: + return uint64(n) + case uint64: + return uint64(n) + case float32: + return float64(n) + case float64: + return n + } + return n + }(in) + + switch num := num.(type) { case int64: - return n - case uint: - return uint64(n) - case uintptr: - return uint64(n) - case uint8: - return uint64(n) - case uint16: - return uint64(n) - case uint32: - return uint64(n) + if !omitempty || num > 0 { + m[key] = strconv.FormatInt(num, 10) + } + return case uint64: - return n - case float32: - return float64(n) + if !omitempty || num > 0 { + m[key] = strconv.FormatUint(num, 10) + } + return + case float64: - return n - } - return n - }(in) + if !omitempty || num > 0 { + m[key] = in + } - switch num := num.(type) { - case nil: - case uint64: - if !omitempty || num > 0 { - m[key] = in + return } - return - - case int64: - if !omitempty || num > 0 { - m[key] = in - } - return - - case float64: - if !omitempty || num > 0 { - m[key] = in - } - - return } - // for the rest (slices, etc..) + // for the rest (string, slices, etc..) // just set the value m[key] = in } diff --git a/pkg/actionlog/types_test.go b/pkg/actionlog/types_test.go index 4ec1052d8..2fd561eed 100644 --- a/pkg/actionlog/types_test.go +++ b/pkg/actionlog/types_test.go @@ -36,10 +36,18 @@ func TestMeta_Set(t *testing.T) { {"bool true", true, false, Meta{"t": true}}, {"bool false", false, false, Meta{"t": false}}, - {"int value, omit", 1, true, Meta{"t": 1}}, - {"int value, keep", 1, false, Meta{"t": 1}}, + {"int value, omit", 1, true, Meta{"t": "1"}}, + {"int value, keep", 1, false, Meta{"t": "1"}}, {"int empty, omit", 0, true, Meta{}}, - {"int empty, keep", 0, false, Meta{"t": 0}}, + {"int empty, keep", 0, false, Meta{"t": "0"}}, + {"uint64 empty, omit", uint64(0), true, Meta{}}, + {"uint64 empty, keep", uint64(0), false, Meta{"t": "0"}}, + {"bigint", 244783268048994492, false, Meta{"t": "244783268048994492"}}, + {"int64", int64(244783268048994492), false, Meta{"t": "244783268048994492"}}, + {"uint64()", uint64(244783268048994492), false, Meta{"t": "244783268048994492"}}, + {"int", 42, false, Meta{"t": "42"}}, + {"float64", float64(42), false, Meta{"t": float64(42)}}, + {"float64", float64(244783268048994492), false, Meta{"t": 2.447832680489945e+17}}, {"slice", []int{0, 1}, false, Meta{"t": []int{0, 1}}}, diff --git a/system/rest/actionlog.go b/system/rest/actionlog.go index 2367987b9..d811462d5 100644 --- a/system/rest/actionlog.go +++ b/system/rest/actionlog.go @@ -2,6 +2,8 @@ package rest import ( "context" + "strconv" + "strings" "github.com/cortezaproject/corteza-server/pkg/actionlog" "github.com/cortezaproject/corteza-server/pkg/filter" @@ -26,7 +28,8 @@ type ( // provide user's email actionlogActionPayload struct { *actionlog.Action - Actor string `json:"actor,omitempty"` + Actor string `json:"actor,omitempty"` + Meta map[string]interface{} `json:"meta,omitempty"` } actionlogPayload struct { @@ -78,6 +81,9 @@ func (ctrl Actionlog) makeFilterPayload(ctx context.Context, ee []*actionlog.Act // Remap events to payload structs for e := range ee { pp[e] = &actionlogActionPayload{Action: ee[e]} + pp[e].Meta = ee[e].Meta + + sanitizeMapStringInterface(pp[e].Meta) } err = userPreloader( @@ -115,6 +121,23 @@ func (ctrl Actionlog) makeFilterPayload(ctx context.Context, ee []*actionlog.Act return &actionlogPayload{Filter: f, Set: pp}, nil } +// Making sure JS can read our values +func sanitizeMapStringInterface(m map[string]interface{}) { + for k := range m { + switch v := m[k].(type) { + case float64: + if strings.HasSuffix(k, "ID") { + // make sure uint64 values on fields ending with ID + // are properly encoded as strings + m[k] = strconv.FormatUint(uint64(v), 10) + } + + case map[string]interface{}: + sanitizeMapStringInterface(v) + } + } +} + // Preloader collects all ids of users, loads them and sets them back // // We'll be accessing the store directly since this is protected with action-log.read operation check.