Skip to content
This repository has been archived by the owner on Dec 12, 2023. It is now read-only.

Add paginator #4

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
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
22 changes: 21 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,24 @@ api.ip_domains('185.27.134.165')
+ ip_blacklist()
+ domain_lookup()
+ domain_blacklist()
+ url_lookup()
+ url_lookup()

## Paginators

Paginators can be used to abstract the process of iterating over truncated API results. The get_paginator() method takes the method name (see Available methods above) and returns a Paginator object.

The paginate() method of the Paginator takes in any arguments accepted by the Cymon method and returns an iterator.

Example:

```
from cymon import Cymon

cymon = Cymon()
paginator = cymon.get_paginator('ip_blacklist')

pages = paginator.paginate('botnet', days=2)
for page in pages:
for item in page:
print item['addr']
```
55 changes: 54 additions & 1 deletion cymon/cymon.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import requests
from urllib import quote_plus


class Cymon(object):

def __init__(self, auth_token=None, endpoint='https://cymon.io/api/nexus/v1'):
Expand All @@ -24,6 +25,25 @@ def post(self, method, params, headers=None):
r.raise_for_status()
return r

def get_paginator(self, method):
"""
Returns a Paginator class to use for handling API pagination.
"""
method = method.lower()
if self._can_paginate(method):
return Paginator(self, method)
else:
raise NoPaginatorError('Cannot paginate {} method'.format(method))

def _can_paginate(self, method):
"""
Basic check to raise exception when method cannot paginate.
"""
if method in ['ip_blacklist', 'domain_blacklist']:
return True
else:
return False

def ip_lookup(self, ip_addr):
r = self.get('/ip/' + ip_addr)
return json.loads(r.text)
Expand All @@ -36,7 +56,7 @@ def ip_domains(self, ip_addr):
r = self.get('/ip/' + ip_addr + '/domains')
return json.loads(r.text)

def ip_urls(self, ip_addr):
def ip_urls(self, ip_addr):
r = self.get('/ip/' + ip_addr + '/urls')
return json.loads(r.text)

Expand All @@ -57,3 +77,36 @@ def domain_blacklist(self, tag, days=1, limit=10, offset=0):
''' supported tags: malware, botnet, spam, phishing, dnsbl, blacklist '''
r = self.get('/blacklist/domain/' + tag + '/?days=%d&limit=%d&offset=%d' %(days,limit,offset))
return json.loads(r.text)


class Paginator(object):
"""
This class uses generators to provide an iterable object for performing
recusive API calls when a result has been paginated.
"""
def __init__(self, cymon, method):
self.cymon = cymon
self.method = method

def paginate(self, *args, **kwargs):
"""
Use Cymon client object to make recursive API calls when
result is paginated.
"""
has_next = False
method_to_call = getattr(self.cymon, self.method)
result = method_to_call(limit=100, *args, **kwargs)
if result['next'] is not None:
has_next = True
yield result['results'] # intial API call to start recursion

while has_next:
resp = requests.get(result['next'])
result = json.loads(resp.text)
if result['next'] is None:
has_next = False
yield result['results']


class NoPaginatorError(Exception):
pass