Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

serialize collections #13

Merged
merged 5 commits into from
Mar 1, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

## [0.4.0] - 2023-03-01

### Added

- Adds support for serialization of collections by serialization writer and parsenode.

## [0.3.0] - 2023-01-26

### Changed
Expand Down
58 changes: 57 additions & 1 deletion form_parse_node.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,63 @@ func (n *FormParseNode) GetCollectionOfObjectValues(ctor absser.ParsableFactory)

// GetCollectionOfPrimitiveValues returns the collection of primitive values from the node.
func (n *FormParseNode) GetCollectionOfPrimitiveValues(targetType string) ([]interface{}, error) {
return nil, errors.New("collections are not supported in form serialization")
if n == nil || n.value == "" {
return nil, nil
}
if targetType == "" {
return nil, errors.New("targetType is empty")
}
valueList := strings.Split(n.value, ",")

result := make([]interface{}, len(valueList))
for i, element := range valueList {
parseNode, err := NewFormParseNode([]byte(element))
if err != nil {
return nil, err
}

val, err := parseNode.getPrimitiveValue(targetType)
if err != nil {
return nil, err
}
result[i] = val
}
return result, nil
}

func (n *FormParseNode) getPrimitiveValue(targetType string) (interface{}, error) {
switch targetType {
case "string":
return n.GetStringValue()
case "bool":
return n.GetBoolValue()
case "uint8":
return n.GetInt8Value()
case "byte":
return n.GetByteValue()
case "float32":
return n.GetFloat32Value()
case "float64":
return n.GetFloat64Value()
case "int32":
return n.GetInt32Value()
case "int64":
return n.GetInt64Value()
case "time":
return n.GetTimeValue()
case "timeonly":
return n.GetTimeOnlyValue()
case "dateonly":
return n.GetDateOnlyValue()
case "isoduration":
return n.GetISODurationValue()
case "uuid":
return n.GetUUIDValue()
case "base64":
return n.GetByteArrayValue()
default:
return nil, errors.New("targetType is not supported")
}
}

// GetCollectionOfEnumValues returns the collection of Enum values from the node.
Expand Down
91 changes: 88 additions & 3 deletions form_parse_node_test.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
package formserialization

import (
testing "testing"

"github.com/stretchr/testify/require"
"testing"

"github.com/microsoft/kiota-serialization-form-go/internal"

absser "github.com/microsoft/kiota-abstractions-go/serialization"
assert "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/assert"
)

