Respect Number field's specified precision
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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}}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user