Skip to content

Commit 0092942

Browse files
Add new functions to lookup AS data
1 parent 0cd403a commit 0092942

File tree

9 files changed

+236
-15
lines changed

9 files changed

+236
-15
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
88
## [Unreleased]
99
### Changed
1010
- Require Python 3.8+.
11+
- Add new functions for retrieving Autonomous System data: `batch_lookup_asns`, `lookup_asn`, `origin_lookup_asn`.
1112
- Add new functions for user-agent header value parsing: `batch_parse_user_agents`, `parse_user_agent`.
1213
- API key is passed as header value and no longer as query parameter.
1314
- Client library method are now wrapped in a new _ApiResponse_ object that includes a mean to retrieve metadata

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,18 @@ credits_consumed = response.credits.consumed
4242
credits_remaining = response.credits.remaining
4343
```
4444

45+
#### Single ASN Lookup
46+
47+
```python
48+
from ipregistry import IpregistryClient
49+
50+
client = IpregistryClient("YOUR_API_KEY")
51+
response = client.lookup_asn(42)
52+
print(response.credits.consumed)
53+
print(response.data.prefixes)
54+
print(response.data.relationships)
55+
```
56+
4557
#### Batch IP Lookup
4658

4759
```python

ipregistry/core.py

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
limitations under the License.
1515
"""
1616
from .cache import IpregistryCache, NoCache
17-
from .json import IpInfo, RequesterIpInfo
17+
from .json import AutonomousSystem, IpInfo
1818
from .model import LookupError, ApiResponse, ApiResponseCredits, ApiResponseThrottling
1919
from .request import DefaultRequestHandler, IpregistryRequestHandler
2020

@@ -30,6 +30,9 @@ def __init__(self, key_or_config, **kwargs):
3030
if not isinstance(self._requestHandler, IpregistryRequestHandler):
3131
raise ValueError("Given request handler instance is not of type IpregistryRequestHandler")
3232

33+
def batch_lookup_asns(self, ips, **options):
34+
return self.batch_request(ips, self._requestHandler.batch_lookup_asns, **options)
35+
3336
def batch_lookup_ips(self, ips, **options):
3437
return self.batch_request(ips, self._requestHandler.batch_lookup_ips, **options)
3538

@@ -77,25 +80,40 @@ def batch_request(self, items, request_handler_func, **options):
7780

7881
return response
7982

80-
def lookup_ip(self, ip='', **options):
83+
def lookup_asn(self, asn, **options):
84+
return self.__lookup_asn(asn, options)
85+
86+
def lookup_ip(self, ip, **options):
8187
if isinstance(ip, str):
8288
return self.__lookup_ip(ip, options)
8389
else:
8490
raise ValueError("Invalid value for 'ip' parameter: " + ip)
8591

92+
def origin_lookup_asn(self, **options):
93+
return self.__lookup_asn('AS', options)
94+
8695
def origin_lookup_ip(self, **options):
8796
return self.__lookup_ip('', options)
8897

8998
def origin_parse_user_agent(self, **options):
9099
return self._requestHandler.origin_parse_user_agent(options)
91100

101+
def __lookup_asn(self, asn, options):
102+
return self.__lookup(
103+
'AS' + str(asn) if IpregistryClient.__is_number(asn) else 'AS',
104+
options, self._requestHandler.lookup_asn,
105+
AutonomousSystem)
106+
92107
def __lookup_ip(self, ip, options):
93-
cache_key = self.__build_cache_key(ip, options)
108+
return self.__lookup(ip, options, self._requestHandler.lookup_ip, IpInfo)
109+
110+
def __lookup(self, key, options, lookup_func, response_type):
111+
cache_key = self.__build_cache_key(key, options)
94112
cache_value = self._cache.get(cache_key)
95113

96114
if cache_value is None:
97-
response = self._requestHandler.lookup_ip(ip, options)
98-
if isinstance(response.data, IpInfo):
115+
response = lookup_func(key, options)
116+
if isinstance(response.data, response_type):
99117
self._cache.put(cache_key, response.data)
100118
return response
101119

@@ -125,6 +143,15 @@ def __build_cache_key(key, options):
125143
def __is_api_error(data):
126144
return 'code' in data
127145

146+
@staticmethod
147+
def __is_number(value):
148+
try:
149+
# Try converting the value to a float
150+
float(value)
151+
return True
152+
except ValueError:
153+
return False
154+
128155

129156
class IpregistryConfig:
130157
def __init__(self, key, base_url="https://api.ipregistry.co", timeout=15):

ipregistry/request.py

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,19 @@
2323
import requests
2424

2525
from .__init__ import __version__
26-
from .model import (ApiError, ApiResponse, ApiResponseCredits, ApiResponseThrottling, ClientError, IpInfo,
27-
LookupError, RequesterIpInfo, RequesterUserAgent, UserAgent)
26+
from .model import (ApiError, ApiResponse, ApiResponseCredits, ApiResponseThrottling, AutonomousSystem,
27+
ClientError, IpInfo, LookupError, RequesterAutonomousSystem, RequesterIpInfo,
28+
RequesterUserAgent, UserAgent)
2829

2930

3031
class IpregistryRequestHandler(ABC):
3132
def __init__(self, config):
3233
self._config = config
3334

35+
@abstractmethod
36+
def batch_lookup_asns(self, ips, options):
37+
pass
38+
3439
@abstractmethod
3540
def batch_lookup_ips(self, ips, options):
3641
pass
@@ -39,6 +44,10 @@ def batch_lookup_ips(self, ips, options):
3944
def batch_parse_user_agents(self, user_agents, options):
4045
pass
4146

47+
@abstractmethod
48+
def lookup_asn(self, asn, options):
49+
pass
50+
4251
@abstractmethod
4352
def lookup_ip(self, ip, options):
4453
pass
@@ -65,6 +74,29 @@ def _build_base_url(self, resource, options):
6574

6675

6776
class DefaultRequestHandler(IpregistryRequestHandler):
77+
def batch_lookup_asns(self, asns, options):
78+
response = None
79+
try:
80+
response = requests.post(
81+
self._build_base_url('', options),
82+
data=json.dumps(list(map(lambda asn: "AS" + str(asn), asns))),
83+
headers=self.__headers(),
84+
timeout=self._config.timeout
85+
)
86+
response.raise_for_status()
87+
results = response.json().get('results', [])
88+
89+
parsed_results = [
90+
LookupError(data) if 'code' in data else AutonomousSystem(**data)
91+
for data in results
92+
]
93+
94+
return self.build_api_response(response, parsed_results)
95+
except requests.HTTPError:
96+
self.__create_api_error(response)
97+
except Exception as e:
98+
raise ClientError(e)
99+
68100
def batch_lookup_ips(self, ips, options):
69101
response = None
70102
try:
@@ -111,6 +143,27 @@ def batch_parse_user_agents(self, user_agents, options):
111143
except Exception as e:
112144
raise ClientError(e)
113145

146+
def lookup_asn(self, asn, options):
147+
response = None
148+
try:
149+
response = requests.get(
150+
self._build_base_url(asn, options),
151+
headers=self.__headers(),
152+
timeout=self._config.timeout
153+
)
154+
response.raise_for_status()
155+
json_response = response.json()
156+
157+
return self.build_api_response(
158+
response,
159+
RequesterAutonomousSystem(**json_response) if asn == 'AS'
160+
else AutonomousSystem(**json_response)
161+
)
162+
except requests.HTTPError:
163+
self.__create_api_error(response)
164+
except Exception as err:
165+
raise ClientError(err)
166+
114167
def lookup_ip(self, ip, options):
115168
response = None
116169
try:

samples/batch-lookup-asns.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
"""
2+
Copyright 2019 Ipregistry (https://ipregistry.co).
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
https://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
"""
16+
17+
from ipregistry import ApiError, AutonomousSystem, ClientError, IpregistryClient
18+
19+
try:
20+
api_key = "tryout"
21+
client = IpregistryClient(api_key)
22+
response = client.batch_lookup_asns([42, 4441, 51933])
23+
data_list = response.data
24+
25+
for entry in data_list:
26+
if isinstance(entry, AutonomousSystem):
27+
print("AutonomousSystem", entry)
28+
else:
29+
print("LookupError", entry)
30+
except ApiError as e:
31+
print("API error", e)
32+
except ClientError as e:
33+
print("Client error", e)
34+
except Exception as e:
35+
print("Unexpected error", e)

samples/batch-lookup-ips.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,13 @@
2020
api_key = "tryout"
2121
client = IpregistryClient(api_key)
2222
response = client.batch_lookup_ips(["73.2.2.2", "8.8.8.8", "2001:67c:2e8:22::c100:68b"])
23-
ip_info_list = response.data
23+
data_list = response.data
2424

25-
for lookup_result in ip_info_list:
26-
if isinstance(lookup_result, IpInfo):
27-
print("IpInfo", lookup_result)
25+
for entry in data_list:
26+
if isinstance(entry, IpInfo):
27+
print("IpInfo", entry)
2828
else:
29-
print("LookupError", lookup_result)
29+
print("LookupError", entry)
3030
except ApiError as e:
3131
print("API error", e)
3232
except ClientError as e:

samples/lookup-asn.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
"""
2+
Copyright 2019 Ipregistry (https://ipregistry.co).
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
https://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
"""
16+
17+
from ipregistry import ApiError, ClientError, IpregistryClient
18+
19+
try:
20+
api_key = "tryout"
21+
client = IpregistryClient(api_key)
22+
response = client.lookup_asn(42)
23+
print(response.credits.consumed)
24+
print(response.data.relationships)
25+
except ApiError as e:
26+
print("API error", e)
27+
except ClientError as e:
28+
print("Client error", e)
29+
except Exception as e:
30+
print("Unexpected error", e)

samples/origin-lookup-asn.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
"""
2+
Copyright 2019 Ipregistry (https://ipregistry.co).
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
https://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
"""
16+
17+
from ipregistry import ApiError, ClientError, IpregistryClient
18+
19+
try:
20+
api_key = "tryout"
21+
client = IpregistryClient(api_key)
22+
response = client.origin_lookup_asn()
23+
as_data = response.data
24+
print(as_data)
25+
except ApiError as e:
26+
print("API error", e)
27+
except ClientError as e:
28+
print("Client error", e)
29+
except Exception as e:
30+
print("Unexpected error", e)

tests/test_client.py

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,27 @@
1717
import os
1818
import unittest
1919

20-
from ipregistry import ApiError, IpInfo, LookupError, ClientError, UserAgent
20+
from ipregistry import ApiError, AutonomousSystem, IpInfo, LookupError, ClientError, UserAgent
2121
from ipregistry.cache import InMemoryCache, NoCache
2222
from ipregistry.core import IpregistryClient, IpregistryConfig
2323

2424

2525
class TestIpregistryClient(unittest.TestCase):
2626

27+
def test_batch_lookup_asns(self):
28+
"""
29+
Test batch asns lookup with valid and invalid inputs
30+
"""
31+
client = IpregistryClient(os.getenv('IPREGISTRY_API_KEY'))
32+
response = client.batch_lookup_asns([33, 'invalid', -1])
33+
print(response)
34+
self.assertEqual(3, len(response.data))
35+
self.assertEqual(True, isinstance(response.data[0], AutonomousSystem))
36+
self.assertEqual(True, isinstance(response.data[1], LookupError))
37+
self.assertEqual('INVALID_ASN', response.data[1].code)
38+
self.assertEqual(True, isinstance(response.data[2], LookupError))
39+
self.assertEqual('INVALID_ASN', response.data[2].code)
40+
2741
def test_batch_lookup_ips(self):
2842
"""
2943
Test batch ips lookup with valid and invalid inputs
@@ -83,6 +97,16 @@ def test_client_cache_inmemory_batch_ips_lookup(self):
8397
batch_ips_response2 = client.batch_lookup_ips(['1.1.1.1', '1.1.1.3'])
8498
self.assertEqual(0, batch_ips_response2.credits.consumed)
8599

100+
def test_lookup_asn(self):
101+
"""
102+
Test Autonomous System data lookup by ASN
103+
"""
104+
client = IpregistryClient(os.getenv('IPREGISTRY_API_KEY'))
105+
response = client.lookup_asn(400923)
106+
self.assertEqual(400923, response.data.asn)
107+
self.assertEqual(1, response.credits.consumed)
108+
self.assertIsNotNone(response.data.relationships)
109+
86110
def test_lookup_ip(self):
87111
"""
88112
Test that a simple IP lookup returns data
@@ -100,7 +124,6 @@ def test_lookup_ip_invalid_input(self):
100124
client = IpregistryClient(os.getenv('IPREGISTRY_API_KEY'))
101125
with self.assertRaises(ApiError) as context:
102126
response = client.lookup_ip('invalid')
103-
print("test", context.exception)
104127
self.assertEqual('INVALID_IP_ADDRESS', context.exception.code)
105128

106129
def test_lookup_ip_cache(self):
@@ -122,12 +145,22 @@ def test_lookup_timeout(self):
122145
with self.assertRaises(ClientError):
123146
client.lookup_ip('1.1.1.1')
124147

148+
def test_origin_asn(self):
149+
"""
150+
Test origin Autonomous System data lookup
151+
"""
152+
client = IpregistryClient(os.getenv('IPREGISTRY_API_KEY'))
153+
response = client.origin_lookup_asn()
154+
self.assertIsNotNone(response.data.asn)
155+
self.assertEqual(1, response.credits.consumed)
156+
self.assertIsNotNone(response.data.relationships)
157+
125158
def test_origin_lookup_ip(self):
126159
"""
127160
Test that a simple origin IP lookup returns data
128161
"""
129162
client = IpregistryClient(os.getenv('IPREGISTRY_API_KEY'))
130-
response = client.lookup_ip()
163+
response = client.origin_lookup_ip()
131164
self.assertIsNotNone(response.data.ip)
132165
self.assertIsNotNone(response.data.user_agent)
133166

0 commit comments

Comments
 (0)