func TestGetRawValue(t *testing.T) {
Expand All @@ -26,6 +25,92 @@ func TestGetRawValue(t *testing.T) {
value, err := someProp.GetRawValue()
assert.Equal(t, "200", *value.(*string))
}

func TestGetCollectionOfPrimitiveValues(t *testing.T) {
source := `id=2&status=200&item=1&item=2&item=3`
sourceArray := []byte(source)
parseNode, err := NewFormParseNode(sourceArray)
require.NoError(t, err)
someProp, err := parseNode.GetChildNode("item")
require.NoError(t, err)

value, err := someProp.GetCollectionOfPrimitiveValues("int32")
require.NoError(t, err)

expected := []interface{}{ref(int32(1)), ref(int32(2)), ref(int32(3))}
assert.Equal(t, expected, value)
}

func TestGetCollectionOfPrimitiveValuesTypes(t *testing.T) {
assert.Equal(t,
[]interface{}{ref("milk"), ref("soda")},
getCollectionValues("id=2&item=milk&item=soda", "item", "string"),
)
assert.Equal(t,
[]interface{}{ref(true), ref(false)},
getCollectionValues("id=2&item=true&item=false", "item", "bool"),
)
assert.Equal(t,
[]interface{}{ref(int8(1)), ref(int8(2)), ref(int8(3))},
getCollectionValues("id=2&status=200&item=1&item=2&item=3", "item", "uint8"),
)
assert.Equal(t,
[]interface{}{ref(byte(1)), ref(byte(2)), ref(byte(3))},
getCollectionValues("id=2&status=200&item=1&item=2&item=3", "item", "byte"),
)
assert.Equal(t,
[]interface{}{ref(float32(1)), ref(float32(2)), ref(float32(3))},
getCollectionValues("id=2&status=200&item=1&item=2&item=3", "item", "float32"),
)
assert.Equal(t,
[]interface{}{ref(float64(1)), ref(float64(2)), ref(float64(3))},
getCollectionValues("id=2&status=200&item=1&item=2&item=3", "item", "float64"),
)
assert.Equal(t,
[]interface{}{ref(float64(1)), ref(float64(2)), ref(float64(3))},
getCollectionValues("id=2&status=200&item=1&item=2&item=3", "item", "float64"),
)
assert.Equal(t,
[]interface{}{ref(int32(1)), ref(int32(2)), ref(int32(3))},
getCollectionValues("id=2&status=200&item=1&item=2&item=3", "item", "int32"),
)
assert.Equal(t,
[]interface{}{ref(int64(1)), ref(int64(2)), ref(int64(3))},
getCollectionValues("id=2&status=200&item=1&item=2&item=3", "item", "int64"),
)
assert.Nil(t,
getCollectionValues("id=2&status=200&item=1&item=2&item=3", "item", "time"),
)
assert.Nil(t,
getCollectionValues("id=2&status=200&item=1&item=2&item=3", "item", "timeonly"),
)
assert.Nil(t,
getCollectionValues("id=2&status=200&item=1&item=2&item=3", "item", "dateonly"),
)
assert.Nil(t,
getCollectionValues("id=2&status=200&item=1&item=2&item=3", "item", "isoduration"),
)
assert.Nil(t,
getCollectionValues("id=2&status=200&item=1&item=2&item=3", "item", "uuid"),
)
assert.Nil(t,
getCollectionValues("id=2&status=200&item=1&item=2&item=3", "item", "base64"),
)
}

func getCollectionValues(source string, indexName string, targetType string) []interface{} {
sourceArray := []byte(source)
parseNode, _ := NewFormParseNode(sourceArray)
someProp, _ := parseNode.GetChildNode(indexName)

value, _ := someProp.GetCollectionOfPrimitiveValues(targetType)
return value
}

func ref[T interface{}](t T) *T {
return &t
}

func TestFormParseNodeHonoursInterface(t *testing.T) {
instance := &FormParseNode{}
assert.Implements(t, (*absser.ParseNode)(nil), instance)
Expand Down
48 changes: 34 additions & 14 deletions form_serialization_writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -271,72 +271,92 @@ func (w *FormSerializationWriter) WriteObjectValue(key string, item absser.Parsa

// WriteCollectionOfObjectValues writes a collection of Parsable values to underlying the byte array.
func (w *FormSerializationWriter) WriteCollectionOfObjectValues(key string, collection []absser.Parsable) error {
return errors.New("collections serialization is not supported with FormSerializationWriter")
if collection != nil && len(collection) > 0 {
baywet marked this conversation as resolved.
Show resolved Hide resolved
for _, item := range collection {
err := w.WriteObjectValue(key, item)
if err != nil {
return err
}
}
}
return nil
}

func writeCollectionOfPrimitiveValues[T interface{}](key string, writer func(string, *T) error, collection []T) error {
if collection != nil && len(collection) > 0 {
for _, item := range collection {
err := writer(key, &item)
if err != nil {
return err
}
}
}
return nil
}

// WriteCollectionOfStringValues writes a collection of String values to underlying the byte array.
func (w *FormSerializationWriter) WriteCollectionOfStringValues(key string, collection []string) error {
return errors.New("collections serialization is not supported with FormSerializationWriter")
return writeCollectionOfPrimitiveValues(key, w.WriteStringValue, collection)
}

// WriteCollectionOfInt32Values writes a collection of Int32 values to underlying the byte array.
func (w *FormSerializationWriter) WriteCollectionOfInt32Values(key string, collection []int32) error {
return errors.New("collections serialization is not supported with FormSerializationWriter")
return writeCollectionOfPrimitiveValues(key, w.WriteInt32Value, collection)
}

// WriteCollectionOfInt64Values writes a collection of Int64 values to underlying the byte array.
func (w *FormSerializationWriter) WriteCollectionOfInt64Values(key string, collection []int64) error {
return errors.New("collections serialization is not supported with FormSerializationWriter")
return writeCollectionOfPrimitiveValues(key, w.WriteInt64Value, collection)
}

// WriteCollectionOfFloat32Values writes a collection of Float32 values to underlying the byte array.
func (w *FormSerializationWriter) WriteCollectionOfFloat32Values(key string, collection []float32) error {
return errors.New("collections serialization is not supported with FormSerializationWriter")
return writeCollectionOfPrimitiveValues(key, w.WriteFloat32Value, collection)
}

// WriteCollectionOfFloat64Values writes a collection of Float64 values to underlying the byte array.
func (w *FormSerializationWriter) WriteCollectionOfFloat64Values(key string, collection []float64) error {
return errors.New("collections serialization is not supported with FormSerializationWriter")
return writeCollectionOfPrimitiveValues(key, w.WriteFloat64Value, collection)
}

// WriteCollectionOfTimeValues writes a collection of Time values to underlying the byte array.
func (w *FormSerializationWriter) WriteCollectionOfTimeValues(key string, collection []time.Time) error {
return errors.New("collections serialization is not supported with FormSerializationWriter")
return writeCollectionOfPrimitiveValues(key, w.WriteTimeValue, collection)
}

// WriteCollectionOfISODurationValues writes a collection of ISODuration values to underlying the byte array.
func (w *FormSerializationWriter) WriteCollectionOfISODurationValues(key string, collection []absser.ISODuration) error {
return errors.New("collections serialization is not supported with FormSerializationWriter")
return writeCollectionOfPrimitiveValues(key, w.WriteISODurationValue, collection)
}

// WriteCollectionOfTimeOnlyValues writes a collection of TimeOnly values to underlying the byte array.
func (w *FormSerializationWriter) WriteCollectionOfTimeOnlyValues(key string, collection []absser.TimeOnly) error {
return errors.New("collections serialization is not supported with FormSerializationWriter")
return writeCollectionOfPrimitiveValues(key, w.WriteTimeOnlyValue, collection)
}

// WriteCollectionOfDateOnlyValues writes a collection of DateOnly values to underlying the byte array.
func (w *FormSerializationWriter) WriteCollectionOfDateOnlyValues(key string, collection []absser.DateOnly) error {
return errors.New("collections serialization is not supported with FormSerializationWriter")
return writeCollectionOfPrimitiveValues(key, w.WriteDateOnlyValue, collection)
}

// WriteCollectionOfUUIDValues writes a collection of UUID values to underlying the byte array.
func (w *FormSerializationWriter) WriteCollectionOfUUIDValues(key string, collection []uuid.UUID) error {
return errors.New("collections serialization is not supported with FormSerializationWriter")
return writeCollectionOfPrimitiveValues(key, w.WriteUUIDValue, collection)
}

// WriteCollectionOfBoolValues writes a collection of Bool values to underlying the byte array.
func (w *FormSerializationWriter) WriteCollectionOfBoolValues(key string, collection []bool) error {
return errors.New("collections serialization is not supported with FormSerializationWriter")
return writeCollectionOfPrimitiveValues(key, w.WriteBoolValue, collection)
}

// WriteCollectionOfByteValues writes a collection of Byte values to underlying the byte array.
func (w *FormSerializationWriter) WriteCollectionOfByteValues(key string, collection []byte) error {
return errors.New("collections serialization is not supported with FormSerializationWriter")
return writeCollectionOfPrimitiveValues(key, w.WriteByteValue, collection)
}

// WriteCollectionOfInt8Values writes a collection of int8 values to underlying the byte array.
func (w *FormSerializationWriter) WriteCollectionOfInt8Values(key string, collection []int8) error {
return errors.New("collections serialization is not supported with FormSerializationWriter")
return writeCollectionOfPrimitiveValues(key, w.WriteInt8Value, collection)
}

// GetSerializedContent returns the resulting byte array from the serialization writer.
Expand Down
45 changes: 45 additions & 0 deletions form_serialization_writer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package formserialization

import (
"fmt"
"github.com/google/uuid"
"github.com/microsoft/kiota-serialization-form-go/internal"
"testing"
"time"
Expand Down Expand Up @@ -163,6 +164,50 @@ func TestWriteMultipleTypes(t *testing.T) {
assert.Equal(t, len("key=value&add1=string&add2=pointer&key2=value2"), len(string(result[:])))
}

func TestWriteMultipleCollections(t *testing.T) {
serializer := NewFormSerializationWriter()
value := "value"
serializer.WriteStringValue("key", &value)

adlData := map[string]interface{}{
"string": []string{"str1", "str2"},
"int32": []int32{34, 56},
"bool": []bool{true, false},
"uint8": []uint8{1, 2},
"float32": []float32{32.0, 34.1},
"float64": []float64{100.1, 400.1},
"int64": []int64{567, 765},
"time": []time.Time{time.Now()},
"timeonly": []*absser.ISODuration{absser.NewDuration(0, 0, 1, 0, 0, 0, 0)},
"dateonly": []*absser.DateOnly{absser.NewDateOnly(time.Now())},
"isoduration": []*absser.ISODuration{absser.NewDuration(0, 0, 1, 0, 0, 0, 0)},
"uuid": []uuid.UUID{uuid.New()},
"base64": []int64{567, 765},
"int8": []int8{2, 3},
}
serializer.WriteAdditionalData(adlData)

result, err := serializer.GetSerializedContent()

assert.Nil(t, err)
resultVal := string(result[:])
assert.Contains(t, resultVal, "key=value&")
assert.Contains(t, resultVal, "string=str1&")
assert.Contains(t, resultVal, "string=str2")
assert.Contains(t, resultVal, "int32=34")
assert.Contains(t, resultVal, "int32=56")
assert.Contains(t, resultVal, "bool=true")
assert.Contains(t, resultVal, "bool=false")
assert.Contains(t, resultVal, "uint8=1")
assert.Contains(t, resultVal, "uint8=2")
assert.Contains(t, resultVal, "float32=32")
assert.Contains(t, resultVal, "float32=34")
assert.Contains(t, resultVal, "float64=100.1")
assert.Contains(t, resultVal, "float64=400.1")
assert.Contains(t, resultVal, "int64=567")
assert.Contains(t, resultVal, "int64=765")
}

func TestEscapesNewLinesInStrings(t *testing.T) {
serializer := NewFormSerializationWriter()
value := "value\nwith\nnew\nlines"
Expand Down