Skip to content

Commit f8b71f6

Browse files
authored
Merge pull request #64 from Worvast/master
Version v3 final
2 parents 1886247 + 6303a4c commit f8b71f6

File tree

20 files changed

+602
-398
lines changed

20 files changed

+602
-398
lines changed

.travis.yml

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
language: python
22
python:
3-
- '2.7'
43
- '3.5'
54
install:
65
- pip install .
76
- pip install pyyaml
87
script:
98
- python run_tests.py
10-
- sonar-scanner
119
deploy:
1210
- provider: pypi
1311
user: worvast
@@ -21,7 +19,4 @@ deploy:
2119
branch: master
2220
distributions: sdist bdist_wheel
2321
skip_existing: true
24-
skip_cleanup: true
25-
addons:
26-
sonarcloud:
27-
organization: "devoinc"
22+
skip_cleanup: true

CHANGELOG.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,31 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
66

77

88
## [3.0.0] - 2019-04-26
9+
#### Removed
10+
* Support to Python 2.7
11+
* Configuration class "key_exist()" and "keys()" functions
12+
* Api and Sender "from_config" are now removed and included in Class init flag
13+
914
#### Added
1015
* for_logging option in Sender, added more values
1116
* Logging streamhandler
1217
* Tests for CLI
1318
* Have Client query method accept Unix timestamps for to and from dates
1419
* Make Client parameters retries, timeout, and sleep configurable
20+
* CLI of devo-sdk now has --version flag.
1521

1622
#### Changed
1723
* Errors exceptions in API and Sender are now full controlled and homogenized
1824
* logging in Common now are created more flexible
1925
* Sender init class, SSLSender and TCPSender too.
2026
* API init class
21-
* Config.from_config to Config.from_dict in API and Sender
2227
* New documentation
28+
* Now devo Configuration object inherits from the dict class
29+
* Configuration set and get are now more dict homogenized
30+
* vars/flags "url" are now like python: "address" and are now tuples
31+
* New "auth" flag to encapsulate key, secret, jwt and token
32+
* Env var "DEVO_AUTH_TOKEN" to "DEVO_API_TOKEN"
33+
* Env var "DEVO_API_URL" to "DEVO_API_ADDRESS"
2334

2435
#### Fixed
2536
* Fixed when add key chain to configuration

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ You can use sources files, clonning the project too:
4646

