Skip to content

Commit 949668a

Browse files
authored
Merge branch 'master' into feature/manage-schedules-with-cron
2 parents 24cde12 + 5521615 commit 949668a

File tree

6 files changed

+59
-6
lines changed

6 files changed

+59
-6
lines changed

.github/workflows/stale.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,5 @@ jobs:
2525
stale-pr-message: 'This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days'
2626
stale-issue-message: 'This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days'
2727
exempt-issue-labels: 'feature,keep'
28-
days-before-stale: 45
29-
days-before-close: 30
28+
days-before-stale: 60
29+
days-before-close: 60

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ Refer to our [contributor documentation](CONTRIBUTING.md).
141141

142142
## Vulnerability Disclosures
143143

144-
Refer to our [vulnerability discolosure documentation](SECURITY.md) for submitting bugs.
144+
Refer to our [Vulnerability Disclosure Documentation](SECURITY.md) for submitting bugs.
145145

146146
## Licensing
147147

app/api/v2/handlers/planner_api.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ def add_routes(self, app: web.Application):
1717
router = app.router
1818
router.add_get('/planners', self.get_planners)
1919
router.add_get('/planners/{planner_id}', self.get_planner_by_id)
20+
router.add_patch('/planners/{planner_id}', self.update_planner)
2021

2122
@aiohttp_apispec.docs(tags=['planners'],
2223
summary='Retrieve planners',
@@ -49,3 +50,20 @@ async def get_planners(self, request: web.Request):
4950
async def get_planner_by_id(self, request: web.Request):
5051
planner = await self.get_object(request)
5152
return web.json_response(planner)
53+
54+
@aiohttp_apispec.docs(tags=['planners'],
55+
summary='Updates an existing planner.',
56+
description='Updates a planner based on the `PlannerSchema` value provided in the message body.',
57+
parameters=[{
58+
'in': 'path',
59+
'name': 'planner_id',
60+
'schema': {'type': 'string'},
61+
'required': 'true',
62+
'description': 'UUID of the Planner to be updated'
63+
}])
64+
@aiohttp_apispec.request_schema(PlannerSchema(partial=True))
65+
@aiohttp_apispec.response_schema(PlannerSchema(partial=True),
66+
description='JSON dictionary representation of the replaced Planner.')
67+
async def update_planner(self, request: web.Request):
68+
planner = await self.update_on_disk_object(request)
69+
return web.json_response(planner.display)

app/api/v2/responses.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1+
import json
2+
13
from aiohttp import web
24
from json import JSONDecodeError
5+
6+
from aiohttp.web_exceptions import HTTPUnprocessableEntity
37
from marshmallow.exceptions import ValidationError
48

59
from app.api.v2 import errors
@@ -51,9 +55,9 @@ async def apispec_request_validation_middleware(request, handler):
5155
)
5256
except ValidationError as ex:
5357
# ex: List of objects sent when single object expected
54-
raise JsonHttpBadRequest(
55-
error='Error parsing JSON: Could not validate Schema',
56-
details=str(ex)
58+
formatted_message = json.dumps({"json": ex.messages}, indent=2)
59+
raise HTTPUnprocessableEntity(
60+
text=formatted_message
5761
)
5862
except JSONDecodeError as ex:
5963
raise JsonHttpBadRequest(

app/objects/c_planner.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@ class PlannerSchema(ma.Schema):
1818
allow_repeatable_abilities = ma.fields.Boolean()
1919
plugin = ma.fields.String(load_default=None)
2020

21+
@ma.pre_load
22+
def fix_id(self, data, **_):
23+
if 'planner_id' in data:
24+
data['id'] = data.pop('planner_id')
25+
return data
26+
2127
@ma.post_load()
2228
def build_planner(self, data, **kwargs):
2329
return None if kwargs.get('partial') is True else Planner(**data)
@@ -54,6 +60,8 @@ def store(self, ram):
5460
existing.update('stopping_conditions', self.stopping_conditions)
5561
existing.update('params', self.params)
5662
existing.update('plugin', self.plugin)
63+
existing.update('description', self.description)
64+
existing.update('allow_repeatable_abilities', self.allow_repeatable_abilities)
5765
return existing
5866

5967
async def which_plugin(self):

tests/api/v2/handlers/test_planners_api.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,13 @@ def expected_test_planner_dump(test_planner):
2626
return test_planner.display_schema.dump(test_planner)
2727

2828

29+
@pytest.fixture
30+
def updated_planner(test_planner):
31+
planner_dict = test_planner.schema.dump(test_planner)
32+
planner_dict.update(dict(description="a test planner with updated description"))
33+
return planner_dict
34+
35+
2936
class TestPlannersApi:
3037
async def test_get_planners(self, api_v2_client, api_cookies, test_planner, expected_test_planner_dump):
3138
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,
5764
assert len(planners_list) == 2
5865
assert planners_list[0]["id"] == "456"
5966
assert planners_list[0]["name"][0] > planners_list[1]["name"][0] # prove that this wasn't an alphabetical sort
67+
68+
async def test_update_planner(self, api_v2_client, api_cookies, test_planner, updated_planner, mocker):
69+
with mocker.patch('app.api.v2.managers.base_api_manager.BaseApiManager.strip_yml') as mock_strip_yml:
70+
mock_strip_yml.return_value = [test_planner.schema.dump(test_planner)]
71+
resp = await api_v2_client.patch('/api/v2/planners/123', cookies=api_cookies, json=updated_planner)
72+
assert resp.status == HTTPStatus.OK
73+
planner = (await BaseService.get_service('data_svc').locate('planners'))[0]
74+
assert planner.description == updated_planner["description"]
75+
76+
async def test_unauthorized_update_planner(self, api_v2_client, updated_planner):
77+
resp = await api_v2_client.patch('/api/v2/planners/123', json=updated_planner)
78+
assert resp.status == HTTPStatus.UNAUTHORIZED
79+
80+
async def test_update_nonexistent_planner(self, api_v2_client, api_cookies, updated_planner):
81+
resp = await api_v2_client.patch('/api/v2/planners/999', cookies=api_cookies, json=updated_planner)
82+
assert resp.status == HTTPStatus.NOT_FOUND

0 commit comments

Comments
 (0)