From c18a4611e6ad21a175633752000e41c65ccd71fb Mon Sep 17 00:00:00 2001 From: sundowndev Date: Mon, 5 Sep 2022 20:39:55 +0400 Subject: [PATCH] fix: numverify scanner --- docs/getting-started/scanners.md | 8 +++- lib/remote/numverify_scanner.go | 18 ++++---- lib/remote/suppliers/numverify.go | 53 ++++++++++------------ lib/remote/suppliers/numverify_test.go | 62 ++++---------------------- web/server_test.go | 36 +++++++++++++-- 5 files changed, 79 insertions(+), 98 deletions(-) diff --git a/docs/getting-started/scanners.md b/docs/getting-started/scanners.md index c1a0c6eb6..afd57f66d 100644 --- a/docs/getting-started/scanners.md +++ b/docs/getting-started/scanners.md @@ -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" @@ -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 ``` diff --git a/lib/remote/numverify_scanner.go b/lib/remote/numverify_scanner.go index 1e1e34ec9..c827d0ad4 100644 --- a/lib/remote/numverify_scanner.go +++ b/lib/remote/numverify_scanner.go @@ -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 { diff --git a/lib/remote/suppliers/numverify.go b/lib/remote/suppliers/numverify.go index d544482f7..23fa11b9c 100644 --- a/lib/remote/suppliers/numverify.go +++ b/lib/remote/suppliers/numverify.go @@ -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"), } } @@ -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{} @@ -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, diff --git a/lib/remote/suppliers/numverify_test.go b/lib/remote/suppliers/numverify_test.go index 94422b829..367d5cb07 100644 --- a/lib/remote/suppliers/numverify_test.go +++ b/lib/remote/suppliers/numverify_test.go @@ -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, @@ -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() @@ -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) { @@ -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") diff --git a/web/server_test.go b/web/server_test.go index b8ba00eeb..dbacf7af4 100644 --- a/web/server_test.go +++ b/web/server_test.go @@ -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" @@ -128,7 +127,7 @@ func TestApi(t *testing.T) { number := "79516566591" - expectedResult := remote.NumverifyScannerResponse{ + expectedResult := suppliers.NumverifyValidateResponse{ Valid: true, Number: "79516566591", LocalFormat: "9516566591", @@ -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). @@ -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) {