Skip to content

Commit

Permalink
Resolve merge conflicts with jszwedko-add-null-types but prefer Decim…
Browse files Browse the repository at this point in the history
…als to NullFloat
  • Loading branch information
lionelbarrow committed Apr 3, 2015
2 parents e13504d + 4ee818e commit 256acac
Show file tree
Hide file tree
Showing 9 changed files with 316 additions and 39 deletions.
14 changes: 7 additions & 7 deletions disbursement_details.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import (
)

type DisbursementDetails struct {
XMLName xml.Name `xml:"disbursement-details"`
DisbursementDate string `xml:"disbursement-date"`
SettlementAmount *Decimal `xml:"settlement-amount"`
SettlementCurrencyIsoCode string `xml:"settlement-currency-iso-code"`
SettlementCurrencyExchangeRate *Decimal `xml:"settlement-currency-exchange-rate"`
FundsHeld string `xml:"funds-held"` // bool
Success string `xml:"success"` // bool
XMLName xml.Name `xml:"disbursement-details"`
DisbursementDate string `xml:"disbursement-date"`
SettlementAmount *Decimal `xml:"settlement-amount"`
SettlementCurrencyIsoCode string `xml:"settlement-currency-iso-code"`
SettlementCurrencyExchangeRate *Decimal `xml:"settlement-currency-exchange-rate"`
FundsHeld *NullBool `xml:"funds-held"`
Success *NullBool `xml:"success"`
}
2 changes: 1 addition & 1 deletion disbursement_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func TestDisbursementTransactions(t *testing.T) {
t.Fatal(err)
}

