Skip to content

Commit 0b112b2

Browse files
ParthS007pamfilos
authored andcommitted
schemas: add GET/PATCH for notifications config
Signed-off-by: Parth Shandilya <parth.shandilya@cern.ch>
1 parent 5e72f21 commit 0b112b2

File tree

3 files changed

+201
-1
lines changed

3 files changed

+201
-1
lines changed

cap/modules/schemas/utils.py

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,11 @@
2424
"""Utils for Schemas module."""
2525

2626
import re
27-
27+
from functools import wraps
28+
from flask import abort
29+
from .permissions import AdminSchemaPermission
30+
from .models import Schema
31+
from invenio_jsonschemas.errors import JSONSchemaNotFound
2832
from jsonpatch import JsonPatchConflict
2933

3034

@@ -135,3 +139,29 @@ def get_default_mapping(name, version):
135139
}
136140
default_mapping["mappings"][mapping_name] = collectiion_mapping
137141
return default_mapping
142+
143+
144+
def get_schema(f):
145+
"""Decorator to check if schema exists by name and/or version."""
146+
@wraps(f)
147+
def wrapper(name=None, version=None, schema=None, *args, **kwargs):
148+
if name:
149+
try:
150+
if version:
151+
schema = Schema.get(name, version)
152+
else:
153+
schema = Schema.get_latest(name)
154+
except JSONSchemaNotFound:
155+
abort(404)
156+
return f(name=name, version=version, schema=schema, *args, **kwargs)
157+
return wrapper
158+
159+
160+
def schema_admin_permission(f):
161+
"""Decorator to check if user has admin permission."""
162+
@wraps(f)
163+
def wrapper(name=None, version=None, schema=None, *args, **kwargs):
164+
if not AdminSchemaPermission(schema).can():
165+
abort(403)
166+
return f(name=name, version=version, schema=schema, *args, **kwargs)
167+
return wrapper

cap/modules/schemas/views.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@
5353
check_allowed_patch_operation,
5454
check_allowed_patch_path,
5555
get_default_mapping,
56+
get_schema,
57+
schema_admin_permission,
5658
)
5759

