Merge branch 'master' into updating-service_sms_senders

This commit is contained in:
Rebecca Law
2017-09-12 12:11:38 +01:00
11 changed files with 32 additions and 161 deletions

View File

@@ -16,7 +16,7 @@ class Encryption:
def hashpw(password): def hashpw(password):
return generate_password_hash(password.encode('UTF-8'), 10) return generate_password_hash(password.encode('UTF-8'), 10).decode('utf-8')
def check_hash(password, hashed_password): def check_hash(password, hashed_password):

View File

@@ -1337,7 +1337,7 @@ class ServiceEmailReplyTo(db.Model):
id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
service_id = db.Column(UUID(as_uuid=True), db.ForeignKey('services.id'), unique=False, index=True, nullable=False) service_id = db.Column(UUID(as_uuid=True), db.ForeignKey('services.id'), unique=False, index=True, nullable=False)
service = db.relationship(Service, backref=db.backref("reply_to_email_addresses", uselist=False)) service = db.relationship(Service, backref=db.backref("reply_to_email_addresses"))
email_address = db.Column(db.Text, nullable=False, index=False, unique=False) email_address = db.Column(db.Text, nullable=False, index=False, unique=False)
is_default = db.Column(db.Boolean, nullable=False, default=True) is_default = db.Column(db.Boolean, nullable=False, default=True)

View File

@@ -203,7 +203,8 @@ class ServiceSchema(BaseSchema):
'service_provider_stats', 'service_provider_stats',
'service_notification_stats', 'service_notification_stats',
'service_sms_senders', 'service_sms_senders',
'monthly_billing' 'monthly_billing',
'reply_to_email_addresses',
) )
strict = True strict = True
@@ -256,7 +257,8 @@ class DetailedServiceSchema(BaseSchema):
'service_notification_stats', 'service_notification_stats',
'organisation', 'organisation',
'service_sms_senders', 'service_sms_senders',
'monthly_billing' 'monthly_billing',
'reply_to_email_addresses'
) )

View File

