From 8bcc1b209728b5b3c9e10db83fdb8f9ca2d1a40f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vincent=20Fran=C3=A7oise?= Date: Fri, 29 Jan 2016 18:03:59 +0100 Subject: [PATCH] Delete linked actions when deleting an action plan When a user deletes an action plan, we now delete all related actions. Partially Implements: blueprint deletion-of-actions-plan Change-Id: I5d519c38458741be78591cbec04dbd410a6dc14b --- watcher/objects/action_plan.py | 11 +++++++- watcher/tests/api/v1/test_actions.py | 32 ++++++++++++++-------- watcher/tests/api/v1/test_actions_plans.py | 26 +++++++++++++++++- 3 files changed, 55 insertions(+), 14 deletions(-) diff --git a/watcher/objects/action_plan.py b/watcher/objects/action_plan.py index e780ffe79..325d51785 100644 --- a/watcher/objects/action_plan.py +++ b/watcher/objects/action_plan.py @@ -71,6 +71,7 @@ state may be one of the following: from watcher.common import exception from watcher.common import utils from watcher.db import api as dbapi +from watcher.objects import action as action_objects from watcher.objects import base from watcher.objects import utils as obj_utils @@ -251,6 +252,14 @@ class ActionPlan(base.WatcherObject): A context should be set when instantiating the object, e.g.: Audit(context) """ + related_actions = action_objects.Action.list( + context=self._context, + filters={"action_plan_uuid": self.uuid}) + + # Cascade soft_delete of related actions + for related_action in related_actions: + related_action.soft_delete() + self.dbapi.soft_delete_action_plan(self.uuid) - self.state = "DELETED" + self.state = State.DELETED self.save() diff --git a/watcher/tests/api/v1/test_actions.py b/watcher/tests/api/v1/test_actions.py index e8a598260..0387fcdfa 100644 --- a/watcher/tests/api/v1/test_actions.py +++ b/watcher/tests/api/v1/test_actions.py @@ -285,33 +285,41 @@ class TestListAction(api_base.FunctionalTest): id=3, uuid=utils.generate_uuid(), audit_id=1) - action_list = [] + + ap1_action_list = [] + ap2_action_list = [] for id_ in range(0, 2): action = obj_utils.create_test_action( self.context, id=id_, - action_plan_id=2, + action_plan_id=action_plan1.id, uuid=utils.generate_uuid()) - action_list.append(action.uuid) + ap1_action_list.append(action) for id_ in range(2, 4): action = obj_utils.create_test_action( self.context, id=id_, - action_plan_id=3, + action_plan_id=action_plan2.id, uuid=utils.generate_uuid()) - action_list.append(action.uuid) + ap2_action_list.append(action) self.delete('/action_plans/%s' % action_plan1.uuid) response = self.get_json('/actions') - self.assertEqual(len(action_list), len(response['actions'])) - for id_ in range(0, 2): - action = response['actions'][id_] - self.assertEqual(None, action['action_plan_uuid']) + # We deleted the actions from the 1st action plan so we've got 2 left + self.assertEqual(len(ap2_action_list), len(response['actions'])) - for id_ in range(2, 4): - action = response['actions'][id_] - self.assertEqual(action_plan2.uuid, action['action_plan_uuid']) + # We deleted them so that's normal + self.assertEqual( + [act for act in response['actions'] + if act['action_plan_uuid'] == action_plan1.uuid], + []) + + # Here are the 2 actions left + self.assertEqual( + set([act['uuid'] for act in response['actions'] + if act['action_plan_uuid'] == action_plan2.uuid]), + set([act.as_dict()['uuid'] for act in ap2_action_list])) def test_many_with_next_uuid(self): action_list = [] diff --git a/watcher/tests/api/v1/test_actions_plans.py b/watcher/tests/api/v1/test_actions_plans.py index 16334033f..ef0336ec3 100644 --- a/watcher/tests/api/v1/test_actions_plans.py +++ b/watcher/tests/api/v1/test_actions_plans.py @@ -307,7 +307,7 @@ class TestDelete(api_base.FunctionalTest): action_plan = objects.ActionPlan.get_by_uuid(self.context, audit_uuid) action_plan.destroy() - def test_delete_action_plan(self): + def test_delete_action_plan_without_action(self): self.delete('/action_plans/%s' % self.action_plan.uuid) response = self.get_json('/action_plans/%s' % self.action_plan.uuid, expect_errors=True) @@ -315,6 +315,30 @@ class TestDelete(api_base.FunctionalTest): self.assertEqual('application/json', response.content_type) self.assertTrue(response.json['error_message']) + def test_delete_action_plan_with_action(self): + action = obj_utils.create_test_action( + self.context, id=self.action_plan.first_action_id) + + self.delete('/action_plans/%s' % self.action_plan.uuid) + ap_response = self.get_json('/action_plans/%s' % self.action_plan.uuid, + expect_errors=True) + acts_response = self.get_json( + '/actions/?action_plan_uuid=%s' % self.action_plan.uuid) + act_response = self.get_json( + '/actions/%s' % action.uuid, + expect_errors=True) + + # The action plan does not exist anymore + self.assertEqual(404, ap_response.status_int) + self.assertEqual('application/json', ap_response.content_type) + self.assertTrue(ap_response.json['error_message']) + + # Nor does the action + self.assertEqual(len(acts_response['actions']), 0) + self.assertEqual(404, act_response.status_int) + self.assertEqual('application/json', act_response.content_type) + self.assertTrue(act_response.json['error_message']) + def test_delete_action_plan_not_found(self): uuid = utils.generate_uuid() response = self.delete('/action_plans/%s' % uuid, expect_errors=True)