def test_missing_elements(self): req_parser = GeoproxyRequestParser(None, None) out = req_parser.find_missing_elements([1, 2, 3], [2]) self.assertEqual(out, [1, 3]) out = req_parser.find_missing_elements([1, 2, 3], [1, 2, 3]) self.assertEqual(out, []) out = req_parser.find_missing_elements([1, 2, 3], [1, 2, 3, 4]) self.assertEqual(out, [])
def test_bad_parse(self): invalid = {"service": ["google"], "bounds": ["1.0,2.0|3.0,4.0"]} mock_handler = MockRequestHandler(invalid) response = GeoproxyResponse() mock_services = {"google": None, "here": None} req_parser = GeoproxyRequestParser(mock_services, response) out = req_parser.parse(mock_handler) # missing address self.assertFalse(out) self.assertEqual(response.status, "INVALID_REQUEST")
def test_bad_parse2(self): invalid = {"address": ["Addr"], "service": ["blah"], "bounds": ["1.0,2.0|3.0,4.0"]} mock_handler = MockRequestHandler(invalid) response = GeoproxyResponse() mock_services = {"google": None, "here": None} req_parser = GeoproxyRequestParser(mock_services, response) out = req_parser.parse(mock_handler) self.assertTrue(out) # invalid primary service self.assertTrue(all(elem in req_parser.services for elem in ["google", "here"])) self.assertEqual(response.query, "Addr")
def test_here_parse(self): valid = {"address": ["Addr"], "service": ["here"], "bounds": ["1.0,2.0|3.0,4.0"]} mock_handler = MockRequestHandler(valid) response = GeoproxyResponse() mock_services = {"google": None, "here": None} req_parser = GeoproxyRequestParser(mock_services, response) out = req_parser.parse(mock_handler) self.assertTrue(out) self.assertEqual(req_parser.address, "Addr") self.assertEqual(req_parser.services, ["here", "google"]) self.assertEqual(req_parser.bounds.top_left.latitude, 3.0)
def test_request_parser_constructor(self): response = GeoproxyResponse() mock_services = {"google": None, "here": None} req_parser = GeoproxyRequestParser(mock_services, response) self.assertEqual(len(req_parser.services), 0) self.assertEqual(req_parser.available_services, mock_services) self.assertEqual(req_parser.geo_proxy_response, response)
def test_parse_bounding_coordinates(self): req_parser = GeoproxyRequestParser(None, None) # way wrong self.assertIsNone(req_parser.parse_bounding_coordinates("Bad")) # not enough coords self.assertIsNone(req_parser.parse_bounding_coordinates("1.0,2.0")) # too many coords self.assertIsNone(req_parser.parse_bounding_coordinates("1.0,2.0|1.0,2.0|1.0,2.0")) # wrong delim self.assertIsNone(req_parser.parse_bounding_coordinates("1.0,2.0;1.0,2.0")) # wrong num lat/long self.assertIsNone(req_parser.parse_bounding_coordinates("1.0,2.0,3.0|1.0,2.0")) # right out = req_parser.parse_bounding_coordinates("1.0,2.0|3.0,4.0") self.assertIsNotNone(out) self.assertEqual(len(out), 4) self.assertEqual(out[1], 2.0)
def get(self): """Request handler for method=GET Responsible for spawning third party geocoder query tasks based on parsed request data Pseudo code: - Create empty response - Parse incoming request - If parse success: - For each third party service: - Build third party service query from incoming request data - Spawn query task and wait on future for third party response - Parse third party response - If success: - Set response result - Break - Next service in loop - Else: - Set response error - Send response """ start_time = time.time() # Create an empty API response geo_proxy_response = GeoproxyResponse() try: # Next, parse the inputs from the RESTful query and ensure they are all valid geo_proxy_request = GeoproxyRequestParser(self.available_services, geo_proxy_response) # if our request parse succeeds, we have valid input data and can proceed if geo_proxy_request.parse(self): self.logger.info( "Incoming request:\n{}".format(geo_proxy_request)) # iterate through each service in request.services until we get a successful result for service in geo_proxy_request.services: self.logger.info( "Querying third-party service: {}".format(service)) # Grab the third party helper object, associated with the service # The helper assists with third party query construction and parsing service_helper = self.available_services[service] # build the third party query based on our request inputs service_helper.build_query(geo_proxy_request.address, geo_proxy_request.bounds) # run the query and yield the response response_json = yield self.query_third_party_geocoder( service_helper.query) if response_json: # if we got a valid response from the third party query, parse it! parse_success = service_helper.parser.parse( response_json) # fragile detection if there was a valid response, but zero results if parse_success == 0: geo_proxy_response.set_error( "Zero results", "ZERO_RESULTS") # otherwise assume the parse was successful, and we extracted data # package it into our response object to be sent out. elif parse_success is not None: geo_proxy_response.error = None geo_proxy_response.set_result( service, service_helper.parser.latitude, service_helper.parser.longitude, service_helper.parser.address) # if we get a valid result, don't keep querying the other third # party services # NOTE: Making an assumption that we are only returning results from the # first valid third party service break # if we had an error with both service requests, but no error has been set, do it now # this handles cases like wrong API keys, offline services, etc. if not geo_proxy_response.status == "OK" and geo_proxy_response.error is None: geo_proxy_response.set_error( "Error in third-party API requests", "UNKNOWN_ERROR") except Exception as e: geo_proxy_response.set_error( "Caught general exception in server: {}".format(e), "UNKNOWN_ERROR") # Ensure that a response is always sent so the socket doesn't bind self.write(geo_proxy_response.to_json()) self.logger.info( "Response completed in {:0.2f} seconds".format(time.time() - start_time))