kinto 23.2.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. kinto/__init__.py +92 -0
  2. kinto/__main__.py +249 -0
  3. kinto/authorization.py +134 -0
  4. kinto/config/__init__.py +94 -0
  5. kinto/config/kinto.tpl +270 -0
  6. kinto/contribute.json +27 -0
  7. kinto/core/__init__.py +246 -0
  8. kinto/core/authentication.py +48 -0
  9. kinto/core/authorization.py +311 -0
  10. kinto/core/cache/__init__.py +131 -0
  11. kinto/core/cache/memcached.py +112 -0
  12. kinto/core/cache/memory.py +104 -0
  13. kinto/core/cache/postgresql/__init__.py +178 -0
  14. kinto/core/cache/postgresql/schema.sql +23 -0
  15. kinto/core/cache/testing.py +208 -0
  16. kinto/core/cornice/__init__.py +93 -0
  17. kinto/core/cornice/cors.py +144 -0
  18. kinto/core/cornice/errors.py +40 -0
  19. kinto/core/cornice/pyramidhook.py +373 -0
  20. kinto/core/cornice/renderer.py +89 -0
  21. kinto/core/cornice/resource.py +205 -0
  22. kinto/core/cornice/service.py +641 -0
  23. kinto/core/cornice/util.py +138 -0
  24. kinto/core/cornice/validators/__init__.py +94 -0
  25. kinto/core/cornice/validators/_colander.py +142 -0
  26. kinto/core/cornice/validators/_marshmallow.py +182 -0
  27. kinto/core/cornice_swagger/__init__.py +92 -0
  28. kinto/core/cornice_swagger/converters/__init__.py +21 -0
  29. kinto/core/cornice_swagger/converters/exceptions.py +6 -0
  30. kinto/core/cornice_swagger/converters/parameters.py +90 -0
  31. kinto/core/cornice_swagger/converters/schema.py +249 -0
  32. kinto/core/cornice_swagger/swagger.py +725 -0
  33. kinto/core/cornice_swagger/templates/index.html +73 -0
  34. kinto/core/cornice_swagger/templates/index_script_template.html +21 -0
  35. kinto/core/cornice_swagger/util.py +42 -0
  36. kinto/core/cornice_swagger/views.py +78 -0
  37. kinto/core/decorators.py +74 -0
  38. kinto/core/errors.py +216 -0
  39. kinto/core/events.py +301 -0
  40. kinto/core/initialization.py +738 -0
  41. kinto/core/listeners/__init__.py +9 -0
  42. kinto/core/metrics.py +94 -0
  43. kinto/core/openapi.py +115 -0
  44. kinto/core/permission/__init__.py +202 -0
  45. kinto/core/permission/memory.py +167 -0
  46. kinto/core/permission/postgresql/__init__.py +489 -0
  47. kinto/core/permission/postgresql/migrations/migration_001_002.sql +18 -0
  48. kinto/core/permission/postgresql/schema.sql +41 -0
  49. kinto/core/permission/testing.py +487 -0
  50. kinto/core/resource/__init__.py +1311 -0
  51. kinto/core/resource/model.py +412 -0
  52. kinto/core/resource/schema.py +502 -0
  53. kinto/core/resource/viewset.py +230 -0
  54. kinto/core/schema.py +119 -0
  55. kinto/core/scripts.py +50 -0
  56. kinto/core/statsd.py +1 -0
  57. kinto/core/storage/__init__.py +436 -0
  58. kinto/core/storage/exceptions.py +53 -0
  59. kinto/core/storage/generators.py +58 -0
  60. kinto/core/storage/memory.py +651 -0
  61. kinto/core/storage/postgresql/__init__.py +1131 -0
  62. kinto/core/storage/postgresql/client.py +120 -0
  63. kinto/core/storage/postgresql/migrations/migration_001_002.sql +10 -0
  64. kinto/core/storage/postgresql/migrations/migration_002_003.sql +33 -0
  65. kinto/core/storage/postgresql/migrations/migration_003_004.sql +18 -0
  66. kinto/core/storage/postgresql/migrations/migration_004_005.sql +20 -0
  67. kinto/core/storage/postgresql/migrations/migration_005_006.sql +11 -0
  68. kinto/core/storage/postgresql/migrations/migration_006_007.sql +74 -0
  69. kinto/core/storage/postgresql/migrations/migration_007_008.sql +66 -0
  70. kinto/core/storage/postgresql/migrations/migration_008_009.sql +41 -0
  71. kinto/core/storage/postgresql/migrations/migration_009_010.sql +98 -0
  72. kinto/core/storage/postgresql/migrations/migration_010_011.sql +14 -0
  73. kinto/core/storage/postgresql/migrations/migration_011_012.sql +9 -0
  74. kinto/core/storage/postgresql/migrations/migration_012_013.sql +71 -0
  75. kinto/core/storage/postgresql/migrations/migration_013_014.sql +14 -0
  76. kinto/core/storage/postgresql/migrations/migration_014_015.sql +95 -0
  77. kinto/core/storage/postgresql/migrations/migration_015_016.sql +4 -0
  78. kinto/core/storage/postgresql/migrations/migration_016_017.sql +81 -0
  79. kinto/core/storage/postgresql/migrations/migration_017_018.sql +25 -0
  80. kinto/core/storage/postgresql/migrations/migration_018_019.sql +8 -0
  81. kinto/core/storage/postgresql/migrations/migration_019_020.sql +7 -0
  82. kinto/core/storage/postgresql/migrations/migration_020_021.sql +68 -0
  83. kinto/core/storage/postgresql/migrations/migration_021_022.sql +62 -0
  84. kinto/core/storage/postgresql/migrations/migration_022_023.sql +5 -0
  85. kinto/core/storage/postgresql/migrations/migration_023_024.sql +6 -0
  86. kinto/core/storage/postgresql/migrations/migration_024_025.sql +6 -0
  87. kinto/core/storage/postgresql/migrator.py +98 -0
  88. kinto/core/storage/postgresql/pool.py +55 -0
  89. kinto/core/storage/postgresql/schema.sql +143 -0
  90. kinto/core/storage/testing.py +1857 -0
  91. kinto/core/storage/utils.py +37 -0
  92. kinto/core/testing.py +182 -0
  93. kinto/core/utils.py +553 -0
  94. kinto/core/views/__init__.py +0 -0
  95. kinto/core/views/batch.py +163 -0
  96. kinto/core/views/errors.py +145 -0
  97. kinto/core/views/heartbeat.py +106 -0
  98. kinto/core/views/hello.py +69 -0
  99. kinto/core/views/openapi.py +35 -0
  100. kinto/core/views/version.py +50 -0
  101. kinto/events.py +3 -0
  102. kinto/plugins/__init__.py +0 -0
  103. kinto/plugins/accounts/__init__.py +94 -0
  104. kinto/plugins/accounts/authentication.py +63 -0
  105. kinto/plugins/accounts/scripts.py +61 -0
  106. kinto/plugins/accounts/utils.py +13 -0
  107. kinto/plugins/accounts/views.py +136 -0
  108. kinto/plugins/admin/README.md +3 -0
  109. kinto/plugins/admin/VERSION +1 -0
  110. kinto/plugins/admin/__init__.py +40 -0
  111. kinto/plugins/admin/build/VERSION +1 -0
  112. kinto/plugins/admin/build/assets/index-CYFwtKtL.css +6 -0
  113. kinto/plugins/admin/build/assets/index-DJ0m93zA.js +149 -0
  114. kinto/plugins/admin/build/assets/logo-VBRiKSPX.png +0 -0
  115. kinto/plugins/admin/build/index.html +18 -0
  116. kinto/plugins/admin/public/help.html +25 -0
  117. kinto/plugins/admin/views.py +42 -0
  118. kinto/plugins/default_bucket/__init__.py +191 -0
  119. kinto/plugins/flush.py +28 -0
  120. kinto/plugins/history/__init__.py +65 -0
  121. kinto/plugins/history/listener.py +181 -0
  122. kinto/plugins/history/views.py +66 -0
  123. kinto/plugins/openid/__init__.py +131 -0
  124. kinto/plugins/openid/utils.py +14 -0
  125. kinto/plugins/openid/views.py +193 -0
  126. kinto/plugins/prometheus.py +300 -0
  127. kinto/plugins/statsd.py +85 -0
  128. kinto/schema_validation.py +135 -0
  129. kinto/views/__init__.py +34 -0
  130. kinto/views/admin.py +195 -0
  131. kinto/views/buckets.py +45 -0
  132. kinto/views/collections.py +58 -0
  133. kinto/views/contribute.py +39 -0
  134. kinto/views/groups.py +90 -0
  135. kinto/views/permissions.py +235 -0
  136. kinto/views/records.py +133 -0
  137. kinto-23.2.1.dist-info/METADATA +232 -0
  138. kinto-23.2.1.dist-info/RECORD +142 -0
  139. kinto-23.2.1.dist-info/WHEEL +5 -0
  140. kinto-23.2.1.dist-info/entry_points.txt +5 -0
  141. kinto-23.2.1.dist-info/licenses/LICENSE +13 -0
  142. kinto-23.2.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,138 @@
