Coverage for girder/api/rest : 92%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
#!/usr/bin/env python # -*- coding: utf-8 -*-
############################################################################### # Copyright 2013 Kitware Inc. # # Licensed under the Apache License, Version 2.0 ( the "License" ); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. ###############################################################################
AccessControlledModel
""" This decorator for getCurrentUser ensures that the authentication procedure is only performed once per request, and is cached on the request for subsequent calls to getCurrentUser(). """
else:
""" This is a meta-decorator that can be used to convert parameters that are ObjectID's into the actual documents. """
id=kwargs[raw], level=level, user=user) else:
raise RestException('Invalid {} id ({}).' .format(model, kwargs[raw]))
""" REST HTTP method endpoints should use this decorator. It converts the return value of the underlying method to the appropriate output format and sets the relevant response headers. It also handles RestExceptions, which are 400-level exceptions in the REST endpoints, AccessExceptions resulting from access denial, and also handles any unexpected errors using 500 status and including a useful traceback in those cases.
If you want a streamed response, simply return a generator function from the inner method. """ # First, we should encode any unicode form data down into # UTF-8 so the actual REST classes are always dealing with # str types. else: params[k] = v # pragma: no cover
# If the endpoint returned a function, we assume it's a # generator function for a streaming response.
# Handle all user-error exceptions from the rest layer val['extra'] = e.extra # Permission exceptions should throw a 401 or 403, depending # on whether the user is logged in or not else: except: # pragma: no cover # These are unexpected failures; send a 500 status logger.exception('500 Error') cherrypy.response.status = 500 t, value, tb = sys.exc_info() val = {'message': '%s: %s' % (t.__name__, str(value)), 'type': 'internal'} curConfig = config.getConfig() if curConfig['server']['mode'] != 'production': # Unless we are in production mode, send a traceback too val['trace'] = traceback.extract_tb(tb)
elif accept.value == 'text/html': # pragma: no cover # Pretty-print and HTMLify the response for the browser cherrypy.response.headers['Content-Type'] = 'text/html' resp = json.dumps(val, indent=4, sort_keys=True, separators=(',', ': '), default=str) resp = resp.replace(' ', ' ').replace('\n', '<br />') resp = '<div style="font-family:monospace;">%s</div>' % resp return resp
# Default behavior will just be normal JSON output. Keep this # outside of the loop body in case no Accept header is passed.
""" Throw a RestException in the case of any sort of incorrect request (i.e. user/client error). Login and permission failures should set a 403 code; almost all other validation errors should use status 400, which is the default. """
""" Define a route for your REST resource.
:param method: The HTTP method, e.g. 'GET', 'POST', 'PUT' :type method: str :param route: The route, as a list of path params relative to the resource root. Elements of this list starting with ':' are assumed to be wildcards. :type route: list :param handler: The method to be called if the route and method are matched by a request. Wildcards in the route will be expanded and passed as kwargs with the same name as the wildcard identifier. :type handler: function :param nodoc: If your route intentionally provides no documentation, set this to True to disable the warning on startup. :type nodoc: bool """ lambda: collections.defaultdict(list))
# Insertion sort to maintain routes in required order. """ Return bool representing whether route a should go before b. Checks by comparing each token in order and making sure routes with literals in forward positions come before routes with wildcards in those positions. """
else:
# Now handle the api doc if the handler has any attached
resource=resource, route=route, method=method, info=handler.description.asDict(), handler=handler) 'WARNING: No description docs present for route {} {}' .format(method, routePath))
""" Match the requested path to its corresponding route, and calls the handler for that route with the appropriate kwargs. If no route matches the path requested, throws a RestException.
This method fires two events for each request if a matching route is found. The names of these events are derived from the route matched by the request. As an example, if the user calls GET /api/v1/item/123, the following two events would be fired:
rest.get.item/:id.before
would be fired prior to calling the default API function, and
rest.get.item/:id.after
would be fired after the route handler returns. The query params are passed in the info of the before and after event handlers as event.info['params'], and the matched route tokens are passed in as dict items of event.info, so in the previous example event.info would also contain an 'id' key with the value of 123. For endpoints with empty sub-routes, the trailing slash is omitted from the event name, e.g.:
rest.post.group.before
:param method: The HTTP method of the current request. :type method: str :param path: The path params of the request. :type path: list """ raise Exception('No routes defined for resource')
# Add before call for the API method. Listeners can return # their own responses by calling preventDefault() and # adding a response on the event.
else:
kwargs) val = event.responses[0] else:
# Fire the after-call event that has a chance to augment the # return value of the API method that was called. You can # reassign the return value completely by adding a response to # the event and calling preventDefault() on it. val = event.responses[0]
method.upper(), '/'.join(path)))
""" Helper function that attempts to match the requested path with a given route specification. Returns False if the requested path does not match the route. If it does match, this will return the dict of kwargs that should be passed to the underlying handler, based on the wildcard tokens of the route.
:param path: The requested path. :type path: list :param route: The route specification to match against. :type route: list """
""" Pass a list of required parameters. """
""" Calling this on a user will ensure that they have admin rights. an AccessException.
:param user: The user to check admin flag on. :type user: dict. :raises AccessException: If the user is not an administrator. """
""" Pass the URL parameters into this function if the request is for a list of resources that should be paginated. It will return a tuple of the form (limit, offset, sort) whose values should be passed directly into the model methods that are finding the resources. If the client did not pass the parameters, this always uses the same defaults of limit=50, offset=0, sort='name', sortdir=pymongo.ASCENDING=1.
:param params: The URL query parameters. :type params: dict :param defaultSortField: If the client did not pass a 'sort' parameter, set this to choose a default sort field. If None, the results will be returned unsorted. :type defaultSortField: str or None """
else: sort = None
""" Returns the current user from the long-term cookie token.
:param returnToken: Whether we should return a tuple that also contains the token. :type returnToken: bool :returns: The user document from the database, or None if the user is not logged in or the cookie token is invalid or expired. If returnToken=True, returns a tuple of (user, token). """ return event.responses[0]
except: return (None, None) if returnToken else None
objectId=False, user=user)
return (None, token) if returnToken else None else: token = self.model('token').load( cherrypy.request.params.get('token'), objectId=False, force=True) user = self.model('user').load(token['userId'], force=True)
if token is None or token['expires'] < datetime.datetime.now(): return (None, token) if returnToken else None else: return (user, token) if returnToken else user
else: # user is not logged in
def DELETE(self, path, params):
def GET(self, path, params):
def POST(self, path, params):
def PUT(self, path, params): |