5860
blueprint = Blueprint(
@@ -115,6 +117,62 @@ def permissions(name=None, version=None):
115117
return jsonify({}), 204
116118

117119

120+
@blueprint.route(
121+
'/<string:name>/notifications', methods=['GET', 'PATCH']
122+
)
123+
@blueprint.route(
124+
'/<string:name>/<string:version>/notifications',
125+
methods=['GET', 'PATCH'],
126+
)
127+
@get_schema
128+
@schema_admin_permission
129+
@super_admin_permission.require(http_exception=403)
130+
def notifications_config(name=None, version=None, schema=None, *args, **kwargs):
131+
"""CRUD operations for schema configuration."""
132+
serialized_config = schema.config_serialize()
133+
notifications_config = serialized_config.get("config", {}).get("notifications", {})
134+
if request.method == "PATCH":
135+
try:
136+
data = request.get_json()
137+
patched_notifications_config = apply_patch(notifications_config, data)
138+
patched_object = {'config': {'notifications': patched_notifications_config}}
139+
schema.update(**patched_object)
140+
db.session.commit()
141+
return jsonify(schema.config_serialize()), 201
142+
except (
143+
JsonPatchException,
144+
JsonPatchConflict,
145+
JsonPointerException,
146+
TypeError,
147+
) as err:
148+
return (
149+
jsonify(
150+
{
151+
'message': 'Could not apply '
152+
'json-patch to object: {}'.format(err)
153+
}
154+
),
155+
400,
156+
)
157+
except IntegrityError:
158+
return (
159+
jsonify(
160+
{
161+
'message': 'Error occured during '
162+
'saving schema in the db.'
163+
}
164+
),
165+
500,
166+
)
167+
except ValidationError as err:
168+
return (
169+
jsonify({'message': err.description, 'errors': err.errors}),
170+
400,
171+
)
172+
173+
return jsonify(notifications_config), 200
174+
175+
118176
class SchemaAPI(MethodView):
119177
"""CRUD views for Schema model."""
120178

tests/integration/schemas/test_schemas_views.py

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1620,3 +1620,115 @@ def test_patch_when_operation_undefined_returns_400(
16201620

16211621
assert resp.status_code == 400
16221622
assert 'Invalid/No patch data provided.' in resp.json['message']
1623+
1624+
1625+
########################
1626+
# api/jsonschemas/<string:name>/notifications [PATCH]
1627+
# api/jsonschemas/<string:name>/<string:version>/notifications [PATCH]
1628+
########################
1629+
def test_patch_notifications_config_when_user_not_logged_in_returns_401(
1630+
client, schema, users, json_headers):
1631+
resp = client.patch(
1632+
'/jsonschemas/{}/{}/notifications'.format(schema.name, schema.version),
1633+
headers=json_headers)
1634+
1635+
assert resp.status_code == 403
1636+
1637+
1638+
def test_patch_notifications_config(
1639+
client, schema, auth_headers_for_user, users, json_headers):
1640+
owner = users['superuser']
1641+
schema = dict(
1642+
name='new-schema',
1643+
version='1.0.0',
1644+
deposit_schema={'title': 'deposit_schema'},
1645+
config={
1646+
'reviewable': True,
1647+
'notifications': {
1648+
'actions': {
1649+
'review': [
1650+
{
1651+
"subject": {
1652+
"template": "Questionnaire for {{ cadi_id }} - New Review on Analysis | CERN Analysis Preservation",
1653+
"ctx": [{
1654+
"name": "cadi_id",
1655+
"path": "analysis_context.cadi_id"
1656+
}]
1657+
},
1658+
"body": {
1659+
"template_file": "mail/body/experiments/cms/questionnaire_message_review.html",
1660+
"ctx": [{
1661+
"name": "cadi_id",
1662+
"path": "analysis_context.cadi_id"
1663+
}, {
1664+
"name": "title",
1665+
"path": "general_title"
1666+
},{
1667+
"method": "working_url"
1668+
}, {
1669+
"method": "creator_email"
1670+
}, {
1671+
"method": "submitter_email"
1672+
}]
1673+
},
1674+
"recipients": {
1675+
"bcc": [{
1676+
"method": "get_owner"
1677+
}, {
1678+
"method": "get_submitter"
1679+
}]
1680+
}
1681+
}
1682+
]
1683+
},
1684+
}
1685+
},
1686+
is_indexed=True,
1687+
use_deposit_as_record=True,
1688+
)
1689+
client.post(
1690+
'/jsonschemas/',
1691+
data=json.dumps(schema),
1692+
headers=json_headers + auth_headers_for_user(owner),
1693+
)
1694+
1695+
valid_patch = [
1696+
{
1697+
"op": "replace",
1698+
"path": "/actions/review/0/body/ctx/0/name",
1699+
"value": "cadi_identification"
1700+
}
1701+
]
1702+
resp = client.patch(
1703+
'/jsonschemas/new-schema/notifications',
1704+
data=json.dumps(valid_patch),
1705+
headers=json_headers + auth_headers_for_user(owner),
1706+
)
1707+
assert resp.status_code == 201
1708+
1709+
invalid_patch = [
1710+
{
1711+
"op": "remove",
1712+
"path": "/actions/review/0/body/ctx/0/name"
1713+
}
1714+
]
1715+
resp = client.patch(
1716+
'/jsonschemas/new-schema/notifications',
1717+
data=json.dumps(invalid_patch),
1718+
headers=json_headers + auth_headers_for_user(owner),
1719+
)
1720+
assert resp.status_code == 400
1721+
assert resp.json['message'] == 'Schema configuration validation error'
1722+
1723+
invalid_patch_test = {
1724+
"op": "remove",
1725+
"path": "/actions/review/0/body/ctx/0/name",
1726+
"value": "cadi_id",
1727+
}
1728+
resp = client.patch(
1729+
'/jsonschemas/new-schema/notifications',
1730+
data=json.dumps(invalid_patch_test),
1731+
headers=json_headers + auth_headers_for_user(owner),
1732+
)
1733+
assert resp.status_code == 400
1734+
assert resp.json['message'] == 'Could not apply json-patch to object: string indices must be integers'

0 commit comments

Comments
 (0)