Skip to content

Commit 8c9adf4

Browse files
authored
Merge pull request #1 from clearip/new-structure
New structure
2 parents c87c359 + 3dab928 commit 8c9adf4

10 files changed

+419
-50
lines changed

Diff for: .travis.yml

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
2+
# This is a weird way of telling Travis to use the fast container-based test
3+
# runner instead of the slow VM-based runner.
4+
sudo: false
5+
6+
language: go
7+
8+
# Force-enable Go modules. This will be unnecessary when Go 1.12 lands.
9+
env:
10+
- GO111MODULE=on
11+
12+
# You don't need to test on very old version of the Go compiler. It's the user's
13+
# responsibility to keep their compilers up to date.
14+
go:
15+
- 1.11.x
16+
17+
# Only clone the most recent commit.
18+
git:
19+
depth: 1
20+
21+
# Skip the install step. Don't `go get` dependencies. Only build with the code
22+
# in vendor/
23+
install: true
24+
25+
# Don't email me the results of the test runs.
26+
notifications:
27+
email: false
28+
29+
# script always runs to completion (set +e). If we have linter issues AND a
30+
# failing test, we want to see both. Configure golangci-lint with a
31+
# .golangci.yml file at the top level of your repo.
32+
script:
33+
- go test -v -race ./... # Run all the tests with the race detector enabled

Diff for: client.go

+16-48
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,31 @@
11
package clearip
22

3-
import (
4-
"encoding/json"
5-
"fmt"
6-
"net/http"
7-
"regexp"
8-
"strings"
9-
"time"
10-
)
3+
import "fmt"
114

12-
// Client is wrapper for net/http client with additional variables
5+
// Client main client for clear ip
136
type Client struct {
14-
APIKey string
15-
URL string
16-
HTTP *http.Client
7+
HTTPClient CLHTTPClient
8+
IPRepo IPInfoRepository
9+
BaseURI string
10+
APIKey string
1711
}
1812

19-
// NewClient returns custome http client
13+
// BaseURI base url for clearip
14+
const defaultBaseURI = "https://api.clearip.io"
15+
16+
// NewClient returns a new ClearIp API client, configured with default HTTPClient.
2017
func NewClient(apiKey string) (*Client, error) {
2118

2219
if len(apiKey) == 0 {
2320
return nil, fmt.Errorf("API key required")
2421
}
25-
26-
httpClient := &http.Client{
27-
Timeout: time.Second * 10,
28-
}
29-
return &Client{
30-
URL: "https://api.clearip.io",
31-
APIKey: apiKey,
32-
HTTP: httpClient,
33-
}, nil
22+
clearip := Client{APIKey: apiKey, BaseURI: defaultBaseURI}
23+
clearip.HTTPClient = NewHTTPClient(clearip.APIKey, clearip.BaseURI)
24+
clearip.setup()
25+
return &clearip, nil
3426
}
3527

