diff --git a/nailgun/nailgun/api/v1/handlers/notifications.py b/nailgun/nailgun/api/v1/handlers/notifications.py index c516b4ea61..e12b6212eb 100644 --- a/nailgun/nailgun/api/v1/handlers/notifications.py +++ b/nailgun/nailgun/api/v1/handlers/notifications.py @@ -19,15 +19,14 @@ Handlers dealing with notifications """ import web +from nailgun.api.v1.handlers.base import BaseHandler from nailgun.api.v1.handlers.base import CollectionHandler -from nailgun.api.v1.handlers.base import SingleHandler - -from nailgun import objects - from nailgun.api.v1.handlers.base import handle_errors from nailgun.api.v1.handlers.base import serialize +from nailgun.api.v1.handlers.base import SingleHandler from nailgun.api.v1.handlers.base import validate from nailgun.api.v1.validators.notification import NotificationValidator +from nailgun import objects class NotificationHandler(SingleHandler): @@ -89,3 +88,22 @@ class NotificationCollectionStatsHandler(CollectionHandler): :http: * 405 (Method not allowed) """ raise self.http(405) + + +class NotificationStatusHandler(BaseHandler): + + validator = NotificationValidator + + @handle_errors + @validate + @serialize + def PUT(self): + """Updates status of all notifications + + :http: * 200 (OK) + * 400 (Invalid data) + """ + web_data = web.data() + data = self.validator.validate_change_status(web_data) + status = data['status'] + objects.NotificationCollection.update_statuses(status) diff --git a/nailgun/nailgun/api/v1/urls.py b/nailgun/nailgun/api/v1/urls.py index 78dbeba013..ee472ac335 100644 --- a/nailgun/nailgun/api/v1/urls.py +++ b/nailgun/nailgun/api/v1/urls.py @@ -88,6 +88,9 @@ from nailgun.api.v1.handlers.plugin_link import PluginLinkHandler from nailgun.api.v1.handlers.notifications import NotificationCollectionHandler from nailgun.api.v1.handlers.notifications import \ NotificationCollectionStatsHandler +from nailgun.api.v1.handlers.notifications import \ + NotificationStatusHandler + from nailgun.api.v1.handlers.notifications import NotificationHandler from nailgun.api.v1.handlers.orchestrator import DefaultDeploymentInfo @@ -336,6 +339,8 @@ urls = ( r'/notifications/?$', NotificationCollectionHandler, + r'/notifications/change_status/?$', + NotificationStatusHandler, r'/notifications/(?P\d+)/?$', NotificationHandler, r'/notifications/stats/?$', diff --git a/nailgun/nailgun/api/v1/validators/json_schema/notification.py b/nailgun/nailgun/api/v1/validators/json_schema/notification.py new file mode 100644 index 0000000000..84f382b030 --- /dev/null +++ b/nailgun/nailgun/api/v1/validators/json_schema/notification.py @@ -0,0 +1,29 @@ +# Copyright 2017 Mirantis, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from nailgun import consts + + +NOTIFICATIONS_CHANGE_STATUS = { + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "required": ["status"], + "properties": { + "status": { + "type": "string", + "enum": list(consts.NOTIFICATION_STATUSES) + }, + + } +} diff --git a/nailgun/nailgun/api/v1/validators/notification.py b/nailgun/nailgun/api/v1/validators/notification.py index 36ca7dbffb..d40594e188 100644 --- a/nailgun/nailgun/api/v1/validators/notification.py +++ b/nailgun/nailgun/api/v1/validators/notification.py @@ -13,11 +13,11 @@ # License for the specific language governing permissions and limitations # under the License. -from nailgun import consts -from nailgun import objects - from nailgun.api.v1.validators.base import BasicValidator +from nailgun.api.v1.validators.json_schema import notification +from nailgun import consts from nailgun import errors +from nailgun import objects class NotificationValidator(BasicValidator): @@ -90,3 +90,9 @@ class NotificationValidator(BasicValidator): without any validations. """ pass + + @classmethod + def validate_change_status(cls, data): + parsed = super(NotificationValidator, cls).validate(data) + cls.validate_schema(parsed, notification.NOTIFICATIONS_CHANGE_STATUS) + return parsed diff --git a/nailgun/nailgun/objects/notification.py b/nailgun/nailgun/objects/notification.py index e7d2bb83e2..ec58405cb7 100644 --- a/nailgun/nailgun/objects/notification.py +++ b/nailgun/nailgun/objects/notification.py @@ -109,3 +109,8 @@ class Notification(NailgunObject): class NotificationCollection(NailgunCollection): single = Notification + + @classmethod + def update_statuses(cls, status): + db().query(cls.single.model).update( + {cls.single.model.status: status}, synchronize_session=False) diff --git a/nailgun/nailgun/test/unit/test_notification_handler.py b/nailgun/nailgun/test/unit/test_notification_handler.py index 69fd57ee3a..fa4b277d32 100644 --- a/nailgun/nailgun/test/unit/test_notification_handler.py +++ b/nailgun/nailgun/test/unit/test_notification_handler.py @@ -16,6 +16,7 @@ from oslo_serialization import jsonutils +from nailgun import consts from nailgun.test.base import BaseIntegrationTest from nailgun.utils import reverse @@ -140,3 +141,39 @@ class TestHandlers(BaseIntegrationTest): expect_errors=True ) self.assertEqual(405, resp.status_code) + + def test_notification_status(self): + self.env.create_notification(status=consts.NOTIFICATION_STATUSES.read) + self.env.create_notification( + status=consts.NOTIFICATION_STATUSES.unread) + self.env.create_notification( + status=consts.NOTIFICATION_STATUSES.unread) + + expected_status = consts.NOTIFICATION_STATUSES.unread + resp = self.app.put( + reverse('NotificationStatusHandler'), + params=jsonutils.dumps({'status': expected_status}), + headers=self.default_headers + ) + self.assertEqual(200, resp.status_code) + + # Checking statuses are changed + resp = self.app.get( + reverse('NotificationCollectionHandler'), + headers=self.default_headers + ) + self.assertEqual(200, resp.status_code) + for notif in resp.json_body: + self.assertEqual(expected_status, notif['status']) + + def test_notification_status_not_allowed_methods(self): + methods = ('get', 'post', 'delete', 'patch', 'head') + url = reverse('NotificationStatusHandler') + for m in methods: + method = getattr(self.app, m) + resp = method( + url, + headers=self.default_headers, + expect_errors=True + ) + self.assertEqual(405, resp.status_code)