def test_create_service_or_go_live_deal_fail_contact_exists( notify_api: Flask, sample_service: Service, mocker: MockFixture, is_go_live: bool, existing_contact_id: Optional[str], ): with requests_mock.mock() as rmock: contact_id = existing_contact_id or "1" rmock.request( contact_http_method(existing_contact_id), url=generate_contact_url(existing_contact_id, sample_service), headers={"Accept": "application/json", "Content-Type": "application/json"}, status_code=200, text=json.dumps({"data": {"id": contact_id, "created_at": "1", "updated_at": "2"}}), ) mocker.patch("app.user.rest.ZenDeskSell.upsert_deal", return_value=None) mocker.patch( "app.user.rest.ZenDeskSell.convert_lead_to_contact", return_value=existing_contact_id, ) contact_delete_mock = mocker.patch("app.user.rest.ZenDeskSell.delete_contact") with notify_api.app_context(): if is_go_live: assert not ZenDeskSell().send_go_live_service(sample_service, sample_service.users[0]) else: assert not ZenDeskSell().send_create_service(sample_service, sample_service.users[0]) contact_delete_mock.assert_not_called()
def create_service(): data = request.get_json() if not data.get("user_id"): errors = {"user_id": ["Missing data for required field."]} raise InvalidRequest(errors, status_code=400) data.pop("service_domain", None) # validate json with marshmallow service_schema.load(data) user = get_user_by_id(data.pop("user_id")) # unpack valid json into service object valid_service = Service.from_json(data) dao_create_service(valid_service, user) try: # try-catch; just in case, we don't want to error here ZenDeskSell().send_create_service(valid_service, user) except Exception as e: current_app.logger.exception(e) return jsonify(data=service_schema.dump(valid_service).data), 201
def test_create_note_invalid_response( notify_api: Flask, sample_service: Service, expected_resp_data: Dict[str, Dict[str, Union[int, str]]], ): with requests_mock.mock() as rmock: rmock.request( "POST", url="https://zendesksell-test.com/v2/notes", headers={"Accept": "application/json", "Content-Type": "application/json"}, status_code=200, text=json.dumps(expected_resp_data), ) data: Dict[str, Any] = { "email_address": "*****@*****.**", "service_name": "service_name", "department_org_name": "department_org_name", "intended_recipients": "intended_recipients", "main_use_case": "main_use_case", "notification_types": "notification_types", "expected_volume": "expected_volume", "service_url": "service_url", "support_type": "go_live_request", } with notify_api.app_context(): note_id = ZenDeskSell().create_note(ZenDeskSell.NoteResourceType.DEAL, "1", ContactRequest(**data)) assert not note_id
def test_create_deal_invalid_response( notify_api: Flask, sample_service: Service, expected_resp_data: Dict[str, Dict[str, Union[int, str]]], ): def match_json(request): expected = { "data": { "contact_id": "123456789", "name": "Sample service", "stage_id": 123456789, "owner_id": ZenDeskSell.OWNER_ID, "custom_fields": {"notify_service_id": str(sample_service.id)}, } } json_matches = request.json() == expected basic_auth_header = request.headers.get("Authorization") == "Bearer zendesksell-api-key" return json_matches and basic_auth_header with requests_mock.mock() as rmock: contact_id = "123456789" rmock.request( "POST", url=f"https://zendesksell-test.com/v2/deals/upsert?" f"custom_fields[notify_service_id]={str(sample_service.id)}", headers={"Accept": "application/json", "Content-Type": "application/json"}, additional_matcher=match_json, status_code=200, text=json.dumps(expected_resp_data), ) with notify_api.app_context(): deal_id = ZenDeskSell().upsert_deal(contact_id, sample_service, 123456789) assert not deal_id
def test_convert_lead_to_contact_search_fails(notify_api: Flask, sample_service: Service, mocker: MockFixture): with notify_api.app_context(): search_lead_id_mock = mocker.patch("app.user.rest.ZenDeskSell.search_lead_id", return_value=None) contact_id = ZenDeskSell().convert_lead_to_contact(sample_service.users[0]) search_lead_id_mock.assert_called_once_with(sample_service.users[0]) assert not contact_id
def test_create_contact_invalid_response( notify_api: Flask, sample_service: Service, existing_contact_id: Optional[str], expected_resp_data: Dict[str, Dict[str, Union[int, str]]], ): def match_json(request): expected = { "data": { "last_name": "User", "first_name": "Test", "email": "*****@*****.**", "mobile": "+16502532222", "owner_id": ZenDeskSell.OWNER_ID, "custom_fields": {"notify_user_id": str(sample_service.users[0].id)}, } } json_matches = request.json() == expected basic_auth_header = request.headers.get("Authorization") == "Bearer zendesksell-api-key" return json_matches and basic_auth_header with requests_mock.mock() as rmock: rmock.request( contact_http_method(existing_contact_id), url=generate_contact_url(existing_contact_id, sample_service), headers={"Accept": "application/json", "Content-Type": "application/json"}, additional_matcher=match_json, status_code=200, text=json.dumps(expected_resp_data), ) with notify_api.app_context(): contact_id, _ = ZenDeskSell().upsert_contact(sample_service.users[0], existing_contact_id) assert not contact_id
def test_send_go_live_service(notify_api: Flask, sample_service: Service, mocker: MockFixture): contact_id = 1 upsert_contact_mock = mocker.patch("app.user.rest.ZenDeskSell.upsert_contact", return_value=(contact_id, True)) upsert_deal_mock = mocker.patch("app.user.rest.ZenDeskSell.upsert_deal", return_value=1) with notify_api.app_context(): assert ZenDeskSell().send_go_live_service(sample_service, sample_service.users[0]) upsert_contact_mock.assert_called_once_with(sample_service.users[0], None) upsert_deal_mock.assert_called_once_with(contact_id, sample_service, ZenDeskSell.STATUS_CLOSE_LIVE)
def test_create_note(notify_api: Flask): resource_id = "1" def match_json(request): expected = { "data": { "resource_type": "deal", "resource_id": resource_id, "content": "\n".join( [ "Live Notes", "service_name just requested to go live.", "", "- Department/org: department_org_name", "- Intended recipients: intended_recipients", "- Purpose: main_use_case", "- Notification types: notification_types", "- Expected monthly volume: expected_volume", "---", "service_url", ] ), } } json_matches = request.json() == expected basic_auth_header = request.headers.get("Authorization") == "Bearer zendesksell-api-key" return json_matches and basic_auth_header with requests_mock.mock() as rmock: expected_note_id = "1" resp_data = {"data": {"id": expected_note_id}} rmock.request( "POST", url="https://zendesksell-test.com/v2/notes", headers={"Accept": "application/json", "Content-Type": "application/json"}, additional_matcher=match_json, status_code=200, text=json.dumps(resp_data), ) data: Dict[str, Any] = { "email_address": "*****@*****.**", "service_name": "service_name", "department_org_name": "department_org_name", "intended_recipients": "intended_recipients", "main_use_case": "main_use_case", "notification_types": "notification_types", "expected_volume": "expected_volume", "service_url": "service_url", "support_type": "go_live_request", } with notify_api.app_context(): note_id = ZenDeskSell().create_note(ZenDeskSell.NoteResourceType.DEAL, resource_id, ContactRequest(**data)) assert expected_note_id == note_id
def test_create_service_or_go_live_contact_fail( notify_api: Flask, sample_service: Service, mocker: MockFixture, is_go_live: bool, existing_contact_id: Optional[str], ): upsert_contact_mock = mocker.patch("app.user.rest.ZenDeskSell.upsert_contact", return_value=(None, False)) convert_lead_to_contact_mock = mocker.patch( "app.user.rest.ZenDeskSell.convert_lead_to_contact", return_value=existing_contact_id, ) with notify_api.app_context(): if is_go_live: assert not ZenDeskSell().send_go_live_service(sample_service, sample_service.users[0]) upsert_contact_mock.assert_called_once_with(sample_service.users[0], existing_contact_id) else: assert not ZenDeskSell().send_create_service(sample_service, sample_service.users[0]) convert_lead_to_contact_mock.assert_called_once_with(sample_service.users[0]) upsert_contact_mock.assert_called_once_with(sample_service.users[0], existing_contact_id)
def update_service(service_id): req_json = request.get_json() fetched_service = dao_fetch_service_by_id(service_id) # Capture the status change here as Marshmallow changes this later service_going_live = fetched_service.restricted and not req_json.get( "restricted", True) message_limit_changed = fetched_service.message_limit != req_json.get( "message_limit", fetched_service.message_limit) current_data = dict(service_schema.dump(fetched_service).data.items()) current_data.update(request.get_json()) service = service_schema.load(current_data).data if "email_branding" in req_json: email_branding_id = req_json["email_branding"] service.email_branding = None if not email_branding_id else EmailBranding.query.get( email_branding_id) if "letter_branding" in req_json: letter_branding_id = req_json["letter_branding"] service.letter_branding = None if not letter_branding_id else LetterBranding.query.get( letter_branding_id) dao_update_service(service) if message_limit_changed: redis_store.delete(daily_limit_cache_key(service_id)) redis_store.delete(near_daily_limit_cache_key(service_id)) redis_store.delete(over_daily_limit_cache_key(service_id)) if not fetched_service.restricted: _warn_service_users_about_message_limit_changed( service_id, current_data) if service_going_live: _warn_services_users_about_going_live(service_id, current_data) try: # Two scenarios, if there is a user that has requested to go live, we will use that user # to create a user-service/contact-deal pair between notify and zendesk sell # If by any chance there is no tracked request to a user, notify will try to identify the user # that created the service and then create a user-service/contact-deal relationship if service.go_live_user_id: user = get_user_by_id(service.go_live_user_id) else: user = dao_fetch_service_creator(service.id) ZenDeskSell().send_go_live_service(service, user) except Exception as e: current_app.logger.exception(e) return jsonify(data=service_schema.dump(fetched_service).data), 200
def test_delete_contact(notify_api: Flask): def match_header(request): return request.headers.get("Authorization") == "Bearer zendesksell-api-key" with requests_mock.mock() as rmock: contact_id = "123456789" rmock.request( "DELETE", url=f"https://zendesksell-test.com/v2/contacts/{contact_id}", headers={"Accept": "application/json", "Content-Type": "application/json"}, additional_matcher=match_header, status_code=200, ) with notify_api.app_context(): # as long as it doesn't throw we are OK as this is a best effort method ZenDeskSell().delete_contact(contact_id)
def test_send_create_service( notify_api: Flask, sample_service: Service, mocker: MockFixture, existing_contact_id: Optional[str], ): contact_id = existing_contact_id or "1" upsert_contact_mock = mocker.patch("app.user.rest.ZenDeskSell.upsert_contact", return_value=(contact_id, True)) convert_lead_to_contact_mock = mocker.patch( "app.user.rest.ZenDeskSell.convert_lead_to_contact", return_value=existing_contact_id, ) upsert_deal_mock = mocker.patch("app.user.rest.ZenDeskSell.upsert_deal", return_value=1) with notify_api.app_context(): assert ZenDeskSell().send_create_service(sample_service, sample_service.users[0]) convert_lead_to_contact_mock.assert_called_once_with(sample_service.users[0]) upsert_contact_mock.assert_called_once_with(sample_service.users[0], existing_contact_id) upsert_deal_mock.assert_called_once_with(contact_id, sample_service, ZenDeskSell.STATUS_CREATE_TRIAL)
def test_convert_lead_to_contact(notify_api: Flask, sample_service: Service): lead_id = "123456789" def match_json(request): expected = { "data": { "lead_id": lead_id, "owner_id": ZenDeskSell.OWNER_ID, "create_deal": False, } } json_matches = request.json() == expected basic_auth_header = request.headers.get("Authorization") == "Bearer zendesksell-api-key" return json_matches and basic_auth_header with requests_mock.mock() as rmock: expected_contact_id = "1234567890" rmock.request( "GET", url=f"https://zendesksell-test.com/v2/leads?email={sample_service.users[0].email_address}", headers={"Accept": "application/json", "Content-Type": "application/json"}, status_code=200, text=json.dumps({"items": [{"data": {"id": lead_id}}]}), ) rmock.request( "POST", url="https://zendesksell-test.com/v2/lead_conversions", headers={"Accept": "application/json", "Content-Type": "application/json"}, additional_matcher=match_json, status_code=200, text=json.dumps({"data": {"individual_id": expected_contact_id}}), ) with notify_api.app_context(): contact_id = ZenDeskSell().convert_lead_to_contact(sample_service.users[0]) assert contact_id == expected_contact_id
def test_send_go_live_request_search_failed(notify_api: Flask, sample_service: Service, mocker: MockFixture): deal_id = "1" search_deal_id_mock = mocker.patch("app.user.rest.ZenDeskSell.search_deal_id", return_value=None) send_create_service_mock = mocker.patch("app.user.rest.ZenDeskSell.send_create_service", return_value=deal_id) create_note_mock = mocker.patch("app.user.rest.ZenDeskSell.create_note", return_value="1") data: Dict[str, Any] = { "email_address": "*****@*****.**", "service_name": "service_name", "department_org_name": "department_org_name", "intended_recipients": "intended_recipients", "main_use_case": "main_use_case", "notification_types": "notification_types", "expected_volume": "expected_volume", "service_url": "service_url", "support_type": "go_live_request", } contact = ContactRequest(**data) with notify_api.app_context(): assert ZenDeskSell().send_go_live_request(sample_service, sample_service.users[0], contact) search_deal_id_mock.assert_called_once_with(sample_service) send_create_service_mock.assert_called_once_with(sample_service, sample_service.users[0]) create_note_mock.assert_called_once_with(ZenDeskSell.NoteResourceType.DEAL, deal_id, contact)
def test_create_lead(notify_api: Flask): def match_json(request): expected = { "data": { "last_name": "User", "first_name": "Test", "organization_name": "", "email": "*****@*****.**", "description": "Program: \n: ", "tags": ["", "en"], "status": "New", "source_id": 2085874, "owner_id": ZenDeskSell.OWNER_ID, "custom_fields": { "Product": ["Notify"], "Intended recipients": "No value", }, } } json_matches = request.json() == expected basic_auth_header = request.headers.get("Authorization") == "Bearer zendesksell-api-key" return json_matches and basic_auth_header with requests_mock.mock() as rmock: rmock.request( "POST", url="https://zendesksell-test.com/v2/leads/[email protected]", headers={"Accept": "application/json", "Content-Type": "application/json"}, additional_matcher=match_json, status_code=201, ) with notify_api.app_context(): response = ZenDeskSell().upsert_lead(ContactRequest(email_address="*****@*****.**", name="Test User")) assert response == 201
def test_create_lead_missing_name(notify_api: Flask): # Name field is a requirement for the zendesk sell API interface with notify_api.app_context(): with pytest.raises(AssertionError): ZenDeskSell().upsert_lead(ContactRequest(email_address="*****@*****.**"))