diff --git a/source/app/blueprints/rest/case/case_assets_routes.py b/source/app/blueprints/rest/case/case_assets_routes.py index 165a5c219..14c125d77 100644 --- a/source/app/blueprints/rest/case/case_assets_routes.py +++ b/source/app/blueprints/rest/case/case_assets_routes.py @@ -24,10 +24,15 @@ from flask_login import current_user from app import db -from app.blueprints.rest.case_comments import case_comment_update +from app.blueprints.case.case_comments import case_comment_update +from app.blueprints.rest.endpoints import endpoint_deprecated +from app.blueprints.rest.endpoints import response_api_deleted +from app.blueprints.rest.endpoints import response_api_error +from app.blueprints.rest.endpoints import response_api_created +from app.business.assets import assets_delete, assets_create, assets_get +from app.business.errors import BusinessProcessingError from app.datamgmt.case.case_assets_db import add_comment_to_asset from app.datamgmt.case.case_assets_db import create_asset -from app.datamgmt.case.case_assets_db import delete_asset from app.datamgmt.case.case_assets_db import delete_asset_comment from app.datamgmt.case.case_assets_db import get_asset from app.datamgmt.case.case_assets_db import get_asset_type_id @@ -113,38 +118,29 @@ def case_assets_state(caseid): @case_assets_rest_blueprint.route('/case/assets/add', methods=['POST']) +@endpoint_deprecated('POST', '/api/v2/cases//assets') @ac_requires_case_identifier(CaseAccessLevel.full_access) @ac_api_requires() -def add_asset(caseid): +def deprecated_add_asset(case_identifier): + asset_schema = CaseAssetsSchema() try: - # validate before saving - add_asset_schema = CaseAssetsSchema() - request_data = call_modules_hook('on_preload_asset_create', data=request.get_json(), caseid=caseid) - - add_asset_schema.is_unique_for_cid(caseid, request_data) - asset = add_asset_schema.load(request_data) - - asset = create_asset(asset=asset, - caseid=caseid, - user_id=current_user.id - ) - - if request_data.get('ioc_links'): - errors, _ = set_ioc_links(request_data.get('ioc_links'), asset.asset_id) - if errors: - return response_error('Encountered errors while linking IOC. Asset has still been updated.') + asset, msg = assets_create(case_identifier, request.get_json()) + return response_success(msg, asset_schema.dump(asset)) + except BusinessProcessingError as e: + return response_error(e.get_message()) - asset = call_modules_hook('on_postload_asset_create', data=asset, caseid=caseid) - if asset: - track_activity(f"added asset \"{asset.asset_name}\"", caseid=caseid) - return response_success("Asset added", data=add_asset_schema.dump(asset)) - - return response_error("Unable to create asset for internal reasons") - - except marshmallow.exceptions.ValidationError as e: - db.session.rollback() - return response_error(msg="Data error", data=e.messages) +@case_assets_rest_blueprint.route('/api/v2/cases//assets', methods=['POST']) +@ac_requires_case_identifier(CaseAccessLevel.full_access) +@ac_api_requires() +def add_asset(caseid): + asset_schema = CaseAssetsSchema() + try: + asset_schema.is_unique_for_cid(caseid, request.get_json()) + asset, _ = assets_create(caseid, request.get_json()) + return response_api_created(asset_schema.dump(asset)) + except BusinessProcessingError as e: + return response_api_error(e.get_message()) @case_assets_rest_blueprint.route('/case/assets/upload', methods=['POST']) @@ -247,23 +243,26 @@ def case_upload_ioc(caseid): @case_assets_rest_blueprint.route('/case/assets/', methods=['GET']) +@endpoint_deprecated('GET', '/api/v2/assets/') @ac_requires_case_identifier(CaseAccessLevel.read_only, CaseAccessLevel.full_access) @ac_api_requires() -def asset_view(cur_id, caseid): - # Get IoCs already linked to the asset - asset_iocs = get_linked_iocs_finfo_from_asset(cur_id) - - ioc_prefill = [row._asdict() for row in asset_iocs] - - asset = get_asset(cur_id, caseid) - if not asset: - return response_error("Invalid asset ID for this case") +def deprecated_asset_view(cur_id, caseid): + try: + asset = assets_get(cur_id, caseid) + return response_success(asset) + except BusinessProcessingError as e: + return response_error(e.get_message()) - asset_schema = CaseAssetsSchema() - data = asset_schema.dump(asset) - data['linked_ioc'] = ioc_prefill - return response_success(data=data) +@case_assets_rest_blueprint.route('/api/v2/assets/', methods=['GET']) +@ac_requires_case_identifier(CaseAccessLevel.read_only, CaseAccessLevel.full_access) +@ac_api_requires() +def asset_view(cur_id, caseid): + try: + asset = assets_get(cur_id, caseid) + return response_api_created(asset) + except BusinessProcessingError as e: + return response_api_error(e.get_message()) @case_assets_rest_blueprint.route('/case/assets/update/', methods=['POST']) @@ -307,23 +306,26 @@ def asset_update(cur_id, caseid): @case_assets_rest_blueprint.route('/case/assets/delete/', methods=['POST']) +@endpoint_deprecated('DELETE', '/api/v2/assets/') @ac_requires_case_identifier(CaseAccessLevel.full_access) @ac_api_requires() -def asset_delete(cur_id, caseid): - call_modules_hook('on_preload_asset_delete', data=cur_id, caseid=caseid) - - asset = get_asset(cur_id, caseid) - if not asset: +def deprecated_asset_delete(cur_id, caseid): + try: + assets_delete(cur_id, caseid) + return response_success("Deleted") + except BusinessProcessingError as _: return response_error("Invalid asset ID for this case") - # Deletes an asset and the potential links with the IoCs from the database - delete_asset(cur_id, caseid) - - call_modules_hook('on_postload_asset_delete', data=cur_id, caseid=caseid) - - track_activity(f"removed asset ID {asset.asset_name}", caseid=caseid) - return response_success("Deleted") +@case_assets_rest_blueprint.route('/api/v2/assets/', methods=['DELETE']) +@ac_requires_case_identifier(CaseAccessLevel.full_access) +@ac_api_requires() +def asset_delete(cur_id, caseid): + try: + assets_delete(cur_id, caseid) + return response_api_deleted() + except BusinessProcessingError as e: + return response_api_error(e.get_message()) @case_assets_rest_blueprint.route('/case/assets//comments/list', methods=['GET']) @@ -402,4 +404,4 @@ def case_comment_asset_delete(cur_id, com_id, caseid): call_modules_hook('on_postload_asset_comment_delete', data=com_id, caseid=caseid) track_activity(f"comment {com_id} on asset {cur_id} deleted", caseid=caseid) - return response_success(msg) + return response_success(msg) \ No newline at end of file diff --git a/source/app/blueprints/rest/case/case_ioc_routes.py b/source/app/blueprints/rest/case/case_ioc_routes.py index 80134b66a..dba3d27e1 100644 --- a/source/app/blueprints/rest/case/case_ioc_routes.py +++ b/source/app/blueprints/rest/case/case_ioc_routes.py @@ -224,7 +224,7 @@ def case_upload_ioc(caseid): @endpoint_deprecated('DELETE', '/api/v2/iocs/') @ac_requires_case_identifier(CaseAccessLevel.full_access) @ac_api_requires() -def case_delete_ioc(cur_id, caseid): +def deprecated_case_delete_ioc(cur_id, caseid): try: msg = iocs_delete(cur_id, caseid) @@ -237,9 +237,8 @@ def case_delete_ioc(cur_id, caseid): @case_ioc_rest_blueprint.route('/api/v2/iocs/', methods=['DELETE']) @ac_requires_case_identifier(CaseAccessLevel.full_access) @ac_api_requires() -def delete_case_ioc(cur_id, caseid): +def case_delete_ioc(cur_id, caseid): try: - iocs_delete(cur_id, caseid) return response_api_deleted() @@ -251,7 +250,7 @@ def delete_case_ioc(cur_id, caseid): @endpoint_deprecated('GET', '/api/v2/iocs/') @ac_requires_case_identifier(CaseAccessLevel.read_only, CaseAccessLevel.full_access) @ac_api_requires() -def case_view_ioc(cur_id, caseid): +def deprecated_case_view_ioc(cur_id, caseid): ioc_schema = IocSchema() ioc = get_ioc(cur_id, caseid) if not ioc: @@ -263,7 +262,7 @@ def case_view_ioc(cur_id, caseid): @case_ioc_rest_blueprint.route('/api/v2/iocs/', methods=['GET']) @ac_requires_case_identifier(CaseAccessLevel.read_only, CaseAccessLevel.full_access) @ac_api_requires() -def get_case_ioc(cur_id, caseid): +def case_view_ioc(cur_id, caseid): ioc_schema = IocSchema() ioc = get_ioc(cur_id, caseid) if not ioc: diff --git a/source/app/blueprints/rest/case/case_tasks_routes.py b/source/app/blueprints/rest/case/case_tasks_routes.py index 49a1e8a28..7e040a376 100644 --- a/source/app/blueprints/rest/case/case_tasks_routes.py +++ b/source/app/blueprints/rest/case/case_tasks_routes.py @@ -30,9 +30,9 @@ from app.blueprints.rest.endpoints import response_api_error from app.blueprints.rest.endpoints import response_api_created from app.business.errors import BusinessProcessingError -from app.business.tasks import delete -from app.business.tasks import create -from app.business.tasks import update +from app.business.tasks import tasks_delete +from app.business.tasks import tasks_create +from app.business.tasks import tasks_update from app.datamgmt.case.case_tasks_db import add_comment_to_task from app.datamgmt.case.case_tasks_db import add_task from app.datamgmt.case.case_tasks_db import delete_task @@ -112,57 +112,32 @@ def case_task_statusupdate(cur_id, caseid): @endpoint_deprecated('POST', '/api/v2/cases//tasks') @ac_requires_case_identifier(CaseAccessLevel.full_access) @ac_api_requires() -def case_add_task(caseid): - +def deprecated_case_add_task(caseid): task_schema = CaseTaskSchema() try: - case, msg = create(caseid, request.get_json()) + case, msg = tasks_create(caseid, request.get_json()) return response_success(msg, data=task_schema.dump(case)) except BusinessProcessingError as e: return response_error(e.get_message(), data=e.get_data()) - except marshmallow.exceptions.ValidationError as e: - return response_error(msg="Data error", data=e.messages) @case_tasks_rest_blueprint.route('/api/v2/cases//tasks', methods=['POST']) @ac_requires_case_identifier(CaseAccessLevel.full_access) @ac_api_requires() -def api_case_add_task(caseid): +def case_add_task(caseid): + task_schema = CaseTaskSchema() try: - # validate before saving - task_schema = CaseTaskSchema() - request_data = call_modules_hook('on_preload_task_create', data=request.get_json(), caseid=caseid) - - if 'task_assignee_id' in request_data or 'task_assignees_id' not in request_data: - return response_api_error('task_assignee_id is not valid anymore since v1.5.0') - - task_assignee_list = request_data['task_assignees_id'] - del request_data['task_assignees_id'] - task = task_schema.load(request_data) - - ctask = add_task(task=task, - assignee_id_list=task_assignee_list, - user_id=current_user.id, - caseid=caseid - ) - - ctask = call_modules_hook('on_postload_task_create', data=ctask, caseid=caseid) - - if ctask: - track_activity(f"added task \"{ctask.task_title}\"", caseid=caseid) - return response_api_created(task_schema.dump(ctask)) - - return response_api_error("Unable to create task for internal reasons") - - except marshmallow.exceptions.ValidationError as e: - return response_api_error(e.messages) + case, _ = tasks_create(caseid, request.get_json()) + return response_api_created(task_schema.dump(case)) + except BusinessProcessingError as e: + return response_api_error(e.get_message()) @case_tasks_rest_blueprint.route('/case/tasks/', methods=['GET']) @endpoint_deprecated('GET', '/api/v2/tasks/') @ac_requires_case_identifier(CaseAccessLevel.read_only, CaseAccessLevel.full_access) @ac_api_requires() -def case_task_view(cur_id, caseid): +def deprecated_case_task_view(cur_id, caseid): task = get_task_with_assignees(task_id=cur_id, case_id=caseid) if not task: return response_error("Invalid task ID for this case") @@ -175,7 +150,7 @@ def case_task_view(cur_id, caseid): @case_tasks_rest_blueprint.route('/api/v2/tasks/', methods=['GET']) @ac_requires_case_identifier(CaseAccessLevel.read_only, CaseAccessLevel.full_access) @ac_api_requires() -def api_case_task_view(cur_id, caseid): +def case_task_view(cur_id, caseid): task = get_task_with_assignees(task_id=cur_id, case_id=caseid) if not task: return response_api_error("Invalid task ID for this case") @@ -190,7 +165,7 @@ def api_case_task_view(cur_id, caseid): @ac_api_requires() def case_edit_task(cur_id, caseid): try: - msg = update(cur_id, caseid, request.get_json()) + msg = tasks_update(cur_id, caseid, request.get_json()) return response_success(msg) except marshmallow.exceptions.ValidationError as e: return response_error(msg="Data error", data=e.messages) @@ -200,9 +175,9 @@ def case_edit_task(cur_id, caseid): @endpoint_deprecated('DELETE', '/api/v2/tasks/') @ac_requires_case_identifier(CaseAccessLevel.full_access) @ac_api_requires() -def case_delete_task(cur_id, caseid): +def deprecated_case_delete_task(cur_id, caseid): try: - msg = delete(cur_id, caseid) + msg = tasks_delete(cur_id, caseid) return response_success(msg) except BusinessProcessingError as e: return response_error(e.get_message()) @@ -211,21 +186,12 @@ def case_delete_task(cur_id, caseid): @case_tasks_rest_blueprint.route('/api/v2/tasks/', methods=['DELETE']) @ac_requires_case_identifier(CaseAccessLevel.full_access) @ac_api_requires() -def api_case_delete_task(cur_id, caseid): - call_modules_hook('on_preload_task_delete', data=cur_id, caseid=caseid) - task = get_task_with_assignees(task_id=cur_id, case_id=caseid) - if not task: - return response_api_error("Invalid task ID for this case") - - delete_task(task.id) - - update_tasks_state(caseid=caseid) - - call_modules_hook('on_postload_task_delete', data=cur_id, caseid=caseid) - - track_activity(f"deleted task \"{task.task_title}\"") - - return response_api_deleted() +def case_delete_task(cur_id, caseid): + try: + tasks_delete(cur_id, caseid) + return response_api_deleted() + except BusinessProcessingError as e: + return response_api_error(e.get_message()) @case_tasks_rest_blueprint.route('/case/tasks//comments/list', methods=['GET']) diff --git a/source/app/business/assets.py b/source/app/business/assets.py new file mode 100644 index 000000000..222da1696 --- /dev/null +++ b/source/app/business/assets.py @@ -0,0 +1,78 @@ +# IRIS Source Code +# Copyright (C) 2024 - DFIR-IRIS +# contact@dfir-iris.org +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 3 of the License, or (at your option) any later version. +# +# This program 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +from flask_login import current_user +from marshmallow.exceptions import ValidationError + +from app.business.errors import BusinessProcessingError +from app.datamgmt.case.case_assets_db import get_asset, create_asset, set_ioc_links, get_linked_iocs_finfo_from_asset +from app.datamgmt.case.case_assets_db import delete_asset +from app.iris_engine.module_handler.module_handler import call_modules_hook +from app.iris_engine.utils.tracker import track_activity +from app.schema.marshables import CaseAssetsSchema + + +def _load(request_data): + try: + add_assets_schema = CaseAssetsSchema() + return add_assets_schema.load(request_data) + except ValidationError as e: + raise BusinessProcessingError('Data error', e.messages) + + +def assets_create(case_identifier, request_json): + request_data = call_modules_hook('on_preload_asset_create', data=request_json, caseid=case_identifier) + asset = _load(request_data) + asset = create_asset(asset=asset, + caseid=case_identifier, + user_id=current_user.id + ) + if request_data.get('ioc_links'): + errors, _ = set_ioc_links(request_data.get('ioc_links'), asset.asset_id) + if errors: + raise BusinessProcessingError('Encountered errors while linking IOC. Asset has still been updated.') + asset = call_modules_hook('on_postload_asset_create', data=asset, caseid=case_identifier) + if asset: + track_activity(f"added asset \"{asset.asset_name}\"", caseid=case_identifier) + return "Asset added", asset + + raise BusinessProcessingError("Unable to create asset for internal reasons") + + +def assets_delete(identifier, case_identifier): + call_modules_hook('on_preload_asset_delete', data=identifier, caseid=case_identifier) + asset = get_asset(identifier, case_identifier) + if not asset: + raise BusinessProcessingError("Invalid asset ID for this case") + # Deletes an asset and the potential links with the IoCs from the database + delete_asset(identifier, case_identifier) + call_modules_hook('on_postload_asset_delete', data=identifier, caseid=case_identifier) + track_activity(f"removed asset ID {asset.asset_name}", caseid=case_identifier) + return "Deleted" + + +def assets_get(identifier, case_identifier): + asset_iocs = get_linked_iocs_finfo_from_asset(identifier) + ioc_prefill = [row._asdict() for row in asset_iocs] + + asset = get_asset(identifier, case_identifier) + if not asset: + raise BusinessProcessingError("Invalid asset ID for this case") + + data = _load.dump(asset) + data['linked_ioc'] = ioc_prefill + return data \ No newline at end of file diff --git a/source/app/business/tasks.py b/source/app/business/tasks.py index 4f9efbcea..82067d312 100644 --- a/source/app/business/tasks.py +++ b/source/app/business/tasks.py @@ -40,7 +40,7 @@ def _load(request_data): raise BusinessProcessingError('Data error', e.messages) -def delete(identifier, context_case_identifier): +def tasks_delete(identifier, context_case_identifier): call_modules_hook('on_preload_task_delete', data=identifier, caseid=context_case_identifier) task = get_task_with_assignees(task_id=identifier, case_id=context_case_identifier) if not task: @@ -52,7 +52,7 @@ def delete(identifier, context_case_identifier): return 'Task deleted' -def create(case_identifier, request_json): +def tasks_create(case_identifier, request_json): request_data = call_modules_hook('on_preload_task_create', data=request_json, caseid=case_identifier) @@ -77,7 +77,7 @@ def create(case_identifier, request_json): raise BusinessProcessingError("Unable to create task for internal reasons") -def update(current_identifier, case_identifier, request_json): +def tasks_update(current_identifier, case_identifier, request_json): task = get_task_with_assignees(task_id=current_identifier, case_id=case_identifier) diff --git a/source/app/datamgmt/case/case_iocs_db.py b/source/app/datamgmt/case/case_iocs_db.py index c310da3e0..d6fc22a55 100644 --- a/source/app/datamgmt/case/case_iocs_db.py +++ b/source/app/datamgmt/case/case_iocs_db.py @@ -279,6 +279,7 @@ def delete_ioc_comment(ioc_id, comment_id): return True, "Comment deleted" + def get_ioc_by_value(ioc_value, caseid=None): if caseid: Ioc.query.filter(Ioc.ioc_value == ioc_value, Ioc.case_id == caseid).first() diff --git a/source/app/static/assets/js/iris/case.asset.js b/source/app/static/assets/js/iris/case.asset.js index 31022ff43..0cd292cfc 100644 --- a/source/app/static/assets/js/iris/case.asset.js +++ b/source/app/static/assets/js/iris/case.asset.js @@ -74,15 +74,15 @@ function add_assets() { } data['custom_attributes'] = attributes; - - post_request_api('assets/add', JSON.stringify(data), true, function () { + case_id = get_caseid() + post_request_api(`/api/v2/cases/${case_id}/assets`, JSON.stringify(data), true, function () { $('#submit_new_assets').text('Saving data..') .attr("disabled", true) .removeClass('bt-outline-success') .addClass('btn-success', 'text-dark'); }) - .done((data) => { - if (data.status == 'success') { + .done((data, textStatus) => { + if (textStatus == 'success') { reload_assets(); if (index == (assets_list.length - 1)) { $('#modal_add_asset').modal('hide'); @@ -162,9 +162,9 @@ function delete_asset(asset_id) { do_deletion_prompt("You are about to delete asset #" + asset_id) .then((doDelete) => { if (doDelete) { - post_request_api('/case/assets/delete/' + asset_id) - .done((data) => { - if (data.status == 'success') { + delete_request_api(`/api/v2/assets/` + asset_id) + .done((data, textStatus) => { + if (textStatus === 'nocontent') { reload_assets(); $('#modal_add_asset').modal('hide'); notify_success('Asset deleted'); diff --git a/tests/iris.py b/tests/iris.py index 9fc457461..563f94a60 100644 --- a/tests/iris.py +++ b/tests/iris.py @@ -134,13 +134,4 @@ def get_cases_filter(self): return self._api.get('/manage/cases/filter').json() def execute_graphql_query(self, payload): - return self._administrator.execute_graphql_query(payload) - - def add_tasks(self, case_identifier, body): - return self._api.post(f'/api/v2/cases/{case_identifier}/tasks', body) - - def get_tasks(self, current_identifier): - return self._api.get(f'/api/v2/tasks/{current_identifier}') - - def delete_tasks(self, current_identifier): - return self._api.delete(f'/api/v2/tasks/{current_identifier}') + return self._administrator.execute_graphql_query(payload) \ No newline at end of file diff --git a/tests/tests_rest.py b/tests/tests_rest.py index 96b77dba3..168c3f5ec 100644 --- a/tests/tests_rest.py +++ b/tests/tests_rest.py @@ -45,10 +45,6 @@ def tearDown(self): identifier = case['case_id'] self._subject.delete(f'/api/v2/cases/{identifier}') - def test_create_asset_should_not_fail(self): - response = self._subject.create_asset() - self.assertEqual('success', response['status']) - def test_get_api_version_should_not_fail(self): response = self._subject.get_api_version() self.assertEqual('success', response['status']) @@ -187,7 +183,33 @@ def test_delete_ioc_should_return_204(self): self.assertEqual(204, response.status_code) def test_delete_ioc_with_missing_ioc_identifier_should_return_404(self): - response = self._subject.delete('/api/v2/iocs/None') + response = self._subject.delete(f'/api/v2/iocs/{None}') + self.assertEqual(404, response.status_code) + + def test_delete_asset_should_return_204(self): + case_identifier = self._subject.create_dummy_case() + body = {"asset_type_id": "1", "asset_name": "admin_laptop_test"} + self._subject.create(f'/api/v2/cases/{case_identifier}/assets', body) + response = self._subject.delete(f'/api/v2/assets/1') + self.assertEqual(204, response.status_code) + + def test_delete_asset_with_missing_asset_identifier_should_return_404(self): + response = self._subject.delete(f'/api/v2/assets/{None}') + self.assertEqual(404, response.status_code) + + def test_create_asset_should_work(self): + case_identifier = self._subject.create_dummy_case() + body = {"asset_type_id": "1", "asset_name": "admin_laptop_test"} + response = self._subject.create(f'/api/v2/cases/{case_identifier}/assets', body) + self.assertEqual(201, response.status_code) + + def test_create_asset_with_missing_case_identifier_should_return_404(self): + body = {"asset_type_id": "1", "asset_name": "admin_laptop_test"} + response = self._subject.create(f'/api/v2/cases/{None}/assets', body) + self.assertEqual(404, response.status_code) + + def test_get_asset_with_missing_asset_identifier_should_return_404(self): + response = self._subject.get('/api/v2/asset/None') self.assertEqual(404, response.status_code) def test_create_alert_should_not_fail(self): @@ -234,39 +256,39 @@ def test_get_timeline_state_should_return_200(self): def test_add_task_should_return_201(self): case_identifier = self._subject.create_dummy_case() body = {"task_assignees_id": [1], "task_description": "", "task_status_id": 1, "task_tags": "", "task_title": "dummy title", "custom_attributes": {}} - response = self._subject.add_tasks(case_identifier, body) + response = self._subject.create(f'/api/v2/cases/{case_identifier}/tasks', body) self.assertEqual(201, response.status_code) def test_add_task_with_missing_task_title_identifier_should_return_400(self): case_identifier = self._subject.create_dummy_case() body = {"task_assignees_id": [1], "task_description": "", "task_status_id": 1, "task_tags": "", "custom_attributes": {}} - response = self._subject.add_tasks(case_identifier, body) + response = self._subject.create(f'/api/v2/cases/{case_identifier}/tasks', body) self.assertEqual(400, response.status_code) - def test_get_tasks_should_return_dummy_title(self): + def test_get_tasks_should_return_201(self): case_identifier = self._subject.create_dummy_case() task_id = 2 body = {"task_assignees_id": [task_id], "task_description": "", "task_status_id": 1, "task_tags": "", "task_title": "dummy title", "custom_attributes": {}} - self._subject.add_tasks(case_identifier, body) - response = self._subject.get_tasks(task_id).json() - self.assertEqual("dummy title", response['task_title']) + self._subject.create(f'/api/v2/cases/{case_identifier}/tasks', body) + response = self._subject.get(f'/api/v2/tasks/{task_id}') + self.assertEqual(201, response.status_code) - def test_get_tasks_with_missing_ioc_identifier_should_return_error(self): + def test_get_tasks_with_missing_task_identifier_should_return_error(self): case_identifier = self._subject.create_dummy_case() task_id = 1 body = {"task_assignees_id": [task_id], "task_description": "", "task_status_id": 1, "task_tags": "", "task_title": "dummy title", "custom_attributes": {}} - self._subject.add_tasks(case_identifier, body) - response = self._subject.get_tasks(None).json() - self.assertEqual('error', response['status']) + self._subject.create(f'/api/v2/cases/{case_identifier}/tasks', body) + response = self._subject.get(f'/api/v2/tasks/{None}') + self.assertEqual(404, response.status_code) def test_delete_task_should_return_204(self): case_identifier = self._subject.create_dummy_case() task_id = 1 body = {"task_assignees_id": [task_id], "task_description": "", "task_status_id": 1, "task_tags": "", "task_title": "dummy title", "custom_attributes": {}} - self._subject.add_tasks(case_identifier, body) - test = self._subject.delete_tasks(task_id) + self._subject.create(f'/api/v2/cases/{case_identifier}/tasks', body) + test = self._subject.delete(f'/api/v2/tasks/{task_id}') self.assertEqual(204, test.status_code) def test_delete_task_with_missing_task_identifier_should_return_404(self): @@ -274,8 +296,8 @@ def test_delete_task_with_missing_task_identifier_should_return_404(self): task_id = 1 body = {"task_assignees_id": [task_id], "task_description": "", "task_status_id": 1, "task_tags": "", "task_title": "dummy title", "custom_attributes": {}} - self._subject.add_tasks(case_identifier, body) - test = self._subject.delete_tasks(None) + self._subject.create(f'/api/v2/cases/{case_identifier}/tasks', body) + test = self._subject.delete(f'/api/v2/tasks/{None}') self.assertEqual(404, test.status_code) def test_get_cases_should_not_fail(self): @@ -332,7 +354,3 @@ def test_get_case_should_have_field_case_name(self): case_identifier = self._subject.create_dummy_case() response = self._subject.get(f'/api/v2/cases/{case_identifier}').json() self.assertIn('case_name', response) - - def test_user_update_should_not_fail(self): - response = self._subject.create('/user/update', {}) - self.assertEqual(200, response.status_code)