Skip to content

Commit 384f0c7

Browse files
authored
Merge pull request #35 from passivetotal/context-headers
Send new request headers for metrics and troubleshooting.
2 parents 9cb234c + cad4374 commit 384f0c7

6 files changed

Lines changed: 98 additions & 11 deletions

File tree

CHANGELOG.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,21 @@
11
# Changelog
22

3+
## v2.5.2
4+
5+
#### Enhancements
6+
7+
- Send new request headers for metrics and troubleshooting with the `set_context`
8+
method on the `analyzer` module and within the core API request libs.
9+
- Abstract package version into a distinct file to consolidate updates and ensure
10+
consistency across docs and pypi. Add `get_version` method to `analyzer` module
11+
for easy access to the current version number.
12+
13+
14+
#### Bug Fixes
15+
16+
17+
18+
319
## v2.5.1
420

521
#### Enhancements

docs/conf.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import sys
1616
import os
17+
import re
1718

1819
# If extensions (or modules to document with autodoc) are in another directory,
1920
# add these directories to sys.path here. If the directory is relative to the
@@ -55,14 +56,12 @@
5556
copyright = u'2021, RiskIQ'
5657
author = u'RiskIQ'
5758

58-
# The version info for the project you're documenting, acts as replacement for
59-
# |version| and |release|, also used in various other places throughout the
60-
# built documents.
61-
#
62-
# The short X.Y version.
63-
version = '2.5'
64-
# The full version, including alpha/beta/rc tags.
65-
release = '2.5.1'
59+
# pylint: disable=locally-disabled, invalid-name
60+
with open('../passivetotal/_version.py', 'r') as fd:
61+
v_match = re.search(r'^VERSION\s*=\s*[\'"]([^\'"]*)[\'"]', fd.read(), re.MULTILINE)
62+
release = v_match.group(1)
63+
version = '.'.join(release.split('.')[0:-1])
64+
# pylint: enable=locally-disabled, invalid-name
6665

6766
# The language for content autogenerated by Sphinx. Refer to documentation
6867
# for a list of supported languages.

passivetotal/_version.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
VERSION="2.5.2"

passivetotal/analyzer/__init__.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
from collections import namedtuple
44
from datetime import datetime, timezone, timedelta
55
from passivetotal import *
6+
from passivetotal._version import VERSION
7+
from passivetotal.api import Context
68
from passivetotal.analyzer._common import AnalyzerError, AnalyzerAPIError, is_ip
79

810
DEFAULT_DAYS_BACK = 90
@@ -56,6 +58,7 @@ def init(**kwargs):
5658
else:
5759
api_clients[name] = c.from_config()
5860
api_clients[name].exception_class = AnalyzerAPIError
61+
api_clients[name].set_context('python','passivetotal',VERSION,'analyzer')
5962
config['is_ready'] = True
6063

6164
def get_api(name):
@@ -91,6 +94,23 @@ def get_object(input, type=None):
9194
raise AnalyzerError('type must be IPAddress or Hostname')
9295
return objs[type](input)
9396

97+
def get_version():
98+
"""Get the current version of this package."""
99+
return VERSION
100+
101+
def set_context(provider, variant, version, feature=''):
102+
"""Define the application context for an implementation using the analyzer module.
103+
104+
Sets a header to be sent in API requests that is used for metrics and troubleshooting.
105+
106+
:param provider: The company, partner, provider or other top-level application context.
107+
:param variant: The specific app, libary subcomponent, or feature category.
108+
:param version: Version of the app, feature or code setting the context.
109+
:param feature: Optional sub-feature, dashboard or script name.
110+
"""
111+
for client in api_clients.values():
112+
client.set_context(provider, variant, version, feature)
113+
94114
def set_date_range(days_back=DEFAULT_DAYS_BACK, start=None, start_date=None, end=None, end_date=None):
95115
"""Set a range of dates for all date-bounded API queries.
96116

passivetotal/api.py

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
"""PassiveTotal API Interface."""
22

3-
__author__ = 'Brandon Dixon (PassiveTotal)'
4-
__version__ = '1.0.0'
53

64
import json
75
import logging
86
import requests
97
import sys
8+
from urllib.parse import quote as urlquote
109
from passivetotal.config import Config
10+
from passivetotal._version import VERSION
11+
12+
__author__ = 'Brandon Dixon (PassiveTotal)'
13+
__version__ = VERSION
14+
1115

1216

1317
class Client(object):
@@ -58,6 +62,7 @@ def __init__(self, username, api_key, server=DEFAULT_SERVER,
5862
if '127.0.0.1' in server:
5963
self.verify = False
6064
self.exception_class = exception_class
65+
self.set_context('python','passivetotal',VERSION)
6166

6267
@classmethod
6368
def from_config(cls):
@@ -78,6 +83,18 @@ def set_debug(self, status):
7883
self.logger.setLevel('DEBUG')
7984
else:
8085
self.logger.setLevel('INFO')
86+
87+
def set_context(self, provider, variant, version, feature=''):
88+
"""Set the context for this request.
89+
90+
:param provider: The company, partner, provider or other top-level application context.
91+
:param variant: The specific app, libary subcomponent, or feature category.
92+
:param version: Version of the app, feature or code setting the context.
93+
:param feature: Optional sub-feature, dashboard or script name.
94+
"""
95+
context = Context(provider, variant, version, feature)
96+
self.context = context
97+
self.headers.update(context.get_header())
8198

8299
def _endpoint(self, endpoint, action, *url_args):
83100
"""Return the URL for the action.
@@ -181,3 +198,30 @@ def _send_data(self, method, endpoint, action,
181198
kwargs['proxies'] = self.proxies
182199
response = requests.request(method, api_url, **kwargs)
183200
return self._json(response)
201+
202+
203+
204+
class Context:
205+
206+
"""Integration context for a set of API requests."""
207+
208+
HEADER_NAME = 'X-RISKIQ-CONTEXT'
209+
210+
def __init__(self, provider, variant, version, feature = ''):
211+
"""Build a new context header.
212+
213+
:param provider: The company, partner, provider or other top-level application context.
214+
:param variant: The specific app, libary subcomponent, or feature category.
215+
:param version: Version of the app, feature or code setting the context.
216+
:param feature: Optional sub-feature, dashboard or script name.
217+
"""
218+
self._fields = (provider, variant, version, feature)
219+
220+
def get_header_name(self):
221+
return self.HEADER_NAME
222+
223+
def get_header_value(self):
224+
return '/'.join(map(lambda f: urlquote(f, safe=''), self._fields))
225+
226+
def get_header(self):
227+
return { self.get_header_name() : self.get_header_value() }

setup.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,21 @@
11
#!/usr/bin/env python
22
import os
3+
import re
34
from setuptools import setup, find_packages
45

56

67
def read(fname):
78
return open(os.path.join(os.path.dirname(__file__), fname)).read()
89

10+
# pylint: disable=locally-disabled, invalid-name
11+
with open('passivetotal/_version.py', 'r') as fd:
12+
v_match = re.search(r'^VERSION\s*=\s*[\'"]([^\'"]*)[\'"]', fd.read(), re.MULTILINE)
13+
__version__ = v_match.group(1) if v_match else 'no version'
14+
# pylint: enable=locally-disabled, invalid-name
15+
916
setup(
1017
name='passivetotal',
11-
version='2.5.1',
18+
version=__version__,
1219
description='Library for the RiskIQ PassiveTotal and Illuminate API',
1320
url="https://github.com/passivetotal/python_api",
1421
author="RiskIQ",

0 commit comments

Comments
 (0)