@@ -177,7 +177,7 @@ email_content = {
post_email_response = { post_email_response = {
"$schema": "http://json-schema.org/draft-04/schema#", "$schema": "http://json-schema.org/draft-04/schema#",
"description": "POST sms notification response schema", "description": "POST email notification response schema",
"type": "object", "type": "object",
"title": "response v2/notifications/email", "title": "response v2/notifications/email",
"properties": { "properties": {

View File

@@ -88,7 +88,7 @@ def post_notification(notification_type):
create_resp_partial = functools.partial( create_resp_partial = functools.partial(
create_post_email_response_from_notification, create_post_email_response_from_notification,
subject=template_with_content.subject, subject=template_with_content.subject,
email_from=authenticated_service.email_from email_from='{}@{}'.format(authenticated_service.email_from, current_app.config['NOTIFY_EMAIL_DOMAIN'])
) )
elif notification_type == LETTER_TYPE: elif notification_type == LETTER_TYPE:
create_resp_partial = functools.partial( create_resp_partial = functools.partial(

View File

@@ -1,10 +1,7 @@
boto3==1.4.7 boto3==1.4.7
celery==3.1.25 # pyup: <4 celery==3.1.25 # pyup: <4
docopt==0.6.2 docopt==0.6.2
# ignore flask-bcrypt - when upgrading, it installs an invalid version of bcrypt that isn't removed when a different Flask-Bcrypt==0.7.1
# branch runs, so can cause other PR builds to fail on jenkins.
# TODO: Upgrade flask-bcrypt in a safe way
Flask-Bcrypt==0.6.2 # pyup: ignore
Flask-Marshmallow==0.8.0 Flask-Marshmallow==0.8.0
Flask-Migrate==2.1.1 Flask-Migrate==2.1.1
Flask-Script==2.0.5 Flask-Script==2.0.5

View File

@@ -5,7 +5,9 @@ pytest-mock==1.6.2
pytest-cov==2.5.1 pytest-cov==2.5.1
pytest-xdist==1.20.0 pytest-xdist==1.20.0
coveralls==1.2.0 coveralls==1.2.0
moto==1.1.2 moto==1.1.4
freezegun==0.3.9 freezegun==0.3.9
requests-mock==1.3.0 requests-mock==1.3.0
# optional requirements for jsonschema
strict-rfc3339==0.7 strict-rfc3339==0.7
rfc3987==1.3.7

View File

@@ -12,7 +12,7 @@ from app.dao.services_dao import dao_remove_user_from_service
from app.dao.templates_dao import dao_redact_template from app.dao.templates_dao import dao_redact_template
from app.dao.users_dao import save_model_user from app.dao.users_dao import save_model_user
from app.models import ( from app.models import (
User, Organisation, Service, ServicePermission, Notification, User, Organisation, Service, ServicePermission, Notification, ServiceEmailReplyTo,
DVLA_ORG_LAND_REGISTRY, DVLA_ORG_LAND_REGISTRY,
KEY_TYPE_NORMAL, KEY_TYPE_TEAM, KEY_TYPE_TEST, KEY_TYPE_NORMAL, KEY_TYPE_TEAM, KEY_TYPE_TEST,
EMAIL_TYPE, SMS_TYPE, LETTER_TYPE, INTERNATIONAL_SMS_TYPE, INBOUND_SMS_TYPE, EMAIL_TYPE, SMS_TYPE, LETTER_TYPE, INTERNATIONAL_SMS_TYPE, INBOUND_SMS_TYPE,
@@ -2133,10 +2133,8 @@ def test_is_service_name_unique_returns_400_when_name_does_not_exist(client):
assert json_resp["message"][1]["email_from"] == ["Can't be empty"] assert json_resp["message"][1]["email_from"] == ["Can't be empty"]
def test_update_service_reply_to_email_address_upserts_email_reply_to(mocker, admin_request, sample_service): def test_update_service_reply_to_email_address_upserts_email_reply_to(admin_request, sample_service):
update_mock = mocker.patch('app.service.rest.create_or_update_email_reply_to') response = admin_request.post(
admin_request.post(
'service.update_service', 'service.update_service',
service_id=sample_service.id, service_id=sample_service.id,
_data={ _data={
@@ -2145,4 +2143,8 @@ def test_update_service_reply_to_email_address_upserts_email_reply_to(mocker, ad
_expected_status=200 _expected_status=200
) )
assert update_mock.called service_reply_to_emails = ServiceEmailReplyTo.query.all()
assert len(service_reply_to_emails) == 1
assert service_reply_to_emails[0].email_address == 'new@mail.com'
assert service_reply_to_emails[0].is_default
assert response['data']['reply_to_email_address'] == 'new@mail.com'

View File

@@ -5,15 +5,12 @@ from flask import json
from freezegun import freeze_time from freezegun import freeze_time
from jsonschema import ValidationError from jsonschema import ValidationError
from app.schema_validation import validate
from app.v2.notifications.notification_schemas import ( from app.v2.notifications.notification_schemas import (
get_notifications_request, get_notifications_request,
get_notification_response,
post_sms_request as post_sms_request_schema, post_sms_request as post_sms_request_schema,
post_sms_response as post_sms_response_schema, post_email_request as post_email_request_schema
post_email_request as post_email_request_schema,
post_email_response as post_email_response_schema
) )
from app.schema_validation import validate
@pytest.mark.parametrize('invalid_statuses, valid_statuses', [ @pytest.mark.parametrize('invalid_statuses, valid_statuses', [
@@ -163,40 +160,6 @@ def test_post_sms_request_schema_invalid_phone_number_and_missing_template():
assert {"error": "ValidationError", "message": "template_id is a required property"} in errors assert {"error": "ValidationError", "message": "template_id is a required property"} in errors
def valid_sms_response():
return {
"id": str(uuid.uuid4()),
"content": {"body": "contents of message",
"from_number": "46045"},
"uri": "http://notify.api/v2/notifications/id",
"template": {
"id": str(uuid.uuid4()),
"version": 1,
"uri": "http://notify.api/v2/template/id"
}
}
def valid_sms_response_with_optionals():
return {
"id": str(uuid.uuid4()),
"reference": "reference_from_service",
"content": {"body": "contents of message",
"from_number": "46045"},
"uri": "http://notify.api/v2/notifications/id",
"template": {
"id": str(uuid.uuid4()),
"version": 1,
"uri": "http://notify.api/v2/template/id"
}
}
@pytest.mark.parametrize('input', [valid_sms_response()])
def test_post_sms_response_schema_schema_is_valid(input):
assert validate(input, post_sms_response_schema) == input
valid_post_email_json = {"email_address": "test@example.gov.uk", valid_post_email_json = {"email_address": "test@example.gov.uk",
"template_id": str(uuid.uuid4()) "template_id": str(uuid.uuid4())
} }
@@ -252,110 +215,6 @@ def valid_email_response():
} }
def valid_email_response_with_optionals():
return {
"id": str(uuid.uuid4()),
"reference": "some reference",
"content": {"body": "the body of the message",
"subject": "subject of the message",
"from_email": "service@dig.gov.uk"},
"uri": "http://notify.api/v2/notifications/id",
"template": {
"id": str(uuid.uuid4()),
"version": 1,
"uri": "http://notify.api/v2/template/id"
},
"schedule_for": "2017-05-12 13:00:00"
}
@pytest.mark.parametrize("input", [valid_email_response(), valid_email_response_with_optionals()])
def test_post_email_response_schema(input):
assert validate(input, post_email_response_schema) == input
@pytest.mark.parametrize('response, schema', [
(valid_email_response(), post_email_response_schema),
(valid_sms_response(), post_sms_response_schema)
])
def test_post_sms_response_schema_missing_uri_raises_validation_error(response, schema):
del response['uri']
with pytest.raises(ValidationError) as e:
validate(response, schema)
error = json.loads(str(e.value))
assert error['status_code'] == 400
assert error['errors'] == [{'error': 'ValidationError',
'message': "uri is a required property"}]
@pytest.mark.parametrize('response, schema', [
(valid_email_response(), post_email_response_schema),
(valid_sms_response(), post_sms_response_schema)
])
def test_post_sms_response_schema_invalid_uri_raises_validation_error(response, schema):
response['uri'] = 'invalid-uri'
with pytest.raises(ValidationError) as e:
validate(response, schema)
error = json.loads(str(e.value))
assert error['status_code'] == 400
assert error['errors'] == [{'error': 'ValidationError',
'message': "uri invalid-uri is not a valid URI."}]
@pytest.mark.parametrize('response, schema', [
(valid_email_response(), post_email_response_schema),
(valid_sms_response(), post_sms_response_schema)
])
def test_post_sms_response_schema_missing_template_uri_raises_validation_error(response, schema):
del response['template']['uri']
with pytest.raises(ValidationError) as e:
validate(response, schema)
error = json.loads(str(e.value))
assert error['status_code'] == 400
assert error['errors'] == [{'error': 'ValidationError',
'message': "template uri is a required property"}]
@pytest.mark.parametrize('response, schema', [
(valid_email_response(), post_email_response_schema),
(valid_sms_response(), post_sms_response_schema)
])
def test_post_sms_response_schema_invalid_template_uri_raises_validation_error(response, schema):
response['template']['uri'] = 'invalid-uri'
with pytest.raises(ValidationError) as e:
validate(response, schema)
error = json.loads(str(e.value))
assert error['status_code'] == 400
assert error['errors'] == [{'error': 'ValidationError',
'message': "template invalid-uri is not a valid URI."}]
def test_get_notifications_response_with_email_and_phone_number():
response = {"id": str(uuid.uuid4()),
"reference": "something",
"email_address": None,
"phone_number": "+447115411111",
"line_1": None,
"line_2": None,
"line_3": None,
"line_4": None,
"line_5": None,
"line_6": None,
"postcode": None,
"type": "email",
"status": "delivered",
"template": {"id": str(uuid.uuid4()), "version": 1, "uri": "http://template/id"},
"body": "some body",
"subject": "some subject",
"created_at": "2016-01-01",
"sent_at": "2016-01-01",
"completed_at": "2016-01-01",
"schedule_for": ""
}
assert validate(response, get_notification_response) == response
@pytest.mark.parametrize("schema", @pytest.mark.parametrize("schema",
[post_email_request_schema, post_sms_request_schema]) [post_email_request_schema, post_sms_request_schema])
@freeze_time("2017-05-12 13:00:00") @freeze_time("2017-05-12 13:00:00")

View File

@@ -12,7 +12,9 @@ from app.models import KEY_TYPE_TEST
from app.models import LETTER_TYPE from app.models import LETTER_TYPE
from app.models import Notification from app.models import Notification
from app.models import SMS_TYPE from app.models import SMS_TYPE
from app.schema_validation import validate
from app.v2.errors import RateLimitError from app.v2.errors import RateLimitError
from app.v2.notifications.notification_schemas import post_letter_response
from app.variables import LETTER_TEST_API_FILENAME from app.variables import LETTER_TEST_API_FILENAME
from app.variables import LETTER_API_FILENAME from app.variables import LETTER_API_FILENAME
@@ -53,6 +55,7 @@ def test_post_letter_notification_returns_201(client, sample_letter_template, mo
resp_json = letter_request(client, data, service_id=sample_letter_template.service_id) resp_json = letter_request(client, data, service_id=sample_letter_template.service_id)
assert validate(resp_json, post_letter_response) == resp_json
job = Job.query.one() job = Job.query.one()
assert job.original_file_name == LETTER_API_FILENAME assert job.original_file_name == LETTER_API_FILENAME
notification = Notification.query.one() notification = Notification.query.one()

View File

@@ -10,7 +10,9 @@ from app.models import (
from flask import json, current_app from flask import json, current_app
from app.models import Notification from app.models import Notification
from app.schema_validation import validate
from app.v2.errors import RateLimitError from app.v2.errors import RateLimitError
from app.v2.notifications.notification_schemas import post_sms_response, post_email_response
from tests import create_authorization_header from tests import create_authorization_header
from tests.app.conftest import ( from tests.app.conftest import (
sample_template as create_sample_template, sample_service, sample_template as create_sample_template, sample_service,
@@ -38,6 +40,7 @@ def test_post_sms_notification_returns_201(client, sample_template_with_placehol
headers=[('Content-Type', 'application/json'), auth_header]) headers=[('Content-Type', 'application/json'), auth_header])
assert response.status_code == 201 assert response.status_code == 201
resp_json = json.loads(response.get_data(as_text=True)) resp_json = json.loads(response.get_data(as_text=True))
assert validate(resp_json, post_sms_response) == resp_json
notifications = Notification.query.all() notifications = Notification.query.all()
assert len(notifications) == 1 assert len(notifications) == 1
notification_id = notifications[0].id notification_id = notifications[0].id
@@ -71,6 +74,7 @@ def test_post_sms_notification_uses_inbound_number_as_sender(client, sample_temp
headers=[('Content-Type', 'application/json'), auth_header]) headers=[('Content-Type', 'application/json'), auth_header])
assert response.status_code == 201 assert response.status_code == 201
resp_json = json.loads(response.get_data(as_text=True)) resp_json = json.loads(response.get_data(as_text=True))
assert validate(resp_json, post_sms_response) == resp_json
notifications = Notification.query.all() notifications = Notification.query.all()
assert len(notifications) == 1 assert len(notifications) == 1
notification_id = notifications[0].id notification_id = notifications[0].id
@@ -170,6 +174,7 @@ def test_post_email_notification_returns_201(client, sample_email_template_with_
headers=[('Content-Type', 'application/json'), auth_header]) headers=[('Content-Type', 'application/json'), auth_header])
assert response.status_code == 201 assert response.status_code == 201
resp_json = json.loads(response.get_data(as_text=True)) resp_json = json.loads(response.get_data(as_text=True))
assert validate(resp_json, post_email_response) == resp_json
notification = Notification.query.first() notification = Notification.query.first()
assert resp_json['id'] == str(notification.id) assert resp_json['id'] == str(notification.id)
assert resp_json['reference'] == reference assert resp_json['reference'] == reference
@@ -178,7 +183,8 @@ def test_post_email_notification_returns_201(client, sample_email_template_with_
.replace('((name))', 'Bob').replace('GOV.UK', u'GOV.\u200bUK') .replace('((name))', 'Bob').replace('GOV.UK', u'GOV.\u200bUK')
assert resp_json['content']['subject'] == sample_email_template_with_placeholders.subject \ assert resp_json['content']['subject'] == sample_email_template_with_placeholders.subject \
.replace('((name))', 'Bob') .replace('((name))', 'Bob')
assert resp_json['content']['from_email'] == sample_email_template_with_placeholders.service.email_from assert resp_json['content']['from_email'] == "{}@{}".format(
sample_email_template_with_placeholders.service.email_from, current_app.config['NOTIFY_EMAIL_DOMAIN'])
assert 'v2/notifications/{}'.format(notification.id) in resp_json['uri'] assert 'v2/notifications/{}'.format(notification.id) in resp_json['uri']
assert resp_json['template']['id'] == str(sample_email_template_with_placeholders.id) assert resp_json['template']['id'] == str(sample_email_template_with_placeholders.id)
assert resp_json['template']['version'] == sample_email_template_with_placeholders.version assert resp_json['template']['version'] == sample_email_template_with_placeholders.version