From e10512b2b879e4e70a59722d00dcb21f6a940dff Mon Sep 17 00:00:00 2001 From: termie Date: Thu, 10 Nov 2011 15:57:54 -0800 Subject: more dyanmic client --- keystonelight/client.py | 33 ++++++++++++++++++++ keystonelight/service.py | 77 ++++++++++++++++++++++++++++++++++++---------- tests/test_identity_api.py | 6 ++-- 3 files changed, 96 insertions(+), 20 deletions(-) diff --git a/keystonelight/client.py b/keystonelight/client.py index 7b971584..25128ba3 100644 --- a/keystonelight/client.py +++ b/keystonelight/client.py @@ -6,9 +6,13 @@ import json import httplib2 import webob +from keystonelight import service from keystonelight import wsgi +URLMAP = service.URLMAP + + class Client(object): def __init__(self, token=None): self.token = token @@ -34,6 +38,35 @@ class Client(object): return headers + def __getattr__(self, key): + """Lazy way to define a bunch of dynamic urls based on URLMAP. + + Turns something like + + c.authenticate(user_id='foo', password='bar') + + into + + c.request('POST', '/token', body={'user_id': 'foo', 'password': 'bar'}) + + """ + if key not in URLMAP: + raise AttributeError(key) + + method, path = URLMAP[key] + + def _internal(method_=method, path_=path, **kw): + path_ = path_ % kw + params = {'method': method_, + 'path': path_} + if method.lower() in ('put', 'post'): + params['body'] = kw + return self.request(**params) + + setattr(self, key, _internal) + + return getattr(self, key) + class HttpClient(Client): def __init__(self, endpoint=None, token=None): diff --git a/keystonelight/service.py b/keystonelight/service.py index a4c46e9e..536a2a05 100644 --- a/keystonelight/service.py +++ b/keystonelight/service.py @@ -1,7 +1,3 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# this is the web service frontend - import json import logging @@ -15,6 +11,41 @@ from keystonelight import utils from keystonelight import wsgi +HIGH_LEVEL_CALLS = { + 'authenticate': ('POST', '/tokens'), + 'get_tenants': ('GET', '/user/%(user_id)s/tenants'), + 'get_user': ('GET', '/user/%(user_id)s'), + 'get_tenant': ('GET', '/tenant/%(tenant_id)s'), + 'get_tenant_by_name': ('GET', '/tenant_name/%(tenant_name)s'), + 'get_extras': ('GET', '/extras/%(tenant_id)s-%(user_id)s'), + 'get_token': ('GET', '/token/%(token_id)s'), + } + +# NOTE(termie): creates are seperate from updates to allow duplication checks +LOW_LEVEL_CALLS = { + # tokens + 'create_token': ('POST', '/token'), + 'delete_token': ('DELETE', '/token/%(token_id)s'), + # users + 'create_user': ('POST', '/user'), + 'update_user': ('PUT', '/user/%(user_id)s'), + 'delete_user': ('DELETE', '/user/%(user_id)s'), + # tenants + 'create_tenant': ('POST', '/tenant'), + 'update_tenant': ('PUT', '/tenant/%(tenant_id)s'), + 'delete_tenant': ('DELETE', '/tenant/%(tenant_id)s'), + # extras + # NOTE(termie): these separators are probably going to bite us eventually + 'create_extras': ('POST', '/extras'), + 'update_extras': ('PUT', '/extras/%(tenant_id)s-%(user_id)s'), + 'delete_extras': ('DELETE', '/extras/%(tenant_id)s-%(user_id)s'), + } + + +URLMAP = HIGH_LEVEL_CALLS.copy() +URLMAP.update(LOW_LEVEL_CALLS) + + class BaseApplication(wsgi.Application): @webob.dec.wsgify def __call__(self, req): @@ -81,10 +112,11 @@ class IdentityController(BaseApplication): logging.debug('TOKEN: %s', token_ref) return token_ref - def get_tenants(self, context): + def get_tenants(self, context, user_id=None): token_id = context.get('token_id') token_ref = self.token_api.get_token(context, token_id) assert token_ref + assert token_ref['user']['id'] == user_id tenants_ref = [] for tenant_id in token_ref['user']['tenants']: tenants_ref.append(self.identity_api.get_tenant(context, @@ -98,20 +130,31 @@ class Router(wsgi.Router): self.options = options self.identity_controller = IdentityController(options) self.token_controller = TokenController(options) - mapper = routes.Mapper() - mapper.connect('/tokens', - controller=self.identity_controller, - action='authenticate') - mapper.connect('/tokens/{token_id}', - controller=self.token_controller, - action='revoke_token', - conditions=dict(method=['DELETE'])) - mapper.connect("/tenants", - controller=self.identity_controller, - action="get_tenants", - conditions=dict(method=["GET"])) + + mapper = self._build_map(URLMAP) super(Router, self).__init__(mapper) + def _build_map(self, urlmap): + """Build a routes.Mapper based on URLMAP.""" + mapper = routes.Mapper() + for k, v in urlmap.iteritems(): + # NOTE(termie): hack + if 'token' in k: + controller = self.token_controller + else: + controller = self.identity_controller + action = k + method, path = v + path = path.replace('%(', '{').replace(')s', '}') + print path + + mapper.connect(path, + controller=controller, + action=action, + conditions=dict(method=[method])) + + return mapper + def app_factory(global_conf, **local_conf): conf = global_conf.copy() diff --git a/tests/test_identity_api.py b/tests/test_identity_api.py index 21b25781..885d4314 100644 --- a/tests/test_identity_api.py +++ b/tests/test_identity_api.py @@ -51,7 +51,7 @@ class IdentityApi(test.TestCase): post_data = {'user_id': self.user_foo['id'], 'tenant_id': self.tenant_bar['id'], 'password': self.user_foo['password']} - resp = c.post('/tokens', body=post_data) + resp = c.authenticate(**post_data) data = json.loads(resp.body) self.assertEquals(self.user_foo['id'], data['user']['id']) self.assertEquals(self.tenant_bar['id'], data['tenant']['id']) @@ -61,7 +61,7 @@ class IdentityApi(test.TestCase): c = client.TestClient(self.app) post_data = {'user_id': self.user_foo['id'], 'password': self.user_foo['password']} - resp = c.post('/tokens', body=post_data) + resp = c.authenticate(**post_data) data = json.loads(resp.body) self.assertEquals(self.user_foo['id'], data['user']['id']) self.assertEquals(None, data['tenant']) @@ -70,6 +70,6 @@ class IdentityApi(test.TestCase): def test_get_tenants(self): token = self._login() c = client.TestClient(self.app, token['id']) - resp = c.get('/tenants') + resp = c.get_tenants(user_id=self.user_foo['id']) data = json.loads(resp.body) self.assertDictEquals(self.tenant_bar, data[0]) -- cgit