summaryrefslogtreecommitdiff
path: root/src/mailman/rest/docs/helpers.rst
diff options
context:
space:
mode:
authorBarry Warsaw2011-09-23 21:42:39 -0400
committerBarry Warsaw2011-09-23 21:42:39 -0400
commit48354a7e6814190455fb566947ab952062ecde76 (patch)
tree874a9afe0c5ca798a83daa8c6462da6ecaecb2bf /src/mailman/rest/docs/helpers.rst
parent87966acc80cf4dabfb7f9d3019f62483376e2037 (diff)
downloadmailman-48354a7e6814190455fb566947ab952062ecde76.tar.gz
mailman-48354a7e6814190455fb566947ab952062ecde76.tar.zst
mailman-48354a7e6814190455fb566947ab952062ecde76.zip
Finally, all doctests are named .rst
Diffstat (limited to 'src/mailman/rest/docs/helpers.rst')
-rw-r--r--src/mailman/rest/docs/helpers.rst202
1 files changed, 202 insertions, 0 deletions
diff --git a/src/mailman/rest/docs/helpers.rst b/src/mailman/rest/docs/helpers.rst
new file mode 100644
index 000000000..4f0b1c804
--- /dev/null
+++ b/src/mailman/rest/docs/helpers.rst
@@ -0,0 +1,202 @@
+================
+REST API helpers
+================
+
+There are a number of helpers that make building out the REST API easier.
+
+
+Resource paths
+==============
+
+For example, most resources don't have to worry about where they are rooted.
+They only need to know where they are relative to the root URI, and this
+function can return them the full path to the resource.
+
+ >>> from mailman.rest.helpers import path_to
+ >>> print path_to('system')
+ http://localhost:9001/3.0/system
+
+Parameters like the ``scheme``, ``host``, ``port``, and API version number can
+be set in the configuration file.
+::
+
+ >>> config.push('helpers', """
+ ... [webservice]
+ ... hostname: geddy
+ ... port: 2112
+ ... use_https: yes
+ ... api_version: 4.2
+ ... """)
+
+ >>> print path_to('system')
+ https://geddy:2112/4.2/system
+
+
+Etags
+=====
+
+HTTP *etags* are a way for clients to decide whether their copy of a resource
+has changed or not. Mailman's REST API calculates this in a cheap and dirty
+way. Pass in the dictionary representing the resource and that dictionary
+gets modified to contain the etag under the ``http_etag`` key.
+
+ >>> from mailman.rest.helpers import etag
+ >>> resource = dict(geddy='bass', alex='guitar', neil='drums')
+ >>> json_data = etag(resource)
+ >>> print resource['http_etag']
+ "43942176d8d5bb4414ccf35e2720ccd5251e66da"
+
+For convenience, the etag function also returns the JSON representation of the
+dictionary after tagging, since that's almost always what you want.
+::
+
+ >>> import json
+ >>> data = json.loads(json_data)
+
+ # This is pretty close to what we want, so it's convenient to use.
+ >>> dump_msgdata(data)
+ alex : guitar
+ geddy : bass
+ http_etag: "43942176d8d5bb4414ccf35e2720ccd5251e66da"
+ neil : drums
+
+
+POST unpacking
+==============
+
+Another helper unpacks ``POST`` request variables, validating and converting
+their values.
+::
+
+ >>> from mailman.rest.validator import Validator
+ >>> validator = Validator(one=int, two=unicode, three=bool)
+
+ >>> class FakeRequest:
+ ... POST = {}
+ >>> FakeRequest.POST = dict(one='1', two='two', three='yes')
+
+On valid input, the validator can be used as a ``**keyword`` argument.
+
+ >>> def print_request(one, two, three):
+ ... print repr(one), repr(two), repr(three)
+ >>> print_request(**validator(FakeRequest))
+ 1 u'two' True
+
+On invalid input, an exception is raised.
+
+ >>> FakeRequest.POST['one'] = 'hello'
+ >>> print_request(**validator(FakeRequest))
+ Traceback (most recent call last):
+ ...
+ ValueError: Cannot convert parameters: one
+
+On missing input, an exception is raised.
+
+ >>> del FakeRequest.POST['one']
+ >>> print_request(**validator(FakeRequest))
+ Traceback (most recent call last):
+ ...
+ ValueError: Missing parameters: one
+
+If more than one key is missing, it will be reflected in the error message.
+
+ >>> del FakeRequest.POST['two']
+ >>> print_request(**validator(FakeRequest))
+ Traceback (most recent call last):
+ ...
+ ValueError: Missing parameters: one, two
+
+Extra keys are also not allowed.
+
+ >>> FakeRequest.POST = dict(one='1', two='two', three='yes',
+ ... four='', five='')
+ >>> print_request(**validator(FakeRequest))
+ Traceback (most recent call last):
+ ...
+ ValueError: Unexpected parameters: five, four
+
+However, if optional keys are missing, it's okay.
+::
+
+ >>> validator = Validator(one=int, two=unicode, three=bool,
+ ... four=int, five=int,
+ ... _optional=('four', 'five'))
+
+ >>> FakeRequest.POST = dict(one='1', two='two', three='yes',
+ ... four='4', five='5')
+ >>> def print_request(one, two, three, four=None, five=None):
+ ... print repr(one), repr(two), repr(three), repr(four), repr(five)
+ >>> print_request(**validator(FakeRequest))
+ 1 u'two' True 4 5
+
+ >>> del FakeRequest.POST['four']
+ >>> print_request(**validator(FakeRequest))
+ 1 u'two' True None 5
+
+ >>> del FakeRequest.POST['five']
+ >>> print_request(**validator(FakeRequest))
+ 1 u'two' True None None
+
+But if the optional values are present, they must of course also be valid.
+
+ >>> FakeRequest.POST = dict(one='1', two='two', three='yes',
+ ... four='no', five='maybe')
+ >>> print_request(**validator(FakeRequest))
+ Traceback (most recent call last):
+ ...
+ ValueError: Cannot convert parameters: five, four
+
+
+Arrays
+------
+
+Some ``POST`` forms include more than one value for a particular key. This is
+how lists and arrays are modeled. The validator does the right thing with
+such form data. Specifically, when a key shows up multiple times in the form
+data, a list is given to the validator.
+::
+
+ # Of course we can't use a normal dictionary, but webob has a useful data
+ # type we can use.
+ >>> from webob.multidict import MultiDict
+ >>> form_data = MultiDict(one='1', many='3')
+ >>> form_data.add('many', '4')
+ >>> form_data.add('many', '5')
+
+This is a validation function that ensures the value is a list.
+
+ >>> def must_be_list(value):
+ ... if not isinstance(value, list):
+ ... raise ValueError('not a list')
+ ... return [int(item) for item in value]
+
+This is a validation function that ensure the value is *not* a list.
+
+ >>> def must_be_scalar(value):
+ ... if isinstance(value, list):
+ ... raise ValueError('is a list')
+ ... return int(value)
+
+And a validator to pull it all together.
+
+ >>> validator = Validator(one=must_be_scalar, many=must_be_list)
+ >>> FakeRequest.POST = form_data
+ >>> values = validator(FakeRequest)
+ >>> print values['one']
+ 1
+ >>> print values['many']
+ [3, 4, 5]
+
+The list values are guaranteed to be in the same order they show up in the
+form data.
+
+ >>> from webob.multidict import MultiDict
+ >>> form_data = MultiDict(one='1', many='3')
+ >>> form_data.add('many', '5')
+ >>> form_data.add('many', '4')
+ >>> FakeRequest.POST = form_data
+ >>> values = validator(FakeRequest)
+ >>> print values['one']
+ 1
+ >>> print values['many']
+ [3, 5, 4]