"Fossies" - the Fresh Open Source Software Archive

Member "zaqar-10.0.0/zaqar/common/api/utils.py" (13 May 2020, 7359 Bytes) of package /linux/misc/openstack/zaqar-10.0.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "utils.py" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 9.0.0_vs_10.0.0.

    1 # Copyright (c) 2015 Red Hat, Inc.
    2 #
    3 # Licensed under the Apache License, Version 2.0 (the "License"); you may not
    4 # use this file except in compliance with the License.  You may obtain a copy
    5 # of the License at
    6 #
    7 #    http://www.apache.org/licenses/LICENSE-2.0
    8 #
    9 # Unless required by applicable law or agreed to in writing, software
   10 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
   11 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
   12 # License for the specific language governing permissions and limitations under
   13 # the License.
   14 
   15 import functools
   16 import uuid
   17 
   18 from oslo_log import log as logging
   19 from oslo_utils import strutils
   20 
   21 import zaqar.common.api.errors as api_errors
   22 import zaqar.common.api.response as response
   23 from zaqar.i18n import _
   24 
   25 LOG = logging.getLogger(__name__)
   26 
   27 
   28 def sanitize(document, spec=None, doctype=dict):
   29     """Validates a document and drops undesired fields.
   30 
   31     :param document: A dict to verify according to `spec`.
   32     :param spec: (Default None) Iterable describing expected fields,
   33         yielding tuples with the form of:
   34 
   35             (field_name, value_type, default_value)
   36 
   37         Note that value_type may either be a Python type, or the
   38         special string '*' to accept any type. default_value is the
   39         default to give the field if it is missing, or None to require
   40         that the field be present.
   41 
   42         If spec is None, the incoming documents will not be validated.
   43     :param doctype: type of document to expect; must be either
   44         JSONObject or JSONArray.
   45     :raises DocumentTypeNotSupported: if document type is not supported
   46     :raises TypeError: if document type is neither a JSONObject
   47         nor JSONArray
   48     :returns: A sanitized, filtered version of the document. If the
   49         document is a list of objects, each object will be filtered
   50         and returned in a new list. If, on the other hand, the document
   51         is expected to contain a single object, that object's fields will
   52         be filtered and the resulting object will be returned.
   53     """
   54 
   55     if doctype is dict:
   56         if not isinstance(document, dict):
   57             raise api_errors.DocumentTypeNotSupported()
   58 
   59         return document if spec is None else filter_fields(document, spec)
   60 
   61     if doctype is list:
   62         if not isinstance(document, list):
   63             raise api_errors.DocumentTypeNotSupported()
   64 
   65         if spec is None:
   66             return document
   67 
   68         return [filter_fields(obj, spec) for obj in document]
   69 
   70     raise TypeError(_(u'Doctype must be either a JSONObject or JSONArray'))
   71 
   72 
   73 def filter_fields(document, spec):
   74     """Validates and retrieves typed fields from a single document.
   75 
   76     Sanitizes a dict-like document by checking it against a
   77     list of field spec, and returning only those fields
   78     specified.
   79 
   80     :param document: dict-like object
   81     :param spec: iterable describing expected fields, yielding
   82         tuples with the form of: (field_name, value_type). Note that
   83         value_type may either be a Python type, or the special
   84         string '*' to accept any type.
   85     :raises BadRequest: if any field is missing or not an
   86         instance of the specified type
   87     :returns: A filtered dict containing only the fields
   88         listed in the spec
   89     """
   90 
   91     filtered = {}
   92     for name, value_type, default_value in spec:
   93         filtered[name] = get_checked_field(document, name,
   94                                            value_type, default_value)
   95 
   96     return filtered
   97 
   98 
   99 def get_checked_field(document, name, value_type, default_value):
  100     """Validates and retrieves a typed field from a document.
  101 
  102     This function attempts to look up doc[name], and raises
  103     appropriate errors if the field is missing or not an
  104     instance of the given type.
  105 
  106     :param document: dict-like object
  107     :param name: field name
  108     :param value_type: expected value type, or '*' to accept any type
  109     :param default_value: Default value to use if the value is missing,
  110         or None to make the value required.
  111     :raises BadRequest: if the field is missing or not an
  112         instance of value_type
  113     :returns: value obtained from doc[name]
  114     """
  115 
  116     try:
  117         value = document[name]
  118     except KeyError:
  119         if default_value is not None:
  120             value = default_value
  121         else:
  122             description = _(u'Missing "{name}" field.').format(name=name)
  123             raise api_errors.BadRequest(description)
  124 
  125     # PERF(kgriffs): We do our own little spec thing because it is way
  126     # faster than jsonschema.
  127     if value_type == '*' or isinstance(value, value_type):
  128         return value
  129 
  130     description = _(u'The value of the "{name}" field must be a {vtype}.')
  131     description = description.format(name=name, vtype=value_type.__name__)
  132     raise api_errors.BadRequest(description)
  133 
  134 
  135 def get_client_uuid(req):
  136     """Read a required Client-ID from a request.
  137 
  138     :param req: Request object
  139     :returns: A UUID object or A string of client id
  140     """
  141 
  142     try:
  143         return uuid.UUID(req._headers.get('Client-ID'))
  144     except ValueError:
  145         return req._headers.get('Client-ID')
  146 
  147 
  148 def get_headers(req):
  149     kwargs = {}
  150 
  151     # TODO(vkmc) We should add a control here to make sure
  152     # that the headers/request combination is possible
  153     # e.g. we cannot have messages_post with grace
  154 
  155     if req._body.get('marker') is not None:
  156         kwargs['marker'] = req._body.get('marker')
  157 
  158     if req._body.get('limit') is not None:
  159         kwargs['limit'] = int(req._body.get('limit'))
  160 
  161     if req._body.get('detailed') is not None:
  162         kwargs['detailed'] = strutils.bool_from_string(
  163             req._body.get('detailed'))
  164 
  165     if req._body.get('echo') is not None:
  166         kwargs['echo'] = strutils.bool_from_string(req._body.get('echo'))
  167 
  168     if req._body.get('include_claimed') is not None:
  169         kwargs['include_claimed'] = strutils.bool_from_string(
  170             req._body.get('include_claimed'))
  171 
  172     if req._body.get('include_delayed') is not None:
  173         kwargs['include_delayed'] = strutils.bool_from_string(
  174             req._body.get('include_delayed'))
  175 
  176     if req._body.get('ttl') is not None:
  177         kwargs['ttl'] = int(req._body.get('ttl'))
  178 
  179     if req._body.get('grace') is not None:
  180         kwargs['grace'] = int(req._body.get('grace'))
  181 
  182     return kwargs
  183 
  184 
  185 def on_exception_sends_500(func):
  186     """Handles generic Exceptions in API endpoints
  187 
  188     This decorator catches generic Exceptions and returns a generic
  189     Response.
  190     """
  191 
  192     @functools.wraps(func)
  193     def wrapper(*args, **kwargs):
  194         try:
  195             return func(*args, **kwargs)
  196         except Exception as ex:
  197             error = _("Unexpected error.")
  198             headers = {'status': 500}
  199             # args[0] - Endpoints object, args[1] - Request object.
  200             req = args[1]
  201             LOG.exception(error)
  202             return error_response(req, ex, headers, error)
  203 
  204     return wrapper
  205 
  206 
  207 def error_response(req, exception, headers=None, error=None):
  208     body = {'exception': str(exception), 'error': error}
  209     resp = response.Response(req, body, headers)
  210     return resp
  211 
  212 
  213 def format_message(message, claim_id=None):
  214     return {
  215         'id': message['id'],
  216         'claim_id': claim_id,
  217         'ttl': message['ttl'],
  218         'age': message['age'],
  219         'body': message['body'],
  220     }