From b18ad52f0e37705c946032bfd0cda20e50bdebdd Mon Sep 17 00:00:00 2001 From: nirajsingh Date: Tue, 30 Jan 2018 15:22:07 +0530 Subject: [PATCH] Implement update segment Added update segment functionality. Also added test cases that actually not covering the line of code but tested the update segment functionally. Partial-Implements: blueprint masakari-dashboard Change-Id: Ib0424ee076ff0e8cc8c3ea898d986d32cc195ed2 --- masakaridashboard/api/api.py | 7 +++ masakaridashboard/segments/forms.py | 39 ++++++++++++ masakaridashboard/segments/tables.py | 14 +++++ masakaridashboard/segments/tabs.py | 62 +++++++++---------- .../segments/templates/segments/_update.html | 6 ++ .../segments/templates/segments/update.html | 7 +++ masakaridashboard/segments/tests.py | 45 ++++++++++++-- masakaridashboard/segments/urls.py | 4 +- masakaridashboard/segments/views.py | 38 ++++++++++++ 9 files changed, 184 insertions(+), 38 deletions(-) create mode 100644 masakaridashboard/segments/templates/segments/_update.html create mode 100644 masakaridashboard/segments/templates/segments/update.html diff --git a/masakaridashboard/api/api.py b/masakaridashboard/api/api.py index 4ccbbba..fb031db 100644 --- a/masakaridashboard/api/api.py +++ b/masakaridashboard/api/api.py @@ -116,3 +116,10 @@ def get_segment(request, segment_id): def segment_delete(request, segment_id, ignore_missing=True): return openstack_connection(request).delete_segment( segment_id, ignore_missing) + + +@handle_errors(_("Unable to update segment"), []) +def segment_update(request, segment_id, fields_to_update): + """Update segment.""" + return openstack_connection(request).update_segment( + segment_id, **fields_to_update) diff --git a/masakaridashboard/segments/forms.py b/masakaridashboard/segments/forms.py index ee0d600..876a8f3 100644 --- a/masakaridashboard/segments/forms.py +++ b/masakaridashboard/segments/forms.py @@ -67,3 +67,42 @@ class CreateSegmentForm(forms.SelfHandlingForm): redirect = reverse('horizon:masakaridashboard:segments:index') exceptions.handle(request, msg, redirect=redirect) return True + + +class UpdateForm(forms.SelfHandlingForm): + uuid = forms.CharField(widget=forms.HiddenInput()) + name = forms.CharField( + label=_('Segment Name'), + widget=forms.TextInput(attrs={'maxlength': 255})) + recovery_method = forms.ChoiceField( + label=_('Recovery Method'), + choices=[('auto', 'auto'), + ('auto_priority', 'auto_priority'), + ('reserved_host', 'reserved_host'), + ('rh_priority', 'rh_priority')], + widget=forms.Select( + attrs={'class': 'switchable', + 'data-slug': 'recovery_method'}), + required=False + ) + description = forms.CharField( + label=_('Description'), + widget=forms.Textarea( + attrs={'width': "100%", 'cols': "80", 'rows': "5", }), + required=False + ) + + def handle(self, request, data): + try: + fields_to_update = {'name': data['name'], + 'recovery_method': data['recovery_method'], + 'description': data['description']} + api.segment_update(request, data['uuid'], fields_to_update) + msg = _('Successfully updated segment.') + messages.success(request, msg) + except Exception: + msg = _('Failed to update segment.') + redirect = reverse('horizon:masakaridashboard:segments:index') + exceptions.handle(request, msg, redirect=redirect) + + return True diff --git a/masakaridashboard/segments/tables.py b/masakaridashboard/segments/tables.py index d5b2980..5aaa76e 100644 --- a/masakaridashboard/segments/tables.py +++ b/masakaridashboard/segments/tables.py @@ -13,6 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. +from django.core.urlresolvers import reverse from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ungettext_lazy @@ -61,6 +62,18 @@ class DeleteSegment(tables.DeleteAction): api.segment_delete(request, segment_uuid, ignore_missing=True) +class UpdateSegment(tables.LinkAction): + name = "update" + verbose_name = _("Update Segment") + classes = ("ajax-modal",) + + def get_link_url(self, datum): + obj_id = datum.uuid + url = "horizon:masakaridashboard:segments:update" + + return reverse(url, args=[obj_id]) + + class FailoverSegmentTable(tables.DataTable): name = tables.WrappingColumn( @@ -83,3 +96,4 @@ class FailoverSegmentTable(tables.DataTable): name = "failover_segment" verbose_name = _("FailoverSegment") table_actions = (DeleteSegment, CreateSegment, SegmentFilterAction) + row_actions = (UpdateSegment,) diff --git a/masakaridashboard/segments/tabs.py b/masakaridashboard/segments/tabs.py index 5074fba..3abd828 100644 --- a/masakaridashboard/segments/tabs.py +++ b/masakaridashboard/segments/tabs.py @@ -1,31 +1,31 @@ -# Copyright (c) 2018 NTT DATA -# -# 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 django.utils.translation import ugettext_lazy as _ - -from horizon import tabs - - -class OverviewTab(tabs.Tab): - name = _("Segments") - slug = "segments" - template_name = ("masakaridashboard/segments/_detail_overview.html") - - def get_context_data(self, request): - return {"segment": self.tab_group.kwargs['segment']} - - -class SegmentDetailTabs(tabs.DetailTabsGroup): - slug = "segment_details" - tabs = (OverviewTab,) +# Copyright (C) 2018 NTT DATA +# +# 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 django.utils.translation import ugettext_lazy as _ + +from horizon import tabs + + +class OverviewTab(tabs.Tab): + name = _("Segments") + slug = "segments" + template_name = ("masakaridashboard/segments/_detail_overview.html") + + def get_context_data(self, request): + return {"segment": self.tab_group.kwargs['segment']} + + +class SegmentDetailTabs(tabs.DetailTabsGroup): + slug = "segment_details" + tabs = (OverviewTab,) diff --git a/masakaridashboard/segments/templates/segments/_update.html b/masakaridashboard/segments/templates/segments/_update.html new file mode 100644 index 0000000..90c46c5 --- /dev/null +++ b/masakaridashboard/segments/templates/segments/_update.html @@ -0,0 +1,6 @@ +{% extends "horizon/common/_modal_form.html" %} +{% load i18n %} +{% block modal-body-right %} +

{% trans "Description:" %}

+

{% trans "Modify name, recovery_method and description of a failover segment." %}

+{% endblock %} diff --git a/masakaridashboard/segments/templates/segments/update.html b/masakaridashboard/segments/templates/segments/update.html new file mode 100644 index 0000000..b17ee6c --- /dev/null +++ b/masakaridashboard/segments/templates/segments/update.html @@ -0,0 +1,7 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "Update Segment" %}{% endblock %} + +{% block main %} + {% include 'masakaridashboard/segment/_update.html' %} +{% endblock %} diff --git a/masakaridashboard/segments/tests.py b/masakaridashboard/segments/tests.py index 6e99d73..bbee210 100644 --- a/masakaridashboard/segments/tests.py +++ b/masakaridashboard/segments/tests.py @@ -13,11 +13,13 @@ # License for the specific language governing permissions and limitations # under the License. +import mock + from django.conf import settings from django.core.urlresolvers import reverse from django.test.utils import override_settings from django.utils.http import urlunquote -import mock +from openstack_dashboard.test import helpers from masakaridashboard.segments import tables as segment_table from masakaridashboard.test import helpers as test @@ -38,7 +40,8 @@ class SegmentTest(test.TestCase): self.assertEqual(res.status_code, 200) self.assertTemplateUsed(res, 'masakaridashboard/segments/index.html') mock_get_segment_list.assert_called_once_with( - filters={}, marker=None, paginate=True, request=mock.ANY) + filters={}, marker=None, paginate=True, + request=helpers.IsHttpRequest()) segments = res.context['failover_segment_table'].data self.assertItemsEqual(segments, self.masakari_segment.list()) @@ -62,7 +65,7 @@ class SegmentTest(test.TestCase): self.assertRedirectsNoFollow(res, INDEX_URL) mocked_create.assert_called_once_with( - mock.ANY, + helpers.IsHttpRequest(), form_data ) @@ -77,7 +80,8 @@ class SegmentTest(test.TestCase): self.assertEqual(res.status_code, 200) self.assertTemplateUsed(res, 'masakaridashboard/segments/index.html') mock_get_segment_list.assert_called_once_with( - filters=filters, marker=marker, paginate=True, request=mock.ANY) + filters=filters, marker=marker, paginate=True, + request=helpers.IsHttpRequest()) return res @@ -167,7 +171,7 @@ class SegmentTest(test.TestCase): self.assertNoFormErrors(res) self.assertRedirectsNoFollow(res, INDEX_URL) mocked_delete.assert_called_once_with( - mock.ANY, + helpers.IsHttpRequest(), segment.uuid, ignore_missing=True ) @@ -185,3 +189,34 @@ class SegmentTest(test.TestCase): self.assertTemplateUsed(res, 'horizon/common/_detail.html') self.assertTemplateUsed( res, 'masakaridashboard/segments/_detail_overview.html') + + def test_update(self): + segment_obj = self.masakari_segment.list()[0] + update_url = reverse('horizon:masakaridashboard:segments:update', + args=[segment_obj.uuid]) + segment_obj.name = 'fake' + form_data = { + 'uuid': segment_obj.uuid, + 'name': segment_obj.name, + 'recovery_method': segment_obj.recovery_method, + 'description': segment_obj.description} + + with mock.patch( + 'masakaridashboard.api.api.get_segment', + return_value=self.masakari_segment.list()[0]), mock.patch( + 'masakaridashboard.api.api.segment_update', + return_value=segment_obj) as mocked_update: + res = self.client.post(update_url, form_data) + self.assertNoFormErrors(res) + self.assertEqual(res.status_code, 302) + self.assertRedirectsNoFollow(res, INDEX_URL) + data_to_update = { + 'name': segment_obj.name, + 'recovery_method': segment_obj.recovery_method, + 'description': segment_obj.description} + + mocked_update.assert_called_once_with( + helpers.IsHttpRequest(), + segment_obj.uuid, + data_to_update + ) diff --git a/masakaridashboard/segments/urls.py b/masakaridashboard/segments/urls.py index e1012e7..35b3306 100644 --- a/masakaridashboard/segments/urls.py +++ b/masakaridashboard/segments/urls.py @@ -21,8 +21,8 @@ from masakaridashboard.segments import views SEGMENT = r'^(?P[^/]+)/%s$' urlpatterns = [ url(r'^$', views.IndexView.as_view(), name='index'), - url(r'^create_segment$', - views.CreateSegmentView.as_view(), + url(r'^create_segment$', views.CreateSegmentView.as_view(), name='create_segment'), url(SEGMENT % 'detail', views.DetailView.as_view(), name='detail'), + url(SEGMENT % 'update', views.UpdateView.as_view(), name='update'), ] diff --git a/masakaridashboard/segments/views.py b/masakaridashboard/segments/views.py index a57ce2d..8c08167 100644 --- a/masakaridashboard/segments/views.py +++ b/masakaridashboard/segments/views.py @@ -132,3 +132,41 @@ class DetailView(tabs.TabbedTableView): def get_tabs(self, request, *args, **kwargs): segment = self.get_data() return self.tab_group_class(request, segment=segment, **kwargs) + + +class UpdateView(forms.ModalFormView): + template_name = 'masakaridashboard/segments/update.html' + modal_header = _("Update Segment") + form_id = "update_segment" + form_class = segment_forms.UpdateForm + submit_label = _("Update") + submit_url = "horizon:masakaridashboard:segments:update" + success_url = reverse_lazy("horizon:masakaridashboard:segments:index") + page_title = _("Update Segment") + + @memoized.memoized_method + def get_object(self): + try: + segment = api.get_segment(self.request, self.kwargs['segment_id']) + return segment + except Exception: + msg = _('Unable to retrieve segment.') + redirect = reverse('horizon:masakaridashboard:segments:index') + exceptions.handle(self.request, msg, redirect=redirect) + + def get_context_data(self, **kwargs): + context = super(UpdateView, self).get_context_data(**kwargs) + context['submit_url'] = reverse( + self.submit_url, + args=[self.kwargs["segment_id"]] + ) + + return context + + def get_initial(self, **kwargs): + segment = self.get_object() + + return {'uuid': self.kwargs['segment_id'], + 'name': segment.name, + 'recovery_method': segment.recovery_method, + 'description': segment.description}