class DNBCompanyChangeRequestView(APIView): """ View for requesting change/s to DNB companies. """ permission_classes = (HasPermissions( f'company.{CompanyPermission.view_company}', f'company.{CompanyPermission.change_company}', ), ) @method_decorator(enforce_request_content_type('application/json')) def post(self, request): """ A thin wrapper around the dnb-service change request API. """ change_request_serializer = DNBCompanyChangeRequestSerializer( data=request.data) change_request_serializer.is_valid(raise_exception=True) try: response = request_changes( **change_request_serializer.validated_data) except ( DNBServiceConnectionError, DNBServiceTimeoutError, DNBServiceError, ) as exc: raise APIUpstreamException(str(exc)) return Response(response) def get(self, request): """ A thin wrapper around the dnb-service change request API. """ duns_number = request.query_params.get('duns_number', None) status = request.query_params.get('status', None) change_request_serializer = DNBGetCompanyChangeRequestSerializer(data={ 'duns_number': duns_number, 'status': status }, ) change_request_serializer.is_valid(raise_exception=True) try: response = get_change_request( **change_request_serializer.validated_data) except ( DNBServiceConnectionError, DNBServiceTimeoutError, DNBServiceError, ) as exc: raise APIUpstreamException(str(exc)) return Response(response)
class DNBCompanyLinkView(APIView): """ View for linking a company to a DNB record. """ required_scopes = (Scope.internal_front_end, ) permission_classes = ( IsAuthenticatedOrTokenHasScope, HasPermissions( f'company.{CompanyPermission.view_company}', f'company.{CompanyPermission.change_company}', ), ) @method_decorator(enforce_request_content_type('application/json')) def post(self, request): """ Given a Data Hub Company ID and a duns-number, link the Data Hub Company to the D&B record. """ link_serializer = DNBCompanyLinkSerializer(data=request.data) link_serializer.is_valid(raise_exception=True) # This bit: validated_data['company_id'].id is weird but the alternative # is to rename the field to `company_id` which would (1) still be weird # and (2) leak the weirdness to the API company_id = link_serializer.validated_data['company_id'].id duns_number = link_serializer.validated_data['duns_number'] try: company = link_company_with_dnb(company_id, duns_number, request.user) except ( DNBServiceConnectionError, DNBServiceInvalidResponse, DNBServiceError, ) as exc: raise APIUpstreamException(str(exc)) except ( DNBServiceInvalidRequest, CompanyAlreadyDNBLinkedException, ) as exc: raise APIBadRequestException(str(exc)) return Response(CompanySerializer().to_representation(company), )
class DNBCompanyInvestigationView(APIView): """ View for creating a new investigation to get D&B to investigate and create a company record. """ permission_classes = (HasPermissions( f'company.{CompanyPermission.view_company}', f'company.{CompanyPermission.change_company}', ), ) @method_decorator(enforce_request_content_type('application/json')) def post(self, request): """ A wrapper around the investigation API endpoint for dnb-service. """ investigation_serializer = DNBCompanyInvestigationSerializer( data=request.data) investigation_serializer.is_valid(raise_exception=True) data = {'company_details': investigation_serializer.validated_data} company = data['company_details'].pop('company') try: response = create_investigation(data) except ( DNBServiceConnectionError, DNBServiceTimeoutError, DNBServiceError, ) as exc: raise APIUpstreamException(str(exc)) company.dnb_investigation_id = response['id'] company.pending_dnb_investigation = True company.save() return Response(response)
class DNBCompanyChangeRequestView(APIView): """ View for requesting change/s to DNB companies. """ required_scopes = (Scope.internal_front_end, ) permission_classes = ( IsAuthenticatedOrTokenHasScope, HasPermissions( f'company.{CompanyPermission.view_company}', f'company.{CompanyPermission.change_company}', ), ) @method_decorator(enforce_request_content_type('application/json')) def post(self, request): """ A thin wrapper around the dnb-service change request API. """ change_request_serializer = DNBCompanyChangeRequestSerializer( data=request.data) change_request_serializer.is_valid(raise_exception=True) try: response = request_changes( **change_request_serializer.validated_data) except ( DNBServiceConnectionError, DNBServiceTimeoutError, DNBServiceError, ) as exc: raise APIUpstreamException(str(exc)) return Response(response)
class ActivityFeedView(APIView): """ Activity Feed View. At the moment it just authenticates the user using the default authentication for the internal_front_end and acts as a proxy for reading from Activity Stream. If the authenticated user doesn't have permissions on all activity models, it returns an empty list. """ permission_classes = (IsAuthenticated,) ACTIVITY_MODELS_PERMISSIONS_REQUIRED = ( 'company_referral.view_companyreferral', 'interaction.view_all_interaction', 'investment.view_all_investmentproject', 'order.view_order', ) @method_decorator(enforce_request_content_type('application/json')) def get(self, request): """Proxy for GET requests.""" content_type = request.content_type or '' # if the user doesn't have permissions on all activity models, return an empty list # TODO: instead of returning an empty list, we need to filter out the # activities that the user doesn't have access to if not request.user.has_perms(self.ACTIVITY_MODELS_PERMISSIONS_REQUIRED): return JsonResponse( { 'hits': { 'total': 0, 'hits': [], }, }, status=status.HTTP_200_OK, content_type=content_type, ) upstream_response = self._get_upstream_response(request) return HttpResponse( upstream_response.text, status=upstream_response.status_code, content_type=upstream_response.headers.get('content-type'), ) def _get_upstream_response(self, request=None): hawk_auth = HawkAuth( settings.ACTIVITY_STREAM_OUTGOING_ACCESS_KEY_ID, settings.ACTIVITY_STREAM_OUTGOING_SECRET_ACCESS_KEY, verify_response=False, ) api_client = APIClient( api_url=settings.ACTIVITY_STREAM_OUTGOING_URL, auth=hawk_auth, request=request, raise_for_status=False, default_timeout=settings.DEFAULT_SERVICE_TIMEOUT, ) return api_client.request( request.method, '', data=request.body, headers={ 'Content-Type': request.content_type, }, )
class DNBCompanySearchView(APIView): """ View for searching DNB companies. """ required_scopes = (Scope.internal_front_end, ) permission_classes = ( IsAuthenticatedOrTokenHasScope, HasPermissions(f'company.{CompanyPermission.view_company}', ), ) @method_decorator(enforce_request_content_type('application/json')) def post(self, request): """ Proxy to DNB search API for POST requests. This will also hydrate results with Data Hub company details if the company exists (and can be matched) on Data Hub. """ upstream_response = search_dnb(request.data) if upstream_response.status_code == status.HTTP_200_OK: response_body = upstream_response.json() response_body['results'] = self._format_and_hydrate( response_body.get('results', []), ) return JsonResponse(response_body) return HttpResponse( upstream_response.text, status=upstream_response.status_code, content_type=upstream_response.headers.get('content-type'), ) def _get_datahub_companies_by_duns(self, duns_numbers): datahub_companies = get_company_queryset().filter( duns_number__in=duns_numbers) return {company.duns_number: company for company in datahub_companies} def _get_datahub_company_data(self, datahub_company): if datahub_company: return DNBMatchedCompanySerializer( datahub_company, context={ 'request': self.request }, ).data return None def _get_hydrated_results(self, dnb_results, datahub_companies_by_duns): dnb_datahub_company_pairs = (( dnb_company, self._get_datahub_company_data( datahub_companies_by_duns.get(dnb_company['duns_number']), ), ) for dnb_company in dnb_results) return [{ 'dnb_company': dnb_company, 'datahub_company': datahub_company, } for dnb_company, datahub_company in dnb_datahub_company_pairs] def _format_and_hydrate(self, dnb_results): """ Format each result from DNB such that there is a "dnb_company" key and a "datahub_company" key. The value for "datahub_company" represents the corresponding Company entry on Data Hub for the DNB result, if it exists. This changes a DNB result entry from: { "duns_number": "999999999", "primary_name": "My Company LTD", ... } To: { "dnb_company": { "duns_number": "999999999", "primary_name": "My Company LTD", ... }, "datahub_company": { "id": "0f5216e0-849f-11e6-ae22-56b6b6499611", "latest_interaction": { "id": "e8c3534f-4f60-4c93-9880-09c22e4fc011", "created_on": "2018-04-08T14:00:00Z", "date": "2018-06-06", "subject": "Meeting with Joe Bloggs" } } } """ duns_numbers = [result['duns_number'] for result in dnb_results] datahub_companies_by_duns = self._get_datahub_companies_by_duns( duns_numbers) hydrated_results = self._get_hydrated_results( dnb_results, datahub_companies_by_duns) return hydrated_results