Skip to content

Commit

Permalink
Merge pull request #67 from GetStream/feature/clean-urls
Browse files Browse the repository at this point in the history
Feature/clean urls
  • Loading branch information
tbarbugli committed Dec 13, 2017
2 parents 0a38299 + 036e69f commit 88572d2
Show file tree
Hide file tree
Showing 4 changed files with 162 additions and 50 deletions.
73 changes: 41 additions & 32 deletions stream/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,15 @@
from stream.utils import validate_feed_slug, validate_user_id
from requests import Request

try:
from urllib.parse import urlparse
except ImportError:
from urlparse import urlparse

logger = logging.getLogger(__name__)


class StreamClient(object):
base_url = 'https://api.stream-io-api.com/api/'

def __init__(self, api_key, api_secret, app_id, version='v1.0', timeout=6.0, base_url=None, location=None):
'''
Expand Down Expand Up @@ -51,19 +54,28 @@ def __init__(self, api_key, api_secret, app_id, version='v1.0', timeout=6.0, bas
self.timeout = timeout
self.location = location

self.base_domain_name = 'stream-io-api.com'
self.api_location = location
self.custom_api_port = None
self.protocol = 'https'

if os.environ.get('LOCAL'):
self.base_url = 'http://localhost:8000/api/'
self.base_domain_name = 'localhost'
self.protocol = 'http'
self.custom_api_port = 8000
self.timeout = 20
elif base_url is not None:
self.base_url = base_url
parsed_url = urlparse(base_url)
self.base_domain_name = parsed_url.hostname
self.protocol = parsed_url.scheme
self.custom_api_port = parsed_url.port
self.api_location = ""
elif location is not None:
self.base_url = 'https://%s-api.stream-io-api.com/api/' % location
self.location = location

self.base_analytics_url = 'https://analytics.stream-io-api.com/analytics/'

self.session = requests.Session()
# TODO: turn this back on after we verify it doesnt retry on slower requests
self.session.mount(self.base_url, HTTPAdapter(max_retries=0))
self.auth = HTTPSignatureAuth(api_key, secret=api_secret)

# setup personalization
Expand Down Expand Up @@ -107,23 +119,23 @@ def get_default_header(self):
}
return base_headers

def get_full_url(self, relative_url):
url = self.base_url + self.version + '/' + relative_url
return url

def get_full_personal_url(self, relative_url):
base_url = self.base_url.split('.') # company.getstream.io
if len(base_url) > 1:
DNS_change = base_url[0] + '-personalization'
base_url[0] = DNS_change
base_url = '.'.join(base_url)
def get_full_url(self, service_name, relative_url):
if self.api_location:
hostname = '%s-%s.%s' % (self.api_location, service_name, self.base_domain_name)
elif service_name:
hostname = '%s.%s' % (service_name, self.base_domain_name)
else:
base_url = self.base_url # if running on localhost
url = base_url + 'personalization/' + relative_url + '/'
return url
hostname = self.base_domain_name

if self.base_domain_name == 'localhost':
hostname = 'localhost'

base_url = "%s://%s" % (self.protocol, hostname)

def get_full_meta_url(self):
url = self.base_url + 'personalization/' + self.version + '/meta/'
if self.custom_api_port:
base_url = "%s:%s" % (base_url, self.custom_api_port)

url = base_url + '/' + service_name + '/' + self.version + '/' + relative_url
return url

def get_user_agent(self):
Expand All @@ -150,7 +162,7 @@ def _make_signed_request(self, method_name, relative_url, params=None, data=None
headers['Date'] = date_header
default_params = self.get_default_params()
default_params.update(params)
url = self.get_full_url(relative_url)
url = self.get_full_url('api', relative_url)
serialized = serializer.dumps(data)
method = getattr(self.session, method_name)
if method_name in ['post', 'put']:
Expand All @@ -176,7 +188,7 @@ def create_jwt_token(self, resource, action, feed_id=None, user_id=None):
payload['user_id'] = user_id
return jwt.encode(payload, self.api_secret).decode("utf-8")

def _make_request(self, method, relative_url, signature, personal=None, params=None, data=None):
def _make_request(self, method, relative_url, signature, service_name='api', params=None, data=None):
params = params or {}
data = data or {}
serialized = None
Expand All @@ -185,15 +197,12 @@ def _make_request(self, method, relative_url, signature, personal=None, params=N
headers = self.get_default_header()
headers['Authorization'] = signature
headers['stream-auth-type'] = 'jwt'
if personal is not None:
if personal == 'personal':
url = self.get_full_personal_url(relative_url)
elif personal == 'meta':
url = self.get_full_meta_url()
else:
raise Exception("keyword 'personal' must be None, personal, or meta")
else:
url = self.get_full_url(relative_url)

if not relative_url.endswith('/'):
relative_url += '/'

url = self.get_full_url(service_name, relative_url)

if method.__name__ in ['post', 'put', 'delete']:
serialized = serializer.dumps(data)
response = method(url, data=serialized, headers=headers,
Expand Down
7 changes: 4 additions & 3 deletions stream/collections.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
class Collections(object):

def __init__(self, client, token):
"""
Used to manipulate data at the 'meta' endpoint
Expand Down Expand Up @@ -26,7 +27,7 @@ def upsert(self, collection_name, data):

data_json = {collection_name: data}

response = self.client.post('meta', personal='meta',
response = self.client.post('meta/', service_name='api',
signature=self.token, data={'data': data_json})
return response

Expand All @@ -52,7 +53,7 @@ def select(self, collection_name, ids):
foreign_ids.append('%s:%s' % (collection_name, ids[i]))
foreign_ids = ','.join(foreign_ids)

response = self.client.get('meta', personal='meta', params={'foreign_ids': foreign_ids},
response = self.client.get('meta/', service_name='api', params={'foreign_ids': foreign_ids},
signature=self.token)

return response
Expand All @@ -75,7 +76,7 @@ def delete(self, collection_name, ids):

data = {'collection_name': collection_name, 'ids': ids}

response = self.client.delete('meta', personal='meta', data=data,
response = self.client.delete('meta/', service_name='api', data=data,
signature=self.token)

return response
6 changes: 3 additions & 3 deletions stream/personalization.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def get(self, resource, **params):
personalization.get('follow_recommendations', user_id=123, limit=10, offset=10)
"""

response = self.client.get(resource, personal='personal', params=params,
response = self.client.get(resource, service_name='personalization', params=params,
signature=self.token)
return response

Expand All @@ -39,7 +39,7 @@ def post(self, resource, **params):

data = params['data'] or None

response = self.client.post(resource, personal='personal', params=params,
response = self.client.post(resource, service_name='personalization', params=params,
signature=self.token, data=data)
return response

Expand All @@ -51,7 +51,7 @@ def delete(self, resource, **params):
:return: data that was deleted if if successful or not.
"""

response = self.client.delete(resource, personal='personal', params=params,
response = self.client.delete(resource, service_name='personalization', params=params,
signature=self.token)

return response
126 changes: 114 additions & 12 deletions stream/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,8 @@ def connect_debug():
return stream.connect(
key,
secret,
location='us-east',
location='qa',
timeout=30,
base_url='https://qa.stream-io-api.com/api/',
# base_url='http://localhost-api.getstream.io:8000/api/',
)

client = connect_debug()
Expand Down Expand Up @@ -120,6 +118,105 @@ def _test_sleep(self, production_wait, local_wait):
sleep_time = local_wait
time.sleep(sleep_time)

def test_collections_url(self):
feed_url = client.get_full_url(relative_url='meta/', service_name='api')

if self.local_tests:
self.assertEqual(
feed_url, 'http://localhost:8000/api/v1.0/meta/')
else:
self.assertEqual(
feed_url, 'https://qa-api.stream-io-api.com/api/v1.0/meta/')

def test_personalization_url(self):
feed_url = client.get_full_url(relative_url='recommended', service_name='personalization')

if self.local_tests:
self.assertEqual(
feed_url, 'http://localhost:8000/personalization/v1.0/recommended')
else:
self.assertEqual(
feed_url, 'https://qa-personalization.stream-io-api.com/personalization/v1.0/recommended')

def test_api_url(self):
feed_url = client.get_full_url(service_name='api', relative_url='feed/')

if self.local_tests:
self.assertEqual(
feed_url, 'http://localhost:8000/api/v1.0/feed/')
else:
self.assertEqual(
feed_url, 'https://qa-api.stream-io-api.com/api/v1.0/feed/')

def test_collections_url_default(self):
client = stream.connect(
'key',
'secret',
)
feed_url = client.get_full_url(relative_url='meta/', service_name='api')

if not self.local_tests:
self.assertEqual(
feed_url, 'https://api.stream-io-api.com/api/v1.0/meta/')

def test_personalization_url_default(self):
client = stream.connect(
'key',
'secret',
)
feed_url = client.get_full_url(relative_url='recommended', service_name='personalization')

if not self.local_tests:
self.assertEqual(
feed_url, 'https://personalization.stream-io-api.com/personalization/v1.0/recommended')

def test_api_url_default(self):
client = stream.connect(
'key',
'secret',
)
feed_url = client.get_full_url(service_name='api', relative_url='feed/')

if not self.local_tests:
self.assertEqual(
feed_url, 'https://api.stream-io-api.com/api/v1.0/feed/')

def test_collections_url_location(self):
client = stream.connect(
'key',
'secret',
location='tokyo',
)
feed_url = client.get_full_url(relative_url='meta/', service_name='api')

if not self.local_tests:
self.assertEqual(
feed_url, 'https://tokyo-api.stream-io-api.com/api/v1.0/meta/')

def test_personalization_url_location(self):
client = stream.connect(
'key',
'secret',
location='tokyo',
)
feed_url = client.get_full_url(relative_url='recommended', service_name='personalization')

if not self.local_tests:
self.assertEqual(
feed_url, 'https://tokyo-personalization.stream-io-api.com/personalization/v1.0/recommended')

def test_api_url_location(self):
client = stream.connect(
'key',
'secret',
location='tokyo',
)
feed_url = client.get_full_url(service_name='api', relative_url='feed/')

if not self.local_tests:
self.assertEqual(
feed_url, 'https://tokyo-api.stream-io-api.com/api/v1.0/feed/')

def test_update_activities_create(self):
activities = [{
'actor': 'user:1',
Expand Down Expand Up @@ -181,13 +278,14 @@ def test_heroku_no_location(self):
self.assertEqual(
client.api_secret, 'twc5ywfste5bm2ngqkzs7ukxk3pn96yweghjrxcmcrarnt3j4dqj3tucbhym5wfd')
self.assertEqual(client.app_id, '669')
feed_url = client.get_full_url('api', 'feed/')

if self.local_tests:
self.assertEqual(
client.base_url, 'http://localhost:8000/api/')
feed_url, 'http://localhost:8000/api/v1.0/feed/')
else:
self.assertEqual(
client.base_url, 'https://api.stream-io-api.com/api/')
feed_url, 'https://api.stream-io-api.com/api/v1.0/feed/')

def test_heroku_location_compat(self):
url = 'https://ahj2ndz7gsan:gthc2t9gh7pzq52f6cky8w4r4up9dr6rju9w3fjgmkv6cdvvav2ufe5fv7e2r9qy@us-east.getstream.io/?app_id=1'
Expand All @@ -197,12 +295,14 @@ def test_heroku_location_compat(self):
self.assertEqual(
client.api_secret, 'gthc2t9gh7pzq52f6cky8w4r4up9dr6rju9w3fjgmkv6cdvvav2ufe5fv7e2r9qy')

feed_url = client.get_full_url('api', 'feed/')
if self.local_tests:
self.assertEqual(
client.base_url, 'http://localhost:8000/api/')
feed_url, 'http://localhost:8000/api/v1.0/feed/')
else:
self.assertEqual(
client.base_url, 'https://us-east-api.stream-io-api.com/api/')
feed_url, 'https://us-east-api.stream-io-api.com/api/v1.0/feed/')

self.assertEqual(client.app_id, '1')

def test_heroku_location(self):
Expand All @@ -213,12 +313,13 @@ def test_heroku_location(self):
self.assertEqual(
client.api_secret, 'gthc2t9gh7pzq52f6cky8w4r4up9dr6rju9w3fjgmkv6cdvvav2ufe5fv7e2r9qy')

feed_url = client.get_full_url('api', 'feed/')
if self.local_tests:
self.assertEqual(
client.base_url, 'http://localhost:8000/api/')
feed_url, 'http://localhost:8000/api/v1.0/feed/')
else:
self.assertEqual(
client.base_url, 'https://us-east-api.stream-io-api.com/api/')
feed_url, 'https://us-east-api.stream-io-api.com/api/v1.0/feed/')
self.assertEqual(client.app_id, '1')

def test_heroku_overwrite(self):
Expand All @@ -232,12 +333,13 @@ def test_heroku_overwrite(self):
def test_location_support(self):
client = stream.connect('a', 'b', 'c', location='us-east')

full_location = 'https://us-east-api.stream-io-api.com/api/'
full_location = 'https://us-east-api.stream-io-api.com/api/v1.0/feed/'
if self.local_tests:
full_location = 'http://localhost:8000/api/'
full_location = 'http://localhost:8000/api/v1.0/feed/'

self.assertEqual(client.location, 'us-east')
self.assertEqual(client.base_url, full_location)
feed_url = client.get_full_url('api', 'feed/')
self.assertEqual(feed_url, full_location)

# test a wrong location, can only work on non-local test running
if not self.local_tests:
Expand Down

0 comments on commit 88572d2

Please sign in to comment.