4747
You has specific documentation in _[docs](docs)_ folder for each part of SDK:
4848
* [Sender](docs/sender.md)
49-
* [Lookup](docs/sender.md#Lookup)
49+
* [Lookups](docs/sender.md#Lookups)
5050
* [Common](docs/common.md)
5151
* API:
5252
* [Api query](docs/api/api.md)

devo/api/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
from .client import Client, DevoClientException, DEFAULT, TO_STR, \
22
TO_BYTES, JSON, JSON_SIMPLE, COMPACT_TO_ARRAY, \
3-
SIMPLECOMPACT_TO_ARRAY, SIMPLECOMPACT_TO_OBJ, Options
3+
SIMPLECOMPACT_TO_ARRAY, SIMPLECOMPACT_TO_OBJ, ClientConfig

devo/api/client.py

Lines changed: 112 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import json
77
import requests
88
from devo.common import default_from, default_to
9-
from .processors import processors, proc_json, proc_default, \
9+
from .processors import processors, proc_json, \
1010
json_compact_simple_names, proc_json_compact_simple_to_jobj
1111

1212
CLIENT_DEFAULT_APP_NAME = 'python-sdk-app'
@@ -41,7 +41,10 @@ def raise_exception(error):
4141
if isinstance(error, str):
4242
raise DevoClientException(proc_json()(error))
4343
elif isinstance(error, DevoClientException):
44-
raise DevoClientException(error.args[0])
44+
if isinstance(error.args[0], str):
45+
raise DevoClientException(proc_json()(error.args[0]))
46+
else:
47+
raise DevoClientException(error.args[0])
4548
elif isinstance(error, dict):
4649
raise DevoClientException(error)
4750
else:
@@ -56,84 +59,131 @@ def _format_error(error):
5659
'"object": "%s"}' % str(error).replace("\"", "\\\"")
5760

5861

59-
class Options:
62+
class ClientConfig:
63+
"""
64+
Main class for configuration of Client class. With diferent configurations
65+
"""
6066
def __init__(self, processor=DEFAULT, response="json/simple/compact",
61-
destination=None, stream=True):
62-
67+
destination=None, stream=True, pragmas=None):
68+
"""
69+
Initialize the API with this params, all optionals
70+
:param processor: processor for response, default is None
71+
:param response: format of response
72+
:param destination: Destination options, see Documentation for more info
73+
:param stream: Stream queries or not
74+
:param pragmas: pragmas por query: user, app_name and comment
75+
"""
6376
self.stream = stream
6477
self.response = response
6578
self.destination = destination
6679
self.proc = processor
6780
self.processor = processors()[self.proc]()
81+
if pragmas:
82+
self.pragmas = pragmas
83+
if "user" not in self.pragmas.keys():
84+
self.pragmas['user'] = CLIENT_DEFAULT_USER
85+
if "app_name" not in self.pragmas.keys():
86+
self.pragmas['app_name'] = CLIENT_DEFAULT_APP_NAME
87+
else:
88+
self.pragmas = {"user": CLIENT_DEFAULT_USER,
89+
"app_name": CLIENT_DEFAULT_APP_NAME}
6890

6991
def set_processor(self, processor=None):
92+
"""
93+
Set processor of response
94+
:param processor: lambda function
95+
:return:
96+
"""
7097
if processor:
7198
self.proc = processor
7299
self.processor = processors()[self.proc]()
73100
return True
74101

102+
def set_user(self, user=CLIENT_DEFAULT_USER):
103+
"""
104+
Set user to put in pragma when make the query
105+
:param user: username string
106+
:return:
107+
"""
108+
self.pragmas['user'] = user
109+
return True
110+
111+
def set_app_name(self, app_name=CLIENT_DEFAULT_APP_NAME):
112+
113+
"""
114+
Set app_name to put in pragma when make the query
115+
:param app_name: app_name string
116+
:return:
117+
"""
118+
self.pragmas['app_name'] = app_name
119+
return True
120+
75121

76122
class Client:
77123
"""
78-
The Devo SERach REst Api main class
124+
The Devo seach rest api main class
79125
"""
80-
def __init__(self, address=None, auth=None, options=None,
81-
retries=3, timeout=30):
126+
def __init__(self, address=None, auth=None, config=None,
127+
retries=None, timeout=None):
82128
"""
83129
Initialize the API with this params, all optionals
84130
:param address: endpoint
85131
:param auth: object with auth params (key, secret, token, jwt)
132+
:param options: options class for Client and queries
86133
:param retries: number of retries for a query
87134
:param timeout: timeout of socket
88135
"""
89-
self.auth = auth
136+
if config is None:
137+
self.config = ClientConfig()
138+
elif isinstance(config, ClientConfig):
139+
self.config = config
140+
else:
141+
address = address if address else config.get("address", None)
142+
auth = auth if auth else config.get("auth",
143+
{"key": config.get("key", None),
144+
"secret": config.get("secret",
145+
None),
146+
"jwt": config.get("jwt", None),
147+
"token": config.get("token",
148+
None)})
149+
150+
retries = retries if retries else config.get("retries", 3)
151+
timeout = timeout if retries else config.get("retries", 30)
152+
self.config = self._from_dict(config)
153+
154+
retries = retries if retries else 3
155+
timeout = timeout if timeout else 30
90156

157+
self.auth = auth
91158
if not address:
92159
raise raise_exception(
93160
_format_error(ERROR_MSGS['no_endpoint'])
94161
)
95162

96163
self.address = self.__get_address_parts(address)
97164

98-
self.pragmas = {"user": CLIENT_DEFAULT_USER,
99-
"app_name": CLIENT_DEFAULT_APP_NAME}
100-
101-
self.opts = Options() if not options else options
102-
103165
self.retries = retries
104166
self.timeout = timeout
105167
self.verify = True
106168

107-
def set_user(self, user=CLIENT_DEFAULT_USER):
108-
self.pragmas['user'] = user
109-
return True
110-
111-
def set_app_name(self, app_name=CLIENT_DEFAULT_APP_NAME):
112-
self.pragmas['app_name'] = app_name
113-
return True
114-
115169
@staticmethod
116-
def from_dict(config):
170+
def _from_dict(config):
117171
"""
118172
Create Client object from config file values
119173
:param config: lt-common config standar
120174
"""
121-
options = Options(processor=config.get("processor", DEFAULT),
122-
response=config.get("response", "json/simple/compact")
123-
,destination=config.get("destination", None),
124-
stream=config.get("stream", True))
175+
return ClientConfig(processor=config.get("processor", DEFAULT),
176+
response=config.get("response",
177+
"json/simple/compact"),
178+
destination=config.get("destination", None),
179+
stream=config.get("stream", True))
125180

126-
if "auth" in config.keys():
127-
return Client(address=config.get("address"),
128-
auth=config.get("auth"),
129-
options=options)
130-
else:
131-
return Client(address=config.get("address"),
132-
auth={"key": config.get("key", None),
133-
"secret": config.get("secret", None),
134-
"jwt": config.get("jwt", None),
135-
"token": config.get("token", None)},
136-
options=options)
181+
def verify_certificates(self, option=True):
182+
"""
183+
Set if verify or not the TSL certificates in https calls
184+
:param option: (bool) True or False
185+
"""
186+
self.verify = option
137187

138188
def __get_address_parts(self, address):
139189
"""
@@ -187,18 +237,18 @@ def _is_correct_response(line):
187237
except ValueError:
188238
return False
189239

190-
def options(self, processor=None, response=None, stream=None,
191-
destination=None):
240+
def configurate(self, processor=None, response=None, stream=None,
241+
destination=None):
192242

193-
self.opts.set_processor(processor)
243+
self.config.set_processor(processor)
194244
if response:
195-
self.opts.response = response
245+
self.config.response = response
196246

197247
if stream:
198-
self.opts.stream = stream
248+
self.config.stream = stream
199249

200250
if destination:
201-
self.opts.destination = destination
251+
self.config.destination = destination
202252

203253
def query(self, query=None, query_id=None, dates=None,
204254
limit=None, offset=None, comment=None):
@@ -210,24 +260,24 @@ def query(self, query=None, query_id=None, dates=None,
210260
:param limit: Max number of rows
211261
:param offset: start of needle for query
212262
:param comment: comment for query
213-
:return: Result of the query (dict) or Buffer object
263+
:return: Result of the query (dict) or Iterator object
214264
"""
215265
dates = self._generate_dates(dates)
216266

217267
if query is not None:
218268
query += self._generate_pragmas(comment=comment)
219269

220270
query_opts = {'limit': limit,
221-
'response': self.opts.response,
271+
'response': self.config.response,
222272
'offset': offset,
223-
'destination': self.opts.destination
273+
'destination': self.config.destination
224274
}
225275

226-
if not self.stream_available(self.opts.response) \
227-
or not self.opts.stream:
276+
if not self.stream_available(self.config.response) \
277+
or not self.config.stream:
228278
if not dates['to']:
229279
dates['to'] = "now()"
230-
self.opts.stream = False
280+
self.config.stream = False
231281

232282
return self._call(
233283
self._get_payload(query, query_id, dates, query_opts)
@@ -240,13 +290,13 @@ def _call(self, payload):
240290
:param stream: boolean, indicate if one call is a streaming call
241291
:return: Response from API
242292
"""
243-
if self.opts.stream:
293+
if self.config.stream:
244294
return self._return_stream(payload)
245295
response = self._make_request(payload)
246296

247297
if isinstance(response, str):
248298
return proc_json()(response)
249-
return self.opts.processor(response.text)
299+
return self.config.processor(response.text)
250300

251301
def _return_stream(self, payload):
252302
"""If its a stream call, return yield lines
@@ -262,15 +312,15 @@ def _return_stream(self, payload):
262312
raise_exception(response)
263313

264314
if self._is_correct_response(first):
265-
if self.opts.proc == SIMPLECOMPACT_TO_OBJ:
315+
if self.config.proc == SIMPLECOMPACT_TO_OBJ:
266316
aux = json_compact_simple_names(proc_json()(first)['m'])
267-
self.opts.processor = proc_json_compact_simple_to_jobj(aux)
268-
elif self.opts.proc == SIMPLECOMPACT_TO_ARRAY:
317+
self.config.processor = proc_json_compact_simple_to_jobj(aux)
318+
elif self.config.proc == SIMPLECOMPACT_TO_ARRAY:
269319
pass
270320
else:
271-
yield self.opts.processor(first)
321+
yield self.config.processor(first)
272322
for line in response:
273-
yield self.opts.processor(line)
323+
yield self.config.processor(line)
274324
else:
275325
yield proc_json()(first)
276326

@@ -289,11 +339,11 @@ def _make_request(self, payload):
289339
headers=self._get_headers(payload),
290340
verify=self.verify,
291341
timeout=self.timeout,
292-
stream=self.opts.stream)
342+
stream=self.config.stream)
293343
if response.status_code != 200:
294344
raise DevoClientException(response)
295345

