From d35332b7e67d6ae6bea3fd50b9405b554a18b491 Mon Sep 17 00:00:00 2001 From: Sergey Nastich Date: Wed, 28 Mar 2018 13:48:02 -0400 Subject: SCALA-20 Use liphonenumber in `PhoneNumber.parse` to accomodate chinese numbers (and other countries) --- build.sbt | 37 +++++----- src/main/scala/xyz/driver/core/domain.scala | 18 +++-- .../scala/xyz/driver/core/PhoneNumberTest.scala | 79 ++++++++++++++++++++++ 3 files changed, 106 insertions(+), 28 deletions(-) create mode 100644 src/test/scala/xyz/driver/core/PhoneNumberTest.scala diff --git a/build.sbt b/build.sbt index 9f878f1..88e4582 100644 --- a/build.sbt +++ b/build.sbt @@ -7,22 +7,23 @@ lazy val core = (project in file(".")) .driverLibrary("core") .settings(lintingSettings ++ formatSettings) .settings(libraryDependencies ++= Seq( - "xyz.driver" %% "tracing" % "0.0.2", - "com.typesafe.akka" %% "akka-http-core" % akkaHttpV, - "com.typesafe.akka" %% "akka-http-spray-json" % akkaHttpV, - "com.typesafe.akka" %% "akka-http-testkit" % akkaHttpV, - "com.pauldijou" %% "jwt-core" % "0.14.0", - "org.scalatest" %% "scalatest" % "3.0.2" % "test", - "org.scalacheck" %% "scalacheck" % "1.13.4" % "test", - "org.scalaz" %% "scalaz-core" % "7.2.19", - "org.mockito" % "mockito-core" % "1.9.5" % "test", - "com.github.swagger-akka-http" %% "swagger-akka-http" % "0.11.2", - "com.amazonaws" % "aws-java-sdk-s3" % "1.11.26", - "com.google.cloud" % "google-cloud-pubsub" % "0.25.0-beta", - "com.google.cloud" % "google-cloud-storage" % "1.7.0", - "com.typesafe.slick" %% "slick" % "3.2.1", - "com.typesafe" % "config" % "1.3.1", - "com.typesafe.scala-logging" %% "scala-logging" % "3.5.0", - "eu.timepit" %% "refined" % "0.8.4", - "ch.qos.logback" % "logback-classic" % "1.1.11" + "xyz.driver" %% "tracing" % "0.0.2", + "com.typesafe.akka" %% "akka-http-core" % akkaHttpV, + "com.typesafe.akka" %% "akka-http-spray-json" % akkaHttpV, + "com.typesafe.akka" %% "akka-http-testkit" % akkaHttpV, + "com.pauldijou" %% "jwt-core" % "0.14.0", + "org.scalatest" %% "scalatest" % "3.0.2" % "test", + "org.scalacheck" %% "scalacheck" % "1.13.4" % "test", + "org.scalaz" %% "scalaz-core" % "7.2.19", + "com.github.swagger-akka-http" %% "swagger-akka-http" % "0.11.2", + "com.typesafe.scala-logging" %% "scala-logging" % "3.5.0", + "eu.timepit" %% "refined" % "0.8.4", + "com.typesafe.slick" %% "slick" % "3.2.1", + "org.mockito" % "mockito-core" % "1.9.5" % "test", + "com.amazonaws" % "aws-java-sdk-s3" % "1.11.26", + "com.google.cloud" % "google-cloud-pubsub" % "0.25.0-beta", + "com.google.cloud" % "google-cloud-storage" % "1.7.0", + "com.typesafe" % "config" % "1.3.1", + "ch.qos.logback" % "logback-classic" % "1.1.11", + "com.googlecode.libphonenumber" % "libphonenumber" % "8.9.2" )) diff --git a/src/main/scala/xyz/driver/core/domain.scala b/src/main/scala/xyz/driver/core/domain.scala index 48943a7..7731345 100644 --- a/src/main/scala/xyz/driver/core/domain.scala +++ b/src/main/scala/xyz/driver/core/domain.scala @@ -1,13 +1,14 @@ package xyz.driver.core +import com.google.i18n.phonenumbers.PhoneNumberUtil import scalaz.Equal -import scalaz.syntax.equal._ import scalaz.std.string._ +import scalaz.syntax.equal._ object domain { final case class Email(username: String, domain: String) { - override def toString = username + "@" + domain + override def toString: String = username + "@" + domain } object Email { @@ -27,16 +28,13 @@ object domain { } object PhoneNumber { - def parse(phoneNumberString: String): Option[PhoneNumber] = { - val onlyDigits = phoneNumberString.replaceAll("[^\\d.]", "") - if (onlyDigits.length < 10) None - else { - val tenDigitNumber = onlyDigits.takeRight(10) - val countryCode = Option(onlyDigits.dropRight(10)).filter(_.nonEmpty).getOrElse("1") + private val phoneUtil = PhoneNumberUtil.getInstance() - Some(PhoneNumber(countryCode, tenDigitNumber)) - } + def parse(phoneNumber: String): Option[PhoneNumber] = { + val phone = phoneUtil.parseAndKeepRawInput(phoneNumber, "US") + if (!phoneUtil.isValidNumber(phone)) None + else Some(PhoneNumber(phone.getCountryCode.toString, phone.getNationalNumber.toString)) } } } diff --git a/src/test/scala/xyz/driver/core/PhoneNumberTest.scala b/src/test/scala/xyz/driver/core/PhoneNumberTest.scala new file mode 100644 index 0000000..384c7be --- /dev/null +++ b/src/test/scala/xyz/driver/core/PhoneNumberTest.scala @@ -0,0 +1,79 @@ +package xyz.driver.core + +import org.scalatest.{FlatSpec, Matchers} +import xyz.driver.core.domain.PhoneNumber + +class PhoneNumberTest extends FlatSpec with Matchers { + + "PhoneNumber.parse" should "recognize US numbers in international format, ignoring non-digits" in { + // format: off + val numbers = List( + "+18005252225", + "+1 800 525 2225", + "+1 (800) 525-2225", + "+1.800.525.2225") + // format: on + + val parsed = numbers.flatMap(PhoneNumber.parse) + + parsed should have size numbers.size + parsed should contain only PhoneNumber("1", "8005252225") + } + + it should "recognize US numbers without the plus sign" in { + PhoneNumber.parse("18005252225") shouldBe Some(PhoneNumber("1", "8005252225")) + } + + it should "recognize US numbers without country code" in { + // format: off + val numbers = List( + "8005252225", + "800 525 2225", + "(800) 525-2225", + "800.525.2225") + // format: on + + val parsed = numbers.flatMap(PhoneNumber.parse) + + parsed should have size numbers.size + parsed should contain only PhoneNumber("1", "8005252225") + } + + it should "recognize CN numbers in international format" in { + PhoneNumber.parse("+868005252225") shouldBe Some(PhoneNumber("86", "8005252225")) + PhoneNumber.parse("+86 134 52 52 2256") shouldBe Some(PhoneNumber("86", "13452522256")) + } + + it should "return None on numbers that are shorter than the minimum number of digits for the country (i.e. US - 10, AR - 11)" in { + withClue("US and CN numbers are 10 digits - 9 digit (and shorter) numbers should not fit") { + // format: off + val numbers = List( + "+1 800 525-222", + "+1 800 525-2", + "+86 800 525-222", + "+86 800 525-2") + // format: on + + numbers.flatMap(PhoneNumber.parse) shouldBe empty + } + + withClue("Argentinian numbers are 11 digits (when prefixed with 0) - 10 digit numbers shouldn't fit") { + // format: off + val numbers = List( + "+54 011 525-22256", + "+54 011 525-2225", + "+54 011 525-222") + // format: on + + numbers.flatMap(PhoneNumber.parse) should contain theSameElementsAs List(PhoneNumber("54", "1152522256")) + } + } + + it should "return None on numbers that are longer than the maximum number of digits for the country (i.e. DK - 8, CN - 11)" in { + val numbers = List("+45 27 45 25 22", "+45 135 525 223", "+86 134 525 22256", "+86 135 525 22256 7") + + numbers.flatMap(PhoneNumber.parse) should contain theSameElementsAs + List(PhoneNumber("45", "27452522"), PhoneNumber("86", "13452522256")) + } + +} -- cgit v1.2.3