From bb7569e3bc9add89bbd61f5a41596e9e3b4f0867 Mon Sep 17 00:00:00 2001 From: Petar Vlahu Date: Mon, 24 Nov 2014 15:45:33 -0600 Subject: [PATCH 1/4] fix for queries with no filters added a code to check whether the user has suplied filters and if not reshape the looker api url so it is correctly signed added test case for queries with no filters --- looker/client.py | 5 ++++- sample.py | 14 ++++++++++---- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/looker/client.py b/looker/client.py index f8e4bfc..761ac32 100644 --- a/looker/client.py +++ b/looker/client.py @@ -57,7 +57,10 @@ def __query_params(self): filters_list = [] for key, value in self.filters.iteritems(): filters_list.append("filters[%s]=%s" % (str(key).lower(), urllib.quote_plus(str(value)))) - return "fields=%s&%s&limit=%i" % (fields_string, "&".join(sorted(filters_list)), self.limit) + if not len(filters_list) == 0: + return "fields=%s&%s&limit=%i" % (fields_string, "&".join(sorted(filters_list)), self.limit) + else: + return "fields=%s&limit=%i" % (fields_string, self.limit) def __headers(self, uri): today = email.Utils.formatdate(localtime=True) diff --git a/sample.py b/sample.py index 1a5aacd..b48b8e5 100644 --- a/sample.py +++ b/sample.py @@ -4,19 +4,25 @@ client = LookerClient('Mkz9GRYoIhyuJ898YG89Ig', 'v1+MNxMg1vdmljYbtBhEDFEQSlAUEZd4xWd', 'https://demo.looker.com') +# create a query object no filters +query1 = client.query('orders', + 'thelook', + ['orders.count', 'users.count'], + {}) # create a query object -query1 = client.query('orders', +query2 = client.query('orders', 'thelook', ['orders.count', 'users.count'], {'users.state': '-%New%', 'orders.created_date': '90 days'}) # create another query object -query2 = client.query('orders', 'thelook', +query3 = client.query('orders', 'thelook', ['orders.count'], {'users.created_date': '90 days'}) -query2.add_filters({'orders.created_date': '90 days'}) +query3.add_filters({'orders.created_date': '90 days'}) print query1.run() -print query2.run() \ No newline at end of file +print query2.run() +print query3.run() From 1bbfa0d53c3ca3ed43a2a285da26c38e045306c8 Mon Sep 17 00:00:00 2001 From: Petar Vlahu Date: Mon, 24 Nov 2014 15:49:03 -0600 Subject: [PATCH 2/4] removed sorting on fields per looker api the fields order will determine the order of returned fields --- looker/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/looker/client.py b/looker/client.py index 761ac32..407e94e 100644 --- a/looker/client.py +++ b/looker/client.py @@ -53,7 +53,7 @@ def add_filters(self, filters): ## private methods ## def __query_params(self): - fields_string = ",".join(sorted([field.lower() for field in self.fields])) + fields_string = ",".join([field.lower() for field in self.fields]) filters_list = [] for key, value in self.filters.iteritems(): filters_list.append("filters[%s]=%s" % (str(key).lower(), urllib.quote_plus(str(value)))) From ba415622f6bafad5827b30a364d9af4319052ee0 Mon Sep 17 00:00:00 2001 From: Paul Tindall Date: Tue, 23 Dec 2014 17:43:16 -0600 Subject: [PATCH 3/4] Force SSL to use TLSv1 to mitigate POODLE issue. Looker version 3.6 and above only allows TLSv1. --- looker/client.py | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/looker/client.py b/looker/client.py index 407e94e..547669e 100644 --- a/looker/client.py +++ b/looker/client.py @@ -9,8 +9,20 @@ import json import email -class LookerClient(object): +from requests.adapters import HTTPAdapter +from requests.packages.urllib3.poolmanager import PoolManager +import ssl + + +class TLSv1Adapter(HTTPAdapter): + def init_poolmanager(self, connections, maxsize, block=False): + self.poolmanager = PoolManager(num_pools=connections, + maxsize=maxsize, + block=block, + ssl_version=ssl.PROTOCOL_TLSv1) + +class LookerClient(object): def __init__(self, token, secret, host, port=443): self.token = token self.secret = secret @@ -22,7 +34,6 @@ def query(self, query, dictionary, fields, filters=None, limit=1000, output='jso class Query(object): - # only support for JSON and GET def __init__(self, credentials, query, dictionary, fields, filters=None, limit=1000, output='json', method='GET'): self.credentials = credentials @@ -39,7 +50,12 @@ def run(self): uri = "/api/dictionaries/%s/queries/%s.%s" % ( self.dictionary, self.query, self.output) url = "%s%s?%s" % (self.credentials.host, uri, self.__query_params()) - r = requests.get(url, headers=self.__headers(uri)) + + # as of Looker 3.6x, TLSv1 support is only option, but requests library needs explicit override to make + # ssl library use TLSv1 + s = requests.Session() + s.mount('https://', TLSv1Adapter()) + r = s.get(url, headers=self.__headers(uri)) return json.loads(r.text) def set_output(self, output): @@ -57,8 +73,8 @@ def __query_params(self): filters_list = [] for key, value in self.filters.iteritems(): filters_list.append("filters[%s]=%s" % (str(key).lower(), urllib.quote_plus(str(value)))) - if not len(filters_list) == 0: - return "fields=%s&%s&limit=%i" % (fields_string, "&".join(sorted(filters_list)), self.limit) + if not len(filters_list) == 0: + return "fields=%s&%s&limit=%i" % (fields_string, "&".join(sorted(filters_list)), self.limit) else: return "fields=%s&limit=%i" % (fields_string, self.limit) From 0c3cc5842cbae13631137eade0102dacb3ab6ea7 Mon Sep 17 00:00:00 2001 From: Paul Tindall Date: Tue, 23 Dec 2014 17:48:20 -0600 Subject: [PATCH 4/4] update to match forked status --- README.md | 2 +- setup.py | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 9cec21f..d904371 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Looker SDK for Python A Python library for Lookers's HTTP-based APIs. -- https://github.com/sdhoover/Python_SDK +- https://github.com/transverse/looker-python-sdk Setup ----- diff --git a/setup.py b/setup.py index 1175e8a..0c02923 100644 --- a/setup.py +++ b/setup.py @@ -1,11 +1,11 @@ from setuptools import setup -setup(name='looker_sdk', - version='0.1', +setup(name='looker-python-sdk', + version='0.2', description='Python SDK for pulling data from Looker', - url='https://github.com/llooker/python_sdk', - author='Scott Hoover', - author_email='scott@looker.com', + url='https://github.com/transverse/looker-python-sdk', + author='Transverse, LLC', + author_email='info@gotransverse.com', license='MIT', packages=['looker'], install_requires=['requests'],