36-
// GetIpinfo send a get request for ip info
37-
func (c *Client) GetIpinfo(ip string) (map[string]interface{}, error) {
38-
39-
matches, _ := regexp.MatchString(`^(([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]).){3}([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$`, ip)
40-
if !matches {
41-
return nil, fmt.Errorf("Ip required")
42-
}
43-
44-
getIPInfoURL := strings.Join([]string{c.URL, "/ip/", ip, "/json?apikey=", c.APIKey}, "")
45-
resp, err := c.HTTP.Get(getIPInfoURL)
46-
if err != nil {
47-
return nil, err
48-
}
49-
50-
if resp.StatusCode != 200 {
51-
return nil, fmt.Errorf("auth error")
52-
}
53-
54-
var response = make(map[string]interface{})
55-
56-
dec := json.NewDecoder(resp.Body)
57-
if err := dec.Decode(&response); err != nil {
58-
return nil, fmt.Errorf("error parsing json")
59-
}
60-
61-
return response, nil
28+
func (c *Client) setup() {
29+
c.IPRepo = IPInfoAPI{HTTP: c.HTTPClient}
6230

6331
}

Diff for: fixtures/ipinfo.json

+98
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
{
2+
"country":"Egypt",
3+
"continent":"Africa",
4+
"country_falg":"🇪🇬",
5+
"CountryCode":"EG",
6+
"City":"Cairo",
7+
"Region":"Cairo Governorate",
8+
"lat":30.0771,
9+
"lng":31.2859,
10+
"tz":"Africa/Cairo",
11+
"isp":"Link Egypt",
12+
"is_anonymous_proxy":false,
13+
"is_satellite_provider":false,
14+
"currency":[
15+
"EGP"
16+
],
17+
"country_details":{
18+
"name":{
19+
"common":"Egypt",
20+
"official":"Arab Republic of Egypt",
21+
"Native":{
22+
"ara":{
23+
"common":"مصر",
24+
"official":"جمهورية مصر العربية"
25+
}
26+
}
27+
},
28+
"EuMember":false,
29+
"LandLocked":false,
30+
"Nationality":"",
31+
"tld":[
32+
".eg",
33+
".مصر"
34+
],
35+
"Languages":{
36+
"ara":"Arabic"
37+
},
38+
"Translations":{
39+
"DEU":{
40+
"common":"Ägypten",
41+
"official":"Arabische Republik Ägypten"
42+
},
43+
"FIN":{
44+
"common":"Egypti",
45+
"official":"Egyptin arabitasavalta"
46+
},
47+
"HRV":{
48+
"common":"Egipat",
49+
"official":"Arapska Republika Egipat"
50+
},
51+
"JPN":{
52+
"common":"エジプト",
53+
"official":"エジプト·アラブ共和国"
54+
},
55+
"NLD":{
56+
"common":"Egypte",
57+
"official":"Arabische Republiek Egypte"
58+
},
59+
"POR":{
60+
"common":"Egito",
61+
"official":"República Árabe do Egipto"
62+
},
63+
"RUS":{
64+
"common":"Египет",
65+
"official":"Арабская Республика Египет"
66+
}
67+
},
68+
"currency":[
69+
"EGP"
70+
],
71+
"Borders":[
72+
"ISR",
73+
"LBY",
74+
"SDN"
75+
],
76+
"cca2":"EG",
77+
"cca3":"EGY",
78+
"CIOC":"EGY",
79+
"CCN3":"818",
80+
"callingCode":[
81+
"20"
82+
],
83+
"InternationalPrefix":"00",
84+
"region":"Africa",
85+
"subregion":"Northern Africa",
86+
"Continent":"Africa",
87+
"capital":"Cairo",
88+
"Area":1002450,
89+
"longitude":"30 00 E",
90+
"latitude":"27 00 N",
91+
"MinLongitude":24.7,
92+
"MinLatitude":20.383333,
93+
"MaxLongitude":36.333332,
94+
"MaxLatitude":31.916668,
95+
"Latitude":26.756104,
96+
"Longitude":29.862297
97+
}
98+
}

Diff for: http_client.go

+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package clearip
2+
3+
import (
4+
"encoding/json"
5+
"io"
6+
"io/ioutil"
7+
"net/http"
8+
"time"
9+
10+
"github.com/google/go-querystring/query"
11+
)
12+
13+
// HTTPClient -
14+
type HTTPClient interface {
15+
Get(string, interface{}) ([]byte, error)
16+
}
17+
18+
// CLHTTPClient - impelemts HTTPClient interface clear ip default client
19+
type CLHTTPClient struct {
20+
*http.Client
21+
BaseURI string
22+
APIKey string
23+
}
24+
25+
//NewHTTPClient - create the clear ip client with api key
26+
func NewHTTPClient(apiKey string, BaseURI string) CLHTTPClient {
27+
return CLHTTPClient{Client: &http.Client{
28+
Timeout: time.Second * 10,
29+
},
30+
APIKey: apiKey,
31+
BaseURI: BaseURI,
32+
}
33+
}
34+
35+
// Get default http client
36+
func (c CLHTTPClient) Get(url string, queryParams interface{}) ([]byte, error) {
37+
// Setup request
38+
req, _ := http.NewRequest("GET", c.BaseURI+url+"?apikey="+c.APIKey, nil)
39+
req.Header.Add("Accept", "application/json")
40+
if queryParams != nil {
41+
addQueryParams(req, queryParams)
42+
}
43+
// Do request
44+
resp, err := c.Client.Do(req)
45+
if err != nil {
46+
return nil, err
47+
}
48+
defer resp.Body.Close()
49+
50+
// Read response
51+
data, err := c.readAll(resp.Body)
52+
if err != nil {
53+
return nil, err
54+
}
55+
if resp.StatusCode >= 400 {
56+
return nil, c.parseResponseError(data, resp.StatusCode)
57+
}
58+
return data, err
59+
}
60+
61+
func (c CLHTTPClient) parseResponseError(data []byte, statusCode int) HTTPError {
62+
errorList := HTTPErrorList{}
63+
err := json.Unmarshal(data, &errorList)
64+
if err != nil {
65+
return NewUnknownHTTPError(statusCode)
66+
}
67+
if len(errorList.Errors) == 0 {
68+
return NewUnknownHTTPError(statusCode)
69+
}
70+
httpError := errorList.Errors[0]
71+
httpError.StatusCode = statusCode
72+
return httpError // only care about the first
73+
}
74+
75+
func (c CLHTTPClient) readAll(body io.Reader) ([]byte, error) {
76+
b, err := ioutil.ReadAll(body)
77+
78+
return b, err
79+
}
80+
81+
func addQueryParams(req *http.Request, params interface{}) {
82+
v, _ := query.Values(params)
83+
req.URL.RawQuery = v.Encode()
84+
}

Diff for: http_client_test.go

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package clearip
2+
3+
type TestHTTPClient struct{}
4+
5+
func (h TestHTTPClient) Get(uri string, queryParams interface{}) ([]byte, error) { return nil, nil }

Diff for: http_error.go

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package clearip
2+
3+
import (
4+
"fmt"
5+
"net/http"
6+
)
7+
8+
// HTTPErrorList -
9+
type HTTPErrorList struct {
10+
Type string `json:"type"`
11+
Errors []HTTPError `json:"errors"`
12+
}
13+
14+
// HTTPError -
15+
type HTTPError struct {
16+
StatusCode int
17+
Code string `json:"code"`
18+
Message string `json:"message"`
19+
}
20+
21+
// NewUnknownHTTPError -
22+
func NewUnknownHTTPError(statusCode int) HTTPError {
23+
message := http.StatusText(statusCode)
24+
if message == "" {
25+
message = "Unknown Error"
26+
}
27+
return HTTPError{Code: "Unknown", Message: message, StatusCode: statusCode}
28+
}
29+
30+
func (e HTTPError) Error() string {
31+
return fmt.Sprintf("%d: %s, %s", e.StatusCode, e.Code, e.Message)
32+
}
33+
34+
// GetStatusCode -
35+
func (e HTTPError) GetStatusCode() int {
36+
return e.StatusCode
37+
}
38+
39+
// GetCode -
40+
func (e HTTPError) GetCode() string {
41+
return e.Code
42+
}
43+
44+
// GetMessage -
45+
func (e HTTPError) GetMessage() string {
46+
return e.Message
47+
}

0 commit comments

Comments
 (0)