3
0

Respect Number field's specified precision

This commit is contained in:
Tomaž Jerman
2020-04-23 17:53:57 +02:00
committed by Denis Arh
parent 90280dabb9
commit ce9c997584
2 changed files with 102 additions and 15 deletions
+58 -8
View File
@@ -1,10 +1,11 @@
package values
import (
"github.com/cortezaproject/corteza-server/compose/types"
"strconv"
"strings"
"time"
"github.com/cortezaproject/corteza-server/compose/types"
)
type (
@@ -88,14 +89,14 @@ func (s sanitizer) Run(m *types.Module, vv types.RecordValueSet) (out types.Reco
v = s.sBool(v, f, m)
case "datetime":
v = s.sDatetime(v, f, m)
case "number":
v = s.sNumber(v, f, m)
// Uncomment when they become relevant for sanitization
//case "email":
// v = s.sEmail(v, f, m)
//case "file":
// v = s.sFile(v, f, m)
//case "number":
// v = s.sNumber(v, f, m)
//case "record":
// v = s.sRecord(v, f, m)
//case "select":
@@ -192,6 +193,60 @@ func (sanitizer) sDatetime(v *types.RecordValue, f *types.ModuleField, m *types.
return v
}
func (sanitizer) sNumber(v *types.RecordValue, f *types.ModuleField, m *types.Module) *types.RecordValue {
// No point in continuing
if v.Value == "" || f.Options == nil {
return v
}
// Default to 0 for consistency
if f.Options["precision"] == nil || f.Options["precision"] == "" {
f.Options["precision"] = 0
}
// Since Options are not structured, there would appear that there can be a bit of a mess
// when it comes to types,so this is needed.
var prec float64
unk := f.Options["precision"]
switch i := unk.(type) {
case float64:
prec = i
case int:
prec = float64(i)
case int64:
prec = float64(i)
case string:
pp, err := strconv.ParseFloat(i, 64)
if err != nil {
prec = 0
break
}
prec = pp
}
// Clamp between 0 and 6; this was originally done in corteza-js so we keep it here.
if prec < 0 {
prec = 0
}
if prec > 6 {
prec = 6
}
base, err := strconv.ParseFloat(v.Value, 64)
if err != nil {
return v
}
// 1. Format the value to the desired precision
// 2. In case of fractures, remove trailing 0's
v.Value = strconv.FormatFloat(base, 'f', int(prec), 64)
if strings.Contains(v.Value, ".") {
v.Value = strings.TrimRight(v.Value, "0")
}
return v
}
//
//func (sanitizer) sEmail(v *types.RecordValue, f *types.ModuleField, m *types.Module) *types.RecordValue {
// // @todo extract from "name" <email> format
@@ -202,11 +257,6 @@ func (sanitizer) sDatetime(v *types.RecordValue, f *types.ModuleField, m *types.
// return v
//}
//
//func (sanitizer) sNumber(v *types.RecordValue, f *types.ModuleField, m *types.Module) *types.RecordValue {
// // @todo cut off the decimals / round up
// // @todo sanitize decimal/thousands dot/comma
// return v
//}
//
//func (sanitizer) sRecord(v *types.RecordValue, f *types.ModuleField, m *types.Module) *types.RecordValue {
// return v
+44 -7
View File
@@ -1,18 +1,20 @@
package values
import (
"github.com/cortezaproject/corteza-server/compose/types"
"reflect"
"testing"
"github.com/cortezaproject/corteza-server/compose/types"
)
func Test_sanitizer_Run(t *testing.T) {
tests := []struct {
name string
kind string
input string
output string
outref uint64
name string
kind string
options map[string]interface{}
input string
output string
outref uint64
}{
{
name: "numbers should be trimmed",
@@ -88,11 +90,46 @@ func Test_sanitizer_Run(t *testing.T) {
input: "2020-03-11T11:20:08.471Z",
output: "2020-03-11T11:20:08Z",
},
{
name: "number precision",
kind: "Number",
options: map[string]interface{}{"precision": 3},
input: "42.44455",
output: "42.445",
},
{
name: "number precision; bigger precision then provided fracture",
kind: "Number",
options: map[string]interface{}{"precision": 3},
input: "42.4",
output: "42.4",
},
{
name: "number precision; default to 0",
kind: "Number",
options: map[string]interface{}{},
input: "42.4",
output: "42",
},
{
name: "number precision; clamped between [0, 6]",
kind: "Number",
options: map[string]interface{}{"precision": 10},
input: "42.5555555555",
output: "42.555556",
},
{
name: "number precision; clamped between [0, 6]",
kind: "Number",
options: map[string]interface{}{"precision": -1},
input: "42.4",
output: "42",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s := sanitizer{}
m := &types.Module{Fields: types.ModuleFieldSet{&types.ModuleField{Name: "testField", Kind: tt.kind}}}
m := &types.Module{Fields: types.ModuleFieldSet{&types.ModuleField{Name: "testField", Kind: tt.kind, Options: tt.options}}}
v := types.RecordValueSet{&types.RecordValue{Name: "testField", Value: tt.input}}
o := types.RecordValueSet{&types.RecordValue{Name: "testField", Value: tt.output, Ref: tt.outref}}