def update_from_dnb(model_admin, request, object_id): """ Tool to let admin users update company with a valid `duns_number` by pulling fresh data from D&B. The company record will be versioned after the update from D&B is applied. The `pending_dnb_investigation` field will be set to False. """ if not model_admin.has_change_permission(request): raise PermissionDenied() dh_company = model_admin.get_object(request, object_id) company_change_page = reverse( admin_urlname(model_admin.model._meta, 'change'), kwargs={'object_id': dh_company.pk}, ) if dh_company is None or dh_company.duns_number is None: raise SuspiciousOperation() try: dnb_company = get_company(dh_company.duns_number, request) except DNBServiceInvalidRequestError: message = 'No matching company found in D&B database.' raise AdminError([message], company_change_page) except DNBServiceBaseError: message = 'Something went wrong in an upstream service.' raise AdminError([message], company_change_page) if request.method == 'GET': return TemplateResponse( request, 'admin/company/company/update-from-dnb.html', { **model_admin.admin_site.each_context(request), 'media': model_admin.media, 'opts': model_admin.model._meta, 'object': dh_company, 'title': gettext_lazy('Confirm update from D&B'), 'diff': format_company_diff(dh_company, dnb_company), }, ) try: update_company_from_dnb(dh_company, dnb_company, request.user) return HttpResponseRedirect(company_change_page) except serializers.ValidationError: message = 'Data from D&B did not pass the Data Hub validation checks.' raise AdminError([message], company_change_page)
def _sync_company_with_dnb( company_id, fields_to_update, task, update_descriptor, retry_failures=True, ): dh_company = Company.objects.get(id=company_id) try: dnb_company = get_company(dh_company.duns_number) except DNBServiceError as exc: if is_server_error(exc.status_code) and retry_failures: raise task.retry(exc=exc, countdown=60) raise except (DNBServiceConnectionError, DNBServiceTimeoutError) as exc: if retry_failures: raise task.retry(exc=exc, countdown=60) raise update_company_from_dnb( dh_company, dnb_company, fields_to_update=fields_to_update, update_descriptor=update_descriptor, )
def _get_company(duns_number, error_url, request=None): try: return get_company(duns_number, request) except DNBServiceInvalidRequestError: message = 'No matching company found in D&B database.' raise AdminError([message], error_url) except DNBServiceBaseError: message = 'Something went wrong in an upstream service.' raise AdminError([message], error_url)
def test_get_company_dnb_service_request_error( caplog, requests_mock, request_exception, expected_exception, expected_message, ): """ Test if there is an error connecting to dnb-service, we log it and raise the exception with an appropriate message. """ requests_mock.post( DNB_SEARCH_URL, exc=request_exception, ) with pytest.raises(expected_exception) as e: get_company('123456789') assert str(e.value) == str(expected_message)
def _get_company(duns_number, error_url): try: return get_company(duns_number) except DNBServiceInvalidRequest: message = 'No matching company found in D&B database.' raise AdminException([message], error_url) except DNBServiceException: message = 'Something went wrong in an upstream service.' raise AdminException([message], error_url)
def test_get_company_invalid_request_response( caplog, requests_mock, search_results, expected_exception, expected_message, ): """ Test if a given `duns_number` gets anything other than a single company from dnb-service, the get_company function raises an exception. """ requests_mock.post( DNB_SEARCH_URL, json={'results': search_results}, ) with pytest.raises(expected_exception) as e: get_company('123456789') assert e.value.args[0] == expected_message assert len(caplog.records) == 1 assert caplog.records[0].getMessage() == expected_message
def test_get_company_dnb_service_error( caplog, requests_mock, dnb_response_status, ): """ Test if the dnb-service returns a status code that is not 200, we log it and raise the exception with an appropriate message. """ requests_mock.post( DNB_SEARCH_URL, status_code=dnb_response_status, ) with pytest.raises(DNBServiceError) as e: get_company('123456789') expected_message = f'DNB service returned an error status: {dnb_response_status}' assert e.value.args[0] == expected_message assert len(caplog.records) == 1 assert caplog.records[0].getMessage() == expected_message
def test_get_company_valid( caplog, requests_mock, dnb_response_uk, ): """ Test if dnb-service returns a valid response, get_company returns a formatted dict. """ requests_mock.post( DNB_V2_SEARCH_URL, json=dnb_response_uk, ) dnb_company = get_company('123456789') assert dnb_company == { 'company_number': '01261539', 'name': 'FOO BICYCLE LIMITED', 'duns_number': '123456789', 'trading_names': [], 'address': { 'area': None, 'country': UUID('80756b9a-5d95-e211-a939-e4115bead28a'), 'county': '', 'line_1': 'Unit 10, Ockham Drive', 'line_2': '', 'postcode': 'UB6 0F2', 'town': 'GREENFORD', }, 'registered_address': { 'area': None, 'country': UUID('80756b9a-5d95-e211-a939-e4115bead28a'), 'county': '', 'line_1': 'C/O LONE VARY', 'line_2': '', 'postcode': 'UB6 0F2', 'town': 'GREENFORD', }, 'number_of_employees': 260, 'is_number_of_employees_estimated': True, 'turnover': 50651895.0, 'is_turnover_estimated': None, 'uk_based': True, 'website': 'http://foo.com', 'global_ultimate_duns_number': '291332174', }
def post(self, request): """ Given a duns_number, get the data for the company from dnb-service and create a record in DataHub. """ duns_serializer = DUNSNumberSerializer(data=request.data) duns_serializer.is_valid(raise_exception=True) duns_number = duns_serializer.validated_data['duns_number'] try: dnb_company = get_company(duns_number) except (DNBServiceConnectionError, DNBServiceError, DNBServiceInvalidResponse) as exc: raise APIUpstreamException(str(exc)) except DNBServiceInvalidRequest as exc: raise APIBadRequestException(str(exc)) company_serializer = DNBCompanySerializer(data=dnb_company, ) try: company_serializer.is_valid(raise_exception=True) except serializers.ValidationError: message = 'Company data from DNB failed DH serializer validation' extra_data = { 'formatted_dnb_company_data': dnb_company, 'dh_company_serializer_errors': company_serializer.errors, } logger.error(message, extra=extra_data) raise datahub_company = company_serializer.save( created_by=request.user, modified_by=request.user, dnb_modified_on=now(), ) statsd.incr(f'dnb.create.company') return Response( company_serializer.to_representation(datahub_company), )