diff --git a/glide.lock b/glide.lock index 59151e32a..1b76bd258 100644 --- a/glide.lock +++ b/glide.lock @@ -333,4 +333,6 @@ imports: version: 279d72ee259701e0e0e58d98a52a82ba172a2f5f subpackages: - internal/testconn +- name: github.com/nyaruka/phonenumbers + version: 6003ee416f90f658f24b7485e6e6af8652be042f testImports: [] diff --git a/glide.yaml b/glide.yaml index 4b60ea54d..765a34a05 100644 --- a/glide.yaml +++ b/glide.yaml @@ -67,3 +67,4 @@ import: version: ^0.7.0 subpackages: - sheets/v4 +- package: github.com/nyaruka/phonenumbers diff --git a/utils/consts.go b/utils/consts.go index 5a187b7b5..2b0c053e7 100755 --- a/utils/consts.go +++ b/utils/consts.go @@ -583,6 +583,7 @@ const ( MetaTenant = "*tenant" ResourceUsage = "ResourceUsage" MetaDuration = "*duration" + MetaPhoneNumber = "*phone_number" MetaReload = "*reload" MetaLoad = "*load" MetaRemove = "*remove" diff --git a/utils/dataconverter.go b/utils/dataconverter.go index 2bd716896..7dbee24e5 100644 --- a/utils/dataconverter.go +++ b/utils/dataconverter.go @@ -23,6 +23,8 @@ import ( "strconv" "strings" "time" + + "github.com/nyaruka/phonenumbers" ) // DataConverters groups together multiple converters, @@ -70,6 +72,8 @@ func NewDataConverter(params string) ( return NewDivideConverter(params[len(MetaDivide)+1:]) case params == MetaDuration: return NewDurationConverter("") + case strings.HasPrefix(params, MetaPhoneNumber): + return NewPhoneNumberConverter(params[len(MetaPhoneNumber)+1:]) default: return nil, fmt.Errorf("unsupported converter definition: <%s>", @@ -230,3 +234,47 @@ func (mS *DurationConverter) Convert(in interface{}) ( out interface{}, err error) { return IfaceAsDuration(in) } + +// NewPhoneNumberConverter create a new phoneNumber converter +// If the format isn't specify by default we use NATIONAL +// Possible fromats are : E164(0) , INTERNATIONAL(1) , NATIONAL(2) ,RFC3966(3) +// Also ContryCode needs to be specified +func NewPhoneNumberConverter(params string) ( + pbDC DataConverter, err error) { + lc := new(PhoneNumberConverter) + var paramsSplt []string + if params != "" { + paramsSplt = strings.Split(params, InInFieldSep) + } + switch len(paramsSplt) { + case 2: + lc.CountryCode = paramsSplt[0] + frm, err := strconv.Atoi(paramsSplt[1]) + if err != nil { + return nil, err + } + lc.Format = phonenumbers.PhoneNumberFormat(frm) + case 1: + lc.CountryCode = paramsSplt[0] + lc.Format = 2 + default: + return nil, fmt.Errorf("unsupported %s converter parameters: <%s>", + MetaPhoneNumber, params) + } + return lc, nil +} + +// PhoneNumberConverter converts +type PhoneNumberConverter struct { + CountryCode string + Format phonenumbers.PhoneNumberFormat +} + +func (lc *PhoneNumberConverter) Convert(in interface{}) ( + out interface{}, err error) { + num, err := phonenumbers.Parse(IfaceAsString(in), lc.CountryCode) + if err != nil { + return nil, err + } + return phonenumbers.Format(num, lc.Format), nil +} diff --git a/utils/dataconverter_test.go b/utils/dataconverter_test.go index 601b27686..c91c70eb4 100644 --- a/utils/dataconverter_test.go +++ b/utils/dataconverter_test.go @@ -21,6 +21,8 @@ import ( "reflect" "testing" "time" + + "github.com/nyaruka/phonenumbers" ) func TestNewDataConverter(t *testing.T) { @@ -226,3 +228,73 @@ func TestDurationConverter(t *testing.T) { t.Errorf("expecting: %d, received: %d", expVal, i) } } + +func TestPhoneNumberConverter(t *testing.T) { + // test for error + if _, err := NewDataConverter("*phone_number:US:1:2:error"); err == nil || + err.Error() != "unsupported *phone_number converter parameters: " { + t.Error(err) + } + + eLc := &PhoneNumberConverter{CountryCode: "US", Format: phonenumbers.NATIONAL} + d, err := NewDataConverter("*phone_number:US") + if err != nil { + t.Error(err) + } else if !reflect.DeepEqual(eLc, d) { + t.Errorf("expecting: %+v, received: %+v", eLc, d) + } + // simulate an E164 number and Format it into a National number + phoneNumberConverted, err := d.Convert("+14431234567") + if err != nil { + t.Error(err) + } else if !reflect.DeepEqual(phoneNumberConverted, "(443) 123-4567") { + t.Errorf("expecting: %+v, received: %+v", "(443) 123-4567", phoneNumberConverted) + } +} + +func TestPhoneNumberConverter2(t *testing.T) { + eLc := &PhoneNumberConverter{CountryCode: "US", Format: phonenumbers.INTERNATIONAL} + d, err := NewDataConverter("*phone_number:US:1") + if err != nil { + t.Error(err) + } else if !reflect.DeepEqual(eLc, d) { + t.Errorf("expecting: %+v, received: %+v", eLc, d) + } + // simulate an E164 number and Format it into a National number + phoneNumberConverted, err := d.Convert("+14431234567") + if err != nil { + t.Error(err) + } else if !reflect.DeepEqual(phoneNumberConverted, "+1 443-123-4567") { + t.Errorf("expecting: %+v, received: %+v", "+1 443-123-4567", phoneNumberConverted) + } +} + +func TestPhoneNumberConverter3(t *testing.T) { + eLc := &PhoneNumberConverter{CountryCode: "DE", Format: phonenumbers.INTERNATIONAL} + d, err := NewDataConverter("*phone_number:DE:1") + if err != nil { + t.Error(err) + } else if !reflect.DeepEqual(eLc, d) { + t.Errorf("expecting: %+v, received: %+v", eLc, d) + } + phoneNumberConverted, err := d.Convert("6502530000") + if err != nil { + t.Error(err) + } else if !reflect.DeepEqual(phoneNumberConverted, "+49 6502 530000") { + t.Errorf("expecting: %+v, received: %+v", "+49 6502 530000", phoneNumberConverted) + } + + eLc = &PhoneNumberConverter{CountryCode: "DE", Format: phonenumbers.E164} + d, err = NewDataConverter("*phone_number:DE:0") + if err != nil { + t.Error(err) + } else if !reflect.DeepEqual(eLc, d) { + t.Errorf("expecting: %+v, received: %+v", eLc, d) + } + phoneNumberConverted, err = d.Convert("6502530000") + if err != nil { + t.Error(err) + } else if !reflect.DeepEqual(phoneNumberConverted, "+496502530000") { + t.Errorf("expecting: %+v, received: %+v", "+496502530000", phoneNumberConverted) + } +}