mirror of
https://github.com/GSA/notifications-api.git
synced 2026-03-24 20:20:34 -04:00
@@ -200,7 +200,7 @@ def create_template_object_for_notification(template, personalisation):
|
||||
|
||||
if (
|
||||
template_object.template_type == SMS_TYPE and
|
||||
template_object.content_count > SMS_CHAR_COUNT_LIMIT
|
||||
template_object.is_message_too_long()
|
||||
):
|
||||
message = 'Content has a character count greater than the limit of {}'.format(SMS_CHAR_COUNT_LIMIT)
|
||||
errors = {'content': [message]}
|
||||
|
||||
@@ -7,6 +7,7 @@ from jsonschema import (Draft7Validator, ValidationError, FormatChecker)
|
||||
from notifications_utils.recipients import (validate_phone_number, validate_email_address, InvalidPhoneError,
|
||||
InvalidEmailError)
|
||||
|
||||
from app.v2.errors import BadRequestError
|
||||
|
||||
format_checker = FormatChecker()
|
||||
|
||||
@@ -56,6 +57,9 @@ def validate_schema_date_with_hour(instance):
|
||||
|
||||
|
||||
def validate(json_to_validate, schema):
|
||||
if json_to_validate is None:
|
||||
raise BadRequestError(message="Request body is empty.",
|
||||
status_code=400)
|
||||
validator = Draft7Validator(schema, format_checker=format_checker)
|
||||
errors = list(validator.iter_errors(json_to_validate))
|
||||
if errors.__len__() > 0:
|
||||
|
||||
@@ -49,7 +49,7 @@ def _content_count_greater_than_limit(content, template_type):
|
||||
if template_type != SMS_TYPE:
|
||||
return False
|
||||
template = SMSMessageTemplate({'content': content, 'template_type': template_type})
|
||||
return template.content_count > SMS_CHAR_COUNT_LIMIT
|
||||
return template.is_message_too_long()
|
||||
|
||||
|
||||
def validate_parent_folder(template_json):
|
||||
|
||||
@@ -62,10 +62,11 @@ from app.v2.notifications.notification_schemas import (
|
||||
|
||||
@v2_notification_blueprint.route('/{}'.format(LETTER_TYPE), methods=['POST'])
|
||||
def post_precompiled_letter_notification():
|
||||
if 'content' not in (request.get_json() or {}):
|
||||
request_json = get_valid_json()
|
||||
if 'content' not in (request_json or {}):
|
||||
return post_notification(LETTER_TYPE)
|
||||
|
||||
form = validate(request.get_json(), post_precompiled_letter_request)
|
||||
form = validate(request_json, post_precompiled_letter_request)
|
||||
|
||||
# Check permission to send letters
|
||||
check_service_has_permission(LETTER_TYPE, authenticated_service.permissions)
|
||||
@@ -99,11 +100,7 @@ def post_precompiled_letter_notification():
|
||||
|
||||
@v2_notification_blueprint.route('/<notification_type>', methods=['POST'])
|
||||
def post_notification(notification_type):
|
||||
try:
|
||||
request_json = request.get_json()
|
||||
except BadRequest as e:
|
||||
raise BadRequestError(message="Error decoding arguments: {}".format(e.description),
|
||||
status_code=400)
|
||||
request_json = get_valid_json()
|
||||
|
||||
if notification_type == EMAIL_TYPE:
|
||||
form = validate(request_json, post_email_request)
|
||||
@@ -176,6 +173,15 @@ def post_notification(notification_type):
|
||||
return jsonify(resp), 201
|
||||
|
||||
|
||||
def get_valid_json():
|
||||
try:
|
||||
request_json = request.get_json(force=True)
|
||||
except BadRequest:
|
||||
raise BadRequestError(message="Invalid JSON supplied in POST data",
|
||||
status_code=400)
|
||||
return request_json
|
||||
|
||||
|
||||
def process_sms_or_email_notification(*, form, notification_type, api_key, template, service, reply_to_text=None):
|
||||
form_send_to = form['email_address'] if notification_type == EMAIL_TYPE else form['phone_number']
|
||||
|
||||
|
||||
@@ -11,6 +11,9 @@ from app.v2.template.template_schemas import post_template_preview_request, crea
|
||||
|
||||
@v2_template_blueprint.route("/<template_id>/preview", methods=['POST'])
|
||||
def post_template_preview(template_id):
|
||||
if not request.content_type or request.content_type != 'application/json':
|
||||
raise BadRequestError(message="Content-Type header is not set to application/json.",
|
||||
status_code=400)
|
||||
_data = request.get_json()
|
||||
if _data is None:
|
||||
_data = {}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Run `make freeze-requirements` to update requirements.txt
|
||||
# with package version changes made in requirements-app.txt
|
||||
|
||||
cffi==1.12.3
|
||||
cffi==1.13.1
|
||||
celery==3.1.26.post2 # pyup: <4
|
||||
docopt==0.6.2
|
||||
Flask-Bcrypt==0.7.1
|
||||
@@ -13,14 +13,14 @@ click-datetime==0.2
|
||||
eventlet==0.25.1
|
||||
gunicorn==19.7.1 # pyup: ignore, >19.8 breaks eventlet patching
|
||||
iso8601==0.1.12
|
||||
jsonschema==3.0.2
|
||||
marshmallow-sqlalchemy==0.17.0
|
||||
jsonschema==3.1.1
|
||||
marshmallow-sqlalchemy==0.19.0
|
||||
marshmallow==2.20.2 # pyup: <3 throws errors
|
||||
psycopg2-binary==2.8.3
|
||||
psycopg2-binary==2.8.4
|
||||
PyJWT==1.7.1
|
||||
SQLAlchemy==1.3.8
|
||||
SQLAlchemy==1.3.10
|
||||
|
||||
notifications-python-client==5.3.0
|
||||
notifications-python-client==5.4.0
|
||||
|
||||
# PaaS
|
||||
awscli-cwlogs>=1.4,<1.5
|
||||
@@ -29,6 +29,6 @@ awscli-cwlogs>=1.4,<1.5
|
||||
# Putting upgrade on hold due to v1.0.0 using sha512 instead of sha1 by default
|
||||
itsdangerous==0.24 # pyup: <1.0.0
|
||||
|
||||
git+https://github.com/alphagov/notifications-utils.git@36.1.0#egg=notifications-utils==36.1.0
|
||||
git+https://github.com/alphagov/notifications-utils.git@36.1.2#egg=notifications-utils==36.1.2
|
||||
|
||||
git+https://github.com/alphagov/boto.git@2.43.0-patch3#egg=boto==2.43.0-patch3
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# Run `make freeze-requirements` to update requirements.txt
|
||||
# with package version changes made in requirements-app.txt
|
||||
|
||||
cffi==1.12.3
|
||||
cffi==1.13.1
|
||||
celery==3.1.26.post2 # pyup: <4
|
||||
docopt==0.6.2
|
||||
Flask-Bcrypt==0.7.1
|
||||
@@ -15,14 +15,14 @@ click-datetime==0.2
|
||||
eventlet==0.25.1
|
||||
gunicorn==19.7.1 # pyup: ignore, >19.8 breaks eventlet patching
|
||||
iso8601==0.1.12
|
||||
jsonschema==3.0.2
|
||||
marshmallow-sqlalchemy==0.17.0
|
||||
jsonschema==3.1.1
|
||||
marshmallow-sqlalchemy==0.19.0
|
||||
marshmallow==2.20.2 # pyup: <3 throws errors
|
||||
psycopg2-binary==2.8.3
|
||||
psycopg2-binary==2.8.4
|
||||
PyJWT==1.7.1
|
||||
SQLAlchemy==1.3.8
|
||||
SQLAlchemy==1.3.10
|
||||
|
||||
notifications-python-client==5.3.0
|
||||
notifications-python-client==5.4.0
|
||||
|
||||
# PaaS
|
||||
awscli-cwlogs>=1.4,<1.5
|
||||
@@ -31,21 +31,21 @@ awscli-cwlogs>=1.4,<1.5
|
||||
# Putting upgrade on hold due to v1.0.0 using sha512 instead of sha1 by default
|
||||
itsdangerous==0.24 # pyup: <1.0.0
|
||||
|
||||
git+https://github.com/alphagov/notifications-utils.git@36.1.0#egg=notifications-utils==36.1.0
|
||||
git+https://github.com/alphagov/notifications-utils.git@36.1.2#egg=notifications-utils==36.1.2
|
||||
|
||||
git+https://github.com/alphagov/boto.git@2.43.0-patch3#egg=boto==2.43.0-patch3
|
||||
|
||||
## The following requirements were added by pip freeze:
|
||||
alembic==1.2.1
|
||||
alembic==1.3.1
|
||||
amqp==1.4.9
|
||||
anyjson==0.3.3
|
||||
attrs==19.3.0
|
||||
awscli==1.16.263
|
||||
awscli==1.16.283
|
||||
bcrypt==3.1.7
|
||||
billiard==3.3.0.23
|
||||
bleach==3.1.0
|
||||
boto3==1.9.221
|
||||
botocore==1.12.253
|
||||
botocore==1.13.19
|
||||
certifi==2019.9.11
|
||||
chardet==3.0.4
|
||||
Click==7.0
|
||||
@@ -53,9 +53,10 @@ colorama==0.4.1
|
||||
dnspython==1.16.0
|
||||
docutils==0.15.2
|
||||
flask-redis==0.4.0
|
||||
future==0.18.1
|
||||
future==0.18.2
|
||||
greenlet==0.4.15
|
||||
idna==2.8
|
||||
importlib-metadata==0.23
|
||||
Jinja2==2.10.3
|
||||
jmespath==0.9.4
|
||||
kombu==3.0.37
|
||||
@@ -63,13 +64,14 @@ Mako==1.1.0
|
||||
MarkupSafe==1.1.1
|
||||
mistune==0.8.4
|
||||
monotonic==1.5
|
||||
more-itertools==7.2.0
|
||||
orderedset==2.0.1
|
||||
phonenumbers==8.10.17
|
||||
pyasn1==0.4.7
|
||||
pyasn1==0.4.8
|
||||
pycparser==2.19
|
||||
PyPDF2==1.26.0
|
||||
pyrsistent==0.15.4
|
||||
python-dateutil==2.8.0
|
||||
pyrsistent==0.15.5
|
||||
python-dateutil==2.8.1
|
||||
python-editor==1.0.4
|
||||
python-json-logger==0.1.11
|
||||
pytz==2019.3
|
||||
@@ -78,9 +80,10 @@ redis==3.3.11
|
||||
requests==2.22.0
|
||||
rsa==3.4.2
|
||||
s3transfer==0.2.1
|
||||
six==1.12.0
|
||||
six==1.13.0
|
||||
smartypants==2.0.1
|
||||
statsd==3.3.0
|
||||
urllib3==1.25.6
|
||||
urllib3==1.25.7
|
||||
webencodings==0.5.1
|
||||
Werkzeug==0.16.0
|
||||
zipp==0.6.0
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
-r requirements.txt
|
||||
flake8==3.7.7
|
||||
moto==1.3.8
|
||||
pytest==5.2.2
|
||||
flake8==3.7.9
|
||||
moto==1.3.14
|
||||
pytest==5.2.4
|
||||
pytest-env==0.6.2
|
||||
pytest-mock==1.10.4
|
||||
pytest-cov==2.6.1
|
||||
coveralls==1.7.0
|
||||
pytest-xdist==1.27.0 # pyup: ignore, version 1.28.0 requires pytest >= 4.4
|
||||
freezegun==0.3.11
|
||||
requests-mock==1.5.2
|
||||
pytest-mock==1.11.2
|
||||
pytest-cov==2.8.1
|
||||
coveralls==1.8.2
|
||||
pytest-xdist==1.30.0
|
||||
freezegun==0.3.12
|
||||
requests-mock==1.7.0
|
||||
# optional requirements for jsonschema
|
||||
strict-rfc3339==0.7
|
||||
rfc3987==1.3.8
|
||||
|
||||
@@ -496,3 +496,23 @@ def test_post_letter_notification_throws_error_for_invalid_postage(client, notif
|
||||
assert resp_json['errors'][0]['message'] == "postage invalid. It must be either first or second."
|
||||
|
||||
assert not Notification.query.first()
|
||||
|
||||
|
||||
@pytest.mark.parametrize('content_type',
|
||||
['application/json', 'application/text'])
|
||||
def test_post_letter_notification_when_payload_is_invalid_json_returns_400(
|
||||
client, sample_service, content_type):
|
||||
auth_header = create_authorization_header(service_id=sample_service.id)
|
||||
payload_not_json = {
|
||||
"template_id": "dont-convert-to-json",
|
||||
}
|
||||
response = client.post(
|
||||
path='/v2/notifications/letter',
|
||||
data=payload_not_json,
|
||||
headers=[('Content-Type', content_type), auth_header],
|
||||
)
|
||||
|
||||
assert response.status_code == 400
|
||||
error_msg = json.loads(response.get_data(as_text=True))["errors"][0]["message"]
|
||||
|
||||
assert error_msg == 'Invalid JSON supplied in POST data'
|
||||
|
||||
@@ -871,3 +871,64 @@ def test_post_notification_returns_400_when_get_json_throws_exception(client, sa
|
||||
data="[",
|
||||
headers=[('Content-Type', 'application/json'), auth_header])
|
||||
assert response.status_code == 400
|
||||
|
||||
|
||||
@pytest.mark.parametrize('notification_type, content_type',
|
||||
[('email', 'application/json'),
|
||||
('email', 'application/text'),
|
||||
('sms', 'application/json'),
|
||||
('sms', 'application/text')]
|
||||
)
|
||||
def test_post_notification_when_payload_is_invalid_json_returns_400(
|
||||
client, sample_service, notification_type, content_type):
|
||||
auth_header = create_authorization_header(service_id=sample_service.id)
|
||||
payload_not_json = {
|
||||
"template_id": "dont-convert-to-json",
|
||||
}
|
||||
response = client.post(
|
||||
path='/v2/notifications/{}'.format(notification_type),
|
||||
data=payload_not_json,
|
||||
headers=[('Content-Type', content_type), auth_header],
|
||||
)
|
||||
|
||||
assert response.status_code == 400
|
||||
error_msg = json.loads(response.get_data(as_text=True))["errors"][0]["message"]
|
||||
|
||||
assert error_msg == 'Invalid JSON supplied in POST data'
|
||||
|
||||
|
||||
@pytest.mark.parametrize('notification_type', ['email', 'sms'])
|
||||
def test_post_notification_returns_201_when_content_type_is_missing_but_payload_is_valid_json(
|
||||
client, sample_service, notification_type, mocker):
|
||||
template = create_template(service=sample_service, template_type=notification_type)
|
||||
mocker.patch('app.celery.provider_tasks.deliver_{}.apply_async'.format(notification_type))
|
||||
auth_header = create_authorization_header(service_id=sample_service.id)
|
||||
|
||||
valid_json = {
|
||||
"template_id": str(template.id),
|
||||
}
|
||||
if notification_type == 'email':
|
||||
valid_json.update({"email_address": sample_service.users[0].email_address})
|
||||
else:
|
||||
valid_json.update({"phone_number": "+447700900855"})
|
||||
response = client.post(
|
||||
path='/v2/notifications/{}'.format(notification_type),
|
||||
data=json.dumps(valid_json),
|
||||
headers=[auth_header],
|
||||
)
|
||||
assert response.status_code == 201
|
||||
|
||||
|
||||
@pytest.mark.parametrize('notification_type', ['email', 'sms'])
|
||||
def test_post_email_notification_when_data_is_empty_returns_400(
|
||||
client, sample_service, notification_type):
|
||||
auth_header = create_authorization_header(service_id=sample_service.id)
|
||||
data = None
|
||||
response = client.post(
|
||||
path='/v2/notifications/{}'.format(notification_type),
|
||||
data=json.dumps(data),
|
||||
headers=[('Content-Type', 'application/json'), auth_header],
|
||||
)
|
||||
error_msg = json.loads(response.get_data(as_text=True))["errors"][0]["message"]
|
||||
assert response.status_code == 400
|
||||
assert error_msg == 'Request body is empty.'
|
||||
|
||||
@@ -152,3 +152,15 @@ def test_post_template_with_non_existent_template_id_returns_404(client, fake_uu
|
||||
],
|
||||
"status_code": 404
|
||||
}
|
||||
|
||||
|
||||
def test_post_template_without_content_header(client, sample_template):
|
||||
response = client.post(
|
||||
path='/v2/template/{}/preview'.format(sample_template.id),
|
||||
data=json.dumps(valid_personalisation),
|
||||
headers=[create_authorization_header(service_id=sample_template.service_id)]
|
||||
)
|
||||
|
||||
assert response.status_code == 400
|
||||
json_response = json.loads(response.get_data(as_text=True))
|
||||
assert json_response['errors'][0]['message'] == 'Content-Type header is not set to application/json.'
|
||||
|
||||
Reference in New Issue
Block a user