From c3277c2e6eb3a85e8580c906266cad46c4043677 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl=20Kervella?= Date: Wed, 27 Jun 2018 11:17:37 +0000 Subject: [PATCH] Do not use endpoint's names Use the URL structure directly to build the requests directly The mapping name <-> endpoint is no longer used See discusion in #1 for details --- re2oapi/client.py | 115 +++++++++++++----------------------- re2oapi/endpoints.py | 131 ------------------------------------------ re2oapi/exceptions.py | 8 --- 3 files changed, 39 insertions(+), 215 deletions(-) delete mode 100644 re2oapi/endpoints.py diff --git a/re2oapi/client.py b/re2oapi/client.py index 09b913e..5adb1bc 100644 --- a/re2oapi/client.py +++ b/re2oapi/client.py @@ -174,7 +174,7 @@ class Re2oAPIClient: # Perform the authentication request response = requests.post( - self.get_url_for('token'), + self.get_url_for('token-auth'), data={'username': self._username, 'password': self._password} ) self.log.debug("Response code: "+str(response.status_code)) @@ -423,11 +423,11 @@ class Re2oAPIClient: """ return self._request('put', *args, **kwargs) - def get_url_for(self, name, **kwargs): + def get_url_for(self, endpoint): """Retrieve the complete URL to use for a given endpoint's name. Args: - name: The name of the endpoint to look for. + endpoint: The path of the endpoint. **kwargs: A dictionnary with the parameter to use to build the URL (using .format() syntax) @@ -438,23 +438,24 @@ class Re2oAPIClient: re2oapi.exception.NameNotExists: The provided name does not correspond to any endpoint. """ - return '{proto}://{host}{endpoint}'.format( + return '{proto}://{host}/{namespace}/{endpoint}'.format( proto=('https' if self.use_tls else 'http'), host=self.hostname, - endpoint=endpoints.get_endpoint_for(name, self.log, **kwargs) + namespace='api', + endpoint=endpoint ) - def _list_for(self, obj_name, max_results=None, params={}, **kwargs): - """List all '{obj_name}' objects on the server. + def list(self, endpoint, max_results=None, params={}): + """List all objects on the server that corresponds to the given + endpoint. The endpoint must be valid for listing objects. Args: + endpoint: The path of the endpoint. + max_results: A limit on the number of result to return params: See `requests.get` params. - **kwargs: A dictionnary used to defines the required parameters - in order to build the URL (using `.format()`). Returns: - The list of all the {obj_name} objects serialized - as returned by the API. + The list of all the objects serialized as returned by the API. Raises: requests.exceptions.RequestException: An error occured while @@ -462,18 +463,19 @@ class Re2oAPIClient: exceptions.PermissionDenied: The user does not have the right to perform this request. """ - self.log.info("Starting listing {} objects".format(obj_name)) + self.log.info("Starting listing objects under '{}'" + .format(endpoint)) self.log.debug("max_results = "+str(max_results)) # For optimization, list all results in one page unless the user # is forcing the use of a different `page_size`. if not 'page_size' in params.keys(): self.log.debug("Forcing 'page_size' parameter to 'all'.") - params['page_size'] = 'all' + params['page_size'] = max_results or 'all' # Performs the request for the first page response = self.get( - self.get_url_for('%s-list' % obj_name, **kwargs), + self.get_url_for(endpoint), params=params ) results = response['results'] @@ -486,20 +488,20 @@ class Re2oAPIClient: # Returns the exact number of results if applicable ret = results[:max_results] if max_results else results - self.log.debug("Listing {} objects successful".format(obj_name)) + self.log.debug("Listing objects under '{}' successful" + .format(endpoint)) return ret - def _count_for(self, obj_name, params={}, **kwargs): - """Count all '{obj_name}' objects on the server: + def count(self, endpoint, params={}): + """Count all objects on the server that corresponds to the given + endpoint. The endpoint must be valid for listing objects. Args: + endpoint: The path of the endpoint. params: See `requests.get` params. - **kwargs: A dictionnary used to defines the required parameters - in order to build the URL (using `.format()`). Returns: - The number of {obj_name} objects on the server as returned by the - API. + The number of objects on the server as returned by the API. Raises: requests.exceptions.RequestException: An error occured while @@ -507,9 +509,10 @@ class Re2oAPIClient: exceptions.PermissionDenied: The user does not have the right to perform this request. """ - self.log.info("Starting counting {} objects".format(obj_name)) + self.log.info("Starting counting objects under '{}'" + .format(endpoint)) - # For optimization, ask fo only 1 result (so the server will take + # For optimization, ask for only 1 result (so the server will take # less time to process the request) unless the user is forcing the # use of a different `page_size`. if not 'page_size' in params.keys(): @@ -518,24 +521,24 @@ class Re2oAPIClient: # Performs the request and return the `count` value in the response. ret = self.get( - self.get_url_for('%s-list' % obj_name, **kwargs), + self.get_url_for(endpoint), params=params )['count'] - self.log.debug("Counting {} objects successful".format(obj_name)) + self.log.debug("Counting objects under '{}' successful" + .format(endpoint)) return ret - def _view_for(self, obj_name, params={}, **kwargs): - """Retrieved the details of a '{obj_name}' object from the server. + def view(self, endpoint, params={}): + """Retrieved the details of an object from the server that corresponds + to the given endpoint. Args: + endpoint: The path of the endpoint. params: See `requests.get` params. - **kwargs: A dictionnary used to defines the required parameters - in order to build the URL (using `.format()`). Returns: - The serialized data of the queried {obj_name} object as returned - by the API. + The serialized data of the queried object as returned by the API. Raises: requests.exceptions.RequestException: An error occured while @@ -543,53 +546,13 @@ class Re2oAPIClient: exceptions.PermissionDenied: The user does not have the right to perform this request. """ - self.log.info("Starting viewing a {} object".format(obj_name)) + self.log.info("Starting viewing an object under '{}'" + .format(endpoint)) ret = self.get( - self.get_url_for('%s-detail' % obj_name, **kwargs), + self.get_url_for(endpoint), params=params ) - self.log.debug("Viewing {} object successful".format(obj_name)) + self.log.debug("Viewing object under '{}' successful" + .format(endpoint)) return ret - - def __getattr__(self, item): - - if item.startswith('list_'): - obj_name = item[len('list_'):] - def f(*args, **kwargs): - return self._list_for(obj_name, *args, **kwargs) - f.__doc__ = self._list_for.__doc__.format(obj_name=obj_name) - f.__name__ = "list_"+obj_name - return f - - elif item.startswith('count_'): - obj_name = item[len('count_'):] - def f(*args, **kwargs): - return self._count_for(obj_name, *args, **kwargs) - f.__doc__ = self._count_for.__doc__.format(obj_name=obj_name) - f.__name__ = "count_"+obj_name - return f - - elif item.startswith('view_'): - obj_name = item[len('view_'):] - def f(*args, **kwargs): - return self._view_for(obj_name, *args, **kwargs) - f.__doc__ = self._view_for.__doc__.format(obj_name=obj_name) - f.__name__ = "view_"+obj_name - return f - - else: - raise AttributeError(item) - - def __dir__(self): - ret = ['use_tls', 'token_file', 'hostname', 'token', - 'need_renew_token', 'get_token', 'delete', 'get', 'patch', - 'post', 'put', 'get_url_for'] - for name in endpoints.get_names(): - if name.endswith('-list'): - ret.append('list_%s' % name[:-len('-list')]) - ret.append('count_%s' % name[:-len('-list')]) - elif name.endswith('-detail'): - ret.append('view_%s' % name[:-len('-detail')]) - return ret - diff --git a/re2oapi/endpoints.py b/re2oapi/endpoints.py deleted file mode 100644 index f27c72d..0000000 --- a/re2oapi/endpoints.py +++ /dev/null @@ -1,131 +0,0 @@ -from . import exceptions - -urls = { - 'root': '/api/', - 'article-list': '/api/cotisations/article/', - 'article-detail': '/api/cotisations/article/{pk}/', - 'banque-list': '/api/cotisations/banque/', - 'banque-detail': '/api/cotisations/banque/{pk}/', - 'cotisation-list': '/api/cotisations/cotisation/', - 'cotisation-detail': '/api/cotisations/cotisation/{pk}/', - 'facture-list': '/api/cotisations/facture/', - 'facture-detail': '/api/cotisations/facture/{pk}/', - 'paiment-list': '/api/cotisations/paiement/', - 'paiement-detail': '/api/cotisations/paiement/{pk}/', - 'vente-list': '/api/cotisations/vente/', - 'vente-detail': '/api/cotisations/vente/{pk}/', - 'domain-list': '/api/machines/domain/', - 'domain-detail': '/api/machines/domain/{pk}/', - 'extension-list': '/api/machines/extension/', - 'extension-detail': '/api/machines/extension/{pk}/', - 'interface-list': '/api/machines/interface/', - 'interface-detail': '/api/machines/interface/{pk}/', - 'iplist-list': '/api/machines/iplist/', - 'iplist-detail': '/api/machines/iplist/{pk}/', - 'iptype-list': '/api/machines/iptype/', - 'iptype-detail': '/api/machines/iptype/{pk}/', - 'ipv6list-list': '/api/machines/ipv6list/', - 'ipv6list-detail': '/api/machines/ipv6list/{pk}/', - 'machine-list': '/api/machines/machine/', - 'machine-detail': '/api/machines/machine/{pk}/', - 'machinetype-list': '/api/machines/machinetype/', - 'machinetype-detail': '/api/machines/machinetype/{pk}/', - 'mx-list': '/api/machines/mx/', - 'mx-detail': '/api/machines/mx/{pk}/', - 'nas-list': '/api/machines/nas/', - 'nas-detail': '/api/machines/nas/{pk}/', - 'ns-list': '/api/machines/ns/', - 'ns-detail': '/api/machines/ns/{pk}/', - 'ouvertureportlist-list': '/api/machines/ouvertureportlist/', - 'ouvertureportlist-detail': '/api/machines/ouvertureportlist/{pk}/', - 'ouvertureport-list': '/api/machines/ouvertureport/', - 'ouvertureport-detail': '/api/machines/ouvertureport/{pk}/', - 'servicelink-list': '/api/machines/servicelink/', - 'servicelink-detail': '/api/machines/servicelink/{pk}/', - 'service-list': '/api/machines/service/', - 'service-detail': '/api/machines/service/{pk}/', - 'soa-list': '/api/machines/soa/', - 'soa-detail': '/api/machines/soa/{pk}/', - 'srv-list': '/api/machines/srv/', - 'srv-detail': '/api/machines/srv/{pk}/', - 'txt-list': '/api/machines/txt/', - 'txt-detail': '/api/machines/txt/{pk}/', - 'vlan-list': '/api/machines/vlan/', - 'vlan-detail': '/api/machines/vlan/{pk}/', - 'optionaluser-detail': '/api/preferences/optionaluser/', - 'optionalmachine-detail': '/api/preferences/optionalmachine/', - 'optionaltopologie-detail': '/api/preferences/optionaltopologie/', - 'generaloption-detail': '/api/preferences/generaloption/', - 'homeservice-list': '/api/preferences/service/', - 'homeservice-detail': '/api/preferences/service/{pk}/', - 'assooption-detail': '/api/preferences/assooption/', - 'homeoption-detail': '/api/preferences/homeoption/', - 'mailmessageoption-detail': '/api/preferences/mailmessageoption/', - 'accesspoint-list': '/api/topologie/acesspoint/', - 'accesspoint-detail': '/api/topologie/acesspoint/{pk}/', - 'building-list': '/api/topologie/building/', - 'building-detail': '/api/topologie/building/{pk}/', - 'constructorswitch-list': '/api/topologie/constructorswitch/', - 'constructorswitch-detail': '/api/topologie/constructorswitch/{pk}/', - 'modelswitch-list': '/api/topologie/modelswitch/', - 'modelswitch-detail': '/api/topologie/modelswitch/{pk}/', - 'room-list': '/api/topologie/room/', - 'room-detail': '/api/topologie/room/{pk}/', - 'server-list': '/api/topologie/server/', - 'server-detail': '/api/topologie/server/{pk}/', - 'stack-list': '/api/topologie/stack/', - 'stack-detail': '/api/topologie/stack/{pk}/', - 'switch-list': '/api/topologie/switch/', - 'switch-detail': '/api/topologie/switch/{pk}/', - 'switchbay-list': '/api/topologie/switchbay/', - 'switchbay-detail': '/api/topologie/switchbay/{pk}/', - 'switchport-list': '/api/topologie/switchport/', - 'switchport-detail': '/api/topologie/switchport/{pk}/', - 'adherent-list': '/api/users/adherent/', - 'adherent-detail': '/api/users/adherent/{pk}/', - 'ban-list': '/api/users/ban/', - 'ban-detail': '/api/users/ban/{pk}/', - 'club-list': '/api/users/club/', - 'club-detail': '/api/users/club/{pk}/', - 'listright-list': '/api/users/listright/', - 'listright-detail': '/api/users/listright/{pk}/', - 'school-list': '/api/users/school/', - 'school-detail': '/api/users/school/{pk}/', - 'serviceuser-list': '/api/users/serviceuser/', - 'serviceuser-detail': '/api/users/serviceuser/{pk}/', - 'shell-list': '/api/users/shell/', - 'shell-detail': '/api/users/shell/{pk}/', - 'user-list': '/api/users/user/', - 'user-detail': '/api/users/user/{pk}/', - 'whitelist-list': '/api/users/whitelist/', - 'whitelist-detail': '/api/users/whitelist/{pk}/', - 'dnszones-list': '/api/dns/zones/', - 'hostmacip-list': '/api/dhcp/hostmacip/', - 'mailingstandard-list': '/api/mailing/standard/', - 'mailingclub-list': '/api/mailing/club/', - 'servicesregen-list': '/api/services/regen/', - 'servicesregen-detail': '/api/services/regen/{pk}/', - 'token': '/api/token-auth/', -} - - -def get_names(): - return urls.keys() - - -def get_endpoint_for(name, logger=None, **kwargs): - try: - url=urls[name] - except KeyError: - e = exceptions.URLNameDoesNotExists(name) - if logger is not None: - logger.warning(e) - raise e - else: - try: - return url.format_map(kwargs) - except KeyError as e: - e = exceptions.URLParameterMissing(name, e) - if logger is not None: - logger.warning(e) - raise e diff --git a/re2oapi/exceptions.py b/re2oapi/exceptions.py index 08d7913..7522485 100644 --- a/re2oapi/exceptions.py +++ b/re2oapi/exceptions.py @@ -7,14 +7,6 @@ class APIClientGenericError(ValueError): super(APIClientGenericError, self).__init__(self.message) -class URLNameDoesNotExists(APIClientGenericError): - template = "The name '{}' was not foud among the possible endpoints." - - -class URLParameterMissing(APIClientGenericError): - template = "The url named '{}' requires the parameter {} to be built." - - class InvalidCredentials(APIClientGenericError): template = "The credentials for {}@{} are not valid."