1
+ # This Source Code Form is subject to the terms of the Mozilla Public
2
+ # License, v. 2.0. If a copy of the MPL was not distributed with this file,
3
+ # You can obtain one at http://mozilla.org/MPL/2.0/.
4
+ import warnings
5
+
6
+
7
+ __all__ = [
8
+ "is_string",
9
+ "to_list",
10
+ "match_accept_header",
11
+ "ContentTypePredicate",
12
+ "current_service",
13
+ "func_name",
14
+ ]
15
+
16
+
17
+ def is_string(s):
18
+ return isinstance(s, str)
19
+
20
+
21
+ def to_list(obj):
22
+ """Convert an object to a list if it is not already one"""
23
+ if not isinstance(obj, (list, tuple)):
24
+ obj = [
25
+ obj,
26
+ ]
27
+ return obj
28
+
29
+
30
+ def match_accept_header(func, context, request):
31
+ """
32
+ Return True if the request ``Accept`` header match
33
+ the list returned by the callable specified in :param:func.
34
+
35
+ Also attach the total list of possible accepted
36
+ egress media types to the request.
37
+
38
+ Utility function for performing content negotiation.
39
+
40
+ :param func:
41
+ The callable returning the list of acceptable
42
+ internet media types for content negotiation.
43
+ It obtains the request object as single argument.
44
+ """
45
+ acceptable = to_list(func(request))
46
+ request.info["acceptable"] = acceptable
47
+ return len(request.accept.acceptable_offers(acceptable)) > 0
48
+
49
+
50
+ def match_content_type_header(func, context, request):
51
+ """
52
+ Return True if the request ``Content-Type`` header match
53
+ the list returned by the callable specified in :param:func.
54
+
55
+ Also attach the total list of possible accepted
56
+ ingress media types to the request.
57
+
58
+ Utility function for performing request body
59
+ media type checks.
60
+
61
+ :param func:
62
+ The callable returning the list of acceptable
63
+ internet media types for request body
64
+ media type checks.
65
+ It obtains the request object as single argument.
66
+ """
67
+ supported_contenttypes = to_list(func(request))
68
+ request.info["supported_contenttypes"] = supported_contenttypes
69
+ return content_type_matches(request, supported_contenttypes)
70
+
71
+
72
+ def extract_json_data(request):
73
+ warnings.warn("Use ``cornice.validators.extract_cstruct()`` instead", DeprecationWarning)
74
+ from kinto.core.cornice.validators import extract_cstruct
75
+
76
+ return extract_cstruct(request)["body"]
77
+
78
+
79
+ def extract_form_urlencoded_data(request):
80
+ warnings.warn("Use ``cornice.validators.extract_cstruct()`` instead", DeprecationWarning)
81
+ return request.POST
82
+
83
+
84
+ def content_type_matches(request, content_types):
85
+ """
86
+ Check whether ``request.content_type``
87
+ matches given list of content types.
88
+ """
89
+ return request.content_type in content_types
90
+
91
+
92
+ class ContentTypePredicate(object):
93
+ """
94
+ Pyramid predicate for matching against ``Content-Type`` request header.
95
+ Should live in ``pyramid.config.predicates``.
96
+
97
+ .. seealso::
98
+ http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/hooks.html
99
+ #view-and-route-predicates
100
+ """
101
+
102
+ def __init__(self, val, config):
103
+ self.val = val
104
+
105
+ def text(self):
106
+ return "content_type = %s" % (self.val,)
107
+
108
+ phash = text
109
+
110
+ def __call__(self, context, request):
111
+ return request.content_type == self.val
112
+
113
+
114
+ def func_name(f):
115
+ """Return the name of a function or class method."""
116
+ if isinstance(f, str):
117
+ return f
118
+ elif hasattr(f, "__qualname__"): # pragma: no cover
119
+ return f.__qualname__ # Python 3
120
+ elif hasattr(f, "im_class"): # pragma: no cover
121
+ return "{0}.{1}".format(f.im_class.__name__, f.__name__) # Python 2
122
+ else: # pragma: no cover
123
+ return f.__name__ # Python 2
124
+
125
+
126
+ def current_service(request):
127
+ """Return the Cornice service matching the specified request.
128
+
129
+ :returns: the service or None if unmatched.
130
+ :rtype: kinto.core.cornice.Service
131
+ """
132
+ if request.matched_route:
133
+ services = request.registry.cornice_services
134
+ pattern = request.matched_route.pattern
135
+ name = request.matched_route.name
136
+ # try pattern first, then route name else return None
137
+ service = services.get(pattern, services.get("__cornice" + name))
138
+ return service
@@ -0,0 +1,94 @@
1
+ # This Source Code Form is subject to the terms of the Mozilla Public
2
+ # License, v. 2.0. If a copy of the MPL was not distributed with this file,
3
+ # You can obtain one at http://mozilla.org/MPL/2.0/.
4
+ import re
5
+
6
+ from webob.multidict import MultiDict
7
+
8
+ from kinto.core.cornice.validators._colander import body_validator as colander_body_validator
9
+ from kinto.core.cornice.validators._colander import headers_validator as colander_headers_validator
10
+ from kinto.core.cornice.validators._colander import path_validator as colander_path_validator
11
+ from kinto.core.cornice.validators._colander import (
12
+ querystring_validator as colander_querystring_validator,
13
+ )
14
+ from kinto.core.cornice.validators._colander import validator as colander_validator
15
+ from kinto.core.cornice.validators._marshmallow import body_validator as marshmallow_body_validator
16
+ from kinto.core.cornice.validators._marshmallow import (
17
+ headers_validator as marshmallow_headers_validator,
18
+ )
19
+ from kinto.core.cornice.validators._marshmallow import path_validator as marshmallow_path_validator
20
+ from kinto.core.cornice.validators._marshmallow import (
21
+ querystring_validator as marshmallow_querystring_validator,
22
+ )
23
+ from kinto.core.cornice.validators._marshmallow import validator as marshmallow_validator
24
+
25
+
26
+ __all__ = [
27
+ "colander_validator",
28
+ "colander_body_validator",
29
+ "colander_headers_validator",
30
+ "colander_path_validator",
31
+ "colander_querystring_validator",
32
+ "marshmallow_validator",
33
+ "marshmallow_body_validator",
34
+ "marshmallow_headers_validator",
35
+ "marshmallow_path_validator",
36
+ "marshmallow_querystring_validator",
37
+ "extract_cstruct",
38
+ "DEFAULT_VALIDATORS",
39
+ "DEFAULT_FILTERS",
40
+ ]
41
+
42
+
43
+ DEFAULT_VALIDATORS = []
44
+ DEFAULT_FILTERS = []
45
+
46
+
47
+ def extract_cstruct(request):
48
+ """
49
+ Extract attributes from the specified `request` such as body, url, path,
50
+ method, querystring, headers, cookies, and returns them in a single dict
51
+ object.
52
+
53
+ :param request: Current request
54
+ :type request: :class:`~pyramid:pyramid.request.Request`
55
+
56
+ :returns: A mapping containing most request attributes.
57
+ :rtype: dict
58
+ """
59
+ is_json = re.match("^application/(.*?)json$", str(request.content_type))
60
+
61
+ if request.content_type in ("application/x-www-form-urlencoded", "multipart/form-data"):
62
+ body = request.POST.mixed()
63
+ elif request.content_type and not is_json:
64
+ body = request.body
65
+ else:
66
+ if request.body:
67
+ try:
68
+ body = request.json_body
69
+ except ValueError as e:
70
+ request.errors.add("body", "", "Invalid JSON: %s" % e)
71
+ return {}
72
+ else:
73
+ if not hasattr(body, "items") and not isinstance(body, list):
74
+ request.errors.add("body", "", "Should be a JSON object or an array")
75
+ return {}
76
+ else:
77
+ body = {}
78
+
79
+ cstruct = {
80
+ "method": request.method,
81
+ "url": request.url,
82
+ "path": request.matchdict,
83
+ "body": body,
84
+ }
85
+
86
+ for sub, attr in (("querystring", "GET"), ("header", "headers"), ("cookies", "cookies")):
87
+ data = getattr(request, attr)
88
+ if isinstance(data, MultiDict):
89
+ data = data.mixed()
90
+ else:
91
+ data = dict(data)
92
+ cstruct[sub] = data
93
+
94
+ return cstruct
@@ -0,0 +1,142 @@
1
+ # This Source Code Form is subject to the terms of the Mozilla Public
2
+ # License, v. 2.0. If a copy of the MPL was not distributed with this file,
3
+ # You can obtain one at http://mozilla.org/MPL/2.0/.
4
+
5
+ import inspect
6
+ import warnings
7
+
8
+
9
+ def _generate_colander_validator(location):
10
+ """
11
+ Generate a colander validator for data from the given location.
12
+
13
+ :param location: The location in the request to find the data to be
14
+ validated, such as "body" or "querystring".
15
+ :type location: str
16
+ :return: Returns a callable that will validate the request at the given
17
+ location.
18
+ :rtype: callable
19
+ """
20
+
21
+ def _validator(request, schema=None, deserializer=None, **kwargs):
22
+ """
23
+ Validate the location against the schema defined on the service.
24
+
25
+ The content of the location is deserialized, validated and stored in
26
+ the ``request.validated`` attribute.
27
+
28
+ .. note::
29
+
30
+ If no schema is defined, this validator does nothing.
31
+ Schema should be of type :class:`~colander:colander.MappingSchema`.
32
+
33
+ :param request: Current request
34
+ :type request: :class:`~pyramid:pyramid.request.Request`
35
+
36
+ :param schema: The Colander schema
37
+ :param deserializer: Optional deserializer, defaults to
38
+ :func:`cornice.validators.extract_cstruct`
39
+ """
40
+ import colander
41
+
42
+ if schema is None:
43
+ return
44
+
45
+ schema_instance = _ensure_instantiated(schema)
46
+
47
+ if not isinstance(schema_instance, colander.MappingSchema):
48
+ raise TypeError("Schema should inherit from colander.MappingSchema.")
49
+
50
+ class RequestSchemaMeta(colander._SchemaMeta):
51
+ """
52
+ A metaclass that will inject a location class attribute into
53
+ RequestSchema.
54
+ """
55
+
56
+ def __new__(cls, name, bases, class_attrs):
57
+ """
58
+ Instantiate the RequestSchema class.
59
+
60
+ :param name: The name of the class we are instantiating. Will
61
+ be "RequestSchema".
62
+ :type name: str
63
+ :param bases: The class's superclasses.
64
+ :type bases: tuple
65
+ :param dct: The class's class attributes.
66
+ :type dct: dict
67
+ """
68
+ class_attrs[location] = schema_instance
69
+ return type(name, bases, class_attrs)
70
+
71
+ class RequestSchema(colander.MappingSchema, metaclass=RequestSchemaMeta): # noqa
72
+ """A schema to validate the request's location attributes."""
73
+
74
+ pass
75
+
76
+ validator(request, RequestSchema(), deserializer, **kwargs)
77
+ validated_location = request.validated.get(location, {})
78
+ request.validated.update(validated_location)
79
+ if location not in validated_location:
80
+ request.validated.pop(location, None)
81
+
82
+ return _validator
83
+
84
+
85
+ body_validator = _generate_colander_validator("body")
86
+ headers_validator = _generate_colander_validator("headers")
87
+ path_validator = _generate_colander_validator("path")
88
+ querystring_validator = _generate_colander_validator("querystring")
89
+
90
+
91
+ def validator(request, schema=None, deserializer=None, **kwargs):
92
+ """
93
+ Validate the full request against the schema defined on the service.
94
+
95
+ Each attribute of the request is deserialized, validated and stored in the
96
+ ``request.validated`` attribute
97
+ (eg. body in ``request.validated['body']``).
98
+
99
+ .. note::
100
+
101
+ If no schema is defined, this validator does nothing.
102
+
103
+ :param request: Current request
104
+ :type request: :class:`~pyramid:pyramid.request.Request`
105
+
106
+ :param schema: The Colander schema
107
+ :param deserializer: Optional deserializer, defaults to
108
+ :func:`cornice.validators.extract_cstruct`
109
+ """
110
+ import colander
111
+
112
+ from kinto.core.cornice.validators import extract_cstruct
113
+
114
+ if deserializer is None:
115
+ deserializer = extract_cstruct
116
+
117
+ if schema is None:
118
+ return
119
+
120
+ schema = _ensure_instantiated(schema)
121
+ cstruct = deserializer(request)
122
+ try:
123
+ deserialized = schema.deserialize(cstruct)
124
+ except colander.Invalid as e:
125
+ translate = request.localizer.translate
126
+ error_dict = e.asdict(translate=translate)
127
+ for name, msg in error_dict.items():
128
+ location, _, field = name.partition(".")
129
+ request.errors.add(location, field, msg)
130
+ else:
131
+ request.validated.update(deserialized)
132
+
133
+
134
+ def _ensure_instantiated(schema):
135
+ if inspect.isclass(schema):
136
+ warnings.warn(
137
+ "Setting schema to a class is deprecated. Set schema to an instance instead.",
138
+ DeprecationWarning,
139
+ stacklevel=2,
140
+ )
141
+ schema = schema()
142
+ return schema
@@ -0,0 +1,182 @@
1
+ # This Source Code Form is subject to the terms of the Mozilla Public
2
+ # License, v. 2.0. If a copy of the MPL was not distributed with this file,
3
+ # You can obtain one at http://mozilla.org/MPL/2.0/.
4
+
5
+ import inspect
6
+
7
+
8
+ def _generate_marshmallow_validator(location):
9
+ """
10
+ Generate a marshmallow validator for data from the given location.
11
+
12
+ :param location: The location in the request to find the data to be
13
+ validated, such as "body" or "querystring".
14
+ :type location: str
15
+ :return: Returns a callable that will validate the request at the given
16
+ location.
17
+ :rtype: callable
18
+ """
19
+
20
+ def _validator(request, schema=None, deserializer=None, **kwargs):
21
+ """
22
+ Validate the location against the schema defined on the service.
23
+
24
+ The content of the location is deserialized, validated and stored in
25
+ the ``request.validated`` attribute.
26
+
27
+ Keyword arguments to be included when initialising the marshmallow
28
+ schema can be passed as a dict in ``kwargs['schema_kwargs']`` variable.
29
+
30
+ .. note::
31
+
32
+ If no schema is defined, this validator does nothing.
33
+
34
+ :param request: Current request
35
+ :type request: :class:`~pyramid:pyramid.request.Request`
36
+
37
+ :param schema: The marshmallow schema
38
+ :param deserializer: Optional deserializer, defaults to
39
+ :func:`cornice.validators.extract_cstruct`
40
+ """
41
+ import marshmallow
42
+ import marshmallow.schema
43
+ from marshmallow.utils import EXCLUDE
44
+
45
+ if schema is None:
46
+ return
47
+
48
+ # see if the user wants to set any keyword arguments for their schema
49
+ schema_kwargs = kwargs.get("schema_kwargs", {})
50
+ schema = _instantiate_schema(schema, **schema_kwargs)
51
+
52
+ class ValidatedField(marshmallow.fields.Field):
53
+ def _deserialize(self, value, attr, data, **kwargs):
54
+ schema.context.setdefault("request", request)
55
+ deserialized = schema.load(value)
56
+ return deserialized
57
+
58
+ class Meta(object):
59
+ strict = True
60
+ ordered = True
61
+ unknown = EXCLUDE
62
+
63
+ class RequestSchemaMeta(marshmallow.schema.SchemaMeta):
64
+ """
65
+ A metaclass that will inject a location class attribute into
66
+ RequestSchema.
67
+ """
68
+
69
+ def __new__(cls, name, bases, class_attrs):
70
+ """
71
+ Instantiate the RequestSchema class.
72
+
73
+ :param name: The name of the class we are instantiating. Will
74
+ be "RequestSchema".
75
+ :type name: str
76
+ :param bases: The class's superclasses.
77
+ :type bases: tuple
78
+ :param dct: The class's class attributes.
79
+ :type dct: dict
80
+ """
81
+
82
+ class_attrs[location] = ValidatedField(
83
+ required=True, metadata={"load_from": location}
84
+ )
85
+ class_attrs["Meta"] = Meta
86
+ return type(name, bases, class_attrs)
87
+
88
+ class RequestSchema(marshmallow.Schema, metaclass=RequestSchemaMeta): # noqa
89
+ """A schema to validate the request's location attributes."""
90
+
91
+ pass
92
+
93
+ validator(request, RequestSchema, deserializer, **kwargs)
94
+ request.validated = request.validated.get(location, {})
95
+
96
+ return _validator
97
+
98
+
99
+ body_validator = _generate_marshmallow_validator("body")
100
+ headers_validator = _generate_marshmallow_validator("header")
101
+ path_validator = _generate_marshmallow_validator("path")
102
+ querystring_validator = _generate_marshmallow_validator("querystring")
103
+
104
+
105
+ def _message_normalizer(exc, no_field_name="_schema"):
106
+ """
107
+ Normally `normalize_messages` will exist on `ValidationError` but pre 2.10
108
+ versions don't have it
109
+ :param exc:
110
+ :param no_field_name:
111
+ :return:
112
+ """
113
+ if isinstance(exc.messages, dict):
114
+ return exc.messages
115
+ field_names = exc.kwargs.get("field_names", [])
116
+ if len(field_names) == 0:
117
+ return {no_field_name: exc.messages}
118
+ return dict((name, exc.messages) for name in field_names)
119
+
120
+
121
+ def validator(request, schema=None, deserializer=None, **kwargs):
122
+ """
123
+ Validate the full request against the schema defined on the service.
124
+
125
+ Each attribute of the request is deserialized, validated and stored in the
126
+ ``request.validated`` attribute
127
+ (eg. body in ``request.validated['body']``).
128
+
129
+ .. note::
130
+
131
+ If no schema is defined, this validator does nothing.
132
+
133
+ :param request: Current request
134
+ :type request: :class:`~pyramid:pyramid.request.Request`
135
+
136
+ :param schema: The marshmallow schema
137
+ :param deserializer: Optional deserializer, defaults to
138
+ :func:`cornice.validators.extract_cstruct`
139
+ """
140
+ import marshmallow
141
+
142
+ from kinto.core.cornice.validators import extract_cstruct
143
+
144
+ if deserializer is None:
145
+ deserializer = extract_cstruct
146
+
147
+ if schema is None:
148
+ return
149
+
150
+ schema = _instantiate_schema(schema)
151
+ schema.context.setdefault("request", request)
152
+
153
+ cstruct = deserializer(request)
154
+ try:
155
+ deserialized = schema.load(cstruct)
156
+ except marshmallow.ValidationError as err:
157
+ # translate = request.localizer.translate
158
+ normalized_errors = _message_normalizer(err)
159
+ for location, details in normalized_errors.items():
160
+ location = location if location != "_schema" else ""
161
+ if hasattr(details, "items"):
162
+ for subfield, msg in details.items():
163
+ request.errors.add(location, subfield, msg)
164
+ else:
165
+ request.errors.add(location, location, details)
166
+ else:
167
+ request.validated.update(deserialized)
168
+
169
+
170
+ def _instantiate_schema(schema, **kwargs):
171
+ """
172
+ Returns an object of the given marshmallow schema.
173
+
174
+ :param schema: The marshmallow schema class with which the request should
175
+ be validated
176
+ :param kwargs: The keyword arguments that will be provided to the
177
+ marshmallow schema's constructor
178
+ :return: The object of the marshmallow schema
179
+ """
180
+ if not inspect.isclass(schema):
181
+ raise ValueError("You need to pass Marshmallow class instead of schema instance")
182
+ return schema(**kwargs)
@@ -0,0 +1,92 @@
1
+ from pyramid.security import NO_PERMISSION_REQUIRED
2
+
3
+ from kinto.core.cornice_swagger.swagger import CorniceSwagger
4
+
5
+
6
+ __author__ = """Josip Delic"""
7
+ __email__ = "delicj@delijati.net"
8
+ __version__ = "0.3.0"
9
+
10
+
11
+ __all__ = ["CorniceSwagger"]
12
+
13
+
14
+ class CorniceSwaggerPredicate(object):
15
+ """Predicate to add simple information to Cornice Swagger."""
16
+
17
+ def __init__(self, schema, config):
18
+ self.schema = schema
19
+
20
+ def phash(self):
21
+ return str(self.schema)
22
+
23
+ def __call__(self, context, request):
24
+ return self.schema
25
+
26
+
27
+ def includeme(config):
28
+ # Custom view parameters
29
+ config.add_view_predicate("response_schemas", CorniceSwaggerPredicate)
30
+ config.add_view_predicate("tags", CorniceSwaggerPredicate)
31
+ config.add_view_predicate("operation_id", CorniceSwaggerPredicate)
32
+ config.add_view_predicate("api_security", CorniceSwaggerPredicate)
33
+ config.add_directive("cornice_enable_openapi_view", cornice_enable_openapi_view)
34
+ config.add_directive("cornice_enable_openapi_explorer", cornice_enable_openapi_explorer)
35
+
36
+
37
+ def cornice_enable_openapi_view(
38
+ config,
39
+ api_path="/api-explorer/swagger.json",
40
+ permission=NO_PERMISSION_REQUIRED,
41
+ route_factory=None,
42
+ **kwargs,
43
+ ):
44
+ """
45
+ :param config:
46
+ Pyramid configurator object
47
+ :param api_path:
48
+ where to expose swagger JSON definition view
49
+ :param permission:
50
+ pyramid permission for those views
51
+ :param route_factory:
52
+ factory for context object for those routes
53
+ :param kwargs:
54
+ kwargs that will be passed to CorniceSwagger's `generate()`
55
+
56
+ This registers and configures the view that serves api definitions
57
+ """
58
+ config.registry.settings["cornice_swagger.spec_kwargs"] = kwargs
59
+ config.add_route("cornice_swagger.open_api_path", api_path, factory=route_factory)
60
+ config.add_view(
61
+ "cornice_swagger.views.open_api_json_view",
62
+ renderer="json",
63
+ permission=permission,
64
+ route_name="cornice_swagger.open_api_path",
65
+ )
66
+
67
+
68
+ def cornice_enable_openapi_explorer(
69
+ config,
70
+ api_explorer_path="/api-explorer",
71
+ permission=NO_PERMISSION_REQUIRED,
72
+ route_factory=None,
73
+ **kwargs,
74
+ ):
75
+ """
76
+ :param config:
77
+ Pyramid configurator object
78
+ :param api_explorer_path:
79
+ where to expose Swagger UI interface view
80
+ :param permission:
81
+ pyramid permission for those views
82
+ :param route_factory:
83
+ factory for context object for those routes
84
+
85
+ This registers and configures the view that serves api explorer
86
+ """
87
+ config.add_route("cornice_swagger.api_explorer_path", api_explorer_path, factory=route_factory)
88
+ config.add_view(
89
+ "cornice_swagger.views.swagger_ui_template_view",
90
+ permission=permission,
91
+ route_name="cornice_swagger.api_explorer_path",
92
+ )
@@ -0,0 +1,21 @@
1
+ """
2
+ This module handles the conversion between colander object schemas and swagger
3
+ object schemas.
4
+ """
5
+
6
+ from kinto.core.cornice_swagger.converters.parameters import ParameterConversionDispatcher
7
+ from kinto.core.cornice_swagger.converters.schema import TypeConversionDispatcher
8
+
9
+
10
+ def convert_schema(schema_node):
11
+ dispatcher = TypeConversionDispatcher()
12
+ converted = dispatcher(schema_node)
13
+
14
+ return converted
15
+
16
+
17
+ def convert_parameter(location, schema_node, definition_handler=convert_schema):
18
+ dispatcher = ParameterConversionDispatcher(definition_handler)
19
+ converted = dispatcher(location, schema_node)
20
+
21
+ return converted
@@ -0,0 +1,6 @@
1
+ class ConversionError(Exception):
2
+ pass
3
+
4
+
5
+ class NoSuchConverter(ConversionError):
6
+ pass