Merge pull request #2662 from alphagov/utils-bump

Utils bump
This commit is contained in:
Leo Hemsted
2019-11-19 10:55:07 +00:00
committed by Rebecca Law
11 changed files with 150 additions and 41 deletions

View File

@@ -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]}

View File

@@ -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:

View File

@@ -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):

View File

@@ -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']

View File

@@ -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 = {}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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'

View File

@@ -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.'

View File

@@ -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.'