Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix numverify scanner #1125

Merged
merged 1 commit into from
Sep 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions docs/getting-started/scanners.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,17 @@ The local scan is probably the simplest scan of PhoneInfoga. By default, the too

Numverify provide standard but useful information such as country code, location, line type and carrier. This scanners requires an API-key which you can get on their website after creating an account. You can use a free API key as long as you don't exceed the monthly quota.

[Read documentation](https://apilayer.com/marketplace/number_verification-api#details-tab)

??? info "Configuration"

1. Go to the [Api layer website](https://apilayer.com/) and create an account
2. Go to "Number Verification API" in the marketplace, click on "Subscribe for free", then choose whatever plan you want
3. Copy the new API token and use it as an environment variable

| Environment variable | Default | Description |
|----------------------|---------|------------------------------------------------------------------------|
| NUMVERIFY_API_KEY | | API key to authenticate to the Numverify API. |
| NUMVERIFY_ENABLE_SSL | false | Whether to use HTTPS or plain HTTP for requests to the Numverify API. |

??? example "Output example"

Expand All @@ -60,7 +65,6 @@ Numverify provide standard but useful information such as country code, location
Country prefix: +41
Country code: CH
Country name: Switzerland (Confederation of)
Location:
Carrier: Sunrise Communications AG
Line type: mobile
```
Expand Down
18 changes: 9 additions & 9 deletions lib/remote/numverify_scanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ type numverifyScanner struct {

type NumverifyScannerResponse struct {
Valid bool `json:"valid" console:"Valid"`
Number string `json:"number" console:"Number"`
LocalFormat string `json:"local_format" console:"Local format"`
InternationalFormat string `json:"international_format" console:"International format"`
CountryPrefix string `json:"country_prefix" console:"Country prefix"`
CountryCode string `json:"country_code" console:"Country code"`
CountryName string `json:"country_name" console:"Country name"`
Location string `json:"location" console:"Location"`
Carrier string `json:"carrier" console:"Carrier"`
LineType string `json:"line_type" console:"Line type"`
Number string `json:"number" console:"Number,omitempty"`
LocalFormat string `json:"local_format" console:"Local format,omitempty"`
InternationalFormat string `json:"international_format" console:"International format,omitempty"`
CountryPrefix string `json:"country_prefix" console:"Country prefix,omitempty"`
CountryCode string `json:"country_code" console:"Country code,omitempty"`
CountryName string `json:"country_name" console:"Country name,omitempty"`
Location string `json:"location" console:"Location,omitempty"`
Carrier string `json:"carrier" console:"Carrier,omitempty"`
LineType string `json:"line_type" console:"Line type,omitempty"`
}

func NewNumverifyScanner(s suppliers.NumverifySupplierInterface) Scanner {
Expand Down
53 changes: 23 additions & 30 deletions lib/remote/suppliers/numverify.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,35 +15,31 @@ type NumverifySupplierInterface interface {
Validate(string) (*NumverifyValidateResponse, error)
}

type numverifyError struct {
Code int `json:"code"`
Info string `json:"info"`
type NumverifyErrorResponse struct {
Message string `json:"message"`
}

// NumverifyValidateResponse REST API response
type NumverifyValidateResponse struct {
Valid bool `json:"valid"`
Number string `json:"number"`
LocalFormat string `json:"local_format"`
InternationalFormat string `json:"international_format"`
CountryPrefix string `json:"country_prefix"`
CountryCode string `json:"country_code"`
CountryName string `json:"country_name"`
Location string `json:"location"`
Carrier string `json:"carrier"`
LineType string `json:"line_type"`
Error numverifyError `json:"error"`
Valid bool `json:"valid"`
Number string `json:"number"`
LocalFormat string `json:"local_format"`
InternationalFormat string `json:"international_format"`
CountryPrefix string `json:"country_prefix"`
CountryCode string `json:"country_code"`
CountryName string `json:"country_name"`
Location string `json:"location"`
Carrier string `json:"carrier"`
LineType string `json:"line_type"`
}

type NumverifySupplier struct {
ApiKey string
EnableSSL string
ApiKey string
}

func NewNumverifySupplier() *NumverifySupplier {
return &NumverifySupplier{
ApiKey: os.Getenv("NUMVERIFY_API_KEY"),
EnableSSL: os.Getenv("NUMVERIFY_ENABLE_SSL"),
ApiKey: os.Getenv("NUMVERIFY_API_KEY"),
}
}

Expand All @@ -52,18 +48,11 @@ func (s *NumverifySupplier) IsAvailable() bool {
}

func (s *NumverifySupplier) Validate(internationalNumber string) (res *NumverifyValidateResponse, err error) {
scheme := "http"

if s.EnableSSL != "" {
scheme = "https"
}

logrus.
WithField("number", internationalNumber).
WithField("scheme", scheme).
Debug("Running validate operation through Numverify API")

url := fmt.Sprintf("%s://api.apilayer.com/number_verification/validate?number=%s", scheme, internationalNumber)
url := fmt.Sprintf("https://api.apilayer.com/number_verification/validate?number=%s", internationalNumber)

// Build the request
client := &http.Client{}
Expand All @@ -80,15 +69,19 @@ func (s *NumverifySupplier) Validate(internationalNumber string) (res *Numverify
// Fill the response with the data from the JSON
var result NumverifyValidateResponse

if response.StatusCode >= 400 {
errorResponse := NumverifyErrorResponse{}
if err := json.NewDecoder(response.Body).Decode(&errorResponse); err != nil {
return nil, err
}
return nil, errors.New(errorResponse.Message)
}

// Use json.Decode for reading streams of JSON data
if err := json.NewDecoder(response.Body).Decode(&result); err != nil {
return nil, err
}

if len(result.Error.Info) > 0 {
return nil, errors.New(result.Error.Info)
}

res = &NumverifyValidateResponse{
Valid: result.Valid,
Number: result.Number,
Expand Down
62 changes: 8 additions & 54 deletions lib/remote/suppliers/numverify_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,7 @@ func TestNumverifySupplierSuccess(t *testing.T) {
number := "11115551212"

_ = os.Setenv("NUMVERIFY_API_KEY", "5ad5554ac240e4d3d31107941b35a5eb")
_ = os.Setenv("NUMVERIFY_ENABLE_SSL", "1")
defer os.Setenv("NUMVERIFY_API_KEY", "")
defer os.Setenv("NUMVERIFY_ENABLE_SSL", "")
defer os.Clearenv()

expectedResult := &NumverifyValidateResponse{
Valid: true,
Expand Down Expand Up @@ -49,65 +47,23 @@ func TestNumverifySupplierSuccess(t *testing.T) {
assert.Equal(t, expectedResult, got)
}

func TestNumverifySupplierWithoutSSL(t *testing.T) {
defer gock.Off() // Flush pending mocks after test execution

number := "11115551212"

_ = os.Setenv("NUMVERIFY_API_KEY", "5ad5554ac240e4d3d31107941b35a5eb")
defer os.Setenv("NUMVERIFY_API_KEY", "")

expectedResult := &NumverifyValidateResponse{
Valid: true,
Number: "79516566591",
LocalFormat: "9516566591",
InternationalFormat: "+79516566591",
CountryPrefix: "+7",
CountryCode: "RU",
CountryName: "Russian Federation",
Location: "Saint Petersburg and Leningrad Oblast",
Carrier: "OJSC St. Petersburg Telecom (OJSC Tele2-Saint-Petersburg)",
LineType: "mobile",
}

gock.New("http://api.apilayer.com").
Get("/number_verification/validate").
MatchHeader("Apikey", "5ad5554ac240e4d3d31107941b35a5eb").
MatchParam("number", number).
Reply(200).
JSON(expectedResult)

s := NewNumverifySupplier()

assert.True(t, s.IsAvailable())

got, err := s.Validate(number)
assert.Nil(t, err)

assert.Equal(t, expectedResult, got)
}

func TestNumverifySupplierError(t *testing.T) {
defer gock.Off() // Flush pending mocks after test execution

number := "11115551212"

_ = os.Setenv("NUMVERIFY_API_KEY", "5ad5554ac240e4d3d31107941b35a5eb")
defer os.Setenv("NUMVERIFY_API_KEY", "")
defer os.Clearenv()

expectedResult := &NumverifyValidateResponse{
Valid: false,
Error: numverifyError{
Code: 100,
Info: "Access Restricted - Your current Subscription Plan does not support HTTPS Encryption.",
},
expectedResult := &NumverifyErrorResponse{
Message: "You have exceeded your daily\\/monthly API rate limit. Please review and upgrade your subscription plan at https:\\/\\/apilayer.com\\/subscriptions to continue.",
}

gock.New("http://api.apilayer.com").
gock.New("https://api.apilayer.com").
Get("/number_verification/validate").
MatchHeader("Apikey", "5ad5554ac240e4d3d31107941b35a5eb").
MatchParam("number", number).
Reply(400).
Reply(429).
JSON(expectedResult)

s := NewNumverifySupplier()
Expand All @@ -116,7 +72,7 @@ func TestNumverifySupplierError(t *testing.T) {

got, err := s.Validate(number)
assert.Nil(t, got)
assert.Equal(t, errors.New("Access Restricted - Your current Subscription Plan does not support HTTPS Encryption."), err)
assert.Equal(t, errors.New("You have exceeded your daily\\/monthly API rate limit. Please review and upgrade your subscription plan at https:\\/\\/apilayer.com\\/subscriptions to continue."), err)
}

func TestNumverifySupplierHTTPError(t *testing.T) {
Expand All @@ -125,9 +81,7 @@ func TestNumverifySupplierHTTPError(t *testing.T) {
number := "11115551212"

_ = os.Setenv("NUMVERIFY_API_KEY", "5ad5554ac240e4d3d31107941b35a5eb")
_ = os.Setenv("NUMVERIFY_ENABLE_SSL", "1")
defer os.Setenv("NUMVERIFY_API_KEY", "")
defer os.Setenv("NUMVERIFY_ENABLE_SSL", "")
defer os.Clearenv()

dummyError := errors.New("test")

Expand Down
36 changes: 33 additions & 3 deletions web/server_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package web

import (
"github.com/sundowndev/phoneinfoga/v2/lib/remote"
"github.com/sundowndev/phoneinfoga/v2/lib/remote/suppliers"
"io/ioutil"
"net/http"
Expand Down Expand Up @@ -128,7 +127,7 @@ func TestApi(t *testing.T) {

number := "79516566591"

expectedResult := remote.NumverifyScannerResponse{
expectedResult := suppliers.NumverifyValidateResponse{
Valid: true,
Number: "79516566591",
LocalFormat: "9516566591",
Expand All @@ -141,7 +140,7 @@ func TestApi(t *testing.T) {
LineType: "mobile",
}

gock.New("http://api.apilayer.com").
gock.New("https://api.apilayer.com").
Get("/number_verification/validate").
MatchHeader("Apikey", "5ad5554ac240e4d3d31107941b35a5eb").
MatchParam("number", number).
Expand All @@ -159,6 +158,37 @@ func TestApi(t *testing.T) {

assert.Equal(t, gock.IsDone(), true, "there should have no pending mocks")
})

t.Run("should handle error", func(t *testing.T) {
defer gock.Off() // Flush pending mocks after test execution

_ = os.Setenv("NUMVERIFY_API_KEY", "5ad5554ac240e4d3d31107941b35a5eb")
defer os.Unsetenv("NUMVERIFY_API_KEY")

number := "79516566591"

expectedResult := &suppliers.NumverifyErrorResponse{
Message: "You have exceeded your daily\\/monthly API rate limit. Please review and upgrade your subscription plan at https:\\/\\/apilayer.com\\/subscriptions to continue.",
}

gock.New("https://api.apilayer.com").
Get("/number_verification/validate").
MatchHeader("Apikey", "5ad5554ac240e4d3d31107941b35a5eb").
MatchParam("number", number).
Reply(429).
JSON(expectedResult)

res, err := performRequest(r, http.MethodGet, "/api/numbers/79516566591/scan/numverify")
assert.Equal(t, nil, err)

body, err := ioutil.ReadAll(res.Body)

assert.Equal(t, nil, err)
assert.Equal(t, 500, res.Result().StatusCode)
assert.Equal(t, `{"success":false,"error":"You have exceeded your daily\\/monthly API rate limit. Please review and upgrade your subscription plan at https:\\/\\/apilayer.com\\/subscriptions to continue."}`, string(body))

assert.Equal(t, gock.IsDone(), true, "there should have no pending mocks")
})
})

t.Run("googleSearchScan - /api/numbers/:number/scan/googlesearch", func(t *testing.T) {
Expand Down