def send_resource( requests: Requests, info: CaseTriggerInfo, resource: dict, repeater_id: str, *, raise_on_ext_id: bool = False, ) -> Response: external_id = info.extra_fields['external_id'] if external_id: endpoint = f"{resource['resourceType']}/{external_id}" response = requests.put(endpoint, json=resource, raise_for_status=True) return response endpoint = f"{resource['resourceType']}/" response = requests.post(endpoint, json=resource, raise_for_status=True) try: _set_external_id(info, response.json()['id'], repeater_id) except (ValueError, KeyError) as err: # The remote service returned a 2xx response, but did not # return JSON, or the JSON does not include an ID. if raise_on_ext_id: msg = 'Unable to parse response from remote FHIR service' raise HTTPError(msg, response=response) from err return response
def send_request(self, repeat_record, payload): """ Sends API request and returns response if ``payload`` is a form that is configured to be forwarded to DHIS2. If ``payload`` is a form that isn't configured to be forwarded, returns True. """ requests = Requests( self.domain, self.url, self.username, self.plaintext_password, verify=self.verify, notify_addresses=self.notify_addresses, payload_id=repeat_record.payload_id, ) for form_config in self.dhis2_config.form_configs: if form_config.xmlns == payload['form']['@xmlns']: try: return send_dhis2_event( requests, form_config, payload, ) except (RequestException, HTTPError, ConfigurationError) as err: requests.notify_error(f"Error sending Events to {self}: {err}") raise return True
def setUp(self): self.requests = Requests(TEST_DOMAIN, TEST_API_URL, TEST_API_USERNAME, TEST_API_PASSWORD, logger=noop_logger) self.org_unit_id = 'abc' self.data_element_id = '123'
def openmrs_raw_api(request, domain, repeater_id, rest_uri): get_params = dict(request.GET) no_links = get_params.pop('links', None) is None repeater = OpenmrsRepeater.get(repeater_id) assert repeater.domain == domain requests = Requests(repeater.domain, repeater.url, repeater.username, repeater.password) raw_json = requests.get('/ws/rest/v1' + rest_uri, get_params).json() if no_links: return JsonResponse(_filter_out_links(raw_json)) return JsonResponse(raw_json)
def import_resource_type( requests: Requests, resource_type: FHIRImportResourceType, child_cases: List[ParentInfo], ): try: for resource in iter_resources(requests, resource_type): import_resource(requests, resource_type, resource, child_cases) except Exception as err: requests.notify_exception(str(err))
class JsonApiRequestTests(SimpleTestCase): def setUp(self): self.requests = Requests(TEST_DOMAIN, TEST_API_URL, TEST_API_USERNAME, TEST_API_PASSWORD) self.org_unit_id = 'abc' self.data_element_id = '123' def test_authentication(self): with patch('corehq.motech.requests.RequestLog', Mock()), \ patch('corehq.motech.requests.requests') as requests_mock: content = {'code': TEST_API_USERNAME} content_json = json.dumps(content) response_mock = Mock() response_mock.status_code = 200 response_mock.content = content_json response_mock.json.return_value = content requests_mock.get.return_value = response_mock response = self.requests.get('me') requests_mock.get.assert_called_with( TEST_API_URL + 'me', headers={'Accept': 'application/json'}, auth=(TEST_API_USERNAME, TEST_API_PASSWORD) ) self.assertEqual(response.status_code, 200) self.assertEqual(response.json()['code'], TEST_API_USERNAME) def test_send_data_value_set(self): with patch('corehq.motech.requests.RequestLog', Mock()), \ patch('corehq.motech.requests.requests') as requests_mock: payload = {'dataValues': [ {'dataElement': self.data_element_id, 'period': "201701", 'orgUnit': self.org_unit_id, 'value': "180"}, {'dataElement': self.data_element_id, 'period': "201702", 'orgUnit': self.org_unit_id, 'value': "200"}, ]} content = {'status': 'SUCCESS', 'importCount': {'imported': 2}} content_json = json.dumps(content) response_mock = Mock() response_mock.status_code = 201 response_mock.content = content_json response_mock.json.return_value = content requests_mock.post.return_value = response_mock response = self.requests.post('dataValueSets', json=payload) requests_mock.post.assert_called_with( 'http://localhost:9080/api/dataValueSets', json=payload, headers={'Content-type': 'application/json', 'Accept': 'application/json'}, auth=(TEST_API_USERNAME, TEST_API_PASSWORD) ) self.assertEqual(response.status_code, 201) self.assertEqual(response.json()['status'], 'SUCCESS') self.assertEqual(response.json()['importCount']['imported'], 2)
def test_verify_ssl(self): with patch('corehq.motech.requests.RequestLog', Mock()), \ patch('corehq.motech.requests.requests') as requests_mock: requests = Requests(TEST_DOMAIN, TEST_API_URL, TEST_API_USERNAME, TEST_API_PASSWORD, verify=False) requests.get('me') requests_mock.get.assert_called_with( TEST_API_URL + 'me', headers={'Accept': 'application/json'}, auth=(TEST_API_USERNAME, TEST_API_PASSWORD), verify=False )
def test_verify_ssl(self): with patch('corehq.motech.requests.RequestLog', Mock()), \ patch('corehq.motech.requests.requests') as requests_mock: requests = Requests(TEST_DOMAIN, TEST_API_URL, TEST_API_USERNAME, TEST_API_PASSWORD, verify=False) requests.get('me') requests_mock.get.assert_called_with( TEST_API_URL + 'me', headers={'Accept': 'application/json'}, auth=(TEST_API_USERNAME, TEST_API_PASSWORD), verify=False)
def send_datasets(domain_name, send_now=False, send_date=None): """ Sends a data set of data values in the following format: { "dataSet": "dataSetID", "completeDate": "date", "period": "period", "orgUnit": "orgUnitID", "attributeOptionCombo", "aocID", "dataValues": [ { "dataElement": "dataElementID", "categoryOptionCombo": "cocID", "value": "1", "comment": "comment1" }, { "dataElement": "dataElementID", "categoryOptionCombo": "cocID", "value": "2", "comment": "comment2" }, { "dataElement": "dataElementID", "categoryOptionCombo": "cocID", "value": "3", "comment": "comment3" } ] } See DHIS2 API docs for more details: https://docs.dhis2.org/master/en/developer/html/webapi_data_values.html """ if not send_date: send_date = datetime.today() dhis2_conn = Dhis2Connection.objects.filter(domain=domain_name).first() dataset_maps = get_dataset_maps(domain_name) if not dataset_maps: return # Nothing to do for dataset_map in dataset_maps: if send_now or dataset_map.should_send_on_date(send_date): if dataset_map.connection_settings: conn = dataset_map.connection_settings url = conn.url endpoint = '/api/dataValueSets' else: conn = dhis2_conn url = conn.server_url endpoint = 'dataValueSets' if not conn: continue dataset = dataset_map.get_dataset(send_date) requests = Requests(domain_name, url, conn.username, conn.plaintext_password, verify=not conn.skip_cert_verify, notify_addresses=conn.notify_addresses if hasattr(conn, 'notify_addresses') else None) requests.post(endpoint, json=dataset)
def test_success(self): with patch.object(Requests, 'get') as request_get: request_get.side_effect = [ ReadTimeout, ReadTimeout, ReadTimeout, Response(), ] requests = Requests( TEST_DOMAIN, 'https://example.com/', auth_manager=self.no_auth, logger=lambda level, entry: None, ) importer = Importer() rows = get_openmrs_patients(requests, importer) self.assertEqual(rows, [ { u'familyName': u'Hornblower', u'givenName': u'Horatio', u'personId': 2 }, { u'familyName': u'Patient', u'givenName': u'John', u'personId': 3 }, ])
def register_patients( requests: Requests, info_resource_list: List[tuple], repeater_id: str, ) -> List[tuple]: info_resource_list_to_send = [] for info, resource in info_resource_list: if resource['resourceType'] != 'Patient': info_resource_list_to_send.append((info, resource)) continue if info.extra_fields['external_id']: # Patient is already registered info_resource_list_to_send.append((info, resource)) continue response = requests.post( 'Patient/', json=resource, raise_for_status=True, ) try: _set_external_id(info, response.json()['id'], repeater_id) # Don't append `resource` to `info_resource_list_to_send` # because the remote service has all its data now. except (ValueError, KeyError) as err: # The remote service returned a 2xx response, but did not # return JSON, or the JSON does not include an ID. msg = 'Unable to parse response from remote FHIR service' raise HTTPError(msg, response=response) from err return info_resource_list_to_send
def requests(self): return Requests( self.domain, self.url, self.username, self.plaintext_password, verify=self.verify )
def test_verify_ssl(self): with patch('corehq.motech.requests.RequestLog', Mock()), \ patch.object(requests.Session, 'request') as request_mock: self.requests = Requests(TEST_DOMAIN, TEST_API_URL, TEST_API_USERNAME, TEST_API_PASSWORD, verify=False) self.requests.get('me') request_mock.assert_called_with( 'GET', TEST_API_URL + 'me', allow_redirects=True, headers={'Accept': 'application/json'}, auth=(TEST_API_USERNAME, TEST_API_PASSWORD), verify=False)
def send_request(self, repeat_record, payload): for form_config in self.dhis2_config.form_configs: if form_config.xmlns == payload['form']['@xmlns']: return send_data_to_dhis2( Requests(self.domain, self.url, self.username, self.plaintext_password, verify=self.verify), form_config, payload, )
def test_verify_ssl(self): with patch.object(requests.Session, 'request') as request_mock: req = Requests(TEST_DOMAIN, TEST_API_URL, TEST_API_USERNAME, TEST_API_PASSWORD, verify=False, logger=noop_logger) req.get('me') request_mock.assert_called_with( 'GET', TEST_API_URL + 'me', allow_redirects=True, headers={'Accept': 'application/json'}, auth=(TEST_API_USERNAME, TEST_API_PASSWORD), timeout=REQUEST_TIMEOUT, verify=False)
def send_resources( requests: Requests, info_resources_list: List[tuple], fhir_version: str, ) -> Response: entries = get_bundle_entries(info_resources_list, fhir_version) bundle = create_bundle(entries, bundle_type='transaction') response = requests.post('/', json=bundle) return response
def generate_identifier(requests, identifier_type): """ Calls the idgen module's generateIdentifier endpoint Identifier source ID is determined from `identifier_type`. If `identifier_type` doesn't have an identifier source, return None. If the identifier source doesn't return an identifier, return None. If anything goes wrong ... return None. Partners in Health have written basic auth support for the idgen module, but it is not yet widely used. Until then, requests must use a session that has been authenticated with the HTML login page. """ identifier = None source_id = None # Create a new Requests session to log in using an HTML login page. # See `authenticate_session()` for details. with Requests( domain_name=requests.domain_name, base_url=requests.base_url, verify=requests.verify, auth_manager=requests.auth_manager, notify_addresses=requests.notify_addresses, payload_id=requests.payload_id, logger=requests.logger, ) as requests_session: authenticate_session(requests_session) try: source_id = get_identifier_source_id(requests_session, identifier_type) except OpenmrsHtmlUiChanged as err: requests.notify_exception('Unexpected OpenMRS HTML UI', details=str(err)) if source_id: # Example request: http://www.example.com/openmrs/module/idgen/generateIdentifier.form?source=1 response = requests_session.get( '/module/idgen/generateIdentifier.form', params={'source': source_id}) try: if not (200 <= response.status_code < 300 and response.content): raise OpenmrsException() try: # Example response: {"identifiers": ["CHR203007"]} identifier = response.json()['identifiers'][0] except (ValueError, IndexError, KeyError): raise OpenmrsException() except OpenmrsException: requests.notify_exception( 'OpenMRS idgen module returned an unexpected response', details= (f'OpenMRS idgen module at "{response.url}" ' f'returned an unexpected response {response.status_code}: \r\n' f'{response.content}')) return identifier
def get_requests(self, payload_id=None): return Requests( self.domain, self.url, self.username, self.plaintext_password, verify=self.verify, notify_addresses=self.notify_addresses, payload_id=payload_id, )
def generate_identifier(requests, identifier_type): """ Calls the idgen module's generateIdentifier endpoint Identifier source ID is determined from `identifier_type`. If `identifier_type` doesn't have an identifier source, return None. If the identifier source doesn't return an identifier, return None. If anything goes wrong ... return None. The idgen module is not a REST API. It does not use API authentication. The user has to be logged in using the HTML login page, and the resulting authenticated session used for sending requests. """ identifier = None source_id = None with Requests(domain_name=requests.domain_name, base_url=requests.base_url, username=requests.username, password=requests.password, verify=requests.verify) as requests_session: authenticate_session(requests_session) try: source_id = get_identifier_source_id(requests_session, identifier_type) except OpenmrsHtmlUiChanged as err: notify_exception( request=None, message='Unexpected OpenMRS HTML UI', details=str(err), ) if source_id: # Example request: http://www.example.com/openmrs/module/idgen/generateIdentifier.form?source=1 response = requests_session.get( '/module/idgen/generateIdentifier.form', params={'source': source_id}) try: if not (200 <= response.status_code < 300 and response.content): raise OpenmrsException() try: # Example response: {"identifiers": ["CHR203007"]} identifier = response.json()['identifiers'][0] except (ValueError, IndexError, KeyError): raise OpenmrsException() except OpenmrsException: notify_exception( request=None, message= 'OpenMRS idgen module returned an unexpected response', details= 'OpenMRS idgen module at "{}" returned an unexpected response {}: "{}"' .format(response.url, response.status_code, response.content)) return identifier
def setUp(self): self.requests = Requests(TEST_DOMAIN, TEST_API_URL, TEST_API_USERNAME, TEST_API_PASSWORD) content = {'status': 'Created'} self.content_json = json.dumps(content) self.request_method = 'POST' self.request_headers = { 'Content-type': 'application/json', 'Accept': 'application/json' } self.status_code = 201 self.error_message = '' self.uri = 'person/' self.json_data = {'name': 'Alice'} self.data = json.dumps(self.json_data) self.response_mock = Mock() self.response_mock.status_code = self.status_code self.response_mock.content = self.content_json self.response_mock.json.return_value = content
def test_generate_identifier(): auth_manager = BasicAuthManager(USERNAME, PASSWORD) requests = Requests( DOMAIN, BASE_URL, verify=False, # demo.mybahmni.org uses a self-issued cert auth_manager=auth_manager, logger=dummy_logger, ) identifier = generate_identifier(requests, IDENTIFIER_TYPE) assert_regexp_matches(identifier, r'^BAH\d{6}$') # e.g. BAH203001
def test_with_session(self): with patch('corehq.motech.requests.RequestLog', Mock()), \ patch.object(requests.Session, 'request'), \ patch.object(requests.Session, 'close') as close_mock: with Requests(TEST_DOMAIN, TEST_API_URL, TEST_API_USERNAME, TEST_API_PASSWORD) as self.requests: self.requests.get('me') self.requests.get('me') self.requests.get('me') self.assertEqual(close_mock.call_count, 1)
def get_requests(self, payload_id, logger): from corehq.motech.requests import Requests return Requests( self.domain, self.url, self.username, self.plaintext_password, verify=not self.skip_cert_verify, notify_addresses=self.notify_addresses, payload_id=payload_id, logger=logger, auth_type=self.auth_type, )
def send_datasets(domain_name, send_now=False, send_date=None): """ Sends a data set of data values in the following format: { "dataSet": "dataSetID", "completeDate": "date", "period": "period", "orgUnit": "orgUnitID", "attributeOptionCombo", "aocID", "dataValues": [ { "dataElement": "dataElementID", "categoryOptionCombo": "cocID", "value": "1", "comment": "comment1" }, { "dataElement": "dataElementID", "categoryOptionCombo": "cocID", "value": "2", "comment": "comment2" }, { "dataElement": "dataElementID", "categoryOptionCombo": "cocID", "value": "3", "comment": "comment3" } ] } See DHIS2 API docs for more details: https://docs.dhis2.org/master/en/developer/html/webapi_data_values.html """ if not send_date: send_date = datetime.today() dhis2_conn = get_dhis2_connection(domain_name) dataset_maps = get_dataset_maps(domain_name) if not dhis2_conn or not dataset_maps: return # Nothing to do requests = Requests( domain_name, dhis2_conn.server_url, dhis2_conn.username, bz2.decompress(b64decode(dhis2_conn.password)), verify=not dhis2_conn.skip_cert_verify, ) endpoint = 'dataValueSets' for dataset_map in dataset_maps: if send_now or dataset_map.should_send_on_date(send_date): dataset = dataset_map.get_dataset(send_date) requests.post(endpoint, json=dataset)
def test_with_session(self): with patch.object(requests.Session, 'request'), \ patch.object(requests.Session, 'close') as close_mock: request = Requests(TEST_DOMAIN, TEST_API_URL, TEST_API_USERNAME, TEST_API_PASSWORD, logger=noop_logger) with request as req: req.get('me') req.get('me') req.get('me') self.assertEqual(close_mock.call_count, 1)
def test_connection(self): with patch.object(Requests, 'get') as request_get: request_get.side_effect = ConnectTimeout requests = Requests( TEST_DOMAIN, 'https://example.com/', auth_manager=self.no_auth, logger=lambda level, entry: None, ) importer = Importer() with self.assertRaises(ConnectTimeout): get_openmrs_patients(requests, importer) self.assertEqual(request_get.call_count, 1)
def send_datasets(domain_name, send_now=False, send_date=None): """ Sends a data set of data values in the following format: { "dataSet": "dataSetID", "completeDate": "date", "period": "period", "orgUnit": "orgUnitID", "attributeOptionCombo", "aocID", "dataValues": [ { "dataElement": "dataElementID", "categoryOptionCombo": "cocID", "value": "1", "comment": "comment1" }, { "dataElement": "dataElementID", "categoryOptionCombo": "cocID", "value": "2", "comment": "comment2" }, { "dataElement": "dataElementID", "categoryOptionCombo": "cocID", "value": "3", "comment": "comment3" } ] } See DHIS2 API docs for more details: https://docs.dhis2.org/master/en/developer/html/webapi_data_values.html """ if not send_date: send_date = datetime.today() dhis2_conn = get_dhis2_connection(domain_name) dataset_maps = get_dataset_maps(domain_name) if not dhis2_conn or not dataset_maps: return # Nothing to do requests = Requests( domain_name, dhis2_conn.server_url, dhis2_conn.username, bz2.decompress(b64decode(dhis2_conn.password)), ) endpoint = 'dataValueSets' for dataset_map in dataset_maps: if send_now or dataset_map.should_send_on_date(send_date): dataset = dataset_map.get_dataset(send_date) requests.post(endpoint, json=dataset)
def send_request(self, repeat_record, payload, verify=None): case_trigger_infos = get_relevant_case_updates_from_form_json( self.domain, payload, case_types=self.white_listed_case_types, extra_fields=[ identifier.case_property for identifier in self.openmrs_config.case_config.patient_identifiers.values() ]) form_question_values = get_form_question_values(payload) return send_openmrs_data( Requests(self.domain, self.url, self.username, self.password), self.domain, payload, self.openmrs_config, case_trigger_infos, form_question_values)
def import_patients_with_importer(importer_json): importer = OpenmrsImporter.wrap(importer_json) password = b64_aes_decrypt(importer.password) requests = Requests(importer.domain, importer.server_url, importer.username, password) if importer.location_type_name: try: location_type = LocationType.objects.get( domain=importer.domain, name=importer.location_type_name) except LocationType.DoesNotExist: logger.error( f'No organization level named "{importer.location_type_name}" ' f'found in project space "{importer.domain}".') return if importer.location_id: location = SQLLocation.objects.filter(domain=importer.domain).get( importer.location_id) locations = location.get_descendants.filter( location_type=location_type) else: locations = SQLLocation.objects.filter(domain=importer.domain, location_type=location_type) for location in locations: # Assign cases to the first user in the location, not to the location itself owner = get_one_commcare_user_at_location(importer.domain, location.location_id) if not owner: logger.error( f'Project space "{importer.domain}" at location ' f'"{location.name}" has no user to own cases imported ' f'from OpenMRS Importer "{importer}"') continue import_patients_of_owner(requests, importer, importer.domain, owner, location) elif importer.owner_id: try: owner = CommCareUser.get(importer.owner_id) except ResourceNotFound: logger.error( f'Project space "{importer.domain}" has no user to own cases ' f'imported from OpenMRS Importer "{importer}"') return import_patients_of_owner(requests, importer, importer.domain, owner) else: logger.error( f'Error importing patients for project space "{importer.domain}" from ' f'OpenMRS Importer "{importer}": Unable to determine the owner of ' 'imported cases without either owner_id or location_type_name')
def test_notify_error_address_list(self): requests = Requests( TEST_DOMAIN, TEST_API_URL, TEST_API_USERNAME, TEST_API_PASSWORD, notify_addresses=['*****@*****.**', '*****@*****.**']) with patch('corehq.motech.requests.send_mail_async') as send_mail_mock: requests.notify_error('foo') send_mail_mock.delay.assert_called_with( 'MOTECH Error', ('foo\r\n' 'Project space: test-domain\r\n' 'Remote API base URL: http://localhost:9080/api/\r\n' 'Remote API username: admin'), from_email=settings.DEFAULT_FROM_EMAIL, recipient_list=['*****@*****.**', '*****@*****.**'])
def get_requests( self, payload_id: Optional[str] = None, logger: Optional[Callable] = None, ): from corehq.motech.requests import Requests auth_manager = self.get_auth_manager() return Requests( self.domain, self.url, verify=not self.skip_cert_verify, auth_manager=auth_manager, notify_addresses=self.notify_addresses, payload_id=payload_id, logger=logger, )
def get_requests( repeater: Repeater, payload_id: Optional[str] = None, ) -> Requests: """ Returns a Requests object instantiated with properties of the given Repeater. ``payload_id`` specifies the payload that the object will be used for sending, if applicable. """ auth_manager = repeater.get_auth_manager() return Requests( repeater.domain, repeater.url, verify=repeater.verify, auth_manager=auth_manager, notify_addresses=repeater.notify_addresses, payload_id=payload_id, )
class RequestsTests(SimpleTestCase): def setUp(self): self.requests = Requests(TEST_DOMAIN, TEST_API_URL, TEST_API_USERNAME, TEST_API_PASSWORD) self.org_unit_id = 'abc' self.data_element_id = '123' def test_authentication(self): with patch('corehq.motech.requests.RequestLog', Mock()), \ patch('corehq.motech.requests.requests') as requests_mock: content = {'code': TEST_API_USERNAME} content_json = json.dumps(content) response_mock = Mock() response_mock.status_code = 200 response_mock.content = content_json response_mock.json.return_value = content requests_mock.get.return_value = response_mock response = self.requests.get('me') requests_mock.get.assert_called_with( TEST_API_URL + 'me', headers={'Accept': 'application/json'}, auth=(TEST_API_USERNAME, TEST_API_PASSWORD) ) self.assertEqual(response.status_code, 200) self.assertEqual(response.json()['code'], TEST_API_USERNAME) def test_send_data_value_set(self): with patch('corehq.motech.requests.RequestLog', Mock()), \ patch('corehq.motech.requests.requests') as requests_mock: payload = {'dataValues': [ {'dataElement': self.data_element_id, 'period': "201701", 'orgUnit': self.org_unit_id, 'value': "180"}, {'dataElement': self.data_element_id, 'period': "201702", 'orgUnit': self.org_unit_id, 'value': "200"}, ]} content = {'status': 'SUCCESS', 'importCount': {'imported': 2}} content_json = json.dumps(content) response_mock = Mock() response_mock.status_code = 201 response_mock.content = content_json response_mock.json.return_value = content requests_mock.post.return_value = response_mock response = self.requests.post('dataValueSets', json=payload) requests_mock.post.assert_called_with( 'http://localhost:9080/api/dataValueSets', json=payload, headers={'Content-type': 'application/json', 'Accept': 'application/json'}, auth=(TEST_API_USERNAME, TEST_API_PASSWORD) ) self.assertEqual(response.status_code, 201) self.assertEqual(response.json()['status'], 'SUCCESS') self.assertEqual(response.json()['importCount']['imported'], 2) def test_verify_ssl(self): with patch('corehq.motech.requests.RequestLog', Mock()), \ patch('corehq.motech.requests.requests') as requests_mock: requests = Requests(TEST_DOMAIN, TEST_API_URL, TEST_API_USERNAME, TEST_API_PASSWORD, verify=False) requests.get('me') requests_mock.get.assert_called_with( TEST_API_URL + 'me', headers={'Accept': 'application/json'}, auth=(TEST_API_USERNAME, TEST_API_PASSWORD), verify=False )
def setUp(self): self.requests = Requests(TEST_DOMAIN, TEST_API_URL, TEST_API_USERNAME, TEST_API_PASSWORD) self.org_unit_id = 'abc' self.data_element_id = '123'