コード例 #1
0
        def decorator(fn):
            location = get_callsite_location()
            # this will be the lineno of the last decorator, so we want one
            # below it for the actual function
            location['lineno'] += 1

            # convert older style version strings
            if introduced_at == '1.0':
                self.introduced_at = 1
            elif introduced_at is not None:
                self.introduced_at = int(introduced_at)

            self.register_view(fn, location, introduced_at)

            # support for legacy @validate_{body,output} decorators
            # we don't know the order of decorators, so allow for both.
            # Note that if these schemas come from the @validate decorators,
            # they are already validated, so we set directly.
            fn._acceptable_metadata = self
            if self._request_schema is None:
                self._request_schema = getattr(fn, '_request_schema', None)
                self._request_schema_location = getattr(
                    fn, '_request_schema_location', None)
            if self._response_schema is None:
                self._response_schema = getattr(fn, '_response_schema', None)
                self._response_schema_location = getattr(
                    fn, '_response_schema_location', None)

            return fn
コード例 #2
0
ファイル: _service.py プロジェクト: woutervb/acceptable
    def __init__(self,
                 service,
                 name,
                 url,
                 introduced_at,
                 options={},
                 location=None,
                 undocumented=False,
                 deprecated_at=None,
                 title=None):

        self.service = service
        self.name = name
        self.url = url
        self.introduced_at = introduced_at
        self.options = options
        self.view_fn = None
        self.view_fn_location = None
        self.docs = None
        self._request_schema = None
        self._request_schema_location = None
        self._response_schema = None
        self._response_schema_location = None
        self._params_schema = None
        self._params_schema_location = None
        self._changelog = OrderedDict()
        self._changelog_locations = OrderedDict()
        if location is None:
            self.location = get_callsite_location()
        else:
            self.location = location
        self.undocumented = undocumented
        self.deprecated_at = deprecated_at
        self.title = title
コード例 #3
0
ファイル: _service.py プロジェクト: woutervb/acceptable
    def django_api(self,
                   name,
                   introduced_at,
                   undocumented=False,
                   deprecated_at=None,
                   title=None,
                   **options):
        """Add a django API handler to the service.

        :param name: This is the name of the django url to use.

        The 'methods' paramater can be supplied as normal, you can also user
        the @api.handler decorator to link this API to its handler.

        """
        from acceptable.djangoutil import DjangoAPI
        location = get_callsite_location()
        api = DjangoAPI(
            self,
            name,
            introduced_at,
            options,
            location=location,
            undocumented=undocumented,
            deprecated_at=deprecated_at,
            title=title,
        )
        self.metadata.register_api(self.name, self.group, api)
        return api
コード例 #4
0
ファイル: _service.py プロジェクト: woutervb/acceptable
    def api(self,
            url,
            name,
            introduced_at=None,
            undocumented=False,
            deprecated_at=None,
            title=None,
            **options):
        """Add an API to the service.

        :param url: This is the url that the API should be registered at.
        :param name: This is the name of the api, and will be registered with
            flask apps under.

        Other keyword arguments may be used, and they will be passed to the
        flask application when initialised. Of particular interest is the
        'methods' keyword argument, which can be used to specify the HTTP
        method the URL will be added for.
        """
        location = get_callsite_location()
        api = AcceptableAPI(
            self,
            name,
            url,
            introduced_at,
            options,
            undocumented=undocumented,
            deprecated_at=deprecated_at,
            title=title,
            location=location,
        )
        self.metadata.register_api(self.name, self.group, api)
        return api
コード例 #5
0
def validate_body(schema):
    """Validate the body of incoming requests for a flask view.

    An example usage might look like this::

        from snapstore_schemas import validate_body


        @validate_body({
            'type': 'array',
            'items': {
                'type': 'object',
                'properties': {
                    'snap_id': {'type': 'string'},
                    'series': {'type': 'string'},
                    'name': {'type': 'string'},
                    'title': {'type': 'string'},
                    'keywords': {
                        'type': 'array',
                        'items': {'type': 'string'}
                    },
                    'summary': {'type': 'string'},
                    'description': {'type': 'string'},
                    'created_at': {'type': 'string'},
                },
                'required': ['snap_id', 'series'],
                'additionalProperties': False
            }
        })
        def my_flask_view():
            # view code here
            return "Hello World", 200

    All incoming request that have been routed to this view will be matched
    against the specified schema. If the request body does not match the schema
    an instance of `DataValidationError` will be raised.

    By default this will cause the flask application to return a 500 response,
    but this can be customised by telling flask how to handle these exceptions.
    The exception instance has an 'error_list' attribute that contains a list
    of all the errors encountered while processing the request body.
    """
    location = get_callsite_location()

    def decorator(fn):
        validate_schema(schema)
        wrapper = wrap_request(fn, schema)
        record_schemas(fn,
                       wrapper,
                       location,
                       request_schema=sort_schema(schema))
        return wrapper

    return decorator
