-
Notifications
You must be signed in to change notification settings - Fork 18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add pinpoint fields to notifications #2326
Changes from 7 commits
84bb526
3d6846e
fdbeee7
46ec67f
5eb0117
96893e0
03feb77
e5cd121
6d6459a
8fdf60c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -230,7 +230,7 @@ def pinpoint_delivered_callback(reference=None, timestamp=1467074434, destinatio | |
"isoCountryCode": "CA", | ||
"mcc": "302", | ||
"mnc": "610", | ||
"carrierName": "Bell Cellular Inc. / Aliant Telecom", | ||
"carrierName": "Bell", | ||
"messageId": reference, | ||
"messageRequestTimestamp": timestamp, | ||
"messageEncoding": "GSM", | ||
|
@@ -254,15 +254,38 @@ def pinpoint_shortcode_delivered_callback(reference=None, timestamp=1467074434, | |
"originationPhoneNumber": "555555", | ||
"destinationPhoneNumber": destination, | ||
"isoCountryCode": "CA", | ||
"carrierName": "Bell", | ||
"messageId": reference, | ||
"messageRequestTimestamp": timestamp, | ||
"messageEncoding": "GSM", | ||
"messageType": "TRANSACTIONAL", | ||
"messageStatus": "SUCCESSFUL", | ||
"messageStatusDescription": "Message has been accepted by phone carrier", | ||
"totalMessageParts": 1, | ||
"totalMessagePrice": 0.02183, | ||
"totalCarrierFee": 0.005, | ||
"totalMessagePrice": 0.00581, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The change in |
||
"totalCarrierFee": 0.006, | ||
} | ||
|
||
return _pinpoint_callback(body) | ||
|
||
|
||
def pinpoint_delivered_callback_missing_sms_data(reference=None, timestamp=1467074434, destination="+1XXX5550100"): | ||
body = { | ||
"eventType": "TEXT_DELIVERED", | ||
"eventVersion": "1.0", | ||
"eventTimestamp": timestamp, | ||
"isFinal": True, | ||
"originationPhoneNumber": "+13655550100", | ||
"destinationPhoneNumber": destination, | ||
"messageId": reference, | ||
"messageRequestTimestamp": timestamp, | ||
"messageEncoding": "GSM", | ||
"messageType": "TRANSACTIONAL", | ||
"messageStatus": "DELIVERED", | ||
"messageStatusDescription": "Message has been accepted by phone", | ||
"totalMessageParts": 1, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||
"totalMessagePrice": 0.00581, | ||
"totalCarrierFee": 0.006, | ||
} | ||
|
||
return _pinpoint_callback(body) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -53,6 +53,14 @@ def process_pinpoint_results(self, response): | |
provider_response = receipt["messageStatusDescription"] | ||
isFinal = receipt["isFinal"] | ||
|
||
# some of these fields might be missing in the receipt | ||
total_message_price = receipt.get("totalMessagePrice") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider adding a default value for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no, want it to be |
||
total_carrier_fee = receipt.get("totalCarrierFee") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider adding a default value for |
||
iso_country_code = receipt.get("isoCountryCode") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider adding a default value for |
||
carrier_name = receipt.get("carrierName") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider adding a default value for |
||
message_encoding = receipt.get("messageEncoding") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider adding a default value for |
||
origination_phone_number = receipt.get("originationPhoneNumber") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider adding a default value for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So many informative fields that we'll be able to get new insights from. 🥳 |
||
|
||
notification_status = determine_pinpoint_status(status, provider_response, isFinal) | ||
|
||
if notification_status == NOTIFICATION_SENT: | ||
|
@@ -88,6 +96,12 @@ def process_pinpoint_results(self, response): | |
notification=notification, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The variable There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah, it's define above this |
||
status=notification_status, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The variable |
||
provider_response=provider_response, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The variable |
||
sms_total_message_price=total_message_price, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The variable |
||
sms_total_carrier_fee=total_carrier_fee, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The variable |
||
sms_iso_country_code=iso_country_code, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The variable |
||
sms_carrier_name=carrier_name, | ||
sms_message_encoding=message_encoding, | ||
sms_origination_phone_number=origination_phone_number, | ||
) | ||
|
||
if notification_status != NOTIFICATION_DELIVERED: | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -198,7 +198,18 @@ def country_records_delivery(phone_prefix): | |
return dlr and dlr.lower() == "yes" | ||
|
||
|
||
def _update_notification_status(notification, status, provider_response=None, bounce_response=None): | ||
def _update_notification_status( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The function signature for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it's fine the way it is but not an unreasonable comment. |
||
notification, | ||
status, | ||
provider_response=None, | ||
bounce_response=None, | ||
sms_total_message_price=None, | ||
sms_total_carrier_fee=None, | ||
sms_iso_country_code=None, | ||
sms_carrier_name=None, | ||
sms_message_encoding=None, | ||
sms_origination_phone_number=None, | ||
): | ||
status = _decide_permanent_temporary_failure(current_status=notification.status, status=status) | ||
notification.status = status | ||
if provider_response: | ||
|
@@ -208,6 +219,19 @@ def _update_notification_status(notification, status, provider_response=None, bo | |
notification.feedback_subtype = bounce_response.get("feedback_subtype") | ||
notification.ses_feedback_id = bounce_response.get("ses_feedback_id") | ||
notification.ses_feedback_date = bounce_response.get("ses_feedback_date") | ||
if sms_total_message_price: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider using a single update statement for the new fields to improve readability and maintainability. For example: notification.sms_total_message_price = sms_total_message_price
notification.sms_total_carrier_fee = sms_total_carrier_fee
notification.sms_iso_country_code = sms_iso_country_code
notification.sms_carrier_name = sms_carrier_name
notification.sms_message_encoding = sms_message_encoding
notification.sms_origination_phone_number = sms_origination_phone_number There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah I was wondering if we need these conditionals too, if a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's a pattern we use already but yeah, I can't really see a reason why it'd make a difference. I'll clean it up as the ai suggests and verify that it's still all good. |
||
notification.sms_total_message_price = sms_total_message_price | ||
if sms_total_carrier_fee: | ||
notification.sms_total_carrier_fee = sms_total_carrier_fee | ||
if sms_iso_country_code: | ||
notification.sms_iso_country_code = sms_iso_country_code | ||
if sms_carrier_name: | ||
notification.sms_carrier_name = sms_carrier_name | ||
if sms_message_encoding: | ||
notification.sms_message_encoding = sms_message_encoding | ||
if sms_origination_phone_number: | ||
notification.sms_origination_phone_number = sms_origination_phone_number | ||
|
||
dao_update_notification(notification) | ||
return notification | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1734,6 +1734,14 @@ class Notification(BaseModel): | |
ses_feedback_id = db.Column(db.String, nullable=True) | ||
ses_feedback_date = db.Column(db.DateTime, nullable=True) | ||
|
||
# SMS columns | ||
sms_total_message_price = db.Column(db.Numeric(), nullable=True) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider specifying a precision and scale for the |
||
sms_total_carrier_fee = db.Column(db.Numeric(), nullable=True) | ||
sms_iso_country_code = db.Column(db.String(), nullable=True) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider adding a length constraint to the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hrm, a good idea. |
||
sms_carrier_name = db.Column(db.String(), nullable=True) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider adding a length constraint to the |
||
sms_message_encoding = db.Column(db.String(), nullable=True) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider adding a length constraint to the |
||
sms_origination_phone_number = db.Column(db.String(), nullable=True) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider adding a length constraint to the |
||
|
||
CheckConstraint( | ||
""" | ||
CASE WHEN notification_type = 'letter' THEN | ||
|
@@ -2045,6 +2053,14 @@ class NotificationHistory(BaseModel, HistoryModel): | |
ses_feedback_id = db.Column(db.String, nullable=True) | ||
ses_feedback_date = db.Column(db.DateTime, nullable=True) | ||
|
||
# SMS columns | ||
sms_total_message_price = db.Column(db.Numeric(), nullable=True) | ||
sms_total_carrier_fee = db.Column(db.Numeric(), nullable=True) | ||
sms_iso_country_code = db.Column(db.String(), nullable=True) | ||
sms_carrier_name = db.Column(db.String(), nullable=True) | ||
sms_message_encoding = db.Column(db.String(), nullable=True) | ||
sms_origination_phone_number = db.Column(db.String(), nullable=True) | ||
|
||
CheckConstraint( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||
""" | ||
CASE WHEN notification_type = 'letter' THEN | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
""" | ||
|
||
Revision ID: 0461_add_pinpoint_fields | ||
Revises: 0460_new_service_columns | ||
Create Date: 2024-10-15 18:24:22.926597 | ||
|
||
""" | ||
import sqlalchemy as sa | ||
from alembic import op | ||
|
||
revision = "0461_add_pinpoint_fields" | ||
down_revision = "0460_new_service_columns" | ||
|
||
|
||
def upgrade(): | ||
op.add_column("notifications", sa.Column("sms_total_message_price", sa.Float(), nullable=True)) | ||
op.add_column("notifications", sa.Column("sms_total_carrier_fee", sa.Float(), nullable=True)) | ||
op.add_column("notifications", sa.Column("sms_iso_country_code", sa.VARCHAR(), nullable=True)) | ||
op.add_column("notifications", sa.Column("sms_carrier_name", sa.VARCHAR(), nullable=True)) | ||
op.add_column("notifications", sa.Column("sms_message_encoding", sa.VARCHAR(), nullable=True)) | ||
op.add_column("notifications", sa.Column("sms_origination_phone_number", sa.VARCHAR(), nullable=True)) | ||
op.add_column("notification_history", sa.Column("sms_total_message_price", sa.Float(), nullable=True)) | ||
op.add_column("notification_history", sa.Column("sms_total_carrier_fee", sa.Float(), nullable=True)) | ||
op.add_column("notification_history", sa.Column("sms_iso_country_code", sa.VARCHAR(), nullable=True)) | ||
op.add_column("notification_history", sa.Column("sms_carrier_name", sa.VARCHAR(), nullable=True)) | ||
op.add_column("notification_history", sa.Column("sms_message_encoding", sa.VARCHAR(), nullable=True)) | ||
op.add_column("notification_history", sa.Column("sms_origination_phone_number", sa.VARCHAR(), nullable=True)) | ||
|
||
|
||
def downgrade(): | ||
op.drop_column("notifications", "sms_total_message_price") | ||
op.drop_column("notifications", "sms_total_carrier_fee") | ||
op.drop_column("notifications", "sms_iso_country_code") | ||
op.drop_column("notifications", "sms_carrier_name") | ||
op.drop_column("notifications", "sms_message_encoding") | ||
op.drop_column("notifications", "sms_origination_phone_number") | ||
op.drop_column("notification_history", "sms_total_message_price") | ||
op.drop_column("notification_history", "sms_total_carrier_fee") | ||
op.drop_column("notification_history", "sms_iso_country_code") | ||
op.drop_column("notification_history", "sms_carrier_name") | ||
op.drop_column("notification_history", "sms_message_encoding") | ||
op.drop_column("notification_history", "sms_origination_phone_number") |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,6 +6,7 @@ | |
from app import statsd_client | ||
from app.aws.mocks import ( | ||
pinpoint_delivered_callback, | ||
pinpoint_delivered_callback_missing_sms_data, | ||
pinpoint_failed_callback, | ||
pinpoint_shortcode_delivered_callback, | ||
pinpoint_successful_callback, | ||
|
@@ -30,13 +31,15 @@ | |
|
||
|
||
@pytest.mark.parametrize( | ||
"callback, expected_response", | ||
"callback, expected_response, origination_phone_number", | ||
[ | ||
(pinpoint_delivered_callback, "Message has been accepted by phone"), | ||
(pinpoint_shortcode_delivered_callback, "Message has been accepted by phone carrier"), | ||
(pinpoint_delivered_callback, "Message has been accepted by phone", "+13655550100"), | ||
(pinpoint_shortcode_delivered_callback, "Message has been accepted by phone carrier", "555555"), | ||
], | ||
) | ||
def test_process_pinpoint_results_delivered(sample_template, notify_db, notify_db_session, callback, expected_response, mocker): | ||
def test_process_pinpoint_results_delivered( | ||
sample_template, notify_db, notify_db_session, callback, expected_response, origination_phone_number, mocker | ||
): | ||
mock_logger = mocker.patch("app.celery.process_pinpoint_receipts_tasks.current_app.logger.info") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is being used later in the method... |
||
mock_callback_task = mocker.patch("app.notifications.callbacks._check_and_queue_callback_task") | ||
|
||
|
@@ -56,6 +59,12 @@ def test_process_pinpoint_results_delivered(sample_template, notify_db, notify_d | |
assert mock_callback_task.called_once_with(get_notification_by_id(notification.id)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. oh.... yeah fixing this led me to realize that the mock wasn't actually mocking the right thing... so good on ya, ai reviewer. |
||
assert get_notification_by_id(notification.id).status == NOTIFICATION_DELIVERED | ||
assert get_notification_by_id(notification.id).provider_response == expected_response | ||
assert float(get_notification_by_id(notification.id).sms_total_message_price) == 0.00581 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider storing the result of |
||
assert float(get_notification_by_id(notification.id).sms_total_carrier_fee) == 0.006 | ||
assert get_notification_by_id(notification.id).sms_iso_country_code == "CA" | ||
assert get_notification_by_id(notification.id).sms_carrier_name == "Bell" | ||
assert get_notification_by_id(notification.id).sms_message_encoding == "GSM" | ||
assert get_notification_by_id(notification.id).sms_origination_phone_number == origination_phone_number | ||
|
||
mock_logger.assert_called_once_with(f"Pinpoint callback return status of delivered for notification: {notification.id}") | ||
|
||
|
@@ -81,6 +90,30 @@ def test_process_pinpoint_results_succeeded(sample_template, notify_db, notify_d | |
assert get_notification_by_id(notification.id).provider_response is None | ||
|
||
|
||
def test_process_pinpoint_results_missing_sms_data(sample_template, notify_db, notify_db_session, mocker): | ||
mock_callback_task = mocker.patch("app.notifications.callbacks._check_and_queue_callback_task") | ||
|
||
notification = create_sample_notification( | ||
notify_db, | ||
notify_db_session, | ||
template=sample_template, | ||
reference="ref", | ||
status=NOTIFICATION_SENT, | ||
sent_by="pinpoint", | ||
sent_at=datetime.utcnow(), | ||
) | ||
assert get_notification_by_id(notification.id).status == NOTIFICATION_SENT | ||
|
||
process_pinpoint_results(pinpoint_delivered_callback_missing_sms_data(reference="ref")) | ||
|
||
assert mock_callback_task.called_once_with(get_notification_by_id(notification.id)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The method |
||
assert get_notification_by_id(notification.id).status == NOTIFICATION_DELIVERED | ||
assert float(get_notification_by_id(notification.id).sms_total_message_price) == 0.00581 | ||
assert float(get_notification_by_id(notification.id).sms_total_carrier_fee) == 0.006 | ||
assert get_notification_by_id(notification.id).sms_iso_country_code is None | ||
assert get_notification_by_id(notification.id).sms_carrier_name is None | ||
|
||
|
||
@pytest.mark.parametrize( | ||
"provider_response, expected_status, should_log_warning, should_save_provider_response", | ||
[ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
pinpoint_shortcode_delivered_callback
function signature should be updated to include the newcarrierName
parameter to ensure it is passed correctly.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nope, we don't want to ever pass it.