def test_should_throw_error_when_invalid_type(self): recipient_identifier = RecipientIdentifier(notification_id="123456", id_type="unknown_type", id_value="123") with pytest.raises(UnsupportedIdentifierException) as e: transform_to_fhir_format(recipient_identifier) assert "No identifier of type" in str(e.value)
def test_should_transform_recipient_identifier_to_mpi_acceptable_format( self, id_type, id_value, expected_fhir_format): recipient_identifier = RecipientIdentifier(notification_id="123456", id_type=id_type.value, id_value=id_value) actual_fhir_format = transform_to_fhir_format(recipient_identifier) assert actual_fhir_format == expected_fhir_format
def lookup_va_profile_id(self, notification_id): current_app.logger.info( f"Retrieving VA Profile ID from MPI for notification {notification_id}" ) notification = notifications_dao.get_notification_by_id(notification_id) try: va_profile_id = mpi_client.get_va_profile_id(notification) notification.recipient_identifiers.set( RecipientIdentifier(notification_id=notification.id, id_type=IdentifierType.VA_PROFILE_ID.value, id_value=va_profile_id)) notifications_dao.dao_update_notification(notification) current_app.logger.info( f"Successfully updated notification {notification_id} with VA PROFILE ID {va_profile_id}" ) except MpiRetryableException as e: current_app.logger.warning( f"Received {str(e)} for notification {notification_id}.") try: self.retry(queue=QueueNames.RETRY) except self.MaxRetriesExceededError: message = "RETRY FAILED: Max retries reached. " \ f"The task lookup_va_profile_id failed for notification {notification_id}. " \ "Notification has been updated to technical-failure" notifications_dao.update_notification_status_by_id( notification_id, NOTIFICATION_TECHNICAL_FAILURE, status_reason=e.failure_reason) raise NotificationTechnicalFailureException(message) from e except (BeneficiaryDeceasedException, IdentifierNotFound, MultipleActiveVaProfileIdsException) as e: message = f"{e.__class__.__name__} - {str(e)}: " \ f"Can't proceed after querying MPI for VA Profile ID for {notification_id}. " \ "Stopping execution of following tasks. Notification has been updated to permanent-failure." current_app.logger.warning(message) self.request.chain = None notifications_dao.update_notification_status_by_id( notification_id, NOTIFICATION_PERMANENT_FAILURE, status_reason=e.failure_reason) except Exception as e: message = f"Failed to retrieve VA Profile ID from MPI for notification: {notification_id} " \ "Notification has been updated to technical-failure" current_app.logger.exception(message) status_reason = e.failure_reason if hasattr( e, 'failure_reason') else 'Unknown error from MPI' notifications_dao.update_notification_status_by_id( notification_id, NOTIFICATION_TECHNICAL_FAILURE, status_reason=status_reason) raise NotificationTechnicalFailureException(message) from e
def notification(): recipient_identifier = RecipientIdentifier( notification_id=notification_id, id_type=IdentifierType.VA_PROFILE_ID.value, id_value=EXAMPLE_VA_PROFILE_ID ) notification = Notification(id=notification_id) notification.recipient_identifiers.set(recipient_identifier) notification.notification_type = LETTER_TYPE return notification
def sample_recipient_identifier(identifier_type=None, notification_id=None): if isinstance(identifier_type, IdentifierType): id_type = identifier_type.value elif identifier_type: id_type = identifier_type else: id_type = random.choice(IdentifierType.values()) # nosec id_value = get_random_alphanumeric_string() _notification_id = notification_id if notification_id else uuid4() return RecipientIdentifier(notification_id=_notification_id, id_type=id_type, id_value=id_value)
def test_should_throw_error_when_no_mapping_for_type(self, mocker): mock_identifier = mocker.Mock(IdentifierType) mock_identifier.name = "MOCKED_IDENTIFIER" mock_identifier.value = "mocked_value" mocker.patch("app.va.identifier.IdentifierType", return_value=mock_identifier) recipient_identifier = RecipientIdentifier( notification_id="123456", id_type=mock_identifier.name, id_value=mock_identifier.value) with pytest.raises(UnsupportedIdentifierException) as e: transform_to_fhir_format(recipient_identifier) assert "No mapping for identifier" in str(e.value)
def persist_notification(*, template_id, template_version, recipient=None, service, personalisation, notification_type, api_key_id, key_type, created_at=None, job_id=None, job_row_number=None, reference=None, client_reference=None, notification_id=None, simulated=False, created_by_id=None, status=NOTIFICATION_CREATED, reply_to_text=None, billable_units=None, postage=None, template_postage=None, recipient_identifier=None): notification_created_at = created_at or datetime.utcnow() if not notification_id: notification_id = uuid.uuid4() notification = Notification(id=notification_id, template_id=template_id, template_version=template_version, to=recipient, service_id=service.id, service=service, personalisation=personalisation, notification_type=notification_type, api_key_id=api_key_id, key_type=key_type, created_at=notification_created_at, job_id=job_id, job_row_number=job_row_number, client_reference=client_reference, reference=reference, created_by_id=created_by_id, status=status, reply_to_text=reply_to_text, billable_units=billable_units) if accept_recipient_identifiers_enabled() and recipient_identifier: _recipient_identifier = RecipientIdentifier( notification_id=notification_id, id_type=recipient_identifier['id_type'], id_value=recipient_identifier['id_value']) notification.recipient_identifiers.set(_recipient_identifier) if notification_type == SMS_TYPE and notification.to: formatted_recipient = validate_and_format_phone_number( recipient, international=True) recipient_info = get_international_phone_info(formatted_recipient) notification.normalised_to = formatted_recipient notification.international = recipient_info.international notification.phone_prefix = recipient_info.country_prefix notification.rate_multiplier = recipient_info.billable_units elif notification_type == EMAIL_TYPE and notification.to: notification.normalised_to = format_email_address(notification.to) elif notification_type == LETTER_TYPE: notification.postage = postage or template_postage # if simulated create a Notification model to return but do not persist the Notification to the dB if not simulated: dao_create_notification(notification) if key_type != KEY_TYPE_TEST: if redis_store.get(redis.daily_limit_cache_key(service.id)): redis_store.incr(redis.daily_limit_cache_key(service.id)) current_app.logger.info("{} {} created at {}".format( notification_type, notification_id, notification_created_at)) return notification
def create_notification( template=None, job=None, job_row_number=None, to_field=None, status='created', reference=None, created_at=None, sent_at=None, updated_at=None, billable_units=1, personalisation=None, api_key=None, key_type=KEY_TYPE_NORMAL, sent_by=None, client_reference=None, rate_multiplier=None, international=False, phone_prefix=None, scheduled_for=None, normalised_to=None, one_off=False, reply_to_text=None, created_by_id=None, postage=None, recipient_identifiers=None ): assert job or template if job: template = job.template if created_at is None: created_at = datetime.utcnow() if to_field is None: to_field = '+16502532222' if template.template_type == SMS_TYPE else '*****@*****.**' if status != 'created': sent_at = sent_at or datetime.utcnow() updated_at = updated_at or datetime.utcnow() if not one_off and (job is None and api_key is None): # we didn't specify in test - lets create it api_key = ApiKey.query.filter(ApiKey.service == template.service, ApiKey.key_type == key_type).first() if not api_key: api_key = create_api_key(template.service, key_type=key_type) if template.template_type == 'letter' and postage is None: postage = 'second' data = { 'id': uuid.uuid4(), 'to': to_field, 'job_id': job and job.id, 'job': job, 'service_id': template.service.id, 'service': template.service, 'template_id': template.id, 'template_version': template.version, 'status': status, 'reference': reference, 'created_at': created_at, 'sent_at': sent_at, 'billable_units': billable_units, 'personalisation': personalisation, 'notification_type': template.template_type, 'api_key': api_key, 'api_key_id': api_key and api_key.id, 'key_type': api_key.key_type if api_key else key_type, 'sent_by': sent_by, 'updated_at': updated_at, 'client_reference': client_reference, 'job_row_number': job_row_number, 'rate_multiplier': rate_multiplier, 'international': international, 'phone_prefix': phone_prefix, 'normalised_to': normalised_to, 'reply_to_text': reply_to_text, 'created_by_id': created_by_id, 'postage': postage } notification = Notification(**data) if recipient_identifiers: for recipient_identifier in recipient_identifiers: _recipient_identifier = RecipientIdentifier( notification_id=notification.id, id_type=recipient_identifier['id_type'], id_value=recipient_identifier['id_value'] ) notification.recipient_identifiers.set(_recipient_identifier) dao_create_notification(notification) if scheduled_for: scheduled_notification = ScheduledNotification(id=uuid.uuid4(), notification_id=notification.id, scheduled_for=datetime.strptime(scheduled_for, "%Y-%m-%d %H:%M")) if status != 'created': scheduled_notification.pending = False dao_created_scheduled_notification(scheduled_notification) return notification