From 103b5c8cd4e4b86431483d0c22caab0119b4b20e Mon Sep 17 00:00:00 2001 From: rot1024 Date: Wed, 10 Nov 2021 19:13:19 +0900 Subject: [PATCH 01/12] add value package --- pkg/value/bool.go | 31 +++++ pkg/value/camera.go | 67 ++++++++++ pkg/value/coordinates.go | 77 ++++++++++++ pkg/value/latlng.go | 54 +++++++++ pkg/value/latlngheight.go | 59 +++++++++ pkg/value/number.go | 117 ++++++++++++++++++ pkg/value/polygon.go | 58 +++++++++ pkg/value/rect.go | 48 ++++++++ pkg/value/ref.go | 18 +++ pkg/value/string.go | 31 +++++ pkg/value/type.go | 56 +++++++++ pkg/value/type_test.go | 109 +++++++++++++++++ pkg/value/typography.go | 130 ++++++++++++++++++++ pkg/value/url.go | 51 ++++++++ pkg/value/value.go | 82 +++++++++++++ pkg/value/value_test.go | 249 ++++++++++++++++++++++++++++++++++++++ 16 files changed, 1237 insertions(+) create mode 100644 pkg/value/bool.go create mode 100644 pkg/value/camera.go create mode 100644 pkg/value/coordinates.go create mode 100644 pkg/value/latlng.go create mode 100644 pkg/value/latlngheight.go create mode 100644 pkg/value/number.go create mode 100644 pkg/value/polygon.go create mode 100644 pkg/value/rect.go create mode 100644 pkg/value/ref.go create mode 100644 pkg/value/string.go create mode 100644 pkg/value/type.go create mode 100644 pkg/value/type_test.go create mode 100644 pkg/value/typography.go create mode 100644 pkg/value/url.go create mode 100644 pkg/value/value.go create mode 100644 pkg/value/value_test.go diff --git a/pkg/value/bool.go b/pkg/value/bool.go new file mode 100644 index 00000000..572f02dc --- /dev/null +++ b/pkg/value/bool.go @@ -0,0 +1,31 @@ +package value + +var TypeBool Type = "bool" + +var propertyBool = TypeProperty{ + I2V: func(i interface{}) (interface{}, bool) { + if v, ok := i.(bool); ok { + return v, true + } + if v, ok := i.(*bool); ok && v != nil { + return *v, true + } + return nil, false + }, + V2I: func(v interface{}) (interface{}, bool) { + return v, true + }, + Validate: func(i interface{}) bool { + _, ok := i.(bool) + return ok + }, + Compatible: []Type{}, +} + +func (v *Value) ValueBool() (vv bool, ok bool) { + if v == nil { + return + } + vv, ok = v.v.(bool) + return +} diff --git a/pkg/value/camera.go b/pkg/value/camera.go new file mode 100644 index 00000000..ec717f90 --- /dev/null +++ b/pkg/value/camera.go @@ -0,0 +1,67 @@ +package value + +import "github.com/mitchellh/mapstructure" + +type Camera struct { + Lat float64 `json:"lat" mapstructure:"lat"` + Lng float64 `json:"lng" mapstructure:"lng"` + Altitude float64 `json:"altitude" mapstructure:"altitude"` + Heading float64 `json:"heading" mapstructure:"heading"` + Pitch float64 `json:"pitch" mapstructure:"pitch"` + Roll float64 `json:"roll" mapstructure:"roll"` + FOV float64 `json:"fov" mapstructure:"fov"` +} + +func (c *Camera) Clone() *Camera { + if c == nil { + return nil + } + return &Camera{ + Lat: c.Lat, + Lng: c.Lng, + Altitude: c.Altitude, + Heading: c.Heading, + Pitch: c.Pitch, + Roll: c.Roll, + FOV: c.FOV, + } +} + +var TypeCamera Type = "camera" + +var propertyCamera = TypeProperty{ + I2V: func(i interface{}) (interface{}, bool) { + if v, ok := i.(Camera); ok { + return v, true + } + + if v, ok := i.(*Camera); ok { + if v != nil { + return *v, true + } + return nil, false + } + + v := Camera{} + if err := mapstructure.Decode(i, &v); err == nil { + return v, true + } + return nil, false + }, + V2I: func(v interface{}) (interface{}, bool) { + return v, true + }, + Validate: func(i interface{}) bool { + _, ok := i.(Camera) + return ok + }, + Compatible: []Type{}, +} + +func (v *Value) ValueCamera() (vv Camera, ok bool) { + if v == nil { + return + } + vv, ok = v.v.(Camera) + return +} diff --git a/pkg/value/coordinates.go b/pkg/value/coordinates.go new file mode 100644 index 00000000..917f5f25 --- /dev/null +++ b/pkg/value/coordinates.go @@ -0,0 +1,77 @@ +package value + +import "github.com/mitchellh/mapstructure" + +type Coordinates []LatLngHeight + +// CoordinatesFrom generates a new Coordinates from slice such as [lon, lat, alt, lon, lat, alt, ...] +func CoordinatesFrom(coords []float64) Coordinates { + if len(coords) == 0 { + return nil + } + + r := make([]LatLngHeight, 0, len(coords)/3) + l := LatLngHeight{} + for i, c := range coords { + switch i % 3 { + case 0: + l = LatLngHeight{} + l.Lng = c + case 1: + l.Lat = c + case 2: + l.Height = c + r = append(r, l) + } + } + + return r +} + +var TypeCoordinates Type = "coordinates" + +var propertyCoordinates = TypeProperty{ + I2V: func(i interface{}) (interface{}, bool) { + if v, ok := i.(Coordinates); ok { + return v, true + } else if v, ok := i.(*Coordinates); ok { + if v != nil { + return *v, true + } + return nil, false + } else if v2, ok := i.([]float64); ok { + if v2 == nil { + return nil, false + } + return CoordinatesFrom(v2), true + } + + v2 := Coordinates{} + if err := mapstructure.Decode(i, &v2); err == nil { + return v2, true + } + + v1 := []float64{} + if err := mapstructure.Decode(i, &v1); err == nil { + return CoordinatesFrom(v1), true + } + + return nil, false + }, + V2I: func(v interface{}) (interface{}, bool) { + return v, true + }, + Validate: func(i interface{}) bool { + _, ok := i.(bool) + return ok + }, + Compatible: []Type{}, +} + +func (v *Value) ValueCoordinates() (vv Coordinates, ok bool) { + if v == nil { + return + } + vv, ok = v.v.(Coordinates) + return +} diff --git a/pkg/value/latlng.go b/pkg/value/latlng.go new file mode 100644 index 00000000..efff6593 --- /dev/null +++ b/pkg/value/latlng.go @@ -0,0 +1,54 @@ +package value + +import "github.com/mitchellh/mapstructure" + +type LatLng struct { + Lat float64 `json:"lat" mapstructure:"lat"` + Lng float64 `json:"lng" mapstructure:"lng"` +} + +func (l *LatLng) Clone() *LatLng { + if l == nil { + return nil + } + return &LatLng{ + Lat: l.Lat, + Lng: l.Lng, + } +} + +var TypeLatLng Type = "latlng" + +var propertyLatLng = TypeProperty{ + I2V: func(i interface{}) (interface{}, bool) { + if v, ok := i.(LatLng); ok { + return v, true + } else if v, ok := i.(*LatLng); ok { + if v != nil { + return *v, true + } + return nil, false + } + v := LatLng{} + if err := mapstructure.Decode(i, &v); err != nil { + return nil, false + } + return v, true + }, + V2I: func(v interface{}) (interface{}, bool) { + return v, true + }, + Validate: func(i interface{}) bool { + _, ok := i.(LatLng) + return ok + }, + Compatible: []Type{}, +} + +func (v *Value) ValueLatLng() (vv LatLng, ok bool) { + if v == nil { + return + } + vv, ok = v.v.(LatLng) + return +} diff --git a/pkg/value/latlngheight.go b/pkg/value/latlngheight.go new file mode 100644 index 00000000..3001f495 --- /dev/null +++ b/pkg/value/latlngheight.go @@ -0,0 +1,59 @@ +package value + +import "github.com/mitchellh/mapstructure" + +type LatLngHeight struct { + Lat float64 `json:"lat" mapstructure:"lat"` + Lng float64 `json:"lng" mapstructure:"lng"` + Height float64 `json:"height" mapstructure:"height"` +} + +func (l *LatLngHeight) Clone() *LatLngHeight { + if l == nil { + return nil + } + return &LatLngHeight{ + Lat: l.Lat, + Lng: l.Lng, + Height: l.Height, + } +} + +var TypeLatLngHeight Type = "latlngheight" + +var propertyLatLngHeight = TypeProperty{ + I2V: func(i interface{}) (interface{}, bool) { + if v, ok := i.(LatLngHeight); ok { + return v, true + } + + if v, ok := i.(*LatLngHeight); ok { + if v != nil { + return *v, false + } + return nil, false + } + + v := LatLngHeight{} + if err := mapstructure.Decode(i, &v); err == nil { + return v, true + } + return nil, false + }, + V2I: func(v interface{}) (interface{}, bool) { + return v, true + }, + Validate: func(i interface{}) bool { + _, ok := i.(LatLngHeight) + return ok + }, + Compatible: []Type{}, +} + +func (v *Value) ValueLatLngHeight() (vv LatLngHeight, ok bool) { + if v == nil { + return + } + vv, ok = v.v.(LatLngHeight) + return +} diff --git a/pkg/value/number.go b/pkg/value/number.go new file mode 100644 index 00000000..cbcbb8f1 --- /dev/null +++ b/pkg/value/number.go @@ -0,0 +1,117 @@ +package value + +import "encoding/json" + +var TypeNumber Type = "number" + +var propertyNumber = TypeProperty{ + I2V: func(i interface{}) (interface{}, bool) { + switch v := i.(type) { + case float64: + return v, true + case float32: + return float64(v), true + case int: + return float64(v), true + case int8: + return float64(v), true + case int16: + return float64(v), true + case int32: + return float64(v), true + case int64: + return float64(v), true + case uint: + return float64(v), true + case uint8: + return float64(v), true + case uint16: + return float64(v), true + case uint32: + return float64(v), true + case uint64: + return float64(v), true + case uintptr: + return float64(v), true + case json.Number: + if f, err := v.Float64(); err == nil { + return f, true + } + case *float64: + if v != nil { + return *v, true + } + case *float32: + if v != nil { + return float64(*v), true + } + case *int: + if v != nil { + return float64(*v), true + } + case *int8: + if v != nil { + return float64(*v), true + } + case *int16: + if v != nil { + return float64(*v), true + } + case *int32: + if v != nil { + return float64(*v), true + } + case *int64: + if v != nil { + return float64(*v), true + } + case *uint: + if v != nil { + return float64(*v), true + } + case *uint8: + if v != nil { + return float64(*v), true + } + case *uint16: + if v != nil { + return float64(*v), true + } + case *uint32: + if v != nil { + return float64(*v), true + } + case *uint64: + if v != nil { + return float64(*v), true + } + case *uintptr: + if v != nil { + return float64(*v), true + } + case *json.Number: + if v != nil { + if f, err := v.Float64(); err == nil { + return f, true + } + } + } + return nil, false + }, + V2I: func(v interface{}) (interface{}, bool) { + return v, true + }, + Validate: func(i interface{}) bool { + _, ok := i.(float64) + return ok + }, + Compatible: []Type{}, +} + +func (v *Value) ValueNumber() (vv float64, ok bool) { + if v == nil { + return + } + vv, ok = v.v.(float64) + return +} diff --git a/pkg/value/polygon.go b/pkg/value/polygon.go new file mode 100644 index 00000000..3d07ade2 --- /dev/null +++ b/pkg/value/polygon.go @@ -0,0 +1,58 @@ +package value + +import "github.com/mitchellh/mapstructure" + +var TypePolygon Type = "polygon" + +type Polygon []Coordinates + +func PolygonFrom(rings [][]float64) Polygon { + p := make([]Coordinates, 0, len(rings)) + for _, ring := range rings { + p = append(p, CoordinatesFrom(ring)) + } + return p +} + +var propertyPolygon = TypeProperty{ + I2V: func(i interface{}) (interface{}, bool) { + if v, ok := i.(Polygon); ok { + return v, true + } + + if v, ok := i.(*Polygon); ok { + if v != nil { + return *v, true + } + return nil, false + } + + v := Polygon{} + if err := mapstructure.Decode(i, &v); err == nil { + return v, true + } + + v2 := [][]float64{} + if err := mapstructure.Decode(i, &v); err == nil { + return PolygonFrom(v2), true + } + + return nil, false + }, + V2I: func(v interface{}) (interface{}, bool) { + return v, true + }, + Validate: func(i interface{}) bool { + _, ok := i.(Polygon) + return ok + }, + Compatible: []Type{}, +} + +func (v *Value) ValuePolygon() (vv Polygon, ok bool) { + if v == nil { + return + } + vv, ok = v.v.(Polygon) + return +} diff --git a/pkg/value/rect.go b/pkg/value/rect.go new file mode 100644 index 00000000..6d209a19 --- /dev/null +++ b/pkg/value/rect.go @@ -0,0 +1,48 @@ +package value + +import "github.com/mitchellh/mapstructure" + +var TypeRect Type = "rect" + +type Rect struct { + West float64 `json:"west" mapstructure:"west"` + South float64 `json:"south" mapstructure:"south"` + East float64 `json:"east" mapstructure:"east"` + North float64 `json:"north" mapstructure:"north"` +} + +var propertyRect = TypeProperty{ + I2V: func(i interface{}) (interface{}, bool) { + if v, ok := i.(Rect); ok { + return v, true + } else if v, ok := i.(*Rect); ok { + if v != nil { + return *v, true + } + return nil, false + } + + v := Rect{} + if err := mapstructure.Decode(i, &v); err == nil { + return v, false + } + + return nil, false + }, + V2I: func(v interface{}) (interface{}, bool) { + return v, true + }, + Validate: func(i interface{}) bool { + _, ok := i.(Rect) + return ok + }, + Compatible: []Type{}, +} + +func (v *Value) ValueRect() (vv Rect, ok bool) { + if v == nil { + return + } + vv, ok = v.v.(Rect) + return +} diff --git a/pkg/value/ref.go b/pkg/value/ref.go new file mode 100644 index 00000000..19436101 --- /dev/null +++ b/pkg/value/ref.go @@ -0,0 +1,18 @@ +package value + +var TypeRef Type = "ref" + +var propertyRef = TypeProperty{ + I2V: propertyString.I2V, + V2I: propertyString.V2I, + Validate: propertyString.Validate, + // Compatible: []Type{}, +} + +func (v *Value) ValueRef() (vv string, ok bool) { + if v == nil { + return + } + vv, ok = v.v.(string) + return +} diff --git a/pkg/value/string.go b/pkg/value/string.go new file mode 100644 index 00000000..638366c9 --- /dev/null +++ b/pkg/value/string.go @@ -0,0 +1,31 @@ +package value + +var TypeString Type = "string" + +var propertyString = TypeProperty{ + I2V: func(i interface{}) (interface{}, bool) { + if v, ok := i.(string); ok { + return v, true + } + if v, ok := i.(*string); ok { + return *v, true + } + return nil, false + }, + V2I: func(v interface{}) (interface{}, bool) { + return v, true + }, + Validate: func(i interface{}) bool { + _, ok := i.(string) + return ok + }, + Compatible: []Type{}, +} + +func (v *Value) ValueString() (vv string, ok bool) { + if v == nil { + return + } + vv, ok = v.v.(string) + return +} diff --git a/pkg/value/type.go b/pkg/value/type.go new file mode 100644 index 00000000..46564099 --- /dev/null +++ b/pkg/value/type.go @@ -0,0 +1,56 @@ +package value + +type Type string + +type TypeProperty struct { + I2V func(interface{}) (interface{}, bool) + V2I func(interface{}) (interface{}, bool) + Validate func(interface{}) bool + Compatible []Type +} + +type TypePropertyMap = map[Type]TypeProperty + +var TypeUnknown = Type("") + +var defaultTypes = TypePropertyMap{ + TypeBool: propertyBool, + TypeCamera: propertyCamera, + TypeCoordinates: propertyCoordinates, + TypeLatLng: propertyLatLng, + TypeLatLngHeight: propertyLatLngHeight, + TypeNumber: propertyNumber, + TypePolygon: propertyPolygon, + TypeRect: propertyRect, + TypeRef: propertyRef, + TypeString: propertyString, + TypeTypography: propertyTypography, + TypeURL: propertyURL, +} + +func (t Type) Default() bool { + _, ok := defaultTypes[t] + return ok +} + +func (t Type) ValueFrom(i interface{}, p TypePropertyMap) *Value { + if t == TypeUnknown || i == nil { + return nil + } + + if p != nil { + if vt, ok := p[t]; ok && vt.I2V != nil { + if v, ok2 := vt.I2V(i); ok2 { + return &Value{p: p, v: v, t: t} + } + } + } + + if vt, ok := defaultTypes[t]; ok && vt.I2V != nil { + if v, ok2 := vt.I2V(i); ok2 { + return &Value{p: p, v: v, t: t} + } + } + + return nil +} diff --git a/pkg/value/type_test.go b/pkg/value/type_test.go new file mode 100644 index 00000000..0cf3e638 --- /dev/null +++ b/pkg/value/type_test.go @@ -0,0 +1,109 @@ +package value + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestType_Default(t *testing.T) { + tests := []struct { + name string + tr Type + want bool + }{ + { + name: "default", + tr: TypeString, + want: true, + }, + { + name: "custom", + tr: Type("foo"), + want: false, + }, + { + name: "unknown", + tr: TypeUnknown, + want: false, + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + assert.Equal(t, tt.want, tt.tr.Default()) + }) + } +} + +func TestType_ValueFrom(t *testing.T) { + tpm := TypePropertyMap{ + Type("foo"): TypeProperty{ + I2V: func(v interface{}) (interface{}, bool) { + return v.(string) + "a", true + }, + }, + } + + type args struct { + i interface{} + p TypePropertyMap + } + + tests := []struct { + name string + tr Type + args args + want *Value + }{ + { + name: "default type", + tr: TypeString, + args: args{ + i: "hoge", + }, + want: &Value{t: TypeString, v: "hoge"}, + }, + { + name: "custom type", + tr: Type("foo"), + args: args{ + i: "hoge", + p: tpm, + }, + want: &Value{p: tpm, t: Type("foo"), v: "hogea"}, + }, + { + name: "nil", + tr: TypeString, + args: args{}, + want: nil, + }, + { + name: "unknown type", + tr: TypeUnknown, + args: args{ + i: "hoge", + }, + want: nil, + }, + { + name: "unknown type + custom type", + tr: Type("bar"), + args: args{ + i: "hoge", + p: tpm, + }, + want: nil, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + assert.Equal(t, tt.want, tt.tr.ValueFrom(tt.args.i, tt.args.p)) + }) + } +} diff --git a/pkg/value/typography.go b/pkg/value/typography.go new file mode 100644 index 00000000..52ba250b --- /dev/null +++ b/pkg/value/typography.go @@ -0,0 +1,130 @@ +package value + +import "github.com/mitchellh/mapstructure" + +type TextAlign string + +const ( + TextAlignLeft TextAlign = "left" + TextAlignCenter TextAlign = "center" + TextAlignRight TextAlign = "right" + TextAlignJustify TextAlign = "justify" + TextAlignJustifyAll TextAlign = "justify_all" +) + +func TextAlignFrom(t string) (TextAlign, bool) { + switch TextAlign(t) { + case TextAlignLeft: + return TextAlignLeft, true + case TextAlignCenter: + return TextAlignCenter, true + case TextAlignRight: + return TextAlignRight, true + case TextAlignJustify: + return TextAlignJustify, true + case TextAlignJustifyAll: + return TextAlignJustifyAll, true + } + return TextAlign(""), false +} + +func TextAlignFromRef(t *string) *TextAlign { + if t == nil { + return nil + } + var t2 TextAlign + switch TextAlign(*t) { + case TextAlignLeft: + t2 = TextAlignLeft + case TextAlignCenter: + t2 = TextAlignCenter + case TextAlignRight: + t2 = TextAlignRight + case TextAlignJustify: + t2 = TextAlignJustify + case TextAlignJustifyAll: + t2 = TextAlignJustifyAll + default: + return nil + } + return &t2 +} + +func (t TextAlign) String() string { + return string(t) +} + +func (t *TextAlign) StringRef() *string { + if t == nil { + return nil + } + t2 := string(*t) + return &t2 +} + +type Typography struct { + FontFamily *string `json:"fontFamily" mapstructure:"fontFamily"` + FontWeight *string `json:"fontWeight" mapstructure:"fontWeight"` + FontSize *int `json:"fontSize" mapstructure:"fontSize"` + Color *string `json:"color" mapstructure:"color"` + TextAlign *TextAlign `json:"textAlign" mapstructure:"textAlign"` + Bold *bool `json:"bold" mapstructure:"bold"` + Italic *bool `json:"italic" mapstructure:"italic"` + Underline *bool `json:"underline" mapstructure:"underline"` +} + +func (t *Typography) Clone() *Typography { + if t == nil { + return nil + } + return &Typography{ + FontFamily: t.FontFamily, + FontWeight: t.FontWeight, + FontSize: t.FontSize, + Color: t.Color, + TextAlign: t.TextAlign, + Bold: t.Bold, + Italic: t.Italic, + Underline: t.Underline, + } +} + +var TypeTypography Type = "typography" + +var propertyTypography = TypeProperty{ + I2V: func(i interface{}) (interface{}, bool) { + if v, ok := i.(Typography); ok { + return v, true + } + + if v, ok := i.(*Typography); ok { + if v != nil { + return *v, true + } + return nil, false + } + + v := Typography{} + if err := mapstructure.Decode(i, &v); err == nil { + return v, true + } + + return nil, false + }, + V2I: func(v interface{}) (interface{}, bool) { + return v, true + }, + Validate: func(i interface{}) bool { + _, ok := i.(Typography) + return ok + }, + Compatible: []Type{}, +} + +func (v *Value) ValueTypography() (vv Typography, ok bool) { + if v == nil { + return + } + vv, ok = v.v.(Typography) + return +} diff --git a/pkg/value/url.go b/pkg/value/url.go new file mode 100644 index 00000000..7d7486a5 --- /dev/null +++ b/pkg/value/url.go @@ -0,0 +1,51 @@ +package value + +import "net/url" + +var TypeURL Type = "url" + +var propertyURL = TypeProperty{ + I2V: func(i interface{}) (interface{}, bool) { + if v, ok := i.(url.URL); ok { + return &v, true + } + + if v, ok := i.(*url.URL); ok { + if v == nil { + return nil, false + } + return v, true + } + + if v, ok := i.(string); ok { + if u, err := url.Parse(v); err == nil { + return u, true + } + } + + return nil, false + }, + V2I: func(v interface{}) (interface{}, bool) { + u, ok := v.(*url.URL) + if !ok { + return nil, false + } + if u == nil { + return "", true + } + return u.String(), true + }, + Validate: func(i interface{}) bool { + _, ok := i.(*url.URL) + return ok + }, + Compatible: []Type{}, +} + +func (v *Value) ValueURL() (vv *url.URL, ok bool) { + if v == nil { + return + } + vv, ok = v.v.(*url.URL) + return +} diff --git a/pkg/value/value.go b/pkg/value/value.go new file mode 100644 index 00000000..7c342f56 --- /dev/null +++ b/pkg/value/value.go @@ -0,0 +1,82 @@ +package value + +import ( + "encoding/json" +) + +type Value struct { + p TypePropertyMap + v interface{} + t Type +} + +func (v *Value) IsEmpty() bool { + return v == nil || v.t == TypeUnknown || v.v == nil +} + +func (v *Value) Clone() *Value { + if v.IsEmpty() { + return nil + } + return v.t.ValueFrom(v.v, v.p) +} + +func (v *Value) Value() interface{} { + if v == nil { + return nil + } + return v.v +} + +func (v *Value) Type() Type { + if v == nil { + return TypeUnknown + } + return v.t +} + +func (v *Value) TypeProperty() (tp TypeProperty) { + if v.IsEmpty() { + return + } + if v.p != nil { + if tp, ok := v.p[v.t]; ok { + return tp + } + } + if tp, ok := defaultTypes[v.t]; ok { + return tp + } + return +} + +// Interface converts the value into generic representation +func (v *Value) Interface() interface{} { + if v == nil || v.t == TypeUnknown { + return nil + } + + if tp := v.TypeProperty(); tp.V2I != nil { + if i, ok2 := tp.V2I(v.v); ok2 { + return i + } + } + + return nil +} + +func (v *Value) Validate() bool { + if v == nil || v.t == TypeUnknown { + return false + } + + if tp := v.TypeProperty(); tp.Validate != nil { + return tp.Validate(v) + } + + return false +} + +func (v *Value) MarshalJSON() ([]byte, error) { + return json.Marshal(v.Interface()) +} diff --git a/pkg/value/value_test.go b/pkg/value/value_test.go new file mode 100644 index 00000000..4b3cf415 --- /dev/null +++ b/pkg/value/value_test.go @@ -0,0 +1,249 @@ +package value + +import ( + "net/url" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestValue_IsEmpty(t *testing.T) { + tests := []struct { + name string + value *Value + want bool + }{ + { + name: "empty", + want: true, + }, + { + name: "nil", + want: true, + }, + { + name: "non-empty", + value: &Value{ + t: Type("hoge"), + v: "foo", + }, + want: false, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + assert.Equal(t, tt.want, tt.value.IsEmpty()) + }) + } +} + +func TestValue_Clone(t *testing.T) { + tests := []struct { + name string + value *Value + wantnil bool + }{ + { + name: "ok", + value: &Value{ + t: TypeString, + v: "foo", + }, + }, + { + name: "custom type property", + value: &Value{ + t: Type("hoge"), + v: "foo", + p: TypePropertyMap{Type("hoge"): TypeProperty{ + I2V: func(i interface{}) (interface{}, bool) { return i, true }, + }}, + }, + }, + { + name: "nil", + }, + { + name: "empty", + value: &Value{}, + wantnil: true, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + want := tt.value + if tt.wantnil { + want = nil + } + assert.Equal(t, want, tt.value.Clone()) + }) + } +} + +func TestValue_Value(t *testing.T) { + u, _ := url.Parse("https://reearth.io") + tests := []struct { + name string + value *Value + want interface{} + }{ + { + name: "ok", + value: &Value{t: TypeURL, v: u}, + want: u, + }, + { + name: "empty", + value: &Value{}, + }, + { + name: "nil", + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + if tt.want == nil { + assert.Nil(t, tt.value.Value()) + } else { + assert.Same(t, tt.want, tt.value.Value()) + } + }) + } +} + +func TestValue_Type(t *testing.T) { + tests := []struct { + name string + value *Value + want Type + }{ + { + name: "ok", + value: &Value{t: TypeString}, + want: TypeString, + }, + { + name: "empty", + value: &Value{}, + want: TypeUnknown, + }, + { + name: "nil", + want: TypeUnknown, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + assert.Equal(t, tt.want, tt.value.Type()) + }) + } +} + +func TestValue_TypeProperty(t *testing.T) { + typePropertyHoge := TypeProperty{} + + tests := []struct { + name string + value *Value + want TypeProperty + }{ + // { + // name: "default type", + // value: &Value{ + // v: "string", + // t: TypeString, + // }, + // want: defaultTypes[TypeString], + // }, + { + name: "custom type", + value: &Value{ + v: "string", + t: Type("hoge"), + p: TypePropertyMap{ + Type("hoge"): typePropertyHoge, + }, + }, + want: typePropertyHoge, + }, + { + name: "empty", + value: &Value{}, + want: TypeProperty{}, + }, + { + name: "nil", + want: TypeProperty{}, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + assert.Equal(t, tt.want, tt.value.TypeProperty()) + }) + } +} + +func TestValue_Interface(t *testing.T) { + tests := []struct { + name string + value *Value + want interface{} + }{ + { + name: "string", + value: &Value{t: TypeString, v: "hoge"}, + want: "hoge", + }, + { + name: "latlng", + value: &Value{t: TypeLatLng, v: LatLng{Lat: 1, Lng: 2}}, + want: LatLng{Lat: 1, Lng: 2}, + }, + { + name: "custom", + value: &Value{ + p: TypePropertyMap{ + Type("foo"): TypeProperty{ + V2I: func(v interface{}) (interface{}, bool) { + return v.(string) + "bar", true + }, + }, + }, + t: Type("foo"), + v: "foo", + }, + want: "foobar", + }, + { + name: "empty", + value: &Value{}, + want: nil, + }, + { + name: "nil", + value: nil, + want: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.want, tt.value.Interface()) + }) + } +} From b9824e14017adbdc4fb07a526162304d746cca6d Mon Sep 17 00:00:00 2001 From: rot1024 Date: Wed, 10 Nov 2021 21:24:10 +0900 Subject: [PATCH 02/12] add optional value type --- pkg/value/optional.go | 51 +++++++ pkg/value/optional_test.go | 300 +++++++++++++++++++++++++++++++++++++ 2 files changed, 351 insertions(+) create mode 100644 pkg/value/optional.go create mode 100644 pkg/value/optional_test.go diff --git a/pkg/value/optional.go b/pkg/value/optional.go new file mode 100644 index 00000000..2a904c15 --- /dev/null +++ b/pkg/value/optional.go @@ -0,0 +1,51 @@ +package value + +type OptionalValue struct { + t Type + v *Value +} + +func NewOptionalValue(t Type, v *Value) *OptionalValue { + if t == TypeUnknown || (v != nil && v.Type() != t) { + return nil + } + return &OptionalValue{ + t: t, + v: v, + } +} + +func OptionalValueFrom(v *Value) *OptionalValue { + if v.Type() == TypeUnknown { + return nil + } + return &OptionalValue{ + t: v.Type(), + v: v, + } +} + +func (ov *OptionalValue) Type() Type { + if ov == nil { + return TypeUnknown + } + return ov.t +} + +func (ov *OptionalValue) Value() *Value { + if ov == nil || ov.t == TypeUnknown || ov.v == nil { + return nil + } + return ov.v.Clone() +} + +func (ov *OptionalValue) TypeAndValue() (Type, *Value) { + return ov.Type(), ov.Value() +} + +func (ov *OptionalValue) SetValue(v *Value) { + if ov == nil || ov.t == TypeUnknown || (v != nil && ov.t != v.Type()) { + return + } + ov.v = v.Clone() +} diff --git a/pkg/value/optional_test.go b/pkg/value/optional_test.go new file mode 100644 index 00000000..8dc15d85 --- /dev/null +++ b/pkg/value/optional_test.go @@ -0,0 +1,300 @@ +package value + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestNewOptionalValue(t *testing.T) { + type args struct { + t Type + v *Value + } + tests := []struct { + name string + args args + want *OptionalValue + }{ + { + name: "default type", + args: args{ + t: TypeString, + v: TypeString.ValueFrom("foo", nil), + }, + want: &OptionalValue{t: TypeString, v: TypeString.ValueFrom("foo", nil)}, + }, + { + name: "custom type", + args: args{ + t: Type("foo"), + v: &Value{t: Type("foo")}, + }, + want: &OptionalValue{t: Type("foo"), v: &Value{t: Type("foo")}}, + }, + { + name: "nil value", + args: args{ + t: Type("foo"), + }, + want: &OptionalValue{t: Type("foo"), v: nil}, + }, + { + name: "invalid value", + args: args{ + t: TypeNumber, + v: TypeString.ValueFrom("foo", nil), + }, + want: nil, + }, + { + name: "invalid type", + args: args{ + t: TypeUnknown, + v: TypeString.ValueFrom("foo", nil), + }, + want: nil, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + assert.Equal(t, tt.want, NewOptionalValue(tt.args.t, tt.args.v)) + }) + } +} + +func TestOptionalValueFrom(t *testing.T) { + type args struct { + v *Value + } + tests := []struct { + name string + args args + want *OptionalValue + }{ + { + name: "default type", + args: args{ + v: TypeString.ValueFrom("foo", nil), + }, + want: &OptionalValue{t: TypeString, v: TypeString.ValueFrom("foo", nil)}, + }, + { + name: "custom type", + args: args{ + v: &Value{t: Type("foo")}, + }, + want: &OptionalValue{t: Type("foo"), v: &Value{t: Type("foo")}}, + }, + { + name: "invalid value", + args: args{ + v: &Value{v: "string"}, + }, + want: nil, + }, + { + name: "nil value", + args: args{}, + want: nil, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + assert.Equal(t, tt.want, OptionalValueFrom(tt.args.v)) + }) + } +} + +func TestOptionalValue_Type(t *testing.T) { + tests := []struct { + name string + value *OptionalValue + want Type + }{ + { + name: "ok", + value: &OptionalValue{t: Type("foo")}, + want: Type("foo"), + }, + { + name: "empty", + value: &OptionalValue{}, + want: TypeUnknown, + }, + { + name: "nil", + value: nil, + want: TypeUnknown, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + assert.Equal(t, tt.want, tt.value.Type()) + }) + } +} + +func TestOptionalValue_Value(t *testing.T) { + tests := []struct { + name string + value *OptionalValue + want *Value + }{ + { + name: "ok", + value: &OptionalValue{t: TypeString, v: &Value{t: TypeString, v: "foobar"}}, + want: &Value{t: TypeString, v: "foobar"}, + }, + { + name: "empty", + value: &OptionalValue{}, + want: nil, + }, + { + name: "nil", + value: nil, + want: nil, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + res := tt.value.Value() + assert.Equal(t, tt.want, res) + if res != nil { + assert.NotSame(t, tt.want, res) + } + }) + } +} + +func TestOptionalValue_TypeAndValue(t *testing.T) { + tests := []struct { + name string + value *OptionalValue + wantt Type + wantv *Value + }{ + { + name: "ok", + value: &OptionalValue{t: TypeString, v: &Value{t: TypeString, v: "foobar"}}, + wantt: TypeString, + wantv: &Value{t: TypeString, v: "foobar"}, + }, + { + name: "empty", + value: &OptionalValue{}, + wantt: TypeUnknown, + wantv: nil, + }, + { + name: "nil", + value: nil, + wantt: TypeUnknown, + wantv: nil, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + ty, tv := tt.value.TypeAndValue() + assert.Equal(t, tt.wantt, ty) + assert.Equal(t, tt.wantv, tv) + if tv != nil { + assert.NotSame(t, tt.wantv, tv) + } + }) + } +} + +func TestOptionalValue_SetValue(t *testing.T) { + type args struct { + v *Value + } + tests := []struct { + name string + value *OptionalValue + args args + invalid bool + }{ + { + name: "set", + value: &OptionalValue{ + t: TypeString, + v: &Value{t: TypeString, v: "foobar"}, + }, + args: args{v: &Value{t: TypeString, v: "bar"}}, + }, + { + name: "set to nil", + value: &OptionalValue{ + t: TypeString, + }, + args: args{v: &Value{t: TypeString, v: "bar"}}, + }, + { + name: "invalid value", + value: &OptionalValue{ + t: TypeNumber, + v: &Value{t: TypeNumber, v: 1}, + }, + args: args{v: &Value{t: TypeString, v: "bar"}}, + invalid: true, + }, + { + name: "nil value", + value: &OptionalValue{ + t: TypeNumber, + v: &Value{t: TypeNumber, v: 1}, + }, + }, + { + name: "empty", + value: &OptionalValue{}, + args: args{v: &Value{t: TypeString, v: "bar"}}, + invalid: true, + }, + { + name: "nil", + args: args{v: &Value{t: TypeString, v: "bar"}}, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + var v *Value + if tt.value != nil { + v = tt.value.v + } + + tt.value.SetValue(tt.args.v) + + if tt.value != nil { + if tt.invalid { + assert.Same(t, v, tt.value.v) + } else { + assert.Equal(t, tt.args.v, tt.value.v) + if tt.args.v != nil { + assert.NotSame(t, tt.args.v, tt.value.v) + } + } + } + }) + } +} From 0884d391f4c9b963f59a481b5ce47f79897668b6 Mon Sep 17 00:00:00 2001 From: rot1024 Date: Thu, 11 Nov 2021 18:36:33 +0900 Subject: [PATCH 03/12] add tests and delete uncommon types --- pkg/value/camera.go | 67 ----------------- pkg/value/latlng_test.go | 41 +++++++++++ pkg/value/latlngheight_test.go | 43 +++++++++++ pkg/value/type.go | 2 - pkg/value/typography.go | 130 --------------------------------- 5 files changed, 84 insertions(+), 199 deletions(-) delete mode 100644 pkg/value/camera.go create mode 100644 pkg/value/latlng_test.go create mode 100644 pkg/value/latlngheight_test.go delete mode 100644 pkg/value/typography.go diff --git a/pkg/value/camera.go b/pkg/value/camera.go deleted file mode 100644 index ec717f90..00000000 --- a/pkg/value/camera.go +++ /dev/null @@ -1,67 +0,0 @@ -package value - -import "github.com/mitchellh/mapstructure" - -type Camera struct { - Lat float64 `json:"lat" mapstructure:"lat"` - Lng float64 `json:"lng" mapstructure:"lng"` - Altitude float64 `json:"altitude" mapstructure:"altitude"` - Heading float64 `json:"heading" mapstructure:"heading"` - Pitch float64 `json:"pitch" mapstructure:"pitch"` - Roll float64 `json:"roll" mapstructure:"roll"` - FOV float64 `json:"fov" mapstructure:"fov"` -} - -func (c *Camera) Clone() *Camera { - if c == nil { - return nil - } - return &Camera{ - Lat: c.Lat, - Lng: c.Lng, - Altitude: c.Altitude, - Heading: c.Heading, - Pitch: c.Pitch, - Roll: c.Roll, - FOV: c.FOV, - } -} - -var TypeCamera Type = "camera" - -var propertyCamera = TypeProperty{ - I2V: func(i interface{}) (interface{}, bool) { - if v, ok := i.(Camera); ok { - return v, true - } - - if v, ok := i.(*Camera); ok { - if v != nil { - return *v, true - } - return nil, false - } - - v := Camera{} - if err := mapstructure.Decode(i, &v); err == nil { - return v, true - } - return nil, false - }, - V2I: func(v interface{}) (interface{}, bool) { - return v, true - }, - Validate: func(i interface{}) bool { - _, ok := i.(Camera) - return ok - }, - Compatible: []Type{}, -} - -func (v *Value) ValueCamera() (vv Camera, ok bool) { - if v == nil { - return - } - vv, ok = v.v.(Camera) - return -} diff --git a/pkg/value/latlng_test.go b/pkg/value/latlng_test.go new file mode 100644 index 00000000..b774fe0b --- /dev/null +++ b/pkg/value/latlng_test.go @@ -0,0 +1,41 @@ +package value + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestLatLng_Clone(t *testing.T) { + tests := []struct { + Name string + LL, Expected *LatLng + }{ + { + Name: "nil latlng", + }, + { + Name: "cloned", + LL: &LatLng{ + Lat: 10, + Lng: 11, + }, + Expected: &LatLng{ + Lat: 10, + Lng: 11, + }, + }, + } + + for _, tc := range tests { + tc := tc + t.Run(tc.Name, func(tt *testing.T) { + tt.Parallel() + res := tc.LL.Clone() + assert.Equal(tt, tc.Expected, res) + if tc.Expected != nil { + assert.NotSame(tt, tc.Expected, res) + } + }) + } +} diff --git a/pkg/value/latlngheight_test.go b/pkg/value/latlngheight_test.go new file mode 100644 index 00000000..5578836c --- /dev/null +++ b/pkg/value/latlngheight_test.go @@ -0,0 +1,43 @@ +package value + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestLatLngHeight_Clone(t *testing.T) { + testCases := []struct { + Name string + LL, Expected *LatLngHeight + }{ + { + Name: "nil LatLngHeight", + }, + { + Name: "cloned", + LL: &LatLngHeight{ + Lat: 10, + Lng: 11, + Height: 12, + }, + Expected: &LatLngHeight{ + Lat: 10, + Lng: 11, + Height: 12, + }, + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.Name, func(tt *testing.T) { + tt.Parallel() + res := tc.LL.Clone() + assert.Equal(tt, tc.Expected, res) + if tc.Expected != nil { + assert.NotSame(tt, tc.Expected, res) + } + }) + } +} diff --git a/pkg/value/type.go b/pkg/value/type.go index 46564099..8f03c3d8 100644 --- a/pkg/value/type.go +++ b/pkg/value/type.go @@ -15,7 +15,6 @@ var TypeUnknown = Type("") var defaultTypes = TypePropertyMap{ TypeBool: propertyBool, - TypeCamera: propertyCamera, TypeCoordinates: propertyCoordinates, TypeLatLng: propertyLatLng, TypeLatLngHeight: propertyLatLngHeight, @@ -24,7 +23,6 @@ var defaultTypes = TypePropertyMap{ TypeRect: propertyRect, TypeRef: propertyRef, TypeString: propertyString, - TypeTypography: propertyTypography, TypeURL: propertyURL, } diff --git a/pkg/value/typography.go b/pkg/value/typography.go deleted file mode 100644 index 52ba250b..00000000 --- a/pkg/value/typography.go +++ /dev/null @@ -1,130 +0,0 @@ -package value - -import "github.com/mitchellh/mapstructure" - -type TextAlign string - -const ( - TextAlignLeft TextAlign = "left" - TextAlignCenter TextAlign = "center" - TextAlignRight TextAlign = "right" - TextAlignJustify TextAlign = "justify" - TextAlignJustifyAll TextAlign = "justify_all" -) - -func TextAlignFrom(t string) (TextAlign, bool) { - switch TextAlign(t) { - case TextAlignLeft: - return TextAlignLeft, true - case TextAlignCenter: - return TextAlignCenter, true - case TextAlignRight: - return TextAlignRight, true - case TextAlignJustify: - return TextAlignJustify, true - case TextAlignJustifyAll: - return TextAlignJustifyAll, true - } - return TextAlign(""), false -} - -func TextAlignFromRef(t *string) *TextAlign { - if t == nil { - return nil - } - var t2 TextAlign - switch TextAlign(*t) { - case TextAlignLeft: - t2 = TextAlignLeft - case TextAlignCenter: - t2 = TextAlignCenter - case TextAlignRight: - t2 = TextAlignRight - case TextAlignJustify: - t2 = TextAlignJustify - case TextAlignJustifyAll: - t2 = TextAlignJustifyAll - default: - return nil - } - return &t2 -} - -func (t TextAlign) String() string { - return string(t) -} - -func (t *TextAlign) StringRef() *string { - if t == nil { - return nil - } - t2 := string(*t) - return &t2 -} - -type Typography struct { - FontFamily *string `json:"fontFamily" mapstructure:"fontFamily"` - FontWeight *string `json:"fontWeight" mapstructure:"fontWeight"` - FontSize *int `json:"fontSize" mapstructure:"fontSize"` - Color *string `json:"color" mapstructure:"color"` - TextAlign *TextAlign `json:"textAlign" mapstructure:"textAlign"` - Bold *bool `json:"bold" mapstructure:"bold"` - Italic *bool `json:"italic" mapstructure:"italic"` - Underline *bool `json:"underline" mapstructure:"underline"` -} - -func (t *Typography) Clone() *Typography { - if t == nil { - return nil - } - return &Typography{ - FontFamily: t.FontFamily, - FontWeight: t.FontWeight, - FontSize: t.FontSize, - Color: t.Color, - TextAlign: t.TextAlign, - Bold: t.Bold, - Italic: t.Italic, - Underline: t.Underline, - } -} - -var TypeTypography Type = "typography" - -var propertyTypography = TypeProperty{ - I2V: func(i interface{}) (interface{}, bool) { - if v, ok := i.(Typography); ok { - return v, true - } - - if v, ok := i.(*Typography); ok { - if v != nil { - return *v, true - } - return nil, false - } - - v := Typography{} - if err := mapstructure.Decode(i, &v); err == nil { - return v, true - } - - return nil, false - }, - V2I: func(v interface{}) (interface{}, bool) { - return v, true - }, - Validate: func(i interface{}) bool { - _, ok := i.(Typography) - return ok - }, - Compatible: []Type{}, -} - -func (v *Value) ValueTypography() (vv Typography, ok bool) { - if v == nil { - return - } - vv, ok = v.v.(Typography) - return -} From ccc9b126548a7ebd33e87cfd05561f7ed0783c4f Mon Sep 17 00:00:00 2001 From: rot1024 Date: Wed, 17 Nov 2021 18:45:22 +0900 Subject: [PATCH 04/12] refactor --- .../adapter/gql/gqlmodel/convert_dataset.go | 45 +- .../adapter/gql/gqlmodel/convert_property.go | 173 +- .../adapter/gql/gqlmodel/convert_value.go | 147 ++ .../adapter/gql/resolver_mutation_property.go | 19 +- internal/infrastructure/memory/dataset.go | 6 +- .../infrastructure/memory/dataset_schema.go | 2 +- .../infrastructure/mongo/dataset_schema.go | 2 +- .../infrastructure/mongo/mongodoc/dataset.go | 14 +- .../mongo/mongodoc/dataset_schema.go | 15 +- .../infrastructure/mongo/mongodoc/property.go | 12 +- .../mongo/mongodoc/property_schema.go | 2 +- .../infrastructure/mongo/mongodoc/util.go | 3 + internal/usecase/interactor/dataset.go | 10 +- internal/usecase/interactor/property.go | 2 +- internal/usecase/repo/dataset_schema.go | 2 +- pkg/dataset/builder.go | 20 +- pkg/dataset/csvparser.go | 36 +- pkg/dataset/csvparser_test.go | 14 +- pkg/dataset/dataset.go | 45 +- pkg/dataset/dataset_test.go | 89 + pkg/dataset/field.go | 41 +- pkg/dataset/graph_iterator.go | 11 +- pkg/dataset/graph_iterator_test.go | 3 +- pkg/dataset/list.go | 27 +- pkg/dataset/list_test.go | 6 +- pkg/dataset/schema.go | 19 +- pkg/dataset/schema_builder.go | 31 +- pkg/dataset/schema_field.go | 13 +- pkg/dataset/schema_field_builder.go | 14 +- pkg/dataset/schema_field_diff.go | 6 +- pkg/dataset/source.go | 9 - pkg/dataset/value.go | 385 ++--- pkg/dataset/value_optional.go | 65 + pkg/dataset/value_optional_test.go | 272 ++++ pkg/dataset/value_test.go | 63 - pkg/layer/decoding/common.go | 120 +- pkg/layer/decoding/reearth.go | 11 +- pkg/layer/decoding/reearth_test.go | 6 +- pkg/layer/encoding/common.go | 11 +- pkg/layer/encoding/common_test.go | 19 +- pkg/layer/encoding/czml.go | 242 +-- pkg/layer/encoding/czml_test.go | 696 +++----- pkg/layer/encoding/geojson.go | 149 +- pkg/layer/encoding/geojson_test.go | 574 ++----- pkg/layer/encoding/kml.go | 392 ++--- pkg/layer/encoding/kml_test.go | 716 +++------ pkg/layer/encoding/shp.go | 148 +- pkg/layer/encoding/shp_test.go | 396 ++--- pkg/layer/layerops/initializer.go | 2 +- pkg/plugin/manifest/convert.go | 10 +- pkg/plugin/manifest/convert_test.go | 2 +- pkg/plugin/manifest/parser_test.go | 4 +- pkg/property/builder_test.go | 12 +- pkg/property/condition_test.go | 10 +- pkg/property/field.go | 8 +- pkg/property/field_builder_test.go | 18 +- pkg/property/field_test.go | 27 +- pkg/property/group_builder_test.go | 4 +- pkg/property/group_list_test.go | 10 +- pkg/property/group_test.go | 24 +- pkg/property/initializer_test.go | 12 +- pkg/property/item_test.go | 2 +- pkg/property/link_test.go | 6 +- pkg/property/merged_test.go | 50 +- pkg/property/property_test.go | 12 +- pkg/property/schema_field_builder.go | 2 +- pkg/property/schema_field_test.go | 12 +- pkg/property/schema_group_builder_test.go | 8 +- pkg/property/sealed.go | 7 + pkg/property/sealed_test.go | 106 +- pkg/property/value.go | 349 ++-- pkg/property/value_camera.go | 69 + pkg/property/value_camera_test.go | 51 + pkg/property/value_converter.go | 41 - pkg/property/value_converter_test.go | 120 -- pkg/property/value_test.go | 354 +--- pkg/property/value_type.go | 603 ------- pkg/property/value_type_test.go | 1426 ----------------- pkg/property/value_typography.go | 132 ++ pkg/property/value_typography_test.go | 204 +++ pkg/scene/builder/builder_test.go | 22 +- pkg/scene/builder/encoder_test.go | 5 +- pkg/value/ref.go | 28 +- pkg/value/string.go | 1 - 84 files changed, 2834 insertions(+), 6022 deletions(-) create mode 100644 internal/adapter/gql/gqlmodel/convert_value.go create mode 100644 pkg/dataset/dataset_test.go delete mode 100644 pkg/dataset/source.go create mode 100644 pkg/dataset/value_optional.go create mode 100644 pkg/dataset/value_optional_test.go delete mode 100644 pkg/dataset/value_test.go create mode 100644 pkg/property/value_camera.go create mode 100644 pkg/property/value_camera_test.go delete mode 100644 pkg/property/value_converter.go delete mode 100644 pkg/property/value_converter_test.go delete mode 100644 pkg/property/value_type.go delete mode 100644 pkg/property/value_type_test.go create mode 100644 pkg/property/value_typography.go create mode 100644 pkg/property/value_typography_test.go diff --git a/internal/adapter/gql/gqlmodel/convert_dataset.go b/internal/adapter/gql/gqlmodel/convert_dataset.go index 1239964f..616fbb6d 100644 --- a/internal/adapter/gql/gqlmodel/convert_dataset.go +++ b/internal/adapter/gql/gqlmodel/convert_dataset.go @@ -1,41 +1,12 @@ package gqlmodel import ( - "net/url" - "github.com/reearth/reearth-backend/pkg/dataset" - "github.com/reearth/reearth-backend/pkg/id" ) func ToDatasetValue(v *dataset.Value) *interface{} { - var res interface{} - if v == nil { - return nil - } - switch v2 := v.Value().(type) { - case bool: - res = v2 - case float64: - res = v2 - case string: - res = v2 - case id.ID: - res = v2.String() - case *url.URL: - res = v2.String() - case dataset.LatLng: - res = LatLng{ - Lat: v2.Lat, - Lng: v2.Lng, - } - case dataset.LatLngHeight: - res = LatLngHeight{ - Lat: v2.Lat, - Lng: v2.Lng, - Height: v2.Height, - } - } - return &res + i := valueInterfaceToGqlValue(v.Value()) + return &i } func ToDatasetValueType(t dataset.ValueType) ValueType { @@ -58,10 +29,6 @@ func ToDatasetValueType(t dataset.ValueType) ValueType { return "" } -func ToDatasetSource(ds dataset.Source) string { - return ds.String() -} - func ToDatasetField(f *dataset.Field, parent *dataset.Dataset) *DatasetField { if f == nil || parent == nil { return nil @@ -72,7 +39,7 @@ func ToDatasetField(f *dataset.Field, parent *dataset.Dataset) *DatasetField { FieldID: f.Field().ID(), Type: ToDatasetValueType(f.Type()), Value: ToDatasetValue(f.Value()), - Source: ToDatasetSource(f.Source()), + Source: f.Source(), } } @@ -90,7 +57,7 @@ func ToDataset(ds *dataset.Dataset) *Dataset { return &Dataset{ ID: ds.ID().ID(), SchemaID: ds.Schema().ID(), - Source: ToDatasetSource(ds.Source()), + Source: ds.Source(), Fields: fields, } } @@ -108,14 +75,14 @@ func ToDatasetSchema(ds *dataset.Schema) *DatasetSchema { Name: f.Name(), Type: ToDatasetValueType(f.Type()), SchemaID: ds.ID().ID(), - Source: ToDatasetSource(f.Source()), + Source: f.Source(), RefID: f.Ref().IDRef(), }) } return &DatasetSchema{ ID: ds.ID().ID(), - Source: ToDatasetSource(ds.Source()), + Source: ds.Source(), Name: ds.Name(), SceneID: ds.Scene().ID(), RepresentativeFieldID: ds.RepresentativeField().IDRef().IDRef(), diff --git a/internal/adapter/gql/gqlmodel/convert_property.go b/internal/adapter/gql/gqlmodel/convert_property.go index 746e4c82..24059bf3 100644 --- a/internal/adapter/gql/gqlmodel/convert_property.go +++ b/internal/adapter/gql/gqlmodel/convert_property.go @@ -1,7 +1,6 @@ package gqlmodel import ( - "net/url" "strings" "github.com/reearth/reearth-backend/pkg/id" @@ -14,27 +13,6 @@ func ToPropertyValue(v *property.Value) *interface{} { return nil } switch v2 := v.Value().(type) { - case bool: - res = v2 - case float64: - res = v2 - case string: - res = v2 - case id.ID: - res = v2.String() - case *url.URL: - res = v2.String() - case property.LatLng: - res = LatLng{ - Lat: v2.Lat, - Lng: v2.Lng, - } - case property.LatLngHeight: - res = LatLngHeight{ - Lat: v2.Lat, - Lng: v2.Lng, - Height: v2.Height, - } case property.Camera: res = Camera{ Lat: v2.Lat, @@ -56,30 +34,8 @@ func ToPropertyValue(v *property.Value) *interface{} { Italic: v2.Italic, Underline: v2.Underline, } - case property.Coordinates: - res2 := make([]LatLngHeight, 0, len(v2)) - for _, c := range v2 { - res2 = append(res2, LatLngHeight{ - Lat: c.Lat, - Lng: c.Lng, - Height: c.Height, - }) - } - res = res2 - case property.Polygon: - res2 := make([][]LatLngHeight, 0, len(v2)) - for _, d := range v2 { - coord := make([]LatLngHeight, 0, len(d)) - for _, c := range d { - coord = append(coord, LatLngHeight{ - Lat: c.Lat, - Lng: c.Lng, - Height: c.Height, - }) - } - res2 = append(res2, coord) - } - res = res2 + default: + res = valueInterfaceToGqlValue(v2) } return &res } @@ -106,88 +62,8 @@ func ToTextAlign(t *property.TextAlign) *TextAlign { return &t3 } -func ToPropertyValueType(t property.ValueType) ValueType { - switch t { - case property.ValueTypeBool: - return ValueTypeBool - case property.ValueTypeNumber: - return ValueTypeNumber - case property.ValueTypeString: - return ValueTypeString - case property.ValueTypeLatLng: - return ValueTypeLatlng - case property.ValueTypeLatLngHeight: - return ValueTypeLatlngheight - case property.ValueTypeURL: - return ValueTypeURL - case property.ValueTypeRef: - return ValueTypeRef - case property.ValueTypeCamera: - return ValueTypeCamera - case property.ValueTypeTypography: - return ValueTypeTypography - case property.ValueTypeCoordinates: - return ValueTypeCoordinates - case property.ValueTypePolygon: - return ValueTypePolygon - case property.ValueTypeRect: - return ValueTypeRect - } - return "" -} - -func FromPropertyValueType(t ValueType) property.ValueType { - switch t { - case ValueTypeBool: - return property.ValueTypeBool - case ValueTypeNumber: - return property.ValueTypeNumber - case ValueTypeString: - return property.ValueTypeString - case ValueTypeLatlng: - return property.ValueTypeLatLng - case ValueTypeLatlngheight: - return property.ValueTypeLatLngHeight - case ValueTypeURL: - return property.ValueTypeURL - case ValueTypeRef: - return property.ValueTypeRef - case ValueTypeCamera: - return property.ValueTypeCamera - case ValueTypeTypography: - return property.ValueTypeTypography - case ValueTypeCoordinates: - return property.ValueTypeCoordinates - case ValueTypePolygon: - return property.ValueTypePolygon - case ValueTypeRect: - return property.ValueTypeRect - } - return "" -} - -func FromPropertyValueAndType(v interface{}, t ValueType) (*property.Value, bool) { +func FromPropertyValueAndType(v interface{}, t ValueType) *property.Value { switch v2 := v.(type) { - case LatLng: - v = property.LatLng{ - Lat: v2.Lat, - Lng: v2.Lng} - case LatLngHeight: - v = property.LatLngHeight{ - Lat: v2.Lat, - Lng: v2.Lng, - Height: v2.Height} - case *LatLng: - v = property.LatLng{ - Lat: v2.Lat, - Lng: v2.Lng, - } - case *LatLngHeight: - v = property.LatLngHeight{ - Lat: v2.Lat, - Lng: v2.Lng, - Height: v2.Height, - } case *Camera: v = property.Camera{ Lat: v2.Lat, @@ -209,39 +85,10 @@ func FromPropertyValueAndType(v interface{}, t ValueType) (*property.Value, bool Italic: v2.Italic, Underline: v2.Underline, } - case []LatLngHeight: - res := make([]property.LatLngHeight, 0, len(v2)) - for _, c := range v2 { - res = append(res, property.LatLngHeight{ - Lat: c.Lat, - Lng: c.Lng, - Height: c.Height, - }) - } - v = res - case [][]LatLngHeight: - res := make([][]property.LatLngHeight, 0, len(v2)) - for _, d := range v2 { - coord := make([]property.LatLngHeight, 0, len(d)) - for _, c := range d { - coord = append(coord, property.LatLngHeight{ - Lat: c.Lat, - Lng: c.Lng, - Height: c.Height, - }) - } - res = append(res, coord) - } - v = res - case *Rect: - v = property.Rect{ - West: v2.West, - East: v2.East, - North: v2.North, - South: v2.South, - } + default: + v = gqlValueToValueInterface(v2) } - return FromPropertyValueType(t).ValueFrom(v) + return property.ValueType(t).ValueFrom(v) } func fromTextAlign(t *TextAlign) *property.TextAlign { @@ -286,7 +133,7 @@ func ToPropertyField(f *property.Field, parent *property.Property, gl *property. SchemaID: parent.Schema(), FieldID: f.Field(), Value: ToPropertyValue(f.Value()), - Type: ToPropertyValueType(f.Type()), + Type: ValueType(f.Type()), Links: links, } } @@ -402,7 +249,7 @@ func ToPropertySchemaField(f *property.SchemaField) *PropertySchemaField { return &PropertySchemaField{ FieldID: f.ID(), - Type: ToPropertyValueType(f.Type()), + Type: ValueType(f.Type()), Title: f.Title().String(), Description: f.Description().String(), Prefix: stringToRef(f.Prefix()), @@ -512,7 +359,7 @@ func ToMergedPropertyField(f *property.MergedField, s id.PropertySchemaID) *Merg SchemaID: s, Links: ToPropertyFieldLinks(f.Links), Value: ToPropertyValue(f.Value), - Type: ToPropertyValueType(f.Type), + Type: ValueType(f.Type), Overridden: f.Overridden, } } @@ -604,7 +451,7 @@ func ToPropertyConditon(c *property.Condition) *PropertyCondition { return &PropertyCondition{ FieldID: c.Field, Value: ToPropertyValue(c.Value), - Type: ToPropertyValueType(c.Value.Type()), + Type: ValueType(c.Value.Type()), } } diff --git a/internal/adapter/gql/gqlmodel/convert_value.go b/internal/adapter/gql/gqlmodel/convert_value.go new file mode 100644 index 00000000..78102e84 --- /dev/null +++ b/internal/adapter/gql/gqlmodel/convert_value.go @@ -0,0 +1,147 @@ +package gqlmodel + +import ( + "net/url" + + "github.com/reearth/reearth-backend/pkg/value" +) + +func valueInterfaceToGqlValue(v interface{}) interface{} { + if v == nil { + return nil + } + switch v2 := v.(type) { + case bool: + return v2 + case float64: + return v2 + case string: + return v2 + case *url.URL: + return v2.String() + case value.LatLng: + return LatLng{ + Lat: v2.Lat, + Lng: v2.Lng, + } + case value.LatLngHeight: + return LatLngHeight{ + Lat: v2.Lat, + Lng: v2.Lng, + Height: v2.Height, + } + case *value.LatLng: + return LatLng{ + Lat: v2.Lat, + Lng: v2.Lng, + } + case *value.LatLngHeight: + return LatLngHeight{ + Lat: v2.Lat, + Lng: v2.Lng, + Height: v2.Height, + } + case []value.LatLngHeight: + res := make([]LatLngHeight, 0, len(v2)) + for _, c := range v2 { + res = append(res, LatLngHeight{ + Lat: c.Lat, + Lng: c.Lng, + Height: c.Height, + }) + } + return res + case [][]value.LatLngHeight: + res := make([][]LatLngHeight, 0, len(v2)) + for _, d := range v2 { + coord := make([]LatLngHeight, 0, len(d)) + for _, c := range d { + coord = append(coord, LatLngHeight{ + Lat: c.Lat, + Lng: c.Lng, + Height: c.Height, + }) + } + res = append(res, coord) + } + return res + case *value.Rect: + return Rect{ + West: v2.West, + East: v2.East, + North: v2.North, + South: v2.South, + } + } + return nil +} + +func gqlValueToValueInterface(v interface{}) interface{} { + if v == nil { + return nil + } + switch v2 := v.(type) { + case bool: + return v2 + case float64: + return v2 + case string: + return v2 + case *url.URL: + return v2 + case LatLng: + return value.LatLng{ + Lat: v2.Lat, + Lng: v2.Lng, + } + case LatLngHeight: + return value.LatLngHeight{ + Lat: v2.Lat, + Lng: v2.Lng, + Height: v2.Height, + } + case *LatLng: + return value.LatLng{ + Lat: v2.Lat, + Lng: v2.Lng, + } + case *LatLngHeight: + return value.LatLngHeight{ + Lat: v2.Lat, + Lng: v2.Lng, + Height: v2.Height, + } + case []LatLngHeight: + res := make([]value.LatLngHeight, 0, len(v2)) + for _, c := range v2 { + res = append(res, value.LatLngHeight{ + Lat: c.Lat, + Lng: c.Lng, + Height: c.Height, + }) + } + return value.Coordinates(res) + case [][]LatLngHeight: + res := make([]value.Coordinates, 0, len(v2)) + for _, d := range v2 { + coord := make([]value.LatLngHeight, 0, len(d)) + for _, c := range d { + coord = append(coord, value.LatLngHeight{ + Lat: c.Lat, + Lng: c.Lng, + Height: c.Height, + }) + } + res = append(res, coord) + } + return value.Polygon(res) + case *Rect: + return value.Rect{ + West: v2.West, + East: v2.East, + North: v2.North, + South: v2.South, + } + } + return nil +} diff --git a/internal/adapter/gql/resolver_mutation_property.go b/internal/adapter/gql/resolver_mutation_property.go index 1e0565e7..88d7bbee 100644 --- a/internal/adapter/gql/resolver_mutation_property.go +++ b/internal/adapter/gql/resolver_mutation_property.go @@ -14,9 +14,12 @@ func (r *mutationResolver) UpdatePropertyValue(ctx context.Context, input gqlmod exit := trace(ctx) defer exit() - v, ok := gqlmodel.FromPropertyValueAndType(input.Value, input.Type) - if !ok { - return nil, errors.New("invalid value") + var v *property.Value + if input.Value != nil { + v = gqlmodel.FromPropertyValueAndType(input.Value, input.Type) + if v == nil { + return nil, errors.New("invalid value") + } } pp, pgl, pg, pf, err := r.usecases.Property.UpdateValue(ctx, interfaces.UpdatePropertyValueParam{ @@ -117,7 +120,10 @@ func (r *mutationResolver) AddPropertyItem(ctx context.Context, input gqlmodel.A var v *property.Value if input.NameFieldType != nil { - v, _ = gqlmodel.FromPropertyValueAndType(input.NameFieldValue, *input.NameFieldType) + v = gqlmodel.FromPropertyValueAndType(input.NameFieldValue, *input.NameFieldType) + if v == nil { + return nil, errors.New("invalid name field value") + } } p, pgl, pi, err := r.usecases.Property.AddItem(ctx, interfaces.AddPropertyItemParam{ @@ -181,7 +187,10 @@ func (r *mutationResolver) UpdatePropertyItems(ctx context.Context, input gqlmod for _, o := range input.Operations { var v *property.Value if o.NameFieldType != nil { - v, _ = gqlmodel.FromPropertyValueAndType(o.NameFieldValue, *o.NameFieldType) + v = gqlmodel.FromPropertyValueAndType(o.NameFieldValue, *o.NameFieldType) + if v == nil { + return nil, errors.New("invalid name field value") + } } op = append(op, interfaces.UpdatePropertyItemsOperationParam{ diff --git a/internal/infrastructure/memory/dataset.go b/internal/infrastructure/memory/dataset.go index 5f2584cd..c3a119c1 100644 --- a/internal/infrastructure/memory/dataset.go +++ b/internal/infrastructure/memory/dataset.go @@ -106,8 +106,10 @@ func (r *Dataset) FindGraph(ctx context.Context, i id.DatasetID, f []id.SceneID, if f := d.Field(nextField); f != nil { if f.Type() == dataset.ValueTypeRef { if l := f.Value().ValueRef(); l != nil { - next = id.DatasetID(*l) - continue + if did, err := id.DatasetIDFrom(*l); err == nil { + next = did + continue + } } } } diff --git a/internal/infrastructure/memory/dataset_schema.go b/internal/infrastructure/memory/dataset_schema.go index 5e1b4c33..372196f6 100644 --- a/internal/infrastructure/memory/dataset_schema.go +++ b/internal/infrastructure/memory/dataset_schema.go @@ -117,7 +117,7 @@ func (r *DatasetSchema) FindDynamicByID(ctx context.Context, id id.DatasetSchema return nil, rerror.ErrNotFound } -func (r *DatasetSchema) FindBySceneAndSource(ctx context.Context, s id.SceneID, src dataset.Source) (dataset.SchemaList, error) { +func (r *DatasetSchema) FindBySceneAndSource(ctx context.Context, s id.SceneID, src string) (dataset.SchemaList, error) { r.lock.Lock() defer r.lock.Unlock() diff --git a/internal/infrastructure/mongo/dataset_schema.go b/internal/infrastructure/mongo/dataset_schema.go index e4887edb..e15c51e3 100644 --- a/internal/infrastructure/mongo/dataset_schema.go +++ b/internal/infrastructure/mongo/dataset_schema.go @@ -82,7 +82,7 @@ func (r *datasetSchemaRepo) FindAllDynamicByScene(ctx context.Context, sceneID i return r.find(ctx, nil, filter) } -func (r *datasetSchemaRepo) FindBySceneAndSource(ctx context.Context, sceneID id.SceneID, source dataset.Source) (dataset.SchemaList, error) { +func (r *datasetSchemaRepo) FindBySceneAndSource(ctx context.Context, sceneID id.SceneID, source string) (dataset.SchemaList, error) { filter := bson.D{ {Key: "scene", Value: sceneID.String()}, {Key: "source", Value: string(source)}, diff --git a/internal/infrastructure/mongo/mongodoc/dataset.go b/internal/infrastructure/mongo/mongodoc/dataset.go index 194e1ffc..3cfd9e08 100644 --- a/internal/infrastructure/mongo/mongodoc/dataset.go +++ b/internal/infrastructure/mongo/mongodoc/dataset.go @@ -128,13 +128,13 @@ func (doc *DatasetDocument) Model() (*dataset.Dataset, error) { f := dataset.NewField( fid, toModelDatasetValue(field.Value, field.Type), - dataset.Source(field.Source), + field.Source, ) fields = append(fields, f) } return dataset.New(). ID(did). - Source(dataset.Source(doc.Source)). + Source(doc.Source). Fields(fields). Schema(ds). Scene(scene). @@ -145,7 +145,7 @@ func NewDataset(dataset *dataset.Dataset) (*DatasetDocument, string) { did := dataset.ID().String() var doc DatasetDocument doc.ID = did - doc.Source = dataset.Source().String() + doc.Source = dataset.Source() doc.Scene = id.ID(dataset.Scene()).String() doc.Schema = id.ID(dataset.Schema()).String() @@ -156,7 +156,7 @@ func NewDataset(dataset *dataset.Dataset) (*DatasetDocument, string) { Field: f.Field().String(), Type: string(f.Type()), Value: f.Value().Interface(), - Source: f.Source().String(), + Source: f.Source(), }) } return &doc, did @@ -183,9 +183,5 @@ func toModelDatasetValue(v interface{}, t string) *dataset.Value { if v2, ok := v.(bson.D); ok { v = v2.Map() } - vt, ok := dataset.ValueTypeFrom(t) - if !ok { - return nil - } - return vt.ValueFrom(v) + return dataset.ValueTypeFrom(t).ValueFrom(v) } diff --git a/internal/infrastructure/mongo/mongodoc/dataset_schema.go b/internal/infrastructure/mongo/mongodoc/dataset_schema.go index 0ac53fdc..80fb6a97 100644 --- a/internal/infrastructure/mongo/mongodoc/dataset_schema.go +++ b/internal/infrastructure/mongo/mongodoc/dataset_schema.go @@ -1,8 +1,6 @@ package mongodoc import ( - "errors" - "go.mongodb.org/mongo-driver/bson" "github.com/reearth/reearth-backend/pkg/dataset" @@ -63,15 +61,12 @@ func (d *DatasetSchemaDocument) Model() (*dataset.Schema, error) { if err != nil { return nil, err } - vt, ok := dataset.ValueType(field.Type).Validate() - if !ok { - return nil, errors.New("invalid value type") - } + vt := dataset.ValueType(field.Type) f, err := dataset.NewSchemaField(). Name(field.Name). ID(fid). Type(vt). - Source(dataset.Source(field.Source)). + Source(field.Source). Build() if err != nil { return nil, err @@ -81,7 +76,7 @@ func (d *DatasetSchemaDocument) Model() (*dataset.Schema, error) { b := dataset.NewSchema(). ID(did). Name(d.Name). - Source(dataset.Source(d.Source)). + Source(d.Source). Scene(scene). Fields(fields) if d.RepresentativeField != nil { @@ -99,7 +94,7 @@ func NewDatasetSchema(dataset *dataset.Schema) (*DatasetSchemaDocument, string) doc := DatasetSchemaDocument{ ID: did, Name: dataset.Name(), - Source: dataset.Source().String(), + Source: dataset.Source(), Scene: id.ID(dataset.Scene()).String(), RepresentativeField: dataset.RepresentativeFieldID().StringRef(), Dynamic: dataset.Dynamic(), @@ -112,7 +107,7 @@ func NewDatasetSchema(dataset *dataset.Schema) (*DatasetSchemaDocument, string) ID: f.ID().String(), Type: string(f.Type()), Name: f.Name(), - Source: f.Source().String(), + Source: f.Source(), }) } diff --git a/internal/infrastructure/mongo/mongodoc/property.go b/internal/infrastructure/mongo/mongodoc/property.go index f55f17c5..0ee1680b 100644 --- a/internal/infrastructure/mongo/mongodoc/property.go +++ b/internal/infrastructure/mongo/mongodoc/property.go @@ -218,7 +218,7 @@ func toModelPropertyField(f *PropertyFieldDocument) *property.Field { flinks = property.NewLinks(links) } - vt, _ := property.ValueTypeFrom(f.Type) + vt := property.ValueType(f.Type) field := property.NewFieldUnsafe(). FieldUnsafe(id.PropertySchemaFieldID(f.Field)). TypeUnsafe(vt). @@ -318,13 +318,5 @@ func (doc *PropertyDocument) Model() (*property.Property, error) { } func toModelPropertyValue(v interface{}, t string) *property.Value { - if v == nil { - return nil - } - v = convertDToM(v) - vt, ok := property.ValueTypeFrom(t) - if !ok { - return nil - } - return vt.ValueFromUnsafe(v) + return property.ValueType(t).ValueFrom(convertDToM(v)) } diff --git a/internal/infrastructure/mongo/mongodoc/property_schema.go b/internal/infrastructure/mongo/mongodoc/property_schema.go index 7fca03ad..3003d380 100644 --- a/internal/infrastructure/mongo/mongodoc/property_schema.go +++ b/internal/infrastructure/mongo/mongodoc/property_schema.go @@ -170,7 +170,7 @@ func ToModelPropertySchemaField(f *PropertySchemaFieldDocument) (*property.Schem Description(f.Description). Prefix(f.Prefix). Suffix(f.Suffix). - DefaultValue(vt.ValueFromUnsafe(f.DefaultValue)). + DefaultValue(vt.ValueFrom(f.DefaultValue)). UIRef(property.SchemaFieldUIFromRef(f.UI)). MinRef(f.Min). MaxRef(f.Max). diff --git a/internal/infrastructure/mongo/mongodoc/util.go b/internal/infrastructure/mongo/mongodoc/util.go index 0b105176..e2e144c1 100644 --- a/internal/infrastructure/mongo/mongodoc/util.go +++ b/internal/infrastructure/mongo/mongodoc/util.go @@ -3,6 +3,9 @@ package mongodoc import "go.mongodb.org/mongo-driver/bson" func convertDToM(i interface{}) interface{} { + if i == nil { + return nil + } switch i2 := i.(type) { case bson.D: return i2.Map() diff --git a/internal/usecase/interactor/dataset.go b/internal/usecase/interactor/dataset.go index eaace244..56350481 100644 --- a/internal/usecase/interactor/dataset.go +++ b/internal/usecase/interactor/dataset.go @@ -129,7 +129,6 @@ func (i *Dataset) AddDynamicDatasetSchema(ctx context.Context, inp interfaces.Ad } func (i *Dataset) AddDynamicDataset(ctx context.Context, inp interfaces.AddDynamicDatasetParam) (_ *dataset.Schema, _ *dataset.Dataset, err error) { - // Begin Db transaction tx, err := i.transaction.Begin() if err != nil { @@ -147,19 +146,18 @@ func (i *Dataset) AddDynamicDataset(ctx context.Context, inp interfaces.AddDynam return nil, nil, err } for _, f := range dss.Fields() { - if f.Name() == "author" { - fields = append(fields, dataset.NewField(f.ID(), dataset.ValueFrom(inp.Author), "")) + fields = append(fields, dataset.NewField(f.ID(), dataset.ValueTypeString.ValueFrom(inp.Author), "")) } if f.Name() == "content" { - fields = append(fields, dataset.NewField(f.ID(), dataset.ValueFrom(inp.Content), "")) + fields = append(fields, dataset.NewField(f.ID(), dataset.ValueTypeString.ValueFrom(inp.Content), "")) } if inp.Target != nil && len(*inp.Target) > 0 && f.Name() == "target" { - fields = append(fields, dataset.NewField(f.ID(), dataset.ValueFrom(inp.Target), "")) + fields = append(fields, dataset.NewField(f.ID(), dataset.ValueTypeString.ValueFrom(inp.Target), "")) } if inp.Lat != nil && inp.Lng != nil && f.Name() == "location" { latlng := dataset.LatLng{Lat: *inp.Lat, Lng: *inp.Lng} - fields = append(fields, dataset.NewField(f.ID(), dataset.ValueFrom(latlng), "")) + fields = append(fields, dataset.NewField(f.ID(), dataset.ValueTypeLatLng.ValueFrom(latlng), "")) } } ds, err := dataset. diff --git a/internal/usecase/interactor/property.go b/internal/usecase/interactor/property.go index a381ba26..ad480908 100644 --- a/internal/usecase/interactor/property.go +++ b/internal/usecase/interactor/property.go @@ -239,7 +239,7 @@ func (i *Property) UploadFile(ctx context.Context, inp interfaces.UploadFilePara return nil, nil, nil, nil, err } - v := property.ValueTypeURL.ValueFromUnsafe(url) + v := property.ValueTypeURL.ValueFrom(url) if v == nil { return nil, nil, nil, nil, interfaces.ErrInvalidPropertyValue } diff --git a/internal/usecase/repo/dataset_schema.go b/internal/usecase/repo/dataset_schema.go index be94c38a..2e42eae9 100644 --- a/internal/usecase/repo/dataset_schema.go +++ b/internal/usecase/repo/dataset_schema.go @@ -13,7 +13,7 @@ type DatasetSchema interface { FindByIDs(context.Context, []id.DatasetSchemaID, []id.SceneID) (dataset.SchemaList, error) FindByScene(context.Context, id.SceneID, *usecase.Pagination) (dataset.SchemaList, *usecase.PageInfo, error) FindBySceneAll(context.Context, id.SceneID) (dataset.SchemaList, error) - FindBySceneAndSource(context.Context, id.SceneID, dataset.Source) (dataset.SchemaList, error) + FindBySceneAndSource(context.Context, id.SceneID, string) (dataset.SchemaList, error) FindDynamicByID(context.Context, id.DatasetSchemaID) (*dataset.Schema, error) FindAllDynamicByScene(context.Context, id.SceneID) (dataset.SchemaList, error) Save(context.Context, *dataset.Schema) error diff --git a/pkg/dataset/builder.go b/pkg/dataset/builder.go index 576f8563..33919ae2 100644 --- a/pkg/dataset/builder.go +++ b/pkg/dataset/builder.go @@ -4,17 +4,14 @@ import ( "github.com/reearth/reearth-backend/pkg/id" ) -// Builder _ type Builder struct { d *Dataset } -// New _ func New() *Builder { return &Builder{d: &Dataset{}} } -// Build _ func (b *Builder) Build() (*Dataset, error) { if id.ID(b.d.id).IsNil() { return nil, id.ErrInvalidID @@ -26,7 +23,6 @@ func (b *Builder) Build() (*Dataset, error) { return b.d, nil } -// MustBuild _ func (b *Builder) MustBuild() *Dataset { r, err := b.Build() if err != nil { @@ -35,47 +31,46 @@ func (b *Builder) MustBuild() *Dataset { return r } -// ID _ func (b *Builder) ID(id id.DatasetID) *Builder { b.d.id = id return b } -// NewID _ func (b *Builder) NewID() *Builder { b.d.id = id.DatasetID(id.New()) return b } -// Scene _ func (b *Builder) Scene(scene id.SceneID) *Builder { b.d.scene = scene return b } -// Source _ -func (b *Builder) Source(source Source) *Builder { +func (b *Builder) Source(source string) *Builder { b.d.source = source return b } -// Schema _ func (b *Builder) Schema(schema id.DatasetSchemaID) *Builder { b.d.schema = schema return b } -// Fields _ func (b *Builder) Fields(fields []*Field) *Builder { b.d.fields = map[id.DatasetSchemaFieldID]*Field{} b.d.order = make([]id.DatasetSchemaFieldID, 0, len(fields)) - sources := map[Source]struct{}{} + + sources := map[string]struct{}{} for _, f := range b.d.fields { if source := f.Source(); source != "" { sources[source] = struct{}{} } } + for _, f := range fields { + if f.IsEmpty() { + continue + } source := f.Source() if source == "" { b.d.fields[f.Field()] = f @@ -86,5 +81,6 @@ func (b *Builder) Fields(fields []*Field) *Builder { sources[source] = struct{}{} } } + return b } diff --git a/pkg/dataset/csvparser.go b/pkg/dataset/csvparser.go index 47348bf4..16f77f6a 100644 --- a/pkg/dataset/csvparser.go +++ b/pkg/dataset/csvparser.go @@ -53,28 +53,6 @@ func (p *DatasetCSVParser) validateLine(line []string) bool { return len(p.headers) == len(line) } -func (p *DatasetCSVParser) getRecord(rec string) *Value { - var v *Value - vint, err := strconv.Atoi(rec) - if err == nil { - v = ValueFrom(vint) - return v - } - - vfloat64, err := strconv.ParseFloat(rec, 64) - if err == nil { - v = ValueFrom(vfloat64) - return v - } - vbool, err := strconv.ParseBool(rec) - if err == nil { - v = ValueFrom(vbool) - return v - } - v = ValueFrom(rec) - return v -} - func (p *DatasetCSVParser) GuessSchema(sid id.SceneID) error { if !p.validateLine(p.firstline) { return ErrFailedToParseCSVorTSVFile @@ -89,7 +67,7 @@ func (p *DatasetCSVParser) GuessSchema(sid id.SceneID) error { haslng = true } if h != "lng" && h != "lat" && strings.TrimSpace(h) != "" { - t := p.getRecord(p.firstline[k]).Type() + t := ValueFromStringOrNumber(p.firstline[k]).Type() field, _ := NewSchemaField().NewID().Name(h).Type(t).Build() schemafields = append(schemafields, field) } @@ -102,7 +80,7 @@ func (p *DatasetCSVParser) GuessSchema(sid id.SceneID) error { NewID(). Scene(sid). Name(p.name). - Source(Source("file:///" + p.name)). + Source("file:///" + p.name). Fields(schemafields). Build() if err != nil { @@ -167,7 +145,7 @@ func (p *DatasetCSVParser) getFields(line []string, sfm map[string]id.DatasetSch fields := []*Field{} var lat, lng *float64 for i, record := range line { - value := p.getRecord(record).Value() + value := ValueFromStringOrNumber(record) if p.headers[i] == "lng" { value, err := strconv.ParseFloat(record, 64) if err != nil { @@ -184,12 +162,12 @@ func (p *DatasetCSVParser) getFields(line []string, sfm map[string]id.DatasetSch } if p.headers[i] != "lat" && p.headers[i] != "lng" { - fields = append(fields, NewField(sfm[p.headers[i]], ValueFrom(value), "")) + fields = append(fields, NewField(sfm[p.headers[i]], value, "")) } } if lat != nil && lng != nil { latlng := LatLng{Lat: *lat, Lng: *lng} - fields = append(fields, NewField(sfm["location"], ValueFrom(latlng), "")) + fields = append(fields, NewField(sfm["location"], ValueTypeLatLng.ValueFrom(latlng), "")) } return append([]*Field{}, fields...), nil } @@ -206,8 +184,8 @@ func (p *DatasetCSVParser) CheckCompatible(s *Schema) error { return ErrIncompatibleSchema } t := fieldsmap[h].Type() - v := p.getRecord(p.firstline[i]) - if !t.ValidateValue(v) { + v := ValueFromStringOrNumber(p.firstline[i]) + if v.Type() != t { return ErrIncompatibleSchema } } diff --git a/pkg/dataset/csvparser_test.go b/pkg/dataset/csvparser_test.go index d6a37b82..534dc32e 100644 --- a/pkg/dataset/csvparser_test.go +++ b/pkg/dataset/csvparser_test.go @@ -27,25 +27,20 @@ func TestCSVParser(t *testing.T) { assert.NotEmpty(t, schema) assert.Equal(t, "hoge.csv", schema.Name()) - assert.Equal(t, Source("file:///hoge.csv"), schema.Source()) + assert.Equal(t, "file:///hoge.csv", schema.Source()) assert.Equal(t, 1, len(datasets)) - sfm := make(map[string]string) - for _, sf := range schema.Fields() { - sfm[sf.ID().String()] = sf.Name() - } + dsfm := make(map[string]interface{}) for _, dsf := range datasets[0].Fields() { - dsfm[sfm[dsf.Field().String()]] = dsf.Value().Interface() + dsfm[schema.Field(dsf.Field()).Name()] = dsf.Value().Interface() } - latlng := map[string]interface{}{"lat": 12.0, "lng": 15.0} assert.Equal(t, map[string]interface{}{ "hoge": 1.0, "foo": "foo", "bar": "bar", - "location": latlng, + "location": LatLng{Lat: 12.0, Lng: 15.0}, }, dsfm) - } func TestCSVParserCheckCompatible(t *testing.T) { @@ -62,5 +57,4 @@ func TestCSVParserCheckCompatible(t *testing.T) { assert.NoError(t, err) result := p.CheckCompatible(ds) assert.NoError(t, result) - } diff --git a/pkg/dataset/dataset.go b/pkg/dataset/dataset.go index abb09d84..f06678ed 100644 --- a/pkg/dataset/dataset.go +++ b/pkg/dataset/dataset.go @@ -2,17 +2,15 @@ package dataset import "github.com/reearth/reearth-backend/pkg/id" -// Dataset _ type Dataset struct { id id.DatasetID - source Source + source string schema id.DatasetSchemaID fields map[id.DatasetSchemaFieldID]*Field order []id.DatasetSchemaFieldID scene id.SceneID } -// ID _ func (d *Dataset) ID() (i id.DatasetID) { if d == nil { return @@ -20,7 +18,6 @@ func (d *Dataset) ID() (i id.DatasetID) { return d.id } -// Scene _ func (d *Dataset) Scene() (i id.SceneID) { if d == nil { return @@ -28,15 +25,13 @@ func (d *Dataset) Scene() (i id.SceneID) { return d.scene } -// Source _ -func (d *Dataset) Source() (i Source) { +func (d *Dataset) Source() string { if d == nil { - return + return "" } return d.source } -// Schema _ func (d *Dataset) Schema() (i id.DatasetSchemaID) { if d == nil { return @@ -44,7 +39,6 @@ func (d *Dataset) Schema() (i id.DatasetSchemaID) { return d.schema } -// Fields _ func (d *Dataset) Fields() []*Field { if d == nil || d.order == nil { return nil @@ -56,7 +50,6 @@ func (d *Dataset) Fields() []*Field { return fields } -// Field _ func (d *Dataset) Field(id id.DatasetSchemaFieldID) *Field { if d == nil || d.fields == nil { return nil @@ -64,7 +57,6 @@ func (d *Dataset) Field(id id.DatasetSchemaFieldID) *Field { return d.fields[id] } -// FieldRef _ func (d *Dataset) FieldRef(id *id.DatasetSchemaFieldID) *Field { if d == nil || id == nil { return nil @@ -72,7 +64,6 @@ func (d *Dataset) FieldRef(id *id.DatasetSchemaFieldID) *Field { return d.fields[*id] } -// NameField _ func (d *Dataset) NameField(ds *Schema) *Field { if d == nil { return nil @@ -87,8 +78,7 @@ func (d *Dataset) NameField(ds *Schema) *Field { return d.fields[f.ID()] } -// FieldBySource _ -func (d *Dataset) FieldBySource(source Source) *Field { +func (d *Dataset) FieldBySource(source string) *Field { if d == nil { return nil } @@ -100,7 +90,6 @@ func (d *Dataset) FieldBySource(source Source) *Field { return nil } -// FieldByType _ func (d *Dataset) FieldByType(t ValueType) *Field { if d == nil { return nil @@ -112,3 +101,29 @@ func (d *Dataset) FieldByType(t ValueType) *Field { } return nil } + +// Interface returns a simple and human-readable representation of the dataset +func (d *Dataset) Interface(s *Schema) map[string]interface{} { + if d == nil || s == nil || d.Schema() != s.ID() { + return nil + } + m := map[string]interface{}{} + for _, f := range d.fields { + key := s.Field(f.Field()).Name() + m[key] = f.Value().Interface() + } + return m +} + +// Interface is almost same as Interface, but keys of the map are IDs of fields. +func (d *Dataset) InterfaceWithFieldIDs() map[string]interface{} { + if d == nil { + return nil + } + m := map[string]interface{}{} + for _, f := range d.fields { + key := f.Field().String() + m[key] = f.Value().Interface() + } + return m +} diff --git a/pkg/dataset/dataset_test.go b/pkg/dataset/dataset_test.go new file mode 100644 index 00000000..9520080a --- /dev/null +++ b/pkg/dataset/dataset_test.go @@ -0,0 +1,89 @@ +package dataset + +import ( + "testing" + + "github.com/reearth/reearth-backend/pkg/id" + "github.com/stretchr/testify/assert" +) + +func TestDataset_Interface(t *testing.T) { + f1 := id.NewDatasetSchemaFieldID() + f2 := id.NewDatasetSchemaFieldID() + sid := id.NewDatasetSchemaID() + + tests := []struct { + name string + schema *Schema + dataset *Dataset + want map[string]interface{} + }{ + { + name: "ok", + schema: NewSchema().ID(sid).Scene(id.NewSceneID()).Fields([]*SchemaField{ + NewSchemaField().ID(f1).Name("foo").Type(ValueTypeNumber).MustBuild(), + NewSchemaField().ID(f2).Name("bar").Type(ValueTypeLatLng).MustBuild(), + }).MustBuild(), + dataset: New().NewID().Scene(id.NewSceneID()).Schema(sid).Fields([]*Field{ + NewField(f1, ValueTypeNumber.ValueFrom(1), ""), + NewField(f2, ValueTypeLatLng.ValueFrom(LatLng{Lat: 1, Lng: 2}), ""), + }).MustBuild(), + want: map[string]interface{}{ + "foo": float64(1), + "bar": LatLng{Lat: 1, Lng: 2}, + }, + }, + { + name: "empty", + dataset: &Dataset{}, + }, + { + name: "nil", + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.want, tt.dataset.Interface(tt.schema)) + }) + } +} + +func TestDataset_InterfaceWithFieldIDs(t *testing.T) { + f1 := id.NewDatasetSchemaFieldID() + f2 := id.NewDatasetSchemaFieldID() + + tests := []struct { + name string + dataset *Dataset + want map[string]interface{} + }{ + { + name: "ok", + dataset: New().NewID().Scene(id.NewSceneID()).Schema(id.NewDatasetSchemaID()).Fields([]*Field{ + NewField(f1, ValueTypeNumber.ValueFrom(1), ""), + NewField(f2, ValueTypeLatLng.ValueFrom(LatLng{Lat: 1, Lng: 2}), ""), + }).MustBuild(), + want: map[string]interface{}{ + f1.String(): float64(1), + f2.String(): LatLng{Lat: 1, Lng: 2}, + }, + }, + { + name: "empty", + dataset: &Dataset{}, + want: map[string]interface{}{}, + }, + { + name: "nil", + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.want, tt.dataset.InterfaceWithFieldIDs()) + }) + } +} diff --git a/pkg/dataset/field.go b/pkg/dataset/field.go index e03407cb..e99dffd3 100644 --- a/pkg/dataset/field.go +++ b/pkg/dataset/field.go @@ -2,25 +2,22 @@ package dataset import "github.com/reearth/reearth-backend/pkg/id" -// Field _ type Field struct { field id.DatasetSchemaFieldID - dtype ValueType value *Value - source Source + source string } -// NewField _ -func NewField(field id.DatasetSchemaFieldID, value *Value, source Source) *Field { +func NewField(field id.DatasetSchemaFieldID, value *Value, source string) *Field { + if value == nil { + return nil + } return &Field{ - dtype: value.Type(), - field: field, - value: value, - source: source, + field: field, + value: value, } } -// Field _ func (d *Field) Field() (i id.DatasetSchemaFieldID) { if d == nil { return @@ -28,7 +25,6 @@ func (d *Field) Field() (i id.DatasetSchemaFieldID) { return d.field } -// FieldRef _ func (d *Field) FieldRef() *id.DatasetSchemaFieldID { if d == nil { return nil @@ -36,26 +32,27 @@ func (d *Field) FieldRef() *id.DatasetSchemaFieldID { return d.field.Ref() } -// Type _ -func (d *Field) Type() (v ValueType) { - if d == nil { - return - } - return d.dtype +func (d *Field) IsEmpty() bool { + return d == nil || d.field.IsNil() || d.value == nil } -// Value _ func (d *Field) Value() *Value { if d == nil { return nil } - return d.value + return d.value.Clone() } -// Source _ -func (d *Field) Source() (s Source) { +func (d *Field) Type() ValueType { if d == nil { - return + return ValueTypeUnknown + } + return d.value.Type() +} + +func (d *Field) Source() string { + if d == nil { + return "" } return d.source } diff --git a/pkg/dataset/graph_iterator.go b/pkg/dataset/graph_iterator.go index cb8e6b98..db4432f7 100644 --- a/pkg/dataset/graph_iterator.go +++ b/pkg/dataset/graph_iterator.go @@ -2,7 +2,7 @@ package dataset import "github.com/reearth/reearth-backend/pkg/id" -// GraphIterator は、データセットをグラフ探索するためのイテレータです。 +// GraphIterator is a iterator for graphically exploring a dataset. type GraphIterator struct { m Map ids [][]id.DatasetID @@ -11,7 +11,6 @@ type GraphIterator struct { maxDepth int } -// GraphIteratorFrom _ func GraphIteratorFrom(root id.DatasetID, depth int) *GraphIterator { return &GraphIterator{ ids: [][]id.DatasetID{{root}}, @@ -19,9 +18,8 @@ func GraphIteratorFrom(root id.DatasetID, depth int) *GraphIterator { } } -// Next _ func (di *GraphIterator) Next(d *Dataset) (id.DatasetID, bool) { - if di == nil || di.maxDepth == 0 || di.ids == nil || len(di.ids) == 0 || d == nil { + if di == nil || di.maxDepth == 0 || len(di.ids) == 0 || d == nil { return id.DatasetID{}, false } if di.currentDepthIndex >= len(di.ids) { @@ -41,7 +39,9 @@ func (di *GraphIterator) Next(d *Dataset) (id.DatasetID, bool) { currentIDs := di.ids[di.currentDepthIndex] for _, f := range d.Fields() { if r := f.Value().ValueRef(); r != nil { - nextDepthIDs = append(nextDepthIDs, id.DatasetID(*r)) + if rid, err := id.DatasetIDFrom(*r); err == nil { + nextDepthIDs = append(nextDepthIDs, rid) + } } } di.ids[di.currentDepthIndex+1] = nextDepthIDs @@ -63,7 +63,6 @@ func (di *GraphIterator) Next(d *Dataset) (id.DatasetID, bool) { return di.ids[di.currentDepthIndex][di.currentIndex], false } -// Result _ func (di *GraphIterator) Result() Map { if di == nil { return nil diff --git a/pkg/dataset/graph_iterator_test.go b/pkg/dataset/graph_iterator_test.go index 73435ecc..254b1a8e 100644 --- a/pkg/dataset/graph_iterator_test.go +++ b/pkg/dataset/graph_iterator_test.go @@ -50,13 +50,14 @@ func TestDatasetGraphIterator(t *testing.T) { } func testTestDatasetGraphIteratorNext(t *testing.T, it *GraphIterator, ds List) { + t.Helper() for i, d := range ds { next, done := it.Next(d) if i == len(ds)-1 { assert.Equal(t, true, done) } else { + assert.False(t, done, "next done %d", i) assert.Equal(t, ds[i+1].ID(), next, "next %d", i) - assert.Equal(t, false, done, "next done %d", i) } } assert.Equal(t, ds.Map(), it.Result()) diff --git a/pkg/dataset/list.go b/pkg/dataset/list.go index 850e7090..e22e8957 100644 --- a/pkg/dataset/list.go +++ b/pkg/dataset/list.go @@ -4,10 +4,8 @@ import ( "github.com/reearth/reearth-backend/pkg/id" ) -// List _ type List []*Dataset -// First _ func (l List) First() *Dataset { if l == nil || len(l) == 0 { return nil @@ -15,7 +13,6 @@ func (l List) First() *Dataset { return l[0] } -// Last _ func (l List) Last() *Dataset { if l == nil || len(l) == 0 { return nil @@ -23,7 +20,6 @@ func (l List) Last() *Dataset { return l[len(l)-1] } -// FindDataset _ func (l List) FindDataset(id id.DatasetID) *Dataset { for _, t := range l { if t.ID() == id { @@ -33,7 +29,6 @@ func (l List) FindDataset(id id.DatasetID) *Dataset { return nil } -// ToDatasetIds _ func (l List) ToDatasetIds() []id.DatasetID { if l == nil { return nil @@ -46,8 +41,7 @@ func (l List) ToDatasetIds() []id.DatasetID { return ids } -// FindDatasetBySource _ -func (l List) FindDatasetBySource(s Source) *Dataset { +func (l List) FindDatasetBySource(s string) *Dataset { for _, t := range l { if t.Source() == s { return t @@ -56,7 +50,6 @@ func (l List) FindDatasetBySource(s Source) *Dataset { return nil } -// FilterByDatasetSchema _ func (l List) FilterByDatasetSchema(s id.DatasetSchemaID) List { n := List{} for _, t := range l { @@ -67,15 +60,14 @@ func (l List) FilterByDatasetSchema(s id.DatasetSchemaID) List { return n } -// DiffBySource _ func (l List) DiffBySource(l2 List) Diff { // l is old, l2 is new added := []*Dataset{} removed := []*Dataset{} - // others := map[DatasetSource]DatasetDiffTouple{} + // others := map[string]DatasetDiffTouple{} others2 := map[id.DatasetID]*Dataset{} - s1 := map[Source]*Dataset{} + s1 := map[string]*Dataset{} for _, d1 := range l { s1[d1.Source()] = d1 } @@ -106,7 +98,6 @@ func (l List) DiffBySource(l2 List) Diff { } } -// Map _ func (l List) Map() Map { if l == nil { return nil @@ -128,10 +119,8 @@ func (l List) GraphLoader() GraphLoader { return GraphLoaderFromMap(l.Map()) } -// Map _ type Map map[id.DatasetID]*Dataset -// Add _ func (dm Map) Add(dss ...*Dataset) { if dss == nil { return @@ -147,7 +136,6 @@ func (dm Map) Add(dss ...*Dataset) { } } -// Slice _ func (dm Map) Slice() List { if dm == nil { return nil @@ -159,7 +147,6 @@ func (dm Map) Slice() List { return res } -// GraphSearchByFields _ func (dm Map) GraphSearchByFields(root id.DatasetID, fields ...id.DatasetSchemaFieldID) (List, *Field) { res := make(List, 0, len(fields)) currentD := dm[root] @@ -177,8 +164,12 @@ func (dm Map) GraphSearchByFields(root id.DatasetID, fields ...id.DatasetSchemaF } if len(fields)-1 == i { return res, field - } else if fid := field.Value().ValueRef(); fid != nil { - currentD = dm[id.DatasetID(*fid)] + } else if fids := field.Value().ValueRef(); fids != nil { + if fid, err := id.DatasetIDFrom(*fids); err == nil { + currentD = dm[id.DatasetID(fid)] + } else { + return res, nil + } } else { return res, nil } diff --git a/pkg/dataset/list_test.go b/pkg/dataset/list_test.go index 9c5fd372..4dfcccbf 100644 --- a/pkg/dataset/list_test.go +++ b/pkg/dataset/list_test.go @@ -9,9 +9,9 @@ import ( func TestDatasetListDiff(t *testing.T) { sid := id.SceneID(id.New()) - source1 := Source("hogehoge/1") - source2 := Source("hogehoge/2") - source3 := Source("hogehoge/3") + source1 := "hogehoge/1" + source2 := "hogehoge/2" + source3 := "hogehoge/3" d1, _ := New().NewID().Scene(sid).Source(source1).Build() d2, _ := New().NewID().Scene(sid).Source(source2).Build() d3, _ := New().NewID().Scene(sid).Source(source2).Build() diff --git a/pkg/dataset/schema.go b/pkg/dataset/schema.go index b3b9208e..ae3bf22a 100644 --- a/pkg/dataset/schema.go +++ b/pkg/dataset/schema.go @@ -2,10 +2,9 @@ package dataset import "github.com/reearth/reearth-backend/pkg/id" -// Schema _ type Schema struct { id id.DatasetSchemaID - source Source + source string name string fields map[id.DatasetSchemaFieldID]*SchemaField order []id.DatasetSchemaFieldID @@ -14,7 +13,6 @@ type Schema struct { dynamic bool } -// ID _ func (d *Schema) ID() (i id.DatasetSchemaID) { if d == nil { return @@ -22,7 +20,6 @@ func (d *Schema) ID() (i id.DatasetSchemaID) { return d.id } -// IDRef _ func (d *Schema) IDRef() *id.DatasetSchemaID { if d == nil { return nil @@ -30,7 +27,6 @@ func (d *Schema) IDRef() *id.DatasetSchemaID { return d.id.Ref() } -// Scene _ func (d *Schema) Scene() (i id.SceneID) { if d == nil { return @@ -38,15 +34,13 @@ func (d *Schema) Scene() (i id.SceneID) { return d.scene } -// Source _ -func (d *Schema) Source() (s Source) { +func (d *Schema) Source() (s string) { if d == nil { return } return d.source } -// Name _ func (d *Schema) Name() string { if d == nil { return "" @@ -54,7 +48,6 @@ func (d *Schema) Name() string { return d.name } -// RepresentativeFieldID _ func (d *Schema) RepresentativeFieldID() *id.DatasetSchemaFieldID { if d == nil { return nil @@ -62,7 +55,6 @@ func (d *Schema) RepresentativeFieldID() *id.DatasetSchemaFieldID { return d.representativeField } -// RepresentativeField _ func (d *Schema) RepresentativeField() *SchemaField { if d == nil || d.representativeField == nil { return nil @@ -70,7 +62,6 @@ func (d *Schema) RepresentativeField() *SchemaField { return d.fields[*d.representativeField] } -// Fields _ func (d *Schema) Fields() []*SchemaField { if d == nil || d.order == nil { return nil @@ -82,7 +73,6 @@ func (d *Schema) Fields() []*SchemaField { return fields } -// Field _ func (d *Schema) Field(id id.DatasetSchemaFieldID) *SchemaField { if d == nil { return nil @@ -90,7 +80,6 @@ func (d *Schema) Field(id id.DatasetSchemaFieldID) *SchemaField { return d.fields[id] } -// FieldRef _ func (d *Schema) FieldRef(id *id.DatasetSchemaFieldID) *SchemaField { if d == nil || id == nil { return nil @@ -98,8 +87,7 @@ func (d *Schema) FieldRef(id *id.DatasetSchemaFieldID) *SchemaField { return d.fields[*id] } -// FieldBySource _ -func (d *Schema) FieldBySource(source Source) *SchemaField { +func (d *Schema) FieldBySource(source string) *SchemaField { if d == nil { return nil } @@ -111,7 +99,6 @@ func (d *Schema) FieldBySource(source Source) *SchemaField { return nil } -// FieldByType _ func (d *Schema) FieldByType(t ValueType) *SchemaField { if d == nil { return nil diff --git a/pkg/dataset/schema_builder.go b/pkg/dataset/schema_builder.go index 7999703f..675b2c80 100644 --- a/pkg/dataset/schema_builder.go +++ b/pkg/dataset/schema_builder.go @@ -4,17 +4,14 @@ import ( "github.com/reearth/reearth-backend/pkg/id" ) -// SchemaBuilder _ type SchemaBuilder struct { d *Schema } -// NewSchema _ func NewSchema() *SchemaBuilder { return &SchemaBuilder{d: &Schema{}} } -// Build _ func (b *SchemaBuilder) Build() (*Schema, error) { if id.ID(b.d.id).IsNil() { return nil, id.ErrInvalidID @@ -26,7 +23,6 @@ func (b *SchemaBuilder) Build() (*Schema, error) { return b.d, nil } -// MustBuild _ func (b *SchemaBuilder) MustBuild() *Schema { r, err := b.Build() if err != nil { @@ -35,69 +31,53 @@ func (b *SchemaBuilder) MustBuild() *Schema { return r } -// ID _ func (b *SchemaBuilder) ID(id id.DatasetSchemaID) *SchemaBuilder { b.d.id = id return b } -// NewID _ func (b *SchemaBuilder) NewID() *SchemaBuilder { b.d.id = id.DatasetSchemaID(id.New()) return b } -// Scene _ func (b *SchemaBuilder) Scene(scene id.SceneID) *SchemaBuilder { b.d.scene = scene return b } -// Name _ func (b *SchemaBuilder) Name(name string) *SchemaBuilder { b.d.name = name return b } -// Dynamic _ func (b *SchemaBuilder) Dynamic(dynamic bool) *SchemaBuilder { b.d.dynamic = dynamic return b } -// Source _ -func (b *SchemaBuilder) Source(source Source) *SchemaBuilder { +func (b *SchemaBuilder) Source(source string) *SchemaBuilder { b.d.source = source return b } -// RepresentativeField _ func (b *SchemaBuilder) RepresentativeField(representativeField id.DatasetSchemaFieldID) *SchemaBuilder { rf := representativeField b.d.representativeField = &rf return b } -// Fields _ func (b *SchemaBuilder) Fields(fields []*SchemaField) *SchemaBuilder { b.d.fields = map[id.DatasetSchemaFieldID]*SchemaField{} - b.d.order = []id.DatasetSchemaFieldID{} + b.d.order = make([]id.DatasetSchemaFieldID, 0, len(fields)) sources := map[string]struct{}{} - for _, f := range b.d.fields { - if f == nil { - continue - } - source := f.Source().String() - if source != "" { - sources[source] = struct{}{} - } - } + for _, f := range fields { if f == nil { continue } - source := f.Source().String() - if source == "" { + + if source := f.Source(); source == "" { copied := *f b.d.fields[f.ID()] = &copied b.d.order = append(b.d.order, f.ID()) @@ -108,5 +88,6 @@ func (b *SchemaBuilder) Fields(fields []*SchemaField) *SchemaBuilder { sources[source] = struct{}{} } } + return b } diff --git a/pkg/dataset/schema_field.go b/pkg/dataset/schema_field.go index b090395d..538dce7f 100644 --- a/pkg/dataset/schema_field.go +++ b/pkg/dataset/schema_field.go @@ -1,19 +1,15 @@ -//go:generate go run github.com/reearth/reearth-backend/tools/cmd/idgen --name DatasetSchemaField --output ../id - package dataset import "github.com/reearth/reearth-backend/pkg/id" -// SchemaField _ type SchemaField struct { id id.DatasetSchemaFieldID name string dataType ValueType - source Source + source string ref *id.DatasetSchemaID } -// ID _ func (d *SchemaField) ID() (i id.DatasetSchemaFieldID) { if d == nil { return @@ -21,7 +17,6 @@ func (d *SchemaField) ID() (i id.DatasetSchemaFieldID) { return d.id } -// IDRef _ func (d *SchemaField) IDRef() *id.DatasetSchemaFieldID { if d == nil { return nil @@ -29,7 +24,6 @@ func (d *SchemaField) IDRef() *id.DatasetSchemaFieldID { return d.id.Ref() } -// Name _ func (d *SchemaField) Name() (n string) { if d == nil { return @@ -37,7 +31,6 @@ func (d *SchemaField) Name() (n string) { return d.name } -// Ref _ func (d *SchemaField) Ref() *id.DatasetSchemaID { if d == nil { return nil @@ -45,7 +38,6 @@ func (d *SchemaField) Ref() *id.DatasetSchemaID { return d.ref } -// Type _ func (d *SchemaField) Type() (v ValueType) { if d == nil { return @@ -53,8 +45,7 @@ func (d *SchemaField) Type() (v ValueType) { return d.dataType } -// Source _ -func (d *SchemaField) Source() (s Source) { +func (d *SchemaField) Source() (s string) { if d == nil { return } diff --git a/pkg/dataset/schema_field_builder.go b/pkg/dataset/schema_field_builder.go index 07adf3b9..b6db538b 100644 --- a/pkg/dataset/schema_field_builder.go +++ b/pkg/dataset/schema_field_builder.go @@ -6,28 +6,24 @@ import ( "github.com/reearth/reearth-backend/pkg/id" ) -// SchemaFieldBuilder _ type SchemaFieldBuilder struct { d *SchemaField } -// NewSchemaField _ func NewSchemaField() *SchemaFieldBuilder { return &SchemaFieldBuilder{d: &SchemaField{}} } -// Build _ func (b *SchemaFieldBuilder) Build() (*SchemaField, error) { if id.ID(b.d.id).IsNil() { return nil, id.ErrInvalidID } - if _, ok := b.d.dataType.Validate(); !ok { + if !b.d.dataType.Default() { return nil, errors.New("invalid value type") } return b.d, nil } -// MustBuild _ func (b *SchemaFieldBuilder) MustBuild() *SchemaField { r, err := b.Build() if err != nil { @@ -36,37 +32,31 @@ func (b *SchemaFieldBuilder) MustBuild() *SchemaField { return r } -// ID _ func (b *SchemaFieldBuilder) ID(id id.DatasetSchemaFieldID) *SchemaFieldBuilder { b.d.id = id return b } -// NewID _ func (b *SchemaFieldBuilder) NewID() *SchemaFieldBuilder { b.d.id = id.DatasetSchemaFieldID(id.New()) return b } -// Name _ func (b *SchemaFieldBuilder) Name(name string) *SchemaFieldBuilder { b.d.name = name return b } -// Type _ func (b *SchemaFieldBuilder) Type(dataType ValueType) *SchemaFieldBuilder { b.d.dataType = dataType return b } -// Source _ -func (b *SchemaFieldBuilder) Source(source Source) *SchemaFieldBuilder { +func (b *SchemaFieldBuilder) Source(source string) *SchemaFieldBuilder { b.d.source = source return b } -// Ref _ func (b *SchemaFieldBuilder) Ref(ref *id.DatasetSchemaID) *SchemaFieldBuilder { if ref == nil { b.d.ref = nil diff --git a/pkg/dataset/schema_field_diff.go b/pkg/dataset/schema_field_diff.go index c6dab0ac..a58b206a 100644 --- a/pkg/dataset/schema_field_diff.go +++ b/pkg/dataset/schema_field_diff.go @@ -2,21 +2,19 @@ package dataset import "github.com/reearth/reearth-backend/pkg/id" -// SchemaFieldDiff _ type SchemaFieldDiff struct { Added []*SchemaField Removed []*SchemaField Replaced map[id.DatasetSchemaFieldID]*SchemaField } -// FieldDiffBySource _ func (d *Schema) FieldDiffBySource(d2 *Schema) SchemaFieldDiff { added := []*SchemaField{} removed := []*SchemaField{} - // others := map[DatasetSource]DatasetDiffTouple{} + // others := map[string]DatasetDiffTouple{} others2 := map[id.DatasetSchemaFieldID]*SchemaField{} - s1 := map[Source]*SchemaField{} + s1 := map[string]*SchemaField{} for _, d1 := range d.fields { s1[d1.Source()] = d1 } diff --git a/pkg/dataset/source.go b/pkg/dataset/source.go deleted file mode 100644 index 068773b5..00000000 --- a/pkg/dataset/source.go +++ /dev/null @@ -1,9 +0,0 @@ -package dataset - -// Source _ -type Source string - -// String implements Stringer -func (d Source) String() string { - return string(d) -} diff --git a/pkg/dataset/value.go b/pkg/dataset/value.go index 100555cf..4fb4aea0 100644 --- a/pkg/dataset/value.go +++ b/pkg/dataset/value.go @@ -2,392 +2,207 @@ package dataset import ( "net/url" + "strconv" - "github.com/mitchellh/mapstructure" - "github.com/reearth/reearth-backend/pkg/id" + "github.com/reearth/reearth-backend/pkg/value" ) -// LatLng _ -type LatLng struct { - Lat float64 `mapstructure:"lat"` - Lng float64 `mapstructure:"lng"` -} +type LatLng = value.LatLng +type LatLngHeight = value.LatLngHeight +type Coordinates = value.Coordinates +type Rect = value.Rect +type Polygon = value.Polygon + +var ( + ValueTypeUnknown = ValueType(value.TypeUnknown) + ValueTypeBool = ValueType(value.TypeBool) + ValueTypeNumber = ValueType(value.TypeNumber) + ValueTypeString = ValueType(value.TypeString) + ValueTypeRef = ValueType(value.TypeRef) + ValueTypeURL = ValueType(value.TypeURL) + ValueTypeLatLng = ValueType(value.TypeLatLng) + ValueTypeLatLngHeight = ValueType(value.TypeLatLngHeight) + ValueTypeCoordinates = ValueType(value.TypeCoordinates) + ValueTypeRect = ValueType(value.TypeRect) + TypePolygon = ValueType(value.TypePolygon) +) -// LatLngFrom _ -func LatLngFrom(m interface{}) (LatLng, bool) { - l := LatLng{} - err := mapstructure.Decode(m, &l) - return l, err == nil -} +type ValueType value.Type -// LatLngHeight _ -type LatLngHeight struct { - Lat float64 `mapstructure:"lat"` - Lng float64 `mapstructure:"lng"` - Height float64 `mapstructure:"height"` +func ValueTypeFrom(t string) ValueType { + return ValueType(value.Type(t)) } -// LatLngHeightFrom _ -func LatLngHeightFrom(m interface{}) (LatLngHeight, bool) { - l := LatLngHeight{} - err := mapstructure.Decode(m, &l) - return l, err == nil +func (t ValueType) Default() bool { + return value.Type(t).Default() } -// ValueType _ -type ValueType string +func (t ValueType) ValueFrom(i interface{}) *Value { + vv := value.Type(t).ValueFrom(i, nil) + if vv == nil { + return nil + } + return &Value{v: *vv} +} -const ( - // ValueTypeBool _ - ValueTypeBool ValueType = "bool" - // ValueTypeNumber _ - ValueTypeNumber ValueType = "number" - // ValueTypeString _ - ValueTypeString ValueType = "string" - // ValueTypeRef _ - ValueTypeRef ValueType = "ref" - // ValueTypeURL _ - ValueTypeURL ValueType = "url" - // ValueTypeLatLng _ - ValueTypeLatLng ValueType = "latlng" - // ValueTypeLatLngHeight _ - ValueTypeLatLngHeight ValueType = "latlngheight" -) +type Value struct { + v value.Value +} -// ValueTypeFrom _ -func ValueTypeFrom(t string) (ValueType, bool) { - switch ValueType(t) { - case ValueTypeBool: - return ValueTypeBool, true - case ValueTypeNumber: - return ValueTypeNumber, true - case ValueTypeString: - return ValueTypeString, true - case ValueTypeRef: - return ValueTypeRef, true - case ValueTypeURL: - return ValueTypeURL, true - case ValueTypeLatLng: - return ValueTypeLatLng, true - case ValueTypeLatLngHeight: - return ValueTypeLatLngHeight, true +func (v *Value) Clone() *Value { + if v == nil { + return nil + } + vv := v.v.Clone() + if vv == nil { + return nil } - return ValueType(""), false + return &Value{v: *vv} } -// Validate _ -func (t ValueType) Validate() (ValueType, bool) { - switch t { - case ValueTypeBool: - fallthrough - case ValueTypeNumber: - fallthrough - case ValueTypeString: - fallthrough - case ValueTypeRef: - fallthrough - case ValueTypeURL: - fallthrough - case ValueTypeLatLng: - fallthrough - case ValueTypeLatLngHeight: - return t, true +func (v *Value) Type() ValueType { + if v == nil { + return ValueTypeUnknown } - return t, false + return ValueType(v.v.Type()) } -// Value _ -type Value struct { - v interface{} - t ValueType +func (v *Value) Value() interface{} { + if v == nil { + return nil + } + return v.v.Value() } -// Value _ -func (v *Value) Value() interface{} { +func (v *Value) Interface() interface{} { if v == nil { return nil } - return v.v + return v.v.Interface() } -// ValueBool _ func (v *Value) ValueBool() *bool { if v == nil { return nil } - if v2, ok := v.v.(bool); ok { - return &v2 + vv, ok := v.v.ValueBool() + if ok { + return &vv } return nil } -// ValueNumber _ func (v *Value) ValueNumber() *float64 { if v == nil { return nil } - if v2, ok := v.v.(float64); ok { - return &v2 + vv, ok := v.v.ValueNumber() + if ok { + return &vv } return nil } -// ValueString _ func (v *Value) ValueString() *string { if v == nil { return nil } - if v2, ok := v.v.(string); ok { - return &v2 + vv, ok := v.v.ValueString() + if ok { + return &vv } return nil } -// ValueRef _ -func (v *Value) ValueRef() *id.ID { +func (v *Value) ValueRef() *string { if v == nil { return nil } - if v2, ok := v.v.(id.ID); ok { - return &v2 + vv, ok := v.v.ValueRef() + if ok { + return &vv } return nil } -// ValueURL _ func (v *Value) ValueURL() *url.URL { if v == nil { return nil } - if v2, ok := v.v.(*url.URL); ok { - return v2 + vv, ok := v.v.ValueURL() + if ok { + return vv } return nil } -// ValueLatLng _ func (v *Value) ValueLatLng() *LatLng { if v == nil { return nil } - if v2, ok := v.v.(LatLng); ok { - return &v2 + vv, ok := v.v.ValueLatLng() + if ok { + return &vv } return nil } -// ValueLatLngHeight _ func (v *Value) ValueLatLngHeight() *LatLngHeight { if v == nil { return nil } - if v2, ok := v.v.(LatLngHeight); ok { - return &v2 + vv, ok := v.v.ValueLatLngHeight() + if ok { + return &vv } return nil } -// Type _ -func (v *Value) Type() ValueType { - if v == nil { - return ValueType("") - } - return v.t -} - -// ValueFrom _ -func (t ValueType) ValueFrom(v interface{}) *Value { +func (v *Value) ValueCoordinates() *Coordinates { if v == nil { return nil } - switch t { - case ValueTypeBool: - if v2, ok := v.(bool); ok { - return &Value{v: v2, t: ValueTypeBool} - } - case ValueTypeNumber: - if v2, ok := v.(float64); ok { - return &Value{v: v2, t: ValueTypeNumber} - } - if v2, ok := v.(int); ok { - return &Value{v: float64(v2), t: ValueTypeNumber} - } - case ValueTypeString: - if v2, ok := v.(string); ok { - return &Value{v: v2, t: ValueTypeString} - } - case ValueTypeRef: - if v2, ok := v.(id.ID); ok { - return &Value{v: v2, t: ValueTypeRef} - } - if v2, ok := v.(string); ok { - if id, err := id.NewIDWith(v2); err == nil { - return &Value{v: id, t: ValueTypeRef} - } - } - case ValueTypeURL: - if v2, ok := v.(*url.URL); ok { - return &Value{v: v2, t: ValueTypeURL} - } - if v2, ok := v.(string); ok { - if u, err := url.Parse(v2); err == nil { - return &Value{v: u, t: ValueTypeURL} - } - } - case ValueTypeLatLng: - if v2, ok := v.(LatLng); ok { - return &Value{v: v2, t: ValueTypeLatLng} - } else if v2, ok := v.(*LatLng); ok { - if v2 == nil { - return nil - } - return &Value{v: *v2, t: ValueTypeLatLng} - } - v2 := LatLng{} - if err := mapstructure.Decode(v, &v2); err != nil { - return nil - } - return &Value{v: v2, t: ValueTypeLatLng} - case ValueTypeLatLngHeight: - if v2, ok := v.(LatLngHeight); ok { - return &Value{v: v2, t: ValueTypeLatLngHeight} - } else if v2, ok := v.(*LatLngHeight); ok { - if v2 == nil { - return nil - } - return &Value{v: *v2, t: ValueTypeLatLngHeight} - } - v2 := LatLngHeight{} - if err := mapstructure.Decode(v, &v2); err != nil { - return nil - } - return &Value{v: v2, t: ValueTypeLatLng} + vv, ok := v.v.ValueCoordinates() + if ok { + return &vv } return nil } -// ValidateValue _ -func (t ValueType) ValidateValue(v *Value) bool { - if v == nil { - return true - } - vv := v.Value() - if vv == nil { - return true - } - switch t { - case ValueTypeBool: - if _, ok := vv.(bool); ok { - return true - } - case ValueTypeNumber: - if _, ok := vv.(float64); ok { - return true - } - case ValueTypeString: - if _, ok := vv.(string); ok { - return true - } - case ValueTypeRef: - if _, ok := vv.(id.ID); ok { - return true - } - case ValueTypeURL: - if _, ok := vv.(*url.URL); ok { - return true - } - case ValueTypeLatLng: - if _, ok := vv.(LatLng); ok { - return true - } - case ValueTypeLatLngHeight: - if _, ok := vv.(LatLngHeight); ok { - return true - } - } - return false -} - -// Clone _ -func (v *Value) Clone() *Value { +func (v *Value) ValueRect() *Rect { if v == nil { return nil } - var v3 interface{} - switch v2 := v.v.(type) { - case bool: - v3 = v2 - case float64: - v3 = v2 - case string: - v3 = v2 - case id.ID: - v3 = v2 - case *url.URL: - v3, _ = url.Parse(v2.String()) - case LatLng: - v3 = LatLng{Lat: v2.Lat, Lng: v2.Lng} - case LatLngHeight: - v3 = LatLngHeight{Lat: v2.Lat, Lng: v2.Lng, Height: v2.Height} + vv, ok := v.v.ValueRect() + if ok { + return &vv } - return &Value{v: v3, t: v.t} + return nil } -// ValueFrom _ -func ValueFrom(v interface{}) *Value { +func (v *Value) ValuePolygon() *Polygon { if v == nil { return nil } - switch v2 := v.(type) { - case bool: - return &Value{v: v2, t: ValueTypeBool} - case int: - return &Value{v: float64(v2), t: ValueTypeNumber} - case float64: - return &Value{v: v2, t: ValueTypeNumber} - case string: - return &Value{v: v2, t: ValueTypeString} - case id.ID: - return &Value{v: v2, t: ValueTypeRef} - case *url.URL: - return &Value{v: v2, t: ValueTypeURL} - case LatLng: - return &Value{v: v2, t: ValueTypeLatLng} - case LatLngHeight: - return &Value{v: v2, t: ValueTypeLatLngHeight} + vv, ok := v.v.ValuePolygon() + if ok { + return &vv } return nil } -// Interface converts the value into generic representation -func (v *Value) Interface() interface{} { - if v == nil { - return nil +func ValueFromStringOrNumber(s string) *Value { + if vint, err := strconv.Atoi(s); err == nil { + return ValueTypeNumber.ValueFrom(vint) } - switch v2 := v.Value().(type) { - case bool: - return v2 - case float64: - return v2 - case string: - return v2 - case id.ID: - return v2.String() - case *url.URL: - return v2.String() - case LatLng: - return encodeValue(&v2) - case LatLngHeight: - return encodeValue(&v2) + + if vfloat64, err := strconv.ParseFloat(s, 64); err == nil { + return ValueTypeNumber.ValueFrom(vfloat64) } - return nil -} -func encodeValue(v interface{}) map[string]interface{} { - var v3 map[string]interface{} - err := mapstructure.Decode(v, &v3) - if err != nil { - return nil + if vbool, err := strconv.ParseBool(s); err == nil { + return ValueTypeBool.ValueFrom(vbool) } - return v3 + + return ValueTypeString.ValueFrom(s) } diff --git a/pkg/dataset/value_optional.go b/pkg/dataset/value_optional.go new file mode 100644 index 00000000..c3a4eefc --- /dev/null +++ b/pkg/dataset/value_optional.go @@ -0,0 +1,65 @@ +package dataset + +import "github.com/reearth/reearth-backend/pkg/value" + +type OptionalValue struct { + ov value.OptionalValue +} + +func NewOptionalValue(t ValueType, v *Value) *OptionalValue { + var vv *value.Value + if v != nil { + vv = &v.v + } + ov := value.NewOptionalValue(value.Type(t), vv) + if ov == nil { + return nil + } + return &OptionalValue{ov: *ov} +} + +func OptionalValueFrom(v *Value) *OptionalValue { + if v == nil { + return nil + } + ov := value.OptionalValueFrom(&v.v) + if ov == nil { + return nil + } + return &OptionalValue{ + ov: *ov, + } +} + +func (ov *OptionalValue) Type() ValueType { + if ov == nil { + return ValueTypeUnknown + } + return ValueType(ov.ov.Type()) +} + +func (ov *OptionalValue) Value() *Value { + if ov == nil { + return nil + } + vv := ov.ov.Value() + if vv == nil { + return nil + } + return &Value{v: *vv} +} + +func (ov *OptionalValue) TypeAndValue() (ValueType, *Value) { + return ov.Type(), ov.Value() +} + +func (ov *OptionalValue) SetValue(v *Value) { + if ov == nil { + return + } + if v == nil { + ov.ov.SetValue(nil) + } else { + ov.ov.SetValue(&v.v) + } +} diff --git a/pkg/dataset/value_optional_test.go b/pkg/dataset/value_optional_test.go new file mode 100644 index 00000000..01d0212c --- /dev/null +++ b/pkg/dataset/value_optional_test.go @@ -0,0 +1,272 @@ +package dataset + +import ( + "testing" + + "github.com/reearth/reearth-backend/pkg/value" + "github.com/stretchr/testify/assert" +) + +func TestNewNilableValue(t *testing.T) { + type args struct { + t ValueType + v *Value + } + tests := []struct { + name string + args args + want *OptionalValue + }{ + { + name: "default type", + args: args{ + t: ValueTypeString, + v: ValueTypeString.ValueFrom("foo"), + }, + want: &OptionalValue{ov: *value.OptionalValueFrom(value.TypeString.ValueFrom("foo", nil))}, + }, + { + name: "nil value", + args: args{ + t: ValueTypeString, + }, + want: &OptionalValue{ov: *value.NewOptionalValue(value.TypeString, nil)}, + }, + { + name: "invalid value", + args: args{ + t: ValueTypeNumber, + v: ValueTypeString.ValueFrom("foo"), + }, + want: nil, + }, + { + name: "invalid type", + args: args{ + t: ValueTypeUnknown, + v: ValueTypeString.ValueFrom("foo"), + }, + want: nil, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + assert.Equal(t, tt.want, NewOptionalValue(tt.args.t, tt.args.v)) + }) + } +} + +func TestOptionalValueFrom(t *testing.T) { + type args struct { + v *Value + } + tests := []struct { + name string + args args + want *OptionalValue + }{ + { + name: "default type", + args: args{ + v: ValueTypeString.ValueFrom("foo"), + }, + want: &OptionalValue{ov: *value.NewOptionalValue(value.TypeString, value.TypeString.ValueFrom("foo", nil))}, + }, + { + name: "empty value", + args: args{ + v: &Value{v: value.Value{}}, + }, + want: nil, + }, + { + name: "nil value", + args: args{}, + want: nil, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + assert.Equal(t, tt.want, OptionalValueFrom(tt.args.v)) + }) + } +} + +func TestOptionalValue_Type(t *testing.T) { + tests := []struct { + name string + value *OptionalValue + want ValueType + }{ + { + name: "ok", + value: &OptionalValue{ov: *value.NewOptionalValue(value.TypeBool, nil)}, + want: ValueTypeBool, + }, + { + name: "empty", + value: &OptionalValue{}, + want: ValueTypeUnknown, + }, + { + name: "nil", + value: nil, + want: ValueTypeUnknown, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + assert.Equal(t, tt.want, tt.value.Type()) + }) + } +} + +func TestOptionalValue_Value(t *testing.T) { + tests := []struct { + name string + value *OptionalValue + want *Value + }{ + { + name: "ok", + value: &OptionalValue{ov: *value.OptionalValueFrom(value.TypeString.ValueFrom("foobar", nil))}, + want: ValueTypeString.ValueFrom("foobar"), + }, + { + name: "empty", + value: &OptionalValue{}, + want: nil, + }, + { + name: "nil", + value: nil, + want: nil, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + res := tt.value.Value() + assert.Equal(t, tt.want, res) + if res != nil { + assert.NotSame(t, tt.want, res) + } + }) + } +} + +func TestOptionalValue_TypeAndValue(t *testing.T) { + tests := []struct { + name string + value *OptionalValue + wantt ValueType + wantv *Value + }{ + { + name: "ok", + value: &OptionalValue{ov: *value.OptionalValueFrom(value.TypeString.ValueFrom("foobar", nil))}, + wantt: ValueTypeString, + wantv: ValueTypeString.ValueFrom("foobar"), + }, + { + name: "empty", + value: &OptionalValue{}, + wantt: ValueTypeUnknown, + wantv: nil, + }, + { + name: "nil", + value: nil, + wantt: ValueTypeUnknown, + wantv: nil, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + ty, tv := tt.value.TypeAndValue() + assert.Equal(t, tt.wantt, ty) + assert.Equal(t, tt.wantv, tv) + if tv != nil { + assert.NotSame(t, tt.wantv, tv) + } + }) + } +} + +func TestOptionalValue_SetValue(t *testing.T) { + type args struct { + v *Value + } + tests := []struct { + name string + value *OptionalValue + args args + invalid bool + }{ + { + name: "set", + value: &OptionalValue{ov: *value.OptionalValueFrom(value.TypeString.ValueFrom("foo", nil))}, + args: args{v: ValueTypeString.ValueFrom("foobar")}, + }, + { + name: "set to nil", + value: &OptionalValue{ov: *value.NewOptionalValue(value.TypeString, nil)}, + args: args{v: ValueTypeString.ValueFrom("foobar")}, + }, + { + name: "invalid value", + value: &OptionalValue{ov: *value.NewOptionalValue(value.TypeString, nil)}, + args: args{v: ValueTypeNumber.ValueFrom(1)}, + invalid: true, + }, + { + name: "nil value", + args: args{v: ValueTypeNumber.ValueFrom(1)}, + }, + { + name: "empty", + value: &OptionalValue{}, + args: args{v: ValueTypeNumber.ValueFrom(1)}, + invalid: true, + }, + { + name: "nil", + args: args{v: ValueTypeNumber.ValueFrom(1)}, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + var v *Value + if tt.value != nil { + v = tt.value.Value() + } + + tt.value.SetValue(tt.args.v) + + if tt.value != nil { + if tt.invalid { + assert.Equal(t, v, tt.value.Value()) + } else { + assert.Equal(t, tt.args.v, tt.value.Value()) + } + } + }) + } +} diff --git a/pkg/dataset/value_test.go b/pkg/dataset/value_test.go deleted file mode 100644 index 6d2ea89e..00000000 --- a/pkg/dataset/value_test.go +++ /dev/null @@ -1,63 +0,0 @@ -package dataset - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestValueInterface(t *testing.T) { - assert.Equal( - t, - map[string]interface{}{ - "lat": 1.2, - "lng": 1.3, - }, - ValueTypeLatLng.ValueFrom(LatLng{ - Lat: 1.2, - Lng: 1.3, - }).Interface(), - ) - - assert.Equal( - t, - map[string]interface{}{ - "lat": 1.2, - "lng": 1.3, - "height": 1.4, - }, - ValueTypeLatLngHeight.ValueFrom(LatLngHeight{ - Lat: 1.2, - Lng: 1.3, - Height: 1.4, - }).Interface(), - ) -} - -func TestValueFromInterface(t *testing.T) { - assert.Equal( - t, - LatLng{ - Lat: 1.2, - Lng: 1.3, - }, - ValueTypeLatLng.ValueFrom(map[string]interface{}{ - "lat": 1.2, - "lng": 1.3, - }).Value(), - ) - - assert.Equal( - t, - LatLngHeight{ - Lat: 1.2, - Lng: 1.3, - Height: 1.4, - }, - ValueTypeLatLngHeight.ValueFrom(map[string]interface{}{ - "lat": 1.2, - "lng": 1.3, - "height": 1.4, - }).Value(), - ) -} diff --git a/pkg/layer/decoding/common.go b/pkg/layer/decoding/common.go index 15822ea2..acae2d58 100644 --- a/pkg/layer/decoding/common.go +++ b/pkg/layer/decoding/common.go @@ -104,14 +104,14 @@ func createProperty(t string, v interface{}, sceneID id.SceneID, styleItem inter switch t { case "Point": if pf, ok := v.(property.LatLngHeight); ok { - v2, ok := property.ValueTypeLatLng.ValueFrom(&property.LatLng{Lat: pf.Lat, Lng: pf.Lng}) - if !ok { + v2 := property.ValueTypeLatLng.ValueFrom(&property.LatLng{Lat: pf.Lat, Lng: pf.Lng}) + if v2 == nil { return nil, ErrFieldType } f.UpdateUnsafe(v2) - v3, ok := property.ValueTypeNumber.ValueFrom(pf.Height) - if !ok { + v3 := property.ValueTypeNumber.ValueFrom(pf.Height) + if v3 == nil { return nil, ErrFieldType } f2, _, _, _ := p.GetOrCreateField( @@ -121,8 +121,8 @@ func createProperty(t string, v interface{}, sceneID id.SceneID, styleItem inter f2.UpdateUnsafe(v3) } else if pf, ok := v.(property.LatLng); ok { - v2, ok := property.ValueTypeLatLng.ValueFrom(&property.LatLng{Lat: pf.Lat, Lng: pf.Lng}) - if !ok { + v2 := property.ValueTypeLatLng.ValueFrom(&property.LatLng{Lat: pf.Lat, Lng: pf.Lng}) + if v2 == nil { return nil, ErrFieldType } f.UpdateUnsafe(v2) @@ -135,8 +135,8 @@ func createProperty(t string, v interface{}, sceneID id.SceneID, styleItem inter return nil, ErrFieldType } if s.IconStyle.Icon != nil && len(s.IconStyle.Icon.Href) > 0 { - imageValue, ok := property.ValueTypeURL.ValueFrom(s.IconStyle.Icon.Href) - if !ok { + imageValue := property.ValueTypeURL.ValueFrom(s.IconStyle.Icon.Href) + if imageValue == nil { return nil, ErrFieldType } imageField, _, _, _ := p.GetOrCreateField( @@ -146,8 +146,8 @@ func createProperty(t string, v interface{}, sceneID id.SceneID, styleItem inter imageField.UpdateUnsafe(imageValue) } if s.IconStyle.Scale != 0 { - scaleValue, ok := property.ValueTypeNumber.ValueFrom(s.IconStyle.Scale) - if !ok { + scaleValue := property.ValueTypeNumber.ValueFrom(s.IconStyle.Scale) + if scaleValue == nil { return nil, ErrFieldType } scaleField, _, _, _ := p.GetOrCreateField( @@ -157,8 +157,8 @@ func createProperty(t string, v interface{}, sceneID id.SceneID, styleItem inter scaleField.UpdateUnsafe(scaleValue) } if len(s.IconStyle.Color) > 0 { - colorValue, ok := property.ValueTypeString.ValueFrom(s.IconStyle.Color) - if !ok { + colorValue := property.ValueTypeString.ValueFrom(s.IconStyle.Color) + if colorValue == nil { return nil, ErrFieldType } colorField, _, _, _ := p.GetOrCreateField( @@ -173,8 +173,8 @@ func createProperty(t string, v interface{}, sceneID id.SceneID, styleItem inter return nil, ErrFieldType } if len(s) > 0 { - colorValue, ok := property.ValueTypeString.ValueFrom(s) - if !ok { + colorValue := property.ValueTypeString.ValueFrom(s) + if colorValue == nil { return nil, ErrFieldType } colorField, _, _, _ := p.GetOrCreateField( @@ -189,8 +189,8 @@ func createProperty(t string, v interface{}, sceneID id.SceneID, styleItem inter return nil, ErrFieldType } if len(s.Color) > 0 { - colorValue, ok := property.ValueTypeString.ValueFrom(s.Color) - if !ok { + colorValue := property.ValueTypeString.ValueFrom(s.Color) + if colorValue == nil { return nil, ErrFieldType } colorField, _, _, _ := p.GetOrCreateField( @@ -200,8 +200,8 @@ func createProperty(t string, v interface{}, sceneID id.SceneID, styleItem inter colorField.UpdateUnsafe(colorValue) } if s.PixelSize != 0 { - sizeValue, ok := property.ValueTypeNumber.ValueFrom(s.PixelSize) - if !ok { + sizeValue := property.ValueTypeNumber.ValueFrom(s.PixelSize) + if sizeValue == nil { return nil, ErrFieldType } sizeField, _, _, _ := p.GetOrCreateField( @@ -213,8 +213,8 @@ func createProperty(t string, v interface{}, sceneID id.SceneID, styleItem inter } } case "Polygon": - v2, ok := property.ValueTypePolygon.ValueFrom(v) - if !ok { + v2 := property.ValueTypePolygon.ValueFrom(v) + if v2 == nil { return nil, ErrFieldType } f.UpdateUnsafe(v2) @@ -226,8 +226,8 @@ func createProperty(t string, v interface{}, sceneID id.SceneID, styleItem inter return nil, ErrFieldType } if s.PolyStyle.Stroke { - stroke, ok := property.ValueTypeBool.ValueFrom(s.PolyStyle.Stroke) - if !ok { + stroke := property.ValueTypeBool.ValueFrom(s.PolyStyle.Stroke) + if stroke == nil { return nil, ErrFieldType } strokeField, _, _, _ := p.GetOrCreateField( @@ -237,8 +237,8 @@ func createProperty(t string, v interface{}, sceneID id.SceneID, styleItem inter strokeField.UpdateUnsafe(stroke) } if s.LineStyle.Width != 0 { - width, ok := property.ValueTypeNumber.ValueFrom(s.LineStyle.Width) - if !ok { + width := property.ValueTypeNumber.ValueFrom(s.LineStyle.Width) + if width == nil { return nil, ErrFieldType } widthField, _, _, _ := p.GetOrCreateField( @@ -248,8 +248,8 @@ func createProperty(t string, v interface{}, sceneID id.SceneID, styleItem inter widthField.UpdateUnsafe(width) } if len(s.LineStyle.Color) > 0 { - color, ok := property.ValueTypeString.ValueFrom(s.LineStyle.Color) - if !ok { + color := property.ValueTypeString.ValueFrom(s.LineStyle.Color) + if color == nil { return nil, ErrFieldType } colorField, _, _, _ := p.GetOrCreateField( @@ -259,8 +259,8 @@ func createProperty(t string, v interface{}, sceneID id.SceneID, styleItem inter colorField.UpdateUnsafe(color) } if s.PolyStyle.Fill { - fill, ok := property.ValueTypeBool.ValueFrom(s.PolyStyle.Fill) - if !ok { + fill := property.ValueTypeBool.ValueFrom(s.PolyStyle.Fill) + if fill == nil { return nil, ErrFieldType } fillField, _, _, _ := p.GetOrCreateField( @@ -270,8 +270,8 @@ func createProperty(t string, v interface{}, sceneID id.SceneID, styleItem inter fillField.UpdateUnsafe(fill) } if len(s.PolyStyle.Color) > 0 { - color, ok := property.ValueTypeString.ValueFrom(s.PolyStyle.Color) - if !ok { + color := property.ValueTypeString.ValueFrom(s.PolyStyle.Color) + if color == nil { return nil, ErrFieldType } colorField, _, _, _ := p.GetOrCreateField( @@ -287,8 +287,8 @@ func createProperty(t string, v interface{}, sceneID id.SceneID, styleItem inter return nil, ErrFieldType } if s.Stroke { - stroke, ok := property.ValueTypeBool.ValueFrom(s.Stroke) - if !ok { + stroke := property.ValueTypeBool.ValueFrom(s.Stroke) + if stroke == nil { return nil, ErrFieldType } strokeField, _, _, _ := p.GetOrCreateField( @@ -298,8 +298,8 @@ func createProperty(t string, v interface{}, sceneID id.SceneID, styleItem inter strokeField.UpdateUnsafe(stroke) } if s.StrokeWidth != 0 { - width, ok := property.ValueTypeNumber.ValueFrom(s.StrokeWidth) - if !ok { + width := property.ValueTypeNumber.ValueFrom(s.StrokeWidth) + if width == nil { return nil, ErrFieldType } widthField, _, _, _ := p.GetOrCreateField( @@ -323,8 +323,8 @@ func createProperty(t string, v interface{}, sceneID id.SceneID, styleItem inter return nil, err } } - color, ok := property.ValueTypeString.ValueFrom(colorValue) - if !ok { + color := property.ValueTypeString.ValueFrom(colorValue) + if color == nil { return nil, ErrFieldType } colorField, _, _, _ := p.GetOrCreateField( @@ -334,8 +334,8 @@ func createProperty(t string, v interface{}, sceneID id.SceneID, styleItem inter colorField.UpdateUnsafe(color) } if s.Fill { - fill, ok := property.ValueTypeBool.ValueFrom(s.Fill) - if !ok { + fill := property.ValueTypeBool.ValueFrom(s.Fill) + if fill == nil { return nil, ErrFieldType } fillField, _, _, _ := p.GetOrCreateField( @@ -359,8 +359,8 @@ func createProperty(t string, v interface{}, sceneID id.SceneID, styleItem inter return nil, err } } - color, ok := property.ValueTypeString.ValueFrom(colorValue) - if !ok { + color := property.ValueTypeString.ValueFrom(colorValue) + if color == nil { return nil, ErrFieldType } colorField, _, _, _ := p.GetOrCreateField( @@ -376,8 +376,8 @@ func createProperty(t string, v interface{}, sceneID id.SceneID, styleItem inter } if s.StrokeWidth > 0 { - width, ok := property.ValueTypeNumber.ValueFrom(s.StrokeWidth) - if !ok { + width := property.ValueTypeNumber.ValueFrom(s.StrokeWidth) + if width == nil { return nil, ErrFieldType } widthField, _, _, _ := p.GetOrCreateField( @@ -388,8 +388,8 @@ func createProperty(t string, v interface{}, sceneID id.SceneID, styleItem inter } if len(s.FillColor) > 0 { - fill, ok := property.ValueTypeString.ValueFrom(s.FillColor) - if !ok { + fill := property.ValueTypeString.ValueFrom(s.FillColor) + if fill == nil { return nil, ErrFieldType } fillField, _, _, _ := p.GetOrCreateField( @@ -400,8 +400,8 @@ func createProperty(t string, v interface{}, sceneID id.SceneID, styleItem inter } if len(s.StrokeColor) > 0 { - color, ok := property.ValueTypeString.ValueFrom(s.StrokeColor) - if !ok { + color := property.ValueTypeString.ValueFrom(s.StrokeColor) + if color == nil { return nil, ErrFieldType } colorField, _, _, _ := p.GetOrCreateField( @@ -413,8 +413,8 @@ func createProperty(t string, v interface{}, sceneID id.SceneID, styleItem inter } } case "Polyline": - v2, ok := property.ValueTypeCoordinates.ValueFrom(v) - if !ok { + v2 := property.ValueTypeCoordinates.ValueFrom(v) + if v2 == nil { return nil, ErrFieldType } f.UpdateUnsafe(v2) @@ -427,8 +427,8 @@ func createProperty(t string, v interface{}, sceneID id.SceneID, styleItem inter } if len(s.LineStyle.Color) > 0 { - color, ok := property.ValueTypeString.ValueFrom(s.LineStyle.Color) - if !ok { + color := property.ValueTypeString.ValueFrom(s.LineStyle.Color) + if color == nil { return nil, ErrFieldType } colorField, _, _, _ := p.GetOrCreateField( @@ -439,8 +439,8 @@ func createProperty(t string, v interface{}, sceneID id.SceneID, styleItem inter } if s.LineStyle.Width != 0 { - width, ok := property.ValueTypeNumber.ValueFrom(s.LineStyle.Width) - if !ok { + width := property.ValueTypeNumber.ValueFrom(s.LineStyle.Width) + if width == nil { return nil, ErrFieldType } widthField, _, _, _ := p.GetOrCreateField( @@ -456,8 +456,8 @@ func createProperty(t string, v interface{}, sceneID id.SceneID, styleItem inter } if s.Width != 0 { - width, ok := property.ValueTypeNumber.ValueFrom(s.Width) - if !ok { + width := property.ValueTypeNumber.ValueFrom(s.Width) + if width == nil { return nil, ErrFieldType } widthField, _, _, _ := p.GetOrCreateField( @@ -485,8 +485,8 @@ func createProperty(t string, v interface{}, sceneID id.SceneID, styleItem inter } } - color, ok := property.ValueTypeString.ValueFrom(colorValue) - if !ok { + color := property.ValueTypeString.ValueFrom(colorValue) + if color == nil { return nil, ErrFieldType } @@ -503,8 +503,8 @@ func createProperty(t string, v interface{}, sceneID id.SceneID, styleItem inter } if s.StrokeWidth > 0 { - width, ok := property.ValueTypeNumber.ValueFrom(s.StrokeWidth) - if !ok { + width := property.ValueTypeNumber.ValueFrom(s.StrokeWidth) + if width == nil { return nil, ErrFieldType } widthField, _, _, _ := p.GetOrCreateField( @@ -515,8 +515,8 @@ func createProperty(t string, v interface{}, sceneID id.SceneID, styleItem inter } if len(s.StrokeColor) > 0 { - color, ok := property.ValueTypeString.ValueFrom(s.StrokeColor) - if !ok { + color := property.ValueTypeString.ValueFrom(s.StrokeColor) + if color == nil { return nil, ErrFieldType } colorField, _, _, _ := p.GetOrCreateField( diff --git a/pkg/layer/decoding/reearth.go b/pkg/layer/decoding/reearth.go index 96e8fcbd..361b0bcd 100644 --- a/pkg/layer/decoding/reearth.go +++ b/pkg/layer/decoding/reearth.go @@ -275,13 +275,8 @@ func (f *ReearthPropertyField) propertyField(key id.PropertySchemaFieldID) *prop return nil } - vt, ok := property.ValueTypeFrom(f.Type) - if !ok { - return nil - } - - v, ok := vt.ValueFrom(f.Value) - if !ok { + v := property.ValueType(f.Type).ValueFrom(f.Value) + if v == nil { return nil } @@ -299,7 +294,7 @@ func (f *ReearthPropertyField) propertyField(key id.PropertySchemaFieldID) *prop return &property.InitializerField{ Field: key, - Type: vt, + Type: v.Type(), Value: v, Links: links, } diff --git a/pkg/layer/decoding/reearth_test.go b/pkg/layer/decoding/reearth_test.go index 8ddf35cc..29b78151 100644 --- a/pkg/layer/decoding/reearth_test.go +++ b/pkg/layer/decoding/reearth_test.go @@ -139,7 +139,7 @@ func TestReearthDecoder_Decode(t *testing.T) { { Field: id.PropertySchemaFieldID("latlng"), Type: property.ValueTypeLatLng, - Value: property.ValueTypeLatLng.MustBeValue(property.LatLng{Lat: 1, Lng: 2}), + Value: property.ValueTypeLatLng.ValueFrom(property.LatLng{Lat: 1, Lng: 2}), }, }, }, @@ -188,7 +188,7 @@ func TestReearthDecoder_Decode(t *testing.T) { { Field: id.PropertySchemaFieldID("foobar"), Type: property.ValueTypeString, - Value: property.ValueTypeString.MustBeValue("bar"), + Value: property.ValueTypeString.ValueFrom("bar"), }, }, }, @@ -198,7 +198,7 @@ func TestReearthDecoder_Decode(t *testing.T) { { Field: id.PropertySchemaFieldID("foobar"), Type: property.ValueTypeString, - Value: property.ValueTypeString.MustBeValue("foo"), + Value: property.ValueTypeString.ValueFrom("foo"), }, }, }, diff --git a/pkg/layer/encoding/common.go b/pkg/layer/encoding/common.go index 63275412..292b6f4c 100644 --- a/pkg/layer/encoding/common.go +++ b/pkg/layer/encoding/common.go @@ -1,7 +1,6 @@ package encoding import ( - "errors" "image/color" "strconv" "strings" @@ -9,9 +8,11 @@ import ( "gopkg.in/go-playground/colors.v1" ) -var ErrInvalidColor = errors.New("invalid color") +func getColor(str string) *color.RGBA { + if len(str) == 0 { + return nil + } -func getColor(str string) (*color.RGBA, error) { cs := str a := "" @@ -27,7 +28,7 @@ func getColor(str string) (*color.RGBA, error) { b, err := colors.Parse(cs) if err != nil || b == nil { - return nil, ErrInvalidColor + return nil } c := b.ToRGBA() @@ -39,5 +40,5 @@ func getColor(str string) (*color.RGBA, error) { alpha = uint8(c.A * 255) } - return &color.RGBA{R: c.R, G: c.G, B: c.B, A: alpha}, nil + return &color.RGBA{R: c.R, G: c.G, B: c.B, A: alpha} } diff --git a/pkg/layer/encoding/common_test.go b/pkg/layer/encoding/common_test.go index 99da072f..cf356e26 100644 --- a/pkg/layer/encoding/common_test.go +++ b/pkg/layer/encoding/common_test.go @@ -8,19 +8,8 @@ import ( ) func TestGetColor(t *testing.T) { - c, err := getColor("#ffffff") - assert.NoError(t, err) - assert.Equal(t, &color.RGBA{R: 255, G: 255, B: 255, A: 255}, c) - - c, err = getColor("#fff") - assert.NoError(t, err) - assert.Equal(t, &color.RGBA{R: 255, G: 255, B: 255, A: 255}, c) - - c, err = getColor("#fffa") - assert.NoError(t, err) - assert.Equal(t, &color.RGBA{R: 255, G: 255, B: 255, A: 170}, c) - - c, err = getColor("#ff0000aa") - assert.NoError(t, err) - assert.Equal(t, &color.RGBA{R: 255, G: 0, B: 0, A: 170}, c) + assert.Equal(t, &color.RGBA{R: 255, G: 255, B: 255, A: 255}, getColor("#ffffff")) + assert.Equal(t, &color.RGBA{R: 255, G: 255, B: 255, A: 255}, getColor("#fff")) + assert.Equal(t, &color.RGBA{R: 255, G: 255, B: 255, A: 170}, getColor("#fffa")) + assert.Equal(t, &color.RGBA{R: 255, G: 0, B: 0, A: 170}, getColor("#ff0000aa")) } diff --git a/pkg/layer/encoding/czml.go b/pkg/layer/encoding/czml.go index 4e386761..5f616c97 100644 --- a/pkg/layer/encoding/czml.go +++ b/pkg/layer/encoding/czml.go @@ -8,7 +8,6 @@ import ( "github.com/reearth/reearth-backend/pkg/czml" "github.com/reearth/reearth-backend/pkg/id" "github.com/reearth/reearth-backend/pkg/layer/merging" - "github.com/reearth/reearth-backend/pkg/property" ) type CZMLEncoder struct { @@ -21,17 +20,12 @@ func NewCZMLEncoder(w io.Writer) *CZMLEncoder { } } -func (e *CZMLEncoder) stringToCZMLColor(s string) (*czml.Color, error) { - c, err := getColor(s) - if err != nil || c == nil { - if err == nil { - err = ErrInvalidColor - } - return nil, err +func (e *CZMLEncoder) stringToCZMLColor(s string) *czml.Color { + c := getColor(s) + if c == nil { + return nil } - return &czml.Color{ - RGBA: []int64{int64(c.R), int64(c.G), int64(c.B), int64(c.A)}, - }, nil + return &czml.Color{RGBA: []int64{int64(c.R), int64(c.G), int64(c.B), int64(c.A)}} } func (e *CZMLEncoder) encodeSingleLayer(li *merging.SealedLayerItem) (*czml.Feature, error) { @@ -39,179 +33,105 @@ func (e *CZMLEncoder) encodeSingleLayer(li *merging.SealedLayerItem) (*czml.Feat return nil, nil } - var ok bool - var err error - var pointSize float64 - var pointColor string feature := czml.Feature{ - Id: "", - Name: "", - Point: nil, + Id: li.Original.String(), + Name: li.Name, } - feature.Name = li.Name + switch li.ExtensionID.String() { case "marker": - latlng := property.LatLng{} - var height float64 - if f := li.Property.Field("location"); f != nil { - latlng, ok = f.PropertyValue.ValueLatLng() - if !ok { - dsll := f.DatasetValue.ValueLatLng() - if dsll != nil { - latlng = property.LatLng{ - Lat: dsll.Lat, - Lng: dsll.Lng, - } - } else { - return nil, errors.New("invalid value type") - } - } - - if f := li.Property.Field("height"); f != nil { - height, ok = f.PropertyValue.ValueNumber() - if !ok { - dsHeight := f.DatasetValue.ValueNumber() - if dsHeight != nil { - height = *dsHeight - } else { - return nil, errors.New("invalid value type") - } - } - position := czml.Position{ - CartographicDegrees: []float64{latlng.Lng, latlng.Lat, height}, - } - feature.Position = &position - } else { - position := czml.Position{ - CartographicDegrees: []float64{latlng.Lng, latlng.Lat}, - } - feature.Position = &position - } + var position czml.Position + point := czml.Point{} + if f := li.Property.Field("location").Value().ValueLatLng(); f != nil { + position = czml.Position{CartographicDegrees: []float64{(*f).Lng, (*f).Lat}} + } else { + return nil, errors.New("invalid value type") } - if f := li.Property.Field("pointColor"); f != nil { - pointColor, ok = f.PropertyValue.ValueString() - if !ok { - return nil, errors.New("invalid value type") - } + + if f := li.Property.Field("height").Value().ValueNumber(); f != nil { + position.CartographicDegrees = append(position.CartographicDegrees, *f) } - if f := li.Property.Field("pointSize"); f != nil { - pointSize, ok = f.PropertyValue.ValueNumber() - if !ok { - return nil, errors.New("invalid value type") - } + + if f := li.Property.Field("pointColor").Value().ValueString(); f != nil { + point.Color = *f } - if pointSize != 0 || len(pointColor) > 0 { - point := czml.Point{ - Color: pointColor, - PixelSize: pointSize, - } - feature.Point = &point + + if f := li.Property.Field("pointSize").Value().ValueNumber(); f != nil { + point.PixelSize = *f } + + feature.Position = &position + feature.Point = &point case "polygon": - var polygon property.Polygon - position := czml.Position{} - var fill, stroke bool - var fillColor, strokeColor *czml.Color - var strokeWidth float64 - if f := li.Property.Field("polygon"); f != nil { - polygon, ok = f.PropertyValue.ValuePolygon() - if !ok { - return nil, errors.New("invalid value type") - } - for _, c := range polygon { - for _, l := range c { - position.CartographicDegrees = append(position.CartographicDegrees, []float64{l.Lng, l.Lat, l.Height}...) - } - } - } - if f := li.Property.Field("fill"); f != nil { - fill, ok = f.PropertyValue.ValueBool() - if !ok { - return nil, errors.New("invalid value type") - } + polygon := czml.Polygon{} + + if f := li.Property.Field("polygon").Value().ValuePolygon(); f != nil && len(*f) > 0 { + // CZML polygon does not support multi inner rings + for _, l := range (*f)[0] { + polygon.Positions.CartographicDegrees = append( + polygon.Positions.CartographicDegrees, + []float64{l.Lng, l.Lat, l.Height}..., + ) + } + } else { + // polygon is required + return nil, errors.New("invalid value type") } - if f := li.Property.Field("stroke"); f != nil { - stroke, ok = f.PropertyValue.ValueBool() - if !ok { - return nil, errors.New("invalid value type") - } + + if f := li.Property.Field("fill").Value().ValueBool(); f != nil { + polygon.Fill = *f } - if f := li.Property.Field("fillColor"); f != nil { - fillStr, ok := f.PropertyValue.ValueString() - if !ok { - return nil, errors.New("invalid value type") - } - fillColor, err = e.stringToCZMLColor(fillStr) - if err != nil { - return nil, err - } + + if f := li.Property.Field("stroke").Value().ValueBool(); f != nil { + polygon.Stroke = *f } - if f := li.Property.Field("strokeColor"); f != nil { - strokeStr, ok := f.PropertyValue.ValueString() - if !ok { - return nil, errors.New("invalid value type") - } - strokeColor, err = e.stringToCZMLColor(strokeStr) - if err != nil { - return nil, err + + if f := li.Property.Field("fillColor").Value().ValueString(); f != nil { + if c := e.stringToCZMLColor(*f); c != nil { + polygon.Material = &czml.Material{SolidColor: &czml.SolidColor{Color: c}} } } - if f := li.Property.Field("strokeWidth"); f != nil { - strokeWidth, ok = f.PropertyValue.ValueNumber() - if !ok { - return nil, errors.New("invalid value type") + + if f := li.Property.Field("strokeColor").Value().ValueString(); f != nil { + if strokeColor := e.stringToCZMLColor(*f); strokeColor != nil { + polygon.StrokeColor = strokeColor } } - polygonCZML := czml.Polygon{ - Positions: position, - Fill: fill, - Material: &czml.Material{SolidColor: &czml.SolidColor{Color: fillColor}}, - Stroke: stroke, - StrokeColor: strokeColor, - StrokeWidth: strokeWidth, + + if f := li.Property.Field("strokeWidth").Value().ValueNumber(); f != nil { + polygon.StrokeWidth = *f } - feature.Polygon = &polygonCZML + + feature.Polygon = &polygon case "polyline": - var polyline property.Coordinates - position := czml.Position{} - var strokeColor *czml.Color - var strokeWidth float64 - if f := li.Property.Field("coordinates"); f != nil { - polyline, ok = f.PropertyValue.ValueCoordinates() - if !ok { - return nil, errors.New("invalid value type") - } - for _, l := range polyline { - position.CartographicDegrees = append(position.CartographicDegrees, []float64{l.Lng, l.Lat, l.Height}...) - } - } + polyline := czml.Polyline{Positions: czml.Position{}} - if f := li.Property.Field("strokeColor"); f != nil { - strokeStr, ok := f.PropertyValue.ValueString() - if !ok { - return nil, errors.New("invalid value type") - } - strokeColor, err = e.stringToCZMLColor(strokeStr) - if err != nil { - return nil, err + if f := li.Property.Field("coordinates").Value().ValueCoordinates(); f != nil { + for _, l := range *f { + polyline.Positions.CartographicDegrees = append( + polyline.Positions.CartographicDegrees, + l.Lng, l.Lat, l.Height, + ) } + } else { + return nil, errors.New("invalid value type") } - if f := li.Property.Field("strokeWidth"); f != nil { - strokeWidth, ok = f.PropertyValue.ValueNumber() - if !ok { - return nil, errors.New("invalid value type") + + if f := li.Property.Field("strokeColor").Value().ValueString(); f != nil { + if strokeColor := e.stringToCZMLColor(*f); strokeColor != nil { + polyline.Material = &czml.Material{ + PolylineOutline: &czml.PolylineOutline{Color: strokeColor}, + } } } - polylineCZML := czml.Polyline{ - Positions: position, - Material: &czml.Material{ - PolylineOutline: &czml.PolylineOutline{Color: strokeColor}, - }, - Width: strokeWidth, + + if f := li.Property.Field("strokeWidth").Value().ValueNumber(); f != nil { + polyline.Width = *f } - feature.Polyline = &polylineCZML + feature.Polyline = &polyline } + return &feature, nil } @@ -248,6 +168,7 @@ func (e *CZMLEncoder) encodeLayerGroup(li *merging.SealedLayerGroup) ([]*czml.Fe func (e *CZMLEncoder) Encode(layer merging.SealedLayer) error { var res []*czml.Feature var err error + if i, ok := layer.(*merging.SealedLayerItem); ok { feature, err := e.encodeSingleLayer(i) if err != nil { @@ -261,9 +182,8 @@ func (e *CZMLEncoder) Encode(layer merging.SealedLayer) error { return err } } - en := json.NewEncoder(e.writer) - err = en.Encode(res) - if err != nil { + + if err := json.NewEncoder(e.writer).Encode(res); err != nil { return err } return nil diff --git a/pkg/layer/encoding/czml_test.go b/pkg/layer/encoding/czml_test.go index 0167ce5b..6b995446 100644 --- a/pkg/layer/encoding/czml_test.go +++ b/pkg/layer/encoding/czml_test.go @@ -3,7 +3,6 @@ package encoding import ( "bytes" "encoding/json" - "io" "testing" "github.com/reearth/reearth-backend/pkg/czml" @@ -15,503 +14,216 @@ import ( "github.com/stretchr/testify/assert" ) -var _ Encoder = (*GeoJSONEncoder)(nil) - -func TestEncodeCZMLPoint(t *testing.T) { - lid := id.MustLayerID(id.New().String()) - sid := id.MustSceneID(id.New().String()) - pid := id.MustPropertyID(id.New().String()) - ex := id.PluginExtensionID("marker") - iid := id.MustPropertyItemID(id.New().String()) - v1 := property.LatLng{ - Lat: 4.4, - Lng: 53.4, - } - - f1 := property.SealedField{ - ID: id.PropertySchemaFieldID("location"), - Type: "latlng", - DatasetValue: nil, - PropertyValue: v1.Value(), - } - fl1 := []*property.SealedField{} - fl1 = append(fl1, &f1) - item1 := property.SealedItem{ - Original: &iid, - Parent: nil, - SchemaGroup: id.PropertySchemaGroupID("default"), - LinkedDataset: nil, - Fields: fl1, - Groups: nil, - } - il := []*property.SealedItem{} - il = append(il, &item1) - - v2 := property.ValueTypeString - f2 := property.SealedField{ - ID: id.PropertySchemaFieldID("pointColor"), - Type: "string", - DatasetValue: nil, - PropertyValue: v2.ValueFromUnsafe("#7fff00ff"), - } - fl2 := []*property.SealedField{} - fl2 = append(fl2, &f2) - item2 := property.SealedItem{ - Original: &iid, - Parent: nil, - SchemaGroup: id.PropertySchemaGroupID("default"), - LinkedDataset: nil, - Fields: fl2, - Groups: nil, - } - il = append(il, &item2) - v3 := property.ValueTypeNumber - f3 := property.SealedField{ - ID: id.PropertySchemaFieldID("height"), - Type: "number", - DatasetValue: nil, - PropertyValue: v3.ValueFromUnsafe(34), - } - fl3 := []*property.SealedField{} - fl3 = append(fl3, &f3) - item3 := property.SealedItem{ - Original: &iid, - Parent: nil, - SchemaGroup: id.PropertySchemaGroupID("default"), - LinkedDataset: nil, - Fields: fl3, - Groups: nil, - } - il = append(il, &item3) - v4 := property.ValueTypeNumber - f4 := property.SealedField{ - ID: id.PropertySchemaFieldID("pointSize"), - Type: "number", - DatasetValue: nil, - PropertyValue: v4.ValueFromUnsafe(2.4), - } - fl4 := []*property.SealedField{} - fl4 = append(fl4, &f4) - item4 := property.SealedItem{ - Original: &iid, - Parent: nil, - SchemaGroup: id.PropertySchemaGroupID("default"), - LinkedDataset: nil, - Fields: fl4, - Groups: nil, - } - il = append(il, &item4) - sp := property.Sealed{ - Original: &pid, - Items: il, - } - l := merging.SealedLayerItem{ - SealedLayerCommon: merging.SealedLayerCommon{ - Merged: layer.Merged{ - Original: lid, - Parent: nil, - Name: "test", - Scene: sid, - Property: nil, - Infobox: nil, - PluginID: &id.OfficialPluginID, - ExtensionID: &ex, +var _ Encoder = (*CZMLEncoder)(nil) + +func TestCZMLEncoder_Encode(t *testing.T) { + lid := id.NewLayerID() + sid := id.NewSceneID() + iid := id.NewPropertyItemID() + + tests := []struct { + name string + target merging.SealedLayer + want []*czml.Feature + }{ + { + name: "marker", + target: &merging.SealedLayerItem{ + SealedLayerCommon: merging.SealedLayerCommon{ + Merged: layer.Merged{ + Original: lid, + Name: "test", + Scene: sid, + PluginID: &id.OfficialPluginID, + ExtensionID: id.PluginExtensionID("marker").Ref(), + }, + Property: &property.Sealed{ + Original: id.NewPropertyID().Ref(), + Items: []*property.SealedItem{ + { + Original: &iid, + SchemaGroup: id.PropertySchemaGroupID("default"), + Fields: []*property.SealedField{ + { + ID: id.PropertySchemaFieldID("location"), + Type: property.ValueTypeLatLng, + PropertyValue: property.ValueTypeLatLng.ValueFrom(property.LatLng{Lat: 4.4, Lng: 53.4}), + }, + { + ID: id.PropertySchemaFieldID("height"), + Type: property.ValueTypeNumber, + PropertyValue: property.ValueTypeNumber.ValueFrom(34), + }, + { + ID: id.PropertySchemaFieldID("pointColor"), + Type: property.ValueTypeString, + PropertyValue: property.ValueTypeString.ValueFrom("#7fff00ff"), + }, + { + ID: id.PropertySchemaFieldID("pointSize"), + Type: property.ValueTypeNumber, + PropertyValue: property.ValueTypeNumber.ValueFrom(2.4), + }, + }, + }, + }, + }, + }, + }, + want: []*czml.Feature{ + { + Id: lid.String(), + Name: "test", + Position: &czml.Position{CartographicDegrees: []float64{53.4, 4.4, 34}}, + Point: &czml.Point{ + Color: "#7fff00ff", + PixelSize: float64(2.4), + }, + }, }, - Property: &sp, - Infobox: nil, - }} - - reader, writer := io.Pipe() - en := NewCZMLEncoder(writer) - var err error - go func() { - defer func() { - _ = writer.Close() - }() - err = en.Encode(&l) - assert.NoError(t, err) - }() - - colorStr, _ := f2.PropertyValue.ValueString() - height, _ := f3.PropertyValue.ValueNumber() - size, _ := f4.PropertyValue.ValueNumber() - expected := []*czml.Feature{} - exPos := czml.Position{CartographicDegrees: []float64{v1.Lng, v1.Lat, height}} - exPoint := czml.Point{ - Color: colorStr, - PixelSize: size, - } - exValue := czml.Feature{ - Id: "", - Name: "test", - Position: &exPos, - Point: &exPoint, - } - expected = append(expected, &exValue) - reader2, writer2 := io.Pipe() - exEn := json.NewEncoder(writer2) - go func() { - defer func() { - _ = writer2.Close() - }() - err = exEn.Encode(expected) - assert.NoError(t, err) - }() - - buf := new(bytes.Buffer) - _, err = buf.ReadFrom(reader) - assert.NoError(t, err) - s := buf.String() - buf2 := new(bytes.Buffer) - _, err = buf2.ReadFrom(reader2) - assert.NoError(t, err) - s2 := buf2.String() - assert.Equal(t, s2, s) -} - -func TestEncodeCZMLPolygon(t *testing.T) { - lid := id.MustLayerID(id.New().String()) - sid := id.MustSceneID(id.New().String()) - pid := id.MustPropertyID(id.New().String()) - ex := id.PluginExtensionID("polygon") - iid := id.MustPropertyItemID(id.New().String()) - vc := property.Coordinates{ - property.LatLngHeight{ - Lat: 3.4, - Lng: 5.34, - Height: 100, - }, property.LatLngHeight{ - Lat: 45.4, - Lng: 2.34, - Height: 100, - }, property.LatLngHeight{ - Lat: 34.66, - Lng: 654.34, - Height: 100, }, - } - v1 := property.Polygon{vc} - f1 := property.SealedField{ - ID: id.PropertySchemaFieldID("polygon"), - Type: "polygon", - DatasetValue: nil, - PropertyValue: v1.Value(), - } - fl1 := []*property.SealedField{} - fl1 = append(fl1, &f1) - item1 := property.SealedItem{ - Original: &iid, - Parent: nil, - SchemaGroup: id.PropertySchemaGroupID("default"), - LinkedDataset: nil, - Fields: fl1, - Groups: nil, - } - il := []*property.SealedItem{} - il = append(il, &item1) - v2 := property.ValueTypeBool - f2 := property.SealedField{ - ID: id.PropertySchemaFieldID("fill"), - Type: "bool", - DatasetValue: nil, - PropertyValue: v2.ValueFromUnsafe(true), - } - fl2 := []*property.SealedField{} - fl2 = append(fl2, &f2) - item2 := property.SealedItem{ - Original: &iid, - Parent: nil, - SchemaGroup: id.PropertySchemaGroupID("default"), - LinkedDataset: nil, - Fields: fl2, - Groups: nil, - } - il = append(il, &item2) - v3 := property.ValueTypeString - f3 := property.SealedField{ - ID: id.PropertySchemaFieldID("fillColor"), - Type: "string", - DatasetValue: nil, - PropertyValue: v3.ValueFromUnsafe("#ff000000"), - } - fl3 := []*property.SealedField{} - fl3 = append(fl3, &f3) - item3 := property.SealedItem{ - Original: &iid, - Parent: nil, - SchemaGroup: id.PropertySchemaGroupID("default"), - LinkedDataset: nil, - Fields: fl3, - Groups: nil, - } - il = append(il, &item3) - v4 := property.ValueTypeBool - f4 := property.SealedField{ - ID: id.PropertySchemaFieldID("stroke"), - Type: "bool", - DatasetValue: nil, - PropertyValue: v4.ValueFromUnsafe(true), - } - fl4 := []*property.SealedField{} - fl4 = append(fl4, &f4) - item4 := property.SealedItem{ - Original: &iid, - Parent: nil, - SchemaGroup: id.PropertySchemaGroupID("default"), - LinkedDataset: nil, - Fields: fl4, - Groups: nil, - } - il = append(il, &item4) - v5 := property.ValueTypeString - f5 := property.SealedField{ - ID: id.PropertySchemaFieldID("strokeColor"), - Type: "string", - DatasetValue: nil, - PropertyValue: v5.ValueFromUnsafe("#ff554555"), - } - fl5 := []*property.SealedField{} - fl5 = append(fl5, &f5) - item5 := property.SealedItem{ - Original: &iid, - Parent: nil, - SchemaGroup: id.PropertySchemaGroupID("default"), - LinkedDataset: nil, - Fields: fl5, - Groups: nil, - } - il = append(il, &item5) - v6 := property.ValueTypeNumber - f6 := property.SealedField{ - ID: id.PropertySchemaFieldID("strokeWidth"), - Type: "number", - DatasetValue: nil, - PropertyValue: v6.ValueFromUnsafe(3), - } - fl6 := []*property.SealedField{} - fl6 = append(fl6, &f6) - item6 := property.SealedItem{ - Original: &iid, - Parent: nil, - SchemaGroup: id.PropertySchemaGroupID("default"), - LinkedDataset: nil, - Fields: fl6, - Groups: nil, - } - il = append(il, &item6) - sp := property.Sealed{ - Original: &pid, - Items: il, - } - l := merging.SealedLayerItem{ - SealedLayerCommon: merging.SealedLayerCommon{ - Merged: layer.Merged{ - Original: lid, - Parent: nil, - Name: "test", - Scene: sid, - Property: nil, - Infobox: nil, - PluginID: &id.OfficialPluginID, - ExtensionID: &ex, + { + name: "polygon", + target: &merging.SealedLayerItem{ + SealedLayerCommon: merging.SealedLayerCommon{ + Merged: layer.Merged{ + Original: lid, + Name: "test", + Scene: sid, + PluginID: &id.OfficialPluginID, + ExtensionID: id.PluginExtensionID("polygon").Ref(), + }, + Property: &property.Sealed{ + Original: id.NewPropertyID().Ref(), + Items: []*property.SealedItem{ + { + Original: &iid, + SchemaGroup: id.PropertySchemaGroupID("default"), + Fields: []*property.SealedField{ + { + ID: id.PropertySchemaFieldID("polygon"), + Type: property.ValueTypePolygon, + PropertyValue: property.ValueTypePolygon.ValueFrom( + property.Polygon{property.Coordinates{ + {Lat: 3.4, Lng: 5.34, Height: 100}, + {Lat: 45.4, Lng: 2.34, Height: 100}, + {Lat: 34.66, Lng: 654.34, Height: 100}, + }}, + ), + }, + { + ID: id.PropertySchemaFieldID("fill"), + Type: property.ValueTypeBool, + PropertyValue: property.ValueTypeBool.ValueFrom(true), + }, + { + ID: id.PropertySchemaFieldID("fillColor"), + Type: property.ValueTypeString, + PropertyValue: property.ValueTypeString.ValueFrom("#ff000000"), + }, + { + ID: id.PropertySchemaFieldID("stroke"), + Type: property.ValueTypeBool, + PropertyValue: property.ValueTypeBool.ValueFrom(true), + }, + { + ID: id.PropertySchemaFieldID("strokeColor"), + Type: property.ValueTypeString, + PropertyValue: property.ValueTypeString.ValueFrom("#ff554555"), + }, + { + ID: id.PropertySchemaFieldID("strokeWidth"), + Type: property.ValueTypeNumber, + PropertyValue: property.ValueTypeNumber.ValueFrom(3), + }, + }, + }, + }, + }, + }, + }, + want: []*czml.Feature{ + { + Id: lid.String(), + Name: "test", + Polygon: &czml.Polygon{ + Positions: czml.Position{CartographicDegrees: []float64{5.34, 3.4, 100, 2.34, 45.4, 100, 654.34, 34.66, 100}}, + Fill: true, + Material: &czml.Material{ + SolidColor: &czml.SolidColor{Color: &czml.Color{RGBA: []int64{255, 0, 0, 0}}}, + }, + Stroke: true, + StrokeColor: &czml.Color{RGBA: []int64{255, 85, 69, 85}}, + StrokeWidth: 3, + }, + }, }, - Property: &sp, - Infobox: nil, - }} - - reader, writer := io.Pipe() - en := NewCZMLEncoder(writer) - var err error - go func() { - defer func() { - _ = writer.Close() - }() - err = en.Encode(&l) - assert.NoError(t, err) - }() - - expected := []*czml.Feature{} - exPos := czml.Position{CartographicDegrees: []float64{5.34, 3.4, 100, 2.34, 45.4, 100, 654.34, 34.66, 100}} - exPoint := czml.Polygon{ - Positions: exPos, - Fill: true, - Material: &czml.Material{SolidColor: &czml.SolidColor{Color: &czml.Color{ - RGBA: []int64{255, 0, 0, 0}, - }}}, - Stroke: true, - StrokeColor: &czml.Color{ - RGBA: []int64{255, 85, 69, 85}, }, - StrokeWidth: 3, - } - exValue := czml.Feature{ - Id: "", - Name: "test", - Polygon: &exPoint, - } - expected = append(expected, &exValue) - reader2, writer2 := io.Pipe() - exEn := json.NewEncoder(writer2) - go func() { - defer func() { - _ = writer2.Close() - }() - err = exEn.Encode(expected) - assert.NoError(t, err) - }() - - assert.NoError(t, err) - buf := new(bytes.Buffer) - _, err = buf.ReadFrom(reader) - assert.NoError(t, err) - s := buf.String() - buf2 := new(bytes.Buffer) - _, err = buf2.ReadFrom(reader2) - assert.NoError(t, err) - s2 := buf2.String() - assert.Equal(t, s2, s) -} - -func TestEncodeCZMLPolyline(t *testing.T) { - lid := id.MustLayerID(id.New().String()) - sid := id.MustSceneID(id.New().String()) - pid := id.MustPropertyID(id.New().String()) - ex := id.PluginExtensionID("polyline") - iid := id.MustPropertyItemID(id.New().String()) - v1 := property.Coordinates{ - property.LatLngHeight{ - Lat: 3.4, - Lng: 5.34, - Height: 100, - }, property.LatLngHeight{ - Lat: 45.4, - Lng: 2.34, - Height: 100, - }, property.LatLngHeight{ - Lat: 34.66, - Lng: 654.34, - Height: 100, + { + name: "polyline", + target: &merging.SealedLayerItem{ + SealedLayerCommon: merging.SealedLayerCommon{ + Merged: layer.Merged{ + Original: lid, + Name: "test", + Scene: sid, + PluginID: &id.OfficialPluginID, + ExtensionID: id.PluginExtensionID("polyline").Ref(), + }, + Property: &property.Sealed{ + Original: id.NewPropertyID().Ref(), + Items: []*property.SealedItem{ + { + Original: &iid, + SchemaGroup: id.PropertySchemaGroupID("default"), + Fields: []*property.SealedField{ + { + ID: id.PropertySchemaFieldID("coordinates"), + Type: property.ValueTypeCoordinates, + PropertyValue: property.ValueTypeCoordinates.ValueFrom(property.Coordinates{ + {Lat: 3.4, Lng: 5.34, Height: 100}, + {Lat: 45.4, Lng: 2.34, Height: 100}, + {Lat: 34.66, Lng: 654.34, Height: 100}, + }), + }, + { + ID: id.PropertySchemaFieldID("strokeColor"), + Type: property.ValueTypeString, + PropertyValue: property.ValueTypeString.ValueFrom("#ff224222"), + }, + { + ID: id.PropertySchemaFieldID("strokeWidth"), + Type: property.ValueTypeNumber, + PropertyValue: property.ValueTypeNumber.ValueFrom(3), + }, + }, + }, + }, + }, + }, + }, + want: []*czml.Feature{ + { + Id: lid.String(), + Name: "test", + Polyline: &czml.Polyline{ + Positions: czml.Position{CartographicDegrees: []float64{5.34, 3.4, 100, 2.34, 45.4, 100, 654.34, 34.66, 100}}, + Material: &czml.Material{PolylineOutline: &czml.PolylineOutline{ + Color: &czml.Color{RGBA: []int64{255, 34, 66, 34}}, + }}, + Width: 3, + }, + }, + }, }, } - f1 := property.SealedField{ - ID: id.PropertySchemaFieldID("coordinates"), - Type: "coordinates", - DatasetValue: nil, - PropertyValue: v1.Value(), - } - fl1 := []*property.SealedField{} - fl1 = append(fl1, &f1) - item1 := property.SealedItem{ - Original: &iid, - Parent: nil, - SchemaGroup: id.PropertySchemaGroupID("default"), - LinkedDataset: nil, - Fields: fl1, - Groups: nil, - } - il := []*property.SealedItem{} - il = append(il, &item1) - - v2 := property.ValueTypeString - f2 := property.SealedField{ - ID: id.PropertySchemaFieldID("strokeColor"), - Type: "string", - DatasetValue: nil, - PropertyValue: v2.ValueFromUnsafe("#ff224222"), - } - fl2 := []*property.SealedField{} - fl2 = append(fl2, &f2) - item2 := property.SealedItem{ - Original: &iid, - Parent: nil, - SchemaGroup: id.PropertySchemaGroupID("default"), - LinkedDataset: nil, - Fields: fl2, - Groups: nil, - } - il = append(il, &item2) - v3 := property.ValueTypeNumber - f3 := property.SealedField{ - ID: id.PropertySchemaFieldID("strokeWidth"), - Type: "number", - DatasetValue: nil, - PropertyValue: v3.ValueFromUnsafe(3), - } - fl3 := []*property.SealedField{} - fl3 = append(fl3, &f3) - item3 := property.SealedItem{ - Original: &iid, - Parent: nil, - SchemaGroup: id.PropertySchemaGroupID("default"), - LinkedDataset: nil, - Fields: fl3, - Groups: nil, - } - il = append(il, &item3) - sp := property.Sealed{ - Original: &pid, - Items: il, - } - l := merging.SealedLayerItem{ - SealedLayerCommon: merging.SealedLayerCommon{ - Merged: layer.Merged{ - Original: lid, - Parent: nil, - Name: "test", - Scene: sid, - Property: nil, - Infobox: nil, - PluginID: &id.OfficialPluginID, - ExtensionID: &ex, - }, - Property: &sp, - Infobox: nil, - }} - reader, writer := io.Pipe() - en := NewCZMLEncoder(writer) - var err error - go func() { - defer func() { - _ = writer.Close() - }() - err = en.Encode(&l) - assert.NoError(t, err) - }() - - expected := []*czml.Feature{} - exPos := czml.Position{CartographicDegrees: []float64{5.34, 3.4, 100, 2.34, 45.4, 100, 654.34, 34.66, 100}} - exPolyline := czml.Polyline{ - Positions: exPos, - Material: &czml.Material{PolylineOutline: &czml.PolylineOutline{Color: &czml.Color{ - RGBA: []int64{255, 34, 66, 34}, - }}}, - Width: 3, - } - exValue := czml.Feature{ - Id: "", - Name: "test", - Polyline: &exPolyline, + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + expected, _ := json.Marshal(tt.want) + writer := bytes.Buffer{} + assert.NoError(t, NewCZMLEncoder(&writer).Encode(tt.target)) + assert.Equal(t, string(expected)+"\n", writer.String()) + }) } - expected = append(expected, &exValue) - reader2, writer2 := io.Pipe() - exEn := json.NewEncoder(writer2) - go func() { - defer func() { - _ = writer2.Close() - }() - err = exEn.Encode(expected) - assert.NoError(t, err) - }() - - assert.NoError(t, err) - buf := new(bytes.Buffer) - _, err = buf.ReadFrom(reader) - assert.NoError(t, err) - s := buf.String() - buf2 := new(bytes.Buffer) - _, err = buf2.ReadFrom(reader2) - assert.NoError(t, err) - s2 := buf2.String() - assert.Equal(t, s2, s) } diff --git a/pkg/layer/encoding/geojson.go b/pkg/layer/encoding/geojson.go index eff43c53..b65d4e70 100644 --- a/pkg/layer/encoding/geojson.go +++ b/pkg/layer/encoding/geojson.go @@ -40,126 +40,67 @@ func (e *GeoJSONEncoder) coordsToFloat(c property.Coordinates) [][]float64 { } func (e *GeoJSONEncoder) encodeSingleLayer(li *merging.SealedLayerItem) (*geojson.Feature, error) { - if li.PluginID == nil || !id.OfficialPluginID.Equal(*li.PluginID) { + if li == nil || li.PluginID == nil || !id.OfficialPluginID.Equal(*li.PluginID) { return nil, nil } - var ok bool - var geo *geojson.Geometry var res *geojson.Feature + switch li.ExtensionID.String() { case "marker": - latlng := property.LatLng{} - var height float64 - if f := li.Property.Field("location"); f != nil { - latlng, ok = f.PropertyValue.ValueLatLng() - if !ok { - dsll := f.DatasetValue.ValueLatLng() - if dsll != nil { - latlng = property.LatLng{ - Lat: dsll.Lat, - Lng: dsll.Lng, - } - } else { - return nil, errors.New("invalid value type") - } - } - if f := li.Property.Field("height"); f != nil { - height, ok = f.PropertyValue.ValueNumber() - if !ok { - dsHeight := f.DatasetValue.ValueNumber() - if dsHeight != nil { - height = *dsHeight - } else { - return nil, errors.New("invalid value type") - } - } - geo = geojson.NewPointGeometry([]float64{latlng.Lng, latlng.Lat, height}) - } else { - geo = geojson.NewPointGeometry([]float64{latlng.Lng, latlng.Lat}) - } - res = geojson.NewFeature(geo) - res.SetProperty("name", li.Name) + var coords []float64 + + if f := li.Property.Field("location").Value().ValueLatLng(); f != nil { + coords = []float64{(*f).Lng, (*f).Lat} + } else { + return nil, errors.New("invalid value type") } - if f := li.Property.Field("pointColor"); f != nil { - pointColor, ok := f.PropertyValue.ValueString() - if !ok { - return nil, errors.New("invalid value type") - } - if res != nil { - res.SetProperty("marker-color", pointColor) - } + + if height := li.Property.Field("height").Value().ValueNumber(); height != nil { + coords = append(coords, *height) } - case "polygon": - var polygon property.Polygon - if f := li.Property.Field("polygon"); f != nil { - polygon, ok = f.PropertyValue.ValuePolygon() - if !ok { - return nil, errors.New("invalid value type") - } - fl := e.polygonToFloat(polygon) - geo = geojson.NewPolygonGeometry(fl) - res = geojson.NewFeature(geo) - res.SetProperty("name", li.Name) + res = geojson.NewFeature(geojson.NewPointGeometry(coords)) + + if f := li.Property.Field("pointColor").Value().ValueString(); f != nil { + res.SetProperty("marker-color", *f) } - if f := li.Property.Field("fillColor"); f != nil { - fillColor, ok := f.PropertyValue.ValueString() - if !ok { - return nil, errors.New("invalid value type") - } - if res != nil { - res.SetProperty("fill", fillColor) - } + case "polygon": + if f := li.Property.Field("polygon").Value().ValuePolygon(); f != nil { + res = geojson.NewFeature(geojson.NewPolygonGeometry(e.polygonToFloat(*f))) + } else { + return nil, errors.New("invalid value type") } - if f := li.Property.Field("strokeColor"); f != nil { - strokeColor, ok := f.PropertyValue.ValueString() - if !ok { - return nil, errors.New("invalid value type") - } - if res != nil { - res.SetProperty("stroke", strokeColor) - } + + if f := li.Property.Field("fillColor").Value().ValueString(); f != nil { + res.SetProperty("fill", *f) } - if f := li.Property.Field("strokeWidth"); f != nil { - strokeWidth, ok := f.PropertyValue.ValueNumber() - if !ok { - return nil, errors.New("invalid value type") - } - if res != nil { - res.SetProperty("stroke-width", strokeWidth) - } + + if f := li.Property.Field("strokeColor").Value().ValueString(); f != nil { + res.SetProperty("stroke", *f) + } + + if f := li.Property.Field("strokeWidth").Value().ValueNumber(); f != nil { + res.SetProperty("stroke-width", *f) } case "polyline": - var polyline property.Coordinates - if f := li.Property.Field("coordinates"); f != nil { - polyline, ok = f.PropertyValue.ValueCoordinates() - if !ok { - return nil, errors.New("invalid value type") - } - fl := e.coordsToFloat(polyline) - geo = geojson.NewLineStringGeometry(fl) - res = geojson.NewFeature(geo) - res.SetProperty("name", li.Name) - } - if f := li.Property.Field("strokeColor"); f != nil { - strokeColor, ok := f.PropertyValue.ValueString() - if !ok { - return nil, errors.New("invalid value type") - } - if res != nil { - res.SetProperty("stroke", strokeColor) - } + if f := li.Property.Field("coordinates").Value().ValueCoordinates(); f != nil { + res = geojson.NewFeature(geojson.NewLineStringGeometry(e.coordsToFloat(*f))) + } else { + return nil, errors.New("invalid value type") } - if f := li.Property.Field("strokeWidth"); f != nil { - strokeWidth, ok := f.PropertyValue.ValueNumber() - if !ok { - return nil, errors.New("invalid value type") - } - if res != nil { - res.SetProperty("stroke-width", strokeWidth) - } + + if f := li.Property.Field("strokeColor").Value().ValueString(); f != nil { + res.SetProperty("stroke", *f) } + + if f := li.Property.Field("strokeWidth").Value().ValueNumber(); f != nil { + res.SetProperty("stroke-width", *f) + } + } + + if res != nil { + res.SetProperty("name", li.Name) } return res, nil } diff --git a/pkg/layer/encoding/geojson_test.go b/pkg/layer/encoding/geojson_test.go index e105a85c..d05a18d4 100644 --- a/pkg/layer/encoding/geojson_test.go +++ b/pkg/layer/encoding/geojson_test.go @@ -2,7 +2,6 @@ package encoding import ( "bytes" - "io" "testing" geojson "github.com/paulmach/go.geojson" @@ -15,416 +14,179 @@ import ( var _ Encoder = (*GeoJSONEncoder)(nil) -func TestPointEncodeGeoJSON(t *testing.T) { - lid := id.MustLayerID(id.New().String()) - sid := id.MustSceneID(id.New().String()) - pid := id.MustPropertyID(id.New().String()) - ex := id.PluginExtensionID("marker") - iid := id.MustPropertyItemID(id.New().String()) - v1 := property.LatLng{ - Lat: 4.4, - Lng: 53.4, - } - - f1 := property.SealedField{ - ID: id.PropertySchemaFieldID("location"), - Type: "latlng", - DatasetValue: nil, - PropertyValue: v1.Value(), - } - fl1 := []*property.SealedField{} - fl1 = append(fl1, &f1) - item1 := property.SealedItem{ - Original: &iid, - Parent: nil, - SchemaGroup: id.PropertySchemaGroupID("default"), - LinkedDataset: nil, - Fields: fl1, - Groups: nil, - } - il := []*property.SealedItem{} - il = append(il, &item1) - - v2 := property.ValueTypeString - f2 := property.SealedField{ - ID: id.PropertySchemaFieldID("pointColor"), - Type: "string", - DatasetValue: nil, - PropertyValue: v2.ValueFromUnsafe("#7fff00ff"), - } - fl2 := []*property.SealedField{} - fl2 = append(fl2, &f2) - item2 := property.SealedItem{ - Original: &iid, - Parent: nil, - SchemaGroup: id.PropertySchemaGroupID("default"), - LinkedDataset: nil, - Fields: fl2, - Groups: nil, - } - il = append(il, &item2) - v3 := property.ValueTypeNumber - f3 := property.SealedField{ - ID: id.PropertySchemaFieldID("height"), - Type: "number", - DatasetValue: nil, - PropertyValue: v3.ValueFromUnsafe(34), - } - fl3 := []*property.SealedField{} - fl3 = append(fl3, &f3) - item3 := property.SealedItem{ - Original: &iid, - Parent: nil, - SchemaGroup: id.PropertySchemaGroupID("default"), - LinkedDataset: nil, - Fields: fl3, - Groups: nil, - } - il = append(il, &item3) - sp := property.Sealed{ - Original: &pid, - Items: il, - } - l := merging.SealedLayerItem{ - SealedLayerCommon: merging.SealedLayerCommon{ - Merged: layer.Merged{ - Original: lid, - Parent: nil, - Name: "test", - Scene: sid, - Property: nil, - Infobox: nil, - PluginID: &id.OfficialPluginID, - ExtensionID: &ex, +func TestGeoJSONEncoder_Encode(t *testing.T) { + tests := []struct { + name string + target merging.SealedLayer + want func() *geojson.Feature + }{ + { + name: "point", + target: &merging.SealedLayerItem{ + SealedLayerCommon: merging.SealedLayerCommon{ + Merged: layer.Merged{ + Original: id.NewLayerID(), + Scene: id.NewSceneID(), + Name: "test", + PluginID: &id.OfficialPluginID, + ExtensionID: id.PluginExtensionID("marker").Ref(), + }, + Property: &property.Sealed{ + Original: id.NewPropertyID().Ref(), + Items: []*property.SealedItem{ + { + Original: id.NewPropertyItemID().Ref(), + SchemaGroup: id.PropertySchemaGroupID("default"), + Fields: []*property.SealedField{ + { + ID: id.PropertySchemaFieldID("location"), + Type: property.ValueTypeLatLng, + PropertyValue: property.ValueTypeLatLng.ValueFrom(property.LatLng{Lat: 4.4, Lng: 53.4}), + }, + { + ID: id.PropertySchemaFieldID("pointColor"), + Type: property.ValueTypeString, + PropertyValue: property.ValueTypeString.ValueFrom("#7fff00ff"), + }, + { + ID: id.PropertySchemaFieldID("height"), + Type: property.ValueTypeNumber, + PropertyValue: property.ValueTypeNumber.ValueFrom(34), + }, + }, + }, + }, + }, + }, + }, + want: func() *geojson.Feature { + f := geojson.NewFeature(geojson.NewPointGeometry([]float64{53.4, 4.4, 34})) + f.SetProperty("marker-color", "#7fff00ff") + f.SetProperty("name", "test") + return f }, - Property: &sp, - Infobox: nil, - }} - - reader, writer := io.Pipe() - en := NewGeoJSONEncoder(writer) - var err error - go func() { - defer func() { - _ = writer.Close() - }() - err = en.Encode(&l) - assert.NoError(t, err) - }() - - colorStr, _ := f2.PropertyValue.ValueString() - height, _ := f3.PropertyValue.ValueNumber() - expected := geojson.NewFeature(geojson.NewPointGeometry([]float64{v1.Lng, v1.Lat, height})) - expected.SetProperty("marker-color", colorStr) - expected.SetProperty("name", l.Name) - reader2, writer2 := io.Pipe() - var data []byte - data, err = expected.MarshalJSON() - go func() { - defer func() { - _ = writer2.Close() - }() - _, err = writer2.Write(data) - assert.NoError(t, err) - }() - - buf := new(bytes.Buffer) - _, err = buf.ReadFrom(reader) - assert.NoError(t, err) - s := buf.String() - buf2 := new(bytes.Buffer) - _, err = buf2.ReadFrom(reader2) - assert.NoError(t, err) - s2 := buf2.String() - assert.Equal(t, s2, s) -} - -func TestPolygonEncodeGeoJSON(t *testing.T) { - lid := id.MustLayerID(id.New().String()) - sid := id.MustSceneID(id.New().String()) - pid := id.MustPropertyID(id.New().String()) - ex := id.PluginExtensionID("polygon") - iid := id.MustPropertyItemID(id.New().String()) - vc := property.Coordinates{ - property.LatLngHeight{ - Lat: 3.4, - Lng: 5.34, - Height: 100, - }, property.LatLngHeight{ - Lat: 45.4, - Lng: 2.34, - Height: 100, - }, property.LatLngHeight{ - Lat: 34.66, - Lng: 654.34, - Height: 100, }, - } - v1 := property.Polygon{vc} - f1 := property.SealedField{ - ID: id.PropertySchemaFieldID("polygon"), - Type: "polygon", - DatasetValue: nil, - PropertyValue: v1.Value(), - } - fl1 := []*property.SealedField{} - fl1 = append(fl1, &f1) - item1 := property.SealedItem{ - Original: &iid, - Parent: nil, - SchemaGroup: id.PropertySchemaGroupID("default"), - LinkedDataset: nil, - Fields: fl1, - Groups: nil, - } - il := []*property.SealedItem{} - il = append(il, &item1) - v2 := property.ValueTypeString - f2 := property.SealedField{ - ID: id.PropertySchemaFieldID("fillColor"), - Type: "string", - DatasetValue: nil, - PropertyValue: v2.ValueFromUnsafe("#7c3b3b"), - } - fl2 := []*property.SealedField{} - fl2 = append(fl2, &f2) - item2 := property.SealedItem{ - Original: &iid, - Parent: nil, - SchemaGroup: id.PropertySchemaGroupID("default"), - LinkedDataset: nil, - Fields: fl2, - Groups: nil, - } - il = append(il, &item2) - v3 := property.ValueTypeString - f3 := property.SealedField{ - ID: id.PropertySchemaFieldID("strokeColor"), - Type: "string", - DatasetValue: nil, - PropertyValue: v3.ValueFromUnsafe("#ff3343"), - } - fl3 := []*property.SealedField{} - fl3 = append(fl3, &f3) - item3 := property.SealedItem{ - Original: &iid, - Parent: nil, - SchemaGroup: id.PropertySchemaGroupID("default"), - LinkedDataset: nil, - Fields: fl3, - Groups: nil, - } - il = append(il, &item3) - v4 := property.ValueTypeNumber - f4 := property.SealedField{ - ID: id.PropertySchemaFieldID("strokeWidth"), - Type: "number", - DatasetValue: nil, - PropertyValue: v4.ValueFromUnsafe(3), - } - fl4 := []*property.SealedField{} - fl4 = append(fl4, &f4) - item4 := property.SealedItem{ - Original: &iid, - Parent: nil, - SchemaGroup: id.PropertySchemaGroupID("default"), - LinkedDataset: nil, - Fields: fl4, - Groups: nil, - } - il = append(il, &item4) - sp := property.Sealed{ - Original: &pid, - Items: il, - } - l := merging.SealedLayerItem{ - SealedLayerCommon: merging.SealedLayerCommon{ - Merged: layer.Merged{ - Original: lid, - Parent: nil, - Name: "test", - Scene: sid, - Property: nil, - Infobox: nil, - PluginID: &id.OfficialPluginID, - ExtensionID: &ex, + { + name: "polygon", + target: &merging.SealedLayerItem{ + SealedLayerCommon: merging.SealedLayerCommon{ + Merged: layer.Merged{ + Original: id.NewLayerID(), + Scene: id.NewSceneID(), + Name: "test", + PluginID: &id.OfficialPluginID, + ExtensionID: id.PluginExtensionID("polygon").Ref(), + }, + Property: &property.Sealed{ + Original: id.NewPropertyID().Ref(), + Items: []*property.SealedItem{ + { + Original: id.NewPropertyItemID().Ref(), + SchemaGroup: id.PropertySchemaGroupID("default"), + Fields: []*property.SealedField{ + { + ID: id.PropertySchemaFieldID("polygon"), + Type: "polygon", + PropertyValue: property.ValueTypePolygon.ValueFrom(property.Polygon{property.Coordinates{ + {Lat: 3.4, Lng: 5.34, Height: 100}, + {Lat: 45.4, Lng: 2.34, Height: 100}, + {Lat: 34.66, Lng: 654.34, Height: 100}, + }}), + }, + { + ID: id.PropertySchemaFieldID("fillColor"), + Type: property.ValueTypeString, + PropertyValue: property.ValueTypeString.ValueFrom("#7c3b3b"), + }, + { + ID: id.PropertySchemaFieldID("strokeColor"), + Type: property.ValueTypeString, + PropertyValue: property.ValueTypeString.ValueFrom("#ff3343"), + }, + { + ID: id.PropertySchemaFieldID("strokeWidth"), + Type: property.ValueTypeNumber, + PropertyValue: property.ValueTypeNumber.ValueFrom(3), + }, + { + ID: id.PropertySchemaFieldID("strokeWidth"), + Type: property.ValueTypeNumber, + PropertyValue: property.ValueTypeNumber.ValueFrom(3), + }, + }, + }, + }, + }, + }, + }, + want: func() *geojson.Feature { + expected := geojson.NewFeature(geojson.NewPolygonGeometry([][][]float64{{{5.34, 3.4, 100}, {2.34, 45.4, 100}, {654.34, 34.66, 100}}})) + expected.SetProperty("name", "test") + expected.SetProperty("fill", "#7c3b3b") + expected.SetProperty("stroke", "#ff3343") + expected.SetProperty("stroke-width", 3) + return expected + }, + }, + { + name: "polyline", + target: &merging.SealedLayerItem{ + SealedLayerCommon: merging.SealedLayerCommon{ + Merged: layer.Merged{ + Original: id.NewLayerID(), + Scene: id.NewSceneID(), + Name: "test", + PluginID: &id.OfficialPluginID, + ExtensionID: id.PluginExtensionID("polyline").Ref(), + }, + Property: &property.Sealed{ + Original: id.NewPropertyID().Ref(), + Items: []*property.SealedItem{ + { + Original: id.NewPropertyItemID().Ref(), + SchemaGroup: id.PropertySchemaGroupID("default"), + Fields: []*property.SealedField{ + { + ID: id.PropertySchemaFieldID("coordinates"), + Type: property.ValueTypeCoordinates, + PropertyValue: property.ValueTypeCoordinates.ValueFrom(property.Coordinates{ + {Lat: 3.4, Lng: 5.34, Height: 100}, + {Lat: 45.4, Lng: 2.34, Height: 100}, + {Lat: 34.66, Lng: 654.34, Height: 100}, + }), + }, + { + ID: id.PropertySchemaFieldID("strokeColor"), + Type: property.ValueTypeString, + PropertyValue: property.ValueTypeString.ValueFrom("#ff3343"), + }, + { + ID: id.PropertySchemaFieldID("strokeWidth"), + Type: property.ValueTypeNumber, + PropertyValue: property.ValueTypeNumber.ValueFrom(3), + }, + }, + }, + }, + }, + }, + }, + want: func() *geojson.Feature { + expected := geojson.NewFeature(geojson.NewLineStringGeometry([][]float64{{5.34, 3.4, 100}, {2.34, 45.4, 100}, {654.34, 34.66, 100}})) + expected.SetProperty("name", "test") + expected.SetProperty("stroke", "#ff3343") + expected.SetProperty("stroke-width", 3) + return expected }, - Property: &sp, - Infobox: nil, - }} - - reader, writer := io.Pipe() - en := NewGeoJSONEncoder(writer) - var err error - go func() { - defer func() { - _ = writer.Close() - }() - err = en.Encode(&l) - assert.NoError(t, err) - }() - - fillStr, _ := f2.PropertyValue.ValueString() - strokeStr, _ := f3.PropertyValue.ValueString() - width, _ := f4.PropertyValue.ValueNumber() - expected := geojson.NewFeature(geojson.NewPolygonGeometry([][][]float64{{{5.34, 3.4, 100}, {2.34, 45.4, 100}, {654.34, 34.66, 100}}})) - expected.SetProperty("name", l.Name) - expected.SetProperty("fill", fillStr) - expected.SetProperty("stroke", strokeStr) - expected.SetProperty("stroke-width", width) - reader2, writer2 := io.Pipe() - var data []byte - data, err = expected.MarshalJSON() - go func() { - defer func() { - _ = writer2.Close() - }() - _, err = writer2.Write(data) - }() - assert.NoError(t, err) - - buf := new(bytes.Buffer) - _, err = buf.ReadFrom(reader) - assert.NoError(t, err) - s := buf.String() - buf2 := new(bytes.Buffer) - _, err = buf2.ReadFrom(reader2) - assert.NoError(t, err) - s2 := buf2.String() - assert.Equal(t, s2, s) -} - -func TestPolylineEncodeGeoJSON(t *testing.T) { - lid := id.MustLayerID(id.New().String()) - sid := id.MustSceneID(id.New().String()) - pid := id.MustPropertyID(id.New().String()) - ex := id.PluginExtensionID("polyline") - iid := id.MustPropertyItemID(id.New().String()) - v1 := property.Coordinates{ - property.LatLngHeight{ - Lat: 3.4, - Lng: 5.34, - Height: 100, - }, property.LatLngHeight{ - Lat: 45.4, - Lng: 2.34, - Height: 100, - }, property.LatLngHeight{ - Lat: 34.66, - Lng: 654.34, - Height: 100, }, } - f1 := property.SealedField{ - ID: id.PropertySchemaFieldID("coordinates"), - Type: "coordinates", - DatasetValue: nil, - PropertyValue: v1.Value(), - } - fl1 := []*property.SealedField{} - fl1 = append(fl1, &f1) - item1 := property.SealedItem{ - Original: &iid, - Parent: nil, - SchemaGroup: id.PropertySchemaGroupID("default"), - LinkedDataset: nil, - Fields: fl1, - Groups: nil, - } - il := []*property.SealedItem{} - il = append(il, &item1) - v2 := property.ValueTypeString - f2 := property.SealedField{ - ID: id.PropertySchemaFieldID("strokeColor"), - Type: "string", - DatasetValue: nil, - PropertyValue: v2.ValueFromUnsafe("#ff3343"), - } - fl2 := []*property.SealedField{} - fl2 = append(fl2, &f2) - item2 := property.SealedItem{ - Original: &iid, - Parent: nil, - SchemaGroup: id.PropertySchemaGroupID("default"), - LinkedDataset: nil, - Fields: fl2, - Groups: nil, - } - il = append(il, &item2) - v3 := property.ValueTypeNumber - f3 := property.SealedField{ - ID: id.PropertySchemaFieldID("strokeWidth"), - Type: "number", - DatasetValue: nil, - PropertyValue: v3.ValueFromUnsafe(3), - } - fl3 := []*property.SealedField{} - fl3 = append(fl3, &f3) - item3 := property.SealedItem{ - Original: &iid, - Parent: nil, - SchemaGroup: id.PropertySchemaGroupID("default"), - LinkedDataset: nil, - Fields: fl3, - Groups: nil, + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + expected, _ := tt.want().MarshalJSON() + writer := bytes.Buffer{} + assert.NoError(t, NewGeoJSONEncoder(&writer).Encode(tt.target)) + assert.Equal(t, string(expected), writer.String()) + }) } - il = append(il, &item3) - sp := property.Sealed{ - Original: &pid, - Items: il, - } - l := merging.SealedLayerItem{ - SealedLayerCommon: merging.SealedLayerCommon{ - Merged: layer.Merged{ - Original: lid, - Parent: nil, - Name: "test", - Scene: sid, - Property: nil, - Infobox: nil, - PluginID: &id.OfficialPluginID, - ExtensionID: &ex, - }, - Property: &sp, - Infobox: nil, - }} - - reader, writer := io.Pipe() - en := NewGeoJSONEncoder(writer) - var err error - go func() { - defer func() { - _ = writer.Close() - }() - err = en.Encode(&l) - assert.NoError(t, err) - }() - - strokeStr, _ := f2.PropertyValue.ValueString() - width, _ := f3.PropertyValue.ValueNumber() - expected := geojson.NewFeature(geojson.NewLineStringGeometry([][]float64{{5.34, 3.4, 100}, {2.34, 45.4, 100}, {654.34, 34.66, 100}})) - expected.SetProperty("name", l.Name) - expected.SetProperty("stroke", strokeStr) - expected.SetProperty("stroke-width", width) - reader2, writer2 := io.Pipe() - var data []byte - data, err = expected.MarshalJSON() - go func() { - defer func() { - _ = writer2.Close() - }() - _, err = writer2.Write(data) - assert.NoError(t, err) - }() - - buf := new(bytes.Buffer) - _, err = buf.ReadFrom(reader) - assert.NoError(t, err) - s := buf.String() - buf2 := new(bytes.Buffer) - _, err = buf2.ReadFrom(reader2) - assert.NoError(t, err) - s2 := buf2.String() - assert.Equal(t, s2, s) } diff --git a/pkg/layer/encoding/kml.go b/pkg/layer/encoding/kml.go index 463dd4e9..750c5cb2 100644 --- a/pkg/layer/encoding/kml.go +++ b/pkg/layer/encoding/kml.go @@ -2,15 +2,10 @@ package encoding import ( "errors" - "image/color" "io" - "math/rand" - "net/url" - "strings" "github.com/reearth/reearth-backend/pkg/id" "github.com/reearth/reearth-backend/pkg/layer/merging" - "github.com/reearth/reearth-backend/pkg/property" kml "github.com/twpayne/go-kml" ) @@ -26,22 +21,8 @@ func NewKMLEncoder(w io.Writer) *KMLEncoder { } // generates a composite string of layer name and id to be used as style tag id -func (e *KMLEncoder) generateStyleId(id string, name string) (string, error) { - if len(id) > 0 { - subid := id[len(id)-5:] - trimmedName := "" - if len(name) > 0 { - trimmedName = strings.Join(strings.Fields(name), "") + "_" - - } - b := make([]byte, 8) - if _, err := rand.Read(b); err != nil { - return "", err - } - return trimmedName + subid, nil - } - - return "", nil +func generateKMLStyleId(id string) string { + return id + "_style" } func (e *KMLEncoder) getName(str string) *kml.SimpleElement { @@ -49,132 +30,79 @@ func (e *KMLEncoder) getName(str string) *kml.SimpleElement { } // encodes style features and return style kml element and used id -func (e *KMLEncoder) encodePointStyle(li *merging.SealedLayerItem) (*kml.SharedElement, string, error) { - var image *url.URL - var styleId string - var err error - var ok bool - var imageSize float64 - var pointColor color.Color - if f := li.Property.Field("image"); f != nil { - if f.PropertyValue != nil { - image, ok = f.PropertyValue.ValueURL() - if !ok { - return nil, "", errors.New("invalid value type") - } - if len(image.String()) == 0 { - return nil, "", errors.New("empty URL") - } - } +func (e *KMLEncoder) encodePointStyle(li *merging.SealedLayerItem) (*kml.SharedElement, string) { + added := false + styleId := generateKMLStyleId(li.Original.String()) + iconStyle := kml.IconStyle() + + if f := li.Property.Field("image").Value().ValueURL(); f != nil { + iconStyle.Add(kml.Icon(kml.Href(f.String()))) + added = true } - if f := li.Property.Field("imageSize"); f != nil { - imageSize, ok = f.PropertyValue.ValueNumber() - if !ok { - return nil, "", errors.New("invalid value type") - } + + if f := li.Property.Field("imageSize").Value().ValueNumber(); f != nil { + iconStyle.Add(kml.Scale(*f)) + added = true } - if f := li.Property.Field("pointColor"); f != nil { - colorStr, ok := f.PropertyValue.ValueString() - if !ok { - return nil, "", errors.New("invalid value type") - } - pointColor, err = getColor(colorStr) - if err != nil { - return nil, "", err + + if f := li.Property.Field("pointColor").Value().ValueString(); f != nil { + if c := getColor(*f); c != nil { + iconStyle.Add(kml.Color(c)) + added = true } } - styleId, err = e.generateStyleId(li.Original.String(), li.Name) - if err != nil { - return nil, "", err - } - if imageSize != 0 || pointColor != nil || (image != nil && len(image.String()) > 0) { - iconStyle := kml.IconStyle() - if imageSize != 0 { - iconStyle.Add(kml.Scale(imageSize)) - } - if pointColor != nil { - iconStyle.Add(kml.Color(pointColor)) - } - if image != nil { - iconStyle.Add(kml.Icon( - kml.Href(image.String()))) - } - return kml.SharedStyle(styleId, iconStyle), styleId, nil + + if !added { + return nil, "" } - return nil, "", nil + + return kml.SharedStyle(styleId, iconStyle), styleId } -func (e *KMLEncoder) encodePolygonStyle(li *merging.SealedLayerItem) (*kml.SharedElement, string, error) { - var styleId string - var fill, stroke bool - var fillColor, strokeColor color.Color - var strokeWidth float64 - var err error - var ok bool - if f := li.Property.Field("fill"); f != nil { - fill, ok = f.PropertyValue.ValueBool() - if !ok { - return nil, "", errors.New("invalid value type") - } - } - if f := li.Property.Field("stroke"); f != nil { - stroke, ok = f.PropertyValue.ValueBool() - if !ok { - return nil, "", errors.New("invalid value type") - } - } +func (e *KMLEncoder) encodePolygonStyle(li *merging.SealedLayerItem) (*kml.SharedElement, string) { + styleId := generateKMLStyleId(li.Original.String()) - if f := li.Property.Field("fillColor"); f != nil { - colorStr, ok := f.PropertyValue.ValueString() - if !ok { - return nil, "", errors.New("invalid value type") - } - fillColor, err = getColor(colorStr) - if err != nil { - return nil, "", err - } - } - if f := li.Property.Field("strokeColor"); f != nil { - colorStr, ok := f.PropertyValue.ValueString() - if !ok { - return nil, "", errors.New("invalid value type") - } - strokeColor, err = getColor(colorStr) - if err != nil { - return nil, "", err - } - } - if f := li.Property.Field("strokeWidth"); f != nil { - strokeWidth, ok = f.PropertyValue.ValueNumber() - if !ok { - return nil, "", errors.New("invalid value type") + var polyStyle, lineStyle *kml.CompoundElement + + if f := li.Property.Field("fill").Value().ValueBool(); f != nil { + if polyStyle == nil { + polyStyle = kml.PolyStyle() } + polyStyle.Add(kml.Fill(*f)) } - styleId, err = e.generateStyleId(li.Original.String(), li.Name) - if err != nil { - return nil, "", err - } - polyStyle := kml.PolyStyle() - lineStyle := kml.LineStyle() - if fill || fillColor != nil { - if fill { - polyStyle.Add(kml.Fill(fill)) - } - if fillColor != nil { + + if f := li.Property.Field("fillColor").Value().ValueString(); f != nil { + if fillColor := getColor(*f); fillColor != nil { + if polyStyle == nil { + polyStyle = kml.PolyStyle() + } polyStyle.Add(kml.Color(fillColor)) } } - if stroke || strokeColor != nil || strokeWidth != 0 { - if stroke { - lineStyle.Add(kml.Outline(stroke)) + + if f := li.Property.Field("stroke").Value().ValueBool(); f != nil { + if lineStyle == nil { + lineStyle = kml.LineStyle() } - if strokeColor != nil { + lineStyle.Add(kml.Outline(*f)) + } + + if f := li.Property.Field("strokeColor").Value().ValueString(); f != nil { + if strokeColor := getColor(*f); lineStyle != nil { + if lineStyle == nil { + lineStyle = kml.LineStyle() + } lineStyle.Add(kml.Color(strokeColor)) } - if strokeWidth != 0 { - lineStyle.Add(kml.Width(strokeWidth)) + } + + if f := li.Property.Field("strokeWidth").Value().ValueNumber(); f != nil { + if lineStyle == nil { + lineStyle = kml.LineStyle() } + lineStyle.Add(kml.Width(*f)) } + style := kml.SharedStyle(styleId) if polyStyle != nil { style.Add(polyStyle) @@ -182,53 +110,37 @@ func (e *KMLEncoder) encodePolygonStyle(li *merging.SealedLayerItem) (*kml.Share if lineStyle != nil { style.Add(lineStyle) } - return style, styleId, nil + return style, styleId } -func (e *KMLEncoder) encodePolylineStyle(li *merging.SealedLayerItem) (*kml.SharedElement, string, error) { - var styleId string - var strokeColor color.Color - var strokeWidth float64 - var err error - var ok bool - - if f := li.Property.Field("strokeColor"); f != nil { - colorStr, ok := f.PropertyValue.ValueString() - if !ok { - return nil, "", errors.New("invalid value type") - } - strokeColor, err = getColor(colorStr) - if err != nil { - return nil, "", err - } - } - if f := li.Property.Field("strokeWidth"); f != nil { - strokeWidth, ok = f.PropertyValue.ValueNumber() - if !ok { - return nil, "", errors.New("invalid value type") - } - } - styleId, err = e.generateStyleId(li.Original.String(), li.Name) - if err != nil { - return nil, "", err - } - lineStyle := kml.LineStyle() - if strokeColor != nil || strokeWidth != 0 { - if strokeColor != nil { +func (e *KMLEncoder) encodePolylineStyle(li *merging.SealedLayerItem) (*kml.SharedElement, string) { + styleId := generateKMLStyleId(li.Original.String()) + style := kml.SharedStyle(styleId) + var lineStyle *kml.CompoundElement + + if f := li.Property.Field("strokeColor").Value().ValueString(); f != nil { + if strokeColor := getColor(*f); strokeColor != nil { + if lineStyle == nil { + lineStyle = kml.LineStyle() + } lineStyle.Add(kml.Color(strokeColor)) } - if strokeWidth != 0 { - lineStyle.Add(kml.Width(strokeWidth)) + } + + if f := li.Property.Field("strokeWidth").Value().ValueNumber(); f != nil { + if lineStyle == nil { + lineStyle = kml.LineStyle() } + lineStyle.Add(kml.Width(*f)) } - style := kml.SharedStyle(styleId) + if lineStyle != nil { style.Add(lineStyle) } - return style, styleId, nil + return style, styleId } -func (e *KMLEncoder) encodeStyle(li *merging.SealedLayerItem) (*kml.SharedElement, string, error) { +func (e *KMLEncoder) encodeStyle(li *merging.SealedLayerItem) (*kml.SharedElement, string) { switch li.ExtensionID.String() { case "marker": return e.encodePointStyle(li) @@ -237,7 +149,7 @@ func (e *KMLEncoder) encodeStyle(li *merging.SealedLayerItem) (*kml.SharedElemen case "polyline": return e.encodePolylineStyle(li) } - return nil, "", nil + return nil, "" } // encodes non style layer features @@ -247,102 +159,65 @@ func (e *KMLEncoder) encodeLayerTag(li *merging.SealedLayerItem) (*kml.CompoundE } var layerTag *kml.CompoundElement - var ok bool - name := e.getName(li.Name) + switch li.ExtensionID.String() { case "marker": - layerTag = kml.Point() - latlng := property.LatLng{} - var height float64 - if f := li.Property.Field("location"); f != nil { - latlng, ok = f.PropertyValue.ValueLatLng() - if !ok { - dsll := f.DatasetValue.ValueLatLng() - if dsll != nil { - latlng = property.LatLng{ - Lat: dsll.Lat, - Lng: dsll.Lng, - } - } else { - return nil, errors.New("invalid value type") - } - } + c := kml.Coordinate{} + + if f := li.Property.Field("location").Value().ValueLatLng(); f != nil { + c.Lat = (*f).Lat + c.Lon = (*f).Lng + } else { + return nil, errors.New("invalid value type") } - if f := li.Property.Field("height"); f != nil { - height, ok = f.PropertyValue.ValueNumber() - if !ok { - dsHeight := f.DatasetValue.ValueNumber() - if dsHeight != nil { - height = *dsHeight - } else { - return nil, errors.New("invalid value type") - } - } + + if f := li.Property.Field("height").Value().ValueNumber(); f != nil { + c.Alt = *f } - layerTag = layerTag.Add( - kml.Coordinates( - kml.Coordinate{ - Lon: latlng.Lng, - Lat: latlng.Lat, - Alt: height, - })) + + layerTag = kml.Point().Add(kml.Coordinates(c)) case "polygon": layerTag = kml.Polygon() - polygon := property.Polygon{} - if f := li.Property.Field("polygon"); f != nil { - polygon, ok = f.PropertyValue.ValuePolygon() - if !ok { - return nil, errors.New("invalid value type") - } - } - // by default, first polygon coords set is for outer boundaries... the second is for inner - if len(polygon) > 0 { - var coords []kml.Coordinate - for _, c := range polygon[0] { - coords = append(coords, kml.Coordinate{ - Lon: c.Lng, - Lat: c.Lat, - Alt: c.Height, - }) - } - layerTag = layerTag.Add(kml.OuterBoundaryIs(kml.LinearRing(kml.Coordinates(coords...)))) - } - //encode inner boundaries - if len(polygon) == 2 { - var coords []kml.Coordinate - for _, c := range polygon[1] { - coords = append(coords, kml.Coordinate{ - Lon: c.Lng, - Lat: c.Lat, - Alt: c.Height, - }) + // polygon := property.Polygon{} + if f := li.Property.Field("polygon").Value().ValuePolygon(); f != nil && len(*f) > 0 { + // by default, first polygon coords set is for outer boundaries... the second is for inner + for i, r := range *f { + var coords []kml.Coordinate + for _, c := range r { + coords = append(coords, kml.Coordinate{ + Lon: c.Lng, + Lat: c.Lat, + Alt: c.Height, + }) + } + if i == 0 { + layerTag = layerTag.Add(kml.OuterBoundaryIs(kml.LinearRing(kml.Coordinates(coords...)))) + } else { + layerTag = layerTag.Add(kml.InnerBoundaryIs(kml.LinearRing(kml.Coordinates(coords...)))) + } } - layerTag.Add(kml.InnerBoundaryIs(kml.LinearRing(kml.Coordinates(coords...)))) + } else { + return nil, errors.New("invalid value type") } case "polyline": - layerTag = kml.LineString() - polyline := property.Coordinates{} - if f := li.Property.Field("coordinates"); f != nil { - polyline, ok = f.PropertyValue.ValueCoordinates() - if !ok { - return nil, errors.New("invalid value type") - } - } - if len(polyline) > 0 { - var coords []kml.Coordinate - for _, c := range polyline { + if f := li.Property.Field("coordinates").Value().ValueCoordinates(); f != nil && len(*f) > 0 { + coords := make([]kml.Coordinate, 0, len(*f)) + for _, c := range *f { coords = append(coords, kml.Coordinate{ Lon: c.Lng, Lat: c.Lat, Alt: c.Height, }) } - layerTag = layerTag.Add(kml.Coordinates(coords...)) + layerTag = kml.LineString().Add(kml.Coordinates(coords...)) + } else { + return nil, errors.New("invalid value type") } } + placemark := kml.Placemark() if len(li.Name) != 0 { - placemark.Add(name) + placemark.Add(e.getName(li.Name)) } placemark = placemark.Add(layerTag) @@ -358,31 +233,22 @@ func (e *KMLEncoder) encodeLayerGroup(li *merging.SealedLayerGroup, parent *kml. for _, ch := range li.Children { if g, ok := ch.(*merging.SealedLayerGroup); ok { folder := kml.Folder() - folder, err := e.encodeLayerGroup(g, folder) if err != nil { return nil, err } - - parent.Add(folder) + parent = parent.Add(folder) } else if i, ok := ch.(*merging.SealedLayerItem); ok { placemark, err := e.encodeLayerTag(i) if err != nil { return nil, err - } - if placemark == nil { + } else if placemark == nil { return nil, nil } - - style, styleId, err := e.encodeStyle(i) - if err != nil { - return nil, err - } - if style != nil { + if style, styleId := e.encodeStyle(i); style != nil { e.styles = append(e.styles, style) placemark.Add(kml.StyleURL("#" + styleId)) } - parent = parent.Add(placemark) } } @@ -390,19 +256,16 @@ func (e *KMLEncoder) encodeLayerGroup(li *merging.SealedLayerGroup, parent *kml. return parent, nil } -func (e *KMLEncoder) Encode(layer merging.SealedLayer) error { +func (e *KMLEncoder) Encode(layer merging.SealedLayer) (err error) { var res *kml.CompoundElement - var err error if i, ok := layer.(*merging.SealedLayerItem); ok { - style, styleId, err := e.encodeStyle(i) - if err != nil { - return err - } + style, styleId := e.encodeStyle(i) l, err := e.encodeLayerTag(i) if err != nil { return err } + if style != nil { res = kml.KML(style) res = res.Add(l) @@ -412,22 +275,23 @@ func (e *KMLEncoder) Encode(layer merging.SealedLayer) error { } } else if g, ok := layer.(*merging.SealedLayerGroup); ok { doc := kml.Document() - doc, err := e.encodeLayerGroup(g, doc) if err != nil { return err } + if len(e.styles) > 0 { for _, s := range e.styles { doc.Add(s) } } + res = kml.KML(doc) } - err = res.WriteIndent(e.writer, "", " ") - if err != nil { + if err := res.WriteIndent(e.writer, "", " "); err != nil { return err } + return nil } diff --git a/pkg/layer/encoding/kml_test.go b/pkg/layer/encoding/kml_test.go index 3ab47457..3db9f93b 100644 --- a/pkg/layer/encoding/kml_test.go +++ b/pkg/layer/encoding/kml_test.go @@ -2,7 +2,6 @@ package encoding import ( "bytes" - "io" "testing" "github.com/reearth/reearth-backend/pkg/id" @@ -15,493 +14,246 @@ import ( var _ Encoder = (*KMLEncoder)(nil) -func TestEncodeKMLMarker(t *testing.T) { - lid := id.MustLayerID(id.New().String()) - sid := id.MustSceneID(id.New().String()) - pid := id.MustPropertyID(id.New().String()) - ex := id.PluginExtensionID("marker") - iid := id.MustPropertyItemID(id.New().String()) - v1 := property.LatLng{ - Lat: 4.4, - Lng: 53.4, - } +func TestKMLEncoder_Encode(t *testing.T) { + lid := id.MustLayerID("01fmph48ykj1nd82r8e4znh6a6") - f1 := property.SealedField{ - ID: id.PropertySchemaFieldID("location"), - Type: "latlng", - DatasetValue: nil, - PropertyValue: v1.Value(), - } - fl1 := []*property.SealedField{} - fl1 = append(fl1, &f1) - item1 := property.SealedItem{ - Original: &iid, - Parent: nil, - SchemaGroup: id.PropertySchemaGroupID("default"), - LinkedDataset: nil, - Fields: fl1, - Groups: nil, - } - il := []*property.SealedItem{} - il = append(il, &item1) - v2 := property.ValueTypeNumber - f2 := property.SealedField{ - ID: id.PropertySchemaFieldID("imageSize"), - Type: "number", - DatasetValue: nil, - PropertyValue: v2.ValueFromUnsafe(4), - } - fl2 := []*property.SealedField{} - fl2 = append(fl2, &f2) - item2 := property.SealedItem{ - Original: &iid, - Parent: nil, - SchemaGroup: id.PropertySchemaGroupID("default"), - LinkedDataset: nil, - Fields: fl2, - Groups: nil, - } - il = append(il, &item2) - v3 := property.ValueTypeURL - f3 := property.SealedField{ - ID: id.PropertySchemaFieldID("image"), - Type: "url", - DatasetValue: nil, - PropertyValue: v3.ValueFromUnsafe("http://maps.google.com/mapfiles/kml/pal4/icon28.png"), - } - fl3 := []*property.SealedField{} - fl3 = append(fl3, &f3) - item3 := property.SealedItem{ - Original: &iid, - Parent: nil, - SchemaGroup: id.PropertySchemaGroupID("default"), - LinkedDataset: nil, - Fields: fl3, - Groups: nil, - } - il = append(il, &item3) - v4 := property.ValueTypeString - f4 := property.SealedField{ - ID: id.PropertySchemaFieldID("pointColor"), - Type: "string", - DatasetValue: nil, - PropertyValue: v4.ValueFromUnsafe("#7fff00ff"), - } - fl4 := []*property.SealedField{} - fl4 = append(fl4, &f4) - item4 := property.SealedItem{ - Original: &iid, - Parent: nil, - SchemaGroup: id.PropertySchemaGroupID("default"), - LinkedDataset: nil, - Fields: fl4, - Groups: nil, - } - il = append(il, &item4) - sp := property.Sealed{ - Original: &pid, - Items: il, - } - l := merging.SealedLayerItem{ - SealedLayerCommon: merging.SealedLayerCommon{ - Merged: layer.Merged{ - Original: lid, - Parent: nil, - Name: "test", - Scene: sid, - Property: nil, - Infobox: nil, - PluginID: &id.OfficialPluginID, - ExtensionID: &ex, + tests := []struct { + name string + target merging.SealedLayer + want func() *kml.CompoundElement + }{ + { + name: "marker", + target: &merging.SealedLayerItem{ + SealedLayerCommon: merging.SealedLayerCommon{ + Merged: layer.Merged{ + Original: lid, + Scene: id.NewSceneID(), + Name: "test", + PluginID: &id.OfficialPluginID, + ExtensionID: id.PluginExtensionID("marker").Ref(), + }, + Property: &property.Sealed{ + Original: id.NewPropertyID().Ref(), + Items: []*property.SealedItem{ + { + Original: id.NewPropertyItemID().Ref(), + SchemaGroup: id.PropertySchemaGroupID("default"), + Fields: []*property.SealedField{ + { + ID: id.PropertySchemaFieldID("location"), + Type: property.ValueTypeLatLng, + PropertyValue: property.ValueTypeLatLng.ValueFrom(property.LatLng{Lat: 4.4, Lng: 53.4}), + }, + { + ID: id.PropertySchemaFieldID("height"), + Type: property.ValueTypeNumber, + PropertyValue: property.ValueTypeNumber.ValueFrom(100), + }, + { + ID: id.PropertySchemaFieldID("imageSize"), + Type: property.ValueTypeNumber, + PropertyValue: property.ValueTypeNumber.ValueFrom(4), + }, + { + ID: id.PropertySchemaFieldID("image"), + Type: property.ValueTypeURL, + PropertyValue: property.ValueTypeURL.ValueFrom("http://maps.google.com/mapfiles/kml/pal4/icon28.png"), + }, + { + ID: id.PropertySchemaFieldID("pointColor"), + Type: property.ValueTypeString, + PropertyValue: property.ValueTypeString.ValueFrom("#7fff00ff"), + }, + }, + }, + }, + }, + }, + }, + want: func() *kml.CompoundElement { + k := kml.KML( + kml.SharedStyle( + "01fmph48ykj1nd82r8e4znh6a6_style", + kml.IconStyle( + kml.Icon(kml.Href("http://maps.google.com/mapfiles/kml/pal4/icon28.png")), + kml.Scale(4), + kml.Color(getColor("#7fff00ff")), + ), + ), + ) + k = k.Add( + kml.Placemark( + kml.Name("test"), + kml.Point(kml.Coordinates(kml.Coordinate{Lon: 53.4, Lat: 4.4, Alt: 100})), + kml.StyleURL("#01fmph48ykj1nd82r8e4znh6a6_style"), + ), + ) + return k }, - Property: &sp, - Infobox: nil, - }} - - reader, writer := io.Pipe() - en := NewKMLEncoder(writer) - var err error - go func() { - defer func() { - _ = writer.Close() - }() - err = en.Encode(&l) - assert.NoError(t, err) - }() - - colorStr, _ := f4.PropertyValue.ValueString() - sizeFloat, _ := f2.PropertyValue.ValueNumber() - urlValue, _ := f3.PropertyValue.ValueURL() - b, _ := getColor(colorStr) - stid, err := en.generateStyleId(l.Original.String(), l.Name) - assert.NoError(t, err) - expected := kml.KML(kml.SharedStyle(stid, kml.IconStyle( - kml.Scale(sizeFloat), - kml.Color(b), - kml.Icon( - kml.Href(urlValue.String()))))) - expected = expected.Add(kml.Placemark(kml.Name("test"), - kml.Point(kml.Coordinates(kml.Coordinate{ - Lon: v1.Lng, - Lat: v1.Lat, - })), - kml.StyleURL("#"+stid))) - reader2, writer2 := io.Pipe() - go func() { - defer func() { - _ = writer2.Close() - }() - err = expected.WriteIndent(writer2, "", " ") - assert.NoError(t, err) - }() - - buf := new(bytes.Buffer) - _, err = buf.ReadFrom(reader) - assert.NoError(t, err) - s := buf.String() - buf2 := new(bytes.Buffer) - _, err = buf2.ReadFrom(reader2) - assert.NoError(t, err) - - s2 := buf2.String() - assert.Equal(t, s2, s) -} -func TestEncodeKMLPolygon(t *testing.T) { - lid := id.MustLayerID(id.New().String()) - sid := id.MustSceneID(id.New().String()) - pid := id.MustPropertyID(id.New().String()) - ex := id.PluginExtensionID("polygon") - iid := id.MustPropertyItemID(id.New().String()) - vc := property.Coordinates{ - property.LatLngHeight{ - Lat: 3.4, - Lng: 5.34, - Height: 100, - }, property.LatLngHeight{ - Lat: 45.4, - Lng: 2.34, - Height: 100, - }, property.LatLngHeight{ - Lat: 34.66, - Lng: 654.34, - Height: 100, }, - } - v1 := property.Polygon{vc} - f1 := property.SealedField{ - ID: id.PropertySchemaFieldID("polygon"), - Type: "polygon", - DatasetValue: nil, - PropertyValue: v1.Value(), - } - fl1 := []*property.SealedField{} - fl1 = append(fl1, &f1) - item1 := property.SealedItem{ - Original: &iid, - Parent: nil, - SchemaGroup: id.PropertySchemaGroupID("default"), - LinkedDataset: nil, - Fields: fl1, - Groups: nil, - } - il := []*property.SealedItem{} - il = append(il, &item1) - v2 := property.ValueTypeBool - f2 := property.SealedField{ - ID: id.PropertySchemaFieldID("fill"), - Type: "bool", - DatasetValue: nil, - PropertyValue: v2.ValueFromUnsafe(true), - } - fl2 := []*property.SealedField{} - fl2 = append(fl2, &f2) - item2 := property.SealedItem{ - Original: &iid, - Parent: nil, - SchemaGroup: id.PropertySchemaGroupID("default"), - LinkedDataset: nil, - Fields: fl2, - Groups: nil, - } - il = append(il, &item2) - v3 := property.ValueTypeString - f3 := property.SealedField{ - ID: id.PropertySchemaFieldID("fillColor"), - Type: "string", - DatasetValue: nil, - PropertyValue: v3.ValueFromUnsafe("#ff334353"), - } - fl3 := []*property.SealedField{} - fl3 = append(fl3, &f3) - item3 := property.SealedItem{ - Original: &iid, - Parent: nil, - SchemaGroup: id.PropertySchemaGroupID("default"), - LinkedDataset: nil, - Fields: fl3, - Groups: nil, - } - il = append(il, &item3) - v4 := property.ValueTypeBool - f4 := property.SealedField{ - ID: id.PropertySchemaFieldID("stroke"), - Type: "bool", - DatasetValue: nil, - PropertyValue: v4.ValueFromUnsafe(true), - } - fl4 := []*property.SealedField{} - fl4 = append(fl4, &f4) - item4 := property.SealedItem{ - Original: &iid, - Parent: nil, - SchemaGroup: id.PropertySchemaGroupID("default"), - LinkedDataset: nil, - Fields: fl4, - Groups: nil, - } - il = append(il, &item4) - v5 := property.ValueTypeString - f5 := property.SealedField{ - ID: id.PropertySchemaFieldID("strokeColor"), - Type: "string", - DatasetValue: nil, - PropertyValue: v5.ValueFromUnsafe("#ff554555"), - } - fl5 := []*property.SealedField{} - fl5 = append(fl5, &f5) - item5 := property.SealedItem{ - Original: &iid, - Parent: nil, - SchemaGroup: id.PropertySchemaGroupID("default"), - LinkedDataset: nil, - Fields: fl5, - Groups: nil, - } - il = append(il, &item5) - v6 := property.ValueTypeNumber - f6 := property.SealedField{ - ID: id.PropertySchemaFieldID("strokeWidth"), - Type: "number", - DatasetValue: nil, - PropertyValue: v6.ValueFromUnsafe(3), - } - fl6 := []*property.SealedField{} - fl6 = append(fl6, &f6) - item6 := property.SealedItem{ - Original: &iid, - Parent: nil, - SchemaGroup: id.PropertySchemaGroupID("default"), - LinkedDataset: nil, - Fields: fl6, - Groups: nil, - } - il = append(il, &item6) - sp := property.Sealed{ - Original: &pid, - Items: il, - } - l := merging.SealedLayerItem{ - SealedLayerCommon: merging.SealedLayerCommon{ - Merged: layer.Merged{ - Original: lid, - Parent: nil, - Name: "test", - Scene: sid, - Property: nil, - Infobox: nil, - PluginID: &id.OfficialPluginID, - ExtensionID: &ex, + { + name: "polygon", + target: &merging.SealedLayerItem{ + SealedLayerCommon: merging.SealedLayerCommon{ + Merged: layer.Merged{ + Original: lid, + Scene: id.NewSceneID(), + Name: "test", + PluginID: &id.OfficialPluginID, + ExtensionID: id.PluginExtensionID("polygon").Ref(), + }, + Property: &property.Sealed{ + Original: id.NewPropertyID().Ref(), + Items: []*property.SealedItem{ + { + Original: id.NewPropertyItemID().Ref(), + SchemaGroup: id.PropertySchemaGroupID("default"), + Fields: []*property.SealedField{ + { + ID: id.PropertySchemaFieldID("polygon"), + Type: property.ValueTypePolygon, + PropertyValue: property.ValueTypePolygon.ValueFrom(property.Polygon{property.Coordinates{ + property.LatLngHeight{Lat: 3.4, Lng: 5.34, Height: 100}, + property.LatLngHeight{Lat: 45.4, Lng: 2.34, Height: 100}, + property.LatLngHeight{Lat: 34.66, Lng: 654.34, Height: 100}, + }}), + }, + { + ID: id.PropertySchemaFieldID("fill"), + Type: property.ValueTypeBool, + PropertyValue: property.ValueTypeBool.ValueFrom(true), + }, + { + ID: id.PropertySchemaFieldID("fillColor"), + Type: property.ValueTypeString, + PropertyValue: property.ValueTypeString.ValueFrom("#ff334353"), + }, + { + ID: id.PropertySchemaFieldID("stroke"), + Type: property.ValueTypeBool, + PropertyValue: property.ValueTypeBool.ValueFrom(true), + }, + { + ID: id.PropertySchemaFieldID("strokeColor"), + Type: property.ValueTypeString, + PropertyValue: property.ValueTypeString.ValueFrom("#ff554555"), + }, + { + ID: id.PropertySchemaFieldID("strokeWidth"), + Type: property.ValueTypeNumber, + PropertyValue: property.ValueTypeNumber.ValueFrom(3), + }, + }, + }, + }, + }, + }, + }, + want: func() *kml.CompoundElement { + k := kml.KML( + kml.SharedStyle( + "01fmph48ykj1nd82r8e4znh6a6_style", + kml.PolyStyle( + kml.Fill(true), + kml.Color(getColor("#ff334353")), + ), + kml.LineStyle( + kml.Outline(true), + kml.Color(getColor("#ff554555")), + kml.Width(3), + ), + ), + ) + k = k.Add( + kml.Placemark(kml.Name("test"), + kml.Polygon(kml.OuterBoundaryIs(kml.LinearRing(kml.Coordinates( + kml.Coordinate{Lon: 5.34, Lat: 3.4, Alt: 100}, + kml.Coordinate{Lon: 2.34, Lat: 45.4, Alt: 100}, + kml.Coordinate{Lon: 654.34, Lat: 34.66, Alt: 100}, + )))), + kml.StyleURL("#01fmph48ykj1nd82r8e4znh6a6_style"), + ), + ) + return k + }, + }, + { + name: "polyline", + target: &merging.SealedLayerItem{ + SealedLayerCommon: merging.SealedLayerCommon{ + Merged: layer.Merged{ + Original: lid, + Scene: id.NewSceneID(), + Name: "test", + PluginID: &id.OfficialPluginID, + ExtensionID: id.PluginExtensionID("polyline").Ref(), + }, + Property: &property.Sealed{ + Original: id.NewPropertyID().Ref(), + Items: []*property.SealedItem{ + { + Original: id.NewPropertyItemID().Ref(), + SchemaGroup: id.PropertySchemaGroupID("default"), + Fields: []*property.SealedField{ + { + ID: id.PropertySchemaFieldID("coordinates"), + Type: property.ValueTypeCoordinates, + PropertyValue: property.ValueTypeCoordinates.ValueFrom(property.Coordinates{ + property.LatLngHeight{Lat: 3.4, Lng: 5.34, Height: 100}, + property.LatLngHeight{Lat: 45.4, Lng: 2.34, Height: 100}, + property.LatLngHeight{Lat: 34.66, Lng: 654.34, Height: 100}, + }), + }, + { + ID: id.PropertySchemaFieldID("strokeColor"), + Type: property.ValueTypeString, + PropertyValue: property.ValueTypeString.ValueFrom("#ff224222"), + }, + { + ID: id.PropertySchemaFieldID("strokeWidth"), + Type: property.ValueTypeNumber, + PropertyValue: property.ValueTypeNumber.ValueFrom(3), + }, + }, + }, + }, + }, + }, + }, + want: func() *kml.CompoundElement { + k := kml.KML( + kml.SharedStyle( + "01fmph48ykj1nd82r8e4znh6a6_style", + kml.LineStyle( + kml.Color(getColor("#ff224222")), + kml.Width(3), + ), + ), + ) + k = k.Add( + kml.Placemark( + kml.Name("test"), + kml.LineString(kml.Coordinates( + kml.Coordinate{Lon: 5.34, Lat: 3.4, Alt: 100}, + kml.Coordinate{Lon: 2.34, Lat: 45.4, Alt: 100}, + kml.Coordinate{Lon: 654.34, Lat: 34.66, Alt: 100}, + )), + kml.StyleURL("#01fmph48ykj1nd82r8e4znh6a6_style"), + ), + ) + return k }, - Property: &sp, - Infobox: nil, - }} - - reader, writer := io.Pipe() - en := NewKMLEncoder(writer) - var err error - go func() { - defer func() { - _ = writer.Close() - }() - err = en.Encode(&l) - }() - fillColorStr, _ := f3.PropertyValue.ValueString() - strokeColorStr, _ := f5.PropertyValue.ValueString() - b1, _ := getColor(fillColorStr) - b2, _ := getColor(strokeColorStr) - stid, err := en.generateStyleId(l.Original.String(), l.Name) - assert.NoError(t, err) - expected := kml.KML(kml.SharedStyle(stid, - kml.PolyStyle( - kml.Fill(true), - kml.Color(b1), - ), - kml.LineStyle( - kml.Outline(true), - kml.Color(b2), - kml.Width(3), - ))) - expected = expected.Add(kml.Placemark(kml.Name("test"), - kml.Polygon(kml.OuterBoundaryIs(kml.LinearRing(kml.Coordinates([]kml.Coordinate{ - {Lon: 5.34, Lat: 3.4, Alt: 100}, - {Lon: 2.34, Lat: 45.4, Alt: 100}, - {Lon: 654.34, Lat: 34.66, Alt: 100}, - }...)))), - kml.StyleURL("#"+stid))) - reader2, writer2 := io.Pipe() - go func() { - defer func() { - _ = writer2.Close() - }() - err = expected.WriteIndent(writer2, "", " ") - }() - assert.NoError(t, err) - buf := new(bytes.Buffer) - _, err = buf.ReadFrom(reader) - assert.NoError(t, err) - s := buf.String() - buf2 := new(bytes.Buffer) - _, err = buf2.ReadFrom(reader2) - assert.NoError(t, err) - s2 := buf2.String() - assert.Equal(t, s2, s) -} -func TestEncodeKMLPolyline(t *testing.T) { - lid := id.MustLayerID(id.New().String()) - sid := id.MustSceneID(id.New().String()) - pid := id.MustPropertyID(id.New().String()) - ex := id.PluginExtensionID("polyline") - iid := id.MustPropertyItemID(id.New().String()) - v1 := property.Coordinates{ - property.LatLngHeight{ - Lat: 3.4, - Lng: 5.34, - Height: 100, - }, property.LatLngHeight{ - Lat: 45.4, - Lng: 2.34, - Height: 100, - }, property.LatLngHeight{ - Lat: 34.66, - Lng: 654.34, - Height: 100, }, } - f1 := property.SealedField{ - ID: id.PropertySchemaFieldID("coordinates"), - Type: "coordinates", - DatasetValue: nil, - PropertyValue: v1.Value(), - } - fl1 := []*property.SealedField{} - fl1 = append(fl1, &f1) - item1 := property.SealedItem{ - Original: &iid, - Parent: nil, - SchemaGroup: id.PropertySchemaGroupID("default"), - LinkedDataset: nil, - Fields: fl1, - Groups: nil, - } - il := []*property.SealedItem{} - il = append(il, &item1) - v2 := property.ValueTypeString - f2 := property.SealedField{ - ID: id.PropertySchemaFieldID("strokeColor"), - Type: "string", - DatasetValue: nil, - PropertyValue: v2.ValueFromUnsafe("#ff224222"), - } - fl2 := []*property.SealedField{} - fl2 = append(fl2, &f2) - item2 := property.SealedItem{ - Original: &iid, - Parent: nil, - SchemaGroup: id.PropertySchemaGroupID("default"), - LinkedDataset: nil, - Fields: fl2, - Groups: nil, + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + we := bytes.Buffer{} + _ = tt.want().WriteIndent(&we, "", " ") + wa := bytes.Buffer{} + assert.NoError(t, NewKMLEncoder(&wa).Encode(tt.target)) + assert.Equal(t, we.String(), wa.String()) + }) } - il = append(il, &item2) - v3 := property.ValueTypeNumber - f3 := property.SealedField{ - ID: id.PropertySchemaFieldID("strokeWidth"), - Type: "number", - DatasetValue: nil, - PropertyValue: v3.ValueFromUnsafe(3), - } - fl3 := []*property.SealedField{} - fl3 = append(fl3, &f3) - item3 := property.SealedItem{ - Original: &iid, - Parent: nil, - SchemaGroup: id.PropertySchemaGroupID("default"), - LinkedDataset: nil, - Fields: fl3, - Groups: nil, - } - il = append(il, &item3) - sp := property.Sealed{ - Original: &pid, - Items: il, - } - l := merging.SealedLayerItem{ - SealedLayerCommon: merging.SealedLayerCommon{ - Merged: layer.Merged{ - Original: lid, - Parent: nil, - Name: "test", - Scene: sid, - Property: nil, - Infobox: nil, - PluginID: &id.OfficialPluginID, - ExtensionID: &ex, - }, - Property: &sp, - Infobox: nil, - }} - - reader, writer := io.Pipe() - en := NewKMLEncoder(writer) - var err error - go func() { - defer func() { - _ = writer.Close() - }() - err = en.Encode(&l) - }() - strokeColorStr, _ := f2.PropertyValue.ValueString() - b1, _ := getColor(strokeColorStr) - stid, err := en.generateStyleId(l.Original.String(), l.Name) - assert.NoError(t, err) - expected := kml.KML(kml.SharedStyle(stid, - kml.LineStyle( - kml.Color(b1), - kml.Width(3), - ))) - expected = expected.Add(kml.Placemark(kml.Name("test"), - kml.LineString(kml.Coordinates([]kml.Coordinate{ - {Lon: 5.34, Lat: 3.4, Alt: 100}, - {Lon: 2.34, Lat: 45.4, Alt: 100}, - {Lon: 654.34, Lat: 34.66, Alt: 100}, - }...)), - kml.StyleURL("#"+stid))) - reader2, writer2 := io.Pipe() - go func() { - defer func() { - _ = writer2.Close() - }() - err = expected.WriteIndent(writer2, "", " ") - }() - assert.NoError(t, err) - buf := new(bytes.Buffer) - _, err = buf.ReadFrom(reader) - assert.NoError(t, err) - s := buf.String() - buf2 := new(bytes.Buffer) - _, err = buf2.ReadFrom(reader2) - assert.NoError(t, err) - s2 := buf2.String() - assert.Equal(t, s2, s) } diff --git a/pkg/layer/encoding/shp.go b/pkg/layer/encoding/shp.go index 2f75a7fa..833c0731 100644 --- a/pkg/layer/encoding/shp.go +++ b/pkg/layer/encoding/shp.go @@ -31,6 +31,7 @@ func coordsToPoints(coords property.Coordinates) []shp.Point { } return res } + func polygonToPoints(poly property.Polygon) ([]shp.Point, []int32) { var res []shp.Point parts := []int32{0} @@ -39,25 +40,47 @@ func polygonToPoints(poly property.Polygon) ([]shp.Point, []int32) { res = append(res, partPoints...) if i > 0 { parts = append(parts, int32(len(partPoints)-1)) - } } return res, parts } + func getMaxMinPoints(points []shp.Point) (shp.Point, shp.Point) { var max, min shp.Point - max = points[0] - min = points[0] - for _, p := range points { - if p.X > max.X && p.Y > max.Y { - max = p + for i, p := range points { + if i == 0 || p.X > min.X { + max.X = p.X + } + if i == 0 || p.X < min.X { + min.X = p.X } - if p.X < min.X && p.Y < min.Y { - min = p + if i == 0 || p.Y > max.Y { + max.Y = p.Y + } + if i == 0 || p.Y < min.Y { + min.Y = p.Y } } return max, min } + +func coordinatesToSHP(coordinates property.Coordinates) *shp.PolyLine { + points := coordsToPoints(coordinates) + max, min := getMaxMinPoints(points) + return &shp.PolyLine{ + Box: shp.Box{ + MinX: min.X, + MinY: min.Y, + MaxX: max.X, + MaxY: max.Y, + }, + NumParts: 1, + NumPoints: int32(len(points)), + Parts: []int32{0}, + Points: points, + } +} + func polygonToSHP(poly property.Polygon) *shp.Polygon { points, parts := polygonToPoints(poly) max, min := getMaxMinPoints(points) @@ -75,78 +98,29 @@ func polygonToSHP(poly property.Polygon) *shp.Polygon { } return &res } -func (e *SHPEncoder) encodeLayer(li *merging.SealedLayerItem) (shp.Shape, shp.ShapeType, error) { + +func (e *SHPEncoder) encodeLayer(li *merging.SealedLayerItem) (sh shp.Shape, st shp.ShapeType, err error) { if li.PluginID == nil || !id.OfficialPluginID.Equal(*li.PluginID) { return nil, 0, nil } - var shapeType shp.ShapeType - var ok bool - var sh shp.Shape switch li.ExtensionID.String() { case "marker": - shapeType = shp.POINT - latlng := property.LatLng{} - if f := li.Property.Field("location"); f != nil { - latlng, ok = f.PropertyValue.ValueLatLng() - if !ok { - dsll := f.DatasetValue.ValueLatLng() - if dsll != nil { - latlng = property.LatLng{ - Lat: dsll.Lat, - Lng: dsll.Lng, - } - } else { - return nil, 0, errors.New("invalid value type") - } - } - sh = &shp.Point{ - X: latlng.Lng, - Y: latlng.Lat, - } - - } + sh, st = e.encodeMarker(li) case "polygon": - shapeType = shp.POLYGON - polygon := property.Polygon{} - if f := li.Property.Field("polygon"); f != nil { - polygon, ok = f.PropertyValue.ValuePolygon() - if !ok { - return nil, 0, errors.New("invalid value type") - } - } - if len(polygon) > 0 { - shpPoly := polygonToSHP(polygon) - sh = shpPoly - } - + sh, st = e.encodePolygon(li) case "polyline": - shapeType = shp.POLYLINE - polyline := property.Coordinates{} - if f := li.Property.Field("coordinates"); f != nil { - polyline, ok = f.PropertyValue.ValueCoordinates() - if !ok { - return nil, 0, errors.New("invalid value type") - } - } - if len(polyline) > 0 { - points := coordsToPoints(polyline) - sh = &shp.PolyLine{ - Box: shp.Box{MinX: 102, MinY: 0, MaxX: 104, MaxY: 0}, - NumParts: 1, - NumPoints: int32(len(points)), - Parts: []int32{0}, - Points: points, - } - } + sh, st = e.encodePolyline(li) + } + if sh == nil || st == 0 { + return nil, 0, errors.New("invalid value type") } - return sh, shapeType, nil + return sh, st, nil } func (e *SHPEncoder) encodeLayerGroup(w *wsc.WriterSeeker, li *merging.SealedLayerGroup, shape *shp.Writer) error { for _, ch := range li.Children { if g, ok := ch.(*merging.SealedLayerGroup); ok { - err := e.encodeLayerGroup(w, g, shape) - if err != nil { + if err := e.encodeLayerGroup(w, g, shape); err != nil { return err } } else if i, ok := ch.(*merging.SealedLayerItem); ok { @@ -154,27 +128,28 @@ func (e *SHPEncoder) encodeLayerGroup(w *wsc.WriterSeeker, li *merging.SealedLay if err != nil { return err } + if shape == nil { shape, err = shp.CreateFrom(w, t) + if err != nil { return err } + defer func() { err = shape.Close() - }() - if err != nil { - return err - } } - _, err = shape.Write(l) - if err != nil { + + if _, err := shape.Write(l); err != nil { return err } } } + return nil } + func (e *SHPEncoder) Encode(layer merging.SealedLayer) error { var err error var w wsc.WriterSeeker @@ -210,3 +185,30 @@ func (e *SHPEncoder) Encode(layer merging.SealedLayer) error { } return nil } + +func (*SHPEncoder) encodeMarker(li *merging.SealedLayerItem) (shp.Shape, shp.ShapeType) { + f := li.Property.Field("location").Value().ValueLatLng() + if f == nil { + return nil, 0 + } + return &shp.Point{ + X: (*f).Lng, + Y: (*f).Lat, + }, shp.POINT +} + +func (*SHPEncoder) encodePolygon(li *merging.SealedLayerItem) (shp.Shape, shp.ShapeType) { + f := li.Property.Field("polygon").Value().ValuePolygon() + if f == nil || len(*f) == 0 { + return nil, 0 + } + return polygonToSHP(*f), shp.POLYGON +} + +func (*SHPEncoder) encodePolyline(li *merging.SealedLayerItem) (shp.Shape, shp.ShapeType) { + f := li.Property.Field("coordinates").Value().ValueCoordinates() + if f == nil || len(*f) == 0 { + return nil, 0 + } + return coordinatesToSHP(*f), shp.POLYLINE +} diff --git a/pkg/layer/encoding/shp_test.go b/pkg/layer/encoding/shp_test.go index 981177d8..c29357b7 100644 --- a/pkg/layer/encoding/shp_test.go +++ b/pkg/layer/encoding/shp_test.go @@ -1,7 +1,6 @@ package encoding import ( - "io" "os" "testing" @@ -16,283 +15,136 @@ import ( var _ Encoder = (*SHPEncoder)(nil) -func TestEncodeSHPMarker(t *testing.T) { - lid := id.MustLayerID(id.New().String()) - sid := id.MustSceneID(id.New().String()) - pid := id.MustPropertyID(id.New().String()) - ex := id.PluginExtensionID("marker") - iid := id.MustPropertyItemID(id.New().String()) - v1 := property.LatLng{ - Lat: 4.4, - Lng: 53.4, - } - f1 := property.SealedField{ - ID: id.PropertySchemaFieldID("location"), - Type: "latlng", - DatasetValue: nil, - PropertyValue: v1.Value(), - } - fl1 := []*property.SealedField{} - fl1 = append(fl1, &f1) - item1 := property.SealedItem{ - Original: &iid, - Parent: nil, - SchemaGroup: id.PropertySchemaGroupID("default"), - LinkedDataset: nil, - Fields: fl1, - Groups: nil, - } - il := []*property.SealedItem{} - il = append(il, &item1) - - sp := property.Sealed{ - Original: &pid, - Items: il, - } - l := merging.SealedLayerItem{ - SealedLayerCommon: merging.SealedLayerCommon{ - Merged: layer.Merged{ - Original: lid, - Parent: nil, - Scene: sid, - Property: nil, - Infobox: nil, - PluginID: &id.OfficialPluginID, - ExtensionID: &ex, +func TestEncodeSHP(t *testing.T) { + tests := []struct { + name string + layer *merging.SealedLayerItem + want shp.Shape + }{ + { + layer: &merging.SealedLayerItem{ + SealedLayerCommon: merging.SealedLayerCommon{ + Merged: layer.Merged{ + Original: id.NewLayerID(), + Parent: nil, + Scene: id.NewSceneID(), + Property: nil, + Infobox: nil, + PluginID: &id.OfficialPluginID, + ExtensionID: id.PluginExtensionID("polygon").Ref(), + }, + Property: &property.Sealed{ + Original: id.NewPropertyID().Ref(), + Items: []*property.SealedItem{ + { + Original: id.NewPropertyItemID().Ref(), + SchemaGroup: id.PropertySchemaGroupID("default"), + Fields: []*property.SealedField{ + { + ID: id.PropertySchemaFieldID("polygon"), + Type: "polygon", + DatasetValue: nil, + PropertyValue: property.ValueTypePolygon.ValueFrom(property.Polygon{property.Coordinates{ + {Lat: 3.4, Lng: 5.34, Height: 100}, + {Lat: 45.4, Lng: 2.34, Height: 100}, + {Lat: 34.66, Lng: 654.34, Height: 100}, + }}), + }, + }, + }, + }, + }, + }, + }, + want: &shp.Polygon{ + Box: shp.Box{ + MinX: 2.34, + MaxX: 654.34, + MinY: 3.4, + MaxY: 45.4, + }, + NumParts: 1, + NumPoints: 3, + Parts: []int32{0}, + Points: []shp.Point{ + {X: 5.34, Y: 3.4}, + {X: 2.34, Y: 45.4}, + {X: 654.34, Y: 34.66}, + }, }, - Property: &sp, - Infobox: nil, - }} - - reader, writer := io.Pipe() - en := NewSHPEncoder(writer) - var err error - go func() { - defer func() { - _ = writer.Close() - }() - err = en.Encode(&l) - assert.NoError(t, err) - }() - tmpFile, err := os.CreateTemp(os.TempDir(), "*.shp") - assert.NoError(t, err) - defer func() { - err := os.Remove(tmpFile.Name()) - assert.NoError(t, err) - }() - b, err := io.ReadAll(reader) - assert.NoError(t, err) - _, err = tmpFile.Write(b) - assert.NoError(t, err) - err = tmpFile.Close() - assert.NoError(t, err) - shape, err := shp.Open(tmpFile.Name()) - assert.NoError(t, err) - defer func() { - err := shape.Close() - assert.NoError(t, err) - }() - var expected *shp.Point - var ok bool - for shape.Next() { - _, p := shape.Shape() - expected, ok = p.(*shp.Point) - } - assert.Equal(t, true, ok) - assert.Equal(t, expected, &shp.Point{ - X: 53.4, - Y: 4.4, - }) -} -func TestEncodeSHPPolygon(t *testing.T) { - lid := id.MustLayerID(id.New().String()) - sid := id.MustSceneID(id.New().String()) - pid := id.MustPropertyID(id.New().String()) - ex := id.PluginExtensionID("polygon") - iid := id.MustPropertyItemID(id.New().String()) - vc := property.Coordinates{ - property.LatLngHeight{ - Lat: 3.4, - Lng: 5.34, - Height: 100, - }, property.LatLngHeight{ - Lat: 45.4, - Lng: 2.34, - Height: 100, - }, property.LatLngHeight{ - Lat: 34.66, - Lng: 654.34, - Height: 100, }, - } - v1 := property.Polygon{vc} - f1 := property.SealedField{ - ID: id.PropertySchemaFieldID("polygon"), - Type: "polygon", - DatasetValue: nil, - PropertyValue: v1.Value(), - } - fl1 := []*property.SealedField{} - fl1 = append(fl1, &f1) - item1 := property.SealedItem{ - Original: &iid, - Parent: nil, - SchemaGroup: id.PropertySchemaGroupID("default"), - LinkedDataset: nil, - Fields: fl1, - Groups: nil, - } - il := []*property.SealedItem{} - il = append(il, &item1) - sp := property.Sealed{ - Original: &pid, - Items: il, - } - l := merging.SealedLayerItem{ - SealedLayerCommon: merging.SealedLayerCommon{ - Merged: layer.Merged{ - Original: lid, - Parent: nil, - Scene: sid, - Property: nil, - Infobox: nil, - PluginID: &id.OfficialPluginID, - ExtensionID: &ex, + { + name: "polyline", + layer: &merging.SealedLayerItem{ + SealedLayerCommon: merging.SealedLayerCommon{ + Merged: layer.Merged{ + Original: id.NewLayerID(), + Parent: nil, + Name: "test", + Scene: id.NewSceneID(), + Property: nil, + Infobox: nil, + PluginID: &id.OfficialPluginID, + ExtensionID: id.PluginExtensionID("polyline").Ref(), + }, + Property: &property.Sealed{ + Original: id.NewPropertyID().Ref(), + Items: []*property.SealedItem{ + { + Original: id.NewPropertyItemID().Ref(), + SchemaGroup: id.PropertySchemaGroupID("default"), + Fields: []*property.SealedField{ + { + ID: id.PropertySchemaFieldID("coordinates"), + Type: "polyline", + DatasetValue: nil, + PropertyValue: property.ValueTypeCoordinates.ValueFrom(property.Coordinates{ + {Lat: 3.4, Lng: 5.34, Height: 100}, + {Lat: 45.4, Lng: 2.34, Height: 100}, + {Lat: 34.66, Lng: 654.34, Height: 100}, + }), + }, + }, + }, + }, + }, + }, + }, + want: &shp.PolyLine{ + Box: shp.Box{ + MinX: 2.34, + MaxX: 654.34, + MinY: 3.4, + MaxY: 45.4, + }, + NumParts: 1, + NumPoints: 3, + Parts: []int32{0}, + Points: []shp.Point{ + {X: 5.34, Y: 3.4}, + {X: 2.34, Y: 45.4}, + {X: 654.34, Y: 34.66}, + }, }, - Property: &sp, - Infobox: nil, - }} - - reader, writer := io.Pipe() - en := NewSHPEncoder(writer) - var err error - go func() { - defer func() { - _ = writer.Close() - }() - err = en.Encode(&l) - }() - tmpFile, err := os.CreateTemp(os.TempDir(), "*.shp") - assert.NoError(t, err) - defer func() { - err := os.Remove(tmpFile.Name()) - assert.NoError(t, err) - }() - b, err := io.ReadAll(reader) - assert.NoError(t, err) - _, err = tmpFile.Write(b) - assert.NoError(t, err) - err = tmpFile.Close() - assert.NoError(t, err) - shape, err := shp.Open(tmpFile.Name()) - assert.NoError(t, err) - defer func() { - err := shape.Close() - assert.NoError(t, err) - }() - var expected *shp.Polygon - var ok bool - for shape.Next() { - _, p := shape.Shape() - expected, ok = p.(*shp.Polygon) - } - assert.Equal(t, true, ok) - assert.Equal(t, expected, &shp.Polygon{Box: shp.Box{MinX: 5.34, MinY: 3.4, MaxX: 654.34, MaxY: 34.66}, NumParts: 1, NumPoints: 3, Parts: []int32{0}, Points: []shp.Point{{X: 5.34, Y: 3.4}, {X: 2.34, Y: 45.4}, {X: 654.34, Y: 34.66}}}) -} - -func TestEncodeSHPPolyline(t *testing.T) { - lid := id.MustLayerID(id.New().String()) - sid := id.MustSceneID(id.New().String()) - pid := id.MustPropertyID(id.New().String()) - ex := id.PluginExtensionID("polyline") - iid := id.MustPropertyItemID(id.New().String()) - v1 := property.Coordinates{ - property.LatLngHeight{ - Lat: 3.4, - Lng: 5.34, - Height: 100, - }, property.LatLngHeight{ - Lat: 45.4, - Lng: 2.34, - Height: 100, - }, property.LatLngHeight{ - Lat: 34.66, - Lng: 654.34, - Height: 100, }, } - f1 := property.SealedField{ - ID: id.PropertySchemaFieldID("coordinates"), - Type: "coordinates", - DatasetValue: nil, - PropertyValue: v1.Value(), - } - fl1 := []*property.SealedField{} - fl1 = append(fl1, &f1) - item1 := property.SealedItem{ - Original: &iid, - Parent: nil, - SchemaGroup: id.PropertySchemaGroupID("default"), - LinkedDataset: nil, - Fields: fl1, - Groups: nil, - } - il := []*property.SealedItem{} - il = append(il, &item1) - sp := property.Sealed{ - Original: &pid, - Items: il, - } - l := merging.SealedLayerItem{ - SealedLayerCommon: merging.SealedLayerCommon{ - Merged: layer.Merged{ - Original: lid, - Parent: nil, - Name: "test", - Scene: sid, - Property: nil, - Infobox: nil, - PluginID: &id.OfficialPluginID, - ExtensionID: &ex, - }, - Property: &sp, - Infobox: nil, - }} - reader, writer := io.Pipe() - en := NewSHPEncoder(writer) - var err error - go func() { - defer func() { - _ = writer.Close() - }() - err = en.Encode(&l) - }() - tmpFile, err := os.CreateTemp(os.TempDir(), "*.shp") - assert.NoError(t, err) - defer func() { - err := os.Remove(tmpFile.Name()) - assert.NoError(t, err) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tmpFile, err := os.CreateTemp(os.TempDir(), "*.shp") + assert.NoError(t, err) + en := NewSHPEncoder(tmpFile) + assert.NoError(t, en.Encode(tt.layer)) + + shape, err := shp.Open(tmpFile.Name()) + assert.True(t, shape.Next()) + + assert.NoError(t, err) + assert.NoError(t, os.Remove(tmpFile.Name())) + assert.NoError(t, shape.Close()) - }() - b, err := io.ReadAll(reader) - assert.NoError(t, err) - _, err = tmpFile.Write(b) - assert.NoError(t, err) - err = tmpFile.Close() - assert.NoError(t, err) - shape, err := shp.Open(tmpFile.Name()) - assert.NoError(t, err) - defer func() { - err := shape.Close() - assert.NoError(t, err) - }() - var expected *shp.PolyLine - var ok bool - for shape.Next() { - _, p := shape.Shape() - expected, ok = p.(*shp.PolyLine) + _, p := shape.Shape() + assert.Equal(t, tt.want, p) + }) } - assert.Equal(t, true, ok) - assert.Equal(t, expected, &shp.PolyLine{Box: shp.Box{MinX: 102, MinY: 0, MaxX: 104, MaxY: 0}, NumParts: 1, NumPoints: 3, Parts: []int32{0}, Points: []shp.Point{{X: 5.34, Y: 3.4}, {X: 2.34, Y: 45.4}, {X: 654.34, Y: 34.66}}}) } diff --git a/pkg/layer/layerops/initializer.go b/pkg/layer/layerops/initializer.go index 49b2bfa1..f69b3e2b 100644 --- a/pkg/layer/layerops/initializer.go +++ b/pkg/layer/layerops/initializer.go @@ -46,7 +46,7 @@ func (i LayerItem) Initialize() (*layer.Item, *property.Property, error) { return nil, nil, err } - p.UpdateLinkableValue(i.LinkablePropertySchema, property.ValueTypeLatLng.ValueFromUnsafe(i.LatLng)) + p.UpdateLinkableValue(i.LinkablePropertySchema, property.ValueTypeLatLng.ValueFrom(i.LatLng)) builder. Plugin(i.Plugin.ID().Ref()). diff --git a/pkg/plugin/manifest/convert.go b/pkg/plugin/manifest/convert.go index c903e8ab..4e9c5421 100644 --- a/pkg/plugin/manifest/convert.go +++ b/pkg/plugin/manifest/convert.go @@ -280,8 +280,8 @@ func (o *PropertyCondition) condition() *property.Condition { } func (i PropertySchemaField) schemaField() (*property.SchemaField, error) { - t, ok := property.ValueTypeFrom(string(i.Type)) - if !ok { + t := property.ValueType(i.Type) + if !t.Valid() { return nil, fmt.Errorf("invalid value type: %s", i.Type) } @@ -342,9 +342,5 @@ func (c *Choice) choice() *property.SchemaFieldChoice { } func toValue(v interface{}, t Valuetype) *property.Value { - vt, ok := property.ValueTypeFrom(string(t)) - if !ok { - return nil - } - return vt.ValueFromUnsafe(v) + return property.ValueType(t).ValueFrom(v) } diff --git a/pkg/plugin/manifest/convert_test.go b/pkg/plugin/manifest/convert_test.go index 8e87ae9a..73d04540 100644 --- a/pkg/plugin/manifest/convert_test.go +++ b/pkg/plugin/manifest/convert_test.go @@ -14,7 +14,7 @@ import ( func TestToValue(t *testing.T) { v := property.ValueTypeBool var vv *property.Value = nil - assert.Equal(t, toValue(false, "bool"), v.ValueFromUnsafe(false)) + assert.Equal(t, toValue(false, "bool"), v.ValueFrom(false)) assert.Equal(t, toValue("xx", "xxx"), vv) } diff --git a/pkg/plugin/manifest/parser_test.go b/pkg/plugin/manifest/parser_test.go index e7397128..08d9661e 100644 --- a/pkg/plugin/manifest/parser_test.go +++ b/pkg/plugin/manifest/parser_test.go @@ -38,10 +38,10 @@ var normalExpected = &Manifest{ Fields([]*property.SchemaField{ property.NewSchemaField().ID(id.PropertySchemaFieldID("a")). Type(property.ValueTypeBool). - DefaultValue(property.ValueTypeBool.MustBeValue(true)). + DefaultValue(property.ValueTypeBool.ValueFrom(true)). IsAvailableIf(&property.Condition{ Field: id.PropertySchemaFieldID("b"), - Value: property.ValueTypeNumber.MustBeValue(1), + Value: property.ValueTypeNumber.ValueFrom(1), }). MustBuild(), property.NewSchemaField().ID(id.PropertySchemaFieldID("b")). diff --git a/pkg/property/builder_test.go b/pkg/property/builder_test.go index 30733109..646b0f6d 100644 --- a/pkg/property/builder_test.go +++ b/pkg/property/builder_test.go @@ -58,7 +58,7 @@ func TestBuilder_Items(t *testing.T) { NewFieldUnsafe(). FieldUnsafe(propertySchemaField1ID). TypeUnsafe(ValueTypeString). - ValueUnsafe(ValueTypeString.ValueFromUnsafe("xxx")). + ValueUnsafe(ValueTypeString.ValueFrom("xxx")). Build(), }).MustBuild(), NewGroup().ID(iid).Schema(propertySchemaID, propertySchemaGroup1ID). @@ -66,7 +66,7 @@ func TestBuilder_Items(t *testing.T) { NewFieldUnsafe(). FieldUnsafe(propertySchemaField1ID). TypeUnsafe(ValueTypeString). - ValueUnsafe(ValueTypeString.ValueFromUnsafe("xxx")). + ValueUnsafe(ValueTypeString.ValueFrom("xxx")). Build(), }).MustBuild(), }, @@ -75,7 +75,7 @@ func TestBuilder_Items(t *testing.T) { NewFieldUnsafe(). FieldUnsafe(propertySchemaField1ID). TypeUnsafe(ValueTypeString). - ValueUnsafe(ValueTypeString.ValueFromUnsafe("xxx")). + ValueUnsafe(ValueTypeString.ValueFrom("xxx")). Build(), }).MustBuild()}, }, @@ -128,7 +128,7 @@ func TestBuilder_Build(t *testing.T) { NewFieldUnsafe(). FieldUnsafe(propertySchemaField1ID). TypeUnsafe(ValueTypeString). - ValueUnsafe(ValueTypeString.ValueFromUnsafe("xxx")). + ValueUnsafe(ValueTypeString.ValueFrom("xxx")). Build(), }).MustBuild()}, Expected: struct { @@ -146,7 +146,7 @@ func TestBuilder_Build(t *testing.T) { NewFieldUnsafe(). FieldUnsafe(propertySchemaField1ID). TypeUnsafe(ValueTypeString). - ValueUnsafe(ValueTypeString.ValueFromUnsafe("xxx")). + ValueUnsafe(ValueTypeString.ValueFrom("xxx")). Build(), }).MustBuild()}, }, @@ -181,7 +181,7 @@ func TestBuilder_Build(t *testing.T) { NewFieldUnsafe(). FieldUnsafe(propertySchemaField1ID). TypeUnsafe(ValueTypeString). - ValueUnsafe(ValueTypeString.ValueFromUnsafe("xxx")). + ValueUnsafe(ValueTypeString.ValueFrom("xxx")). Build(), }).MustBuild()}, Err: ErrInvalidItem, diff --git a/pkg/property/condition_test.go b/pkg/property/condition_test.go index 236d827f..74a7ea2f 100644 --- a/pkg/property/condition_test.go +++ b/pkg/property/condition_test.go @@ -20,17 +20,11 @@ func TestCondition_Clone(t *testing.T) { Name: "nil condition", Con: &Condition{ Field: "a", - Value: &Value{ - v: true, - t: ValueTypeBool, - }, + Value: ValueTypeBool.ValueFrom(true), }, Expected: &Condition{ Field: "a", - Value: &Value{ - v: true, - t: ValueTypeBool, - }, + Value: ValueTypeBool.ValueFrom(true), }, }, } diff --git a/pkg/property/field.go b/pkg/property/field.go index 0ecd7fb3..d068af73 100644 --- a/pkg/property/field.go +++ b/pkg/property/field.go @@ -60,8 +60,7 @@ func (p *Field) ActualValue(ds *dataset.Dataset) *Value { ldsfid := l.DatasetSchemaField() if ldid != nil || ldsfid != nil || ds.ID() == *ldid { if f := ds.Field(*ldsfid); f != nil { - v1, _ := valueFromDataset(f.Value()) - return v1 + return valueFromDataset(f.Value()) } } } @@ -144,7 +143,7 @@ func (p *Field) MigrateSchema(ctx context.Context, newSchema *Schema, dl dataset if dsid, dsfid := l.Last().Dataset(), l.Last().DatasetSchemaField(); dsid != nil && dsfid != nil { dss, _ := dl(ctx, *dsid) if dsf := dss[0].Field(*dsfid); dsf != nil { - if schemaField.Type() != valueTypeFromDataset(dsf.Type()) { + if schemaField.Type() != ValueType(dsf.Type()) { p.Unlink() } } @@ -185,9 +184,6 @@ func (p *Field) ValidateSchema(ps *SchemaField) error { if p.ptype != p.value.Type() { return errors.New("invalid field value type") } - if !p.ptype.ValidateValue(p.value) { - return errors.New("invalid field value") - } return nil } diff --git a/pkg/property/field_builder_test.go b/pkg/property/field_builder_test.go index f8c850c3..05194394 100644 --- a/pkg/property/field_builder_test.go +++ b/pkg/property/field_builder_test.go @@ -10,7 +10,7 @@ import ( func TestFieldBuilder_Value(t *testing.T) { p := NewSchemaField().ID("A").Type(ValueTypeString).MustBuild() - v := ValueTypeString.ValueFromUnsafe("vvv") + v := ValueTypeString.ValueFrom("vvv") b := NewField(p).Value(v).MustBuild() assert.Equal(t, v, b.Value()) } @@ -49,7 +49,7 @@ func TestFieldBuilder_Build(t *testing.T) { { Name: "fail invalid property type", SF: NewSchemaField().ID("A").Type(ValueTypeBool).MustBuild(), - Value: ValueTypeString.ValueFromUnsafe("vvv"), + Value: ValueTypeString.ValueFrom("vvv"), Expected: struct { PType ValueType Links *Links @@ -61,7 +61,7 @@ func TestFieldBuilder_Build(t *testing.T) { Name: "success", SF: NewSchemaField().ID("A").Type(ValueTypeString).MustBuild(), Links: NewLinks([]*Link{l}), - Value: ValueTypeString.ValueFromUnsafe("vvv"), + Value: ValueTypeString.ValueFrom("vvv"), Expected: struct { PType ValueType Links *Links @@ -69,7 +69,7 @@ func TestFieldBuilder_Build(t *testing.T) { }{ PType: ValueTypeString, Links: NewLinks([]*Link{l}), - Value: ValueTypeString.ValueFromUnsafe("vvv"), + Value: ValueTypeString.ValueFrom("vvv"), }, Err: nil, }, @@ -116,7 +116,7 @@ func TestFieldBuilder_MustBuild(t *testing.T) { { Name: "fail invalid property type", SF: NewSchemaField().ID("A").Type(ValueTypeBool).MustBuild(), - Value: ValueTypeString.ValueFromUnsafe("vvv"), + Value: ValueTypeString.ValueFrom("vvv"), Fails: true, Expected: struct { PType ValueType @@ -128,7 +128,7 @@ func TestFieldBuilder_MustBuild(t *testing.T) { Name: "success", SF: NewSchemaField().ID("A").Type(ValueTypeString).MustBuild(), Links: NewLinks([]*Link{l}), - Value: ValueTypeString.ValueFromUnsafe("vvv"), + Value: ValueTypeString.ValueFrom("vvv"), Expected: struct { PType ValueType Links *Links @@ -136,7 +136,7 @@ func TestFieldBuilder_MustBuild(t *testing.T) { }{ PType: ValueTypeString, Links: NewLinks([]*Link{l}), - Value: ValueTypeString.ValueFromUnsafe("vvv"), + Value: ValueTypeString.ValueFrom("vvv"), }, }, } @@ -185,7 +185,7 @@ func TestFieldUnsafeBuilder_Build(t *testing.T) { { Name: "success", Links: NewLinks([]*Link{l}), - Value: ValueTypeString.ValueFromUnsafe("vvv"), + Value: ValueTypeString.ValueFrom("vvv"), Type: ValueTypeString, Field: "a", Expected: struct { @@ -197,7 +197,7 @@ func TestFieldUnsafeBuilder_Build(t *testing.T) { PType: ValueTypeString, Field: "a", Links: NewLinks([]*Link{l}), - Value: ValueTypeString.ValueFromUnsafe("vvv"), + Value: ValueTypeString.ValueFrom("vvv"), }, }, { diff --git a/pkg/property/field_test.go b/pkg/property/field_test.go index 35d26480..024aad2e 100644 --- a/pkg/property/field_test.go +++ b/pkg/property/field_test.go @@ -24,19 +24,24 @@ func TestField_ActualValue(t *testing.T) { }{ { Name: "nil links", - Field: NewField(p).Value(ValueTypeString.ValueFromUnsafe("vvv")).MustBuild(), - Expected: ValueTypeString.ValueFromUnsafe("vvv"), + Field: NewField(p).Value(ValueTypeString.ValueFrom("vvv")).MustBuild(), + Expected: ValueTypeString.ValueFrom("vvv"), }, { Name: "nil last link", - Field: NewField(p).Value(ValueTypeString.ValueFromUnsafe("vvv")).Link(&Links{}).MustBuild(), + Field: NewField(p).Value(ValueTypeString.ValueFrom("vvv")).Link(&Links{}).MustBuild(), Expected: nil, }, { - Name: "dataset value", - Field: NewField(p).Value(ValueTypeString.ValueFromUnsafe("vvv")).Link(ls).MustBuild(), - DS: dataset.New().ID(dsid).Schema(dssid).Fields([]*dataset.Field{dataset.NewField(dssfid, dataset.ValueFrom("xxx"), "")}).MustBuild(), - Expected: ValueTypeString.ValueFromUnsafe("xxx"), + Name: "dataset value", + Field: NewField(p).Value(ValueTypeString.ValueFrom("vvv")).Link(ls).MustBuild(), + DS: dataset.New(). + ID(dsid).Schema(dssid). + Fields([]*dataset.Field{ + dataset.NewField(dssfid, dataset.ValueTypeString.ValueFrom("xxx"), "")}, + ). + MustBuild(), + Expected: ValueTypeString.ValueFrom("xxx"), }, } @@ -65,7 +70,7 @@ func TestField_CollectDatasets(t *testing.T) { }{ { Name: "list of one datasets", - Field: NewField(p).Value(ValueTypeString.ValueFromUnsafe("vvv")).Link(ls).MustBuild(), + Field: NewField(p).Value(ValueTypeString.ValueFrom("vvv")).Link(ls).MustBuild(), Expected: []id.DatasetID{dsid}, }, { @@ -88,7 +93,7 @@ func TestField_Clone(t *testing.T) { p := NewSchemaField().ID("A").Type(ValueTypeString).MustBuild() l := NewLink(id.NewDatasetID(), id.NewDatasetSchemaID(), id.NewDatasetSchemaFieldID()) ls := NewLinks([]*Link{l}) - b := NewField(p).Value(ValueTypeString.ValueFromUnsafe("vvv")).Link(ls).MustBuild() + b := NewField(p).Value(ValueTypeString.ValueFrom("vvv")).Link(ls).MustBuild() r := b.Clone() assert.Equal(t, b, r) } @@ -109,8 +114,8 @@ func TestField(t *testing.T) { func TestField_Update(t *testing.T) { p := NewSchemaField().ID("A").Type(ValueTypeString).MustBuild() - b := NewField(p).Value(ValueTypeString.ValueFromUnsafe("vvv")).MustBuild() - v := ValueTypeString.ValueFromUnsafe("xxx") + b := NewField(p).Value(ValueTypeString.ValueFrom("vvv")).MustBuild() + v := ValueTypeString.ValueFrom("xxx") b.UpdateUnsafe(v) assert.Equal(t, v, b.Value()) } diff --git a/pkg/property/group_builder_test.go b/pkg/property/group_builder_test.go index 8eb75aaa..6a5ee1ce 100644 --- a/pkg/property/group_builder_test.go +++ b/pkg/property/group_builder_test.go @@ -12,7 +12,7 @@ func TestGroupBuilder_Build(t *testing.T) { iid := id.NewPropertyItemID() sid := id.MustPropertySchemaID("xx~1.0.0/aa") sf := NewSchemaField().ID("a").Type(ValueTypeString).MustBuild() - v := ValueTypeString.ValueFromUnsafe("vvv") + v := ValueTypeString.ValueFrom("vvv") f := NewField(sf).Value(v).MustBuild() testCases := []struct { Name string @@ -74,7 +74,7 @@ func TestGroupBuilder_MustBuild(t *testing.T) { iid := id.NewPropertyItemID() sid := id.MustPropertySchemaID("xx~1.0.0/aa") sf := NewSchemaField().ID("a").Type(ValueTypeString).MustBuild() - v := ValueTypeString.ValueFromUnsafe("vvv") + v := ValueTypeString.ValueFrom("vvv") f := NewField(sf).Value(v).MustBuild() testCases := []struct { Name string diff --git a/pkg/property/group_list_test.go b/pkg/property/group_list_test.go index a7ee4c37..e19e12df 100644 --- a/pkg/property/group_list_test.go +++ b/pkg/property/group_list_test.go @@ -44,7 +44,7 @@ func TestGroupList_SchemaRef(t *testing.T) { func TestGroupList_HasLinkedField(t *testing.T) { pid := id.NewPropertyItemID() sf := NewSchemaField().ID("a").Type(ValueTypeString).MustBuild() - v := ValueTypeString.ValueFromUnsafe("vvv") + v := ValueTypeString.ValueFrom("vvv") dsid := id.NewDatasetID() dssid := id.NewDatasetSchemaID() f := NewField(sf).Value(v).Link(&Links{links: []*Link{NewLink(dsid, dssid, id.NewDatasetSchemaFieldID())}}).MustBuild() @@ -82,7 +82,7 @@ func TestGroupList_HasLinkedField(t *testing.T) { func TestGroupList_CollectDatasets(t *testing.T) { pid := id.NewPropertyItemID() sf := NewSchemaField().ID("a").Type(ValueTypeString).MustBuild() - v := ValueTypeString.ValueFromUnsafe("vvv") + v := ValueTypeString.ValueFrom("vvv") dsid := id.NewDatasetID() dssid := id.NewDatasetSchemaID() f := NewField(sf).Value(v).Link(&Links{links: []*Link{NewLink(dsid, dssid, id.NewDatasetSchemaFieldID())}}).MustBuild() @@ -119,7 +119,7 @@ func TestGroupList_CollectDatasets(t *testing.T) { func TestGroupList_FieldsByLinkedDataset(t *testing.T) { pid := id.NewPropertyItemID() sf := NewSchemaField().ID("a").Type(ValueTypeString).MustBuild() - v := ValueTypeString.ValueFromUnsafe("vvv") + v := ValueTypeString.ValueFrom("vvv") dsid := id.NewDatasetID() dssid := id.NewDatasetSchemaID() f := NewField(sf).Value(v).Link(&Links{links: []*Link{NewLink(dsid, dssid, id.NewDatasetSchemaFieldID())}}).MustBuild() @@ -156,7 +156,7 @@ func TestGroupList_FieldsByLinkedDataset(t *testing.T) { func TestGroupList_IsEmpty(t *testing.T) { pid := id.NewPropertyItemID() sf := NewSchemaField().ID("a").Type(ValueTypeString).MustBuild() - v := ValueTypeString.ValueFromUnsafe("vvv") + v := ValueTypeString.ValueFrom("vvv") dsid := id.NewDatasetID() dssid := id.NewDatasetSchemaID() f := NewField(sf).Value(v).Link(&Links{links: []*Link{NewLink(dsid, dssid, id.NewDatasetSchemaFieldID())}}).MustBuild() @@ -191,7 +191,7 @@ func TestGroupList_IsEmpty(t *testing.T) { func TestGroupList_Prune(t *testing.T) { sf := NewSchemaField().ID("a").Type(ValueTypeString).MustBuild() - v := ValueTypeString.ValueFromUnsafe("vvv") + v := ValueTypeString.ValueFrom("vvv") f := NewField(sf).Value(v).MustBuild() f2 := NewField(sf).MustBuild() pid := id.NewPropertyItemID() diff --git a/pkg/property/group_test.go b/pkg/property/group_test.go index 053507d1..5c823dd5 100644 --- a/pkg/property/group_test.go +++ b/pkg/property/group_test.go @@ -28,7 +28,7 @@ func TestGroup_SchemaGroup(t *testing.T) { func TestGroup_HasLinkedField(t *testing.T) { sf := NewSchemaField().ID("a").Type(ValueTypeString).MustBuild() - v := ValueTypeString.ValueFromUnsafe("vvv") + v := ValueTypeString.ValueFrom("vvv") l := NewLink(id.NewDatasetID(), id.NewDatasetSchemaID(), id.NewDatasetSchemaFieldID()) ls := NewLinks([]*Link{l}) f := NewField(sf).Value(v).Link(ls).MustBuild() @@ -66,7 +66,7 @@ func TestGroup_HasLinkedField(t *testing.T) { } func TestGroup_IsDatasetLinked(t *testing.T) { sf := NewSchemaField().ID("a").Type(ValueTypeString).MustBuild() - v := ValueTypeString.ValueFromUnsafe("vvv") + v := ValueTypeString.ValueFrom("vvv") dsid := id.NewDatasetID() dssid := id.NewDatasetSchemaID() l := NewLink(dsid, dssid, id.NewDatasetSchemaFieldID()) @@ -109,7 +109,7 @@ func TestGroup_IsDatasetLinked(t *testing.T) { func TestGroup_CollectDatasets(t *testing.T) { sf := NewSchemaField().ID("a").Type(ValueTypeString).MustBuild() - v := ValueTypeString.ValueFromUnsafe("vvv") + v := ValueTypeString.ValueFrom("vvv") dsid := id.NewDatasetID() l := NewLink(dsid, id.NewDatasetSchemaID(), id.NewDatasetSchemaFieldID()) ls := NewLinks([]*Link{l}) @@ -143,7 +143,7 @@ func TestGroup_CollectDatasets(t *testing.T) { func TestGroup_FieldsByLinkedDataset(t *testing.T) { sf := NewSchemaField().ID("a").Type(ValueTypeString).MustBuild() - v := ValueTypeString.ValueFromUnsafe("vvv") + v := ValueTypeString.ValueFrom("vvv") dsid := id.NewDatasetID() dssid := id.NewDatasetSchemaID() l := NewLink(dsid, dssid, id.NewDatasetSchemaFieldID()) @@ -180,7 +180,7 @@ func TestGroup_FieldsByLinkedDataset(t *testing.T) { func TestGroup_IsEmpty(t *testing.T) { sf := NewSchemaField().ID("a").Type(ValueTypeString).MustBuild() - v := ValueTypeString.ValueFromUnsafe("vvv") + v := ValueTypeString.ValueFrom("vvv") f := NewField(sf).Value(v).MustBuild() f2 := NewField(sf).MustBuild() @@ -213,7 +213,7 @@ func TestGroup_IsEmpty(t *testing.T) { func TestGroup_Prune(t *testing.T) { sf := NewSchemaField().ID("a").Type(ValueTypeString).MustBuild() - v := ValueTypeString.ValueFromUnsafe("vvv") + v := ValueTypeString.ValueFrom("vvv") f := NewField(sf).Value(v).MustBuild() f2 := NewField(sf).MustBuild() @@ -309,7 +309,7 @@ func TestGroup_GetOrCreateField(t *testing.T) { func TestGroup_RemoveField(t *testing.T) { sf := NewSchemaField().ID("a").Type(ValueTypeString).MustBuild() sf2 := NewSchemaField().ID("b").Type(ValueTypeString).MustBuild() - v := ValueTypeString.ValueFromUnsafe("vvv") + v := ValueTypeString.ValueFrom("vvv") f := NewField(sf).Value(v).MustBuild() f2 := NewField(sf2).MustBuild() @@ -343,7 +343,7 @@ func TestGroup_RemoveField(t *testing.T) { func TestGroup_FieldIDs(t *testing.T) { sf := NewSchemaField().ID("a").Type(ValueTypeString).MustBuild() sf2 := NewSchemaField().ID("b").Type(ValueTypeString).MustBuild() - v := ValueTypeString.ValueFromUnsafe("vvv") + v := ValueTypeString.ValueFrom("vvv") f := NewField(sf).Value(v).MustBuild() f2 := NewField(sf2).MustBuild() @@ -375,7 +375,7 @@ func TestGroup_FieldIDs(t *testing.T) { func TestGroup_Field(t *testing.T) { sf := NewSchemaField().ID("a").Type(ValueTypeString).MustBuild() sf2 := NewSchemaField().ID("b").Type(ValueTypeString).MustBuild() - v := ValueTypeString.ValueFromUnsafe("vvv") + v := ValueTypeString.ValueFrom("vvv") f := NewField(sf).Value(v).MustBuild() f2 := NewField(sf2).MustBuild() @@ -442,15 +442,15 @@ func TestGroup_UpdateNameFieldValue(t *testing.T) { Name: "update value", Group: NewGroup().NewID().Schema(id.MustPropertySchemaID("xx~1.0.0/aa"), "aa").MustBuild(), PS: NewSchema().ID(id.MustPropertySchemaID("xx~1.0.0/aa")).Groups([]*SchemaGroup{sg}).MustBuild(), - Value: ValueTypeString.ValueFromUnsafe("abc"), + Value: ValueTypeString.ValueFrom("abc"), FID: "aa", - Expected: NewField(sf).Value(ValueTypeString.ValueFromUnsafe("abc")).MustBuild(), + Expected: NewField(sf).Value(ValueTypeString.ValueFrom("abc")).MustBuild(), }, { Name: "invalid property field", Group: NewGroup().NewID().Schema(id.MustPropertySchemaID("xx~1.0.0/aa"), "aa").MustBuild(), PS: NewSchema().ID(id.MustPropertySchemaID("xx~1.0.0/bb")).Groups([]*SchemaGroup{sg2}).MustBuild(), - Value: ValueTypeString.ValueFromUnsafe("abc"), + Value: ValueTypeString.ValueFrom("abc"), FID: "aa", Expected: nil, Err: ErrInvalidPropertyField, diff --git a/pkg/property/initializer_test.go b/pkg/property/initializer_test.go index a965137c..7eb55f15 100644 --- a/pkg/property/initializer_test.go +++ b/pkg/property/initializer_test.go @@ -94,7 +94,7 @@ func TestInitializerItem_Clone(t *testing.T) { Fields: []*InitializerField{{ Field: id.PropertySchemaFieldID("name"), Type: ValueTypeString, - Value: ValueTypeString.ValueFromUnsafe("aaa"), + Value: ValueTypeString.ValueFrom("aaa"), Links: []*InitializerLink{{ Dataset: id.NewDatasetID().Ref(), Schema: id.NewDatasetSchemaID(), @@ -141,7 +141,7 @@ func TestInitializerItem_PropertyGroup(t *testing.T) { Fields: []*InitializerField{{ Field: id.PropertySchemaFieldID("name"), Type: ValueTypeString, - Value: ValueTypeString.ValueFromUnsafe("aaa"), + Value: ValueTypeString.ValueFrom("aaa"), }}, } @@ -183,7 +183,7 @@ func TestInitializerGroup_Clone(t *testing.T) { Fields: []*InitializerField{{ Field: id.PropertySchemaFieldID("name"), Type: ValueTypeString, - Value: ValueTypeString.ValueFromUnsafe("aaa"), + Value: ValueTypeString.ValueFrom("aaa"), Links: []*InitializerLink{{ Dataset: id.NewDatasetID().Ref(), Schema: id.NewDatasetSchemaID(), @@ -208,7 +208,7 @@ func TestInitializerGroup_PropertyGroup(t *testing.T) { Fields: []*InitializerField{{ Field: id.PropertySchemaFieldID("name"), Type: ValueTypeString, - Value: ValueTypeString.ValueFromUnsafe("aaa"), + Value: ValueTypeString.ValueFrom("aaa"), }}, } @@ -231,7 +231,7 @@ func TestInitializerField_Clone(t *testing.T) { field := &InitializerField{ Field: id.PropertySchemaFieldID("name"), Type: ValueTypeString, - Value: ValueTypeString.ValueFromUnsafe("aaa"), + Value: ValueTypeString.ValueFrom("aaa"), Links: []*InitializerLink{{ Dataset: id.NewDatasetID().Ref(), Schema: id.NewDatasetSchemaID(), @@ -249,7 +249,7 @@ func TestInitializerField_PropertyField(t *testing.T) { field := &InitializerField{ Field: id.PropertySchemaFieldID("name"), Type: ValueTypeString, - Value: ValueTypeString.ValueFromUnsafe("aaa"), + Value: ValueTypeString.ValueFrom("aaa"), Links: []*InitializerLink{{ Dataset: id.NewDatasetID().Ref(), Schema: id.NewDatasetSchemaID(), diff --git a/pkg/property/item_test.go b/pkg/property/item_test.go index 77b197f1..adf797dc 100644 --- a/pkg/property/item_test.go +++ b/pkg/property/item_test.go @@ -60,7 +60,7 @@ func TestToGroup(t *testing.T) { NewFieldUnsafe(). FieldUnsafe(propertySchemaField1ID). TypeUnsafe(ValueTypeString). - ValueUnsafe(ValueTypeString.ValueFromUnsafe("xxx")). + ValueUnsafe(ValueTypeString.ValueFrom("xxx")). Build(), }).MustBuild(), } diff --git a/pkg/property/link_test.go b/pkg/property/link_test.go index 2e192740..706b8565 100644 --- a/pkg/property/link_test.go +++ b/pkg/property/link_test.go @@ -137,7 +137,7 @@ func TestLinks_Validate(t *testing.T) { }, DM: dataset.Map{ did1: dataset.New().ID(did1).Schema(dsid1).Fields([]*dataset.Field{ - dataset.NewField(dfid1, dataset.ValueFrom("vvv"), ""), + dataset.NewField(dfid1, dataset.ValueTypeString.ValueFrom("vvv"), ""), }).MustBuild(), }, Links: NewLinks([]*Link{NewLink(did1, dsid1, dfid1)}), @@ -416,7 +416,7 @@ func TestLink_Validate(t *testing.T) { { Name: "input schema id != link schema", DS: dataset.New().ID(did1).Schema(dsid1).Fields([]*dataset.Field{ - dataset.NewField(dfid1, dataset.ValueFrom("vvv"), "")}).MustBuild(), + dataset.NewField(dfid1, dataset.ValueTypeString.ValueFrom("vvv"), "")}).MustBuild(), DSS: dataset.NewSchema().NewID().Fields([]*dataset.SchemaField{ dataset.NewSchemaField(). ID(dfid1). @@ -452,7 +452,7 @@ func TestLink_Validate(t *testing.T) { { Name: "valid", DS: dataset.New().ID(did1).Schema(dsid1).Fields([]*dataset.Field{ - dataset.NewField(dfid1, dataset.ValueFrom("vvv"), "")}).MustBuild(), + dataset.NewField(dfid1, dataset.ValueTypeString.ValueFrom("vvv"), "")}).MustBuild(), DSS: dataset.NewSchema().ID(dsid1).Fields([]*dataset.SchemaField{ dataset.NewSchemaField(). ID(dfid1). diff --git a/pkg/property/merged_test.go b/pkg/property/merged_test.go index 4928117f..e534b4b5 100644 --- a/pkg/property/merged_test.go +++ b/pkg/property/merged_test.go @@ -31,15 +31,15 @@ func TestMerge(t *testing.T) { i8id := id.NewPropertyItemID() fields1 := []*Field{ - NewFieldUnsafe().FieldUnsafe(id.PropertySchemaFieldID("a")).ValueUnsafe(ValueTypeString.ValueFromUnsafe("a")).Build(), - NewFieldUnsafe().FieldUnsafe(id.PropertySchemaFieldID("b")).ValueUnsafe(ValueTypeString.ValueFromUnsafe("b")).Build(), + NewFieldUnsafe().FieldUnsafe(id.PropertySchemaFieldID("a")).ValueUnsafe(ValueTypeString.ValueFrom("a")).Build(), + NewFieldUnsafe().FieldUnsafe(id.PropertySchemaFieldID("b")).ValueUnsafe(ValueTypeString.ValueFrom("b")).Build(), NewFieldUnsafe().FieldUnsafe(id.PropertySchemaFieldID("e")).TypeUnsafe(ValueTypeString).LinksUnsafe(NewLinks([]*Link{NewLink(d2, ds, df)})).Build(), NewFieldUnsafe().FieldUnsafe(id.PropertySchemaFieldID("f")).TypeUnsafe(ValueTypeNumber).Build(), } fields2 := []*Field{ - NewFieldUnsafe().FieldUnsafe(id.PropertySchemaFieldID("a")).ValueUnsafe(ValueTypeString.ValueFromUnsafe("1")).Build(), - NewFieldUnsafe().FieldUnsafe(id.PropertySchemaFieldID("c")).ValueUnsafe(ValueTypeString.ValueFromUnsafe("2")).Build(), + NewFieldUnsafe().FieldUnsafe(id.PropertySchemaFieldID("a")).ValueUnsafe(ValueTypeString.ValueFrom("1")).Build(), + NewFieldUnsafe().FieldUnsafe(id.PropertySchemaFieldID("c")).ValueUnsafe(ValueTypeString.ValueFrom("2")).Build(), NewFieldUnsafe().FieldUnsafe(id.PropertySchemaFieldID("d")).TypeUnsafe(ValueTypeString).LinksUnsafe(NewLinks([]*Link{NewLinkFieldOnly(ds, df)})).Build(), NewFieldUnsafe().FieldUnsafe(id.PropertySchemaFieldID("f")).TypeUnsafe(ValueTypeString).Build(), } @@ -90,12 +90,12 @@ func TestMerge(t *testing.T) { Fields: []*MergedField{ { ID: id.PropertySchemaFieldID("a"), - Value: ValueTypeString.ValueFromUnsafe("a"), + Value: ValueTypeString.ValueFrom("a"), Type: ValueTypeString, }, { ID: id.PropertySchemaFieldID("b"), - Value: ValueTypeString.ValueFromUnsafe("b"), + Value: ValueTypeString.ValueFrom("b"), Type: ValueTypeString, }, { @@ -119,13 +119,13 @@ func TestMerge(t *testing.T) { Fields: []*MergedField{ { ID: id.PropertySchemaFieldID("a"), - Value: ValueTypeString.ValueFromUnsafe("a"), + Value: ValueTypeString.ValueFrom("a"), Type: ValueTypeString, Overridden: true, }, { ID: id.PropertySchemaFieldID("b"), - Value: ValueTypeString.ValueFromUnsafe("b"), + Value: ValueTypeString.ValueFrom("b"), Type: ValueTypeString, }, { @@ -135,7 +135,7 @@ func TestMerge(t *testing.T) { }, { ID: id.PropertySchemaFieldID("c"), - Value: ValueTypeString.ValueFromUnsafe("2"), + Value: ValueTypeString.ValueFrom("2"), Type: ValueTypeString, }, { @@ -153,12 +153,12 @@ func TestMerge(t *testing.T) { Fields: []*MergedField{ { ID: id.PropertySchemaFieldID("a"), - Value: ValueTypeString.ValueFromUnsafe("a"), + Value: ValueTypeString.ValueFrom("a"), Type: ValueTypeString, }, { ID: id.PropertySchemaFieldID("b"), - Value: ValueTypeString.ValueFromUnsafe("b"), + Value: ValueTypeString.ValueFrom("b"), Type: ValueTypeString, }, { @@ -180,12 +180,12 @@ func TestMerge(t *testing.T) { Fields: []*MergedField{ { ID: id.PropertySchemaFieldID("a"), - Value: ValueTypeString.ValueFromUnsafe("1"), + Value: ValueTypeString.ValueFrom("1"), Type: ValueTypeString, }, { ID: id.PropertySchemaFieldID("c"), - Value: ValueTypeString.ValueFromUnsafe("2"), + Value: ValueTypeString.ValueFrom("2"), Type: ValueTypeString, }, { @@ -223,12 +223,12 @@ func TestMerge(t *testing.T) { Fields: []*MergedField{ { ID: id.PropertySchemaFieldID("a"), - Value: ValueTypeString.ValueFromUnsafe("a"), + Value: ValueTypeString.ValueFrom("a"), Type: ValueTypeString, }, { ID: id.PropertySchemaFieldID("b"), - Value: ValueTypeString.ValueFromUnsafe("b"), + Value: ValueTypeString.ValueFrom("b"), Type: ValueTypeString, }, { @@ -252,12 +252,12 @@ func TestMerge(t *testing.T) { Fields: []*MergedField{ { ID: id.PropertySchemaFieldID("a"), - Value: ValueTypeString.ValueFromUnsafe("a"), + Value: ValueTypeString.ValueFrom("a"), Type: ValueTypeString, }, { ID: id.PropertySchemaFieldID("b"), - Value: ValueTypeString.ValueFromUnsafe("b"), + Value: ValueTypeString.ValueFrom("b"), Type: ValueTypeString, }, { @@ -279,12 +279,12 @@ func TestMerge(t *testing.T) { Fields: []*MergedField{ { ID: id.PropertySchemaFieldID("a"), - Value: ValueTypeString.ValueFromUnsafe("a"), + Value: ValueTypeString.ValueFrom("a"), Type: ValueTypeString, }, { ID: id.PropertySchemaFieldID("b"), - Value: ValueTypeString.ValueFromUnsafe("b"), + Value: ValueTypeString.ValueFrom("b"), Type: ValueTypeString, }, { @@ -322,12 +322,12 @@ func TestMerge(t *testing.T) { Fields: []*MergedField{ { ID: id.PropertySchemaFieldID("a"), - Value: ValueTypeString.ValueFromUnsafe("1"), + Value: ValueTypeString.ValueFrom("1"), Type: ValueTypeString, }, { ID: id.PropertySchemaFieldID("c"), - Value: ValueTypeString.ValueFromUnsafe("2"), + Value: ValueTypeString.ValueFrom("2"), Type: ValueTypeString, }, { @@ -351,12 +351,12 @@ func TestMerge(t *testing.T) { Fields: []*MergedField{ { ID: id.PropertySchemaFieldID("a"), - Value: ValueTypeString.ValueFromUnsafe("1"), + Value: ValueTypeString.ValueFrom("1"), Type: ValueTypeString, }, { ID: id.PropertySchemaFieldID("c"), - Value: ValueTypeString.ValueFromUnsafe("2"), + Value: ValueTypeString.ValueFrom("2"), Type: ValueTypeString, }, { @@ -378,12 +378,12 @@ func TestMerge(t *testing.T) { Fields: []*MergedField{ { ID: id.PropertySchemaFieldID("a"), - Value: ValueTypeString.ValueFromUnsafe("1"), + Value: ValueTypeString.ValueFrom("1"), Type: ValueTypeString, }, { ID: id.PropertySchemaFieldID("c"), - Value: ValueTypeString.ValueFromUnsafe("2"), + Value: ValueTypeString.ValueFrom("2"), Type: ValueTypeString, }, { diff --git a/pkg/property/property_test.go b/pkg/property/property_test.go index 56ee60c1..4747fe68 100644 --- a/pkg/property/property_test.go +++ b/pkg/property/property_test.go @@ -53,19 +53,19 @@ func TestPropertyMigrateSchema(t *testing.T) { fields := []*Field{ // should remain NewFieldUnsafe().FieldUnsafe(schemaField1ID). - ValueUnsafe(ValueTypeString.ValueFromUnsafe("foobar")). + ValueUnsafe(ValueTypeString.ValueFrom("foobar")). Build(), // should be removed because of max NewFieldUnsafe().FieldUnsafe(schemaField2ID). - ValueUnsafe(ValueTypeNumber.ValueFromUnsafe(101)). + ValueUnsafe(ValueTypeNumber.ValueFrom(101)). Build(), // should remain NewFieldUnsafe().FieldUnsafe(schemaField3ID). - ValueUnsafe(ValueTypeNumber.ValueFromUnsafe(1)). + ValueUnsafe(ValueTypeNumber.ValueFrom(1)). Build(), // should be removed because of choices NewFieldUnsafe().FieldUnsafe(schemaField4ID). - ValueUnsafe(ValueTypeString.ValueFromUnsafe("z")). + ValueUnsafe(ValueTypeString.ValueFrom("z")). Build(), // should remain NewFieldUnsafe().FieldUnsafe(schemaField5ID). @@ -81,11 +81,11 @@ func TestPropertyMigrateSchema(t *testing.T) { Build(), // should be removed because of type NewFieldUnsafe().FieldUnsafe(schemaField7ID). - ValueUnsafe(ValueTypeString.ValueFromUnsafe("hogehoge")). + ValueUnsafe(ValueTypeString.ValueFrom("hogehoge")). Build(), // should be removed because of not existing field NewFieldUnsafe().FieldUnsafe(schemaField8ID). - ValueUnsafe(ValueTypeString.ValueFromUnsafe("hogehoge")). + ValueUnsafe(ValueTypeString.ValueFrom("hogehoge")). Build(), } items := []Item{ diff --git a/pkg/property/schema_field_builder.go b/pkg/property/schema_field_builder.go index 4fcf1f9c..268b1508 100644 --- a/pkg/property/schema_field_builder.go +++ b/pkg/property/schema_field_builder.go @@ -25,7 +25,7 @@ func (b *SchemaFieldBuilder) Build() (*SchemaField, error) { if b.p.min != nil && b.p.max != nil && *b.p.min > *b.p.max { return nil, errors.New("invalid min and max") } - if _, ok := b.p.propertyType.Validate(); !ok { + if ok := b.p.propertyType.Valid(); !ok { return nil, errors.New("invalid value type") } return b.p, nil diff --git a/pkg/property/schema_field_test.go b/pkg/property/schema_field_test.go index dce9058d..fee1dedf 100644 --- a/pkg/property/schema_field_test.go +++ b/pkg/property/schema_field_test.go @@ -131,19 +131,19 @@ func TestSchemaField_Validate(t *testing.T) { { Name: "property type != value type", SF: NewSchemaField().ID("A").Type(ValueTypeNumber).MustBuild(), - Input: ValueTypeBool.ValueFromUnsafe(true), + Input: ValueTypeBool.ValueFrom(true), Expected: false, }, { Name: "validate min", SF: NewSchemaField().ID("A").Type(ValueTypeNumber).Min(10).MustBuild(), - Input: ValueTypeNumber.ValueFromUnsafe(9), + Input: ValueTypeNumber.ValueFrom(9), Expected: false, }, { Name: "validate max", SF: NewSchemaField().ID("A").Type(ValueTypeNumber).Max(10).MustBuild(), - Input: ValueTypeNumber.ValueFromUnsafe(11), + Input: ValueTypeNumber.ValueFrom(11), Expected: false, }, { @@ -160,7 +160,7 @@ func TestSchemaField_Validate(t *testing.T) { Icon: "", }, }).MustBuild(), - Input: ValueTypeString.ValueFromUnsafe("xxx"), + Input: ValueTypeString.ValueFrom("xxx"), Expected: true, }, { @@ -177,13 +177,13 @@ func TestSchemaField_Validate(t *testing.T) { Icon: "", }, }).MustBuild(), - Input: ValueTypeString.ValueFromUnsafe("aaa"), + Input: ValueTypeString.ValueFrom("aaa"), Expected: false, }, { Name: "validate other", SF: NewSchemaField().ID("A").Type(ValueTypeLatLng).MustBuild(), - Input: ValueTypeLatLng.ValueFromUnsafe(LatLng{Lat: 10, Lng: 11}), + Input: ValueTypeLatLng.ValueFrom(LatLng{Lat: 10, Lng: 11}), Expected: true, }, } diff --git a/pkg/property/schema_group_builder_test.go b/pkg/property/schema_group_builder_test.go index 31876c49..6b88a95b 100644 --- a/pkg/property/schema_group_builder_test.go +++ b/pkg/property/schema_group_builder_test.go @@ -45,7 +45,7 @@ func TestSchemaGroupBuilder_Build(t *testing.T) { List: true, IsAvailableIf: &Condition{ Field: "ff", - Value: ValueTypeString.ValueFromUnsafe("abc"), + Value: ValueTypeString.ValueFrom("abc"), }, Title: i18n.StringFrom("tt"), Expected: expected{ @@ -55,7 +55,7 @@ func TestSchemaGroupBuilder_Build(t *testing.T) { List: true, IsAvailableIf: &Condition{ Field: "ff", - Value: ValueTypeString.ValueFromUnsafe("abc"), + Value: ValueTypeString.ValueFrom("abc"), }, Title: i18n.StringFrom("tt"), }, @@ -68,7 +68,7 @@ func TestSchemaGroupBuilder_Build(t *testing.T) { List: true, IsAvailableIf: &Condition{ Field: "ff", - Value: ValueTypeString.ValueFromUnsafe("abc"), + Value: ValueTypeString.ValueFrom("abc"), }, Title: i18n.StringFrom("tt"), Expected: expected{ @@ -78,7 +78,7 @@ func TestSchemaGroupBuilder_Build(t *testing.T) { List: true, IsAvailableIf: &Condition{ Field: "ff", - Value: ValueTypeString.ValueFromUnsafe("abc"), + Value: ValueTypeString.ValueFrom("abc"), }, Title: i18n.StringFrom("tt"), }, diff --git a/pkg/property/sealed.go b/pkg/property/sealed.go index fa89cb4b..e2869a99 100644 --- a/pkg/property/sealed.go +++ b/pkg/property/sealed.go @@ -31,6 +31,13 @@ type SealedField struct { PropertyValue *Value } +func (f *SealedField) Value() *Value { + if f == nil { + return nil + } + return datasetValueOrPropertyValue(f.DatasetValue, f.PropertyValue) +} + func Seal(ctx context.Context, p *Merged, d dataset.GraphLoader) (*Sealed, error) { if p == nil { return nil, nil diff --git a/pkg/property/sealed_test.go b/pkg/property/sealed_test.go index eb0a2a14..4c5bbea1 100644 --- a/pkg/property/sealed_test.go +++ b/pkg/property/sealed_test.go @@ -58,12 +58,12 @@ func TestSeal(t *testing.T) { Fields: []*MergedField{ { ID: id.PropertySchemaFieldID("a"), - Value: ValueTypeString.ValueFromUnsafe("a"), + Value: ValueTypeString.ValueFrom("a"), Type: ValueTypeString, }, { ID: id.PropertySchemaFieldID("b"), - Value: ValueTypeString.ValueFromUnsafe("b"), + Value: ValueTypeString.ValueFrom("b"), Links: NewLinks([]*Link{NewLink(d, ds, df)}), Type: ValueTypeString, }, @@ -79,12 +79,12 @@ func TestSeal(t *testing.T) { Fields: []*MergedField{ { ID: id.PropertySchemaFieldID("a"), - Value: ValueTypeString.ValueFromUnsafe("aaa"), + Value: ValueTypeString.ValueFrom("aaa"), Type: ValueTypeString, }, { ID: id.PropertySchemaFieldID("b"), - Value: ValueTypeString.ValueFromUnsafe("aaa"), + Value: ValueTypeString.ValueFrom("aaa"), Links: NewLinks([]*Link{NewLink(d, ds, df)}), Type: ValueTypeString, }, @@ -117,12 +117,12 @@ func TestSeal(t *testing.T) { { ID: "a", Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFromUnsafe("a"), + PropertyValue: ValueTypeString.ValueFrom("a"), }, { ID: "b", Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFromUnsafe("b"), + PropertyValue: ValueTypeString.ValueFrom("b"), DatasetValue: dataset.ValueTypeString.ValueFrom("bbb"), }, }, @@ -138,12 +138,12 @@ func TestSeal(t *testing.T) { { ID: "a", Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFromUnsafe("aaa"), + PropertyValue: ValueTypeString.ValueFrom("aaa"), }, { ID: "b", Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFromUnsafe("aaa"), + PropertyValue: ValueTypeString.ValueFrom("aaa"), DatasetValue: dataset.ValueTypeString.ValueFrom("bbb"), }, }, @@ -227,12 +227,12 @@ func TestSealedItemFrom(t *testing.T) { Fields: []*MergedField{ { ID: id.PropertySchemaFieldID("a"), - Value: ValueTypeString.ValueFromUnsafe("a"), + Value: ValueTypeString.ValueFrom("a"), Type: ValueTypeString, }, { ID: id.PropertySchemaFieldID("b"), - Value: ValueTypeString.ValueFromUnsafe("b"), + Value: ValueTypeString.ValueFrom("b"), Links: NewLinks([]*Link{NewLink(d, ds, df)}), Type: ValueTypeString, }, @@ -259,12 +259,12 @@ func TestSealedItemFrom(t *testing.T) { { ID: "a", Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFromUnsafe("a"), + PropertyValue: ValueTypeString.ValueFrom("a"), }, { ID: "b", Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFromUnsafe("b"), + PropertyValue: ValueTypeString.ValueFrom("b"), DatasetValue: dataset.ValueTypeString.ValueFrom("bbb"), }, }, @@ -289,12 +289,12 @@ func TestSealedItemFrom(t *testing.T) { Fields: []*MergedField{ { ID: id.PropertySchemaFieldID("a"), - Value: ValueTypeString.ValueFromUnsafe("aaa"), + Value: ValueTypeString.ValueFrom("aaa"), Type: ValueTypeString, }, { ID: id.PropertySchemaFieldID("b"), - Value: ValueTypeString.ValueFromUnsafe("aaa"), + Value: ValueTypeString.ValueFrom("aaa"), Links: NewLinks([]*Link{NewLink(d, ds, df)}), Type: ValueTypeString, }, @@ -322,12 +322,12 @@ func TestSealedItemFrom(t *testing.T) { { ID: "a", Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFromUnsafe("aaa"), + PropertyValue: ValueTypeString.ValueFrom("aaa"), }, { ID: "b", Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFromUnsafe("aaa"), + PropertyValue: ValueTypeString.ValueFrom("aaa"), DatasetValue: dataset.ValueTypeString.ValueFrom("bbb"), }, }, @@ -382,12 +382,12 @@ func TestSealed_Interface(t *testing.T) { { ID: "a", Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFromUnsafe("a"), + PropertyValue: ValueTypeString.ValueFrom("a"), }, { ID: "b", Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFromUnsafe("b"), + PropertyValue: ValueTypeString.ValueFrom("b"), DatasetValue: dataset.ValueTypeString.ValueFrom("bbb"), }, }, @@ -403,12 +403,12 @@ func TestSealed_Interface(t *testing.T) { { ID: "a", Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFromUnsafe("aaa"), + PropertyValue: ValueTypeString.ValueFrom("aaa"), }, { ID: "b", Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFromUnsafe("aaa"), + PropertyValue: ValueTypeString.ValueFrom("aaa"), DatasetValue: dataset.ValueTypeString.ValueFrom("bbb"), }, }, @@ -467,12 +467,12 @@ func TestSealedItem_Match(t *testing.T) { { ID: "a", Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFromUnsafe("a"), + PropertyValue: ValueTypeString.ValueFrom("a"), }, { ID: "b", Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFromUnsafe("b"), + PropertyValue: ValueTypeString.ValueFrom("b"), DatasetValue: dataset.ValueTypeString.ValueFrom("bbb"), }, }, @@ -527,12 +527,12 @@ func TestSealed_ItemBy(t *testing.T) { { ID: "a", Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFromUnsafe("a"), + PropertyValue: ValueTypeString.ValueFrom("a"), }, { ID: "b", Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFromUnsafe("b"), + PropertyValue: ValueTypeString.ValueFrom("b"), DatasetValue: dataset.ValueTypeString.ValueFrom("bbb"), }, }, @@ -548,12 +548,12 @@ func TestSealed_ItemBy(t *testing.T) { { ID: "a", Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFromUnsafe("aaa"), + PropertyValue: ValueTypeString.ValueFrom("aaa"), }, { ID: "b", Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFromUnsafe("aaa"), + PropertyValue: ValueTypeString.ValueFrom("aaa"), DatasetValue: dataset.ValueTypeString.ValueFrom("bbb"), }, }, @@ -575,12 +575,12 @@ func TestSealed_ItemBy(t *testing.T) { { ID: "a", Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFromUnsafe("a"), + PropertyValue: ValueTypeString.ValueFrom("a"), }, { ID: "b", Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFromUnsafe("b"), + PropertyValue: ValueTypeString.ValueFrom("b"), DatasetValue: dataset.ValueTypeString.ValueFrom("bbb"), }, }, @@ -610,12 +610,12 @@ func TestSealed_ItemBy(t *testing.T) { { ID: "a", Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFromUnsafe("a"), + PropertyValue: ValueTypeString.ValueFrom("a"), }, { ID: "b", Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFromUnsafe("b"), + PropertyValue: ValueTypeString.ValueFrom("b"), DatasetValue: dataset.ValueTypeString.ValueFrom("bbb"), }, }, @@ -631,12 +631,12 @@ func TestSealed_ItemBy(t *testing.T) { { ID: "a", Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFromUnsafe("aaa"), + PropertyValue: ValueTypeString.ValueFrom("aaa"), }, { ID: "b", Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFromUnsafe("aaa"), + PropertyValue: ValueTypeString.ValueFrom("aaa"), DatasetValue: dataset.ValueTypeString.ValueFrom("bbb"), }, }, @@ -658,12 +658,12 @@ func TestSealed_ItemBy(t *testing.T) { { ID: "a", Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFromUnsafe("a"), + PropertyValue: ValueTypeString.ValueFrom("a"), }, { ID: "b", Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFromUnsafe("b"), + PropertyValue: ValueTypeString.ValueFrom("b"), DatasetValue: dataset.ValueTypeString.ValueFrom("bbb"), }, }, @@ -693,12 +693,12 @@ func TestSealed_ItemBy(t *testing.T) { { ID: "a", Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFromUnsafe("a"), + PropertyValue: ValueTypeString.ValueFrom("a"), }, { ID: "b", Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFromUnsafe("b"), + PropertyValue: ValueTypeString.ValueFrom("b"), DatasetValue: dataset.ValueTypeString.ValueFrom("bbb"), }, }, @@ -714,12 +714,12 @@ func TestSealed_ItemBy(t *testing.T) { { ID: "a", Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFromUnsafe("aaa"), + PropertyValue: ValueTypeString.ValueFrom("aaa"), }, { ID: "b", Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFromUnsafe("aaa"), + PropertyValue: ValueTypeString.ValueFrom("aaa"), DatasetValue: dataset.ValueTypeString.ValueFrom("bbb"), }, }, @@ -774,12 +774,12 @@ func TestSealed_FieldBy(t *testing.T) { { ID: "a", Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFromUnsafe("a"), + PropertyValue: ValueTypeString.ValueFrom("a"), }, { ID: "b", Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFromUnsafe("b"), + PropertyValue: ValueTypeString.ValueFrom("b"), DatasetValue: dataset.ValueTypeString.ValueFrom("bbb"), }, }, @@ -795,12 +795,12 @@ func TestSealed_FieldBy(t *testing.T) { { ID: "a", Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFromUnsafe("aaa"), + PropertyValue: ValueTypeString.ValueFrom("aaa"), }, { ID: "b", Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFromUnsafe("aaa"), + PropertyValue: ValueTypeString.ValueFrom("aaa"), DatasetValue: dataset.ValueTypeString.ValueFrom("bbb"), }, }, @@ -811,7 +811,7 @@ func TestSealed_FieldBy(t *testing.T) { Expected: &SealedField{ ID: "a", Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFromUnsafe("aaa"), + PropertyValue: ValueTypeString.ValueFrom("aaa"), }, }, { @@ -836,12 +836,12 @@ func TestSealed_FieldBy(t *testing.T) { { ID: "a", Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFromUnsafe("a"), + PropertyValue: ValueTypeString.ValueFrom("a"), }, { ID: "b", Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFromUnsafe("b"), + PropertyValue: ValueTypeString.ValueFrom("b"), DatasetValue: dataset.ValueTypeString.ValueFrom("bbb"), }, }, @@ -857,12 +857,12 @@ func TestSealed_FieldBy(t *testing.T) { { ID: "a", Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFromUnsafe("aaa"), + PropertyValue: ValueTypeString.ValueFrom("aaa"), }, { ID: "b", Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFromUnsafe("aaa"), + PropertyValue: ValueTypeString.ValueFrom("aaa"), DatasetValue: dataset.ValueTypeString.ValueFrom("bbb"), }, }, @@ -873,7 +873,7 @@ func TestSealed_FieldBy(t *testing.T) { Expected: &SealedField{ ID: "a", Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFromUnsafe("aaa"), + PropertyValue: ValueTypeString.ValueFrom("aaa"), }, }, { @@ -898,12 +898,12 @@ func TestSealed_FieldBy(t *testing.T) { { ID: "a", Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFromUnsafe("a"), + PropertyValue: ValueTypeString.ValueFrom("a"), }, { ID: "b", Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFromUnsafe("b"), + PropertyValue: ValueTypeString.ValueFrom("b"), DatasetValue: dataset.ValueTypeString.ValueFrom("bbb"), }, }, @@ -919,12 +919,12 @@ func TestSealed_FieldBy(t *testing.T) { { ID: "a", Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFromUnsafe("aaa"), + PropertyValue: ValueTypeString.ValueFrom("aaa"), }, { ID: "b", Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFromUnsafe("aaa"), + PropertyValue: ValueTypeString.ValueFrom("aaa"), DatasetValue: dataset.ValueTypeString.ValueFrom("bbb"), }, }, @@ -935,7 +935,7 @@ func TestSealed_FieldBy(t *testing.T) { Expected: &SealedField{ ID: "a", Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFromUnsafe("aaa"), + PropertyValue: ValueTypeString.ValueFrom("aaa"), }, }, } diff --git a/pkg/property/value.go b/pkg/property/value.go index e22c1d3c..85be67e8 100644 --- a/pkg/property/value.go +++ b/pkg/property/value.go @@ -1,240 +1,227 @@ package property -type ValueInner interface { - Value() *Value +import ( + "net/url" + "strconv" + + "github.com/reearth/reearth-backend/pkg/dataset" + "github.com/reearth/reearth-backend/pkg/value" +) + +type LatLng = value.LatLng +type LatLngHeight = value.LatLngHeight +type Coordinates = value.Coordinates +type Rect = value.Rect +type Polygon = value.Polygon + +type ValueType value.Type + +var ( + ValueTypeBool = ValueType(value.TypeBool) + ValueTypeNumber = ValueType(value.TypeNumber) + ValueTypeString = ValueType(value.TypeString) + ValueTypeRef = ValueType(value.TypeRef) + ValueTypeURL = ValueType(value.TypeURL) + ValueTypeLatLng = ValueType(value.TypeLatLng) + ValueTypeLatLngHeight = ValueType(value.TypeLatLngHeight) + ValueTypeCoordinates = ValueType(value.TypeCoordinates) + ValueTypePolygon = ValueType(value.TypePolygon) + ValueTypeRect = ValueType(value.TypeRect) +) + +var types = value.TypePropertyMap{ + TypeTypography: typePropertyTypography, + TypeCamera: typePropertyCamera, } -// LatLng _ -type LatLng struct { - Lat float64 `json:"lat" mapstructure:"lat"` - Lng float64 `json:"lng" mapstructure:"lng"` +func (vt ValueType) Valid() bool { + if _, ok := types[value.Type(vt)]; ok { + return true + } + return value.Type(vt).Default() } -// Clone _ -func (l *LatLng) Clone() *LatLng { - if l == nil { +func (vt ValueType) ValueFrom(i interface{}) *Value { + v := value.Type(vt).ValueFrom(i, types) + if v == nil { return nil } - return &LatLng{ - Lat: l.Lat, - Lng: l.Lng, - } + return &Value{v: *v} +} + +type Value struct { + v value.Value } -// LatLngHeight _ -type LatLngHeight struct { - Lat float64 `json:"lat" mapstructure:"lat"` - Lng float64 `json:"lng" mapstructure:"lng"` - Height float64 `json:"height" mapstructure:"height"` +func (v *Value) IsEmpty() bool { + return v == nil || v.v.IsEmpty() } -// Clone _ -func (l *LatLngHeight) Clone() *LatLngHeight { - if l == nil { +func (v *Value) Clone() *Value { + if v == nil { return nil } - return &LatLngHeight{ - Lat: l.Lat, - Lng: l.Lng, - Height: l.Height, + vv := v.v.Clone() + if vv == nil { + return nil } + return &Value{v: *vv} } -// Camera _ -type Camera struct { - Lat float64 `json:"lat" mapstructure:"lat"` - Lng float64 `json:"lng" mapstructure:"lng"` - Altitude float64 `json:"altitude" mapstructure:"altitude"` - Heading float64 `json:"heading" mapstructure:"heading"` - Pitch float64 `json:"pitch" mapstructure:"pitch"` - Roll float64 `json:"roll" mapstructure:"roll"` - FOV float64 `json:"fov" mapstructure:"fov"` +func (v *Value) Type() ValueType { + if v == nil { + return ValueType(value.TypeUnknown) + } + return ValueType(v.v.Type()) } -// Clone _ -func (c *Camera) Clone() *Camera { - if c == nil { +func (v *Value) Value() interface{} { + if v == nil { return nil } - return &Camera{ - Lat: c.Lat, - Lng: c.Lng, - Altitude: c.Altitude, - Heading: c.Heading, - Pitch: c.Pitch, - Roll: c.Roll, - FOV: c.FOV, - } + return v.v.Value() } -// Typography _ -type Typography struct { - FontFamily *string `json:"fontFamily" mapstructure:"fontFamily"` - FontWeight *string `json:"fontWeight" mapstructure:"fontWeight"` - FontSize *int `json:"fontSize" mapstructure:"fontSize"` - Color *string `json:"color" mapstructure:"color"` - TextAlign *TextAlign `json:"textAlign" mapstructure:"textAlign"` - Bold *bool `json:"bold" mapstructure:"bold"` - Italic *bool `json:"italic" mapstructure:"italic"` - Underline *bool `json:"underline" mapstructure:"underline"` +func (v *Value) Interface() interface{} { + if v == nil { + return nil + } + return v.v.Interface() } -// Clone _ -func (t *Typography) Clone() *Typography { - if t == nil { +func (v *Value) ValueBool() *bool { + if v == nil { return nil } - return &Typography{ - FontFamily: t.FontFamily, - FontWeight: t.FontWeight, - FontSize: t.FontSize, - Color: t.Color, - TextAlign: t.TextAlign, - Bold: t.Bold, - Italic: t.Italic, - Underline: t.Underline, - } -} - -// TextAlign _ -type TextAlign string - -const ( - // TextAlignLeft _ - TextAlignLeft TextAlign = "left" - // TextAlignCenter _ - TextAlignCenter TextAlign = "center" - // TextAlignRight _ - TextAlignRight TextAlign = "right" - // TextAlignJustify _ - TextAlignJustify TextAlign = "justify" - // TextAlignJustifyAll _ - TextAlignJustifyAll TextAlign = "justify_all" -) + vv, ok := v.v.ValueBool() + if ok { + return &vv + } + return nil +} -// TextAlignFrom _ -func TextAlignFrom(t string) (TextAlign, bool) { - switch TextAlign(t) { - case TextAlignLeft: - return TextAlignLeft, true - case TextAlignCenter: - return TextAlignCenter, true - case TextAlignRight: - return TextAlignRight, true - case TextAlignJustify: - return TextAlignJustify, true - case TextAlignJustifyAll: - return TextAlignJustifyAll, true - } - return TextAlign(""), false -} - -// TextAlignFromRef _ -func TextAlignFromRef(t *string) *TextAlign { - if t == nil { +func (v *Value) ValueNumber() *float64 { + if v == nil { return nil } - var t2 TextAlign - switch TextAlign(*t) { - case TextAlignLeft: - t2 = TextAlignLeft - case TextAlignCenter: - t2 = TextAlignCenter - case TextAlignRight: - t2 = TextAlignRight - case TextAlignJustify: - t2 = TextAlignJustify - case TextAlignJustifyAll: - t2 = TextAlignJustifyAll - default: - return nil + vv, ok := v.v.ValueNumber() + if ok { + return &vv } - return &t2 + return nil } -// String _ -func (t TextAlign) String() string { - return string(t) +func (v *Value) ValueString() *string { + if v == nil { + return nil + } + vv, ok := v.v.ValueString() + if ok { + return &vv + } + return nil } -// StringRef _ -func (t *TextAlign) StringRef() *string { - if t == nil { +func (v *Value) ValueRef() *string { + if v == nil { return nil } - t2 := string(*t) - return &t2 + vv, ok := v.v.ValueRef() + if ok { + return &vv + } + return nil } -// Coordinates _ -type Coordinates []LatLngHeight - -// CoordinatesFrom generates a new Coordinates from slice such as [lon, lat, alt, lon, lat, alt, ...] -func CoordinatesFrom(coords []float64) Coordinates { - if len(coords) == 0 { +func (v *Value) ValueURL() *url.URL { + if v == nil { return nil } - - r := make([]LatLngHeight, 0, len(coords)/3) - l := LatLngHeight{} - for i, c := range coords { - switch i % 3 { - case 0: - l = LatLngHeight{} - l.Lng = c - case 1: - l.Lat = c - case 2: - l.Height = c - r = append(r, l) - } + vv, ok := v.v.ValueURL() + if ok { + return vv } - - return r + return nil } -// Polygon _ -type Polygon []Coordinates - -// Rect _ -type Rect struct { - West float64 `json:"west" mapstructure:"west"` - South float64 `json:"south" mapstructure:"south"` - East float64 `json:"east" mapstructure:"east"` - North float64 `json:"north" mapstructure:"north"` +func (v *Value) ValueLatLng() *LatLng { + if v == nil { + return nil + } + vv, ok := v.v.ValueLatLng() + if ok { + return &vv + } + return nil } -// Value _ -func (l LatLng) Value() *Value { - return ValueTypeLatLng.ValueFromUnsafe(l) +func (v *Value) ValueLatLngHeight() *LatLngHeight { + if v == nil { + return nil + } + vv, ok := v.v.ValueLatLngHeight() + if ok { + return &vv + } + return nil } -// Value _ -func (l LatLngHeight) Value() *Value { - return ValueTypeLatLngHeight.ValueFromUnsafe(l) +func (v *Value) ValueCoordinates() *Coordinates { + if v == nil { + return nil + } + vv, ok := v.v.ValueCoordinates() + if ok { + return &vv + } + return nil } -// Value _ -func (c Camera) Value() *Value { - return ValueTypeCamera.ValueFromUnsafe(c) +func (v *Value) ValueRect() *Rect { + if v == nil { + return nil + } + vv, ok := v.v.ValueRect() + if ok { + return &vv + } + return nil } -// Value _ -func (t Typography) Value() *Value { - return ValueTypeTypography.ValueFromUnsafe(t) +func (v *Value) ValuePolygon() *Polygon { + if v == nil { + return nil + } + vv, ok := v.v.ValuePolygon() + if ok { + return &vv + } + return nil } -// Value _ -func (t Coordinates) Value() *Value { - return ValueTypeCoordinates.ValueFromUnsafe(t) +func ValueFromStringOrNumber(s string) *Value { + if vint, err := strconv.Atoi(s); err == nil { + return ValueTypeNumber.ValueFrom(vint) + } + + if vfloat64, err := strconv.ParseFloat(s, 64); err == nil { + return ValueTypeNumber.ValueFrom(vfloat64) + } + + if vbool, err := strconv.ParseBool(s); err == nil { + return ValueTypeBool.ValueFrom(vbool) + } + + return ValueTypeString.ValueFrom(s) } -// Value _ -func (t Polygon) Value() *Value { - return ValueTypePolygon.ValueFromUnsafe(t) +func valueFromDataset(v *dataset.Value) *Value { + return ValueType(value.Type(v.Type())).ValueFrom(v.Value()) } -// Value _ -func (t Rect) Value() *Value { - return ValueTypeRect.ValueFromUnsafe(t) +func datasetValueOrPropertyValue(dv *dataset.Value, pv *Value) *Value { + if dv != nil { + return valueFromDataset(dv) + } + return pv } diff --git a/pkg/property/value_camera.go b/pkg/property/value_camera.go new file mode 100644 index 00000000..3e8670b2 --- /dev/null +++ b/pkg/property/value_camera.go @@ -0,0 +1,69 @@ +package property + +import ( + "github.com/mitchellh/mapstructure" + "github.com/reearth/reearth-backend/pkg/value" +) + +var TypeCamera = value.Type("camera") + +type Camera struct { + Lat float64 `json:"lat" mapstructure:"lat"` + Lng float64 `json:"lng" mapstructure:"lng"` + Altitude float64 `json:"altitude" mapstructure:"altitude"` + Heading float64 `json:"heading" mapstructure:"heading"` + Pitch float64 `json:"pitch" mapstructure:"pitch"` + Roll float64 `json:"roll" mapstructure:"roll"` + FOV float64 `json:"fov" mapstructure:"fov"` +} + +func (c *Camera) Clone() *Camera { + if c == nil { + return nil + } + return &Camera{ + Lat: c.Lat, + Lng: c.Lng, + Altitude: c.Altitude, + Heading: c.Heading, + Pitch: c.Pitch, + Roll: c.Roll, + FOV: c.FOV, + } +} + +var typePropertyCamera = value.TypeProperty{ + I2V: func(i interface{}) (interface{}, bool) { + if v, ok := i.(Camera); ok { + return v, true + } + + if v, ok := i.(*Camera); ok { + if v != nil { + return *v, true + } + return nil, false + } + + v := Camera{} + if err := mapstructure.Decode(i, &v); err == nil { + return v, true + } + return nil, false + }, + V2I: func(v interface{}) (interface{}, bool) { + return v, true + }, + Validate: func(i interface{}) bool { + _, ok := i.(Camera) + return ok + }, +} + +func (v *Value) ValueCamera() (vv Camera, ok bool) { + if v == nil { + return + } + vv, ok = v.Value().(Camera) + return +} diff --git a/pkg/property/value_camera_test.go b/pkg/property/value_camera_test.go new file mode 100644 index 00000000..0eb0f535 --- /dev/null +++ b/pkg/property/value_camera_test.go @@ -0,0 +1,51 @@ +package property + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestCamera_Clone(t *testing.T) { + tests := []struct { + Name string + Camera, Expected *Camera + }{ + { + Name: "nil Camera", + }, + { + Name: "cloned", + Camera: &Camera{ + Lat: 1, + Lng: 1, + Altitude: 2, + Heading: 4, + Pitch: 5, + Roll: 6, + FOV: 7, + }, + Expected: &Camera{ + Lat: 1, + Lng: 1, + Altitude: 2, + Heading: 4, + Pitch: 5, + Roll: 6, + FOV: 7, + }, + }, + } + + for _, tc := range tests { + tc := tc + t.Run(tc.Name, func(tt *testing.T) { + tt.Parallel() + res := tc.Camera.Clone() + assert.Equal(tt, tc.Expected, res) + if tc.Expected != nil { + assert.NotSame(tt, tc.Expected, res) + } + }) + } +} diff --git a/pkg/property/value_converter.go b/pkg/property/value_converter.go deleted file mode 100644 index d81937ae..00000000 --- a/pkg/property/value_converter.go +++ /dev/null @@ -1,41 +0,0 @@ -package property - -import "github.com/reearth/reearth-backend/pkg/dataset" - -func valueFromDataset(v *dataset.Value) (*Value, bool) { - v2 := v.Value() - switch v3 := v2.(type) { - case *dataset.LatLng: - return ValueTypeLatLng.ValueFrom(LatLng{ - Lat: v3.Lat, - Lng: v3.Lng, - }) - case *dataset.LatLngHeight: - return ValueTypeLatLngHeight.ValueFrom(LatLngHeight{ - Lat: v3.Lat, - Lng: v3.Lng, - Height: v3.Height, - }) - } - return valueTypeFromDataset(v.Type()).ValueFrom(v2) -} - -func valueTypeFromDataset(v dataset.ValueType) ValueType { - switch v { - case dataset.ValueTypeBool: - return ValueTypeBool - case dataset.ValueTypeLatLng: - return ValueTypeLatLng - case dataset.ValueTypeLatLngHeight: - return ValueTypeLatLngHeight - case dataset.ValueTypeNumber: - return ValueTypeNumber - case dataset.ValueTypeRef: - return ValueTypeRef - case dataset.ValueTypeString: - return ValueTypeString - case dataset.ValueTypeURL: - return ValueTypeURL - } - return ValueType("") -} diff --git a/pkg/property/value_converter_test.go b/pkg/property/value_converter_test.go deleted file mode 100644 index 87abbdf7..00000000 --- a/pkg/property/value_converter_test.go +++ /dev/null @@ -1,120 +0,0 @@ -package property - -import ( - "testing" - - "github.com/reearth/reearth-backend/pkg/dataset" - "github.com/stretchr/testify/assert" -) - -func TestValueFromDataset(t *testing.T) { - testCases := []struct { - Name string - Input *dataset.Value - Expected struct { - V *Value - Ok bool - } - }{ - { - Name: "latlng", - Input: dataset.ValueFrom(dataset.LatLng{ - Lat: 10, - Lng: 12, - }), - Expected: struct { - V *Value - Ok bool - }{ - V: ValueTypeLatLng.ValueFromUnsafe(LatLng{ - Lat: 10, - Lng: 12, - }), - Ok: true, - }, - }, - { - Name: "LatLngHeight", - Input: dataset.ValueFrom(dataset.LatLngHeight{ - Lat: 10, - Lng: 12, - Height: 14, - }), - Expected: struct { - V *Value - Ok bool - }{ - V: ValueTypeLatLngHeight.ValueFromUnsafe(LatLngHeight{ - Lat: 10, - Lng: 12, - Height: 14, - }), - Ok: true, - }, - }, - } - for _, tc := range testCases { - t.Run(tc.Name, func(tt *testing.T) { - tt.Parallel() - res, ok := valueFromDataset(tc.Input) - assert.Equal(tt, tc.Expected.V, res) - assert.Equal(tt, tc.Expected.Ok, ok) - }) - } -} - -func TestValueTypeFromDataset(t *testing.T) { - testCases := []struct { - Name string - Input dataset.ValueType - Expected ValueType - }{ - { - Name: "latlng", - Input: dataset.ValueTypeLatLng, - Expected: ValueTypeLatLng, - }, - { - Name: "latlngheight", - Input: dataset.ValueTypeLatLngHeight, - Expected: ValueTypeLatLngHeight, - }, - { - Name: "string", - Input: dataset.ValueTypeString, - Expected: ValueTypeString, - }, - { - Name: "bool", - Input: dataset.ValueTypeBool, - Expected: ValueTypeBool, - }, - { - Name: "ref", - Input: dataset.ValueTypeRef, - Expected: ValueTypeRef, - }, - { - Name: "url", - Input: dataset.ValueTypeURL, - Expected: ValueTypeURL, - }, - { - Name: "number", - Input: dataset.ValueTypeNumber, - Expected: ValueTypeNumber, - }, - { - Name: "undefined", - Input: dataset.ValueType("xxx"), - Expected: ValueType(""), - }, - } - for _, tc := range testCases { - t.Run(tc.Name, func(tt *testing.T) { - tt.Parallel() - res := valueTypeFromDataset(tc.Input) - assert.Equal(tt, tc.Expected, res) - }) - } -} diff --git a/pkg/property/value_test.go b/pkg/property/value_test.go index ce09faa2..a51528a3 100644 --- a/pkg/property/value_test.go +++ b/pkg/property/value_test.go @@ -3,225 +3,53 @@ package property import ( "testing" + "github.com/reearth/reearth-backend/pkg/dataset" "github.com/stretchr/testify/assert" ) -func getStrRef(i string) *string { - return &i -} -func getBoolRef(i bool) *bool { - return &i -} - -func TestLatLng_Clone(t *testing.T) { - testCases := []struct { - Name string - LL, Expected *LatLng - }{ - { - Name: "nil latlng", - }, - { - Name: "cloned", - LL: &LatLng{ - Lat: 10, - Lng: 11, - }, - Expected: &LatLng{ - Lat: 10, - Lng: 11, - }, - }, - } - for _, tc := range testCases { - t.Run(tc.Name, func(tt *testing.T) { - tt.Parallel() - res := tc.LL.Clone() - assert.Equal(tt, tc.Expected, res) - }) - } -} - -func TestLatLngHeight_Clone(t *testing.T) { - testCases := []struct { - Name string - LL, Expected *LatLngHeight - }{ - { - Name: "nil LatLngHeight", - }, - { - Name: "cloned", - LL: &LatLngHeight{ - Lat: 10, - Lng: 11, - Height: 12, - }, - Expected: &LatLngHeight{ - Lat: 10, - Lng: 11, - Height: 12, - }, - }, - } - for _, tc := range testCases { - t.Run(tc.Name, func(tt *testing.T) { - tt.Parallel() - res := tc.LL.Clone() - assert.Equal(tt, tc.Expected, res) - }) - } -} - -func TestCamera_Clone(t *testing.T) { - testCases := []struct { - Name string - Camera, Expected *Camera - }{ - { - Name: "nil Camera", - }, - { - Name: "cloned", - Camera: &Camera{ - Lat: 1, - Lng: 1, - Altitude: 2, - Heading: 4, - Pitch: 5, - Roll: 6, - FOV: 7, - }, - Expected: &Camera{ - Lat: 1, - Lng: 1, - Altitude: 2, - Heading: 4, - Pitch: 5, - Roll: 6, - FOV: 7, - }, - }, - } - for _, tc := range testCases { - t.Run(tc.Name, func(tt *testing.T) { - tt.Parallel() - res := tc.Camera.Clone() - assert.Equal(tt, tc.Expected, res) - }) - } -} - -func TestTypography_Clone(t *testing.T) { - - i := 10 - - testCases := []struct { - Name string - Typography, Expected *Typography - }{ - { - Name: "nil typography", - }, - { - Name: "cloned", - Typography: &Typography{ - FontFamily: getStrRef("x"), - FontWeight: getStrRef("b"), - FontSize: &i, - Color: getStrRef("red"), - TextAlign: TextAlignFromRef(getStrRef(TextAlignCenter.String())), - Bold: getBoolRef(true), - Italic: getBoolRef(false), - Underline: getBoolRef(true), - }, - Expected: &Typography{ - FontFamily: getStrRef("x"), - FontWeight: getStrRef("b"), - FontSize: &i, - Color: getStrRef("red"), - TextAlign: TextAlignFromRef(getStrRef("center")), - Bold: getBoolRef(true), - Italic: getBoolRef(false), - Underline: getBoolRef(true), - }, - }, - } - for _, tc := range testCases { - t.Run(tc.Name, func(tt *testing.T) { - tt.Parallel() - res := tc.Typography.Clone() - assert.Equal(tt, tc.Expected, res) - }) - } -} - -func TestTextAlignFrom(t *testing.T) { +func TestValueFromDataset(t *testing.T) { testCases := []struct { Name string + Input *dataset.Value Expected struct { - TA TextAlign - Bool bool + V *Value + Ok bool } }{ { - Name: "left", - Expected: struct { - TA TextAlign - Bool bool - }{ - TA: TextAlignLeft, - Bool: true, - }, - }, - { - Name: "right", - Expected: struct { - TA TextAlign - Bool bool - }{ - TA: TextAlignRight, - Bool: true, - }, - }, - { - Name: "center", - Expected: struct { - TA TextAlign - Bool bool - }{ - TA: TextAlignCenter, - Bool: true, - }, - }, - { - Name: "justify", - Expected: struct { - TA TextAlign - Bool bool - }{ - TA: TextAlignJustify, - Bool: true, - }, - }, - { - Name: "justify_all", + Name: "latlng", + Input: dataset.ValueTypeLatLng.ValueFrom(dataset.LatLng{ + Lat: 10, + Lng: 12, + }), Expected: struct { - TA TextAlign - Bool bool + V *Value + Ok bool }{ - TA: TextAlignJustifyAll, - Bool: true, + V: ValueTypeLatLng.ValueFrom(LatLng{ + Lat: 10, + Lng: 12, + }), + Ok: true, }, }, { - Name: "undefined", + Name: "LatLngHeight", + Input: dataset.ValueTypeLatLngHeight.ValueFrom(dataset.LatLngHeight{ + Lat: 10, + Lng: 12, + Height: 14, + }), Expected: struct { - TA TextAlign - Bool bool + V *Value + Ok bool }{ - TA: TextAlign(""), - Bool: false, + V: ValueTypeLatLngHeight.ValueFrom(LatLngHeight{ + Lat: 10, + Lng: 12, + Height: 14, + }), + Ok: true, }, }, } @@ -230,125 +58,7 @@ func TestTextAlignFrom(t *testing.T) { tc := tc t.Run(tc.Name, func(tt *testing.T) { tt.Parallel() - res, ok := TextAlignFrom(tc.Name) - assert.Equal(tt, tc.Expected.TA, res) - assert.Equal(tt, tc.Expected.Bool, ok) + assert.Equal(tt, tc.Expected.V, valueFromDataset(tc.Input)) }) } } - -func TestTextAlignFromRef(t *testing.T) { - ja := TextAlignJustifyAll - j := TextAlignJustify - c := TextAlignCenter - l := TextAlignLeft - r := TextAlignRight - testCases := []struct { - Name string - Input *string - Expected *TextAlign - }{ - { - Name: "left", - Input: getStrRef("left"), - Expected: &l, - }, - { - Name: "right", - Input: getStrRef("right"), - Expected: &r, - }, - { - Name: "center", - Input: getStrRef("center"), - Expected: &c, - }, - { - Name: "justify", - Input: getStrRef("justify"), - Expected: &j, - }, - { - Name: "justify_all", - Input: getStrRef("justify_all"), - Expected: &ja, - }, - { - Name: "undefined", - Input: getStrRef("undefined"), - }, - { - Name: "nil input", - }, - } - - for _, tc := range testCases { - tc := tc - t.Run(tc.Name, func(tt *testing.T) { - tt.Parallel() - res := TextAlignFromRef(tc.Input) - assert.Equal(tt, tc.Expected, res) - }) - } -} - -func TestTextAlign_StringRef(t *testing.T) { - var ta *TextAlign - assert.Nil(t, ta.StringRef()) -} - -func TestValue(t *testing.T) { - ll := LatLng{ - Lat: 1, - Lng: 2, - } - assert.True(t, ValueTypeLatLng.ValidateValue(ll.Value())) - - llh := LatLngHeight{ - Lat: 1, - Lng: 2, - Height: 3, - } - assert.True(t, ValueTypeLatLngHeight.ValidateValue(llh.Value())) - - ca := Camera{ - Lat: 1, - Lng: 2, - Altitude: 3, - Heading: 4, - Pitch: 5, - Roll: 6, - FOV: 7, - } - assert.True(t, ValueTypeCamera.ValidateValue(ca.Value())) - - ty := Typography{ - FontFamily: getStrRef("x"), - FontWeight: getStrRef("b"), - FontSize: nil, - Color: getStrRef("red"), - TextAlign: TextAlignFromRef(getStrRef(TextAlignCenter.String())), - Bold: getBoolRef(true), - Italic: getBoolRef(false), - Underline: getBoolRef(true), - } - assert.True(t, ValueTypeTypography.ValidateValue(ty.Value())) - - co := Coordinates{ - llh, - } - assert.True(t, ValueTypeCoordinates.ValidateValue(co.Value())) - - po := Polygon{ - co, - } - assert.True(t, ValueTypePolygon.ValidateValue(po.Value())) - - rc := Rect{ - West: 10, - South: 3, - East: 5, - North: 2, - } - assert.True(t, ValueTypeRect.ValidateValue(rc.Value())) -} diff --git a/pkg/property/value_type.go b/pkg/property/value_type.go deleted file mode 100644 index 1b0ce23f..00000000 --- a/pkg/property/value_type.go +++ /dev/null @@ -1,603 +0,0 @@ -package property - -import ( - "encoding/json" - "fmt" - "net/url" - - "github.com/mitchellh/mapstructure" - "github.com/reearth/reearth-backend/pkg/id" -) - -// ValueType _ -type ValueType string - -const ( - // ValueTypeBool _ - ValueTypeBool ValueType = "bool" - // ValueTypeNumber _ - ValueTypeNumber ValueType = "number" - // ValueTypeString _ - ValueTypeString ValueType = "string" - // ValueTypeRef _ - ValueTypeRef ValueType = "ref" - // ValueTypeURL _ - ValueTypeURL ValueType = "url" - // ValueTypeLatLng _ - ValueTypeLatLng ValueType = "latlng" - // ValueTypeLatLngHeight _ - ValueTypeLatLngHeight ValueType = "latlngheight" - // ValueTypeCamera _ - ValueTypeCamera ValueType = "camera" - // ValueTypeTypography _ - ValueTypeTypography ValueType = "typography" - // ValueTypeCoordinates _ - ValueTypeCoordinates ValueType = "coordinates" - // ValueTypePolygon - ValueTypePolygon ValueType = "polygon" - // ValueTypeRect - ValueTypeRect ValueType = "rect" -) - -// ValueTypeFrom _ -func ValueTypeFrom(t string) (ValueType, bool) { - switch ValueType(t) { - case ValueTypeBool: - return ValueTypeBool, true - case ValueTypeNumber: - return ValueTypeNumber, true - case ValueTypeString: - return ValueTypeString, true - case ValueTypeRef: - return ValueTypeRef, true - case ValueTypeURL: - return ValueTypeURL, true - case ValueTypeLatLng: - return ValueTypeLatLng, true - case ValueTypeLatLngHeight: - return ValueTypeLatLngHeight, true - case ValueTypeCamera: - return ValueTypeCamera, true - case ValueTypeTypography: - return ValueTypeTypography, true - case ValueTypeCoordinates: - return ValueTypeCoordinates, true - case ValueTypePolygon: - return ValueTypePolygon, true - case ValueTypeRect: - return ValueTypeRect, true - } - return ValueType(""), false -} - -// Validate _ -func (t ValueType) Validate() (ValueType, bool) { - switch t { - case ValueTypeBool: - fallthrough - case ValueTypeNumber: - fallthrough - case ValueTypeString: - fallthrough - case ValueTypeRef: - fallthrough - case ValueTypeURL: - fallthrough - case ValueTypeLatLng: - fallthrough - case ValueTypeLatLngHeight: - fallthrough - case ValueTypeCamera: - fallthrough - case ValueTypeTypography: - fallthrough - case ValueTypeCoordinates: - fallthrough - case ValueTypePolygon: - fallthrough - case ValueTypeRect: - return t, true - } - return t, false -} - -// Value _ -type Value struct { - v interface{} - t ValueType -} - -// IsEmpty _ -func (v *Value) IsEmpty() bool { - return v == nil || v.v == nil -} - -// Clone _ -func (v *Value) Clone() *Value { - if v == nil { - return nil - } - return v.t.ValueFromUnsafe(v.v) -} - -// Value _ -func (v *Value) Value() interface{} { - if v == nil { - return nil - } - return v.v -} - -// ValueBool _ -func (v *Value) ValueBool() (vv bool, ok bool) { - if v == nil { - return - } - vv, ok = v.v.(bool) - return -} - -// ValueNumber _ -func (v *Value) ValueNumber() (vv float64, ok bool) { - if v == nil { - return - } - vv, ok = v.v.(float64) - return -} - -// ValueString _ -func (v *Value) ValueString() (vv string, ok bool) { - if v == nil { - return - } - vv, ok = v.v.(string) - return -} - -// ValueRef _ -func (v *Value) ValueRef() (vv id.ID, ok bool) { - if v == nil { - return - } - vv, ok = v.v.(id.ID) - return -} - -// ValueURL _ -func (v *Value) ValueURL() (vv *url.URL, ok bool) { - if v == nil { - return - } - vv, ok = v.v.(*url.URL) - return -} - -// ValueLatLng _ -func (v *Value) ValueLatLng() (vv LatLng, ok bool) { - if v == nil { - return - } - vv, ok = v.v.(LatLng) - return -} - -// ValueLatLngHeight _ -func (v *Value) ValueLatLngHeight() (vv LatLngHeight, ok bool) { - if v == nil { - return - } - vv, ok = v.v.(LatLngHeight) - return -} - -// ValueCamera _ -func (v *Value) ValueCamera() (vv Camera, ok bool) { - if v == nil { - return - } - vv, ok = v.v.(Camera) - return -} - -// ValueTypography _ -func (v *Value) ValueTypography() (vv Typography, ok bool) { - if v == nil { - return - } - vv, ok = v.v.(Typography) - return -} - -// ValueCoordinates _ -func (v *Value) ValueCoordinates() (vv Coordinates, ok bool) { - if v == nil { - return - } - vv, ok = v.v.(Coordinates) - return -} - -// ValuePolygon _ -func (v *Value) ValuePolygon() (vv Polygon, ok bool) { - if v == nil { - return - } - vv, ok = v.v.(Polygon) - return -} - -// ValueRect _ -func (v *Value) ValueRect() (vv Rect, ok bool) { - if v == nil { - return - } - vv, ok = v.v.(Rect) - return -} - -// Type _ -func (v *Value) Type() ValueType { - if v == nil { - return ValueType("") - } - return v.t -} - -// ValueFromUnsafe _ -func (t ValueType) ValueFromUnsafe(v interface{}) *Value { - v2, _ := t.ValueFrom(v) - return v2 -} - -func (t ValueType) MustBeValue(v interface{}) *Value { - v2, ok := t.ValueFrom(v) - if !ok { - panic("incompatible value for property value") - } - return v2 -} - -// ValueFrom _ -func (t ValueType) ValueFrom(v interface{}) (*Value, bool) { - if t == "" { - return nil, false - } - if v == nil { - return nil, true - } - - switch t { - case ValueTypeBool: - if v2, ok := v.(bool); ok { - return &Value{v: v2, t: ValueTypeBool}, true - } - case ValueTypeNumber: - if v2, ok := v.(json.Number); ok { - if v3, err := v2.Float64(); err == nil { - return &Value{v: v3, t: ValueTypeNumber}, true - } - } else if v2, ok := v.(float32); ok { - return &Value{v: v2, t: ValueTypeNumber}, true - } else if v2, ok := v.(float64); ok { - return &Value{v: v2, t: ValueTypeNumber}, true - } else if v2, ok := v.(int); ok { - return &Value{v: float64(v2), t: ValueTypeNumber}, true - } else if v2, ok := v.(int8); ok { - return &Value{v: float64(v2), t: ValueTypeNumber}, true - } else if v2, ok := v.(int16); ok { - return &Value{v: float64(v2), t: ValueTypeNumber}, true - } else if v2, ok := v.(int32); ok { - return &Value{v: float64(v2), t: ValueTypeNumber}, true - } else if v2, ok := v.(int64); ok { - return &Value{v: float64(v2), t: ValueTypeNumber}, true - } else if v2, ok := v.(uint); ok { - return &Value{v: float64(v2), t: ValueTypeNumber}, true - } else if v2, ok := v.(uint8); ok { - return &Value{v: float64(v2), t: ValueTypeNumber}, true - } else if v2, ok := v.(uint16); ok { - return &Value{v: float64(v2), t: ValueTypeNumber}, true - } else if v2, ok := v.(uint32); ok { - return &Value{v: float64(v2), t: ValueTypeNumber}, true - } else if v2, ok := v.(uint64); ok { - return &Value{v: float64(v2), t: ValueTypeNumber}, true - } - case ValueTypeString: - if v2, ok := v.(string); ok { - return &Value{v: v2, t: ValueTypeString}, true - } - case ValueTypeRef: - if v2, ok := v.(id.ID); ok { - return &Value{v: v2, t: ValueTypeRef}, true - } else if v2, ok := v.(string); ok { - if id, err := id.NewIDWith(v2); err == nil { - return &Value{v: id, t: ValueTypeRef}, true - } - } - case ValueTypeURL: - if v2, ok := v.(*url.URL); ok { - if v2 == nil { - return nil, false - } - return &Value{v: v2, t: ValueTypeURL}, true - } else if v2, ok := v.(string); ok { - if u, err := url.Parse(v2); err == nil { - return &Value{v: u, t: ValueTypeURL}, true - } - } - case ValueTypeLatLng: - if v2, ok := v.(LatLng); ok { - return &Value{v: v2, t: ValueTypeLatLng}, true - } else if v2, ok := v.(*LatLng); ok { - if v2 == nil { - return nil, false - } - return &Value{v: *v2, t: ValueTypeLatLng}, true - } - v2 := LatLng{} - if err := mapstructure.Decode(v, &v2); err != nil { - return nil, false - } - return &Value{v: v2, t: ValueTypeLatLng}, true - case ValueTypeLatLngHeight: - if v2, ok := v.(LatLngHeight); ok { - return &Value{v: v2, t: ValueTypeLatLngHeight}, true - } else if v2, ok := v.(*LatLngHeight); ok { - if v2 == nil { - return nil, false - } - return &Value{v: *v2, t: ValueTypeLatLngHeight}, true - } - v2 := LatLngHeight{} - if err := mapstructure.Decode(v, &v2); err != nil { - return nil, false - } - return &Value{v: v2, t: ValueTypeLatLngHeight}, true - case ValueTypeCamera: - if v2, ok := v.(Camera); ok { - return &Value{v: v2, t: ValueTypeCamera}, true - } else if v2, ok := v.(*Camera); ok { - if v2 == nil { - return nil, false - } - return &Value{v: *v2, t: ValueTypeCamera}, true - } - v2 := Camera{} - if err := mapstructure.Decode(v, &v2); err != nil { - return nil, false - } - return &Value{v: v2, t: ValueTypeCamera}, true - case ValueTypeTypography: - if v2, ok := v.(Typography); ok { - return &Value{v: v2, t: ValueTypeTypography}, true - } else if v2, ok := v.(*Typography); ok { - if v2 == nil { - return nil, false - } - return &Value{v: *v2, t: ValueTypeTypography}, true - } - v2 := Typography{} - if err := mapstructure.Decode(v, &v2); err != nil { - return nil, false - } - return &Value{v: v2, t: ValueTypeTypography}, true - case ValueTypeCoordinates: - if v2, ok := v.(Coordinates); ok { - return &Value{v: v2, t: ValueTypeCoordinates}, true - } else if v2, ok := v.(*Coordinates); ok { - if v2 == nil { - return nil, false - } - return &Value{v: *v2, t: ValueTypeCoordinates}, true - } else if v2, ok := v.([]float64); ok { - if v2 == nil { - return nil, false - } - return &Value{v: CoordinatesFrom(v2), t: ValueTypeCoordinates}, true - } - - v2 := []float64{} - if err := mapstructure.Decode(v, &v2); err == nil { - return &Value{v: CoordinatesFrom(v2), t: ValueTypeCoordinates}, true - } - - v3 := Coordinates{} - if err := mapstructure.Decode(v, &v3); err != nil { - return nil, false - } - return &Value{v: v3, t: ValueTypeCoordinates}, true - case ValueTypePolygon: - if v2, ok := v.(Polygon); ok { - return &Value{v: v2, t: ValueTypePolygon}, true - } else if v2, ok := v.(*Polygon); ok { - if v2 == nil { - return nil, false - } - return &Value{v: *v2, t: ValueTypePolygon}, true - } - v2 := Polygon{} - if err := mapstructure.Decode(v, &v2); err != nil { - return nil, false - } - return &Value{v: v2, t: ValueTypePolygon}, true - case ValueTypeRect: - if v2, ok := v.(Rect); ok { - return &Value{v: v2, t: ValueTypeRect}, true - } else if v2, ok := v.(*Rect); ok { - if v2 == nil { - return nil, false - } - return &Value{v: *v2, t: ValueTypeRect}, true - } - v2 := Rect{} - if err := mapstructure.Decode(v, &v2); err != nil { - return nil, false - } - return &Value{v: v2, t: ValueTypeRect}, true - } - return nil, false -} - -// ValidateValue _ -func (t ValueType) ValidateValue(v *Value) bool { - if v == nil { - return true - } - vv := v.Value() - if vv == nil { - return true - } - switch t { - case ValueTypeBool: - if _, ok := vv.(bool); ok { - return true - } - case ValueTypeNumber: - if _, ok := vv.(float64); ok { - return true - } - case ValueTypeString: - if _, ok := vv.(string); ok { - return true - } - case ValueTypeRef: - if _, ok := vv.(id.ID); ok { - return true - } - case ValueTypeURL: - if _, ok := vv.(*url.URL); ok { - return true - } - case ValueTypeLatLng: - if _, ok := vv.(LatLng); ok { - return true - } - case ValueTypeLatLngHeight: - if _, ok := vv.(LatLngHeight); ok { - return true - } - case ValueTypeCamera: - if _, ok := vv.(Camera); ok { - return true - } - case ValueTypeTypography: - if _, ok := vv.(Typography); ok { - return true - } - case ValueTypeCoordinates: - if _, ok := vv.(Coordinates); ok { - return true - } - case ValueTypePolygon: - if _, ok := vv.(Polygon); ok { - return true - } - case ValueTypeRect: - if _, ok := vv.(Rect); ok { - return true - } - } - return false -} - -func (t *ValueType) MarshalJSON() ([]byte, error) { - if t == nil { - return nil, nil - } - return json.Marshal(string(*t)) -} - -func (t *ValueType) UnmarshalJSON(bs []byte) (err error) { - var vtstr string - if err = json.Unmarshal(bs, &vtstr); err != nil { - return - } - var ok bool - *t, ok = ValueTypeFrom(vtstr) - if !ok { - return fmt.Errorf("invalid property value type: %s", vtstr) - } - return -} - -func (t *ValueType) MarshalText() ([]byte, error) { - if t == nil { - return nil, nil - } - return []byte(*t), nil -} - -func (t *ValueType) UnmarshalText(text []byte) (err error) { - var ok bool - *t, ok = ValueTypeFrom(string(text)) - if !ok { - return fmt.Errorf("invalid property value type: %s", text) - } - return -} - -// Interface converts the value into generic representation -func (v *Value) Interface() interface{} { - if v == nil { - return nil - } - switch v2 := v.Value().(type) { - case bool: - return v2 - case float64: - return v2 - case string: - return v2 - case id.ID: - return v2.String() - case *url.URL: - return v2.String() - case LatLng: - var v3 map[string]interface{} - if err := mapstructure.Decode(&v2, &v3); err != nil { - return nil - } - return v3 - case LatLngHeight: - var v3 map[string]interface{} - if err := mapstructure.Decode(&v2, &v3); err != nil { - return nil - } - return v3 - case Camera: - var v3 map[string]interface{} - if err := mapstructure.Decode(&v2, &v3); err != nil { - return nil - } - return v3 - case Typography: - var v3 map[string]interface{} - if err := mapstructure.Decode(&v2, &v3); err != nil { - return nil - } - return v3 - case Coordinates: - var v3 []map[string]interface{} - if err := mapstructure.Decode(&v2, &v3); err != nil { - return nil - } - return v3 - case Polygon: - var v3 [][]map[string]interface{} - if err := mapstructure.Decode(&v2, &v3); err != nil { - return nil - } - return v3 - case Rect: - var v3 map[string]interface{} - if err := mapstructure.Decode(&v2, &v3); err != nil { - return nil - } - return v3 - } - return nil -} - -func (v *Value) MarshalJSON() ([]byte, error) { - return json.Marshal(v.Interface()) -} diff --git a/pkg/property/value_type_test.go b/pkg/property/value_type_test.go deleted file mode 100644 index 5f003f68..00000000 --- a/pkg/property/value_type_test.go +++ /dev/null @@ -1,1426 +0,0 @@ -package property - -import ( - "encoding/json" - "net/url" - "strconv" - "testing" - - "github.com/reearth/reearth-backend/pkg/id" - "github.com/stretchr/testify/assert" -) - -func TestValueTypeFrom(t *testing.T) { - testCases := []struct { - Name, Input string - Expected struct { - V ValueType - B bool - } - }{ - { - Name: "bool", - Input: "bool", - Expected: struct { - V ValueType - B bool - }{ - V: ValueTypeBool, - B: true, - }, - }, - { - Name: "number", - Input: "number", - Expected: struct { - V ValueType - B bool - }{ - V: ValueTypeNumber, - B: true, - }, - }, - { - Name: "ref", - Input: "ref", - Expected: struct { - V ValueType - B bool - }{ - V: ValueTypeRef, - B: true, - }, - }, - { - Name: "url", - Input: "url", - Expected: struct { - V ValueType - B bool - }{ - V: ValueTypeURL, - B: true, - }, - }, - { - Name: "string", - Input: "string", - Expected: struct { - V ValueType - B bool - }{ - V: ValueTypeString, - B: true, - }, - }, { - Name: "camera", - Input: "camera", - Expected: struct { - V ValueType - B bool - }{ - V: ValueTypeCamera, - B: true, - }, - }, - { - Name: "bool", - Input: "bool", - Expected: struct { - V ValueType - B bool - }{ - V: ValueTypeBool, - B: true, - }, - }, - { - Name: "LatLngHeight", - Input: "latlngheight", - Expected: struct { - V ValueType - B bool - }{ - V: ValueTypeLatLngHeight, - B: true, - }, - }, - { - Name: "latlng", - Input: "latlng", - Expected: struct { - V ValueType - B bool - }{ - V: ValueTypeLatLng, - B: true, - }, - }, - { - Name: "polygon", - Input: "polygon", - Expected: struct { - V ValueType - B bool - }{ - V: ValueTypePolygon, - B: true, - }, - }, - { - Name: "rect", - Input: "rect", - Expected: struct { - V ValueType - B bool - }{ - V: ValueTypeRect, - B: true, - }, - }, - { - Name: "coordinates", - Input: "coordinates", - Expected: struct { - V ValueType - B bool - }{ - V: ValueTypeCoordinates, - B: true, - }, - }, - { - Name: "typography", - Input: "typography", - Expected: struct { - V ValueType - B bool - }{ - V: ValueTypeTypography, - B: true, - }, - }, - { - Name: "unknown", - Input: "", - Expected: struct { - V ValueType - B bool - }{ - V: ValueType(""), - B: false, - }, - }, - } - - for _, tc := range testCases { - tc := tc - t.Run(tc.Name, func(tt *testing.T) { - tt.Parallel() - v, b := ValueTypeFrom(tc.Input) - assert.Equal(tt, tc.Expected.V, v) - assert.Equal(tt, tc.Expected.B, b) - - v2, b2 := v.Validate() - assert.Equal(tt, tc.Expected.V, v2) - assert.Equal(tt, tc.Expected.B, b2) - }) - } -} - -func TestValue_IsEmpty(t *testing.T) { - var v *Value - assert.True(t, v.IsEmpty()) -} - -func TestValue_Clone(t *testing.T) { - var v *Value - assert.Nil(t, v.Clone()) - v, _ = ValueTypeBool.ValueFrom(true) - assert.Equal(t, v, v.Clone()) -} - -func TestValue_Value(t *testing.T) { - var v *Value - assert.Nil(t, v.Value()) - v, _ = ValueTypeBool.ValueFrom(true) - assert.Equal(t, true, v.Value()) -} - -func TestValue_Type(t *testing.T) { - var v *Value - assert.Equal(t, ValueType(""), v.Type()) - v, _ = ValueTypeBool.ValueFrom(true) - assert.Equal(t, ValueTypeBool, v.Type()) -} - -func TestValue_ValueBool(t *testing.T) { - testCases := []struct { - Name string - V *Value - Expected struct { - V, Ok bool - } - }{ - { - Name: "nil value", - }, - { - Name: "success", - V: ValueTypeBool.ValueFromUnsafe(true), - Expected: struct { - V, Ok bool - }{ - V: true, - Ok: true, - }, - }, - } - - for _, tc := range testCases { - tc := tc - t.Run(tc.Name, func(tt *testing.T) { - tt.Parallel() - v, ok := tc.V.ValueBool() - assert.Equal(tt, tc.Expected.V, v) - assert.Equal(tt, tc.Expected.Ok, ok) - }) - } -} - -func TestValue_ValueString(t *testing.T) { - testCases := []struct { - Name string - V *Value - Expected struct { - V string - Ok bool - } - }{ - { - Name: "nil value", - }, - { - Name: "success", - V: ValueTypeString.ValueFromUnsafe("xxx"), - Expected: struct { - V string - Ok bool - }{V: "xxx", Ok: true}, - }, - } - - for _, tc := range testCases { - tc := tc - t.Run(tc.Name, func(tt *testing.T) { - tt.Parallel() - v, ok := tc.V.ValueString() - assert.Equal(tt, tc.Expected.V, v) - assert.Equal(tt, tc.Expected.Ok, ok) - }) - } -} - -func TestValue_ValueNumber(t *testing.T) { - testCases := []struct { - Name string - V *Value - Expected struct { - V float64 - Ok bool - } - }{ - { - Name: "nil value", - }, - { - Name: "success", - V: ValueTypeNumber.ValueFromUnsafe(5.5), - Expected: struct { - V float64 - Ok bool - }{V: 5.5, Ok: true}, - }, - } - - for _, tc := range testCases { - tc := tc - t.Run(tc.Name, func(tt *testing.T) { - tt.Parallel() - v, ok := tc.V.ValueNumber() - assert.Equal(tt, tc.Expected.V, v) - assert.Equal(tt, tc.Expected.Ok, ok) - }) - } -} - -func TestValue_ValueLatLng(t *testing.T) { - testCases := []struct { - Name string - V *Value - Expected struct { - V LatLng - Ok bool - } - }{ - { - Name: "nil value", - }, - { - Name: "success", - V: ValueTypeLatLng.ValueFromUnsafe(map[string]interface{}{ - "Lat": 1, - "Lng": 2, - }), - Expected: struct { - V LatLng - Ok bool - }{ - V: LatLng{ - Lat: 1, - Lng: 2, - }, - Ok: true, - }, - }, - } - - for _, tc := range testCases { - tc := tc - t.Run(tc.Name, func(tt *testing.T) { - tt.Parallel() - v, ok := tc.V.ValueLatLng() - assert.Equal(tt, tc.Expected.V, v) - assert.Equal(tt, tc.Expected.Ok, ok) - }) - } -} - -func TestValue_ValueLatLngHeight(t *testing.T) { - testCases := []struct { - Name string - V *Value - Expected struct { - V LatLngHeight - Ok bool - } - }{ - { - Name: "nil value", - }, - { - Name: "success", - V: ValueTypeLatLngHeight.ValueFromUnsafe(map[string]interface{}{ - "Lat": 1, - "Lng": 2, - "Height": 22, - }), - Expected: struct { - V LatLngHeight - Ok bool - }{ - V: LatLngHeight{ - Lat: 1, - Lng: 2, - Height: 22, - }, - Ok: true, - }, - }, - } - - for _, tc := range testCases { - tc := tc - t.Run(tc.Name, func(tt *testing.T) { - tt.Parallel() - v, ok := tc.V.ValueLatLngHeight() - assert.Equal(tt, tc.Expected.V, v) - assert.Equal(tt, tc.Expected.Ok, ok) - }) - } -} - -func TestValue_ValueCamera(t *testing.T) { - testCases := []struct { - Name string - V *Value - Expected struct { - V Camera - Ok bool - } - }{ - { - Name: "nil value", - }, - { - Name: "success", - V: ValueTypeCamera.ValueFromUnsafe( - map[string]interface{}{ - "Lat": 1, - "Lng": 2, - "Altitude": 3, - "Heading": 4, - "Pitch": 5, - "Roll": 6, - "FOV": 7, - }), - Expected: struct { - V Camera - Ok bool - }{ - V: Camera{ - Lat: 1, - Lng: 2, - Altitude: 3, - Heading: 4, - Pitch: 5, - Roll: 6, - FOV: 7, - }, - Ok: true, - }, - }, - } - - for _, tc := range testCases { - tc := tc - t.Run(tc.Name, func(tt *testing.T) { - tt.Parallel() - v, ok := tc.V.ValueCamera() - assert.Equal(tt, tc.Expected.V, v) - assert.Equal(tt, tc.Expected.Ok, ok) - }) - } -} - -func TestValue_ValueCoordinates(t *testing.T) { - testCases := []struct { - Name string - V *Value - Expected struct { - V Coordinates - Ok bool - } - }{ - { - Name: "nil value", - }, - { - Name: "success", - V: ValueTypeCoordinates.ValueFromUnsafe( - []map[string]interface{}{ - { - "lat": 1, - "lng": 2, - "height": 3, - }, - }), - Expected: struct { - V Coordinates - Ok bool - }{ - V: Coordinates{ - LatLngHeight{ - Lat: 1, - Lng: 2, - Height: 3, - }, - }, - Ok: true, - }, - }, - } - - for _, tc := range testCases { - tc := tc - t.Run(tc.Name, func(tt *testing.T) { - tt.Parallel() - v, ok := tc.V.ValueCoordinates() - assert.Equal(tt, tc.Expected.V, v) - assert.Equal(tt, tc.Expected.Ok, ok) - }) - } -} - -func TestValue_ValuePolygon(t *testing.T) { - testCases := []struct { - Name string - V *Value - Expected struct { - V Polygon - Ok bool - } - }{ - { - Name: "nil value", - }, - { - Name: "success", - V: ValueTypePolygon.ValueFromUnsafe( - [][]map[string]interface{}{ - { - { - "lat": 1, - "lng": 2, - "height": 3, - }, - }, - }), - Expected: struct { - V Polygon - Ok bool - }{ - V: []Coordinates{ - []LatLngHeight{ - { - Lat: 1, - Lng: 2, - Height: 3, - }, - }, - }, - Ok: true, - }, - }, - } - - for _, tc := range testCases { - tc := tc - t.Run(tc.Name, func(tt *testing.T) { - tt.Parallel() - v, ok := tc.V.ValuePolygon() - assert.Equal(tt, tc.Expected.V, v) - assert.Equal(tt, tc.Expected.Ok, ok) - }) - } -} - -func TestValue_ValueRect(t *testing.T) { - testCases := []struct { - Name string - V *Value - Expected struct { - V Rect - Ok bool - } - }{ - { - Name: "nil value", - }, - { - Name: "success", - V: ValueTypeRect.ValueFromUnsafe( - map[string]interface{}{ - "West": 2, - "South": 3, - "East": 4, - "North": 5, - }), - Expected: struct { - V Rect - Ok bool - }{ - V: Rect{ - West: 2, - South: 3, - East: 4, - North: 5, - }, - Ok: true, - }, - }, - } - - for _, tc := range testCases { - tc := tc - t.Run(tc.Name, func(tt *testing.T) { - tt.Parallel() - v, ok := tc.V.ValueRect() - assert.Equal(tt, tc.Expected.V, v) - assert.Equal(tt, tc.Expected.Ok, ok) - }) - } -} - -func TestValue_ValueRef(t *testing.T) { - uid := id.New() - testCases := []struct { - Name string - V *Value - Expected struct { - V id.ID - Ok bool - } - }{ - { - Name: "nil value", - }, - { - Name: "success", - V: ValueTypeRef.ValueFromUnsafe(uid), - Expected: struct { - V id.ID - Ok bool - }{V: uid, Ok: true}, - }, - } - - for _, tc := range testCases { - tc := tc - t.Run(tc.Name, func(tt *testing.T) { - tt.Parallel() - v, ok := tc.V.ValueRef() - assert.Equal(tt, tc.Expected.V, v) - assert.Equal(tt, tc.Expected.Ok, ok) - }) - } -} - -func TestValue_ValueURL(t *testing.T) { - testCases := []struct { - Name string - V *Value - Expected struct { - V *url.URL - Ok bool - } - }{ - { - Name: "nil value", - }, - { - Name: "success", - V: ValueTypeURL.ValueFromUnsafe(map[string]interface{}{ - "Scheme": "xx", - "Opaque": "aa.hh", - "Path": "zz/vv.bb", - "ForceQuery": false, - }), - }, - } - - for _, tc := range testCases { - tc := tc - t.Run(tc.Name, func(tt *testing.T) { - tt.Parallel() - v, ok := tc.V.ValueURL() - assert.Equal(tt, tc.Expected.V, v) - assert.Equal(tt, tc.Expected.Ok, ok) - }) - } -} - -func TestValue_ValueTypography(t *testing.T) { - ff, fs, ts := "Times New Roman", 10, TextAlignLeft - var c, fw *string - var b, i, u *bool - - testCases := []struct { - Name string - V *Value - Expected struct { - V Typography - Ok bool - } - }{ - { - Name: "nil value", - }, - { - Name: "success", - V: ValueTypeTypography.ValueFromUnsafe(map[string]interface{}{ - "fontFamily": &ff, - "fontSize": &fs, - "textAlign": &ts, - "color": c, - "fontWeight": fw, - "bold": b, - "italic": i, - "underline": u, - }), - Expected: struct { - V Typography - Ok bool - }{ - V: Typography{ - FontFamily: &ff, - FontWeight: fw, - FontSize: &fs, - Color: c, - TextAlign: &ts, - Bold: b, - Italic: i, - Underline: u, - }, - Ok: true, - }, - }, - } - - for _, tc := range testCases { - tc := tc - t.Run(tc.Name, func(tt *testing.T) { - tt.Parallel() - v, ok := tc.V.ValueTypography() - assert.Equal(tt, tc.Expected.V, v) - assert.Equal(tt, tc.Expected.Ok, ok) - }) - } -} - -func TestValueType_ValueFrom(t *testing.T) { - var llh *LatLngHeight - var ll *LatLng - var ca *Camera - var rc *Rect - var cords *Coordinates - var p *Polygon - var ty *Typography - iid := id.New() - testCases := []struct { - Name string - Input interface{} - VT ValueType - Expected struct { - V interface{} - Ok bool - } - }{ - { - Name: "valueType is nil", - VT: "", - Expected: struct { - V interface{} - Ok bool - }{ - V: nil, - Ok: false, - }, - }, - { - Name: "input is nil", - VT: ValueTypeBool, - Expected: struct { - V interface{} - Ok bool - }{ - V: nil, - Ok: true, - }, - }, - { - Name: "bool", - Input: true, - VT: ValueTypeBool, - Expected: struct { - V interface{} - Ok bool - }{ - V: true, - Ok: true, - }, - }, - { - Name: "string", - Input: "xxx", - VT: ValueTypeString, - Expected: struct { - V interface{} - Ok bool - }{ - V: "xxx", - Ok: true, - }, - }, - { - Name: "number: json number", - Input: json.Number(strconv.FormatFloat(10, 'e', 0, 64)), - VT: ValueTypeNumber, - Expected: struct { - V interface{} - Ok bool - }{ - V: float64(10), - Ok: true, - }, - }, - { - Name: "number: float64", - Input: float64(11), - VT: ValueTypeNumber, - Expected: struct { - V interface{} - Ok bool - }{ - V: float64(11), - Ok: true, - }, - }, - { - Name: "number: int64", - Input: 12, - VT: ValueTypeNumber, - Expected: struct { - V interface{} - Ok bool - }{ - V: float64(12), - Ok: true, - }, - }, - { - Name: "ref: string", - Input: iid.String(), - VT: ValueTypeRef, - Expected: struct { - V interface{} - Ok bool - }{ - V: iid, - Ok: true, - }, - }, - { - Name: "ref: id", - Input: iid, - VT: ValueTypeRef, - Expected: struct { - V interface{} - Ok bool - }{ - V: iid, - Ok: true, - }, - }, - { - Name: "latlng", - Input: LatLng{ - Lat: 10, - Lng: 11, - }, - VT: ValueTypeLatLng, - Expected: struct { - V interface{} - Ok bool - }{ - V: LatLng{ - Lat: 10, - Lng: 11, - }, - Ok: true, - }, - }, - { - Name: "latlng: nil", - Input: ll, - VT: ValueTypeLatLng, - Expected: struct { - V interface{} - Ok bool - }{ - Ok: false, - }, - }, - { - Name: "latlng: ref", - Input: &LatLng{ - Lat: 10, - Lng: 11, - }, - VT: ValueTypeLatLng, - Expected: struct { - V interface{} - Ok bool - }{ - V: LatLng{ - Lat: 10, - Lng: 11, - }, - Ok: true, - }, - }, - { - Name: "latlng: map", - Input: map[string]interface{}{ - "lat": 10, - "lng": 11, - }, - VT: ValueTypeLatLng, - Expected: struct { - V interface{} - Ok bool - }{ - V: LatLng{ - Lat: 10, - Lng: 11, - }, - Ok: true, - }, - }, - { - Name: "latlngheight: map", - Input: map[string]interface{}{ - "lng": 11, - "lat": 12, - "height": 13, - }, - VT: ValueTypeLatLngHeight, - Expected: struct { - V interface{} - Ok bool - }{ - V: LatLngHeight{ - Lat: 12, - Lng: 11, - Height: 13, - }, - Ok: true, - }, - }, - { - Name: "latlngheight: nil", - Input: llh, - VT: ValueTypeLatLngHeight, - Expected: struct { - V interface{} - Ok bool - }{ - Ok: false, - }, - }, - { - Name: "latlngheight", - Input: LatLngHeight{ - Lat: 12, - Lng: 11, - Height: 13, - }, - VT: ValueTypeLatLngHeight, - Expected: struct { - V interface{} - Ok bool - }{ - V: LatLngHeight{ - Lat: 12, - Lng: 11, - Height: 13, - }, - Ok: true, - }, - }, - { - Name: "latlngheight: ref", - Input: &LatLngHeight{ - Lat: 12, - Lng: 11, - Height: 13, - }, - VT: ValueTypeLatLngHeight, - Expected: struct { - V interface{} - Ok bool - }{ - V: LatLngHeight{ - Lat: 12, - Lng: 11, - Height: 13, - }, - Ok: true, - }, - }, - { - Name: "camera: map", - Input: map[string]interface{}{ - "Lat": 1, - "Lng": 2, - "Altitude": 3, - "Heading": 4, - "Pitch": 5, - "Roll": 6, - "FOV": 7, - }, - VT: ValueTypeCamera, - Expected: struct { - V interface{} - Ok bool - }{ - V: Camera{ - Lat: 1, - Lng: 2, - Altitude: 3, - Heading: 4, - Pitch: 5, - Roll: 6, - FOV: 7, - }, - Ok: true, - }, - }, - { - Name: "camera", - Input: Camera{ - Lat: 1, - Lng: 2, - Altitude: 3, - Heading: 4, - Pitch: 5, - Roll: 6, - FOV: 7, - }, - VT: ValueTypeCamera, - Expected: struct { - V interface{} - Ok bool - }{ - V: Camera{ - Lat: 1, - Lng: 2, - Altitude: 3, - Heading: 4, - Pitch: 5, - Roll: 6, - FOV: 7, - }, - Ok: true, - }, - }, - { - Name: "camera: ref", - Input: &Camera{ - Lat: 1, - Lng: 2, - Altitude: 3, - Heading: 4, - Pitch: 5, - Roll: 6, - FOV: 7, - }, - VT: ValueTypeCamera, - Expected: struct { - V interface{} - Ok bool - }{ - V: Camera{ - Lat: 1, - Lng: 2, - Altitude: 3, - Heading: 4, - Pitch: 5, - Roll: 6, - FOV: 7, - }, - Ok: true, - }, - }, - { - Name: "camera: nil", - Input: ca, - VT: ValueTypeCamera, - Expected: struct { - V interface{} - Ok bool - }{}, - }, - { - Name: "rect: nil", - Input: rc, - VT: ValueTypeRect, - Expected: struct { - V interface{} - Ok bool - }{}, - }, - { - Name: "rect: map", - Input: map[string]interface{}{ - "West": 2, - "South": 3, - "East": 4, - "North": 5, - }, - VT: ValueTypeRect, - Expected: struct { - V interface{} - Ok bool - }{ - V: Rect{ - West: 2, - South: 3, - East: 4, - North: 5, - }, - Ok: true, - }, - }, - { - Name: "rect", - Input: Rect{ - West: 2, - South: 3, - East: 4, - North: 5, - }, - VT: ValueTypeRect, - Expected: struct { - V interface{} - Ok bool - }{ - V: Rect{ - West: 2, - South: 3, - East: 4, - North: 5, - }, - Ok: true, - }, - }, - { - Name: "rect: ref", - Input: &Rect{ - West: 2, - South: 3, - East: 4, - North: 5, - }, - VT: ValueTypeRect, - Expected: struct { - V interface{} - Ok bool - }{ - V: Rect{ - West: 2, - South: 3, - East: 4, - North: 5, - }, - Ok: true, - }, - }, - { - Name: "cods: map", - Input: []map[string]interface{}{ - { - "lat": 1, - "lng": 2, - "height": 3, - }, - }, - VT: ValueTypeCoordinates, - Expected: struct { - V interface{} - Ok bool - }{ - V: Coordinates{ - { - Lat: 1, - Lng: 2, - Height: 3, - }, - }, - Ok: true, - }, - }, - { - Name: "cods: ref", - Input: &Coordinates{ - { - Lat: 1, - Lng: 2, - Height: 3, - }, - }, - VT: ValueTypeCoordinates, - Expected: struct { - V interface{} - Ok bool - }{ - V: Coordinates{ - { - Lat: 1, - Lng: 2, - Height: 3, - }, - }, - Ok: true, - }, - }, - { - Name: "cods: nil", - Input: cords, - VT: ValueTypeCoordinates, - }, - { - Name: "polygon: nil", - Input: p, - VT: ValueTypePolygon, - }, - { - Name: "polygon: nil", - Input: &Polygon{ - Coordinates{ - { - Lat: 1, - Lng: 2, - Height: 3, - }, - }, - }, - VT: ValueTypePolygon, - Expected: struct { - V interface{} - Ok bool - }{ - V: Polygon{ - Coordinates{ - { - Lat: 1, - Lng: 2, - Height: 3, - }, - }, - }, - Ok: true, - }, - }, - { - Name: "typography: nil", - Input: ty, - VT: ValueTypeTypography, - }, - { - Name: "undefined", - Input: "ttt", - VT: "xxx", - }, - } - - for _, tc := range testCases { - tc := tc - t.Run(tc.Name, func(tt *testing.T) { - tt.Parallel() - v, ok := tc.VT.ValueFrom(tc.Input) - assert.Equal(tt, tc.Expected.V, v.Value()) - assert.Equal(tt, tc.Expected.Ok, ok) - }) - } -} - -func TestValue_Interface(t *testing.T) { - ff, fs, ts := "Times New Roman", 10, TextAlignLeft - var c, fw *string - var b, i, u *bool - testCases := []struct { - Name string - V *Value - Expected interface{} - }{ - { - Name: "nil", - }, - { - Name: "undefined", - V: ValueType("uu").ValueFromUnsafe("xx"), - Expected: nil, - }, - { - Name: "bool", - V: ValueTypeBool.ValueFromUnsafe(true), - Expected: true, - }, - { - Name: "latlng", - V: ValueTypeLatLng.ValueFromUnsafe( - LatLng{ - Lat: 1, - Lng: 2, - }), - Expected: map[string]interface{}{ - "lat": 1.0, - "lng": 2.0, - }, - }, - { - Name: "Typography", - V: ValueTypeTypography.ValueFromUnsafe( - Typography{ - FontFamily: &ff, - FontWeight: fw, - FontSize: &fs, - Color: c, - TextAlign: &ts, - Bold: b, - Italic: i, - Underline: u, - }), - Expected: map[string]interface{}{ - "fontFamily": &ff, - "fontSize": &fs, - "textAlign": &ts, - "color": c, - "fontWeight": fw, - "bold": b, - "italic": i, - "underline": u, - }, - }, - { - Name: "camera", - V: ValueTypeCamera.ValueFromUnsafe( - Camera{ - Lat: 1, - Lng: 2, - Altitude: 3, - Heading: 4, - Pitch: 5, - Roll: 6, - FOV: 7, - }), - Expected: map[string]interface{}{ - "lat": 1.0, - "lng": 2.0, - "altitude": 3.0, - "heading": 4.0, - "pitch": 5.0, - "roll": 6.0, - "fov": 7.0, - }, - }, - { - Name: "rect", - V: ValueTypeRect.ValueFromUnsafe( - Rect{ - West: 2, - South: 3, - East: 4, - North: 5, - }), - Expected: map[string]interface{}{ - "west": 2.0, - "south": 3.0, - "east": 4.0, - "north": 5.0, - }, - }, - { - Name: "latlngheight", - V: ValueTypeLatLngHeight.ValueFromUnsafe( - LatLngHeight{ - Lat: 1, - Lng: 2, - Height: 3, - }), - Expected: map[string]interface{}{ - "lat": 1.0, - "lng": 2.0, - "height": 3.0, - }, - }, - { - Name: "coordinates", - V: ValueTypeCoordinates.ValueFromUnsafe( - Coordinates{ - LatLngHeight{ - Lat: 1, - Lng: 2, - Height: 3, - }, - }), - Expected: []map[string]interface{}{ - { - "lat": 1.0, - "lng": 2.0, - "height": 3.0, - }, - }, - }, - { - Name: "polygon", - V: ValueTypePolygon.ValueFromUnsafe( - Polygon{ - Coordinates{ - LatLngHeight{ - Lat: 1, - Lng: 2, - Height: 3, - }, - }, - }), - Expected: [][]map[string]interface{}{ - {{ - "lat": 1.0, - "lng": 2.0, - "height": 3.0, - }}, - }, - }, - } - - for _, tc := range testCases { - tc := tc - t.Run(tc.Name, func(tt *testing.T) { - tt.Parallel() - i := tc.V.Interface() - assert.Equal(tt, tc.Expected, i) - }) - } -} diff --git a/pkg/property/value_typography.go b/pkg/property/value_typography.go new file mode 100644 index 00000000..a3ae3726 --- /dev/null +++ b/pkg/property/value_typography.go @@ -0,0 +1,132 @@ +package property + +import ( + "github.com/mitchellh/mapstructure" + "github.com/reearth/reearth-backend/pkg/value" +) + +var TypeTypography = value.Type("typography") + +type Typography struct { + FontFamily *string `json:"fontFamily" mapstructure:"fontFamily"` + FontWeight *string `json:"fontWeight" mapstructure:"fontWeight"` + FontSize *int `json:"fontSize" mapstructure:"fontSize"` + Color *string `json:"color" mapstructure:"color"` + TextAlign *TextAlign `json:"textAlign" mapstructure:"textAlign"` + Bold *bool `json:"bold" mapstructure:"bold"` + Italic *bool `json:"italic" mapstructure:"italic"` + Underline *bool `json:"underline" mapstructure:"underline"` +} + +func (t *Typography) Clone() *Typography { + if t == nil { + return nil + } + return &Typography{ + FontFamily: t.FontFamily, + FontWeight: t.FontWeight, + FontSize: t.FontSize, + Color: t.Color, + TextAlign: t.TextAlign, + Bold: t.Bold, + Italic: t.Italic, + Underline: t.Underline, + } +} + +type TextAlign string + +const ( + TextAlignLeft TextAlign = "left" + TextAlignCenter TextAlign = "center" + TextAlignRight TextAlign = "right" + TextAlignJustify TextAlign = "justify" + TextAlignJustifyAll TextAlign = "justify_all" +) + +func TextAlignFrom(t string) (TextAlign, bool) { + switch TextAlign(t) { + case TextAlignLeft: + return TextAlignLeft, true + case TextAlignCenter: + return TextAlignCenter, true + case TextAlignRight: + return TextAlignRight, true + case TextAlignJustify: + return TextAlignJustify, true + case TextAlignJustifyAll: + return TextAlignJustifyAll, true + } + return TextAlign(""), false +} + +func TextAlignFromRef(t *string) *TextAlign { + if t == nil { + return nil + } + var t2 TextAlign + switch TextAlign(*t) { + case TextAlignLeft: + t2 = TextAlignLeft + case TextAlignCenter: + t2 = TextAlignCenter + case TextAlignRight: + t2 = TextAlignRight + case TextAlignJustify: + t2 = TextAlignJustify + case TextAlignJustifyAll: + t2 = TextAlignJustifyAll + default: + return nil + } + return &t2 +} + +func (t TextAlign) String() string { + return string(t) +} + +func (t *TextAlign) StringRef() *string { + if t == nil { + return nil + } + t2 := string(*t) + return &t2 +} + +var typePropertyTypography = value.TypeProperty{ + I2V: func(i interface{}) (interface{}, bool) { + if v, ok := i.(Typography); ok { + return v, true + } + + if v, ok := i.(*Typography); ok { + if v != nil { + return *v, true + } + return nil, false + } + + v := Typography{} + if err := mapstructure.Decode(i, &v); err == nil { + return v, true + } + + return nil, false + }, + V2I: func(v interface{}) (interface{}, bool) { + return v, true + }, + Validate: func(i interface{}) bool { + _, ok := i.(Typography) + return ok + }, +} + +func (v *Value) ValueTypography() (vv Typography, ok bool) { + if v == nil { + return + } + vv, ok = v.Value().(Typography) + return +} diff --git a/pkg/property/value_typography_test.go b/pkg/property/value_typography_test.go new file mode 100644 index 00000000..9f9e7312 --- /dev/null +++ b/pkg/property/value_typography_test.go @@ -0,0 +1,204 @@ +package property + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func getStrRef(i string) *string { + return &i +} + +func getBoolRef(i bool) *bool { + return &i +} + +func TestTypography_Clone(t *testing.T) { + i := 10 + + testes := []struct { + Name string + Typography, Expected *Typography + }{ + { + Name: "nil typography", + }, + { + Name: "cloned", + Typography: &Typography{ + FontFamily: getStrRef("x"), + FontWeight: getStrRef("b"), + FontSize: &i, + Color: getStrRef("red"), + TextAlign: TextAlignFromRef(getStrRef(TextAlignCenter.String())), + Bold: getBoolRef(true), + Italic: getBoolRef(false), + Underline: getBoolRef(true), + }, + Expected: &Typography{ + FontFamily: getStrRef("x"), + FontWeight: getStrRef("b"), + FontSize: &i, + Color: getStrRef("red"), + TextAlign: TextAlignFromRef(getStrRef("center")), + Bold: getBoolRef(true), + Italic: getBoolRef(false), + Underline: getBoolRef(true), + }, + }, + } + + for _, tc := range testes { + tc := tc + t.Run(tc.Name, func(tt *testing.T) { + tt.Parallel() + res := tc.Typography.Clone() + assert.Equal(tt, tc.Expected, res) + if tc.Expected != nil { + assert.NotSame(tt, tc.Expected, res) + } + }) + } +} + +func TestTextAlignFrom(t *testing.T) { + testCases := []struct { + Name string + Expected struct { + TA TextAlign + Bool bool + } + }{ + { + Name: "left", + Expected: struct { + TA TextAlign + Bool bool + }{ + TA: TextAlignLeft, + Bool: true, + }, + }, + { + Name: "right", + Expected: struct { + TA TextAlign + Bool bool + }{ + TA: TextAlignRight, + Bool: true, + }, + }, + { + Name: "center", + Expected: struct { + TA TextAlign + Bool bool + }{ + TA: TextAlignCenter, + Bool: true, + }, + }, + { + Name: "justify", + Expected: struct { + TA TextAlign + Bool bool + }{ + TA: TextAlignJustify, + Bool: true, + }, + }, + { + Name: "justify_all", + Expected: struct { + TA TextAlign + Bool bool + }{ + TA: TextAlignJustifyAll, + Bool: true, + }, + }, + { + Name: "undefined", + Expected: struct { + TA TextAlign + Bool bool + }{ + TA: TextAlign(""), + Bool: false, + }, + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.Name, func(tt *testing.T) { + tt.Parallel() + res, ok := TextAlignFrom(tc.Name) + assert.Equal(tt, tc.Expected.TA, res) + assert.Equal(tt, tc.Expected.Bool, ok) + }) + } +} + +func TestTextAlignFromRef(t *testing.T) { + ja := TextAlignJustifyAll + j := TextAlignJustify + c := TextAlignCenter + l := TextAlignLeft + r := TextAlignRight + testCases := []struct { + Name string + Input *string + Expected *TextAlign + }{ + { + Name: "left", + Input: getStrRef("left"), + Expected: &l, + }, + { + Name: "right", + Input: getStrRef("right"), + Expected: &r, + }, + { + Name: "center", + Input: getStrRef("center"), + Expected: &c, + }, + { + Name: "justify", + Input: getStrRef("justify"), + Expected: &j, + }, + { + Name: "justify_all", + Input: getStrRef("justify_all"), + Expected: &ja, + }, + { + Name: "undefined", + Input: getStrRef("undefined"), + }, + { + Name: "nil input", + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.Name, func(tt *testing.T) { + tt.Parallel() + res := TextAlignFromRef(tc.Input) + assert.Equal(tt, tc.Expected, res) + }) + } +} + +func TestTextAlign_StringRef(t *testing.T) { + var ta *TextAlign + assert.Nil(t, ta.StringRef()) +} diff --git a/pkg/scene/builder/builder_test.go b/pkg/scene/builder/builder_test.go index a3c92725..80c85cbf 100644 --- a/pkg/scene/builder/builder_test.go +++ b/pkg/scene/builder/builder_test.go @@ -78,12 +78,12 @@ func TestSceneBuilder(t *testing.T) { property.NewFieldUnsafe(). FieldUnsafe(propertySchemaField1ID). TypeUnsafe(property.ValueTypeString). - ValueUnsafe(property.ValueTypeString.ValueFromUnsafe("xxx")). + ValueUnsafe(property.ValueTypeString.ValueFrom("xxx")). Build(), property.NewFieldUnsafe(). FieldUnsafe(propertySchemaField2ID). TypeUnsafe(property.ValueTypeNumber). - ValueUnsafe(property.ValueTypeNumber.ValueFromUnsafe(1)). + ValueUnsafe(property.ValueTypeNumber.ValueFrom(1)). Build(), }).MustBuild(), }). @@ -107,12 +107,12 @@ func TestSceneBuilder(t *testing.T) { property.NewFieldUnsafe(). FieldUnsafe(propertySchemaField1ID). TypeUnsafe(property.ValueTypeString). - ValueUnsafe(property.ValueTypeString.ValueFromUnsafe("yyy")). + ValueUnsafe(property.ValueTypeString.ValueFrom("yyy")). Build(), property.NewFieldUnsafe(). FieldUnsafe(propertySchemaField2ID). TypeUnsafe(property.ValueTypeNumber). - ValueUnsafe(property.ValueTypeNumber.ValueFromUnsafe(1)). + ValueUnsafe(property.ValueTypeNumber.ValueFrom(1)). Build(), }).MustBuild(), }). @@ -134,12 +134,12 @@ func TestSceneBuilder(t *testing.T) { property.NewFieldUnsafe(). FieldUnsafe(propertySchemaField1ID). TypeUnsafe(property.ValueTypeString). - ValueUnsafe(property.ValueTypeString.ValueFromUnsafe("xxx")). + ValueUnsafe(property.ValueTypeString.ValueFrom("xxx")). Build(), property.NewFieldUnsafe(). FieldUnsafe(propertySchemaField3ID). TypeUnsafe(property.ValueTypeString). - ValueUnsafe(property.ValueTypeString.ValueFromUnsafe("test")). + ValueUnsafe(property.ValueTypeString.ValueFrom("test")). Build(), }).MustBuild(), }). @@ -201,7 +201,7 @@ func TestSceneBuilder(t *testing.T) { property.NewFieldUnsafe(). FieldUnsafe(propertySchemaField2ID). TypeUnsafe(property.ValueTypeNumber). - ValueUnsafe(property.ValueTypeNumber.ValueFromUnsafe(1)). + ValueUnsafe(property.ValueTypeNumber.ValueFrom(1)). Build(), }).MustBuild(), }). @@ -236,7 +236,7 @@ func TestSceneBuilder(t *testing.T) { property.NewFieldUnsafe(). FieldUnsafe(propertySchemaField3ID). TypeUnsafe(property.ValueTypeString). - ValueUnsafe(property.ValueTypeString.ValueFromUnsafe("xxx")). + ValueUnsafe(property.ValueTypeString.ValueFrom("xxx")). Build(), }).MustBuild(), }). @@ -330,7 +330,7 @@ func TestSceneBuilder(t *testing.T) { property.NewFieldUnsafe(). FieldUnsafe(propertySchemaField1ID). TypeUnsafe(property.ValueTypeString). - ValueUnsafe(property.ValueTypeString.ValueFromUnsafe("XYZ")). + ValueUnsafe(property.ValueTypeString.ValueFrom("XYZ")). Build(), }).MustBuild(), property.NewGroup().ID(propertyItemID2).Schema(propertySchemaID, propertySchemaGroup2ID). @@ -338,7 +338,7 @@ func TestSceneBuilder(t *testing.T) { property.NewFieldUnsafe(). FieldUnsafe(propertySchemaField1ID). TypeUnsafe(property.ValueTypeString). - ValueUnsafe(property.ValueTypeString.ValueFromUnsafe("ZYX")). + ValueUnsafe(property.ValueTypeString.ValueFrom("ZYX")). Build(), }).MustBuild(), }).MustBuild(), @@ -372,7 +372,7 @@ func TestSceneBuilder(t *testing.T) { property.NewFieldUnsafe(). FieldUnsafe(propertySchemaField1ID). TypeUnsafe(property.ValueTypeString). - ValueUnsafe(property.ValueTypeString.ValueFromUnsafe("hogehoge")). + ValueUnsafe(property.ValueTypeString.ValueFrom("hogehoge")). Build(), }).MustBuild(), }). diff --git a/pkg/scene/builder/encoder_test.go b/pkg/scene/builder/encoder_test.go index afce3cc5..9d3db9b5 100644 --- a/pkg/scene/builder/encoder_test.go +++ b/pkg/scene/builder/encoder_test.go @@ -102,7 +102,7 @@ func TestEncoder_Layers(t *testing.T) { ID: id.PropertySchemaFieldID("location"), Type: "latlng", DatasetValue: nil, - PropertyValue: v1.Value(), + PropertyValue: property.ValueTypeLatLng.ValueFrom(v1), } fl1 := []*property.SealedField{} fl1 = append(fl1, &f1) @@ -157,7 +157,7 @@ func TestEncoder_Layers(t *testing.T) { PluginID: id.OfficialPluginID.StringRef(), ExtensionID: ex.StringRef(), Name: "test", - Property: map[string]interface{}{"default": map[string]interface{}{"location": map[string]interface{}{"lat": 4.4, "lng": 53.4}}}, + Property: map[string]interface{}{"default": map[string]interface{}{"location": property.LatLng{Lat: 4.4, Lng: 53.4}}}, Infobox: nil, }, }, @@ -177,7 +177,6 @@ func TestEncoder_Layers(t *testing.T) { assert.Equal(tt, tc.Expected.Name, res.Name) assert.Equal(tt, *tc.Expected.PluginID, *res.PluginID) } - }) } } diff --git a/pkg/value/ref.go b/pkg/value/ref.go index 19436101..928e6c32 100644 --- a/pkg/value/ref.go +++ b/pkg/value/ref.go @@ -1,12 +1,32 @@ package value +import "github.com/reearth/reearth-backend/pkg/id" + var TypeRef Type = "ref" var propertyRef = TypeProperty{ - I2V: propertyString.I2V, - V2I: propertyString.V2I, - Validate: propertyString.Validate, - // Compatible: []Type{}, + I2V: func(i interface{}) (interface{}, bool) { + if v, ok := i.(string); ok { + return v, true + } + if v, ok := i.(*string); ok { + return *v, true + } + if v, ok := i.(id.ID); ok { + return v.String(), true + } + if v, ok := i.(*id.ID); ok && v != nil { + return v.String(), true + } + return nil, false + }, + V2I: func(v interface{}) (interface{}, bool) { + return v, true + }, + Validate: func(i interface{}) bool { + _, ok := i.(string) + return ok + }, } func (v *Value) ValueRef() (vv string, ok bool) { diff --git a/pkg/value/string.go b/pkg/value/string.go index 638366c9..3b20537f 100644 --- a/pkg/value/string.go +++ b/pkg/value/string.go @@ -19,7 +19,6 @@ var propertyString = TypeProperty{ _, ok := i.(string) return ok }, - Compatible: []Type{}, } func (v *Value) ValueString() (vv string, ok bool) { From 95da40e66591963f5ef6439e931e73a4a5be8975 Mon Sep 17 00:00:00 2001 From: rot1024 Date: Thu, 18 Nov 2021 20:33:52 +0900 Subject: [PATCH 05/12] add optional value to property --- pkg/property/value.go | 1 + pkg/property/value_optional.go | 65 +++++++ pkg/property/value_optional_test.go | 272 ++++++++++++++++++++++++++++ 3 files changed, 338 insertions(+) create mode 100644 pkg/property/value_optional.go create mode 100644 pkg/property/value_optional_test.go diff --git a/pkg/property/value.go b/pkg/property/value.go index 85be67e8..c01d3152 100644 --- a/pkg/property/value.go +++ b/pkg/property/value.go @@ -17,6 +17,7 @@ type Polygon = value.Polygon type ValueType value.Type var ( + ValueTypeUnknown = ValueType(value.TypeUnknown) ValueTypeBool = ValueType(value.TypeBool) ValueTypeNumber = ValueType(value.TypeNumber) ValueTypeString = ValueType(value.TypeString) diff --git a/pkg/property/value_optional.go b/pkg/property/value_optional.go new file mode 100644 index 00000000..fa8311d4 --- /dev/null +++ b/pkg/property/value_optional.go @@ -0,0 +1,65 @@ +package property + +import "github.com/reearth/reearth-backend/pkg/value" + +type OptionalValue struct { + ov value.OptionalValue +} + +func NewOptionalValue(t ValueType, v *Value) *OptionalValue { + var vv *value.Value + if v != nil { + vv = &v.v + } + ov := value.NewOptionalValue(value.Type(t), vv) + if ov == nil { + return nil + } + return &OptionalValue{ov: *ov} +} + +func OptionalValueFrom(v *Value) *OptionalValue { + if v == nil { + return nil + } + ov := value.OptionalValueFrom(&v.v) + if ov == nil { + return nil + } + return &OptionalValue{ + ov: *ov, + } +} + +func (ov *OptionalValue) Type() ValueType { + if ov == nil { + return ValueTypeUnknown + } + return ValueType(ov.ov.Type()) +} + +func (ov *OptionalValue) Value() *Value { + if ov == nil { + return nil + } + vv := ov.ov.Value() + if vv == nil { + return nil + } + return &Value{v: *vv} +} + +func (ov *OptionalValue) TypeAndValue() (ValueType, *Value) { + return ov.Type(), ov.Value() +} + +func (ov *OptionalValue) SetValue(v *Value) { + if ov == nil { + return + } + if v == nil { + ov.ov.SetValue(nil) + } else { + ov.ov.SetValue(&v.v) + } +} diff --git a/pkg/property/value_optional_test.go b/pkg/property/value_optional_test.go new file mode 100644 index 00000000..eb820e4e --- /dev/null +++ b/pkg/property/value_optional_test.go @@ -0,0 +1,272 @@ +package property + +import ( + "testing" + + "github.com/reearth/reearth-backend/pkg/value" + "github.com/stretchr/testify/assert" +) + +func TestNewNilableValue(t *testing.T) { + type args struct { + t ValueType + v *Value + } + tests := []struct { + name string + args args + want *OptionalValue + }{ + { + name: "default type", + args: args{ + t: ValueTypeString, + v: ValueTypeString.ValueFrom("foo"), + }, + want: &OptionalValue{ov: *value.OptionalValueFrom(value.TypeString.ValueFrom("foo", types))}, + }, + { + name: "nil value", + args: args{ + t: ValueTypeString, + }, + want: &OptionalValue{ov: *value.NewOptionalValue(value.TypeString, nil)}, + }, + { + name: "invalid value", + args: args{ + t: ValueTypeNumber, + v: ValueTypeString.ValueFrom("foo"), + }, + want: nil, + }, + { + name: "invalid type", + args: args{ + t: ValueTypeUnknown, + v: ValueTypeString.ValueFrom("foo"), + }, + want: nil, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + assert.Equal(t, tt.want, NewOptionalValue(tt.args.t, tt.args.v)) + }) + } +} + +func TestOptionalValueFrom(t *testing.T) { + type args struct { + v *Value + } + tests := []struct { + name string + args args + want *OptionalValue + }{ + { + name: "default type", + args: args{ + v: ValueTypeString.ValueFrom("foo"), + }, + want: &OptionalValue{ov: *value.NewOptionalValue(value.TypeString, value.TypeString.ValueFrom("foo", types))}, + }, + { + name: "empty value", + args: args{ + v: &Value{v: value.Value{}}, + }, + want: nil, + }, + { + name: "nil value", + args: args{}, + want: nil, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + assert.Equal(t, tt.want, OptionalValueFrom(tt.args.v)) + }) + } +} + +func TestOptionalValue_Type(t *testing.T) { + tests := []struct { + name string + value *OptionalValue + want ValueType + }{ + { + name: "ok", + value: &OptionalValue{ov: *value.NewOptionalValue(value.TypeBool, nil)}, + want: ValueTypeBool, + }, + { + name: "empty", + value: &OptionalValue{}, + want: ValueTypeUnknown, + }, + { + name: "nil", + value: nil, + want: ValueTypeUnknown, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + assert.Equal(t, tt.want, tt.value.Type()) + }) + } +} + +func TestOptionalValue_Value(t *testing.T) { + tests := []struct { + name string + value *OptionalValue + want *Value + }{ + { + name: "ok", + value: &OptionalValue{ov: *value.OptionalValueFrom(value.TypeString.ValueFrom("foobar", types))}, + want: ValueTypeString.ValueFrom("foobar"), + }, + { + name: "empty", + value: &OptionalValue{}, + want: nil, + }, + { + name: "nil", + value: nil, + want: nil, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + res := tt.value.Value() + assert.Equal(t, tt.want, res) + if res != nil { + assert.NotSame(t, tt.want, res) + } + }) + } +} + +func TestOptionalValue_TypeAndValue(t *testing.T) { + tests := []struct { + name string + value *OptionalValue + wantt ValueType + wantv *Value + }{ + { + name: "ok", + value: &OptionalValue{ov: *value.OptionalValueFrom(value.TypeString.ValueFrom("foobar", types))}, + wantt: ValueTypeString, + wantv: ValueTypeString.ValueFrom("foobar"), + }, + { + name: "empty", + value: &OptionalValue{}, + wantt: ValueTypeUnknown, + wantv: nil, + }, + { + name: "nil", + value: nil, + wantt: ValueTypeUnknown, + wantv: nil, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + ty, tv := tt.value.TypeAndValue() + assert.Equal(t, tt.wantt, ty) + assert.Equal(t, tt.wantv, tv) + if tv != nil { + assert.NotSame(t, tt.wantv, tv) + } + }) + } +} + +func TestOptionalValue_SetValue(t *testing.T) { + type args struct { + v *Value + } + tests := []struct { + name string + value *OptionalValue + args args + invalid bool + }{ + { + name: "set", + value: &OptionalValue{ov: *value.OptionalValueFrom(value.TypeString.ValueFrom("foo", types))}, + args: args{v: ValueTypeString.ValueFrom("foobar")}, + }, + { + name: "set to nil", + value: &OptionalValue{ov: *value.NewOptionalValue(value.TypeString, nil)}, + args: args{v: ValueTypeString.ValueFrom("foobar")}, + }, + { + name: "invalid value", + value: &OptionalValue{ov: *value.NewOptionalValue(value.TypeString, nil)}, + args: args{v: ValueTypeNumber.ValueFrom(1)}, + invalid: true, + }, + { + name: "nil value", + args: args{v: ValueTypeNumber.ValueFrom(1)}, + }, + { + name: "empty", + value: &OptionalValue{}, + args: args{v: ValueTypeNumber.ValueFrom(1)}, + invalid: true, + }, + { + name: "nil", + args: args{v: ValueTypeNumber.ValueFrom(1)}, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + var v *Value + if tt.value != nil { + v = tt.value.Value() + } + + tt.value.SetValue(tt.args.v) + + if tt.value != nil { + if tt.invalid { + assert.Equal(t, v, tt.value.Value()) + } else { + assert.Equal(t, tt.args.v, tt.value.Value()) + } + } + }) + } +} From 49f288d8d670dd32b94858fd7c4cf53bbdf1c9b4 Mon Sep 17 00:00:00 2001 From: rot1024 Date: Thu, 18 Nov 2021 21:43:14 +0900 Subject: [PATCH 06/12] add property.ValueAndDatasetValue --- pkg/layer/encoding/czml_test.go | 135 +++++---- pkg/layer/encoding/geojson_test.go | 115 +++++--- pkg/layer/encoding/kml_test.go | 142 ++++++---- pkg/layer/encoding/shp_test.go | 36 +-- pkg/property/sealed.go | 29 +- pkg/property/sealed_test.go | 426 ++++++++++++++++++----------- pkg/property/value.go | 12 - pkg/property/value_dataset.go | 67 +++++ pkg/property/value_dataset_test.go | 295 ++++++++++++++++++++ pkg/scene/builder/encoder_test.go | 10 +- 10 files changed, 922 insertions(+), 345 deletions(-) create mode 100644 pkg/property/value_dataset.go create mode 100644 pkg/property/value_dataset_test.go diff --git a/pkg/layer/encoding/czml_test.go b/pkg/layer/encoding/czml_test.go index 6b995446..805d7ab7 100644 --- a/pkg/layer/encoding/czml_test.go +++ b/pkg/layer/encoding/czml_test.go @@ -45,24 +45,36 @@ func TestCZMLEncoder_Encode(t *testing.T) { SchemaGroup: id.PropertySchemaGroupID("default"), Fields: []*property.SealedField{ { - ID: id.PropertySchemaFieldID("location"), - Type: property.ValueTypeLatLng, - PropertyValue: property.ValueTypeLatLng.ValueFrom(property.LatLng{Lat: 4.4, Lng: 53.4}), + ID: id.PropertySchemaFieldID("location"), + Val: property.NewValueAndDatasetValue( + property.ValueTypeLatLng, + nil, + property.ValueTypeLatLng.ValueFrom(property.LatLng{Lat: 4.4, Lng: 53.4}), + ), }, { - ID: id.PropertySchemaFieldID("height"), - Type: property.ValueTypeNumber, - PropertyValue: property.ValueTypeNumber.ValueFrom(34), + ID: id.PropertySchemaFieldID("height"), + Val: property.NewValueAndDatasetValue( + property.ValueTypeNumber, + nil, + property.ValueTypeNumber.ValueFrom(34), + ), }, { - ID: id.PropertySchemaFieldID("pointColor"), - Type: property.ValueTypeString, - PropertyValue: property.ValueTypeString.ValueFrom("#7fff00ff"), + ID: id.PropertySchemaFieldID("pointColor"), + Val: property.NewValueAndDatasetValue( + property.ValueTypeString, + nil, + property.ValueTypeString.ValueFrom("#7fff00ff"), + ), }, { - ID: id.PropertySchemaFieldID("pointSize"), - Type: property.ValueTypeNumber, - PropertyValue: property.ValueTypeNumber.ValueFrom(2.4), + ID: id.PropertySchemaFieldID("pointSize"), + Val: property.NewValueAndDatasetValue( + property.ValueTypeNumber, + nil, + property.ValueTypeNumber.ValueFrom(2.4), + ), }, }, }, @@ -101,40 +113,58 @@ func TestCZMLEncoder_Encode(t *testing.T) { SchemaGroup: id.PropertySchemaGroupID("default"), Fields: []*property.SealedField{ { - ID: id.PropertySchemaFieldID("polygon"), - Type: property.ValueTypePolygon, - PropertyValue: property.ValueTypePolygon.ValueFrom( - property.Polygon{property.Coordinates{ - {Lat: 3.4, Lng: 5.34, Height: 100}, - {Lat: 45.4, Lng: 2.34, Height: 100}, - {Lat: 34.66, Lng: 654.34, Height: 100}, - }}, + ID: id.PropertySchemaFieldID("polygon"), + Val: property.NewValueAndDatasetValue( + property.ValueTypePolygon, + nil, + property.ValueTypePolygon.ValueFrom( + property.Polygon{property.Coordinates{ + {Lat: 3.4, Lng: 5.34, Height: 100}, + {Lat: 45.4, Lng: 2.34, Height: 100}, + {Lat: 34.66, Lng: 654.34, Height: 100}, + }}, + ), ), }, { - ID: id.PropertySchemaFieldID("fill"), - Type: property.ValueTypeBool, - PropertyValue: property.ValueTypeBool.ValueFrom(true), + ID: id.PropertySchemaFieldID("fill"), + Val: property.NewValueAndDatasetValue( + property.ValueTypeBool, + nil, + property.ValueTypeBool.ValueFrom(true), + ), }, { - ID: id.PropertySchemaFieldID("fillColor"), - Type: property.ValueTypeString, - PropertyValue: property.ValueTypeString.ValueFrom("#ff000000"), + ID: id.PropertySchemaFieldID("fillColor"), + Val: property.NewValueAndDatasetValue( + property.ValueTypeString, + nil, + property.ValueTypeString.ValueFrom("#ff000000"), + ), }, { - ID: id.PropertySchemaFieldID("stroke"), - Type: property.ValueTypeBool, - PropertyValue: property.ValueTypeBool.ValueFrom(true), + ID: id.PropertySchemaFieldID("stroke"), + Val: property.NewValueAndDatasetValue( + property.ValueTypeBool, + nil, + property.ValueTypeBool.ValueFrom(true), + ), }, { - ID: id.PropertySchemaFieldID("strokeColor"), - Type: property.ValueTypeString, - PropertyValue: property.ValueTypeString.ValueFrom("#ff554555"), + ID: id.PropertySchemaFieldID("strokeColor"), + Val: property.NewValueAndDatasetValue( + property.ValueTypeString, + nil, + property.ValueTypeString.ValueFrom("#ff554555"), + ), }, { - ID: id.PropertySchemaFieldID("strokeWidth"), - Type: property.ValueTypeNumber, - PropertyValue: property.ValueTypeNumber.ValueFrom(3), + ID: id.PropertySchemaFieldID("strokeWidth"), + Val: property.NewValueAndDatasetValue( + property.ValueTypeNumber, + nil, + property.ValueTypeNumber.ValueFrom(3), + ), }, }, }, @@ -178,23 +208,32 @@ func TestCZMLEncoder_Encode(t *testing.T) { SchemaGroup: id.PropertySchemaGroupID("default"), Fields: []*property.SealedField{ { - ID: id.PropertySchemaFieldID("coordinates"), - Type: property.ValueTypeCoordinates, - PropertyValue: property.ValueTypeCoordinates.ValueFrom(property.Coordinates{ - {Lat: 3.4, Lng: 5.34, Height: 100}, - {Lat: 45.4, Lng: 2.34, Height: 100}, - {Lat: 34.66, Lng: 654.34, Height: 100}, - }), + ID: id.PropertySchemaFieldID("coordinates"), + Val: property.NewValueAndDatasetValue( + property.ValueTypeCoordinates, + nil, + property.ValueTypeCoordinates.ValueFrom(property.Coordinates{ + {Lat: 3.4, Lng: 5.34, Height: 100}, + {Lat: 45.4, Lng: 2.34, Height: 100}, + {Lat: 34.66, Lng: 654.34, Height: 100}, + }), + ), }, { - ID: id.PropertySchemaFieldID("strokeColor"), - Type: property.ValueTypeString, - PropertyValue: property.ValueTypeString.ValueFrom("#ff224222"), + ID: id.PropertySchemaFieldID("strokeColor"), + Val: property.NewValueAndDatasetValue( + property.ValueTypeString, + nil, + property.ValueTypeString.ValueFrom("#ff224222"), + ), }, { - ID: id.PropertySchemaFieldID("strokeWidth"), - Type: property.ValueTypeNumber, - PropertyValue: property.ValueTypeNumber.ValueFrom(3), + ID: id.PropertySchemaFieldID("strokeWidth"), + Val: property.NewValueAndDatasetValue( + property.ValueTypeNumber, + nil, + property.ValueTypeNumber.ValueFrom(3), + ), }, }, }, diff --git a/pkg/layer/encoding/geojson_test.go b/pkg/layer/encoding/geojson_test.go index d05a18d4..04095a28 100644 --- a/pkg/layer/encoding/geojson_test.go +++ b/pkg/layer/encoding/geojson_test.go @@ -39,19 +39,28 @@ func TestGeoJSONEncoder_Encode(t *testing.T) { SchemaGroup: id.PropertySchemaGroupID("default"), Fields: []*property.SealedField{ { - ID: id.PropertySchemaFieldID("location"), - Type: property.ValueTypeLatLng, - PropertyValue: property.ValueTypeLatLng.ValueFrom(property.LatLng{Lat: 4.4, Lng: 53.4}), + ID: id.PropertySchemaFieldID("location"), + Val: property.NewValueAndDatasetValue( + property.ValueTypeLatLng, + nil, + property.ValueTypeLatLng.ValueFrom(property.LatLng{Lat: 4.4, Lng: 53.4}), + ), }, { - ID: id.PropertySchemaFieldID("pointColor"), - Type: property.ValueTypeString, - PropertyValue: property.ValueTypeString.ValueFrom("#7fff00ff"), + ID: id.PropertySchemaFieldID("pointColor"), + Val: property.NewValueAndDatasetValue( + property.ValueTypeString, + nil, + property.ValueTypeString.ValueFrom("#7fff00ff"), + ), }, { - ID: id.PropertySchemaFieldID("height"), - Type: property.ValueTypeNumber, - PropertyValue: property.ValueTypeNumber.ValueFrom(34), + ID: id.PropertySchemaFieldID("height"), + Val: property.NewValueAndDatasetValue( + property.ValueTypeNumber, + nil, + property.ValueTypeNumber.ValueFrom(34), + ), }, }, }, @@ -85,33 +94,48 @@ func TestGeoJSONEncoder_Encode(t *testing.T) { SchemaGroup: id.PropertySchemaGroupID("default"), Fields: []*property.SealedField{ { - ID: id.PropertySchemaFieldID("polygon"), - Type: "polygon", - PropertyValue: property.ValueTypePolygon.ValueFrom(property.Polygon{property.Coordinates{ - {Lat: 3.4, Lng: 5.34, Height: 100}, - {Lat: 45.4, Lng: 2.34, Height: 100}, - {Lat: 34.66, Lng: 654.34, Height: 100}, - }}), + ID: id.PropertySchemaFieldID("polygon"), + Val: property.NewValueAndDatasetValue( + property.ValueTypePolygon, + nil, + property.ValueTypePolygon.ValueFrom(property.Polygon{property.Coordinates{ + {Lat: 3.4, Lng: 5.34, Height: 100}, + {Lat: 45.4, Lng: 2.34, Height: 100}, + {Lat: 34.66, Lng: 654.34, Height: 100}, + }}), + ), }, { - ID: id.PropertySchemaFieldID("fillColor"), - Type: property.ValueTypeString, - PropertyValue: property.ValueTypeString.ValueFrom("#7c3b3b"), + ID: id.PropertySchemaFieldID("fillColor"), + Val: property.NewValueAndDatasetValue( + property.ValueTypeString, + nil, + property.ValueTypeString.ValueFrom("#7c3b3b"), + ), }, { - ID: id.PropertySchemaFieldID("strokeColor"), - Type: property.ValueTypeString, - PropertyValue: property.ValueTypeString.ValueFrom("#ff3343"), + ID: id.PropertySchemaFieldID("strokeColor"), + Val: property.NewValueAndDatasetValue( + property.ValueTypeString, + nil, + property.ValueTypeString.ValueFrom("#ff3343"), + ), }, { - ID: id.PropertySchemaFieldID("strokeWidth"), - Type: property.ValueTypeNumber, - PropertyValue: property.ValueTypeNumber.ValueFrom(3), + ID: id.PropertySchemaFieldID("strokeWidth"), + Val: property.NewValueAndDatasetValue( + property.ValueTypeNumber, + nil, + property.ValueTypeNumber.ValueFrom(3), + ), }, { - ID: id.PropertySchemaFieldID("strokeWidth"), - Type: property.ValueTypeNumber, - PropertyValue: property.ValueTypeNumber.ValueFrom(3), + ID: id.PropertySchemaFieldID("strokeWidth"), + Val: property.NewValueAndDatasetValue( + property.ValueTypeNumber, + nil, + property.ValueTypeNumber.ValueFrom(3), + ), }, }, }, @@ -147,23 +171,32 @@ func TestGeoJSONEncoder_Encode(t *testing.T) { SchemaGroup: id.PropertySchemaGroupID("default"), Fields: []*property.SealedField{ { - ID: id.PropertySchemaFieldID("coordinates"), - Type: property.ValueTypeCoordinates, - PropertyValue: property.ValueTypeCoordinates.ValueFrom(property.Coordinates{ - {Lat: 3.4, Lng: 5.34, Height: 100}, - {Lat: 45.4, Lng: 2.34, Height: 100}, - {Lat: 34.66, Lng: 654.34, Height: 100}, - }), + ID: id.PropertySchemaFieldID("coordinates"), + Val: property.NewValueAndDatasetValue( + property.ValueTypeCoordinates, + nil, + property.ValueTypeCoordinates.ValueFrom(property.Coordinates{ + {Lat: 3.4, Lng: 5.34, Height: 100}, + {Lat: 45.4, Lng: 2.34, Height: 100}, + {Lat: 34.66, Lng: 654.34, Height: 100}, + }), + ), }, { - ID: id.PropertySchemaFieldID("strokeColor"), - Type: property.ValueTypeString, - PropertyValue: property.ValueTypeString.ValueFrom("#ff3343"), + ID: id.PropertySchemaFieldID("strokeColor"), + Val: property.NewValueAndDatasetValue( + property.ValueTypeString, + nil, + property.ValueTypeString.ValueFrom("#ff3343"), + ), }, { - ID: id.PropertySchemaFieldID("strokeWidth"), - Type: property.ValueTypeNumber, - PropertyValue: property.ValueTypeNumber.ValueFrom(3), + ID: id.PropertySchemaFieldID("strokeWidth"), + Val: property.NewValueAndDatasetValue( + property.ValueTypeNumber, + nil, + property.ValueTypeNumber.ValueFrom(3), + ), }, }, }, diff --git a/pkg/layer/encoding/kml_test.go b/pkg/layer/encoding/kml_test.go index 3db9f93b..686b366f 100644 --- a/pkg/layer/encoding/kml_test.go +++ b/pkg/layer/encoding/kml_test.go @@ -41,29 +41,44 @@ func TestKMLEncoder_Encode(t *testing.T) { SchemaGroup: id.PropertySchemaGroupID("default"), Fields: []*property.SealedField{ { - ID: id.PropertySchemaFieldID("location"), - Type: property.ValueTypeLatLng, - PropertyValue: property.ValueTypeLatLng.ValueFrom(property.LatLng{Lat: 4.4, Lng: 53.4}), + ID: id.PropertySchemaFieldID("location"), + Val: property.NewValueAndDatasetValue( + property.ValueTypeLatLng, + nil, + property.ValueTypeLatLng.ValueFrom(property.LatLng{Lat: 4.4, Lng: 53.4}), + ), }, { - ID: id.PropertySchemaFieldID("height"), - Type: property.ValueTypeNumber, - PropertyValue: property.ValueTypeNumber.ValueFrom(100), + ID: id.PropertySchemaFieldID("height"), + Val: property.NewValueAndDatasetValue( + property.ValueTypeNumber, + nil, + property.ValueTypeNumber.ValueFrom(100), + ), }, { - ID: id.PropertySchemaFieldID("imageSize"), - Type: property.ValueTypeNumber, - PropertyValue: property.ValueTypeNumber.ValueFrom(4), + ID: id.PropertySchemaFieldID("imageSize"), + Val: property.NewValueAndDatasetValue( + property.ValueTypeNumber, + nil, + property.ValueTypeNumber.ValueFrom(4), + ), }, { - ID: id.PropertySchemaFieldID("image"), - Type: property.ValueTypeURL, - PropertyValue: property.ValueTypeURL.ValueFrom("http://maps.google.com/mapfiles/kml/pal4/icon28.png"), + ID: id.PropertySchemaFieldID("image"), + Val: property.NewValueAndDatasetValue( + property.ValueTypeURL, + nil, + property.ValueTypeURL.ValueFrom("http://maps.google.com/mapfiles/kml/pal4/icon28.png"), + ), }, { - ID: id.PropertySchemaFieldID("pointColor"), - Type: property.ValueTypeString, - PropertyValue: property.ValueTypeString.ValueFrom("#7fff00ff"), + ID: id.PropertySchemaFieldID("pointColor"), + Val: property.NewValueAndDatasetValue( + property.ValueTypeString, + nil, + property.ValueTypeString.ValueFrom("#7fff00ff"), + ), }, }, }, @@ -111,38 +126,56 @@ func TestKMLEncoder_Encode(t *testing.T) { SchemaGroup: id.PropertySchemaGroupID("default"), Fields: []*property.SealedField{ { - ID: id.PropertySchemaFieldID("polygon"), - Type: property.ValueTypePolygon, - PropertyValue: property.ValueTypePolygon.ValueFrom(property.Polygon{property.Coordinates{ - property.LatLngHeight{Lat: 3.4, Lng: 5.34, Height: 100}, - property.LatLngHeight{Lat: 45.4, Lng: 2.34, Height: 100}, - property.LatLngHeight{Lat: 34.66, Lng: 654.34, Height: 100}, - }}), + ID: id.PropertySchemaFieldID("polygon"), + Val: property.NewValueAndDatasetValue( + property.ValueTypePolygon, + nil, + property.ValueTypePolygon.ValueFrom(property.Polygon{property.Coordinates{ + property.LatLngHeight{Lat: 3.4, Lng: 5.34, Height: 100}, + property.LatLngHeight{Lat: 45.4, Lng: 2.34, Height: 100}, + property.LatLngHeight{Lat: 34.66, Lng: 654.34, Height: 100}, + }}), + ), }, { - ID: id.PropertySchemaFieldID("fill"), - Type: property.ValueTypeBool, - PropertyValue: property.ValueTypeBool.ValueFrom(true), + ID: id.PropertySchemaFieldID("fill"), + Val: property.NewValueAndDatasetValue( + property.ValueTypeBool, + nil, + property.ValueTypeBool.ValueFrom(true), + ), }, { - ID: id.PropertySchemaFieldID("fillColor"), - Type: property.ValueTypeString, - PropertyValue: property.ValueTypeString.ValueFrom("#ff334353"), + ID: id.PropertySchemaFieldID("fillColor"), + Val: property.NewValueAndDatasetValue( + property.ValueTypeString, + nil, + property.ValueTypeString.ValueFrom("#ff334353"), + ), }, { - ID: id.PropertySchemaFieldID("stroke"), - Type: property.ValueTypeBool, - PropertyValue: property.ValueTypeBool.ValueFrom(true), + ID: id.PropertySchemaFieldID("stroke"), + Val: property.NewValueAndDatasetValue( + property.ValueTypeBool, + nil, + property.ValueTypeBool.ValueFrom(true), + ), }, { - ID: id.PropertySchemaFieldID("strokeColor"), - Type: property.ValueTypeString, - PropertyValue: property.ValueTypeString.ValueFrom("#ff554555"), + ID: id.PropertySchemaFieldID("strokeColor"), + Val: property.NewValueAndDatasetValue( + property.ValueTypeString, + nil, + property.ValueTypeString.ValueFrom("#ff554555"), + ), }, { - ID: id.PropertySchemaFieldID("strokeWidth"), - Type: property.ValueTypeNumber, - PropertyValue: property.ValueTypeNumber.ValueFrom(3), + ID: id.PropertySchemaFieldID("strokeWidth"), + Val: property.NewValueAndDatasetValue( + property.ValueTypeNumber, + nil, + property.ValueTypeNumber.ValueFrom(3), + ), }, }, }, @@ -197,23 +230,32 @@ func TestKMLEncoder_Encode(t *testing.T) { SchemaGroup: id.PropertySchemaGroupID("default"), Fields: []*property.SealedField{ { - ID: id.PropertySchemaFieldID("coordinates"), - Type: property.ValueTypeCoordinates, - PropertyValue: property.ValueTypeCoordinates.ValueFrom(property.Coordinates{ - property.LatLngHeight{Lat: 3.4, Lng: 5.34, Height: 100}, - property.LatLngHeight{Lat: 45.4, Lng: 2.34, Height: 100}, - property.LatLngHeight{Lat: 34.66, Lng: 654.34, Height: 100}, - }), + ID: id.PropertySchemaFieldID("coordinates"), + Val: property.NewValueAndDatasetValue( + property.ValueTypeCoordinates, + nil, + property.ValueTypeCoordinates.ValueFrom(property.Coordinates{ + property.LatLngHeight{Lat: 3.4, Lng: 5.34, Height: 100}, + property.LatLngHeight{Lat: 45.4, Lng: 2.34, Height: 100}, + property.LatLngHeight{Lat: 34.66, Lng: 654.34, Height: 100}, + }), + ), }, { - ID: id.PropertySchemaFieldID("strokeColor"), - Type: property.ValueTypeString, - PropertyValue: property.ValueTypeString.ValueFrom("#ff224222"), + ID: id.PropertySchemaFieldID("strokeColor"), + Val: property.NewValueAndDatasetValue( + property.ValueTypeString, + nil, + property.ValueTypeString.ValueFrom("#ff224222"), + ), }, { - ID: id.PropertySchemaFieldID("strokeWidth"), - Type: property.ValueTypeNumber, - PropertyValue: property.ValueTypeNumber.ValueFrom(3), + ID: id.PropertySchemaFieldID("strokeWidth"), + Val: property.NewValueAndDatasetValue( + property.ValueTypeNumber, + nil, + property.ValueTypeNumber.ValueFrom(3), + ), }, }, }, diff --git a/pkg/layer/encoding/shp_test.go b/pkg/layer/encoding/shp_test.go index c29357b7..b7256f5b 100644 --- a/pkg/layer/encoding/shp_test.go +++ b/pkg/layer/encoding/shp_test.go @@ -41,14 +41,16 @@ func TestEncodeSHP(t *testing.T) { SchemaGroup: id.PropertySchemaGroupID("default"), Fields: []*property.SealedField{ { - ID: id.PropertySchemaFieldID("polygon"), - Type: "polygon", - DatasetValue: nil, - PropertyValue: property.ValueTypePolygon.ValueFrom(property.Polygon{property.Coordinates{ - {Lat: 3.4, Lng: 5.34, Height: 100}, - {Lat: 45.4, Lng: 2.34, Height: 100}, - {Lat: 34.66, Lng: 654.34, Height: 100}, - }}), + ID: id.PropertySchemaFieldID("polygon"), + Val: property.NewValueAndDatasetValue( + property.ValueTypePolygon, + nil, + property.ValueTypePolygon.ValueFrom(property.Polygon{property.Coordinates{ + {Lat: 3.4, Lng: 5.34, Height: 100}, + {Lat: 45.4, Lng: 2.34, Height: 100}, + {Lat: 34.66, Lng: 654.34, Height: 100}, + }}), + ), }, }, }, @@ -95,14 +97,16 @@ func TestEncodeSHP(t *testing.T) { SchemaGroup: id.PropertySchemaGroupID("default"), Fields: []*property.SealedField{ { - ID: id.PropertySchemaFieldID("coordinates"), - Type: "polyline", - DatasetValue: nil, - PropertyValue: property.ValueTypeCoordinates.ValueFrom(property.Coordinates{ - {Lat: 3.4, Lng: 5.34, Height: 100}, - {Lat: 45.4, Lng: 2.34, Height: 100}, - {Lat: 34.66, Lng: 654.34, Height: 100}, - }), + ID: id.PropertySchemaFieldID("coordinates"), + Val: property.NewValueAndDatasetValue( + property.ValueTypeCoordinates, + nil, + property.ValueTypeCoordinates.ValueFrom(property.Coordinates{ + {Lat: 3.4, Lng: 5.34, Height: 100}, + {Lat: 45.4, Lng: 2.34, Height: 100}, + {Lat: 34.66, Lng: 654.34, Height: 100}, + }), + ), }, }, }, diff --git a/pkg/property/sealed.go b/pkg/property/sealed.go index e2869a99..2279ad0c 100644 --- a/pkg/property/sealed.go +++ b/pkg/property/sealed.go @@ -25,17 +25,15 @@ type SealedItem struct { } type SealedField struct { - ID id.PropertySchemaFieldID - Type ValueType - DatasetValue *dataset.Value - PropertyValue *Value + ID id.PropertySchemaFieldID + Val *ValueAndDatasetValue } func (f *SealedField) Value() *Value { if f == nil { return nil } - return datasetValueOrPropertyValue(f.DatasetValue, f.PropertyValue) + return f.Val.Value() } func Seal(ctx context.Context, p *Merged, d dataset.GraphLoader) (*Sealed, error) { @@ -109,12 +107,13 @@ func sealedGroup(ctx context.Context, fields []*MergedField, d dataset.GraphLoad if err != nil { return nil, err } - res = append(res, &SealedField{ - ID: f.ID, - Type: f.Type, - PropertyValue: f.Value.Clone(), - DatasetValue: dv.Clone(), - }) + + if val := NewValueAndDatasetValue(f.Type, dv.Clone(), f.Value.Clone()); val != nil { + res = append(res, &SealedField{ + ID: f.ID, + Val: val, + }) + } } return res, nil } @@ -159,13 +158,7 @@ func sealedFieldsInterface(fields []*SealedField) map[string]interface{} { item := map[string]interface{}{} for _, f := range fields { - var v interface{} - if f.DatasetValue != nil { - v = f.DatasetValue.Interface() - } else { - v = f.PropertyValue.Interface() - } - item[f.ID.String()] = v + item[f.ID.String()] = f.Val.Value().Interface() } return item diff --git a/pkg/property/sealed_test.go b/pkg/property/sealed_test.go index 4c5bbea1..209856c8 100644 --- a/pkg/property/sealed_test.go +++ b/pkg/property/sealed_test.go @@ -115,15 +115,20 @@ func TestSeal(t *testing.T) { LinkedDataset: &d, Fields: []*SealedField{ { - ID: "a", - Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFrom("a"), + ID: "a", + Val: NewValueAndDatasetValue( + ValueTypeString, + nil, + ValueTypeString.ValueFrom("a"), + ), }, { - ID: "b", - Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFrom("b"), - DatasetValue: dataset.ValueTypeString.ValueFrom("bbb"), + ID: "b", + Val: NewValueAndDatasetValue( + ValueTypeString, + dataset.ValueTypeString.ValueFrom("bbb"), + ValueTypeString.ValueFrom("b"), + ), }, }, }, @@ -136,15 +141,20 @@ func TestSeal(t *testing.T) { LinkedDataset: &d, Fields: []*SealedField{ { - ID: "a", - Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFrom("aaa"), + ID: "a", + Val: NewValueAndDatasetValue( + ValueTypeString, + nil, + ValueTypeString.ValueFrom("aaa"), + ), }, { - ID: "b", - Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFrom("aaa"), - DatasetValue: dataset.ValueTypeString.ValueFrom("bbb"), + ID: "b", + Val: NewValueAndDatasetValue( + ValueTypeString, + dataset.ValueTypeString.ValueFrom("bbb"), + ValueTypeString.ValueFrom("aaa"), + ), }, }, }, @@ -257,15 +267,20 @@ func TestSealedItemFrom(t *testing.T) { LinkedDataset: &d, Fields: []*SealedField{ { - ID: "a", - Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFrom("a"), + ID: "a", + Val: NewValueAndDatasetValue( + ValueTypeString, + nil, + ValueTypeString.ValueFrom("a"), + ), }, { - ID: "b", - Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFrom("b"), - DatasetValue: dataset.ValueTypeString.ValueFrom("bbb"), + ID: "b", + Val: NewValueAndDatasetValue( + ValueTypeString, + dataset.ValueTypeString.ValueFrom("bbb"), + ValueTypeString.ValueFrom("b"), + ), }, }, }, @@ -320,15 +335,20 @@ func TestSealedItemFrom(t *testing.T) { LinkedDataset: &d, Fields: []*SealedField{ { - ID: "a", - Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFrom("aaa"), + ID: "a", + Val: NewValueAndDatasetValue( + ValueTypeString, + nil, + ValueTypeString.ValueFrom("aaa"), + ), }, { - ID: "b", - Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFrom("aaa"), - DatasetValue: dataset.ValueTypeString.ValueFrom("bbb"), + ID: "b", + Val: NewValueAndDatasetValue( + ValueTypeString, + dataset.ValueTypeString.ValueFrom("bbb"), + ValueTypeString.ValueFrom("aaa"), + ), }, }, }, @@ -380,15 +400,20 @@ func TestSealed_Interface(t *testing.T) { LinkedDataset: &d, Fields: []*SealedField{ { - ID: "a", - Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFrom("a"), + ID: "a", + Val: NewValueAndDatasetValue( + ValueTypeString, + nil, + ValueTypeString.ValueFrom("a"), + ), }, { - ID: "b", - Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFrom("b"), - DatasetValue: dataset.ValueTypeString.ValueFrom("bbb"), + ID: "b", + Val: NewValueAndDatasetValue( + ValueTypeString, + dataset.ValueTypeString.ValueFrom("bbb"), + ValueTypeString.ValueFrom("b"), + ), }, }, }, @@ -401,15 +426,20 @@ func TestSealed_Interface(t *testing.T) { LinkedDataset: &d, Fields: []*SealedField{ { - ID: "a", - Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFrom("aaa"), + ID: "a", + Val: NewValueAndDatasetValue( + ValueTypeString, + nil, + ValueTypeString.ValueFrom("aaa"), + ), }, { - ID: "b", - Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFrom("aaa"), - DatasetValue: dataset.ValueTypeString.ValueFrom("bbb"), + ID: "b", + Val: NewValueAndDatasetValue( + ValueTypeString, + dataset.ValueTypeString.ValueFrom("bbb"), + ValueTypeString.ValueFrom("aaa"), + ), }, }, }, @@ -465,15 +495,20 @@ func TestSealedItem_Match(t *testing.T) { LinkedDataset: &d, Fields: []*SealedField{ { - ID: "a", - Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFrom("a"), + ID: "a", + Val: NewValueAndDatasetValue( + ValueTypeString, + nil, + ValueTypeString.ValueFrom("a"), + ), }, { - ID: "b", - Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFrom("b"), - DatasetValue: dataset.ValueTypeString.ValueFrom("bbb"), + ID: "b", + Val: NewValueAndDatasetValue( + ValueTypeString, + dataset.ValueTypeString.ValueFrom("bbb"), + ValueTypeString.ValueFrom("b"), + ), }, }, }, @@ -525,15 +560,20 @@ func TestSealed_ItemBy(t *testing.T) { LinkedDataset: &d, Fields: []*SealedField{ { - ID: "a", - Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFrom("a"), + ID: "a", + Val: NewValueAndDatasetValue( + ValueTypeString, + nil, + ValueTypeString.ValueFrom("a"), + ), }, { - ID: "b", - Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFrom("b"), - DatasetValue: dataset.ValueTypeString.ValueFrom("bbb"), + ID: "b", + Val: NewValueAndDatasetValue( + ValueTypeString, + dataset.ValueTypeString.ValueFrom("bbb"), + ValueTypeString.ValueFrom("b"), + ), }, }, }, @@ -546,15 +586,20 @@ func TestSealed_ItemBy(t *testing.T) { LinkedDataset: &d, Fields: []*SealedField{ { - ID: "a", - Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFrom("aaa"), + ID: "a", + Val: NewValueAndDatasetValue( + ValueTypeString, + nil, + ValueTypeString.ValueFrom("b"), + ), }, { - ID: "b", - Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFrom("aaa"), - DatasetValue: dataset.ValueTypeString.ValueFrom("bbb"), + ID: "b", + Val: NewValueAndDatasetValue( + ValueTypeString, + dataset.ValueTypeString.ValueFrom("bbb"), + ValueTypeString.ValueFrom("aaa"), + ), }, }, }, @@ -573,15 +618,20 @@ func TestSealed_ItemBy(t *testing.T) { LinkedDataset: &d, Fields: []*SealedField{ { - ID: "a", - Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFrom("a"), + ID: "a", + Val: NewValueAndDatasetValue( + ValueTypeString, + nil, + ValueTypeString.ValueFrom("a"), + ), }, { - ID: "b", - Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFrom("b"), - DatasetValue: dataset.ValueTypeString.ValueFrom("bbb"), + ID: "b", + Val: NewValueAndDatasetValue( + ValueTypeString, + dataset.ValueTypeString.ValueFrom("bbb"), + ValueTypeString.ValueFrom("b"), + ), }, }, }, @@ -608,15 +658,20 @@ func TestSealed_ItemBy(t *testing.T) { LinkedDataset: &d, Fields: []*SealedField{ { - ID: "a", - Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFrom("a"), + ID: "a", + Val: NewValueAndDatasetValue( + ValueTypeString, + nil, + ValueTypeString.ValueFrom("a"), + ), }, { - ID: "b", - Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFrom("b"), - DatasetValue: dataset.ValueTypeString.ValueFrom("bbb"), + ID: "b", + Val: NewValueAndDatasetValue( + ValueTypeString, + dataset.ValueTypeString.ValueFrom("bbb"), + ValueTypeString.ValueFrom("b"), + ), }, }, }, @@ -629,15 +684,20 @@ func TestSealed_ItemBy(t *testing.T) { LinkedDataset: &d, Fields: []*SealedField{ { - ID: "a", - Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFrom("aaa"), + ID: "a", + Val: NewValueAndDatasetValue( + ValueTypeString, + nil, + ValueTypeString.ValueFrom("aaa"), + ), }, { - ID: "b", - Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFrom("aaa"), - DatasetValue: dataset.ValueTypeString.ValueFrom("bbb"), + ID: "b", + Val: NewValueAndDatasetValue( + ValueTypeString, + dataset.ValueTypeString.ValueFrom("bbb"), + ValueTypeString.ValueFrom("aaa"), + ), }, }, }, @@ -656,15 +716,20 @@ func TestSealed_ItemBy(t *testing.T) { LinkedDataset: &d, Fields: []*SealedField{ { - ID: "a", - Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFrom("a"), + ID: "a", + Val: NewValueAndDatasetValue( + ValueTypeString, + nil, + ValueTypeString.ValueFrom("a"), + ), }, { - ID: "b", - Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFrom("b"), - DatasetValue: dataset.ValueTypeString.ValueFrom("bbb"), + ID: "b", + Val: NewValueAndDatasetValue( + ValueTypeString, + dataset.ValueTypeString.ValueFrom("bbb"), + ValueTypeString.ValueFrom("b"), + ), }, }, }, @@ -691,15 +756,20 @@ func TestSealed_ItemBy(t *testing.T) { LinkedDataset: &d, Fields: []*SealedField{ { - ID: "a", - Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFrom("a"), + ID: "a", + Val: NewValueAndDatasetValue( + ValueTypeString, + nil, + ValueTypeString.ValueFrom("a"), + ), }, { - ID: "b", - Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFrom("b"), - DatasetValue: dataset.ValueTypeString.ValueFrom("bbb"), + ID: "b", + Val: NewValueAndDatasetValue( + ValueTypeString, + dataset.ValueTypeString.ValueFrom("bbb"), + ValueTypeString.ValueFrom("b"), + ), }, }, }, @@ -712,15 +782,20 @@ func TestSealed_ItemBy(t *testing.T) { LinkedDataset: &d, Fields: []*SealedField{ { - ID: "a", - Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFrom("aaa"), + ID: "a", + Val: NewValueAndDatasetValue( + ValueTypeString, + nil, + ValueTypeString.ValueFrom("aaa"), + ), }, { - ID: "b", - Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFrom("aaa"), - DatasetValue: dataset.ValueTypeString.ValueFrom("bbb"), + ID: "b", + Val: NewValueAndDatasetValue( + ValueTypeString, + dataset.ValueTypeString.ValueFrom("bbb"), + ValueTypeString.ValueFrom("aaa"), + ), }, }, }, @@ -772,15 +847,20 @@ func TestSealed_FieldBy(t *testing.T) { LinkedDataset: &d, Fields: []*SealedField{ { - ID: "a", - Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFrom("a"), + ID: "a", + Val: NewValueAndDatasetValue( + ValueTypeString, + nil, + ValueTypeString.ValueFrom("a"), + ), }, { - ID: "b", - Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFrom("b"), - DatasetValue: dataset.ValueTypeString.ValueFrom("bbb"), + ID: "b", + Val: NewValueAndDatasetValue( + ValueTypeString, + dataset.ValueTypeString.ValueFrom("bbb"), + ValueTypeString.ValueFrom("b"), + ), }, }, }, @@ -793,15 +873,20 @@ func TestSealed_FieldBy(t *testing.T) { LinkedDataset: &d, Fields: []*SealedField{ { - ID: "a", - Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFrom("aaa"), + ID: "a", + Val: NewValueAndDatasetValue( + ValueTypeString, + nil, + ValueTypeString.ValueFrom("aaa"), + ), }, { - ID: "b", - Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFrom("aaa"), - DatasetValue: dataset.ValueTypeString.ValueFrom("bbb"), + ID: "b", + Val: NewValueAndDatasetValue( + ValueTypeString, + dataset.ValueTypeString.ValueFrom("bbb"), + ValueTypeString.ValueFrom("aaa"), + ), }, }, }, @@ -809,9 +894,12 @@ func TestSealed_FieldBy(t *testing.T) { }, Input: NewPointer(psiid1.Ref(), i1id.Ref(), id.PropertySchemaFieldID("a").Ref()), Expected: &SealedField{ - ID: "a", - Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFrom("aaa"), + ID: "a", + Val: NewValueAndDatasetValue( + ValueTypeString, + nil, + ValueTypeString.ValueFrom("aaa"), + ), }, }, { @@ -834,15 +922,20 @@ func TestSealed_FieldBy(t *testing.T) { LinkedDataset: &d, Fields: []*SealedField{ { - ID: "a", - Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFrom("a"), + ID: "a", + Val: NewValueAndDatasetValue( + ValueTypeString, + nil, + ValueTypeString.ValueFrom("a"), + ), }, { - ID: "b", - Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFrom("b"), - DatasetValue: dataset.ValueTypeString.ValueFrom("bbb"), + ID: "b", + Val: NewValueAndDatasetValue( + ValueTypeString, + dataset.ValueTypeString.ValueFrom("b"), + ValueTypeString.ValueFrom("bbb"), + ), }, }, }, @@ -855,15 +948,20 @@ func TestSealed_FieldBy(t *testing.T) { LinkedDataset: &d, Fields: []*SealedField{ { - ID: "a", - Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFrom("aaa"), + ID: "a", + Val: NewValueAndDatasetValue( + ValueTypeString, + nil, + ValueTypeString.ValueFrom("aaa"), + ), }, { - ID: "b", - Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFrom("aaa"), - DatasetValue: dataset.ValueTypeString.ValueFrom("bbb"), + ID: "b", + Val: NewValueAndDatasetValue( + ValueTypeString, + dataset.ValueTypeString.ValueFrom("bbb"), + ValueTypeString.ValueFrom("aaa"), + ), }, }, }, @@ -871,9 +969,12 @@ func TestSealed_FieldBy(t *testing.T) { }, Input: NewPointer(nil, i3id.Ref(), id.PropertySchemaFieldID("a").Ref()), Expected: &SealedField{ - ID: "a", - Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFrom("aaa"), + ID: "a", + Val: NewValueAndDatasetValue( + ValueTypeString, + nil, + ValueTypeString.ValueFrom("aaa"), + ), }, }, { @@ -896,15 +997,20 @@ func TestSealed_FieldBy(t *testing.T) { LinkedDataset: &d, Fields: []*SealedField{ { - ID: "a", - Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFrom("a"), + ID: "a", + Val: NewValueAndDatasetValue( + ValueTypeString, + nil, + ValueTypeString.ValueFrom("b"), + ), }, { - ID: "b", - Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFrom("b"), - DatasetValue: dataset.ValueTypeString.ValueFrom("bbb"), + ID: "b", + Val: NewValueAndDatasetValue( + ValueTypeString, + dataset.ValueTypeString.ValueFrom("bbb"), + ValueTypeString.ValueFrom("b"), + ), }, }, }, @@ -917,15 +1023,20 @@ func TestSealed_FieldBy(t *testing.T) { LinkedDataset: &d, Fields: []*SealedField{ { - ID: "a", - Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFrom("aaa"), + ID: "a", + Val: NewValueAndDatasetValue( + ValueTypeString, + nil, + ValueTypeString.ValueFrom("aaa"), + ), }, { - ID: "b", - Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFrom("aaa"), - DatasetValue: dataset.ValueTypeString.ValueFrom("bbb"), + ID: "b", + Val: NewValueAndDatasetValue( + ValueTypeString, + dataset.ValueTypeString.ValueFrom("bbb"), + ValueTypeString.ValueFrom("aaa"), + ), }, }, }, @@ -933,9 +1044,12 @@ func TestSealed_FieldBy(t *testing.T) { }, Input: NewPointer(nil, nil, id.PropertySchemaFieldID("a").Ref()), Expected: &SealedField{ - ID: "a", - Type: ValueTypeString, - PropertyValue: ValueTypeString.ValueFrom("aaa"), + ID: "a", + Val: NewValueAndDatasetValue( + ValueTypeString, + nil, + ValueTypeString.ValueFrom("aaa"), + ), }, }, } diff --git a/pkg/property/value.go b/pkg/property/value.go index c01d3152..d4494160 100644 --- a/pkg/property/value.go +++ b/pkg/property/value.go @@ -4,7 +4,6 @@ import ( "net/url" "strconv" - "github.com/reearth/reearth-backend/pkg/dataset" "github.com/reearth/reearth-backend/pkg/value" ) @@ -215,14 +214,3 @@ func ValueFromStringOrNumber(s string) *Value { return ValueTypeString.ValueFrom(s) } - -func valueFromDataset(v *dataset.Value) *Value { - return ValueType(value.Type(v.Type())).ValueFrom(v.Value()) -} - -func datasetValueOrPropertyValue(dv *dataset.Value, pv *Value) *Value { - if dv != nil { - return valueFromDataset(dv) - } - return pv -} diff --git a/pkg/property/value_dataset.go b/pkg/property/value_dataset.go new file mode 100644 index 00000000..3443ad0f --- /dev/null +++ b/pkg/property/value_dataset.go @@ -0,0 +1,67 @@ +package property + +import ( + "github.com/reearth/reearth-backend/pkg/dataset" + "github.com/reearth/reearth-backend/pkg/value" +) + +type ValueAndDatasetValue struct { + t ValueType + d *dataset.Value + p *Value +} + +func NewValueAndDatasetValue(ty ValueType, d *dataset.Value, p *Value) *ValueAndDatasetValue { + if !ty.Valid() { + return nil + } + + if d != nil && ValueType(d.Type()) != ty { + d = nil + } + + if p != nil && p.Type() != ty { + p = nil + } + + return &ValueAndDatasetValue{ + t: ty, + d: d, + p: p, + } +} + +func (v *ValueAndDatasetValue) Type() ValueType { + if v == nil { + return ValueTypeUnknown + } + return v.t +} + +func (v *ValueAndDatasetValue) DatasetValue() *dataset.Value { + if v == nil || v.t == ValueTypeUnknown { + return nil + } + return v.d +} + +func (v *ValueAndDatasetValue) PropertyValue() *Value { + if v == nil || v.t == ValueTypeUnknown { + return nil + } + return v.p +} + +func (v *ValueAndDatasetValue) Value() *Value { + if v == nil || v.t == ValueTypeUnknown { + return nil + } + if v.d != nil { + return valueFromDataset(v.d) + } + return v.p +} + +func valueFromDataset(v *dataset.Value) *Value { + return ValueType(value.Type(v.Type())).ValueFrom(v.Value()) +} diff --git a/pkg/property/value_dataset_test.go b/pkg/property/value_dataset_test.go new file mode 100644 index 00000000..ecaefd48 --- /dev/null +++ b/pkg/property/value_dataset_test.go @@ -0,0 +1,295 @@ +package property + +import ( + "testing" + + "github.com/reearth/reearth-backend/pkg/dataset" + "github.com/stretchr/testify/assert" +) + +func TestNewValueAndDatasetValue(t *testing.T) { + type args struct { + ty ValueType + d *dataset.Value + p *Value + } + tests := []struct { + name string + args args + want *ValueAndDatasetValue + }{ + { + name: "ok", + args: args{ + ty: ValueTypeBool, + d: dataset.ValueTypeBool.ValueFrom(false), + p: ValueTypeBool.ValueFrom(true), + }, + want: &ValueAndDatasetValue{ + t: ValueTypeBool, + d: dataset.ValueTypeBool.ValueFrom(false), + p: ValueTypeBool.ValueFrom(true), + }, + }, + { + name: "invalid type", + args: args{ + ty: ValueType("foobar"), + d: dataset.ValueTypeBool.ValueFrom(false), + p: ValueTypeBool.ValueFrom(true), + }, + want: nil, + }, + { + name: "invalid dataset value", + args: args{ + ty: ValueTypeBool, + d: dataset.ValueTypeString.ValueFrom("false"), + p: ValueTypeBool.ValueFrom(true), + }, + want: &ValueAndDatasetValue{ + t: ValueTypeBool, + d: nil, + p: ValueTypeBool.ValueFrom(true), + }, + }, + { + name: "invalid property value", + args: args{ + ty: ValueTypeBool, + d: dataset.ValueTypeBool.ValueFrom(false), + p: ValueTypeString.ValueFrom("true"), + }, + want: &ValueAndDatasetValue{ + t: ValueTypeBool, + d: dataset.ValueTypeBool.ValueFrom(false), + p: nil, + }, + }, + { + name: "nil dataset value", + args: args{ + ty: ValueTypeBool, + d: nil, + p: ValueTypeBool.ValueFrom(false), + }, + want: &ValueAndDatasetValue{ + t: ValueTypeBool, + d: nil, + p: ValueTypeBool.ValueFrom(false), + }, + }, + { + name: "nil property value", + args: args{ + ty: ValueTypeBool, + d: dataset.ValueTypeBool.ValueFrom(false), + p: nil, + }, + want: &ValueAndDatasetValue{ + t: ValueTypeBool, + d: dataset.ValueTypeBool.ValueFrom(false), + p: nil, + }, + }, + { + name: "nil value", + args: args{ + ty: ValueTypeBool, + d: nil, + p: nil, + }, + want: &ValueAndDatasetValue{ + t: ValueTypeBool, + d: nil, + p: nil, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.want, NewValueAndDatasetValue(tt.args.ty, tt.args.d, tt.args.p)) + }) + } +} + +func TestValueAndDatasetValue_Type(t *testing.T) { + tests := []struct { + name string + target *ValueAndDatasetValue + want ValueType + }{ + { + name: "ok", + target: &ValueAndDatasetValue{t: ValueTypeString}, + want: ValueTypeString, + }, + { + name: "empty", + target: &ValueAndDatasetValue{}, + want: ValueTypeUnknown, + }, + { + name: "nil", + target: nil, + want: ValueTypeUnknown, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.want, tt.target.Type()) + }) + } +} + +func TestValueAndDatasetValue_DatasetValuee(t *testing.T) { + tests := []struct { + name string + target *ValueAndDatasetValue + want *dataset.Value + }{ + { + name: "dataset only", + target: &ValueAndDatasetValue{ + t: ValueTypeString, + d: dataset.ValueTypeString.ValueFrom("foo"), + }, + want: dataset.ValueTypeString.ValueFrom("foo"), + }, + { + name: "property only", + target: &ValueAndDatasetValue{ + t: ValueTypeString, + p: ValueTypeString.ValueFrom("bar"), + }, + want: nil, + }, + { + name: "dataset and property", + target: &ValueAndDatasetValue{ + t: ValueTypeString, + d: dataset.ValueTypeString.ValueFrom("foo"), + p: ValueTypeString.ValueFrom("bar"), + }, + want: dataset.ValueTypeString.ValueFrom("foo"), + }, + { + name: "empty", + target: &ValueAndDatasetValue{}, + want: nil, + }, + { + name: "nil", + target: nil, + want: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.want, tt.target.DatasetValue()) + }) + } +} + +func TestValueAndDatasetValue_PropertyValue(t *testing.T) { + tests := []struct { + name string + target *ValueAndDatasetValue + want *Value + }{ + { + name: "dataset only", + target: &ValueAndDatasetValue{ + t: ValueTypeString, + d: dataset.ValueTypeString.ValueFrom("foo"), + }, + want: nil, + }, + { + name: "property only", + target: &ValueAndDatasetValue{ + t: ValueTypeString, + p: ValueTypeString.ValueFrom("bar"), + }, + want: ValueTypeString.ValueFrom("bar"), + }, + { + name: "dataset and property", + target: &ValueAndDatasetValue{ + t: ValueTypeString, + d: dataset.ValueTypeString.ValueFrom("foo"), + p: ValueTypeString.ValueFrom("bar"), + }, + want: ValueTypeString.ValueFrom("bar"), + }, + { + name: "empty", + target: &ValueAndDatasetValue{}, + want: nil, + }, + { + name: "nil", + target: nil, + want: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.want, tt.target.PropertyValue()) + }) + } +} + +func TestValueAndDatasetValue_Value(t *testing.T) { + tests := []struct { + name string + target *ValueAndDatasetValue + want *Value + }{ + { + name: "dataset only", + target: &ValueAndDatasetValue{ + t: ValueTypeString, + d: dataset.ValueTypeString.ValueFrom("foo"), + }, + want: ValueTypeString.ValueFrom("foo"), + }, + { + name: "property only", + target: &ValueAndDatasetValue{ + t: ValueTypeString, + p: ValueTypeString.ValueFrom("bar"), + }, + want: ValueTypeString.ValueFrom("bar"), + }, + { + name: "dataset and property", + target: &ValueAndDatasetValue{ + t: ValueTypeString, + d: dataset.ValueTypeString.ValueFrom("foo"), + p: ValueTypeString.ValueFrom("bar"), + }, + want: ValueTypeString.ValueFrom("foo"), + }, + { + name: "empty", + target: &ValueAndDatasetValue{}, + want: nil, + }, + { + name: "nil", + target: nil, + want: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.want, tt.target.Value()) + }) + } +} diff --git a/pkg/scene/builder/encoder_test.go b/pkg/scene/builder/encoder_test.go index 9d3db9b5..0bb83d02 100644 --- a/pkg/scene/builder/encoder_test.go +++ b/pkg/scene/builder/encoder_test.go @@ -99,10 +99,12 @@ func TestEncoder_Layers(t *testing.T) { } f1 := property.SealedField{ - ID: id.PropertySchemaFieldID("location"), - Type: "latlng", - DatasetValue: nil, - PropertyValue: property.ValueTypeLatLng.ValueFrom(v1), + ID: id.PropertySchemaFieldID("location"), + Val: property.NewValueAndDatasetValue( + property.ValueTypeLatLng, + nil, + property.ValueTypeLatLng.ValueFrom(v1), + ), } fl1 := []*property.SealedField{} fl1 = append(fl1, &f1) From 1053a225e44761bd508fd6546fcaba77ee7d835e Mon Sep 17 00:00:00 2001 From: rot1024 Date: Thu, 18 Nov 2021 21:52:09 +0900 Subject: [PATCH 07/12] add OptionalValue.Clone --- pkg/dataset/value_optional.go | 13 ++++++++++++ pkg/dataset/value_optional_test.go | 31 +++++++++++++++++++++++++++++ pkg/property/value_optional.go | 13 ++++++++++++ pkg/property/value_optional_test.go | 31 +++++++++++++++++++++++++++++ pkg/value/optional.go | 10 ++++++++++ pkg/value/optional_test.go | 30 ++++++++++++++++++++++++++++ 6 files changed, 128 insertions(+) diff --git a/pkg/dataset/value_optional.go b/pkg/dataset/value_optional.go index c3a4eefc..61affc8c 100644 --- a/pkg/dataset/value_optional.go +++ b/pkg/dataset/value_optional.go @@ -63,3 +63,16 @@ func (ov *OptionalValue) SetValue(v *Value) { ov.ov.SetValue(&v.v) } } + +func (ov *OptionalValue) Clone() *OptionalValue { + if ov == nil { + return nil + } + nov := ov.ov.Clone() + if nov == nil { + return nil + } + return &OptionalValue{ + ov: *nov, + } +} diff --git a/pkg/dataset/value_optional_test.go b/pkg/dataset/value_optional_test.go index 01d0212c..8767264a 100644 --- a/pkg/dataset/value_optional_test.go +++ b/pkg/dataset/value_optional_test.go @@ -270,3 +270,34 @@ func TestOptionalValue_SetValue(t *testing.T) { }) } } + +func TestOptionalValue_Clone(t *testing.T) { + tests := []struct { + name string + target *OptionalValue + }{ + { + name: "ok", + target: &OptionalValue{ + ov: *value.NewOptionalValue(value.TypeString, value.TypeString.ValueFrom("foo", nil)), + }, + }, + { + name: "empty", + target: &OptionalValue{}, + }, + { + name: "nil", + target: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + res := tt.target.Clone() + assert.Equal(t, tt.target, res) + if tt.target != nil { + assert.NotSame(t, tt.target, res) + } + }) + } +} diff --git a/pkg/property/value_optional.go b/pkg/property/value_optional.go index fa8311d4..6e01c84b 100644 --- a/pkg/property/value_optional.go +++ b/pkg/property/value_optional.go @@ -53,6 +53,19 @@ func (ov *OptionalValue) TypeAndValue() (ValueType, *Value) { return ov.Type(), ov.Value() } +func (ov *OptionalValue) Clone() *OptionalValue { + if ov == nil { + return nil + } + nov := ov.ov.Clone() + if nov == nil { + return nil + } + return &OptionalValue{ + ov: *nov, + } +} + func (ov *OptionalValue) SetValue(v *Value) { if ov == nil { return diff --git a/pkg/property/value_optional_test.go b/pkg/property/value_optional_test.go index eb820e4e..61e35b02 100644 --- a/pkg/property/value_optional_test.go +++ b/pkg/property/value_optional_test.go @@ -270,3 +270,34 @@ func TestOptionalValue_SetValue(t *testing.T) { }) } } + +func TestOptionalValue_Clone(t *testing.T) { + tests := []struct { + name string + target *OptionalValue + }{ + { + name: "ok", + target: &OptionalValue{ + ov: *value.NewOptionalValue(value.TypeString, value.TypeString.ValueFrom("foo", types)), + }, + }, + { + name: "empty", + target: &OptionalValue{}, + }, + { + name: "nil", + target: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + res := tt.target.Clone() + assert.Equal(t, tt.target, res) + if tt.target != nil { + assert.NotSame(t, tt.target, res) + } + }) + } +} diff --git a/pkg/value/optional.go b/pkg/value/optional.go index 2a904c15..2b0b230e 100644 --- a/pkg/value/optional.go +++ b/pkg/value/optional.go @@ -49,3 +49,13 @@ func (ov *OptionalValue) SetValue(v *Value) { } ov.v = v.Clone() } + +func (ov *OptionalValue) Clone() *OptionalValue { + if ov == nil { + return nil + } + return &OptionalValue{ + t: ov.t, + v: ov.v.Clone(), + } +} diff --git a/pkg/value/optional_test.go b/pkg/value/optional_test.go index 8dc15d85..19e0f601 100644 --- a/pkg/value/optional_test.go +++ b/pkg/value/optional_test.go @@ -298,3 +298,33 @@ func TestOptionalValue_SetValue(t *testing.T) { }) } } + +func TestOptionalValue_Clone(t *testing.T) { + tests := []struct { + name string + target *OptionalValue + }{ + { + name: "ok", + target: &OptionalValue{t: TypeString, v: TypeString.ValueFrom("foo", nil)}, + }, + { + name: "empty", + target: &OptionalValue{}, + }, + { + name: "nil", + target: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + res := tt.target.Clone() + assert.Equal(t, tt.target, res) + if tt.target != nil { + assert.NotSame(t, tt.target, res) + } + }) + } +} From 03801bed030baddb565368d401e975349ac91fe0 Mon Sep 17 00:00:00 2001 From: rot1024 Date: Thu, 18 Nov 2021 22:24:44 +0900 Subject: [PATCH 08/12] use property.OptionalValue at property.Field --- .../infrastructure/mongo/mongodoc/property.go | 3 +- pkg/property/builder_test.go | 18 +++----- pkg/property/field.go | 45 ++++++++++++------- pkg/property/field_builder.go | 45 +++---------------- pkg/property/field_builder_test.go | 10 ++--- pkg/property/field_test.go | 12 ++--- pkg/property/group_builder_test.go | 4 +- pkg/property/group_list_test.go | 10 ++--- pkg/property/group_test.go | 24 +++++----- pkg/property/initializer.go | 2 +- pkg/property/initializer_test.go | 7 ++- pkg/property/item_test.go | 3 +- pkg/property/merged_test.go | 16 +++---- pkg/property/property_test.go | 12 ++--- pkg/property/schema_field.go | 12 ++--- pkg/property/schema_field_test.go | 27 ++++++++--- pkg/scene/builder/builder_test.go | 43 +++++++----------- 17 files changed, 130 insertions(+), 163 deletions(-) diff --git a/internal/infrastructure/mongo/mongodoc/property.go b/internal/infrastructure/mongo/mongodoc/property.go index 0ee1680b..3e962bac 100644 --- a/internal/infrastructure/mongo/mongodoc/property.go +++ b/internal/infrastructure/mongo/mongodoc/property.go @@ -221,8 +221,7 @@ func toModelPropertyField(f *PropertyFieldDocument) *property.Field { vt := property.ValueType(f.Type) field := property.NewFieldUnsafe(). FieldUnsafe(id.PropertySchemaFieldID(f.Field)). - TypeUnsafe(vt). - ValueUnsafe(toModelPropertyValue(f.Value, f.Type)). + ValueUnsafe(property.NewOptionalValue(vt, toModelPropertyValue(f.Value, f.Type))). LinksUnsafe(flinks). Build() diff --git a/pkg/property/builder_test.go b/pkg/property/builder_test.go index 646b0f6d..55171030 100644 --- a/pkg/property/builder_test.go +++ b/pkg/property/builder_test.go @@ -57,16 +57,14 @@ func TestBuilder_Items(t *testing.T) { Fields([]*Field{ NewFieldUnsafe(). FieldUnsafe(propertySchemaField1ID). - TypeUnsafe(ValueTypeString). - ValueUnsafe(ValueTypeString.ValueFrom("xxx")). + ValueUnsafe(OptionalValueFrom(ValueTypeString.ValueFrom("xxx"))). Build(), }).MustBuild(), NewGroup().ID(iid).Schema(propertySchemaID, propertySchemaGroup1ID). Fields([]*Field{ NewFieldUnsafe(). FieldUnsafe(propertySchemaField1ID). - TypeUnsafe(ValueTypeString). - ValueUnsafe(ValueTypeString.ValueFrom("xxx")). + ValueUnsafe(OptionalValueFrom(ValueTypeString.ValueFrom("xxx"))). Build(), }).MustBuild(), }, @@ -74,8 +72,7 @@ func TestBuilder_Items(t *testing.T) { Fields([]*Field{ NewFieldUnsafe(). FieldUnsafe(propertySchemaField1ID). - TypeUnsafe(ValueTypeString). - ValueUnsafe(ValueTypeString.ValueFrom("xxx")). + ValueUnsafe(OptionalValueFrom(ValueTypeString.ValueFrom("xxx"))). Build(), }).MustBuild()}, }, @@ -127,8 +124,7 @@ func TestBuilder_Build(t *testing.T) { Fields([]*Field{ NewFieldUnsafe(). FieldUnsafe(propertySchemaField1ID). - TypeUnsafe(ValueTypeString). - ValueUnsafe(ValueTypeString.ValueFrom("xxx")). + ValueUnsafe(OptionalValueFrom(ValueTypeString.ValueFrom("xxx"))). Build(), }).MustBuild()}, Expected: struct { @@ -145,8 +141,7 @@ func TestBuilder_Build(t *testing.T) { Fields([]*Field{ NewFieldUnsafe(). FieldUnsafe(propertySchemaField1ID). - TypeUnsafe(ValueTypeString). - ValueUnsafe(ValueTypeString.ValueFrom("xxx")). + ValueUnsafe(OptionalValueFrom(ValueTypeString.ValueFrom("xxx"))). Build(), }).MustBuild()}, }, @@ -180,8 +175,7 @@ func TestBuilder_Build(t *testing.T) { Fields([]*Field{ NewFieldUnsafe(). FieldUnsafe(propertySchemaField1ID). - TypeUnsafe(ValueTypeString). - ValueUnsafe(ValueTypeString.ValueFrom("xxx")). + ValueUnsafe(OptionalValueFrom(ValueTypeString.ValueFrom("xxx"))). Build(), }).MustBuild()}, Err: ErrInvalidItem, diff --git a/pkg/property/field.go b/pkg/property/field.go index d068af73..d38ffa13 100644 --- a/pkg/property/field.go +++ b/pkg/property/field.go @@ -17,17 +17,15 @@ var ( type Field struct { field id.PropertySchemaFieldID - ptype ValueType links *Links - value *Value + v *OptionalValue } func (p *Field) Clone() *Field { return &Field{ field: p.field, - ptype: p.ptype, - value: p.value.Clone(), links: p.links.Clone(), + v: p.v.Clone(), } } @@ -43,14 +41,17 @@ func (p *Field) Links() *Links { } func (p *Field) Type() ValueType { - return p.ptype + if p == nil { + return ValueTypeUnknown + } + return p.v.Type() } func (p *Field) Value() *Value { if p == nil { return nil } - return p.value + return p.v.Value() } func (p *Field) ActualValue(ds *dataset.Dataset) *Value { @@ -66,7 +67,7 @@ func (p *Field) ActualValue(ds *dataset.Dataset) *Value { } return nil } - return p.value + return p.Value() } func (p *Field) HasLinkedField() bool { @@ -94,26 +95,41 @@ func (p *Field) IsDatasetLinked(s id.DatasetSchemaID, i id.DatasetID) bool { } func (p *Field) Update(value *Value, field *SchemaField) error { - if field == nil || p.field != field.ID() || !field.Validate(value) { + if p == nil { + return nil + } + if field == nil || p.field != field.ID() || !field.Validate(p.v) { return ErrInvalidPropertyValue } - p.value = value + p.v.SetValue(value) return nil } func (p *Field) UpdateUnsafe(value *Value) { - p.value = value + if p == nil { + return + } + p.v.SetValue(value) } func (p *Field) Link(links *Links) { + if p == nil { + return + } p.links = links.Clone() } func (p *Field) Unlink() { + if p == nil { + return + } p.links = nil } func (p *Field) UpdateField(field id.PropertySchemaFieldID) { + if p == nil { + return + } p.field = field } @@ -133,7 +149,7 @@ func (p *Field) MigrateSchema(ctx context.Context, newSchema *Schema, dl dataset invalid := schemaField == nil // if value is not compatible for type, value will be cleared - if !schemaField.Validate(p.Value()) { + if !schemaField.Validate(p.v) { p.UpdateUnsafe(nil) } @@ -178,11 +194,8 @@ func (p *Field) ValidateSchema(ps *SchemaField) error { if ps == nil { return errors.New("schema not found") } - if p.ptype != ps.Type() { - return errors.New("invalid field type") - } - if p.ptype != p.value.Type() { - return errors.New("invalid field value type") + if p.v == nil { + return errors.New("invalid field value and type") } return nil } diff --git a/pkg/property/field_builder.go b/pkg/property/field_builder.go index 3bd7ab99..00bbbae5 100644 --- a/pkg/property/field_builder.go +++ b/pkg/property/field_builder.go @@ -2,18 +2,15 @@ package property import "github.com/reearth/reearth-backend/pkg/id" -// FieldBuilder _ type FieldBuilder struct { p *Field psf *SchemaField } -// FieldUnsafeBuilder _ type FieldUnsafeBuilder struct { p *Field } -// NewField _ func NewField(p *SchemaField) *FieldBuilder { b := &FieldBuilder{ p: &Field{}, @@ -21,18 +18,16 @@ func NewField(p *SchemaField) *FieldBuilder { return b.schemaField(p) } -// Build _ func (b *FieldBuilder) Build() (*Field, error) { if b.p.field == id.PropertySchemaFieldID("") { return nil, id.ErrInvalidID } - if b.psf != nil && !b.psf.Validate(b.p.value) { + if b.psf != nil && !b.psf.Validate(b.p.v) { return nil, ErrInvalidPropertyValue } return b.p, nil } -// MustBuild _ func (b *FieldBuilder) MustBuild() *Field { p, err := b.Build() if err != nil { @@ -45,69 +40,41 @@ func (b *FieldBuilder) schemaField(p *SchemaField) *FieldBuilder { if p != nil { b.psf = p b.p.field = p.ID() - b.p.ptype = p.Type() - if dv := p.DefaultValue(); dv != nil { - dv2 := *dv - b.p.value = &dv2 - } + b.p.v = NewOptionalValue(p.Type(), p.DefaultValue().Clone()) } return b } -// Value _ -func (b *FieldBuilder) Value(v *Value) *FieldBuilder { - if b.p.field == id.PropertySchemaFieldID("") { - return b - } - v2 := *v - b.p.value = &v2 +func (b *FieldBuilder) Value(v *OptionalValue) *FieldBuilder { + b.p.v = v.Clone() return b } -// Link _ func (b *FieldBuilder) Link(l *Links) *FieldBuilder { b.p.links = l.Clone() return b } -// NewFieldUnsafe _ func NewFieldUnsafe() *FieldUnsafeBuilder { return &FieldUnsafeBuilder{ p: &Field{}, } } -// Build _ func (b *FieldUnsafeBuilder) Build() *Field { return b.p } -// FieldUnsafe _ func (b *FieldUnsafeBuilder) FieldUnsafe(f id.PropertySchemaFieldID) *FieldUnsafeBuilder { b.p.field = f return b } -// TypeUnsafe _ -func (b *FieldUnsafeBuilder) TypeUnsafe(t ValueType) *FieldUnsafeBuilder { - b.p.ptype = t - return b -} - -// ValueUnsafe _ -func (b *FieldUnsafeBuilder) ValueUnsafe(v *Value) *FieldUnsafeBuilder { - if v == nil { - b.p.value = nil - return b - } - - v2 := *v - b.p.value = &v2 - b.p.ptype = v.Type() +func (b *FieldUnsafeBuilder) ValueUnsafe(v *OptionalValue) *FieldUnsafeBuilder { + b.p.v = v.Clone() return b } -// LinksUnsafe _ func (b *FieldUnsafeBuilder) LinksUnsafe(l *Links) *FieldUnsafeBuilder { b.p.links = l.Clone() return b diff --git a/pkg/property/field_builder_test.go b/pkg/property/field_builder_test.go index 05194394..0322ddef 100644 --- a/pkg/property/field_builder_test.go +++ b/pkg/property/field_builder_test.go @@ -11,7 +11,7 @@ import ( func TestFieldBuilder_Value(t *testing.T) { p := NewSchemaField().ID("A").Type(ValueTypeString).MustBuild() v := ValueTypeString.ValueFrom("vvv") - b := NewField(p).Value(v).MustBuild() + b := NewField(p).Value(OptionalValueFrom(v)).MustBuild() assert.Equal(t, v, b.Value()) } @@ -78,7 +78,7 @@ func TestFieldBuilder_Build(t *testing.T) { tc := tc t.Run(tc.Name, func(tt *testing.T) { tt.Parallel() - res, err := NewField(tc.SF).Value(tc.Value).Link(tc.Links).Build() + res, err := NewField(tc.SF).Value(OptionalValueFrom(tc.Value)).Link(tc.Links).Build() if err == nil { assert.Equal(tt, tc.Expected.Links, res.Links()) assert.Equal(tt, tc.Expected.PType, res.Type()) @@ -151,9 +151,9 @@ func TestFieldBuilder_MustBuild(t *testing.T) { assert.Nil(tt, res) } }() - res = NewField(tc.SF).Value(tc.Value).Link(tc.Links).MustBuild() + res = NewField(tc.SF).Value(OptionalValueFrom(tc.Value)).Link(tc.Links).MustBuild() } else { - res = NewField(tc.SF).Value(tc.Value).Link(tc.Links).MustBuild() + res = NewField(tc.SF).Value(OptionalValueFrom(tc.Value)).Link(tc.Links).MustBuild() assert.Equal(tt, tc.Expected.Links, res.Links()) assert.Equal(tt, tc.Expected.PType, res.Type()) assert.Equal(tt, tc.Expected.Value, res.Value()) @@ -223,7 +223,7 @@ func TestFieldUnsafeBuilder_Build(t *testing.T) { tc := tc t.Run(tc.Name, func(tt *testing.T) { tt.Parallel() - res := NewFieldUnsafe().ValueUnsafe(tc.Value).LinksUnsafe(tc.Links).TypeUnsafe(tc.Type).FieldUnsafe(tc.Field).Build() + res := NewFieldUnsafe().ValueUnsafe(NewOptionalValue(tc.Type, tc.Value)).LinksUnsafe(tc.Links).FieldUnsafe(tc.Field).Build() assert.Equal(tt, tc.Expected.Links, res.Links()) assert.Equal(tt, tc.Expected.PType, res.Type()) assert.Equal(tt, tc.Expected.Value, res.Value()) diff --git a/pkg/property/field_test.go b/pkg/property/field_test.go index 024aad2e..aade07fb 100644 --- a/pkg/property/field_test.go +++ b/pkg/property/field_test.go @@ -24,17 +24,17 @@ func TestField_ActualValue(t *testing.T) { }{ { Name: "nil links", - Field: NewField(p).Value(ValueTypeString.ValueFrom("vvv")).MustBuild(), + Field: NewField(p).Value(OptionalValueFrom(ValueTypeString.ValueFrom("vvv"))).MustBuild(), Expected: ValueTypeString.ValueFrom("vvv"), }, { Name: "nil last link", - Field: NewField(p).Value(ValueTypeString.ValueFrom("vvv")).Link(&Links{}).MustBuild(), + Field: NewField(p).Value(OptionalValueFrom(ValueTypeString.ValueFrom("vvv"))).Link(&Links{}).MustBuild(), Expected: nil, }, { Name: "dataset value", - Field: NewField(p).Value(ValueTypeString.ValueFrom("vvv")).Link(ls).MustBuild(), + Field: NewField(p).Value(OptionalValueFrom(ValueTypeString.ValueFrom("vvv"))).Link(ls).MustBuild(), DS: dataset.New(). ID(dsid).Schema(dssid). Fields([]*dataset.Field{ @@ -70,7 +70,7 @@ func TestField_CollectDatasets(t *testing.T) { }{ { Name: "list of one datasets", - Field: NewField(p).Value(ValueTypeString.ValueFrom("vvv")).Link(ls).MustBuild(), + Field: NewField(p).Value(OptionalValueFrom(ValueTypeString.ValueFrom("vvv"))).Link(ls).MustBuild(), Expected: []id.DatasetID{dsid}, }, { @@ -93,7 +93,7 @@ func TestField_Clone(t *testing.T) { p := NewSchemaField().ID("A").Type(ValueTypeString).MustBuild() l := NewLink(id.NewDatasetID(), id.NewDatasetSchemaID(), id.NewDatasetSchemaFieldID()) ls := NewLinks([]*Link{l}) - b := NewField(p).Value(ValueTypeString.ValueFrom("vvv")).Link(ls).MustBuild() + b := NewField(p).Value(OptionalValueFrom(ValueTypeString.ValueFrom("vvv"))).Link(ls).MustBuild() r := b.Clone() assert.Equal(t, b, r) } @@ -114,7 +114,7 @@ func TestField(t *testing.T) { func TestField_Update(t *testing.T) { p := NewSchemaField().ID("A").Type(ValueTypeString).MustBuild() - b := NewField(p).Value(ValueTypeString.ValueFrom("vvv")).MustBuild() + b := NewField(p).Value(OptionalValueFrom(ValueTypeString.ValueFrom("vvv"))).MustBuild() v := ValueTypeString.ValueFrom("xxx") b.UpdateUnsafe(v) assert.Equal(t, v, b.Value()) diff --git a/pkg/property/group_builder_test.go b/pkg/property/group_builder_test.go index 6a5ee1ce..504a3754 100644 --- a/pkg/property/group_builder_test.go +++ b/pkg/property/group_builder_test.go @@ -13,7 +13,7 @@ func TestGroupBuilder_Build(t *testing.T) { sid := id.MustPropertySchemaID("xx~1.0.0/aa") sf := NewSchemaField().ID("a").Type(ValueTypeString).MustBuild() v := ValueTypeString.ValueFrom("vvv") - f := NewField(sf).Value(v).MustBuild() + f := NewField(sf).Value(OptionalValueFrom(v)).MustBuild() testCases := []struct { Name string Id id.PropertyItemID @@ -75,7 +75,7 @@ func TestGroupBuilder_MustBuild(t *testing.T) { sid := id.MustPropertySchemaID("xx~1.0.0/aa") sf := NewSchemaField().ID("a").Type(ValueTypeString).MustBuild() v := ValueTypeString.ValueFrom("vvv") - f := NewField(sf).Value(v).MustBuild() + f := NewField(sf).Value(OptionalValueFrom(v)).MustBuild() testCases := []struct { Name string Fail bool diff --git a/pkg/property/group_list_test.go b/pkg/property/group_list_test.go index e19e12df..39329d43 100644 --- a/pkg/property/group_list_test.go +++ b/pkg/property/group_list_test.go @@ -47,7 +47,7 @@ func TestGroupList_HasLinkedField(t *testing.T) { v := ValueTypeString.ValueFrom("vvv") dsid := id.NewDatasetID() dssid := id.NewDatasetSchemaID() - f := NewField(sf).Value(v).Link(&Links{links: []*Link{NewLink(dsid, dssid, id.NewDatasetSchemaFieldID())}}).MustBuild() + f := NewField(sf).Value(OptionalValueFrom(v)).Link(&Links{links: []*Link{NewLink(dsid, dssid, id.NewDatasetSchemaFieldID())}}).MustBuild() groups := []*Group{NewGroup().ID(pid).Fields([]*Field{f}).MustBuild()} groups2 := []*Group{NewGroup().ID(pid).MustBuild()} testCases := []struct { @@ -85,7 +85,7 @@ func TestGroupList_CollectDatasets(t *testing.T) { v := ValueTypeString.ValueFrom("vvv") dsid := id.NewDatasetID() dssid := id.NewDatasetSchemaID() - f := NewField(sf).Value(v).Link(&Links{links: []*Link{NewLink(dsid, dssid, id.NewDatasetSchemaFieldID())}}).MustBuild() + f := NewField(sf).Value(OptionalValueFrom(v)).Link(&Links{links: []*Link{NewLink(dsid, dssid, id.NewDatasetSchemaFieldID())}}).MustBuild() groups := []*Group{NewGroup().ID(pid).Fields([]*Field{f}).MustBuild()} groups2 := []*Group{NewGroup().ID(pid).MustBuild()} testCases := []struct { @@ -122,7 +122,7 @@ func TestGroupList_FieldsByLinkedDataset(t *testing.T) { v := ValueTypeString.ValueFrom("vvv") dsid := id.NewDatasetID() dssid := id.NewDatasetSchemaID() - f := NewField(sf).Value(v).Link(&Links{links: []*Link{NewLink(dsid, dssid, id.NewDatasetSchemaFieldID())}}).MustBuild() + f := NewField(sf).Value(OptionalValueFrom(v)).Link(&Links{links: []*Link{NewLink(dsid, dssid, id.NewDatasetSchemaFieldID())}}).MustBuild() groups := []*Group{NewGroup().ID(pid).Fields([]*Field{f}).MustBuild()} groups2 := []*Group{NewGroup().ID(pid).MustBuild()} testCases := []struct { @@ -159,7 +159,7 @@ func TestGroupList_IsEmpty(t *testing.T) { v := ValueTypeString.ValueFrom("vvv") dsid := id.NewDatasetID() dssid := id.NewDatasetSchemaID() - f := NewField(sf).Value(v).Link(&Links{links: []*Link{NewLink(dsid, dssid, id.NewDatasetSchemaFieldID())}}).MustBuild() + f := NewField(sf).Value(OptionalValueFrom(v)).Link(&Links{links: []*Link{NewLink(dsid, dssid, id.NewDatasetSchemaFieldID())}}).MustBuild() groups := []*Group{NewGroup().ID(pid).Fields([]*Field{f}).MustBuild()} testCases := []struct { Name string @@ -192,7 +192,7 @@ func TestGroupList_IsEmpty(t *testing.T) { func TestGroupList_Prune(t *testing.T) { sf := NewSchemaField().ID("a").Type(ValueTypeString).MustBuild() v := ValueTypeString.ValueFrom("vvv") - f := NewField(sf).Value(v).MustBuild() + f := NewField(sf).Value(OptionalValueFrom(v)).MustBuild() f2 := NewField(sf).MustBuild() pid := id.NewPropertyItemID() groups := []*Group{NewGroup().ID(pid).Fields([]*Field{f, f2}).MustBuild()} diff --git a/pkg/property/group_test.go b/pkg/property/group_test.go index 5c823dd5..1321a14c 100644 --- a/pkg/property/group_test.go +++ b/pkg/property/group_test.go @@ -31,8 +31,8 @@ func TestGroup_HasLinkedField(t *testing.T) { v := ValueTypeString.ValueFrom("vvv") l := NewLink(id.NewDatasetID(), id.NewDatasetSchemaID(), id.NewDatasetSchemaFieldID()) ls := NewLinks([]*Link{l}) - f := NewField(sf).Value(v).Link(ls).MustBuild() - f2 := NewField(sf).Value(v).MustBuild() + f := NewField(sf).Value(OptionalValueFrom(v)).Link(ls).MustBuild() + f2 := NewField(sf).Value(OptionalValueFrom(v)).MustBuild() testCases := []struct { Name string @@ -71,8 +71,8 @@ func TestGroup_IsDatasetLinked(t *testing.T) { dssid := id.NewDatasetSchemaID() l := NewLink(dsid, dssid, id.NewDatasetSchemaFieldID()) ls := NewLinks([]*Link{l}) - f := NewField(sf).Value(v).Link(ls).MustBuild() - f2 := NewField(sf).Value(v).MustBuild() + f := NewField(sf).Value(OptionalValueFrom(v)).Link(ls).MustBuild() + f2 := NewField(sf).Value(OptionalValueFrom(v)).MustBuild() testCases := []struct { Name string @@ -113,7 +113,7 @@ func TestGroup_CollectDatasets(t *testing.T) { dsid := id.NewDatasetID() l := NewLink(dsid, id.NewDatasetSchemaID(), id.NewDatasetSchemaFieldID()) ls := NewLinks([]*Link{l}) - f := NewField(sf).Value(v).Link(ls).MustBuild() + f := NewField(sf).Value(OptionalValueFrom(v)).Link(ls).MustBuild() testCases := []struct { Name string @@ -148,7 +148,7 @@ func TestGroup_FieldsByLinkedDataset(t *testing.T) { dssid := id.NewDatasetSchemaID() l := NewLink(dsid, dssid, id.NewDatasetSchemaFieldID()) ls := NewLinks([]*Link{l}) - f := NewField(sf).Value(v).Link(ls).MustBuild() + f := NewField(sf).Value(OptionalValueFrom(v)).Link(ls).MustBuild() testCases := []struct { Name string @@ -181,7 +181,7 @@ func TestGroup_FieldsByLinkedDataset(t *testing.T) { func TestGroup_IsEmpty(t *testing.T) { sf := NewSchemaField().ID("a").Type(ValueTypeString).MustBuild() v := ValueTypeString.ValueFrom("vvv") - f := NewField(sf).Value(v).MustBuild() + f := NewField(sf).Value(OptionalValueFrom(v)).MustBuild() f2 := NewField(sf).MustBuild() testCases := []struct { @@ -214,7 +214,7 @@ func TestGroup_IsEmpty(t *testing.T) { func TestGroup_Prune(t *testing.T) { sf := NewSchemaField().ID("a").Type(ValueTypeString).MustBuild() v := ValueTypeString.ValueFrom("vvv") - f := NewField(sf).Value(v).MustBuild() + f := NewField(sf).Value(OptionalValueFrom(v)).MustBuild() f2 := NewField(sf).MustBuild() testCases := []struct { @@ -310,7 +310,7 @@ func TestGroup_RemoveField(t *testing.T) { sf := NewSchemaField().ID("a").Type(ValueTypeString).MustBuild() sf2 := NewSchemaField().ID("b").Type(ValueTypeString).MustBuild() v := ValueTypeString.ValueFrom("vvv") - f := NewField(sf).Value(v).MustBuild() + f := NewField(sf).Value(OptionalValueFrom(v)).MustBuild() f2 := NewField(sf2).MustBuild() testCases := []struct { @@ -344,7 +344,7 @@ func TestGroup_FieldIDs(t *testing.T) { sf := NewSchemaField().ID("a").Type(ValueTypeString).MustBuild() sf2 := NewSchemaField().ID("b").Type(ValueTypeString).MustBuild() v := ValueTypeString.ValueFrom("vvv") - f := NewField(sf).Value(v).MustBuild() + f := NewField(sf).Value(OptionalValueFrom(v)).MustBuild() f2 := NewField(sf2).MustBuild() testCases := []struct { @@ -376,7 +376,7 @@ func TestGroup_Field(t *testing.T) { sf := NewSchemaField().ID("a").Type(ValueTypeString).MustBuild() sf2 := NewSchemaField().ID("b").Type(ValueTypeString).MustBuild() v := ValueTypeString.ValueFrom("vvv") - f := NewField(sf).Value(v).MustBuild() + f := NewField(sf).Value(OptionalValueFrom(v)).MustBuild() f2 := NewField(sf2).MustBuild() testCases := []struct { @@ -444,7 +444,7 @@ func TestGroup_UpdateNameFieldValue(t *testing.T) { PS: NewSchema().ID(id.MustPropertySchemaID("xx~1.0.0/aa")).Groups([]*SchemaGroup{sg}).MustBuild(), Value: ValueTypeString.ValueFrom("abc"), FID: "aa", - Expected: NewField(sf).Value(ValueTypeString.ValueFrom("abc")).MustBuild(), + Expected: NewField(sf).Value(OptionalValueFrom(ValueTypeString.ValueFrom("abc"))).MustBuild(), }, { Name: "invalid property field", diff --git a/pkg/property/initializer.go b/pkg/property/initializer.go index 245cc958..aff489fe 100644 --- a/pkg/property/initializer.go +++ b/pkg/property/initializer.go @@ -276,7 +276,7 @@ func (p *InitializerField) PropertyField() *Field { plinks = NewLinks(links) } - return NewFieldUnsafe().LinksUnsafe(plinks).FieldUnsafe(p.Field).TypeUnsafe(p.Type).ValueUnsafe(p.Value.Clone()).Build() + return NewFieldUnsafe().LinksUnsafe(plinks).FieldUnsafe(p.Field).ValueUnsafe(NewOptionalValue(p.Type, p.Value.Clone())).Build() } type InitializerLink struct { diff --git a/pkg/property/initializer_test.go b/pkg/property/initializer_test.go index 7eb55f15..ebd6fe1e 100644 --- a/pkg/property/initializer_test.go +++ b/pkg/property/initializer_test.go @@ -146,7 +146,7 @@ func TestInitializerItem_PropertyGroup(t *testing.T) { } expected := NewItem().ID(*item.ID).Schema(parent, item.SchemaItem).Group().Fields([]*Field{ - NewFieldUnsafe().FieldUnsafe(item.Fields[0].Field).TypeUnsafe(item.Fields[0].Type).ValueUnsafe(item.Fields[0].Value).Build(), + NewFieldUnsafe().FieldUnsafe(item.Fields[0].Field).ValueUnsafe(NewOptionalValue(item.Fields[0].Type, item.Fields[0].Value)).Build(), }).MustBuild() assert.Equal(t, expected, item.PropertyGroup(parent)) @@ -213,7 +213,7 @@ func TestInitializerGroup_PropertyGroup(t *testing.T) { } expected := NewItem().ID(*item.ID).Schema(parent, parentItem).Group().Fields([]*Field{ - NewFieldUnsafe().FieldUnsafe(item.Fields[0].Field).TypeUnsafe(item.Fields[0].Type).ValueUnsafe(item.Fields[0].Value).Build(), + NewFieldUnsafe().FieldUnsafe(item.Fields[0].Field).ValueUnsafe(NewOptionalValue(item.Fields[0].Type, item.Fields[0].Value)).Build(), }).MustBuild() p, err := item.PropertyGroup(parent, parentItem) @@ -259,8 +259,7 @@ func TestInitializerField_PropertyField(t *testing.T) { expected := NewFieldUnsafe(). FieldUnsafe(field.Field). - TypeUnsafe(field.Type). - ValueUnsafe(field.Value). + ValueUnsafe(NewOptionalValue(field.Type, field.Value)). LinksUnsafe(NewLinks([]*Link{NewLink(*field.Links[0].Dataset.CopyRef(), field.Links[0].Schema, field.Links[0].Field)})). Build() diff --git a/pkg/property/item_test.go b/pkg/property/item_test.go index adf797dc..8ff6eff3 100644 --- a/pkg/property/item_test.go +++ b/pkg/property/item_test.go @@ -59,8 +59,7 @@ func TestToGroup(t *testing.T) { Fields([]*Field{ NewFieldUnsafe(). FieldUnsafe(propertySchemaField1ID). - TypeUnsafe(ValueTypeString). - ValueUnsafe(ValueTypeString.ValueFrom("xxx")). + ValueUnsafe(OptionalValueFrom(ValueTypeString.ValueFrom("xxx"))). Build(), }).MustBuild(), } diff --git a/pkg/property/merged_test.go b/pkg/property/merged_test.go index e534b4b5..e21e9606 100644 --- a/pkg/property/merged_test.go +++ b/pkg/property/merged_test.go @@ -31,17 +31,17 @@ func TestMerge(t *testing.T) { i8id := id.NewPropertyItemID() fields1 := []*Field{ - NewFieldUnsafe().FieldUnsafe(id.PropertySchemaFieldID("a")).ValueUnsafe(ValueTypeString.ValueFrom("a")).Build(), - NewFieldUnsafe().FieldUnsafe(id.PropertySchemaFieldID("b")).ValueUnsafe(ValueTypeString.ValueFrom("b")).Build(), - NewFieldUnsafe().FieldUnsafe(id.PropertySchemaFieldID("e")).TypeUnsafe(ValueTypeString).LinksUnsafe(NewLinks([]*Link{NewLink(d2, ds, df)})).Build(), - NewFieldUnsafe().FieldUnsafe(id.PropertySchemaFieldID("f")).TypeUnsafe(ValueTypeNumber).Build(), + NewFieldUnsafe().FieldUnsafe(id.PropertySchemaFieldID("a")).ValueUnsafe(OptionalValueFrom(ValueTypeString.ValueFrom("a"))).Build(), + NewFieldUnsafe().FieldUnsafe(id.PropertySchemaFieldID("b")).ValueUnsafe(OptionalValueFrom(ValueTypeString.ValueFrom("b"))).Build(), + NewFieldUnsafe().FieldUnsafe(id.PropertySchemaFieldID("e")).ValueUnsafe(NewOptionalValue(ValueTypeString, nil)).LinksUnsafe(NewLinks([]*Link{NewLink(d2, ds, df)})).Build(), + NewFieldUnsafe().FieldUnsafe(id.PropertySchemaFieldID("f")).ValueUnsafe(NewOptionalValue(ValueTypeNumber, nil)).Build(), } fields2 := []*Field{ - NewFieldUnsafe().FieldUnsafe(id.PropertySchemaFieldID("a")).ValueUnsafe(ValueTypeString.ValueFrom("1")).Build(), - NewFieldUnsafe().FieldUnsafe(id.PropertySchemaFieldID("c")).ValueUnsafe(ValueTypeString.ValueFrom("2")).Build(), - NewFieldUnsafe().FieldUnsafe(id.PropertySchemaFieldID("d")).TypeUnsafe(ValueTypeString).LinksUnsafe(NewLinks([]*Link{NewLinkFieldOnly(ds, df)})).Build(), - NewFieldUnsafe().FieldUnsafe(id.PropertySchemaFieldID("f")).TypeUnsafe(ValueTypeString).Build(), + NewFieldUnsafe().FieldUnsafe(id.PropertySchemaFieldID("a")).ValueUnsafe(OptionalValueFrom(ValueTypeString.ValueFrom("1"))).Build(), + NewFieldUnsafe().FieldUnsafe(id.PropertySchemaFieldID("c")).ValueUnsafe(OptionalValueFrom(ValueTypeString.ValueFrom("2"))).Build(), + NewFieldUnsafe().FieldUnsafe(id.PropertySchemaFieldID("d")).ValueUnsafe(NewOptionalValue(ValueTypeString, nil)).LinksUnsafe(NewLinks([]*Link{NewLinkFieldOnly(ds, df)})).Build(), + NewFieldUnsafe().FieldUnsafe(id.PropertySchemaFieldID("f")).ValueUnsafe(NewOptionalValue(ValueTypeString, nil)).Build(), } groups1 := []*Group{ diff --git a/pkg/property/property_test.go b/pkg/property/property_test.go index 4747fe68..153a1cdc 100644 --- a/pkg/property/property_test.go +++ b/pkg/property/property_test.go @@ -53,19 +53,19 @@ func TestPropertyMigrateSchema(t *testing.T) { fields := []*Field{ // should remain NewFieldUnsafe().FieldUnsafe(schemaField1ID). - ValueUnsafe(ValueTypeString.ValueFrom("foobar")). + ValueUnsafe(OptionalValueFrom(ValueTypeString.ValueFrom("foobar"))). Build(), // should be removed because of max NewFieldUnsafe().FieldUnsafe(schemaField2ID). - ValueUnsafe(ValueTypeNumber.ValueFrom(101)). + ValueUnsafe(OptionalValueFrom(ValueTypeNumber.ValueFrom(101))). Build(), // should remain NewFieldUnsafe().FieldUnsafe(schemaField3ID). - ValueUnsafe(ValueTypeNumber.ValueFrom(1)). + ValueUnsafe(OptionalValueFrom(ValueTypeNumber.ValueFrom(1))). Build(), // should be removed because of choices NewFieldUnsafe().FieldUnsafe(schemaField4ID). - ValueUnsafe(ValueTypeString.ValueFrom("z")). + ValueUnsafe(OptionalValueFrom(ValueTypeString.ValueFrom("z"))). Build(), // should remain NewFieldUnsafe().FieldUnsafe(schemaField5ID). @@ -81,11 +81,11 @@ func TestPropertyMigrateSchema(t *testing.T) { Build(), // should be removed because of type NewFieldUnsafe().FieldUnsafe(schemaField7ID). - ValueUnsafe(ValueTypeString.ValueFrom("hogehoge")). + ValueUnsafe(OptionalValueFrom(ValueTypeString.ValueFrom("hogehoge"))). Build(), // should be removed because of not existing field NewFieldUnsafe().FieldUnsafe(schemaField8ID). - ValueUnsafe(ValueTypeString.ValueFrom("hogehoge")). + ValueUnsafe(OptionalValueFrom(ValueTypeString.ValueFrom("hogehoge"))). Build(), } items := []Item{ diff --git a/pkg/property/schema_field.go b/pkg/property/schema_field.go index 2b470fd7..402be2b0 100644 --- a/pkg/property/schema_field.go +++ b/pkg/property/schema_field.go @@ -118,17 +118,11 @@ func (p *SchemaField) IsAvailableIf() *Condition { return p.cond.Clone() } -func (p *SchemaField) Validate(value *Value) bool { - if p == nil { - return false - } - if value == nil { - return true - } - if p.propertyType != value.Type() { +func (p *SchemaField) Validate(value *OptionalValue) bool { + if p == nil || value == nil || p.propertyType != value.Type() { return false } - switch v := value.Value().(type) { + switch v := value.Value().Value().(type) { case float64: if min := p.Min(); min != nil { if v < *min { diff --git a/pkg/property/schema_field_test.go b/pkg/property/schema_field_test.go index fee1dedf..e339c3e6 100644 --- a/pkg/property/schema_field_test.go +++ b/pkg/property/schema_field_test.go @@ -117,33 +117,46 @@ func TestSchemaField_Validate(t *testing.T) { testCases := []struct { Name string SF *SchemaField - Input *Value + Input *OptionalValue Expected bool }{ { Name: "nil sf", }, + { + Name: "nil optional value", + SF: NewSchemaField().ID("A").Type(ValueTypeNumber).MustBuild(), + Input: nil, + Expected: false, + }, { Name: "nil value", SF: NewSchemaField().ID("A").Type(ValueTypeNumber).MustBuild(), + Input: NewOptionalValue(ValueTypeNumber, nil), Expected: true, }, { Name: "property type != value type", SF: NewSchemaField().ID("A").Type(ValueTypeNumber).MustBuild(), - Input: ValueTypeBool.ValueFrom(true), + Input: OptionalValueFrom(ValueTypeBool.ValueFrom(true)), + Expected: false, + }, + { + Name: "property type != value type with nil value", + SF: NewSchemaField().ID("A").Type(ValueTypeNumber).MustBuild(), + Input: NewOptionalValue(ValueTypeBool, nil), Expected: false, }, { Name: "validate min", SF: NewSchemaField().ID("A").Type(ValueTypeNumber).Min(10).MustBuild(), - Input: ValueTypeNumber.ValueFrom(9), + Input: OptionalValueFrom(ValueTypeNumber.ValueFrom(9)), Expected: false, }, { Name: "validate max", SF: NewSchemaField().ID("A").Type(ValueTypeNumber).Max(10).MustBuild(), - Input: ValueTypeNumber.ValueFrom(11), + Input: OptionalValueFrom(ValueTypeNumber.ValueFrom(11)), Expected: false, }, { @@ -160,7 +173,7 @@ func TestSchemaField_Validate(t *testing.T) { Icon: "", }, }).MustBuild(), - Input: ValueTypeString.ValueFrom("xxx"), + Input: OptionalValueFrom(ValueTypeString.ValueFrom("xxx")), Expected: true, }, { @@ -177,13 +190,13 @@ func TestSchemaField_Validate(t *testing.T) { Icon: "", }, }).MustBuild(), - Input: ValueTypeString.ValueFrom("aaa"), + Input: OptionalValueFrom(ValueTypeString.ValueFrom("aaa")), Expected: false, }, { Name: "validate other", SF: NewSchemaField().ID("A").Type(ValueTypeLatLng).MustBuild(), - Input: ValueTypeLatLng.ValueFrom(LatLng{Lat: 10, Lng: 11}), + Input: OptionalValueFrom(ValueTypeLatLng.ValueFrom(LatLng{Lat: 10, Lng: 11})), Expected: true, }, } diff --git a/pkg/scene/builder/builder_test.go b/pkg/scene/builder/builder_test.go index 80c85cbf..dc240799 100644 --- a/pkg/scene/builder/builder_test.go +++ b/pkg/scene/builder/builder_test.go @@ -77,13 +77,11 @@ func TestSceneBuilder(t *testing.T) { Fields([]*property.Field{ property.NewFieldUnsafe(). FieldUnsafe(propertySchemaField1ID). - TypeUnsafe(property.ValueTypeString). - ValueUnsafe(property.ValueTypeString.ValueFrom("xxx")). + ValueUnsafe(property.OptionalValueFrom(property.ValueTypeString.ValueFrom("xxx"))). Build(), property.NewFieldUnsafe(). FieldUnsafe(propertySchemaField2ID). - TypeUnsafe(property.ValueTypeNumber). - ValueUnsafe(property.ValueTypeNumber.ValueFrom(1)). + ValueUnsafe(property.OptionalValueFrom(property.ValueTypeNumber.ValueFrom(1))). Build(), }).MustBuild(), }). @@ -106,13 +104,11 @@ func TestSceneBuilder(t *testing.T) { Fields([]*property.Field{ property.NewFieldUnsafe(). FieldUnsafe(propertySchemaField1ID). - TypeUnsafe(property.ValueTypeString). - ValueUnsafe(property.ValueTypeString.ValueFrom("yyy")). + ValueUnsafe(property.OptionalValueFrom(property.ValueTypeString.ValueFrom("yyy"))). Build(), property.NewFieldUnsafe(). FieldUnsafe(propertySchemaField2ID). - TypeUnsafe(property.ValueTypeNumber). - ValueUnsafe(property.ValueTypeNumber.ValueFrom(1)). + ValueUnsafe(property.OptionalValueFrom(property.ValueTypeNumber.ValueFrom(1))). Build(), }).MustBuild(), }). @@ -133,13 +129,11 @@ func TestSceneBuilder(t *testing.T) { Fields([]*property.Field{ property.NewFieldUnsafe(). FieldUnsafe(propertySchemaField1ID). - TypeUnsafe(property.ValueTypeString). - ValueUnsafe(property.ValueTypeString.ValueFrom("xxx")). + ValueUnsafe(property.OptionalValueFrom(property.ValueTypeString.ValueFrom("xxx"))). Build(), property.NewFieldUnsafe(). FieldUnsafe(propertySchemaField3ID). - TypeUnsafe(property.ValueTypeString). - ValueUnsafe(property.ValueTypeString.ValueFrom("test")). + ValueUnsafe(property.OptionalValueFrom(property.ValueTypeString.ValueFrom("test"))). Build(), }).MustBuild(), }). @@ -168,7 +162,7 @@ func TestSceneBuilder(t *testing.T) { Fields([]*property.Field{ property.NewFieldUnsafe(). FieldUnsafe(propertySchemaField1ID). - TypeUnsafe(property.ValueTypeString). + ValueUnsafe(property.NewOptionalValue(property.ValueTypeString, nil)). LinksUnsafe(property.NewLinks([]*property.Link{ property.NewLink(ds2id, dss2id, ds2f1), property.NewLink(ds3id, dss3id, ds3f1), @@ -200,8 +194,7 @@ func TestSceneBuilder(t *testing.T) { Fields([]*property.Field{ property.NewFieldUnsafe(). FieldUnsafe(propertySchemaField2ID). - TypeUnsafe(property.ValueTypeNumber). - ValueUnsafe(property.ValueTypeNumber.ValueFrom(1)). + ValueUnsafe(property.OptionalValueFrom(property.ValueTypeNumber.ValueFrom(1))). Build(), }).MustBuild(), }). @@ -228,15 +221,14 @@ func TestSceneBuilder(t *testing.T) { Fields([]*property.Field{ property.NewFieldUnsafe(). FieldUnsafe(propertySchemaField1ID). - TypeUnsafe(property.ValueTypeString). + ValueUnsafe(property.NewOptionalValue(property.ValueTypeString, nil)). LinksUnsafe(property.NewLinks([]*property.Link{ property.NewLinkFieldOnly(dss3id, ds3f1), })). Build(), property.NewFieldUnsafe(). FieldUnsafe(propertySchemaField3ID). - TypeUnsafe(property.ValueTypeString). - ValueUnsafe(property.ValueTypeString.ValueFrom("xxx")). + ValueUnsafe(property.OptionalValueFrom(property.ValueTypeString.ValueFrom("xxx"))). Build(), }).MustBuild(), }). @@ -266,7 +258,7 @@ func TestSceneBuilder(t *testing.T) { Fields([]*property.Field{ property.NewFieldUnsafe(). FieldUnsafe(propertySchemaField1ID). - TypeUnsafe(property.ValueTypeString). + ValueUnsafe(property.NewOptionalValue(property.ValueTypeString, nil)). LinksUnsafe(property.NewLinks([]*property.Link{ property.NewLinkFieldOnly(dss1id, ds1f2), })). @@ -291,7 +283,7 @@ func TestSceneBuilder(t *testing.T) { Fields([]*property.Field{ property.NewFieldUnsafe(). FieldUnsafe(propertySchemaField1ID). - TypeUnsafe(property.ValueTypeString). + ValueUnsafe(property.NewOptionalValue(property.ValueTypeString, nil)). LinksUnsafe(property.NewLinks([]*property.Link{ property.NewLinkFieldOnly(dss1id, ds1f1), property.NewLinkFieldOnly(dss2id, ds2f1), @@ -300,7 +292,7 @@ func TestSceneBuilder(t *testing.T) { Build(), property.NewFieldUnsafe(). FieldUnsafe(propertySchemaField2ID). - TypeUnsafe(property.ValueTypeString). + ValueUnsafe(property.NewOptionalValue(property.ValueTypeString, nil)). LinksUnsafe(property.NewLinks([]*property.Link{ property.NewLinkFieldOnly(dss1id, ds1f1), property.NewLinkFieldOnly(dss2id, ds2f1), @@ -329,16 +321,14 @@ func TestSceneBuilder(t *testing.T) { Fields([]*property.Field{ property.NewFieldUnsafe(). FieldUnsafe(propertySchemaField1ID). - TypeUnsafe(property.ValueTypeString). - ValueUnsafe(property.ValueTypeString.ValueFrom("XYZ")). + ValueUnsafe(property.OptionalValueFrom(property.ValueTypeString.ValueFrom("XYZ"))). Build(), }).MustBuild(), property.NewGroup().ID(propertyItemID2).Schema(propertySchemaID, propertySchemaGroup2ID). Fields([]*property.Field{ property.NewFieldUnsafe(). FieldUnsafe(propertySchemaField1ID). - TypeUnsafe(property.ValueTypeString). - ValueUnsafe(property.ValueTypeString.ValueFrom("ZYX")). + ValueUnsafe(property.OptionalValueFrom(property.ValueTypeString.ValueFrom("ZYX"))). Build(), }).MustBuild(), }).MustBuild(), @@ -371,8 +361,7 @@ func TestSceneBuilder(t *testing.T) { property.NewGroup().NewID().Schema(propertySchemaID, propertySchemaGroup1ID).Fields([]*property.Field{ property.NewFieldUnsafe(). FieldUnsafe(propertySchemaField1ID). - TypeUnsafe(property.ValueTypeString). - ValueUnsafe(property.ValueTypeString.ValueFrom("hogehoge")). + ValueUnsafe(property.OptionalValueFrom(property.ValueTypeString.ValueFrom("hogehoge"))). Build(), }).MustBuild(), }). From e7e641835b6ba0a5ac0af93dfd8018580f05ec9a Mon Sep 17 00:00:00 2001 From: rot1024 Date: Wed, 24 Nov 2021 14:15:24 +0900 Subject: [PATCH 09/12] use interface for type property --- pkg/property/value.go | 4 +- pkg/property/value_camera.go | 44 +++---- pkg/property/value_typography.go | 46 ++++---- pkg/value/bool.go | 35 +++--- pkg/value/coordinates.go | 63 +++++----- pkg/value/latlng.go | 47 ++++---- pkg/value/latlngheight.go | 45 +++---- pkg/value/number.go | 197 ++++++++++++++++--------------- pkg/value/polygon.go | 57 ++++----- pkg/value/rect.go | 47 ++++---- pkg/value/ref.go | 46 ++++---- pkg/value/string.go | 34 +++--- pkg/value/type.go | 33 +++--- pkg/value/type_test.go | 18 ++- pkg/value/url.go | 61 +++++----- pkg/value/value.go | 4 +- pkg/value/value_test.go | 91 ++++++++------ 17 files changed, 455 insertions(+), 417 deletions(-) diff --git a/pkg/property/value.go b/pkg/property/value.go index d4494160..a2ca520d 100644 --- a/pkg/property/value.go +++ b/pkg/property/value.go @@ -30,8 +30,8 @@ var ( ) var types = value.TypePropertyMap{ - TypeTypography: typePropertyTypography, - TypeCamera: typePropertyCamera, + TypeTypography: &typePropertyTypography{}, + TypeCamera: &typePropertyCamera{}, } func (vt ValueType) Valid() bool { diff --git a/pkg/property/value_camera.go b/pkg/property/value_camera.go index 3e8670b2..07c31d47 100644 --- a/pkg/property/value_camera.go +++ b/pkg/property/value_camera.go @@ -32,32 +32,34 @@ func (c *Camera) Clone() *Camera { } } -var typePropertyCamera = value.TypeProperty{ - I2V: func(i interface{}) (interface{}, bool) { - if v, ok := i.(Camera); ok { - return v, true - } +type typePropertyCamera struct{} - if v, ok := i.(*Camera); ok { - if v != nil { - return *v, true - } - return nil, false - } +func (*typePropertyCamera) I2V(i interface{}) (interface{}, bool) { + if v, ok := i.(Camera); ok { + return v, true + } - v := Camera{} - if err := mapstructure.Decode(i, &v); err == nil { - return v, true + if v, ok := i.(*Camera); ok { + if v != nil { + return *v, true } return nil, false - }, - V2I: func(v interface{}) (interface{}, bool) { + } + + v := Camera{} + if err := mapstructure.Decode(i, &v); err == nil { return v, true - }, - Validate: func(i interface{}) bool { - _, ok := i.(Camera) - return ok - }, + } + return nil, false +} + +func (*typePropertyCamera) V2I(v interface{}) (interface{}, bool) { + return v, true +} + +func (*typePropertyCamera) Validate(i interface{}) bool { + _, ok := i.(Camera) + return ok } func (v *Value) ValueCamera() (vv Camera, ok bool) { diff --git a/pkg/property/value_typography.go b/pkg/property/value_typography.go index a3ae3726..a342e40e 100644 --- a/pkg/property/value_typography.go +++ b/pkg/property/value_typography.go @@ -94,33 +94,35 @@ func (t *TextAlign) StringRef() *string { return &t2 } -var typePropertyTypography = value.TypeProperty{ - I2V: func(i interface{}) (interface{}, bool) { - if v, ok := i.(Typography); ok { - return v, true - } +type typePropertyTypography struct{} - if v, ok := i.(*Typography); ok { - if v != nil { - return *v, true - } - return nil, false - } +func (*typePropertyTypography) I2V(i interface{}) (interface{}, bool) { + if v, ok := i.(Typography); ok { + return v, true + } - v := Typography{} - if err := mapstructure.Decode(i, &v); err == nil { - return v, true + if v, ok := i.(*Typography); ok { + if v != nil { + return *v, true } - return nil, false - }, - V2I: func(v interface{}) (interface{}, bool) { + } + + v := Typography{} + if err := mapstructure.Decode(i, &v); err == nil { return v, true - }, - Validate: func(i interface{}) bool { - _, ok := i.(Typography) - return ok - }, + } + + return nil, false +} + +func (*typePropertyTypography) V2I(v interface{}) (interface{}, bool) { + return v, true +} + +func (*typePropertyTypography) Validate(i interface{}) bool { + _, ok := i.(Typography) + return ok } func (v *Value) ValueTypography() (vv Typography, ok bool) { diff --git a/pkg/value/bool.go b/pkg/value/bool.go index 572f02dc..538e3f5f 100644 --- a/pkg/value/bool.go +++ b/pkg/value/bool.go @@ -2,24 +2,25 @@ package value var TypeBool Type = "bool" -var propertyBool = TypeProperty{ - I2V: func(i interface{}) (interface{}, bool) { - if v, ok := i.(bool); ok { - return v, true - } - if v, ok := i.(*bool); ok && v != nil { - return *v, true - } - return nil, false - }, - V2I: func(v interface{}) (interface{}, bool) { +type propertyBool struct{} + +func (*propertyBool) I2V(i interface{}) (interface{}, bool) { + if v, ok := i.(bool); ok { return v, true - }, - Validate: func(i interface{}) bool { - _, ok := i.(bool) - return ok - }, - Compatible: []Type{}, + } + if v, ok := i.(*bool); ok && v != nil { + return *v, true + } + return nil, false +} + +func (*propertyBool) V2I(v interface{}) (interface{}, bool) { + return v, true +} + +func (*propertyBool) Validate(i interface{}) bool { + _, ok := i.(bool) + return ok } func (v *Value) ValueBool() (vv bool, ok bool) { diff --git a/pkg/value/coordinates.go b/pkg/value/coordinates.go index 917f5f25..40288969 100644 --- a/pkg/value/coordinates.go +++ b/pkg/value/coordinates.go @@ -30,42 +30,43 @@ func CoordinatesFrom(coords []float64) Coordinates { var TypeCoordinates Type = "coordinates" -var propertyCoordinates = TypeProperty{ - I2V: func(i interface{}) (interface{}, bool) { - if v, ok := i.(Coordinates); ok { - return v, true - } else if v, ok := i.(*Coordinates); ok { - if v != nil { - return *v, true - } +type propertyCoordinates struct{} + +func (*propertyCoordinates) I2V(i interface{}) (interface{}, bool) { + if v, ok := i.(Coordinates); ok { + return v, true + } else if v, ok := i.(*Coordinates); ok { + if v != nil { + return *v, true + } + return nil, false + } else if v2, ok := i.([]float64); ok { + if v2 == nil { return nil, false - } else if v2, ok := i.([]float64); ok { - if v2 == nil { - return nil, false - } - return CoordinatesFrom(v2), true } + return CoordinatesFrom(v2), true + } - v2 := Coordinates{} - if err := mapstructure.Decode(i, &v2); err == nil { - return v2, true - } + v2 := Coordinates{} + if err := mapstructure.Decode(i, &v2); err == nil { + return v2, true + } - v1 := []float64{} - if err := mapstructure.Decode(i, &v1); err == nil { - return CoordinatesFrom(v1), true - } + v1 := []float64{} + if err := mapstructure.Decode(i, &v1); err == nil { + return CoordinatesFrom(v1), true + } - return nil, false - }, - V2I: func(v interface{}) (interface{}, bool) { - return v, true - }, - Validate: func(i interface{}) bool { - _, ok := i.(bool) - return ok - }, - Compatible: []Type{}, + return nil, false +} + +func (*propertyCoordinates) V2I(v interface{}) (interface{}, bool) { + return v, true +} + +func (*propertyCoordinates) Validate(i interface{}) bool { + _, ok := i.(bool) + return ok } func (v *Value) ValueCoordinates() (vv Coordinates, ok bool) { diff --git a/pkg/value/latlng.go b/pkg/value/latlng.go index efff6593..7612eb54 100644 --- a/pkg/value/latlng.go +++ b/pkg/value/latlng.go @@ -19,30 +19,31 @@ func (l *LatLng) Clone() *LatLng { var TypeLatLng Type = "latlng" -var propertyLatLng = TypeProperty{ - I2V: func(i interface{}) (interface{}, bool) { - if v, ok := i.(LatLng); ok { - return v, true - } else if v, ok := i.(*LatLng); ok { - if v != nil { - return *v, true - } - return nil, false - } - v := LatLng{} - if err := mapstructure.Decode(i, &v); err != nil { - return nil, false - } - return v, true - }, - V2I: func(v interface{}) (interface{}, bool) { +type propertyLatLng struct{} + +func (*propertyLatLng) I2V(i interface{}) (interface{}, bool) { + if v, ok := i.(LatLng); ok { return v, true - }, - Validate: func(i interface{}) bool { - _, ok := i.(LatLng) - return ok - }, - Compatible: []Type{}, + } else if v, ok := i.(*LatLng); ok { + if v != nil { + return *v, true + } + return nil, false + } + v := LatLng{} + if err := mapstructure.Decode(i, &v); err != nil { + return nil, false + } + return v, true +} + +func (*propertyLatLng) V2I(v interface{}) (interface{}, bool) { + return v, true +} + +func (*propertyLatLng) Validate(i interface{}) bool { + _, ok := i.(LatLng) + return ok } func (v *Value) ValueLatLng() (vv LatLng, ok bool) { diff --git a/pkg/value/latlngheight.go b/pkg/value/latlngheight.go index 3001f495..173f3875 100644 --- a/pkg/value/latlngheight.go +++ b/pkg/value/latlngheight.go @@ -21,33 +21,34 @@ func (l *LatLngHeight) Clone() *LatLngHeight { var TypeLatLngHeight Type = "latlngheight" -var propertyLatLngHeight = TypeProperty{ - I2V: func(i interface{}) (interface{}, bool) { - if v, ok := i.(LatLngHeight); ok { - return v, true - } +type propertyLatLngHeight struct{} - if v, ok := i.(*LatLngHeight); ok { - if v != nil { - return *v, false - } - return nil, false - } +func (*propertyLatLngHeight) I2V(i interface{}) (interface{}, bool) { + if v, ok := i.(LatLngHeight); ok { + return v, true + } - v := LatLngHeight{} - if err := mapstructure.Decode(i, &v); err == nil { - return v, true + if v, ok := i.(*LatLngHeight); ok { + if v != nil { + return *v, false } return nil, false - }, - V2I: func(v interface{}) (interface{}, bool) { + } + + v := LatLngHeight{} + if err := mapstructure.Decode(i, &v); err == nil { return v, true - }, - Validate: func(i interface{}) bool { - _, ok := i.(LatLngHeight) - return ok - }, - Compatible: []Type{}, + } + return nil, false +} + +func (*propertyLatLngHeight) V2I(v interface{}) (interface{}, bool) { + return v, true +} + +func (*propertyLatLngHeight) Validate(i interface{}) bool { + _, ok := i.(LatLngHeight) + return ok } func (v *Value) ValueLatLngHeight() (vv LatLngHeight, ok bool) { diff --git a/pkg/value/number.go b/pkg/value/number.go index cbcbb8f1..275e5325 100644 --- a/pkg/value/number.go +++ b/pkg/value/number.go @@ -4,108 +4,109 @@ import "encoding/json" var TypeNumber Type = "number" -var propertyNumber = TypeProperty{ - I2V: func(i interface{}) (interface{}, bool) { - switch v := i.(type) { - case float64: - return v, true - case float32: - return float64(v), true - case int: - return float64(v), true - case int8: - return float64(v), true - case int16: - return float64(v), true - case int32: - return float64(v), true - case int64: - return float64(v), true - case uint: - return float64(v), true - case uint8: - return float64(v), true - case uint16: - return float64(v), true - case uint32: - return float64(v), true - case uint64: - return float64(v), true - case uintptr: - return float64(v), true - case json.Number: +type propertyNumber struct{} + +func (*propertyNumber) I2V(i interface{}) (interface{}, bool) { + switch v := i.(type) { + case float64: + return v, true + case float32: + return float64(v), true + case int: + return float64(v), true + case int8: + return float64(v), true + case int16: + return float64(v), true + case int32: + return float64(v), true + case int64: + return float64(v), true + case uint: + return float64(v), true + case uint8: + return float64(v), true + case uint16: + return float64(v), true + case uint32: + return float64(v), true + case uint64: + return float64(v), true + case uintptr: + return float64(v), true + case json.Number: + if f, err := v.Float64(); err == nil { + return f, true + } + case *float64: + if v != nil { + return *v, true + } + case *float32: + if v != nil { + return float64(*v), true + } + case *int: + if v != nil { + return float64(*v), true + } + case *int8: + if v != nil { + return float64(*v), true + } + case *int16: + if v != nil { + return float64(*v), true + } + case *int32: + if v != nil { + return float64(*v), true + } + case *int64: + if v != nil { + return float64(*v), true + } + case *uint: + if v != nil { + return float64(*v), true + } + case *uint8: + if v != nil { + return float64(*v), true + } + case *uint16: + if v != nil { + return float64(*v), true + } + case *uint32: + if v != nil { + return float64(*v), true + } + case *uint64: + if v != nil { + return float64(*v), true + } + case *uintptr: + if v != nil { + return float64(*v), true + } + case *json.Number: + if v != nil { if f, err := v.Float64(); err == nil { return f, true } - case *float64: - if v != nil { - return *v, true - } - case *float32: - if v != nil { - return float64(*v), true - } - case *int: - if v != nil { - return float64(*v), true - } - case *int8: - if v != nil { - return float64(*v), true - } - case *int16: - if v != nil { - return float64(*v), true - } - case *int32: - if v != nil { - return float64(*v), true - } - case *int64: - if v != nil { - return float64(*v), true - } - case *uint: - if v != nil { - return float64(*v), true - } - case *uint8: - if v != nil { - return float64(*v), true - } - case *uint16: - if v != nil { - return float64(*v), true - } - case *uint32: - if v != nil { - return float64(*v), true - } - case *uint64: - if v != nil { - return float64(*v), true - } - case *uintptr: - if v != nil { - return float64(*v), true - } - case *json.Number: - if v != nil { - if f, err := v.Float64(); err == nil { - return f, true - } - } } - return nil, false - }, - V2I: func(v interface{}) (interface{}, bool) { - return v, true - }, - Validate: func(i interface{}) bool { - _, ok := i.(float64) - return ok - }, - Compatible: []Type{}, + } + return nil, false +} + +func (*propertyNumber) V2I(v interface{}) (interface{}, bool) { + return v, true +} + +func (*propertyNumber) Validate(i interface{}) bool { + _, ok := i.(float64) + return ok } func (v *Value) ValueNumber() (vv float64, ok bool) { diff --git a/pkg/value/polygon.go b/pkg/value/polygon.go index 3d07ade2..4dccecfd 100644 --- a/pkg/value/polygon.go +++ b/pkg/value/polygon.go @@ -14,39 +14,40 @@ func PolygonFrom(rings [][]float64) Polygon { return p } -var propertyPolygon = TypeProperty{ - I2V: func(i interface{}) (interface{}, bool) { - if v, ok := i.(Polygon); ok { - return v, true - } - - if v, ok := i.(*Polygon); ok { - if v != nil { - return *v, true - } - return nil, false - } +type propertyPolygon struct{} - v := Polygon{} - if err := mapstructure.Decode(i, &v); err == nil { - return v, true - } +func (*propertyPolygon) I2V(i interface{}) (interface{}, bool) { + if v, ok := i.(Polygon); ok { + return v, true + } - v2 := [][]float64{} - if err := mapstructure.Decode(i, &v); err == nil { - return PolygonFrom(v2), true + if v, ok := i.(*Polygon); ok { + if v != nil { + return *v, true } - return nil, false - }, - V2I: func(v interface{}) (interface{}, bool) { + } + + v := Polygon{} + if err := mapstructure.Decode(i, &v); err == nil { return v, true - }, - Validate: func(i interface{}) bool { - _, ok := i.(Polygon) - return ok - }, - Compatible: []Type{}, + } + + v2 := [][]float64{} + if err := mapstructure.Decode(i, &v); err == nil { + return PolygonFrom(v2), true + } + + return nil, false +} + +func (*propertyPolygon) V2I(v interface{}) (interface{}, bool) { + return v, true +} + +func (*propertyPolygon) Validate(i interface{}) bool { + _, ok := i.(Polygon) + return ok } func (v *Value) ValuePolygon() (vv Polygon, ok bool) { diff --git a/pkg/value/rect.go b/pkg/value/rect.go index 6d209a19..90caf01d 100644 --- a/pkg/value/rect.go +++ b/pkg/value/rect.go @@ -11,32 +11,33 @@ type Rect struct { North float64 `json:"north" mapstructure:"north"` } -var propertyRect = TypeProperty{ - I2V: func(i interface{}) (interface{}, bool) { - if v, ok := i.(Rect); ok { - return v, true - } else if v, ok := i.(*Rect); ok { - if v != nil { - return *v, true - } - return nil, false - } +type propertyRect struct{} - v := Rect{} - if err := mapstructure.Decode(i, &v); err == nil { - return v, false +func (*propertyRect) I2V(i interface{}) (interface{}, bool) { + if v, ok := i.(Rect); ok { + return v, true + } else if v, ok := i.(*Rect); ok { + if v != nil { + return *v, true } - return nil, false - }, - V2I: func(v interface{}) (interface{}, bool) { - return v, true - }, - Validate: func(i interface{}) bool { - _, ok := i.(Rect) - return ok - }, - Compatible: []Type{}, + } + + v := Rect{} + if err := mapstructure.Decode(i, &v); err == nil { + return v, false + } + + return nil, false +} + +func (*propertyRect) V2I(v interface{}) (interface{}, bool) { + return v, true +} + +func (*propertyRect) Validate(i interface{}) bool { + _, ok := i.(Rect) + return ok } func (v *Value) ValueRect() (vv Rect, ok bool) { diff --git a/pkg/value/ref.go b/pkg/value/ref.go index 928e6c32..f065ee91 100644 --- a/pkg/value/ref.go +++ b/pkg/value/ref.go @@ -4,29 +4,31 @@ import "github.com/reearth/reearth-backend/pkg/id" var TypeRef Type = "ref" -var propertyRef = TypeProperty{ - I2V: func(i interface{}) (interface{}, bool) { - if v, ok := i.(string); ok { - return v, true - } - if v, ok := i.(*string); ok { - return *v, true - } - if v, ok := i.(id.ID); ok { - return v.String(), true - } - if v, ok := i.(*id.ID); ok && v != nil { - return v.String(), true - } - return nil, false - }, - V2I: func(v interface{}) (interface{}, bool) { +type propertyRef struct{} + +func (*propertyRef) I2V(i interface{}) (interface{}, bool) { + if v, ok := i.(string); ok { return v, true - }, - Validate: func(i interface{}) bool { - _, ok := i.(string) - return ok - }, + } + if v, ok := i.(*string); ok { + return *v, true + } + if v, ok := i.(id.ID); ok { + return v.String(), true + } + if v, ok := i.(*id.ID); ok && v != nil { + return v.String(), true + } + return nil, false +} + +func (*propertyRef) V2I(v interface{}) (interface{}, bool) { + return v, true +} + +func (*propertyRef) Validate(i interface{}) bool { + _, ok := i.(string) + return ok } func (v *Value) ValueRef() (vv string, ok bool) { diff --git a/pkg/value/string.go b/pkg/value/string.go index 3b20537f..3979d0cb 100644 --- a/pkg/value/string.go +++ b/pkg/value/string.go @@ -2,23 +2,25 @@ package value var TypeString Type = "string" -var propertyString = TypeProperty{ - I2V: func(i interface{}) (interface{}, bool) { - if v, ok := i.(string); ok { - return v, true - } - if v, ok := i.(*string); ok { - return *v, true - } - return nil, false - }, - V2I: func(v interface{}) (interface{}, bool) { +type propertyString struct{} + +func (*propertyString) I2V(i interface{}) (interface{}, bool) { + if v, ok := i.(string); ok { return v, true - }, - Validate: func(i interface{}) bool { - _, ok := i.(string) - return ok - }, + } + if v, ok := i.(*string); ok { + return *v, true + } + return nil, false +} + +func (*propertyString) V2I(v interface{}) (interface{}, bool) { + return v, true +} + +func (*propertyString) Validate(i interface{}) bool { + _, ok := i.(string) + return ok } func (v *Value) ValueString() (vv string, ok bool) { diff --git a/pkg/value/type.go b/pkg/value/type.go index 8f03c3d8..963747c4 100644 --- a/pkg/value/type.go +++ b/pkg/value/type.go @@ -2,11 +2,10 @@ package value type Type string -type TypeProperty struct { - I2V func(interface{}) (interface{}, bool) - V2I func(interface{}) (interface{}, bool) - Validate func(interface{}) bool - Compatible []Type +type TypeProperty interface { + I2V(interface{}) (interface{}, bool) + V2I(interface{}) (interface{}, bool) + Validate(interface{}) bool } type TypePropertyMap = map[Type]TypeProperty @@ -14,16 +13,16 @@ type TypePropertyMap = map[Type]TypeProperty var TypeUnknown = Type("") var defaultTypes = TypePropertyMap{ - TypeBool: propertyBool, - TypeCoordinates: propertyCoordinates, - TypeLatLng: propertyLatLng, - TypeLatLngHeight: propertyLatLngHeight, - TypeNumber: propertyNumber, - TypePolygon: propertyPolygon, - TypeRect: propertyRect, - TypeRef: propertyRef, - TypeString: propertyString, - TypeURL: propertyURL, + TypeBool: &propertyBool{}, + TypeCoordinates: &propertyCoordinates{}, + TypeLatLng: &propertyLatLng{}, + TypeLatLngHeight: &propertyLatLngHeight{}, + TypeNumber: &propertyNumber{}, + TypePolygon: &propertyPolygon{}, + TypeRect: &propertyRect{}, + TypeRef: &propertyRef{}, + TypeString: &propertyString{}, + TypeURL: &propertyURL{}, } func (t Type) Default() bool { @@ -37,14 +36,14 @@ func (t Type) ValueFrom(i interface{}, p TypePropertyMap) *Value { } if p != nil { - if vt, ok := p[t]; ok && vt.I2V != nil { + if vt, ok := p[t]; ok && vt != nil { if v, ok2 := vt.I2V(i); ok2 { return &Value{p: p, v: v, t: t} } } } - if vt, ok := defaultTypes[t]; ok && vt.I2V != nil { + if vt, ok := defaultTypes[t]; ok && vt != nil { if v, ok2 := vt.I2V(i); ok2 { return &Value{p: p, v: v, t: t} } diff --git a/pkg/value/type_test.go b/pkg/value/type_test.go index 0cf3e638..6f0d83ce 100644 --- a/pkg/value/type_test.go +++ b/pkg/value/type_test.go @@ -6,6 +6,18 @@ import ( "github.com/stretchr/testify/assert" ) +type tpmock struct { + TypeProperty +} + +func (*tpmock) I2V(i interface{}) (interface{}, bool) { + return i.(string) + "a", true +} + +func (*tpmock) V2I(v interface{}) (interface{}, bool) { + return v.(string) + "bar", true +} + func TestType_Default(t *testing.T) { tests := []struct { name string @@ -39,11 +51,7 @@ func TestType_Default(t *testing.T) { func TestType_ValueFrom(t *testing.T) { tpm := TypePropertyMap{ - Type("foo"): TypeProperty{ - I2V: func(v interface{}) (interface{}, bool) { - return v.(string) + "a", true - }, - }, + Type("foo"): &tpmock{}, } type args struct { diff --git a/pkg/value/url.go b/pkg/value/url.go index 7d7486a5..0745a793 100644 --- a/pkg/value/url.go +++ b/pkg/value/url.go @@ -4,42 +4,43 @@ import "net/url" var TypeURL Type = "url" -var propertyURL = TypeProperty{ - I2V: func(i interface{}) (interface{}, bool) { - if v, ok := i.(url.URL); ok { - return &v, true - } +type propertyURL struct{} + +func (*propertyURL) I2V(i interface{}) (interface{}, bool) { + if v, ok := i.(url.URL); ok { + return &v, true + } - if v, ok := i.(*url.URL); ok { - if v == nil { - return nil, false - } - return v, true + if v, ok := i.(*url.URL); ok { + if v == nil { + return nil, false } + return v, true + } - if v, ok := i.(string); ok { - if u, err := url.Parse(v); err == nil { - return u, true - } + if v, ok := i.(string); ok { + if u, err := url.Parse(v); err == nil { + return u, true } + } + + return nil, false +} +func (*propertyURL) V2I(v interface{}) (interface{}, bool) { + u, ok := v.(*url.URL) + if !ok { return nil, false - }, - V2I: func(v interface{}) (interface{}, bool) { - u, ok := v.(*url.URL) - if !ok { - return nil, false - } - if u == nil { - return "", true - } - return u.String(), true - }, - Validate: func(i interface{}) bool { - _, ok := i.(*url.URL) - return ok - }, - Compatible: []Type{}, + } + if u == nil { + return "", true + } + return u.String(), true +} + +func (*propertyURL) Validate(i interface{}) bool { + _, ok := i.(*url.URL) + return ok } func (v *Value) ValueURL() (vv *url.URL, ok bool) { diff --git a/pkg/value/value.go b/pkg/value/value.go index 7c342f56..79cea656 100644 --- a/pkg/value/value.go +++ b/pkg/value/value.go @@ -56,7 +56,7 @@ func (v *Value) Interface() interface{} { return nil } - if tp := v.TypeProperty(); tp.V2I != nil { + if tp := v.TypeProperty(); tp != nil { if i, ok2 := tp.V2I(v.v); ok2 { return i } @@ -70,7 +70,7 @@ func (v *Value) Validate() bool { return false } - if tp := v.TypeProperty(); tp.Validate != nil { + if tp := v.TypeProperty(); tp != nil { return tp.Validate(v) } diff --git a/pkg/value/value_test.go b/pkg/value/value_test.go index 4b3cf415..b71a78fb 100644 --- a/pkg/value/value_test.go +++ b/pkg/value/value_test.go @@ -41,10 +41,15 @@ func TestValue_IsEmpty(t *testing.T) { } func TestValue_Clone(t *testing.T) { + tp := &tpmock{} + tpm := TypePropertyMap{ + Type("hoge"): tp, + } + tests := []struct { - name string - value *Value - wantnil bool + name string + value *Value + want *Value }{ { name: "ok", @@ -52,24 +57,33 @@ func TestValue_Clone(t *testing.T) { t: TypeString, v: "foo", }, + want: &Value{ + t: TypeString, + v: "foo", + }, }, { name: "custom type property", value: &Value{ t: Type("hoge"), v: "foo", - p: TypePropertyMap{Type("hoge"): TypeProperty{ - I2V: func(i interface{}) (interface{}, bool) { return i, true }, - }}, + p: tpm, + }, + want: &Value{ + t: Type("hoge"), + v: "fooa", + p: tpm, }, }, { - name: "nil", + name: "nil", + value: nil, + want: nil, }, { - name: "empty", - value: &Value{}, - wantnil: true, + name: "empty", + value: &Value{}, + want: nil, }, } @@ -77,11 +91,7 @@ func TestValue_Clone(t *testing.T) { tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() - want := tt.value - if tt.wantnil { - want = nil - } - assert.Equal(t, want, tt.value.Clone()) + assert.Equal(t, tt.want, tt.value.Clone()) }) } } @@ -152,40 +162,41 @@ func TestValue_Type(t *testing.T) { } func TestValue_TypeProperty(t *testing.T) { - typePropertyHoge := TypeProperty{} + tp := &tpmock{} + tpm := TypePropertyMap{ + Type("hoge"): tp, + } tests := []struct { name string value *Value want TypeProperty }{ - // { - // name: "default type", - // value: &Value{ - // v: "string", - // t: TypeString, - // }, - // want: defaultTypes[TypeString], - // }, + { + name: "default type", + value: &Value{ + v: "string", + t: TypeString, + }, + want: defaultTypes[TypeString], + }, { name: "custom type", value: &Value{ v: "string", t: Type("hoge"), - p: TypePropertyMap{ - Type("hoge"): typePropertyHoge, - }, + p: tpm, }, - want: typePropertyHoge, + want: tp, }, { name: "empty", value: &Value{}, - want: TypeProperty{}, + want: nil, }, { name: "nil", - want: TypeProperty{}, + want: nil, }, } @@ -193,12 +204,22 @@ func TestValue_TypeProperty(t *testing.T) { tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() - assert.Equal(t, tt.want, tt.value.TypeProperty()) + res := tt.value.TypeProperty() + if tt.want == nil { + assert.Nil(t, res) + } else { + assert.Same(t, tt.want, res) + } }) } } func TestValue_Interface(t *testing.T) { + tp := &tpmock{} + tpm := TypePropertyMap{ + "foo": tp, + } + tests := []struct { name string value *Value @@ -217,13 +238,7 @@ func TestValue_Interface(t *testing.T) { { name: "custom", value: &Value{ - p: TypePropertyMap{ - Type("foo"): TypeProperty{ - V2I: func(v interface{}) (interface{}, bool) { - return v.(string) + "bar", true - }, - }, - }, + p: tpm, t: Type("foo"), v: "foo", }, From 4f213edbbbbce3e1388cb1d4837f59892ac96081 Mon Sep 17 00:00:00 2001 From: rot1024 Date: Fri, 26 Nov 2021 17:37:01 +0900 Subject: [PATCH 10/12] fix --- .../adapter/gql/gqlmodel/convert_property.go | 79 +++++++++++++++++-- pkg/property/value.go | 4 +- pkg/property/value_camera.go | 3 +- pkg/property/value_typography.go | 3 +- 4 files changed, 78 insertions(+), 11 deletions(-) diff --git a/internal/adapter/gql/gqlmodel/convert_property.go b/internal/adapter/gql/gqlmodel/convert_property.go index 24059bf3..55ca11a6 100644 --- a/internal/adapter/gql/gqlmodel/convert_property.go +++ b/internal/adapter/gql/gqlmodel/convert_property.go @@ -88,7 +88,7 @@ func FromPropertyValueAndType(v interface{}, t ValueType) *property.Value { default: v = gqlValueToValueInterface(v2) } - return property.ValueType(t).ValueFrom(v) + return FromPropertyValueType(t).ValueFrom(v) } func fromTextAlign(t *TextAlign) *property.TextAlign { @@ -133,11 +133,71 @@ func ToPropertyField(f *property.Field, parent *property.Property, gl *property. SchemaID: parent.Schema(), FieldID: f.Field(), Value: ToPropertyValue(f.Value()), - Type: ValueType(f.Type()), + Type: ToPropertyValueType(f.Type()), Links: links, } } +func ToPropertyValueType(t property.ValueType) ValueType { + switch t { + case property.ValueTypeBool: + return ValueTypeBool + case property.ValueTypeString: + return ValueTypeString + case property.ValueTypeNumber: + return ValueTypeNumber + case property.ValueTypeLatLng: + return ValueTypeLatlng + case property.ValueTypeLatLngHeight: + return ValueTypeLatlngheight + case property.ValueTypeCoordinates: + return ValueTypeCoordinates + case property.ValueTypePolygon: + return ValueTypePolygon + case property.ValueTypeRect: + return ValueTypeRect + case property.ValueTypeURL: + return ValueTypeURL + case property.ValueTypeRef: + return ValueTypeRef + case property.ValueTypeTypography: + return ValueTypeTypography + case property.ValueTypeCamera: + return ValueTypeCamera + } + return "" +} + +func FromPropertyValueType(t ValueType) property.ValueType { + switch t { + case ValueTypeBool: + return property.ValueTypeBool + case ValueTypeString: + return property.ValueTypeString + case ValueTypeNumber: + return property.ValueTypeNumber + case ValueTypeLatlng: + return property.ValueTypeLatLng + case ValueTypeLatlngheight: + return property.ValueTypeLatLngHeight + case ValueTypeCoordinates: + return property.ValueTypeCoordinates + case ValueTypePolygon: + return property.ValueTypePolygon + case ValueTypeRect: + return property.ValueTypeRect + case ValueTypeURL: + return property.ValueTypeURL + case ValueTypeRef: + return property.ValueTypeRef + case ValueTypeTypography: + return property.ValueTypeTypography + case ValueTypeCamera: + return property.ValueTypeCamera + } + return property.ValueTypeUnknown +} + func ToPropertyFieldLinks(flinks *property.Links) []*PropertyFieldLink { if flinks == nil { return nil @@ -249,7 +309,7 @@ func ToPropertySchemaField(f *property.SchemaField) *PropertySchemaField { return &PropertySchemaField{ FieldID: f.ID(), - Type: ValueType(f.Type()), + Type: ToPropertyValueType(f.Type()), Title: f.Title().String(), Description: f.Description().String(), Prefix: stringToRef(f.Prefix()), @@ -359,7 +419,7 @@ func ToMergedPropertyField(f *property.MergedField, s id.PropertySchemaID) *Merg SchemaID: s, Links: ToPropertyFieldLinks(f.Links), Value: ToPropertyValue(f.Value), - Type: ValueType(f.Type), + Type: ToPropertyValueType(f.Type), Overridden: f.Overridden, } } @@ -451,7 +511,7 @@ func ToPropertyConditon(c *property.Condition) *PropertyCondition { return &PropertyCondition{ FieldID: c.Field, Value: ToPropertyValue(c.Value), - Type: ValueType(c.Value.Type()), + Type: ToPropertyValueType(c.Value.Type()), } } @@ -488,3 +548,12 @@ func propertyFieldID(property *property.Property, groupList *property.GroupList, return sb.String() } + +func getPropertySchemaFieldIDFromGQLPropertyFieldID(i string) string { + const sep = "_" + s := strings.Split(i, sep) + if len(s) > 0 { + return s[len(s)-1] + } + return "" +} diff --git a/pkg/property/value.go b/pkg/property/value.go index a2ca520d..35404a98 100644 --- a/pkg/property/value.go +++ b/pkg/property/value.go @@ -30,8 +30,8 @@ var ( ) var types = value.TypePropertyMap{ - TypeTypography: &typePropertyTypography{}, - TypeCamera: &typePropertyCamera{}, + value.Type(ValueTypeTypography): &typePropertyTypography{}, + value.Type(ValueTypeCamera): &typePropertyCamera{}, } func (vt ValueType) Valid() bool { diff --git a/pkg/property/value_camera.go b/pkg/property/value_camera.go index 07c31d47..378273c7 100644 --- a/pkg/property/value_camera.go +++ b/pkg/property/value_camera.go @@ -2,10 +2,9 @@ package property import ( "github.com/mitchellh/mapstructure" - "github.com/reearth/reearth-backend/pkg/value" ) -var TypeCamera = value.Type("camera") +var ValueTypeCamera = ValueType("camera") type Camera struct { Lat float64 `json:"lat" mapstructure:"lat"` diff --git a/pkg/property/value_typography.go b/pkg/property/value_typography.go index a342e40e..48f24987 100644 --- a/pkg/property/value_typography.go +++ b/pkg/property/value_typography.go @@ -2,10 +2,9 @@ package property import ( "github.com/mitchellh/mapstructure" - "github.com/reearth/reearth-backend/pkg/value" ) -var TypeTypography = value.Type("typography") +var ValueTypeTypography = ValueType("typography") type Typography struct { FontFamily *string `json:"fontFamily" mapstructure:"fontFamily"` From 75d39b32fbf606cc85a325ffd10c89cd5d1eabd6 Mon Sep 17 00:00:00 2001 From: rot1024 Date: Fri, 26 Nov 2021 17:37:14 +0900 Subject: [PATCH 11/12] fix gql property field id type --- internal/adapter/gql/generated.go | 6 +++--- internal/adapter/gql/gqlmodel/convert_property.go | 3 +-- internal/adapter/gql/gqlmodel/models.go | 4 ++-- internal/adapter/gql/gqlmodel/models_gen.go | 2 +- schema.graphql | 2 +- 5 files changed, 8 insertions(+), 9 deletions(-) diff --git a/internal/adapter/gql/generated.go b/internal/adapter/gql/generated.go index 4522471f..869bd492 100644 --- a/internal/adapter/gql/generated.go +++ b/internal/adapter/gql/generated.go @@ -6667,7 +6667,7 @@ type PropertyGroupList { } type PropertyField { - id: PropertySchemaFieldID! + id: String! parentId: ID! schemaId: PropertySchemaID! fieldId: PropertySchemaFieldID! @@ -22904,9 +22904,9 @@ func (ec *executionContext) _PropertyField_id(ctx context.Context, field graphql } return graphql.Null } - res := resTmp.(id.PropertySchemaFieldID) + res := resTmp.(string) fc.Result = res - return ec.marshalNPropertySchemaFieldID2githubᚗcomᚋreearthᚋreearthᚑbackendᚋpkgᚋidᚐPropertySchemaFieldID(ctx, field.Selections, res) + return ec.marshalNString2string(ctx, field.Selections, res) } func (ec *executionContext) _PropertyField_parentId(ctx context.Context, field graphql.CollectedField, obj *gqlmodel.PropertyField) (ret graphql.Marshaler) { diff --git a/internal/adapter/gql/gqlmodel/convert_property.go b/internal/adapter/gql/gqlmodel/convert_property.go index 55ca11a6..3f10bc6a 100644 --- a/internal/adapter/gql/gqlmodel/convert_property.go +++ b/internal/adapter/gql/gqlmodel/convert_property.go @@ -127,8 +127,7 @@ func ToPropertyField(f *property.Field, parent *property.Property, gl *property. } return &PropertyField{ - // TODO: PropertySchemaFieldID is mismatched - ID: id.PropertySchemaFieldID(propertyFieldID(parent, gl, g, f)), + ID: propertyFieldID(parent, gl, g, f), ParentID: parent.ID().ID(), SchemaID: parent.Schema(), FieldID: f.Field(), diff --git a/internal/adapter/gql/gqlmodel/models.go b/internal/adapter/gql/gqlmodel/models.go index 0e94c006..1886a626 100644 --- a/internal/adapter/gql/gqlmodel/models.go +++ b/internal/adapter/gql/gqlmodel/models.go @@ -51,7 +51,7 @@ func (d *Property) Field(id id.PropertySchemaFieldID) *PropertyField { for _, g := range d.Items { if gi, ok := g.(*PropertyGroup); ok { for _, f := range gi.Fields { - if f.ID == id { + if s := getPropertySchemaFieldIDFromGQLPropertyFieldID(f.ID); s == string(id) { return f } } @@ -145,7 +145,7 @@ func (d *PropertyGroup) Field(id id.PropertySchemaFieldID) *PropertyField { return nil } for _, f := range d.Fields { - if f.ID == id { + if s := getPropertySchemaFieldIDFromGQLPropertyFieldID(f.ID); s == string(id) { return f } } diff --git a/internal/adapter/gql/gqlmodel/models_gen.go b/internal/adapter/gql/gqlmodel/models_gen.go index 3adf45b2..3ddeb118 100644 --- a/internal/adapter/gql/gqlmodel/models_gen.go +++ b/internal/adapter/gql/gqlmodel/models_gen.go @@ -764,7 +764,7 @@ type PropertyCondition struct { } type PropertyField struct { - ID id.PropertySchemaFieldID `json:"id"` + ID string `json:"id"` ParentID id.ID `json:"parentId"` SchemaID id.PropertySchemaID `json:"schemaId"` FieldID id.PropertySchemaFieldID `json:"fieldId"` diff --git a/schema.graphql b/schema.graphql index 567207e6..746cc817 100644 --- a/schema.graphql +++ b/schema.graphql @@ -524,7 +524,7 @@ type PropertyGroupList { } type PropertyField { - id: PropertySchemaFieldID! + id: String! parentId: ID! schemaId: PropertySchemaID! fieldId: PropertySchemaFieldID! From 3f364d1d6e5c9f58329368c0f1b8c97d2b204192 Mon Sep 17 00:00:00 2001 From: rot1024 Date: Fri, 26 Nov 2021 17:54:04 +0900 Subject: [PATCH 12/12] fix value type conversion --- .../adapter/gql/gqlmodel/convert_dataset.go | 25 +------ .../adapter/gql/gqlmodel/convert_property.go | 71 ++----------------- .../adapter/gql/gqlmodel/convert_value.go | 9 +++ 3 files changed, 18 insertions(+), 87 deletions(-) diff --git a/internal/adapter/gql/gqlmodel/convert_dataset.go b/internal/adapter/gql/gqlmodel/convert_dataset.go index 616fbb6d..3c0969ac 100644 --- a/internal/adapter/gql/gqlmodel/convert_dataset.go +++ b/internal/adapter/gql/gqlmodel/convert_dataset.go @@ -2,6 +2,7 @@ package gqlmodel import ( "github.com/reearth/reearth-backend/pkg/dataset" + "github.com/reearth/reearth-backend/pkg/value" ) func ToDatasetValue(v *dataset.Value) *interface{} { @@ -9,26 +10,6 @@ func ToDatasetValue(v *dataset.Value) *interface{} { return &i } -func ToDatasetValueType(t dataset.ValueType) ValueType { - switch t { - case dataset.ValueTypeBool: - return ValueTypeBool - case dataset.ValueTypeNumber: - return ValueTypeNumber - case dataset.ValueTypeString: - return ValueTypeString - case dataset.ValueTypeLatLng: - return ValueTypeLatlng - case dataset.ValueTypeLatLngHeight: - return ValueTypeLatlngheight - case dataset.ValueTypeURL: - return ValueTypeURL - case dataset.ValueTypeRef: - return ValueTypeRef - } - return "" -} - func ToDatasetField(f *dataset.Field, parent *dataset.Dataset) *DatasetField { if f == nil || parent == nil { return nil @@ -37,7 +18,7 @@ func ToDatasetField(f *dataset.Field, parent *dataset.Dataset) *DatasetField { return &DatasetField{ SchemaID: parent.Schema().ID(), FieldID: f.Field().ID(), - Type: ToDatasetValueType(f.Type()), + Type: ToValueType(value.Type(f.Type())), Value: ToDatasetValue(f.Value()), Source: f.Source(), } @@ -73,7 +54,7 @@ func ToDatasetSchema(ds *dataset.Schema) *DatasetSchema { fields = append(fields, &DatasetSchemaField{ ID: f.ID().ID(), Name: f.Name(), - Type: ToDatasetValueType(f.Type()), + Type: ToValueType(value.Type(f.Type())), SchemaID: ds.ID().ID(), Source: f.Source(), RefID: f.Ref().IDRef(), diff --git a/internal/adapter/gql/gqlmodel/convert_property.go b/internal/adapter/gql/gqlmodel/convert_property.go index 3f10bc6a..ce36a8ad 100644 --- a/internal/adapter/gql/gqlmodel/convert_property.go +++ b/internal/adapter/gql/gqlmodel/convert_property.go @@ -5,6 +5,7 @@ import ( "github.com/reearth/reearth-backend/pkg/id" "github.com/reearth/reearth-backend/pkg/property" + "github.com/reearth/reearth-backend/pkg/value" ) func ToPropertyValue(v *property.Value) *interface{} { @@ -88,7 +89,7 @@ func FromPropertyValueAndType(v interface{}, t ValueType) *property.Value { default: v = gqlValueToValueInterface(v2) } - return FromPropertyValueType(t).ValueFrom(v) + return property.ValueType(FromValueType(t)).ValueFrom(v) } func fromTextAlign(t *TextAlign) *property.TextAlign { @@ -132,71 +133,11 @@ func ToPropertyField(f *property.Field, parent *property.Property, gl *property. SchemaID: parent.Schema(), FieldID: f.Field(), Value: ToPropertyValue(f.Value()), - Type: ToPropertyValueType(f.Type()), + Type: ToValueType(value.Type(f.Type())), Links: links, } } -func ToPropertyValueType(t property.ValueType) ValueType { - switch t { - case property.ValueTypeBool: - return ValueTypeBool - case property.ValueTypeString: - return ValueTypeString - case property.ValueTypeNumber: - return ValueTypeNumber - case property.ValueTypeLatLng: - return ValueTypeLatlng - case property.ValueTypeLatLngHeight: - return ValueTypeLatlngheight - case property.ValueTypeCoordinates: - return ValueTypeCoordinates - case property.ValueTypePolygon: - return ValueTypePolygon - case property.ValueTypeRect: - return ValueTypeRect - case property.ValueTypeURL: - return ValueTypeURL - case property.ValueTypeRef: - return ValueTypeRef - case property.ValueTypeTypography: - return ValueTypeTypography - case property.ValueTypeCamera: - return ValueTypeCamera - } - return "" -} - -func FromPropertyValueType(t ValueType) property.ValueType { - switch t { - case ValueTypeBool: - return property.ValueTypeBool - case ValueTypeString: - return property.ValueTypeString - case ValueTypeNumber: - return property.ValueTypeNumber - case ValueTypeLatlng: - return property.ValueTypeLatLng - case ValueTypeLatlngheight: - return property.ValueTypeLatLngHeight - case ValueTypeCoordinates: - return property.ValueTypeCoordinates - case ValueTypePolygon: - return property.ValueTypePolygon - case ValueTypeRect: - return property.ValueTypeRect - case ValueTypeURL: - return property.ValueTypeURL - case ValueTypeRef: - return property.ValueTypeRef - case ValueTypeTypography: - return property.ValueTypeTypography - case ValueTypeCamera: - return property.ValueTypeCamera - } - return property.ValueTypeUnknown -} - func ToPropertyFieldLinks(flinks *property.Links) []*PropertyFieldLink { if flinks == nil { return nil @@ -308,7 +249,7 @@ func ToPropertySchemaField(f *property.SchemaField) *PropertySchemaField { return &PropertySchemaField{ FieldID: f.ID(), - Type: ToPropertyValueType(f.Type()), + Type: ToValueType(value.Type(f.Type())), Title: f.Title().String(), Description: f.Description().String(), Prefix: stringToRef(f.Prefix()), @@ -418,7 +359,7 @@ func ToMergedPropertyField(f *property.MergedField, s id.PropertySchemaID) *Merg SchemaID: s, Links: ToPropertyFieldLinks(f.Links), Value: ToPropertyValue(f.Value), - Type: ToPropertyValueType(f.Type), + Type: ToValueType(value.Type(f.Type)), Overridden: f.Overridden, } } @@ -510,7 +451,7 @@ func ToPropertyConditon(c *property.Condition) *PropertyCondition { return &PropertyCondition{ FieldID: c.Field, Value: ToPropertyValue(c.Value), - Type: ToPropertyValueType(c.Value.Type()), + Type: ToValueType(value.Type(c.Value.Type())), } } diff --git a/internal/adapter/gql/gqlmodel/convert_value.go b/internal/adapter/gql/gqlmodel/convert_value.go index 78102e84..3ebe8202 100644 --- a/internal/adapter/gql/gqlmodel/convert_value.go +++ b/internal/adapter/gql/gqlmodel/convert_value.go @@ -2,6 +2,7 @@ package gqlmodel import ( "net/url" + "strings" "github.com/reearth/reearth-backend/pkg/value" ) @@ -145,3 +146,11 @@ func gqlValueToValueInterface(v interface{}) interface{} { } return nil } + +func ToValueType(t value.Type) ValueType { + return ValueType(strings.ToUpper(string(t))) +} + +func FromValueType(t ValueType) value.Type { + return value.Type(strings.ToLower(string(t))) +}