diff --git a/.github/workflows/linter.yaml b/.github/workflows/linter.yaml
index 6c66e224..e19c3ce8 100644
--- a/.github/workflows/linter.yaml
+++ b/.github/workflows/linter.yaml
@@ -30,6 +30,10 @@ jobs:
run: |
go mod tidy -v
go mod vendor
+
+ # tmp ignore due to issue: https://github.com/francoispqt/gojay/pull/157
+ # this ignore should be removed once the pull request has been merged
+ git update-index --assume-unchanged vendor/github.com/francoispqt/gojay/*
- name: Git changes
run: |
diff --git a/.gitignore b/.gitignore
index f52d087e..91bf7bdf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -20,3 +20,4 @@
bin/
tmp/
+
diff --git a/pkg/codec/json/array.go b/pkg/codec/json/array.go
index 9eabac01..367674e9 100644
--- a/pkg/codec/json/array.go
+++ b/pkg/codec/json/array.go
@@ -4,159 +4,74 @@ import (
"github.com/francoispqt/gojay"
"github.com/jexia/semaphore/pkg/references"
"github.com/jexia/semaphore/pkg/specs"
- "github.com/jexia/semaphore/pkg/specs/types"
)
-// NewArray constructs a new JSON array encoder/decoder
-func NewArray(resource string, template specs.Template, refs references.Store) *Array {
- // skip arrays which have no elements
- if len(template.Repeated) == 0 && template.Reference == nil {
- return nil
- }
-
- var reference *references.Reference
- if template.Reference != nil {
- reference = refs.Load(template.Reference.Resource, template.Reference.Path)
- }
-
- if template.Reference != nil && reference == nil {
- return nil
- }
+// Array represents a JSON array.
+type Array struct {
+ resource string
+ template specs.Template
+ repeated specs.Repeated
+ reference *specs.PropertyReference
+ store references.Store
+}
- template, err := template.Repeated.Template()
+// NewArray creates a new array to be JSON encoded/decoded.
+func NewArray(resource string, repeated specs.Repeated, reference *specs.PropertyReference, store references.Store) *Array {
+ template, err := repeated.Template()
if err != nil {
panic(err)
}
- generator := &Array{
- resource: resource,
- template: template,
- ref: reference,
+ return &Array{
+ resource: resource,
+ template: template,
+ repeated: repeated,
+ reference: reference,
+ store: store,
}
-
- if template.Repeated != nil {
- generator.keys = len(template.Repeated)
- }
-
- return generator
}
-// Array represents a JSON array
-type Array struct {
- resource string
- template specs.Template
- ref *references.Reference
- keys int
-}
+// MarshalJSONArray encodes the array into the given gojay encoder.
+func (array *Array) MarshalJSONArray(encoder *gojay.Encoder) {
+ if array.reference == nil {
+ for _, template := range array.repeated {
+ encodeElement(encoder, "", template, array.store)
+ }
-// MarshalJSONArray encodes the array into the given gojay encoder
-func (array *Array) MarshalJSONArray(enc *gojay.Encoder) {
- if array == nil || array.ref == nil {
return
}
- for _, store := range array.ref.Repeated {
- switch {
- case array.template.Message != nil:
- object := NewObject(array.resource, array.template.Message, store)
- if object == nil {
- break
- }
-
- enc.AddObject(object)
- break
- case array.template.Repeated != nil:
- array := NewArray(array.resource, array.template, store)
- if array == nil {
- break
- }
+ var reference = array.store.Load(array.reference.Resource, array.reference.Path)
- enc.AddArray(array)
- break
- case array.template.Enum != nil:
- // TODO: check if enums in arrays work
- if array.template.Reference == nil {
- break
- }
-
- ref := store.Load("", "")
- if ref == nil || ref.Enum == nil {
- break
- }
-
- key := array.template.Enum.Positions[*ref.Enum].Key
- AddType(enc, types.String, key)
- break
- case array.template.Scalar != nil:
- val := array.template.Scalar.Default
+ if reference == nil {
+ return
+ }
- ref := store.Load("", "")
- if ref != nil {
- val = ref.Value
- }
+ for _, store := range reference.Repeated {
+ array.template.Reference = new(specs.PropertyReference)
- AddType(enc, array.template.Scalar.Type, val)
- break
- }
+ encodeElement(encoder, array.resource, array.template, store)
}
}
-// UnmarshalJSONArray unmarshals the given specs into the configured reference store
-func (array *Array) UnmarshalJSONArray(dec *gojay.Decoder) error {
- if array == nil {
- return nil
- }
-
- // FIXME: array.keys is derrived from a wrong value
- store := references.NewReferenceStore(array.keys)
+// UnmarshalJSONArray unmarshals the given specs into the configured reference store.
+func (array *Array) UnmarshalJSONArray(decoder *gojay.Decoder) error {
+ store := references.NewReferenceStore(0)
- switch {
- case array.template.Message != nil:
- object := NewObject(array.resource, array.template.Message, store)
- err := dec.AddObject(object)
- if err != nil {
- return err
- }
-
- array.ref.Append(store)
- break
- case array.template.Repeated != nil:
- array := NewArray(array.resource, array.template, store)
- err := dec.AddArray(array)
- if err != nil {
- return err
- }
-
- array.ref.Append(store)
- break
- case array.template.Enum != nil:
- var key string
- err := dec.AddString(&key)
- if err != nil {
- return err
- }
-
- enum := array.template.Enum.Keys[key]
- if enum != nil {
- store.StoreEnum("", "", enum.Position)
- array.ref.Append(store)
- }
- break
- case array.template.Scalar != nil:
- val, err := DecodeType(dec, array.template.Scalar.Type)
- if err != nil {
- return err
+ if array.reference != nil {
+ var reference = array.store.Load(array.reference.Resource, array.reference.Path)
+ if reference == nil {
+ return nil
}
- store.StoreValue("", "", val)
- array.ref.Append(store)
- break
+ reference.Append(store)
}
- return nil
+ // NOTE: always consume an array even if the reference is not set
+ return decodeElement(decoder, "", "", array.template, store)
}
-// IsNil returns whether the given array is null or not
+// IsNil returns whether the given array is null or not.
func (array *Array) IsNil() bool {
- return false
+ return array == nil
}
diff --git a/pkg/codec/json/decode.go b/pkg/codec/json/decode.go
new file mode 100644
index 00000000..02180e0f
--- /dev/null
+++ b/pkg/codec/json/decode.go
@@ -0,0 +1,36 @@
+package json
+
+import (
+ "github.com/francoispqt/gojay"
+ "github.com/jexia/semaphore/pkg/references"
+ "github.com/jexia/semaphore/pkg/specs"
+)
+
+func decodeElement(decoder *gojay.Decoder, resource, path string, template specs.Template, store references.Store) error {
+ var reference = specs.PropertyReference{
+ Resource: resource,
+ Path: path,
+ }
+
+ switch {
+ case template.Message != nil:
+ object := NewObject(resource, template.Message, store)
+ if object == nil {
+ break
+ }
+
+ return decoder.Object(object)
+ case template.Repeated != nil:
+ store.StoreReference(resource, &references.Reference{Path: path})
+
+ return decoder.Array(
+ NewArray(resource, template.Repeated, &reference, store),
+ )
+ case template.Enum != nil:
+ return NewEnum("", template.Enum, &reference, store).UnmarshalJSONEnum(decoder)
+ case template.Scalar != nil:
+ return NewScalar("", template.Scalar, &reference, store).UnmarshalJSONScalar(decoder)
+ }
+
+ return nil
+}
diff --git a/pkg/codec/json/encode.go b/pkg/codec/json/encode.go
new file mode 100644
index 00000000..a086720e
--- /dev/null
+++ b/pkg/codec/json/encode.go
@@ -0,0 +1,43 @@
+package json
+
+import (
+ "github.com/francoispqt/gojay"
+ "github.com/jexia/semaphore/pkg/references"
+ "github.com/jexia/semaphore/pkg/specs"
+)
+
+func encodeElement(encoder *gojay.Encoder, resource string, template specs.Template, store references.Store) {
+ switch {
+ case template.Message != nil:
+ encoder.Object(
+ NewObject(resource, template.Message, store),
+ )
+ case template.Repeated != nil:
+ encoder.Array(
+ NewArray(resource, template.Repeated, template.Reference, store),
+ )
+ case template.Enum != nil:
+ NewEnum("", template.Enum, template.Reference, store).MarshalJSONEnum(encoder)
+ case template.Scalar != nil:
+ NewScalar("", template.Scalar, template.Reference, store).MarshalJSONScalar(encoder)
+ }
+}
+
+func encodeElementKey(encoder *gojay.Encoder, resource, key string, template specs.Template, store references.Store) {
+ switch {
+ case template.Message != nil:
+ encoder.ObjectKey(
+ key,
+ NewObject(resource, template.Message, store),
+ )
+ case template.Repeated != nil:
+ encoder.ArrayKey(
+ key,
+ NewArray(resource, template.Repeated, template.Reference, store),
+ )
+ case template.Enum != nil:
+ NewEnum(key, template.Enum, template.Reference, store).MarshalJSONEnumKey(encoder)
+ case template.Scalar != nil:
+ NewScalar(key, template.Scalar, template.Reference, store).MarshalJSONScalarKey(encoder)
+ }
+}
diff --git a/pkg/codec/json/enum.go b/pkg/codec/json/enum.go
new file mode 100644
index 00000000..4bff0caf
--- /dev/null
+++ b/pkg/codec/json/enum.go
@@ -0,0 +1,87 @@
+package json
+
+import (
+ "github.com/francoispqt/gojay"
+ "github.com/jexia/semaphore/pkg/references"
+ "github.com/jexia/semaphore/pkg/specs"
+ "github.com/jexia/semaphore/pkg/specs/types"
+)
+
+// Enum is a vrapper over specs.Enum providing JSON encoding/decoding.
+type Enum struct {
+ name string
+ enum *specs.Enum
+ reference *specs.PropertyReference
+ store references.Store
+}
+
+// NewEnum creates a new enum by wrapping provided specs.Enum.
+func NewEnum(name string, enum *specs.Enum, reference *specs.PropertyReference, store references.Store) *Enum {
+ return &Enum{
+ name: name,
+ enum: enum,
+ reference: reference,
+ store: store,
+ }
+}
+
+func (enum *Enum) value() *specs.EnumValue {
+ if enum.reference == nil {
+ return nil
+ }
+
+ var reference = enum.store.Load(enum.reference.Resource, enum.reference.Path)
+ if reference == nil || reference.Enum == nil {
+ return nil
+ }
+
+ if position := reference.Enum; position != nil {
+ return enum.enum.Positions[*reference.Enum]
+ }
+
+ return nil
+}
+
+// MarshalJSONEnum marshals enum which is not an object field (array element or
+// standalone enum).
+func (enum Enum) MarshalJSONEnum(encoder *gojay.Encoder) {
+ var value interface{}
+ if enumValue := enum.value(); enumValue != nil {
+ value = enumValue.Key
+ }
+
+ AddType(encoder, types.String, value)
+}
+
+// MarshalJSONEnumKey marshals enum which is an object field.
+func (enum Enum) MarshalJSONEnumKey(encoder *gojay.Encoder) {
+ var enumValue = enum.value()
+ if enumValue == nil {
+ return
+ }
+
+ AddTypeKey(encoder, enum.name, types.String, enumValue.Key)
+}
+
+// UnmarshalJSONEnum unmarshals enum value from decoder to the reference store.
+func (enum Enum) UnmarshalJSONEnum(decoder *gojay.Decoder) error {
+ var key string
+ if err := decoder.AddString(&key); err != nil {
+ return err
+ }
+
+ var (
+ reference = &references.Reference{
+ Path: enum.reference.Path,
+ }
+ enumValue = enum.enum.Keys[key]
+ )
+
+ if enumValue != nil {
+ reference.Enum = &enumValue.Position
+ }
+
+ enum.store.StoreReference(enum.reference.Resource, reference)
+
+ return nil
+}
diff --git a/pkg/codec/json/errors.go b/pkg/codec/json/errors.go
index a6ba1fa1..24a06366 100644
--- a/pkg/codec/json/errors.go
+++ b/pkg/codec/json/errors.go
@@ -1,17 +1,13 @@
package json
-import (
- "fmt"
-
- "github.com/jexia/semaphore/pkg/prettyerr"
-)
+import "github.com/jexia/semaphore/pkg/prettyerr"
// ErrUndefinedSpecs occurs when spacs are nil
type ErrUndefinedSpecs struct{}
// Error returns a description of the given error as a string
func (e ErrUndefinedSpecs) Error() string {
- return fmt.Sprint("no object specs defined")
+ return "no object specs defined"
}
// Prettify returns the prettified version of the given error
diff --git a/pkg/codec/json/json.go b/pkg/codec/json/json.go
index daedba06..c5e81cb2 100644
--- a/pkg/codec/json/json.go
+++ b/pkg/codec/json/json.go
@@ -1,9 +1,8 @@
package json
import (
- "bytes"
+ "bufio"
"io"
- "io/ioutil"
"github.com/francoispqt/gojay"
"github.com/jexia/semaphore/pkg/codec"
@@ -11,18 +10,19 @@ import (
"github.com/jexia/semaphore/pkg/specs"
)
-// NewConstructor constructs a new JSON constructor
-func NewConstructor() *Constructor {
- return &Constructor{}
-}
+// Constructor is capable of constructing new codec managers for the given resource and specs.
+type Constructor struct{}
-// Constructor is capable of constructing new codec managers for the given resource and specs
-type Constructor struct {
-}
+// NewConstructor constructs a new JSON constructor.
+func NewConstructor() *Constructor { return &Constructor{} }
+
+// Name returns the name of the JSON codec constructor.
+func (constructor *Constructor) Name() string { return "json" }
-// Name returns the name of the JSON codec constructor
-func (constructor *Constructor) Name() string {
- return "json"
+// Manager manages a specs object and allows to encode/decode messages.
+type Manager struct {
+ resource string
+ property *specs.Property
}
// New constructs a new JSON codec manager
@@ -33,63 +33,65 @@ func (constructor *Constructor) New(resource string, specs *specs.ParameterMap)
return &Manager{
resource: resource,
- specs: specs.Property,
+ property: specs.Property,
}, nil
}
-// Manager manages a specs object and allows to encode/decode messages
-type Manager struct {
- resource string
- specs *specs.Property
-}
-
// Name returns the proto codec name
-func (manager *Manager) Name() string {
- return "json"
-}
+func (manager *Manager) Name() string { return "json" }
// Property returns the manager property which is used to marshal and unmarshal data
-func (manager *Manager) Property() *specs.Property {
- return manager.specs
-}
+func (manager *Manager) Property() *specs.Property { return manager.property }
// Marshal marshals the given reference store into a JSON message.
// This method is called during runtime to encode a new message with the values stored inside the given reference store
-func (manager *Manager) Marshal(refs references.Store) (io.Reader, error) {
- if manager.specs == nil {
+func (manager *Manager) Marshal(store references.Store) (io.Reader, error) {
+ if manager.property == nil {
return nil, nil
}
- object := NewObject(manager.resource, manager.specs.Message, refs)
- bb, err := gojay.MarshalJSONObject(object)
- if err != nil {
- return nil, err
- }
+ var (
+ reader, writer = io.Pipe()
+ encoder = gojay.BorrowEncoder(writer)
+ )
+
+ go func() {
+ defer encoder.Release()
+
+ encodeElement(encoder, manager.resource, manager.property.Template, store)
+
+ if _, err := encoder.Write(); err != nil {
+ _ = writer.CloseWithError(err)
- return bytes.NewBuffer(bb), nil
+ return
+ }
+
+ writer.Close()
+ }()
+
+ return reader, nil
}
// Unmarshal unmarshals the given JSON io reader into the given reference store.
// This method is called during runtime to decode a new message and store it inside the given reference store
-func (manager *Manager) Unmarshal(reader io.Reader, refs references.Store) error {
- if manager.specs == nil {
+func (manager *Manager) Unmarshal(reader io.Reader, store references.Store) error {
+ if manager.property == nil {
return nil
}
- bb, err := ioutil.ReadAll(reader)
- if err != nil {
- return err
- }
+ var (
+ buff = bufio.NewReader(reader)
+ _, err = buff.ReadByte()
+ )
- if len(bb) == 0 {
+ if err == io.EOF {
return nil
}
- object := NewObject(manager.resource, manager.specs.Message, refs)
- err = gojay.UnmarshalJSONObject(bb, object)
- if err != nil {
- return err
- }
+ _ = buff.UnreadByte()
+
+ var decoder = gojay.NewDecoder(buff)
+ defer decoder.Release()
- return nil
+ return decodeElement(decoder, manager.resource, manager.property.Path, manager.property.Template, store)
}
diff --git a/pkg/codec/json/json_test.go b/pkg/codec/json/json_test.go
index 1543b70f..f578853c 100644
--- a/pkg/codec/json/json_test.go
+++ b/pkg/codec/json/json_test.go
@@ -5,19 +5,18 @@ import (
"encoding/json"
"io/ioutil"
"path/filepath"
- "reflect"
"testing"
"github.com/jexia/semaphore"
"github.com/jexia/semaphore/cmd/semaphore/daemon/providers"
"github.com/jexia/semaphore/pkg/broker"
"github.com/jexia/semaphore/pkg/broker/logger"
+ "github.com/jexia/semaphore/pkg/codec/tests"
"github.com/jexia/semaphore/pkg/functions"
"github.com/jexia/semaphore/pkg/providers/hcl"
"github.com/jexia/semaphore/pkg/providers/mock"
"github.com/jexia/semaphore/pkg/references"
"github.com/jexia/semaphore/pkg/specs"
- "github.com/jexia/semaphore/pkg/specs/template"
)
func NewMock() (specs.FlowListInterface, error) {
@@ -52,73 +51,6 @@ func NewMock() (specs.FlowListInterface, error) {
return collection.FlowListInterface, nil
}
-func ValidateStore(t *testing.T, prop *specs.Property, resource string, origin string, input map[string]interface{}, store references.Store) {
- if prop.Message == nil {
- t.Fatalf("%s, property message not set", prop.Path)
- }
-
- for key, value := range input {
- nprop := prop.Message[key]
- if nprop == nil {
- nprop = prop
- }
-
- path := template.JoinPath(origin, key)
- nested, is := value.(map[string]interface{})
- if is {
- ValidateStore(t, nprop, resource, path, nested, store)
- continue
- }
-
- repeated, is := value.([]map[string]interface{})
- if is {
- repeating := store.Load(resource, path)
- if repeating == nil {
- t.Fatalf("resource not found %s:%s", resource, path)
- }
-
- for index, store := range repeating.Repeated {
- ValidateStore(t, nprop, resource, path, repeated[index], store)
- }
- continue
- }
-
- values, is := value.([]interface{})
- if is {
- repeating := store.Load(resource, path)
- if repeating == nil {
- t.Fatalf("resource not found %s:%s", resource, path)
- }
-
- for index, store := range repeating.Repeated {
- // small wrapper that allows to reuse functionalities
- wrapper := map[string]interface{}{
- "": values[index],
- }
-
- ValidateStore(t, nprop, "", "", wrapper, store)
- }
- continue
- }
-
- ref := store.Load(resource, path)
- if ref == nil {
- t.Fatalf("resource not found %s:%s", resource, path)
- }
-
- if ref.Enum != nil && nprop.Enum != nil {
- if nprop.Enum.Positions[*ref.Enum] == nil {
- t.Fatalf("unexpected enum value at %s '%+v', expected '%+v'", path, ref.Enum, value)
- }
- continue
- }
-
- if ref.Value != value {
- t.Fatalf("unexpected value at %s '%+v', expected '%+v'", path, ref.Value, value)
- }
- }
-}
-
func BenchmarkSimpleMarshal(b *testing.B) {
input := map[string]interface{}{
"message": "message",
@@ -150,7 +82,9 @@ func BenchmarkSimpleMarshal(b *testing.B) {
b.Fatal(err)
}
- ioutil.ReadAll(reader)
+ if _, err := ioutil.ReadAll(reader); err != nil {
+ b.Fatal(err)
+ }
}
}
@@ -187,7 +121,9 @@ func BenchmarkNestedMarshal(b *testing.B) {
b.Fatal(err)
}
- ioutil.ReadAll(reader)
+ if _, err := ioutil.ReadAll(reader); err != nil {
+ b.Fatal(err)
+ }
}
}
@@ -226,7 +162,9 @@ func BenchmarkRepeatedMessagesMarshal(b *testing.B) {
b.Fatal(err)
}
- ioutil.ReadAll(reader)
+ if _, err := ioutil.ReadAll(reader); err != nil {
+ b.Fatal(err)
+ }
}
}
@@ -263,7 +201,9 @@ func BenchmarkRepeatedValuesMarshal(b *testing.B) {
b.Fatal(err)
}
- ioutil.ReadAll(reader)
+ if _, err := ioutil.ReadAll(reader); err != nil {
+ b.Fatal(err)
+ }
}
}
@@ -446,103 +386,174 @@ func TestMarshal(t *testing.T) {
}
flow := flows.Get("complete")
- req := flow.GetNodes().Get("first").Call.Request
+ schema := flow.GetNodes().Get("first").Call.Request
- constructor := &Constructor{}
- manager, err := constructor.New("input", req)
- if err != nil {
- t.Fatal(err)
+ type test struct {
+ input map[string]interface{}
+ schema *specs.ParameterMap
+ expected string
}
- tests := map[string]map[string]interface{}{
+ tests := map[string]test{
+ "nil schema": {
+ schema: new(specs.ParameterMap),
+ },
+ "scalar from reference": {
+ input: map[string]interface{}{
+ "integer": int32(42),
+ },
+ schema: &specs.ParameterMap{
+ Property: func() *specs.Property {
+ var property = tests.PropInteger()
+ property.Reference = &specs.PropertyReference{
+ Resource: "input",
+ Path: "integer",
+ }
+
+ return property
+ }(),
+ },
+ expected: `42`,
+ },
+ "scalar default value": {
+ input: map[string]interface{}{
+ "integer": int32(42),
+ },
+ schema: &specs.ParameterMap{
+ Property: func() *specs.Property {
+ var property = tests.PropInteger()
+ property.Scalar.Default = int32(42)
+
+ return property
+ }(),
+ },
+ expected: `42`,
+ },
+ "array empty": {
+ schema: tests.SchemaArrayDefaultEmpty,
+ expected: `[null]`,
+ },
+ "array default reference": {
+ input: map[string]interface{}{
+ "string": "foo",
+ },
+ schema: tests.SchemaArrayWithValues,
+ expected: `["foo","bar"]`,
+ },
+ "array default no reference value": {
+ input: map[string]interface{}{},
+ schema: tests.SchemaArrayWithValues,
+ expected: `[null,"bar"]`,
+ },
+ "array of arrays": {
+ input: map[string]interface{}{},
+ schema: tests.SchemaArrayOfArrays,
+ expected: `[[null,"bar"]]`,
+ },
"simple": {
- "message": "some message",
- "nested": map[string]interface{}{},
+ input: map[string]interface{}{
+ "message": "some message",
+ "nested": map[string]interface{}{},
+ },
+ schema: schema,
+ expected: `{"message":"some message","nested":{},"repeating":[],"repeating_values":[],"repeating_enum":[]}`,
},
"nested": {
- "nested": map[string]interface{}{
- "value": "some message",
+ input: map[string]interface{}{
+ "nested": map[string]interface{}{
+ "value": "some message",
+ },
},
+ schema: schema,
+ expected: `{"nested":{"value":"some message"},"repeating":[],"repeating_values":[],"repeating_enum":[]}`,
},
"enum": {
- "nested": map[string]interface{}{},
- "enum": references.Enum("PENDING", 2),
+ input: map[string]interface{}{
+ "nested": map[string]interface{}{},
+ "enum": references.Enum("PENDING", 2),
+ },
+ schema: schema,
+ expected: `{"nested":{},"repeating":[],"repeating_values":[],"enum":"PENDING","repeating_enum":[]}`,
},
"repeating_enum": {
- "nested": map[string]interface{}{},
- "repeating_enum": []interface{}{
- references.Enum("UNKNOWN", 1),
- references.Enum("PENDING", 2),
+ input: map[string]interface{}{
+ "repeating_enum": []interface{}{
+ references.Enum("UNKNOWN", 1),
+ references.Enum("PENDING", 2),
+ },
},
+ schema: schema,
+ expected: `{"nested":{},"repeating":[],"repeating_values":[],"repeating_enum":["UNKNOWN","PENDING"]}`,
},
- "repeating": {
- "nested": map[string]interface{}{},
- "repeating": []map[string]interface{}{
- {
- "value": "repeating value",
- },
- {
- "value": "repeating value",
+ "repeating objects": {
+ input: map[string]interface{}{
+ "repeating": []map[string]interface{}{
+ {
+ "value": "repeating value",
+ },
+ {
+ "value": "repeating value",
+ },
},
},
+ schema: schema,
+ expected: `{"nested":{},"repeating":[{"value":"repeating value"},{"value":"repeating value"}],"repeating_values":[],"repeating_enum":[]}`,
},
- "repeating_values": {
- "nested": map[string]interface{}{},
- "repeating_values": []interface{}{
- "repeating value",
- "repeating value",
+ "repeating values from reference": {
+ input: map[string]interface{}{
+ "repeating_values": []interface{}{
+ "repeating one",
+ "repeating two",
+ },
},
+ schema: schema,
+ expected: `{"nested":{},"repeating":[],"repeating_values":["repeating one","repeating two"],"repeating_enum":[]}`,
},
"complex": {
- "message": "hello world",
- "nested": map[string]interface{}{
- "value": "nested value",
- },
- "repeating": []map[string]interface{}{
- {
- "value": "repeating value",
+ input: map[string]interface{}{
+ "message": "hello world",
+ "nested": map[string]interface{}{
+ "value": "nested value",
},
- {
- "value": "repeating value",
+ "repeating": []map[string]interface{}{
+ {
+ "value": "repeating value",
+ },
+ {
+ "value": "repeating value",
+ },
},
},
+ schema: schema,
+ expected: `{"message":"hello world","nested":{"value":"nested value"},"repeating":[{"value":"repeating value"},{"value":"repeating value"}],"repeating_values":[],"repeating_enum":[]}`,
},
}
- for key, input := range tests {
+ for key, test := range tests {
t.Run(key, func(t *testing.T) {
- inputAsJSON, err := json.Marshal(input)
+ constructor := &Constructor{}
+ manager, err := constructor.New("input", test.schema)
if err != nil {
t.Fatal(err)
}
- refs := references.NewReferenceStore(len(input))
- refs.StoreValues("input", "", input)
+ refs := references.NewReferenceStore(len(test.input))
+ refs.StoreValues("input", "", test.input)
reader, err := manager.Marshal(refs)
if err != nil {
t.Fatal(err)
}
- responseAsJSON, err := ioutil.ReadAll(reader)
- if err != nil {
- t.Fatal(err)
- }
-
- result := map[string]interface{}{}
- err = json.Unmarshal(responseAsJSON, &result)
- if err != nil {
- t.Fatal(err)
- }
-
- expected := map[string]interface{}{}
- err = json.Unmarshal(inputAsJSON, &expected)
- if err != nil {
- t.Fatal(err)
- }
+ if test.expected != "" {
+ data, err := ioutil.ReadAll(reader)
+ if err != nil {
+ t.Fatal(err)
+ }
- if !reflect.DeepEqual(expected, result) {
- t.Errorf("unexpected response %s, expected %s", string(responseAsJSON), string(inputAsJSON))
+ if actual := string(data); actual != test.expected {
+ t.Errorf("unexpected output %s, expected %s", data, test.expected)
+ }
}
})
}
@@ -561,83 +572,214 @@ func TestUnmarshal(t *testing.T) {
t.Fatal(err)
}
- flow := flows.Get("complete")
- req := flow.GetNodes().Get("first").Call.Request
+ var (
+ flow = flows.Get("complete")
+ schema = flow.GetNodes().Get("first").Call.Request
+ )
- constructor := &Constructor{}
- manager, err := constructor.New("input", req)
- if err != nil {
- t.Fatal(err)
+ type test struct {
+ input string
+ schema *specs.ParameterMap
+ expected tests.Expect
}
- tests := map[string]map[string]interface{}{
+ testsCases := map[string]test{
+ "nil schema": {
+ schema: new(specs.ParameterMap),
+ },
+ "empty": {
+ input: ``,
+ schema: schema,
+ },
+ "array": {
+ input: `[null,"bar"]`,
+ schema: tests.SchemaArrayDefaultEmpty,
+ expected: tests.Expect{
+ Nested: map[string]tests.Expect{
+ "array": {
+ Repeated: []tests.Expect{
+ {
+ Value: nil,
+ },
+ {
+ Value: "bar",
+ },
+ },
+ },
+ },
+ },
+ },
+ "array of arrays": {
+ input: `[[null,"bar"]]`,
+ schema: tests.SchemaArrayOfArrays,
+ expected: tests.Expect{
+ Nested: map[string]tests.Expect{
+ "array": {
+ Repeated: []tests.Expect{
+ {
+ Repeated: []tests.Expect{
+ {
+ Value: nil,
+ },
+ {
+ Value: "bar",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
"simple": {
- "message": "some message",
- "nested": map[string]interface{}{},
+ input: `{"message":"some message"}`,
+ schema: schema,
+ expected: tests.Expect{
+ Nested: map[string]tests.Expect{
+ "message": {
+ Value: "some message",
+ },
+ },
+ },
},
"nested": {
- "nested": map[string]interface{}{
- "value": "some message",
+ input: `{"nested":{"value":"some message"}}`,
+ schema: schema,
+ expected: tests.Expect{
+ Nested: map[string]tests.Expect{
+ "nested.value": {
+ Value: "some message",
+ },
+ },
},
},
"enum": {
- "enum": "PENDING",
+ input: `{"enum":"PENDING"}`,
+ schema: schema,
+ expected: tests.Expect{
+ Nested: map[string]tests.Expect{
+ "enum": {
+ Enum: func() *int32 { i := int32(2); return &i }(),
+ },
+ },
+ },
},
"repeating_enum": {
- "repeating_enum": []interface{}{
- "UNKNOWN",
- "PENDING",
+ input: `{"repeating_enum":["UNKNOWN","PENDING"]}`,
+ schema: schema,
+ expected: tests.Expect{
+ Nested: map[string]tests.Expect{
+ "repeating_enum": {
+ Repeated: []tests.Expect{
+ {
+ Enum: func() *int32 { i := int32(1); return &i }(),
+ },
+ {
+ Enum: func() *int32 { i := int32(2); return &i }(),
+ },
+ },
+ },
+ },
},
},
- "repeating": {
- "nested": map[string]interface{}{},
- "repeating": []map[string]interface{}{
- {
- "value": "repeating value",
- },
- {
- "value": "repeating value",
+ "repeating_values": {
+ input: `{"repeating_values":["repeating one","repeating two"]}`,
+ schema: schema,
+ expected: tests.Expect{
+ Nested: map[string]tests.Expect{
+ "repeating_values": {
+ Repeated: []tests.Expect{
+ {
+ Value: "repeating one",
+ },
+ {
+ Value: "repeating two",
+ },
+ },
+ },
},
},
},
- "repeating_values": {
- "nested": map[string]interface{}{},
- "repeating_values": []interface{}{
- "repeating value",
- "repeating value",
+ "repeating objects": {
+ input: `{"repeating":[{"value":"repeating one"},{"value":"repeating two"}]}`,
+ schema: schema,
+ expected: tests.Expect{
+ Nested: map[string]tests.Expect{
+ "repeating": {
+ Repeated: []tests.Expect{
+ {
+ Nested: map[string]tests.Expect{
+ "repeating.value": {
+ Value: "repeating one",
+ },
+ },
+ },
+ {
+ Nested: map[string]tests.Expect{
+ "repeating.value": {
+ Value: "repeating two",
+ },
+ },
+ },
+ },
+ },
+ },
},
},
"complex": {
- "message": "hello world",
- "nested": map[string]interface{}{
- "value": "nested value",
- },
- "repeating": []map[string]interface{}{
- {
- "value": "repeating value",
- },
- {
- "value": "repeating value",
+ input: `{"message":"hello world","nested":{"value":"hello nested world"},"repeating":[{"value":"repeating one"},{"value":"repeating two"}]}`,
+ schema: schema,
+ expected: tests.Expect{
+ Nested: map[string]tests.Expect{
+ "message": {
+ Value: "hello world",
+ },
+ "nested": {
+ Nested: map[string]tests.Expect{
+ "value": {
+ Value: "hello nested world",
+ },
+ },
+ },
+ "repeating": {
+ Repeated: []tests.Expect{
+ {
+ Nested: map[string]tests.Expect{
+ "repeating.value": {
+ Value: "repeating one",
+ },
+ },
+ },
+ {
+ Nested: map[string]tests.Expect{
+ "repeating.value": {
+ Value: "repeating two",
+ },
+ },
+ },
+ },
+ },
},
},
},
}
- for key, input := range tests {
+ for key, test := range testsCases {
t.Run(key, func(t *testing.T) {
- inputAsJSON, err := json.Marshal(input)
+ constructor := &Constructor{}
+ manager, err := constructor.New("input", test.schema)
if err != nil {
t.Fatal(err)
}
- store := references.NewReferenceStore(len(input))
- err = manager.Unmarshal(bytes.NewBuffer(inputAsJSON), store)
+ store := references.NewReferenceStore(0)
+ err = manager.Unmarshal(bytes.NewBuffer([]byte(test.input)), store)
if err != nil {
t.Fatal(err)
}
- ValidateStore(t, req.Property, "input", "", input, store)
+ tests.Assert(t, "input", "", store, test.expected)
})
}
}
diff --git a/pkg/codec/json/object.go b/pkg/codec/json/object.go
index 39361ebc..44697b92 100644
--- a/pkg/codec/json/object.go
+++ b/pkg/codec/json/object.go
@@ -4,154 +4,45 @@ import (
"github.com/francoispqt/gojay"
"github.com/jexia/semaphore/pkg/references"
"github.com/jexia/semaphore/pkg/specs"
- "github.com/jexia/semaphore/pkg/specs/types"
)
-// NewObject constructs a new object encoder/decoder for the given specs
-func NewObject(resource string, items specs.Message, refs references.Store) *Object {
- return &Object{
- resource: resource,
- length: len(items),
- refs: refs,
- specs: items,
- }
-}
-
// Object represents a JSON object
type Object struct {
resource string
- specs specs.Message
- refs references.Store
+ message specs.Message
+ store references.Store
length int
}
-// MarshalJSONObject encodes the given specs object into the given gojay encoder
-func (object *Object) MarshalJSONObject(encoder *gojay.Encoder) {
- if object == nil {
- return
+// NewObject constructs a new object encoder/decoder for the given specs
+func NewObject(resource string, message specs.Message, store references.Store) *Object {
+ return &Object{
+ resource: resource,
+ message: message,
+ store: store,
+ length: len(message),
}
+}
- for _, prop := range object.specs {
- switch {
- case prop.Repeated != nil:
- array := NewArray(object.resource, prop.Template, object.refs)
- if array == nil {
- break
- }
-
- encoder.AddArrayKey(prop.Name, array)
- break
- case prop.Message != nil:
- result := NewObject(object.resource, prop.Message, object.refs)
- if result == nil {
- break
- }
-
- encoder.AddObjectKey(prop.Name, result)
- break
- case prop.Enum != nil:
- if prop.Reference == nil {
- break
- }
-
- ref := object.refs.Load(prop.Reference.Resource, prop.Reference.Path)
- if ref == nil || ref.Enum == nil {
- break
- }
-
- enum := prop.Enum.Positions[*ref.Enum]
- if enum == nil {
- break
- }
-
- AddTypeKey(encoder, prop.Name, types.String, enum.Key)
- break
- case prop.Scalar != nil:
- val := prop.Scalar.Default
-
- if prop.Reference != nil {
- ref := object.refs.Load(prop.Reference.Resource, prop.Reference.Path)
- if ref != nil && ref.Value != nil {
- val = ref.Value
- }
- }
-
- if val == nil {
- break
- }
-
- AddTypeKey(encoder, prop.Name, prop.Scalar.Type, val)
- break
- }
-
+// MarshalJSONObject encodes the given specs object into the given gojay encoder
+func (object *Object) MarshalJSONObject(encoder *gojay.Encoder) {
+ for _, prop := range object.message.SortedProperties() {
+ encodeElementKey(encoder, object.resource, prop.Name, prop.Template, object.store)
}
}
// UnmarshalJSONObject unmarshals the given specs into the configured reference store
-func (object *Object) UnmarshalJSONObject(dec *gojay.Decoder, key string) error {
+func (object *Object) UnmarshalJSONObject(decoder *gojay.Decoder, key string) error {
if object == nil {
return nil
}
- property, has := object.specs[key]
+ property, has := object.message[key]
if !has {
return nil
}
- switch {
- case property.Message != nil:
- object := NewObject(object.resource, property.Message, object.refs)
- if object == nil {
- break
- }
-
- return dec.AddObject(object)
- case property.Repeated != nil:
- ref := &references.Reference{
- Path: property.Path,
- }
-
- array := NewArray(object.resource, property.Template, object.refs)
- if array == nil {
- break
- }
-
- err := dec.AddArray(array)
- if err != nil {
- return err
- }
-
- object.refs.StoreReference(object.resource, ref)
- return nil
- }
-
- ref := &references.Reference{
- Path: property.Path,
- }
-
- switch {
- case property.Enum != nil:
- var key string
- dec.AddString(&key)
-
- enum := property.Enum.Keys[key]
- if enum != nil {
- ref.Enum = &enum.Position
- }
-
- break
- case property.Scalar != nil:
- value, err := DecodeType(dec, property.Scalar.Type)
- if err != nil {
- return err
- }
-
- ref.Value = value
- break
- }
-
- object.refs.StoreReference(object.resource, ref)
- return nil
+ return decodeElement(decoder, object.resource, property.Path, property.Template, object.store)
}
// NKeys returns the amount of available keys inside the given object
@@ -161,5 +52,5 @@ func (object *Object) NKeys() int {
// IsNil returns whether the given object is null or not
func (object *Object) IsNil() bool {
- return false
+ return object == nil
}
diff --git a/pkg/codec/json/scalar.go b/pkg/codec/json/scalar.go
new file mode 100644
index 00000000..6d66e77b
--- /dev/null
+++ b/pkg/codec/json/scalar.go
@@ -0,0 +1,70 @@
+package json
+
+import (
+ "github.com/francoispqt/gojay"
+ "github.com/jexia/semaphore/pkg/references"
+ "github.com/jexia/semaphore/pkg/specs"
+)
+
+// Scalar wraps specs.Scalar to be JSON encoded/decoded.
+type Scalar struct {
+ name string
+ scalar *specs.Scalar
+ reference *specs.PropertyReference
+ store references.Store
+}
+
+// NewScalar creates new specs.Scalar wrapper.
+func NewScalar(name string, scalar *specs.Scalar, reference *specs.PropertyReference, store references.Store) *Scalar {
+ return &Scalar{
+ name: name,
+ scalar: scalar,
+ reference: reference,
+ store: store,
+ }
+}
+
+func (scalar Scalar) value() interface{} {
+ var value = scalar.scalar.Default
+
+ if scalar.reference != nil {
+ var reference = scalar.store.Load(scalar.reference.Resource, scalar.reference.Path)
+ if reference != nil && reference.Value != nil {
+ value = reference.Value
+ }
+ }
+
+ return value
+}
+
+// MarshalJSONScalar marshals standalone scalar to JSON.
+func (scalar Scalar) MarshalJSONScalar(encoder *gojay.Encoder) {
+ AddType(encoder, scalar.scalar.Type, scalar.value())
+}
+
+// MarshalJSONScalarKey marshals scalar as an object field.
+func (scalar Scalar) MarshalJSONScalarKey(encoder *gojay.Encoder) {
+ var value = scalar.value()
+ if value == nil {
+ return
+ }
+
+ AddTypeKey(encoder, scalar.name, scalar.scalar.Type, value)
+}
+
+// UnmarshalJSONScalar unmarshals scalar from decoder to the reference store.
+func (scalar Scalar) UnmarshalJSONScalar(decoder *gojay.Decoder) error {
+ value, err := DecodeType(decoder, scalar.scalar.Type)
+ if err != nil {
+ return err
+ }
+
+ var reference = &references.Reference{
+ Path: scalar.reference.Path,
+ Value: value,
+ }
+
+ scalar.store.StoreReference(scalar.reference.Resource, reference)
+
+ return nil
+}
diff --git a/pkg/codec/json/tests/schema.yaml b/pkg/codec/json/tests/schema.yaml
index b3777b7d..284e1615 100644
--- a/pkg/codec/json/tests/schema.yaml
+++ b/pkg/codec/json/tests/schema.yaml
@@ -4,12 +4,14 @@ properties:
template:
message:
"message":
+ position: 1
name: "message"
label: "optional"
template:
scalar:
type: "string"
"nested":
+ position: 2
name: "nested"
label: "optional"
template:
@@ -22,6 +24,7 @@ properties:
type: "string"
"repeating":
+ position: 3
name: "repeating"
label: "optional"
template:
@@ -34,6 +37,7 @@ properties:
scalar:
type: "string"
"repeating_values":
+ position: 4
name: "repeating_values"
label: "optional"
template:
@@ -41,30 +45,32 @@ properties:
- scalar:
type: "string"
"enum":
- name: "enum"
- label: "optional"
- template:
- enum:
- name: "STATUS"
- keys:
- "UNKNOWN":
- key: "UNKNOWN"
- position: 1
- description: "unknown status"
- "PENDING":
- key: "PENDING"
- position: 2
- description: "pending status"
- positions:
- 1:
- key: "UNKNOWN"
- position: 1
- description: "unknown status"
- 2:
- key: "PENDING"
- position: 2
- description: "pending status"
+ position: 5
+ name: "enum"
+ label: "optional"
+ template:
+ enum:
+ name: "STATUS"
+ keys:
+ "UNKNOWN":
+ key: "UNKNOWN"
+ position: 1
+ description: "unknown status"
+ "PENDING":
+ key: "PENDING"
+ position: 2
+ description: "pending status"
+ positions:
+ 1:
+ key: "UNKNOWN"
+ position: 1
+ description: "unknown status"
+ 2:
+ key: "PENDING"
+ position: 2
+ description: "pending status"
"repeating_enum":
+ position: 6
name: "repeating_enum"
label: "optional"
template:
diff --git a/pkg/codec/json/types.go b/pkg/codec/json/types.go
index 44ac626f..536ca55b 100644
--- a/pkg/codec/json/types.go
+++ b/pkg/codec/json/types.go
@@ -51,6 +51,13 @@ func AddTypeKey(encoder *gojay.Encoder, key string, typed types.Type, value inte
// AddType encodes the given value into the given encoder
func AddType(encoder *gojay.Encoder, typed types.Type, value interface{}) {
+ // do not skip NULL values while encoding array elements
+ if value == nil {
+ encoder.AddNull()
+
+ return
+ }
+
switch typed {
case types.Double:
encoder.AddFloat64(Float64Empty(value))
@@ -132,7 +139,9 @@ func DecodeType(decoder *gojay.Decoder, prop types.Type) (interface{}, error) {
return value, err
case types.Bytes:
var raw string
- decoder.AddString(&raw)
+ if err := decoder.AddString(&raw); err != nil {
+ return nil, err
+ }
value := make([]byte, len(raw))
_, err := base64.StdEncoding.Decode(value, []byte(raw))
diff --git a/pkg/codec/tests/assert.go b/pkg/codec/tests/assert.go
new file mode 100644
index 00000000..fc4204f5
--- /dev/null
+++ b/pkg/codec/tests/assert.go
@@ -0,0 +1,69 @@
+package tests
+
+import (
+ "testing"
+
+ "github.com/jexia/semaphore/pkg/references"
+)
+
+type Expect struct {
+ Value interface{}
+ Enum *int32
+ Repeated []Expect
+ Nested map[string]Expect
+}
+
+func buildPath(prefix, property string) string {
+ if prefix == "" {
+ return property
+ }
+
+ return prefix + "." + property
+}
+
+func Assert(t *testing.T, resource, path string, store references.Store, input Expect) {
+ ref := store.Load(resource, path)
+
+ switch {
+ case input.Nested != nil:
+ for key, value := range input.Nested {
+ Assert(t, resource, buildPath(path, key), store, value)
+ }
+ case input.Enum != nil:
+ if ref == nil {
+ t.Fatalf("reference %q was expected to be set", path)
+ }
+
+ if ref.Enum == nil {
+ t.Fatalf("reference %q was expected to have a enum value", path)
+ }
+
+ if *input.Enum != *ref.Enum {
+ t.Errorf("reference %q was expected to have enum value [%d], not [%d]", path, *input.Enum, *ref.Enum)
+ }
+ case input.Value != nil:
+ if ref == nil {
+ t.Fatalf("reference %q was expected to be set", path)
+ }
+
+ if ref.Value != input.Value {
+ t.Errorf("reference %q was expected to be %T(%v), got %T(%v)", path, input.Value, input.Value, ref.Value, ref.Value)
+ }
+ case input.Repeated != nil:
+ if ref == nil {
+ t.Fatalf("reference %q was expected to be set", path)
+ }
+
+ if ref.Repeated == nil {
+ t.Fatalf("reference %q was expected to have a repeated value", path)
+ }
+
+ if expected, actual := len(input.Repeated), len(ref.Repeated); actual != expected {
+ t.Fatalf("invalid number of repeated values, expected %d, got %d", expected, actual)
+ }
+
+ for index, expected := range input.Repeated {
+ Assert(t, "", "", ref.Repeated[index], expected)
+ }
+ }
+}
diff --git a/pkg/codec/xml/schema_test.go b/pkg/codec/tests/schema.go
similarity index 89%
rename from pkg/codec/xml/schema_test.go
rename to pkg/codec/tests/schema.go
index f646d9ea..deb299c9 100644
--- a/pkg/codec/xml/schema_test.go
+++ b/pkg/codec/tests/schema.go
@@ -1,4 +1,4 @@
-package xml
+package tests
import (
"github.com/jexia/semaphore/pkg/specs"
@@ -7,7 +7,7 @@ import (
"github.com/jexia/semaphore/pkg/specs/types"
)
-func propString() *specs.Property {
+func PropString() *specs.Property {
return &specs.Property{
Name: "string",
Path: "string",
@@ -20,7 +20,7 @@ func propString() *specs.Property {
}
}
-func propInteger() *specs.Property {
+func PropInteger() *specs.Property {
return &specs.Property{
Name: "integer",
Path: "integer",
@@ -33,20 +33,20 @@ func propInteger() *specs.Property {
}
}
-func propArray() *specs.Property {
+func PropArray() *specs.Property {
return &specs.Property{
Name: "array",
Path: "array",
Label: labels.Optional,
Template: specs.Template{
Repeated: specs.Repeated{
- propString().Template,
+ PropString().Template,
},
},
}
}
-func propEnum() *specs.Property {
+func PropEnum() *specs.Property {
return &specs.Property{
Name: "status",
Path: "status",
@@ -90,7 +90,8 @@ var (
Repeated: specs.Repeated{
{
Scalar: &specs.Scalar{
- Type: types.String,
+ Type: types.String,
+ Default: nil,
},
},
},
@@ -132,14 +133,14 @@ var (
Template: specs.Template{
Message: specs.Message{
"integer": func() *specs.Property {
- var clone = propInteger()
+ var clone = PropInteger()
clone.Position = 1
clone.Path = "root." + clone.Path
return clone
}(),
"array": func() *specs.Property {
- var clone = propArray()
+ var clone = PropArray()
clone.Position = 2
clone.Path = "root." + clone.Path
@@ -150,6 +151,37 @@ var (
},
}
+ SchemaArrayOfArrays = &specs.ParameterMap{
+ Property: &specs.Property{
+ Name: "array",
+ Path: "array",
+ Label: labels.Optional,
+ Template: specs.Template{
+ Repeated: specs.Repeated{
+ {
+ Repeated: specs.Repeated{
+ {
+ Reference: &specs.PropertyReference{
+ Resource: template.InputResource,
+ Path: "string",
+ },
+ Scalar: &specs.Scalar{
+ Type: types.String,
+ },
+ },
+ {
+ Scalar: &specs.Scalar{
+ Type: types.String,
+ Default: "bar",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+
SchemaObject = &specs.ParameterMap{
Property: &specs.Property{
Name: "root",
@@ -157,14 +189,14 @@ var (
Template: specs.Template{
Message: specs.Message{
"status": func() *specs.Property {
- var clone = propEnum()
+ var clone = PropEnum()
clone.Position = 1
clone.Path = "root." + clone.Path
return clone
}(),
"integer": func() *specs.Property {
- var clone = propInteger()
+ var clone = PropInteger()
clone.Position = 2
clone.Path = "root." + clone.Path
@@ -189,14 +221,14 @@ var (
Template: specs.Template{
Message: specs.Message{
"status": func() *specs.Property {
- var clone = propEnum()
+ var clone = PropEnum()
clone.Position = 1
clone.Path = "root.nested." + clone.Path
return clone
}(),
"integer": func() *specs.Property {
- var clone = propInteger()
+ var clone = PropInteger()
clone.Position = 2
clone.Path = "root.nested." + clone.Path
@@ -206,7 +238,7 @@ var (
},
},
"string": func() *specs.Property {
- var clone = propString()
+ var clone = PropString()
clone.Position = 2
clone.Path = "root." + clone.Path
diff --git a/pkg/codec/xml/xml.go b/pkg/codec/xml/xml.go
index d89fb5d6..2447c024 100644
--- a/pkg/codec/xml/xml.go
+++ b/pkg/codec/xml/xml.go
@@ -65,13 +65,13 @@ func (manager *Manager) Marshal(refs references.Store) (io.Reader, error) {
manager.property.Template,
refs,
); err != nil {
- writer.CloseWithError(err)
+ _ = writer.CloseWithError(err)
return
}
if err := encoder.Flush(); err != nil {
- writer.CloseWithError(err)
+ _ = writer.CloseWithError(err)
return
}
diff --git a/pkg/codec/xml/xml_test.go b/pkg/codec/xml/xml_test.go
index e08940ed..e488a960 100644
--- a/pkg/codec/xml/xml_test.go
+++ b/pkg/codec/xml/xml_test.go
@@ -8,6 +8,7 @@ import (
"strings"
"testing"
+ "github.com/jexia/semaphore/pkg/codec/tests"
"github.com/jexia/semaphore/pkg/references"
"github.com/jexia/semaphore/pkg/specs"
"github.com/jexia/semaphore/pkg/specs/template"
@@ -29,7 +30,7 @@ func TestName(t *testing.T) {
}
})
- manager, err := xml.New("mock", SchemaObject)
+ manager, err := xml.New("mock", tests.SchemaObject)
if err != nil {
t.Fatal(err)
}
@@ -55,20 +56,20 @@ func TestMarshal(t *testing.T) {
var tests = map[string]test{
"array empty": {
- schema: SchemaArrayDefaultEmpty,
+ schema: tests.SchemaArrayDefaultEmpty,
},
"array default reference": {
input: map[string]interface{}{
"string": "foo",
},
- schema: SchemaArrayWithValues,
+ schema: tests.SchemaArrayWithValues,
expected: "foobar",
},
"simple": {
input: map[string]interface{}{
"message": "hello world",
},
- schema: SchemaObjectComplex,
+ schema: tests.SchemaObjectComplex,
expected: "hello world",
},
"enum": {
@@ -76,7 +77,7 @@ func TestMarshal(t *testing.T) {
"nested": map[string]interface{}{},
"status": references.Enum("PENDING", 1),
},
- schema: SchemaObjectComplex,
+ schema: tests.SchemaObjectComplex,
expected: "PENDING",
},
"nested": {
@@ -86,7 +87,7 @@ func TestMarshal(t *testing.T) {
"second": "bar",
},
},
- schema: SchemaObjectComplex,
+ schema: tests.SchemaObjectComplex,
expected: "foobar",
},
"repeating string": {
@@ -97,7 +98,7 @@ func TestMarshal(t *testing.T) {
nil, // TODO: nil (null) values should not be ignored
},
},
- schema: SchemaObjectComplex,
+ schema: tests.SchemaObjectComplex,
expected: "repeating onerepeating two",
},
"repeating enum": {
@@ -107,7 +108,7 @@ func TestMarshal(t *testing.T) {
references.Enum("PENDING", 1),
},
},
- schema: SchemaObjectComplex,
+ schema: tests.SchemaObjectComplex,
expected: "UNKNOWNPENDING",
},
"repeating nested": {
@@ -121,7 +122,7 @@ func TestMarshal(t *testing.T) {
},
},
},
- schema: SchemaObjectComplex,
+ schema: tests.SchemaObjectComplex,
expected: "repeating onerepeating two",
},
"complex": {
@@ -141,7 +142,7 @@ func TestMarshal(t *testing.T) {
},
},
},
- schema: SchemaObjectComplex,
+ schema: tests.SchemaObjectComplex,
expected: "42hello worldfoobarrepeating onerepeating two",
},
}
@@ -173,10 +174,6 @@ func TestMarshal(t *testing.T) {
}
}
-type readerFunc func([]byte) (int, error)
-
-func (fn readerFunc) Read(p []byte) (int, error) { return fn(p) }
-
func errorString(err error) string {
if err != nil {
return err.Error()
@@ -189,17 +186,17 @@ func TestUnmarshal(t *testing.T) {
type test struct {
input io.Reader
schema *specs.ParameterMap
- expected expect
+ expected tests.Expect
error error
}
- tests := map[string]test{
+ testCases := map[string]test{
"empty scalar with unexpected element": {
input: strings.NewReader(
"",
),
schema: &specs.ParameterMap{
- Property: propInteger(),
+ Property: tests.PropInteger(),
},
error: errFailedToDecode{
errStack{
@@ -219,7 +216,7 @@ func TestUnmarshal(t *testing.T) {
"42",
),
schema: &specs.ParameterMap{
- Property: propInteger(),
+ Property: tests.PropInteger(),
},
error: errFailedToDecode{
errStack{
@@ -238,7 +235,7 @@ func TestUnmarshal(t *testing.T) {
"foo",
),
schema: &specs.ParameterMap{
- Property: propInteger(),
+ Property: tests.PropInteger(),
},
error: errFailedToDecode{
errStack{
@@ -252,12 +249,12 @@ func TestUnmarshal(t *testing.T) {
"",
),
schema: &specs.ParameterMap{
- Property: propInteger(),
+ Property: tests.PropInteger(),
},
- expected: expect{
- nested: map[string]expect{
+ expected: tests.Expect{
+ Nested: map[string]tests.Expect{
"integer": {
- value: nil,
+ Value: nil,
},
},
},
@@ -267,12 +264,12 @@ func TestUnmarshal(t *testing.T) {
"42",
),
schema: &specs.ParameterMap{
- Property: propInteger(),
+ Property: tests.PropInteger(),
},
- expected: expect{
- nested: map[string]expect{
+ expected: tests.Expect{
+ Nested: map[string]tests.Expect{
"integer": {
- value: int32(42),
+ Value: int32(42),
},
},
},
@@ -282,7 +279,7 @@ func TestUnmarshal(t *testing.T) {
"",
),
schema: &specs.ParameterMap{
- Property: propEnum(),
+ Property: tests.PropEnum(),
},
error: errFailedToDecode{
errStack{
@@ -302,7 +299,7 @@ func TestUnmarshal(t *testing.T) {
"UNKNOWN",
),
schema: &specs.ParameterMap{
- Property: propEnum(),
+ Property: tests.PropEnum(),
},
error: errFailedToDecode{
errStack{
@@ -321,7 +318,7 @@ func TestUnmarshal(t *testing.T) {
"foo",
),
schema: &specs.ParameterMap{
- Property: propEnum(),
+ Property: tests.PropEnum(),
},
error: errFailedToDecode{
errStack{
@@ -335,7 +332,7 @@ func TestUnmarshal(t *testing.T) {
"",
),
schema: &specs.ParameterMap{
- Property: propEnum(),
+ Property: tests.PropEnum(),
},
},
"enum": {
@@ -343,12 +340,12 @@ func TestUnmarshal(t *testing.T) {
"PENDING",
),
schema: &specs.ParameterMap{
- Property: propEnum(),
+ Property: tests.PropEnum(),
},
- expected: expect{
- nested: map[string]expect{
+ expected: tests.Expect{
+ Nested: map[string]tests.Expect{
"status": {
- enum: func() *int32 { i := int32(1); return &i }(),
+ Enum: func() *int32 { i := int32(1); return &i }(),
},
},
},
@@ -360,14 +357,14 @@ func TestUnmarshal(t *testing.T) {
42
`,
),
- schema: SchemaObject,
- expected: expect{
- nested: map[string]expect{
+ schema: tests.SchemaObject,
+ expected: tests.Expect{
+ Nested: map[string]tests.Expect{
"root.status": {
- enum: func() *int32 { i := int32(1); return &i }(),
+ Enum: func() *int32 { i := int32(1); return &i }(),
},
"root.integer": {
- value: int32(42),
+ Value: int32(42),
},
},
},
@@ -382,7 +379,7 @@ func TestUnmarshal(t *testing.T) {
foobar
`,
),
- schema: SchemaObjectNested,
+ schema: tests.SchemaObjectNested,
error: errFailedToDecode{
errStack: errStack{
property: "root",
@@ -410,17 +407,17 @@ func TestUnmarshal(t *testing.T) {
foobar
`,
),
- schema: SchemaObjectNested,
- expected: expect{
- nested: map[string]expect{
+ schema: tests.SchemaObjectNested,
+ expected: tests.Expect{
+ Nested: map[string]tests.Expect{
"root.nested.status": {
- enum: func() *int32 { i := int32(1); return &i }(),
+ Enum: func() *int32 { i := int32(1); return &i }(),
},
"root.nested.integer": {
- value: int32(42),
+ Value: int32(42),
},
"root.string": {
- value: "foobar",
+ Value: "foobar",
},
},
},
@@ -432,20 +429,20 @@ func TestUnmarshal(t *testing.T) {
bar`,
),
schema: &specs.ParameterMap{
- Property: propArray(),
+ Property: tests.PropArray(),
},
- expected: expect{
- nested: map[string]expect{
+ expected: tests.Expect{
+ Nested: map[string]tests.Expect{
"array": {
- repeated: []expect{
+ Repeated: []tests.Expect{
{
- value: "foo",
+ Value: "foo",
},
{
- value: nil,
+ Value: nil,
},
{
- value: "bar",
+ Value: "bar",
},
},
},
@@ -461,22 +458,22 @@ func TestUnmarshal(t *testing.T) {
bar
`,
),
- schema: SchemaNestedArray,
- expected: expect{
- nested: map[string]expect{
+ schema: tests.SchemaNestedArray,
+ expected: tests.Expect{
+ Nested: map[string]tests.Expect{
"root.integer": {
- value: int32(42),
+ Value: int32(42),
},
"root.array": {
- repeated: []expect{
+ Repeated: []tests.Expect{
{
- value: "foo",
+ Value: "foo",
},
{
- value: nil,
+ Value: nil,
},
{
- value: "bar",
+ Value: "bar",
},
},
},
@@ -502,41 +499,41 @@ func TestUnmarshal(t *testing.T) {
`,
),
- schema: SchemaObjectComplex,
- expected: expect{
- nested: map[string]expect{
+ schema: tests.SchemaObjectComplex,
+ expected: tests.Expect{
+ Nested: map[string]tests.Expect{
"root.repeating_string": {
- repeated: []expect{
+ Repeated: []tests.Expect{
{
- value: "foo",
+ Value: "foo",
},
{
- value: "bar",
+ Value: "bar",
},
},
},
"root.message": {
- value: "hello world",
+ Value: "hello world",
},
"root.nested.first": {
- value: "foo",
+ Value: "foo",
},
"root.nested.second": {
- value: "bar",
+ Value: "bar",
},
"root.repeating": {
- repeated: []expect{
+ Repeated: []tests.Expect{
{
- nested: map[string]expect{
+ Nested: map[string]tests.Expect{
"value": {
- value: "repeating one",
+ Value: "repeating one",
},
},
},
{
- nested: map[string]expect{
+ Nested: map[string]tests.Expect{
"value": {
- value: "repeating two",
+ Value: "repeating two",
},
},
},
@@ -547,7 +544,7 @@ func TestUnmarshal(t *testing.T) {
},
}
- for title, test := range tests {
+ for title, test := range testCases {
t.Run(title, func(t *testing.T) {
xml := NewConstructor()
if xml == nil {
@@ -559,8 +556,8 @@ func TestUnmarshal(t *testing.T) {
t.Fatal(err)
}
- var refs = references.NewReferenceStore(0)
- err = manager.Unmarshal(test.input, refs)
+ var store = references.NewReferenceStore(0)
+ err = manager.Unmarshal(test.input, store)
if test.error != nil {
if err == nil {
@@ -577,49 +574,7 @@ func TestUnmarshal(t *testing.T) {
}
}
- assert(t, "mock", "", refs, test.expected)
+ tests.Assert(t, "mock", "", store, test.expected)
})
}
}
-
-type expect struct {
- value interface{}
- enum *int32
- repeated []expect
- nested map[string]expect
-}
-
-func assert(t *testing.T, resource, path string, store references.Store, input expect) {
- ref := store.Load(resource, path)
-
- switch {
- case input.nested != nil:
- for key, value := range input.nested {
- assert(t, resource, key, store, value)
- }
- case input.enum != nil:
- if ref == nil || ref.Enum == nil {
- t.Fatalf("reference %q was expected to have a enum value", path)
- }
-
- if *input.enum != *ref.Enum {
- t.Errorf("reference %q was expected to have enum value [%d], not [%d]", path, *input.enum, *ref.Enum)
- }
- case input.value != nil:
- if ref == nil || ref.Value != input.value {
- t.Errorf("reference %q was expected to be %T(%v), got %T(%v)", path, input.value, input.value, ref.Value, ref.Value)
- }
- case input.repeated != nil:
- if ref == nil || ref.Repeated == nil {
- t.Fatalf("reference %q was expected to have a repeated value", path)
- }
-
- if expected, actual := len(input.repeated), len(ref.Repeated); actual != expected {
- t.Fatalf("invalid number of repeated values, expected %d, got %d", expected, actual)
- }
-
- for index, expected := range input.repeated {
- assert(t, "", "", ref.Repeated[index], expected)
- }
- }
-}
diff --git a/vendor/github.com/francoispqt/gojay/encode.go b/vendor/github.com/francoispqt/gojay/encode.go
index 92edaafa..577c3c24 100644
--- a/vendor/github.com/francoispqt/gojay/encode.go
+++ b/vendor/github.com/francoispqt/gojay/encode.go
@@ -196,7 +196,11 @@ func (enc *Encoder) Write() (int, error) {
return i, err
}
-func (enc *Encoder) getPreviousRune() byte {
+func (enc *Encoder) getPreviousRune() (byte, bool) {
last := len(enc.buf) - 1
- return enc.buf[last]
+ if last < 0 {
+ return ' ', false
+ }
+
+ return enc.buf[last], true
}
diff --git a/vendor/github.com/francoispqt/gojay/encode_array.go b/vendor/github.com/francoispqt/gojay/encode_array.go
index 5e9d49e8..286bbd15 100644
--- a/vendor/github.com/francoispqt/gojay/encode_array.go
+++ b/vendor/github.com/francoispqt/gojay/encode_array.go
@@ -62,8 +62,8 @@ func (enc *Encoder) AddArrayKeyNullEmpty(key string, v MarshalerJSONArray) {
func (enc *Encoder) Array(v MarshalerJSONArray) {
if v.IsNil() {
enc.grow(3)
- r := enc.getPreviousRune()
- if r != '[' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '[' {
enc.writeByte(',')
}
enc.writeByte('[')
@@ -71,8 +71,8 @@ func (enc *Encoder) Array(v MarshalerJSONArray) {
return
}
enc.grow(100)
- r := enc.getPreviousRune()
- if r != '[' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '[' {
enc.writeByte(',')
}
enc.writeByte('[')
@@ -87,8 +87,8 @@ func (enc *Encoder) ArrayOmitEmpty(v MarshalerJSONArray) {
return
}
enc.grow(4)
- r := enc.getPreviousRune()
- if r != '[' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '[' {
enc.writeByte(',')
}
enc.writeByte('[')
@@ -100,8 +100,8 @@ func (enc *Encoder) ArrayOmitEmpty(v MarshalerJSONArray) {
// value must implement Marshaler
func (enc *Encoder) ArrayNullEmpty(v MarshalerJSONArray) {
enc.grow(4)
- r := enc.getPreviousRune()
- if r != '[' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '[' {
enc.writeByte(',')
}
if v.IsNil() {
@@ -123,8 +123,8 @@ func (enc *Encoder) ArrayKey(key string, v MarshalerJSONArray) {
}
if v.IsNil() {
enc.grow(2 + len(key))
- r := enc.getPreviousRune()
- if r != '{' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '{' {
enc.writeByte(',')
}
enc.writeByte('"')
@@ -134,8 +134,8 @@ func (enc *Encoder) ArrayKey(key string, v MarshalerJSONArray) {
return
}
enc.grow(5 + len(key))
- r := enc.getPreviousRune()
- if r != '{' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '{' {
enc.writeByte(',')
}
enc.writeByte('"')
@@ -157,8 +157,8 @@ func (enc *Encoder) ArrayKeyOmitEmpty(key string, v MarshalerJSONArray) {
return
}
enc.grow(5 + len(key))
- r := enc.getPreviousRune()
- if r != '{' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '{' {
enc.writeByte(',')
}
enc.writeByte('"')
@@ -177,8 +177,8 @@ func (enc *Encoder) ArrayKeyNullEmpty(key string, v MarshalerJSONArray) {
}
}
enc.grow(5 + len(key))
- r := enc.getPreviousRune()
- if r != '{' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '{' {
enc.writeByte(',')
}
if v.IsNil() {
diff --git a/vendor/github.com/francoispqt/gojay/encode_bool.go b/vendor/github.com/francoispqt/gojay/encode_bool.go
index 253e0378..60f647a4 100644
--- a/vendor/github.com/francoispqt/gojay/encode_bool.go
+++ b/vendor/github.com/francoispqt/gojay/encode_bool.go
@@ -62,8 +62,8 @@ func (enc *Encoder) AddBoolKeyNullEmpty(key string, v bool) {
// Bool adds a bool to be encoded, must be used inside a slice or array encoding (does not encode a key)
func (enc *Encoder) Bool(v bool) {
enc.grow(5)
- r := enc.getPreviousRune()
- if r != '[' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '[' {
enc.writeByte(',')
}
if v {
@@ -79,8 +79,8 @@ func (enc *Encoder) BoolOmitEmpty(v bool) {
return
}
enc.grow(5)
- r := enc.getPreviousRune()
- if r != '[' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '[' {
enc.writeByte(',')
}
enc.writeString("true")
@@ -89,8 +89,8 @@ func (enc *Encoder) BoolOmitEmpty(v bool) {
// BoolNullEmpty adds a bool to be encoded, must be used inside a slice or array encoding (does not encode a key)
func (enc *Encoder) BoolNullEmpty(v bool) {
enc.grow(5)
- r := enc.getPreviousRune()
- if r != '[' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '[' {
enc.writeByte(',')
}
if v == false {
@@ -108,8 +108,8 @@ func (enc *Encoder) BoolKey(key string, value bool) {
}
}
enc.grow(5 + len(key))
- r := enc.getPreviousRune()
- if r != '{' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '{' {
enc.writeByte(',')
}
enc.writeByte('"')
@@ -130,8 +130,8 @@ func (enc *Encoder) BoolKeyOmitEmpty(key string, v bool) {
return
}
enc.grow(5 + len(key))
- r := enc.getPreviousRune()
- if r != '{' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '{' {
enc.writeByte(',')
}
enc.writeByte('"')
@@ -149,8 +149,8 @@ func (enc *Encoder) BoolKeyNullEmpty(key string, v bool) {
}
}
enc.grow(5 + len(key))
- r := enc.getPreviousRune()
- if r != '{' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '{' {
enc.writeByte(',')
}
enc.writeByte('"')
diff --git a/vendor/github.com/francoispqt/gojay/encode_embedded_json.go b/vendor/github.com/francoispqt/gojay/encode_embedded_json.go
index 4c99a057..6cfee9fb 100644
--- a/vendor/github.com/francoispqt/gojay/encode_embedded_json.go
+++ b/vendor/github.com/francoispqt/gojay/encode_embedded_json.go
@@ -25,8 +25,8 @@ func (enc *Encoder) encodeEmbeddedJSON(v *EmbeddedJSON) ([]byte, error) {
// it expects the JSON to be of proper format.
func (enc *Encoder) AddEmbeddedJSON(v *EmbeddedJSON) {
enc.grow(len(*v) + 4)
- r := enc.getPreviousRune()
- if r != '[' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '[' {
enc.writeByte(',')
}
enc.writeBytes(*v)
@@ -40,8 +40,8 @@ func (enc *Encoder) AddEmbeddedJSONOmitEmpty(v *EmbeddedJSON) {
if v == nil || len(*v) == 0 {
return
}
- r := enc.getPreviousRune()
- if r != '[' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '[' {
enc.writeByte(',')
}
enc.writeBytes(*v)
@@ -58,8 +58,8 @@ func (enc *Encoder) AddEmbeddedJSONKey(key string, v *EmbeddedJSON) {
}
}
enc.grow(len(key) + len(*v) + 5)
- r := enc.getPreviousRune()
- if r != '{' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '{' {
enc.writeByte(',')
}
enc.writeByte('"')
@@ -82,8 +82,8 @@ func (enc *Encoder) AddEmbeddedJSONKeyOmitEmpty(key string, v *EmbeddedJSON) {
return
}
enc.grow(len(key) + len(*v) + 5)
- r := enc.getPreviousRune()
- if r != '{' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '{' {
enc.writeByte(',')
}
enc.writeByte('"')
diff --git a/vendor/github.com/francoispqt/gojay/encode_null.go b/vendor/github.com/francoispqt/gojay/encode_null.go
index cec4e639..ec3a6164 100644
--- a/vendor/github.com/francoispqt/gojay/encode_null.go
+++ b/vendor/github.com/francoispqt/gojay/encode_null.go
@@ -8,8 +8,8 @@ func (enc *Encoder) AddNull() {
// Null adds a `null` to be encoded. Must be used while encoding an array.`
func (enc *Encoder) Null() {
enc.grow(5)
- r := enc.getPreviousRune()
- if r != '[' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '[' {
enc.writeByte(',')
}
enc.writeBytes(nullBytes)
@@ -28,8 +28,8 @@ func (enc *Encoder) NullKey(key string) {
}
}
enc.grow(5 + len(key))
- r := enc.getPreviousRune()
- if r != '{' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '{' {
enc.writeByte(',')
}
enc.writeByte('"')
diff --git a/vendor/github.com/francoispqt/gojay/encode_number_float.go b/vendor/github.com/francoispqt/gojay/encode_number_float.go
index b45f8442..eff7a67c 100644
--- a/vendor/github.com/francoispqt/gojay/encode_number_float.go
+++ b/vendor/github.com/francoispqt/gojay/encode_number_float.go
@@ -121,8 +121,8 @@ func (enc *Encoder) AddFloat64OmitEmpty(v float64) {
// Float64 adds a float64 to be encoded, must be used inside a slice or array encoding (does not encode a key)
func (enc *Encoder) Float64(v float64) {
enc.grow(10)
- r := enc.getPreviousRune()
- if r != '[' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '[' {
enc.writeByte(',')
}
enc.buf = strconv.AppendFloat(enc.buf, v, 'f', -1, 64)
@@ -135,8 +135,8 @@ func (enc *Encoder) Float64OmitEmpty(v float64) {
return
}
enc.grow(10)
- r := enc.getPreviousRune()
- if r != '[' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '[' {
enc.writeByte(',')
}
enc.buf = strconv.AppendFloat(enc.buf, v, 'f', -1, 64)
@@ -146,8 +146,8 @@ func (enc *Encoder) Float64OmitEmpty(v float64) {
// must be used inside a slice or array encoding (does not encode a key).
func (enc *Encoder) Float64NullEmpty(v float64) {
enc.grow(10)
- r := enc.getPreviousRune()
- if r != '[' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '[' {
enc.writeByte(',')
}
if v == 0 {
@@ -175,8 +175,8 @@ func (enc *Encoder) Float64Key(key string, value float64) {
return
}
}
- r := enc.getPreviousRune()
- if r != '{' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '{' {
enc.writeByte(',')
}
enc.grow(10)
@@ -198,8 +198,8 @@ func (enc *Encoder) Float64KeyOmitEmpty(key string, v float64) {
return
}
enc.grow(10 + len(key))
- r := enc.getPreviousRune()
- if r != '{' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '{' {
enc.writeByte(',')
}
enc.writeByte('"')
@@ -217,8 +217,8 @@ func (enc *Encoder) Float64KeyNullEmpty(key string, v float64) {
}
}
enc.grow(10 + len(key))
- r := enc.getPreviousRune()
- if r != '{' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '{' {
enc.writeByte(',')
}
enc.writeByte('"')
@@ -250,8 +250,8 @@ func (enc *Encoder) AddFloat32NullEmpty(v float32) {
// Float32 adds a float32 to be encoded, must be used inside a slice or array encoding (does not encode a key)
func (enc *Encoder) Float32(v float32) {
- r := enc.getPreviousRune()
- if r != '[' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '[' {
enc.writeByte(',')
}
enc.buf = strconv.AppendFloat(enc.buf, float64(v), 'f', -1, 32)
@@ -264,8 +264,8 @@ func (enc *Encoder) Float32OmitEmpty(v float32) {
return
}
enc.grow(10)
- r := enc.getPreviousRune()
- if r != '[' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '[' {
enc.writeByte(',')
}
enc.buf = strconv.AppendFloat(enc.buf, float64(v), 'f', -1, 32)
@@ -275,8 +275,8 @@ func (enc *Encoder) Float32OmitEmpty(v float32) {
// must be used inside a slice or array encoding (does not encode a key).
func (enc *Encoder) Float32NullEmpty(v float32) {
enc.grow(10)
- r := enc.getPreviousRune()
- if r != '[' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '[' {
enc.writeByte(',')
}
if v == 0 {
@@ -311,8 +311,8 @@ func (enc *Encoder) Float32Key(key string, v float32) {
}
}
enc.grow(10 + len(key))
- r := enc.getPreviousRune()
- if r != '{' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '{' {
enc.writeByte(',')
}
enc.writeByte('"')
@@ -334,8 +334,8 @@ func (enc *Encoder) Float32KeyOmitEmpty(key string, v float32) {
return
}
enc.grow(10 + len(key))
- r := enc.getPreviousRune()
- if r != '{' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '{' {
enc.writeByte(',')
}
enc.writeByte('"')
@@ -353,8 +353,8 @@ func (enc *Encoder) Float32KeyNullEmpty(key string, v float32) {
}
}
enc.grow(10 + len(key))
- r := enc.getPreviousRune()
- if r != '{' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '{' {
enc.writeByte(',')
}
enc.writeByte('"')
diff --git a/vendor/github.com/francoispqt/gojay/encode_number_int.go b/vendor/github.com/francoispqt/gojay/encode_number_int.go
index 2c4bbe34..244a2121 100644
--- a/vendor/github.com/francoispqt/gojay/encode_number_int.go
+++ b/vendor/github.com/francoispqt/gojay/encode_number_int.go
@@ -60,8 +60,8 @@ func (enc *Encoder) AddIntNullEmpty(v int) {
// Int adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key)
func (enc *Encoder) Int(v int) {
enc.grow(10)
- r := enc.getPreviousRune()
- if r != '[' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '[' {
enc.writeByte(',')
}
enc.buf = strconv.AppendInt(enc.buf, int64(v), 10)
@@ -74,8 +74,8 @@ func (enc *Encoder) IntOmitEmpty(v int) {
return
}
enc.grow(10)
- r := enc.getPreviousRune()
- if r != '[' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '[' {
enc.writeByte(',')
}
enc.buf = strconv.AppendInt(enc.buf, int64(v), 10)
@@ -85,8 +85,8 @@ func (enc *Encoder) IntOmitEmpty(v int) {
// must be used inside a slice or array encoding (does not encode a key).
func (enc *Encoder) IntNullEmpty(v int) {
enc.grow(10)
- r := enc.getPreviousRune()
- if r != '[' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '[' {
enc.writeByte(',')
}
if v == 0 {
@@ -121,8 +121,8 @@ func (enc *Encoder) IntKey(key string, v int) {
}
}
enc.grow(10 + len(key))
- r := enc.getPreviousRune()
- if r != '{' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '{' {
enc.writeByte(',')
}
enc.writeByte('"')
@@ -143,8 +143,8 @@ func (enc *Encoder) IntKeyOmitEmpty(key string, v int) {
return
}
enc.grow(10 + len(key))
- r := enc.getPreviousRune()
- if r != '{' && r != '[' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '{' && r != '[' {
enc.writeByte(',')
}
enc.writeByte('"')
@@ -162,8 +162,8 @@ func (enc *Encoder) IntKeyNullEmpty(key string, v int) {
}
}
enc.grow(10 + len(key))
- r := enc.getPreviousRune()
- if r != '{' && r != '[' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '{' && r != '[' {
enc.writeByte(',')
}
enc.writeByte('"')
@@ -196,8 +196,8 @@ func (enc *Encoder) AddInt64NullEmpty(v int64) {
// Int64 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key)
func (enc *Encoder) Int64(v int64) {
enc.grow(10)
- r := enc.getPreviousRune()
- if r != '[' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '[' {
enc.writeByte(',')
}
enc.buf = strconv.AppendInt(enc.buf, v, 10)
@@ -210,8 +210,8 @@ func (enc *Encoder) Int64OmitEmpty(v int64) {
return
}
enc.grow(10)
- r := enc.getPreviousRune()
- if r != '[' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '[' {
enc.writeByte(',')
}
enc.buf = strconv.AppendInt(enc.buf, v, 10)
@@ -221,8 +221,8 @@ func (enc *Encoder) Int64OmitEmpty(v int64) {
// must be used inside a slice or array encoding (does not encode a key).
func (enc *Encoder) Int64NullEmpty(v int64) {
enc.grow(10)
- r := enc.getPreviousRune()
- if r != '[' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '[' {
enc.writeByte(',')
}
if v == 0 {
@@ -257,8 +257,8 @@ func (enc *Encoder) Int64Key(key string, v int64) {
}
}
enc.grow(10 + len(key))
- r := enc.getPreviousRune()
- if r != '{' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '{' {
enc.writeByte(',')
}
enc.writeByte('"')
@@ -274,8 +274,8 @@ func (enc *Encoder) Int64KeyOmitEmpty(key string, v int64) {
return
}
enc.grow(10 + len(key))
- r := enc.getPreviousRune()
- if r != '{' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '{' {
enc.writeByte(',')
}
enc.writeByte('"')
@@ -293,8 +293,8 @@ func (enc *Encoder) Int64KeyNullEmpty(key string, v int64) {
}
}
enc.grow(10 + len(key))
- r := enc.getPreviousRune()
- if r != '{' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '{' {
enc.writeByte(',')
}
enc.writeByte('"')
diff --git a/vendor/github.com/francoispqt/gojay/encode_number_uint.go b/vendor/github.com/francoispqt/gojay/encode_number_uint.go
index cd69b13f..1e2b94f5 100644
--- a/vendor/github.com/francoispqt/gojay/encode_number_uint.go
+++ b/vendor/github.com/francoispqt/gojay/encode_number_uint.go
@@ -41,8 +41,8 @@ func (enc *Encoder) AddUint64NullEmpty(v uint64) {
// Uint64 adds an int to be encoded, must be used inside a slice or array encoding (does not encode a key)
func (enc *Encoder) Uint64(v uint64) {
enc.grow(10)
- r := enc.getPreviousRune()
- if r != '[' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '[' {
enc.writeByte(',')
}
enc.buf = strconv.AppendUint(enc.buf, v, 10)
@@ -55,8 +55,8 @@ func (enc *Encoder) Uint64OmitEmpty(v uint64) {
return
}
enc.grow(10)
- r := enc.getPreviousRune()
- if r != '[' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '[' {
enc.writeByte(',')
}
enc.buf = strconv.AppendUint(enc.buf, v, 10)
@@ -66,8 +66,8 @@ func (enc *Encoder) Uint64OmitEmpty(v uint64) {
// must be used inside a slice or array encoding (does not encode a key).
func (enc *Encoder) Uint64NullEmpty(v uint64) {
enc.grow(10)
- r := enc.getPreviousRune()
- if r != '[' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '[' {
enc.writeByte(',')
}
if v == 0 {
@@ -102,8 +102,8 @@ func (enc *Encoder) Uint64Key(key string, v uint64) {
}
}
enc.grow(10 + len(key))
- r := enc.getPreviousRune()
- if r != '{' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '{' {
enc.writeByte(',')
}
enc.writeByte('"')
@@ -124,8 +124,8 @@ func (enc *Encoder) Uint64KeyOmitEmpty(key string, v uint64) {
return
}
enc.grow(10 + len(key))
- r := enc.getPreviousRune()
- if r != '{' && r != '[' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '{' && r != '[' {
enc.writeByte(',')
}
enc.writeByte('"')
@@ -143,8 +143,8 @@ func (enc *Encoder) Uint64KeyNullEmpty(key string, v uint64) {
}
}
enc.grow(10 + len(key))
- r := enc.getPreviousRune()
- if r != '{' && r != '[' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '{' && r != '[' {
enc.writeByte(',')
}
enc.writeByte('"')
diff --git a/vendor/github.com/francoispqt/gojay/encode_object.go b/vendor/github.com/francoispqt/gojay/encode_object.go
index 5f2c8cf3..60dd7e38 100644
--- a/vendor/github.com/francoispqt/gojay/encode_object.go
+++ b/vendor/github.com/francoispqt/gojay/encode_object.go
@@ -102,8 +102,8 @@ func (enc *Encoder) AddObjectKeyNullEmpty(key string, v MarshalerJSONObject) {
func (enc *Encoder) Object(v MarshalerJSONObject) {
if v.IsNil() {
enc.grow(2)
- r := enc.getPreviousRune()
- if r != '{' && r != '[' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '{' && r != '[' {
enc.writeByte(',')
}
enc.writeByte('{')
@@ -111,8 +111,8 @@ func (enc *Encoder) Object(v MarshalerJSONObject) {
return
}
enc.grow(4)
- r := enc.getPreviousRune()
- if r != '[' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '[' {
enc.writeByte(',')
}
enc.writeByte('{')
@@ -135,8 +135,8 @@ func (enc *Encoder) Object(v MarshalerJSONObject) {
func (enc *Encoder) ObjectWithKeys(v MarshalerJSONObject, keys []string) {
if v.IsNil() {
enc.grow(2)
- r := enc.getPreviousRune()
- if r != '{' && r != '[' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '{' && r != '[' {
enc.writeByte(',')
}
enc.writeByte('{')
@@ -144,8 +144,8 @@ func (enc *Encoder) ObjectWithKeys(v MarshalerJSONObject, keys []string) {
return
}
enc.grow(4)
- r := enc.getPreviousRune()
- if r != '[' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '[' {
enc.writeByte(',')
}
enc.writeByte('{')
@@ -171,8 +171,8 @@ func (enc *Encoder) ObjectOmitEmpty(v MarshalerJSONObject) {
return
}
enc.grow(2)
- r := enc.getPreviousRune()
- if r != '[' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '[' {
enc.writeByte(',')
}
enc.writeByte('{')
@@ -195,8 +195,8 @@ func (enc *Encoder) ObjectOmitEmpty(v MarshalerJSONObject) {
// value must implement MarshalerJSONObject
func (enc *Encoder) ObjectNullEmpty(v MarshalerJSONObject) {
enc.grow(2)
- r := enc.getPreviousRune()
- if r != '[' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '[' {
enc.writeByte(',')
}
if v.IsNil() {
@@ -228,8 +228,8 @@ func (enc *Encoder) ObjectKey(key string, v MarshalerJSONObject) {
}
if v.IsNil() {
enc.grow(2 + len(key))
- r := enc.getPreviousRune()
- if r != '{' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '{' {
enc.writeByte(',')
}
enc.writeByte('"')
@@ -239,8 +239,8 @@ func (enc *Encoder) ObjectKey(key string, v MarshalerJSONObject) {
return
}
enc.grow(5 + len(key))
- r := enc.getPreviousRune()
- if r != '{' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '{' {
enc.writeByte(',')
}
enc.writeByte('"')
@@ -270,8 +270,8 @@ func (enc *Encoder) ObjectKeyWithKeys(key string, value MarshalerJSONObject, key
}
if value.IsNil() {
enc.grow(2 + len(key))
- r := enc.getPreviousRune()
- if r != '{' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '{' {
enc.writeByte(',')
}
enc.writeByte('"')
@@ -281,8 +281,8 @@ func (enc *Encoder) ObjectKeyWithKeys(key string, value MarshalerJSONObject, key
return
}
enc.grow(5 + len(key))
- r := enc.getPreviousRune()
- if r != '{' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '{' {
enc.writeByte(',')
}
enc.writeByte('"')
@@ -311,8 +311,8 @@ func (enc *Encoder) ObjectKeyOmitEmpty(key string, v MarshalerJSONObject) {
return
}
enc.grow(5 + len(key))
- r := enc.getPreviousRune()
- if r != '{' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '{' {
enc.writeByte(',')
}
enc.writeByte('"')
@@ -342,8 +342,8 @@ func (enc *Encoder) ObjectKeyNullEmpty(key string, v MarshalerJSONObject) {
}
}
enc.grow(5 + len(key))
- r := enc.getPreviousRune()
- if r != '{' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '{' {
enc.writeByte(',')
}
enc.writeByte('"')
diff --git a/vendor/github.com/francoispqt/gojay/encode_string.go b/vendor/github.com/francoispqt/gojay/encode_string.go
index 438c773f..c5882abe 100644
--- a/vendor/github.com/francoispqt/gojay/encode_string.go
+++ b/vendor/github.com/francoispqt/gojay/encode_string.go
@@ -67,8 +67,8 @@ func (enc *Encoder) AddStringKeyNullEmpty(key, v string) {
// String adds a string to be encoded, must be used inside a slice or array encoding (does not encode a key)
func (enc *Encoder) String(v string) {
enc.grow(len(v) + 4)
- r := enc.getPreviousRune()
- if r != '[' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '[' {
enc.writeTwoBytes(',', '"')
} else {
enc.writeByte('"')
@@ -83,8 +83,8 @@ func (enc *Encoder) StringOmitEmpty(v string) {
if v == "" {
return
}
- r := enc.getPreviousRune()
- if r != '[' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '[' {
enc.writeTwoBytes(',', '"')
} else {
enc.writeByte('"')
@@ -96,9 +96,9 @@ func (enc *Encoder) StringOmitEmpty(v string) {
// StringNullEmpty adds a string to be encoded or skips it if it is zero value.
// Must be used inside a slice or array encoding (does not encode a key)
func (enc *Encoder) StringNullEmpty(v string) {
- r := enc.getPreviousRune()
+ r, ok := enc.getPreviousRune()
if v == "" {
- if r != '[' {
+ if ok && r != '[' {
enc.writeByte(',')
enc.writeBytes(nullBytes)
} else {
@@ -123,8 +123,8 @@ func (enc *Encoder) StringKey(key, v string) {
}
}
enc.grow(len(key) + len(v) + 5)
- r := enc.getPreviousRune()
- if r != '{' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '{' {
enc.writeTwoBytes(',', '"')
} else {
enc.writeByte('"')
@@ -147,8 +147,8 @@ func (enc *Encoder) StringKeyOmitEmpty(key, v string) {
return
}
enc.grow(len(key) + len(v) + 5)
- r := enc.getPreviousRune()
- if r != '{' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '{' {
enc.writeTwoBytes(',', '"')
} else {
enc.writeByte('"')
@@ -168,8 +168,8 @@ func (enc *Encoder) StringKeyNullEmpty(key, v string) {
}
}
enc.grow(len(key) + len(v) + 5)
- r := enc.getPreviousRune()
- if r != '{' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '{' {
enc.writeTwoBytes(',', '"')
} else {
enc.writeByte('"')
diff --git a/vendor/github.com/francoispqt/gojay/encode_time.go b/vendor/github.com/francoispqt/gojay/encode_time.go
index 6f99e342..15e4446e 100644
--- a/vendor/github.com/francoispqt/gojay/encode_time.go
+++ b/vendor/github.com/francoispqt/gojay/encode_time.go
@@ -38,8 +38,8 @@ func (enc *Encoder) TimeKey(key string, t *time.Time, format string) {
}
}
enc.grow(10 + len(key))
- r := enc.getPreviousRune()
- if r != '{' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '{' {
enc.writeTwoBytes(',', '"')
} else {
enc.writeByte('"')
@@ -58,8 +58,8 @@ func (enc *Encoder) AddTime(t *time.Time, format string) {
// Time adds an *time.Time to be encoded with the given format, must be used inside a slice or array encoding (does not encode a key)
func (enc *Encoder) Time(t *time.Time, format string) {
enc.grow(10)
- r := enc.getPreviousRune()
- if r != '[' {
+ r, ok := enc.getPreviousRune()
+ if ok && r != '[' {
enc.writeByte(',')
}
enc.writeByte('"')