Skip to content
Snippets Groups Projects
Commit d6318fbb authored by Jono Wenger's avatar Jono Wenger
Browse files

Add support for json.Marshal and json.Unmarshal to id.ID

parent 53405cf1
Branches
Tags
2 merge requests!24Release,!20Add support for json.Marshal and json.Unmarshal to id.ID
...@@ -15,6 +15,7 @@ import ( ...@@ -15,6 +15,7 @@ import (
"encoding/base64" "encoding/base64"
"encoding/binary" "encoding/binary"
"encoding/hex" "encoding/hex"
"encoding/json"
"github.com/pkg/errors" "github.com/pkg/errors"
jww "github.com/spf13/jwalterweatherman" jww "github.com/spf13/jwalterweatherman"
"io" "io"
...@@ -112,6 +113,30 @@ func (id *ID) SetType(idType Type) { ...@@ -112,6 +113,30 @@ func (id *ID) SetType(idType Type) {
id[ArrIDLen-1] = byte(idType) id[ArrIDLen-1] = byte(idType)
} }
// UnmarshalJSON is part of the json.Unmarshaler interface and allows IDs to be
// marshaled into JSON.
func (id *ID) UnmarshalJSON(b []byte) error {
var buff []byte
if err := json.Unmarshal(b, &buff); err != nil {
return err
}
newID, err := Unmarshal(buff)
if err != nil {
return err
}
*id = *newID
return nil
}
// MarshalJSON is part of the json.Marshaler interface and allows IDs to be
// marshaled into JSON.
func (id ID) MarshalJSON() ([]byte, error) {
return json.Marshal(id.Marshal())
}
// NewRandomID generates a random ID using the passed in io.Reader r // NewRandomID generates a random ID using the passed in io.Reader r
// and sets the ID to Type t. If the base64 string of the generated // and sets the ID to Type t. If the base64 string of the generated
// ID does not begin with an alphanumeric character, then another ID // ID does not begin with an alphanumeric character, then another ID
......
...@@ -10,6 +10,7 @@ import ( ...@@ -10,6 +10,7 @@ import (
"bytes" "bytes"
"encoding/base64" "encoding/base64"
"encoding/binary" "encoding/binary"
"encoding/json"
"fmt" "fmt"
"math/rand" "math/rand"
"reflect" "reflect"
...@@ -301,6 +302,50 @@ func TestID_SetType_NilError(t *testing.T) { ...@@ -301,6 +302,50 @@ func TestID_SetType_NilError(t *testing.T) {
id.SetType(Generic) id.SetType(Generic)
} }
// Tests that an ID can be JSON marshaled and unmarshalled.
func TestID_MarshalJSON_UnmarshalJSON(t *testing.T) {
testID := NewIdFromBytes(rngBytes(ArrIDLen, 42, t), t)
jsonData, err := json.Marshal(testID)
if err != nil {
t.Errorf("json.Marshal returned an error: %+v", err)
}
newID := &ID{}
err = json.Unmarshal(jsonData, newID)
if err != nil {
t.Errorf("json.Unmarshal returned an error: %+v", err)
}
if *testID != *newID {
t.Errorf("Failed the JSON marshal and unmarshal ID."+
"\noriginal ID: %s\nreceived ID: %s", testID, newID)
}
}
// Error path: supplied data is invalid JSON.
func TestID_UnmarshalJSON_JsonUnmarshalError(t *testing.T) {
expectedErr := "invalid character"
id := ID{}
err := id.UnmarshalJSON([]byte("invalid JSON"))
if err == nil || !strings.Contains(err.Error(), expectedErr) {
t.Errorf("UnmarshalJSON failed to return the expected error for "+
"invalid JSON.\nexpected: %s\nreceived: %+v", expectedErr, err)
}
}
// Error path: supplied data is valid JSON but an invalid ID.
func TestID_UnmarshalJSON_IdUnmarshalError(t *testing.T) {
expectedErr := fmt.Sprintf("Failed to unmarshal ID: length of data "+
"must be %d, length received is %d", ArrIDLen, 0)
id := ID{}
err := id.UnmarshalJSON([]byte("\"\""))
if err == nil || !strings.Contains(err.Error(), expectedErr) {
t.Errorf("UnmarshalJSON failed to return the expected error for "+
"invalid ID.\nexpected: %s\nreceived: %+v", expectedErr, err)
}
}
// Tests that NewRandomID returns the expected IDs for a given PRNG. // Tests that NewRandomID returns the expected IDs for a given PRNG.
func TestNewRandomID_Consistency(t *testing.T) { func TestNewRandomID_Consistency(t *testing.T) {
prng := rand.New(rand.NewSource(42)) prng := rand.New(rand.NewSource(42))
...@@ -599,3 +644,19 @@ func (prng *alphaNumericPRNG) Read(p []byte) (n int, err error) { ...@@ -599,3 +644,19 @@ func (prng *alphaNumericPRNG) Read(p []byte) (n int, err error) {
return len(p), nil return len(p), nil
} }
} }
// Generates a byte slice of the specified length containing random numbers.
func rngBytes(length int, seed int64, t *testing.T) []byte {
prng := rand.New(rand.NewSource(seed))
// Create new byte slice of the correct size
idBytes := make([]byte, length)
// Create random bytes
_, err := prng.Read(idBytes)
if err != nil {
t.Fatalf("Failed to generate random bytes: %+v", err)
}
return idBytes
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment