diff --git a/app/api/v2/handlers/planner_api.py b/app/api/v2/handlers/planner_api.py index f109907a8..8b9b358fd 100644 --- a/app/api/v2/handlers/planner_api.py +++ b/app/api/v2/handlers/planner_api.py @@ -17,6 +17,7 @@ def add_routes(self, app: web.Application): router = app.router router.add_get('/planners', self.get_planners) router.add_get('/planners/{planner_id}', self.get_planner_by_id) + router.add_patch('/planners/{planner_id}', self.update_planner) @aiohttp_apispec.docs(tags=['planners'], summary='Retrieve planners', @@ -49,3 +50,20 @@ async def get_planners(self, request: web.Request): async def get_planner_by_id(self, request: web.Request): planner = await self.get_object(request) return web.json_response(planner) + + @aiohttp_apispec.docs(tags=['planners'], + summary='Updates an existing planner.', + description='Updates a planner based on the `PlannerSchema` value provided in the message body.', + parameters=[{ + 'in': 'path', + 'name': 'planner_id', + 'schema': {'type': 'string'}, + 'required': 'true', + 'description': 'UUID of the Planner to be updated' + }]) + @aiohttp_apispec.request_schema(PlannerSchema(partial=True)) + @aiohttp_apispec.response_schema(PlannerSchema(partial=True), + description='JSON dictionary representation of the replaced Planner.') + async def update_planner(self, request: web.Request): + planner = await self.update_on_disk_object(request) + return web.json_response(planner.display) diff --git a/app/objects/c_planner.py b/app/objects/c_planner.py index 010c08752..ed06f2af0 100644 --- a/app/objects/c_planner.py +++ b/app/objects/c_planner.py @@ -18,6 +18,12 @@ class PlannerSchema(ma.Schema): allow_repeatable_abilities = ma.fields.Boolean() plugin = ma.fields.String(load_default=None) + @ma.pre_load + def fix_id(self, data, **_): + if 'planner_id' in data: + data['id'] = data.pop('planner_id') + return data + @ma.post_load() def build_planner(self, data, **kwargs): return None if kwargs.get('partial') is True else Planner(**data) @@ -54,6 +60,8 @@ def store(self, ram): existing.update('stopping_conditions', self.stopping_conditions) existing.update('params', self.params) existing.update('plugin', self.plugin) + existing.update('description', self.description) + existing.update('allow_repeatable_abilities', self.allow_repeatable_abilities) return existing async def which_plugin(self): diff --git a/tests/api/v2/handlers/test_planners_api.py b/tests/api/v2/handlers/test_planners_api.py index 47afc2b22..09b371115 100644 --- a/tests/api/v2/handlers/test_planners_api.py +++ b/tests/api/v2/handlers/test_planners_api.py @@ -26,6 +26,13 @@ def expected_test_planner_dump(test_planner): return test_planner.display_schema.dump(test_planner) +@pytest.fixture +def updated_planner(test_planner): + planner_dict = test_planner.schema.dump(test_planner) + planner_dict.update(dict(description="a test planner with updated description")) + return planner_dict + + class TestPlannersApi: async def test_get_planners(self, api_v2_client, api_cookies, test_planner, expected_test_planner_dump): resp = await api_v2_client.get('/api/v2/planners', cookies=api_cookies) @@ -57,3 +64,19 @@ async def test_planner_defaults(self, api_v2_client, api_cookies, test_planner, assert len(planners_list) == 2 assert planners_list[0]["id"] == "456" assert planners_list[0]["name"][0] > planners_list[1]["name"][0] # prove that this wasn't an alphabetical sort + + async def test_update_planner(self, api_v2_client, api_cookies, test_planner, updated_planner, mocker): + with mocker.patch('app.api.v2.managers.base_api_manager.BaseApiManager.strip_yml') as mock_strip_yml: + mock_strip_yml.return_value = [test_planner.schema.dump(test_planner)] + resp = await api_v2_client.patch('/api/v2/planners/123', cookies=api_cookies, json=updated_planner) + assert resp.status == HTTPStatus.OK + planner = (await BaseService.get_service('data_svc').locate('planners'))[0] + assert planner.description == updated_planner["description"] + + async def test_unauthorized_update_planner(self, api_v2_client, updated_planner): + resp = await api_v2_client.patch('/api/v2/planners/123', json=updated_planner) + assert resp.status == HTTPStatus.UNAUTHORIZED + + async def test_update_nonexistent_planner(self, api_v2_client, api_cookies, updated_planner): + resp = await api_v2_client.patch('/api/v2/planners/999', cookies=api_cookies, json=updated_planner) + assert resp.status == HTTPStatus.NOT_FOUND