コード例 #6
0
    def __call__(self, fn):
        wrapped = fn
        if self.response_schema:
            wrapped = _validation.wrap_response(wrapped, self.response_schema)
        if self.request_schema:
            wrapped = _validation.wrap_request(wrapped, self.request_schema)

        location = get_callsite_location()
        # this will be the lineno of the last decorator, so we want one
        # below it for the actual function
        location['lineno'] += 1
        self.register_view(wrapped, location)
        return wrapped
コード例 #7
0
    def __init__(self, name, group=None, metadata=None):
        """Create an instance of AcceptableService.

        :param name: The service name.
        :param group: An arbitrary API group within a service.
        """
        self.name = name
        self.group = group
        if metadata is None:
            self.metadata = get_metadata()
        else:
            self.metadata = metadata

        self.location = get_callsite_location()
        self.metadata.register_service(name, group)
コード例 #8
0
    def __init__(self, name, group=None, metadata=Metadata):
        """Create an instance of AcceptableService.

        :param name: The service name.
        :param group: An arbitrary API group within a service.
        :raises TypeError: If the name string is something other than a
            string.
        """
        if not isinstance(name, str):
            raise TypeError("name must be a string, not %s" %
                            type(name).__name__)
        self.name = name
        self.group = group
        self.metadata = metadata
        self.metadata.register_service(name, group)
        self.location = get_callsite_location()
コード例 #9
0
ファイル: _service.py プロジェクト: woutervb/acceptable
    def __init__(self, name, group=None, title=None, metadata=None):
        """Create an instance of AcceptableService.

        :param name: The service name.
        :param group: An arbitrary API group within a service.
        """
        self.name = name
        self.group = group
        if metadata is None:
            self.metadata = get_metadata()
        else:
            self.metadata = metadata

        self.location = get_callsite_location()
        self.doc = None
        module = self.location['module']
        docs = None
        if module and module.__doc__:
            docs = clean_docstring(module.__doc__)
        self.metadata.register_service(name, group, docs, title)
コード例 #10
0
def validate_params(schema):
    """Validate the request parameters.

    The request parameters (request.args) are validated against the schema.

    The root of the schema should be an object and each of its properties
    is a parameter.

    An example usage might look like this::

        from snapstore_schemas import validate_params


        @validate_params({
            "type": "object",
            "properties": {
                "id": {
                    "type": "string",
                    "description": "A test property.",
                    "pattern": "[0-9A-F]{8}",
                }
            },
            required: ["id"]
        })
        def my_flask_view():
            ...

    """
    location = get_callsite_location()

    def decorator(fn):
        validate_schema(schema)
        wrapper = wrap_request_params(fn, schema)
        record_schemas(fn,
                       wrapper,
                       location,
                       params_schema=sort_schema(schema))
        return wrapper

    return decorator
コード例 #11
0
def validate_output(schema):
    """Validate the body of a response from a flask view.

    Like `validate_body`, this function compares a json document to a
    jsonschema specification. However, this function applies the schema to the
    view response.

    Instead of the view returning a flask response object, it should instead
    return a Python list or dictionary. For example::

        from snapstore_schemas import validate_output

        @validate_output({
            'type': 'object',
            'properties': {
                'ok': {'type': 'boolean'},
            },
            'required': ['ok'],
            'additionalProperties': False
        }
        def my_flask_view():
            # view code here
            return {'ok': True}

    Every view response will be evaluated against the schema. Any that do not
    comply with the schema will cause DataValidationError to be raised.
    """
    location = get_callsite_location()

    def decorator(fn):
        validate_schema(schema)
        wrapper = wrap_response(fn, schema)
        record_schemas(fn,
                       wrapper,
                       location,
                       response_schema=sort_schema(schema))
        return wrapper

    return decorator
コード例 #12
0
 def __init__(self,
              name,
              url,
              introduced_at,
              options={},
              location=None,
              undocumented=False):
     self.name = name
     self.url = url
     self.introduced_at = introduced_at
     self.options = options
     self.view_fn = None
     self.docs = None
     self._request_schema = None
     self._request_schema_location = None
     self._response_schema = None
     self._response_schema_location = None
     self._changelog = OrderedDict()
     self._changelog_locations = OrderedDict()
     if location is None:
         self.location = get_callsite_location()
     else:
         self.location = location
     self.undocumented = undocumented
コード例 #13
0
 def changelog(self, api_version, doc):
     """Add a changelog entry for this api."""
     doc = textwrap.dedent(doc).strip()
     self._changelog[api_version] = doc
     self._changelog_locations[api_version] = get_callsite_location()
コード例 #14
0
 def response_schema(self, schema):
     if schema is not None:
         _validation.validate_schema(schema)
     self._response_schema = schema
     # this location is the last item in the dict, sadly
     self._response_schema_location = get_callsite_location()
コード例 #15
0
ファイル: _service.py プロジェクト: woutervb/acceptable
 def params_schema(self, schema):
     if schema is not None:
         _validation.validate_schema(schema)
     self._params_schema = sort_schema(schema)
     self._params_schema_location = get_callsite_location()