if result.TotalItems != "1" {
if !result.TotalItems.Valid || result.TotalItems.Int64 != 1 {
t.Fatal(result)
}

Expand Down
135 changes: 135 additions & 0 deletions null_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package braintree

import (
"database/sql"
"strconv"
)

// NullInt64 wraps sql.NullInt64 to allow it to be serializable to/from XML
// via TextMarshaler and TextUnmarshaler
type NullInt64 struct {
sql.NullInt64
}

// NewNullInt64 creats a new NullInt64
func NewNullInt64(n int64, valid bool) NullInt64 {
return NullInt64{
sql.NullInt64{
Valid: valid,
Int64: n,
},
}
}

// UnmarshalText initializes an invalid NullInt64 if text is empty
// otherwise it tries to parse it as an integer in base 10
func (n *NullInt64) UnmarshalText(text []byte) (err error) {
if len(text) == 0 {
n.Valid = false
return nil
}

n.Int64, err = strconv.ParseInt(string(text), 10, 64)
if err != nil {
return err
}

n.Valid = true
return nil
}

// UnmarshalText initializes an invalid NullInt64 if text is empty
// otherwise it tries to parse it as an integer in base 10
// MarshalText returns "" for invalid NullInt64s, otherwise the integer value
func (n NullInt64) MarshalText() ([]byte, error) {
if !n.Valid {
return []byte{}, nil
}
return []byte(strconv.FormatInt(n.Int64, 10)), nil
}

// NullFloat64 wraps sql.NullFloat64 to allow it to be serializable to/from XML
// via TextMarshaler and TextUnmarshaler
type NullFloat64 struct {
sql.NullFloat64
}

// NewNullFloat64 creats a new NullFloat64
func NewNullFloat64(n float64, valid bool) NullFloat64 {
return NullFloat64{
sql.NullFloat64{
Valid: valid,
Float64: n,
},
}
}

// UnmarshalText initializes an invalid NullFloat64 if text is empty
// otherwise it tries to parse it as an integer in base 10
func (n *NullFloat64) UnmarshalText(text []byte) (err error) {
if len(text) == 0 {
n.Valid = false
return nil
}

n.Float64, err = strconv.ParseFloat(string(text), 64)
if err != nil {
return err
}

n.Valid = true
return nil
}

// UnmarshalText initializes an invalid NullFloat64 if text is empty
// otherwise it tries to parse it as an integer in base 10
// MarshalText returns "" for invalid NullFloat64s, otherwise the float string
func (n NullFloat64) MarshalText() ([]byte, error) {
if !n.Valid {
return []byte{}, nil
}
return []byte(strconv.FormatFloat(n.Float64, 'f', -1, 64)), nil
}

// NullBool wraps sql.NullBool to allow it to be serializable to/from XML
// via TextMarshaler and TextUnmarshaler
type NullBool struct {
sql.NullBool
}

// NewNullBool creats a new NullBool
func NewNullBool(b bool, valid bool) NullBool {
return NullBool{
sql.NullBool{
Valid: valid,
Bool: b,
},
}
}

// UnmarshalText initializes an invalid NullBool if text is empty
// otherwise it tries to parse it as a boolean
func (n *NullBool) UnmarshalText(text []byte) (err error) {
if len(text) == 0 {
n.Valid = false
return nil
}

n.Bool, err = strconv.ParseBool(string(text))
if err != nil {
return err
}

n.Valid = true
return nil
}

// UnmarshalText initializes an invalid NullBool if text is empty
// otherwise it tries to parse it as an integer in base 10
// MarshalText returns "" for invalid NullBools, otherwise the boolean value
func (n NullBool) MarshalText() ([]byte, error) {
if !n.Valid {
return []byte{}, nil
}
return []byte(strconv.FormatBool(n.Bool)), nil
}
153 changes: 153 additions & 0 deletions null_types_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
package braintree

import (
"bytes"
"testing"
)

func TestNullInt64UnmarshalText(t *testing.T) {
tests := []struct {
in []byte
out NullInt64
sholudError bool
}{
{[]byte(""), NewNullInt64(0, false), false},
{[]byte("10"), NewNullInt64(10, true), false},
{[]byte("abcd"), NewNullInt64(0, false), true},
}

for _, tt := range tests {
n := NullInt64{}
err := n.UnmarshalText(tt.in)

if tt.sholudError {
if err == nil {
t.Errorf("expected UnmarshalText(%q) => to error, but it did not", tt.in)
}
} else {
if err != nil {
t.Errorf("expected UnmarshalText(%q) => to not error, but it did with %s", tt.in, err)
}
}

if n != tt.out {
t.Errorf("UnmarshalText(%q) => %q, want %q", tt.in, n, tt.out)
}
}
}

func TestNullInt64MarshalText(t *testing.T) {
tests := []struct {
in NullInt64
out []byte
}{
{NewNullInt64(0, false), []byte("")},
{NewNullInt64(10, true), []byte("10")},
}

for _, tt := range tests {
b, err := tt.in.MarshalText()

if !bytes.Equal(b, tt.out) || err != nil {
t.Errorf("%q.MarshalText() => (%s, %s), want (%s, %s)", tt.in, b, err, tt.out, nil)
}
}
}

func TestNullFloat64UnmarshalText(t *testing.T) {
tests := []struct {
in []byte
out NullFloat64
sholudError bool
}{
{[]byte(""), NewNullFloat64(0, false), false},
{[]byte("10"), NewNullFloat64(10, true), false},
{[]byte("abcd"), NewNullFloat64(0, false), true},
}

for _, tt := range tests {
n := NullFloat64{}
err := n.UnmarshalText(tt.in)

if tt.sholudError {
if err == nil {
t.Errorf("expected UnmarshalText(%q) => to error, but it did not", tt.in)
}
} else {
if err != nil {
t.Errorf("expected UnmarshalText(%q) => to not error, but it did with %s", tt.in, err)
}
}

if n != tt.out {
t.Errorf("UnmarshalText(%q) => %q, want %q", tt.in, n, tt.out)
}
}
}

func TestNullFloat64MarshalText(t *testing.T) {
tests := []struct {
in NullFloat64
out []byte
}{
{NewNullFloat64(0, false), []byte("")},
{NewNullFloat64(10, true), []byte("10")},
}

for _, tt := range tests {
b, err := tt.in.MarshalText()

if !bytes.Equal(b, tt.out) || err != nil {
t.Errorf("%q.MarshalText() => (%s, %s), want (%s, %s)", tt.in, b, err, tt.out, nil)
}
}
}

func TestNullBoolUnmarshalText(t *testing.T) {
tests := []struct {
in []byte
out NullBool
sholudError bool
}{
{[]byte(""), NewNullBool(false, false), false},
{[]byte("true"), NewNullBool(true, true), false},
{[]byte("abcd"), NewNullBool(false, false), true},
}

for _, tt := range tests {
n := NullBool{}
err := n.UnmarshalText(tt.in)

if tt.sholudError {
if err == nil {
t.Errorf("expected UnmarshalText(%q) => to error, but it did not", tt.in)
}
} else {
if err != nil {
t.Errorf("expected UnmarshalText(%q) => to not error, but it did with %s", tt.in, err)
}
}

if n != tt.out {
t.Errorf("UnmarshalText(%q) => %q, want %q", tt.in, n, tt.out)
}
}
}

func TestNullBoolMarshalText(t *testing.T) {
tests := []struct {
in NullBool
out []byte
}{
{NewNullBool(false, false), []byte("")},
{NewNullBool(true, true), []byte("true")},
}

for _, tt := range tests {
b, err := tt.in.MarshalText()

if !bytes.Equal(b, tt.out) || err != nil {
t.Errorf("%q.MarshalText() => (%s, %s), want (%s, %s)", tt.in, b, err, tt.out, nil)
}
}
}
16 changes: 5 additions & 11 deletions plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,20 @@ type Plan struct {
XMLName string `xml:"plan"`
Id string `xml:"id"`
MerchantId string `xml:"merchant-id"`
BillingDayOfMonth string `xml:"billing-day-of-month"` // int
BillingFrequency string `xml:"billing-frequency"` // int
BillingDayOfMonth *NullInt64 `xml:"billing-day-of-month"`
BillingFrequency *NullInt64 `xml:"billing-frequency"`
CurrencyISOCode string `xml:"currency-iso-code"`
Description string `xml:"description"`
Name string `xml:"name"`
NumberOfBillingCycles string `xml:"number-of-billing-cycles"` // int
NumberOfBillingCycles *NullInt64 `xml:"number-of-billing-cycles"`
Price *Decimal `xml:"price"`
TrialDuration string `xml:"trial-duration"` // int
TrialDuration *NullInt64 `xml:"trial-duration"`
TrialDurationUnit string `xml:"trial-duration-unit"`
TrialPeriod string `xml:"trial-period"` // bool
TrialPeriod *NullBool `xml:"trial-period"`
CreatedAt *time.Time `xml:"created-at"`
UpdatedAt *time.Time `xml:"updated-at"`
}

// TODO(eaigner): it is suboptimal that we use string instead of int/bool types here,
// but I see no way around this atm to avoid integer parse errors if the field is empty.
//
// If there is a better method, and it can be unmarshalled directly to the correct type
// without errors, this needs to be changed.

type Plans struct {
XMLName string `xml:"plans"`
Plan []*Plan `xml:"plan"`
Expand Down
11 changes: 4 additions & 7 deletions plan_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func TestPlan(t *testing.T) {
if x := plan.MerchantId; x == "" {
t.Fatal(x)
}
if x := plan.BillingFrequency; x != "1" {
if x := plan.BillingFrequency; !x.Valid || x.Int64 != 1 {
t.Fatal(x)
}
if x := plan.CurrencyISOCode; x != "USD" {
Expand All @@ -46,19 +46,19 @@ func TestPlan(t *testing.T) {
if x := plan.Name; x != "test_plan_name" {
t.Fatal(x)
}
if x := plan.NumberOfBillingCycles; x != "2" {
if x := plan.NumberOfBillingCycles; !x.Valid || x.Int64 != 2 {
t.Fatal(x)
}
if x := plan.Price; x.Cmp(NewDecimal(1000, 2)) != 0 {
t.Fatal(x)
}
if x := plan.TrialDuration; x != "14" {
if x := plan.TrialDuration; !x.Valid || x.Int64 != 14 {
t.Fatal(x)
}
if x := plan.TrialDurationUnit; x != "day" {
t.Fatal(x)
}
if x := plan.TrialPeriod; x != "true" {
if x := plan.TrialPeriod; !x.Valid || !x.Bool {
t.Fatal(x)
}
if x := plan.CreatedAt; x == nil {
Expand All @@ -76,7 +76,4 @@ func TestPlan(t *testing.T) {
if plan2.Id != "test_plan_2" {
t.Fatal(plan2)
}
if x := plan2.BillingDayOfMonth; x != "31" {
t.Fatal(x)
}
}
Loading

0 comments on commit 256acac

Please sign in to comment.