296-
if self.opts.stream:
346+
if self.config.stream:
297347
return response.iter_lines()
298348
return response
299349
except requests.exceptions.ConnectionError as error:
@@ -402,7 +452,8 @@ def _generate_pragmas(self, comment=None):
402452
"""
403453
str_pragmas = ' pragma comment.id:"{}" ' \
404454
'pragma comment.user:"{}"'\
405-
.format(self.pragmas['app_name'], self.pragmas['user'])
455+
.format(self.config.pragmas['app_name'],
456+
self.config.pragmas['user'])
406457

407458
if comment:
408459
return str_pragmas + ' pragma comment.free:"{}"'.format(comment)
@@ -460,7 +511,7 @@ def _call_jobs(self, address):
460511
response = None
461512
try:
462513
response = requests.get("https://{}".format(address),
463-
headers=self._get_jobs_headers(),
514+
headers=self._get_headers(""),
464515
verify=self.verify,
465516
timeout=self.timeout)
466517
except ConnectionError as error:
@@ -478,10 +529,3 @@ def _call_jobs(self, address):
478529
tries += 1
479530
time.sleep(self.timeout)
480531
return {}
481-
482-
def _get_jobs_headers(self):
483-
tstamp = str(int(time.time()) * 1000)
484-
return {'x-logtrust-timestamp': tstamp,
485-
'x-logtrust-apikey': self.auth.get("key"),
486-
'x-logtrust-sign': self._get_sign("", tstamp)
487-
}

0 commit comments

Comments
 (0)