summaryrefslogtreecommitdiff
path: root/src/mailman/rest/header_matches.py
diff options
context:
space:
mode:
authorAurélien Bompard2016-02-02 18:37:35 +0100
committerBarry Warsaw2016-02-29 21:52:13 -0500
commit7604ceaae42beb8d001e0ec6850bcd65164d56d7 (patch)
tree51e1b68bdc6ce408fce8cba98c7c55fd21965528 /src/mailman/rest/header_matches.py
parent994660913bbd7dc08b8cef909b6715f43d37f0d5 (diff)
downloadmailman-7604ceaae42beb8d001e0ec6850bcd65164d56d7.tar.gz
mailman-7604ceaae42beb8d001e0ec6850bcd65164d56d7.tar.zst
mailman-7604ceaae42beb8d001e0ec6850bcd65164d56d7.zip
Diffstat (limited to 'src/mailman/rest/header_matches.py')
-rw-r--r--src/mailman/rest/header_matches.py165
1 files changed, 165 insertions, 0 deletions
diff --git a/src/mailman/rest/header_matches.py b/src/mailman/rest/header_matches.py
new file mode 100644
index 000000000..d93a90fd6
--- /dev/null
+++ b/src/mailman/rest/header_matches.py
@@ -0,0 +1,165 @@
+# Copyright (C) 2016 by the Free Software Foundation, Inc.
+#
+# This file is part of GNU Mailman.
+#
+# GNU Mailman is free software: you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or (at your option)
+# any later version.
+#
+# GNU Mailman is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+# more details.
+#
+# You should have received a copy of the GNU General Public License along with
+# GNU Mailman. If not, see <http://www.gnu.org/licenses/>.
+
+"""REST API for a mailing list's header matches."""
+
+__all__ = [
+ 'HeaderMatch',
+ 'HeaderMatches',
+ ]
+
+
+from mailman.interfaces.action import Action
+from mailman.interfaces.mailinglist import IHeaderMatchList
+from mailman.rest.helpers import (
+ CollectionMixin, GetterSetter, bad_request, child, created, etag,
+ no_content, not_found, okay)
+from mailman.rest.validator import Validator, enum_validator
+
+
+def lowercase(value):
+ return str(value).lower()
+
+
+class _HeaderMatchBase:
+ """Common base class."""
+
+ def __init__(self, mlist):
+ self._mlist = mlist
+ self.header_matches = IHeaderMatchList(self._mlist)
+
+ def _location(self, index):
+ return self.api.path_to('lists/{}/header-matches/{}'.format(self._mlist.list_id, index))
+
+ def _resource_as_dict(self, header_match):
+ """See `CollectionMixin`."""
+ resource = dict(
+ index=header_match.index,
+ header=header_match.header,
+ pattern=header_match.pattern,
+ self_link=self._location(header_match.index),
+ )
+ if header_match.action is not None:
+ resource['action'] = header_match.action
+ return resource
+
+
+class HeaderMatch(_HeaderMatchBase):
+ """A header match."""
+
+ def __init__(self, mlist, index):
+ super().__init__(mlist)
+ self._index = index
+
+ def on_get(self, request, response):
+ """Get a header match."""
+ try:
+ header_match = self.header_matches[self._index]
+ except IndexError:
+ not_found(response, 'No header match at this index: {}'.format(
+ self._index))
+ else:
+ okay(response, etag(self._resource_as_dict(header_match)))
+
+ def on_delete(self, request, response):
+ """Remove a header match."""
+ try:
+ del self.header_matches[self._index]
+ except IndexError:
+ not_found(response, 'No header match at this index: {}'.format(
+ self._index))
+ else:
+ no_content(response)
+
+ def patch_put(self, request, response, is_optional):
+ """Update the header match."""
+ try:
+ header_match = self.header_matches[self._index]
+ except IndexError:
+ not_found(response, 'No header match at this index: {}'.format(
+ self._index))
+ return
+ kws = dict(
+ header=GetterSetter(lowercase),
+ pattern=GetterSetter(str),
+ index=GetterSetter(int),
+ action=GetterSetter(enum_validator(Action)),
+ )
+ if is_optional:
+ # For a PATCH, all attributes are optional.
+ kws['_optional'] = kws.keys()
+ else:
+ # For a PUT, index can remain unchanged and action can be None.
+ kws['_optional'] = ('action', 'index')
+ try:
+ Validator(**kws).update(header_match, request)
+ except ValueError as error:
+ bad_request(response, str(error))
+ return
+ else:
+ no_content(response)
+
+ def on_put(self, request, response):
+ """Full update of the header match."""
+ self.patch_put(request, response, is_optional=False)
+
+ def on_patch(self, request, response):
+ """Partial update of the header match."""
+ self.patch_put(request, response, is_optional=True)
+
+
+class HeaderMatches(_HeaderMatchBase, CollectionMixin):
+ """The list of all header matches."""
+
+ def _get_collection(self, request):
+ """See `CollectionMixin`."""
+ return list(self.header_matches)
+
+ def on_get(self, request, response):
+ """/header-matches"""
+ resource = self._make_collection(request)
+ okay(response, etag(resource))
+
+ def on_post(self, request, response):
+ """Add a header match."""
+ validator = Validator(
+ header=str,
+ pattern=str,
+ action=enum_validator(Action),
+ _optional=('action',)
+ )
+ try:
+ arguments = validator(request)
+ except ValueError as error:
+ bad_request(response, str(error))
+ return
+ try:
+ self.header_matches.append(**arguments)
+ except ValueError:
+ bad_request(response, b'This header match already exists')
+ else:
+ header_match = self.header_matches[-1]
+ created(response, self._location(header_match.index))
+
+ def on_delete(self, request, response):
+ """Delete all header matches for this mailing list."""
+ self.header_matches.clear()
+ no_content(response)
+
+ @child(r'^(?P<index>\d+)')
+ def header_match(self, request, segments, **kw):
+ return HeaderMatch(self._mlist, int(kw['index']))