def test_valid_geo_request_context_no_ip_address(self): expected = Geo( **{ "city": "SAN FRANCISCO", "country_code": "US", "latitude": 37.75, "longitude": -122.4, "state_code": "CA" }) artifact = deepcopy(ARTIFACT_BLANK) artifact["geoTargetingEnabled"] = True geo_provider = GeoProvider(self.config, artifact) with patch.object( geo_provider.pool_manager, "request", return_value=self.mock_geo_response) as mock_http_call: geo_input = Geo(ip_address=None) result = geo_provider.valid_geo_request_context(geo_input) time.sleep(1) self.assertEqual(result, expected) self.assertEqual(mock_http_call.call_count, 1) self.assertEqual(mock_http_call.call_args[0][1], "https://assets.adobetarget.com/v1/geo") self.assertIsNone(mock_http_call.call_args[1].get("headers").get( HTTP_HEADER_FORWARDED_FOR))
def valid_geo_request_context(self, geo_request_context=None): """ :param geo_request_context: (delivery_api_client.Model.geo.Geo) geo object :return: (delivery_api_client.Model.geo.Geo) geo object """ if not geo_request_context: geo_request_context = Geo() validated_geo_request_context = deepcopy(geo_request_context) # When ipAddress is the only geo value passed in to getOffers(), do IP-to-Geo lookup. geo_lookup_path = get_geo_lookup_path(self.config) if self.geo_targeting_enabled and is_missing_geo_fields( geo_request_context): headers = {} if geo_request_context.ip_address: headers[ HTTP_HEADER_FORWARDED_FOR] = geo_request_context.ip_address try: response = self._request_geo(geo_lookup_path, headers) return self.geo_response_handler( response, validated_geo_request_context) except Exception as err: self.logger.error( "Exception while fetching geo data at: {} - error: {}". format(geo_lookup_path, (str(err)))) return None return validated_geo_request_context
def test_valid_geo_request_context_not_missing_geo_fields(self): expected = Geo(city="Las Vegas") artifact = deepcopy(ARTIFACT_BLANK) artifact["geoTargetingEnabled"] = True geo_provider = GeoProvider(self.config, artifact) with patch.object( geo_provider.pool_manager, "request", return_value=self.mock_geo_response) as mock_http_call: geo_input = Geo(ip_address=None, city="Las Vegas") result = geo_provider.valid_geo_request_context(geo_input) time.sleep(1) self.assertEqual(result, expected) self.assertEqual(mock_http_call.call_count, 0)
def test_create_or_update_geo_object_merge_with_existing(self): existing_geo = Geo(latitude=37.773972, longitude=-122.431297) headers = { "x-geo-country-code": "US", "x-geo-region-code": "CA", "x-geo-city": "SANFRANCISCO" } result = create_or_update_geo_object(geo_data=headers, existing_geo_context=existing_geo) expected = Geo( **{ "latitude": 37.773972, "longitude": -122.431297, "city": "SANFRANCISCO", "country_code": "US", "state_code": "CA" }) self.assertEqual(result, expected)
def test_create_or_update_geo_object_no_existing(self): result = create_or_update_geo_object(geo_data=self.headers) expected = Geo( **{ "latitude": 37.773972, "longitude": -122.431297, "city": "SANFRANCISCO", "country_code": "US", "state_code": "CA" }) self.assertEqual(result, expected)
def create_geo(geo): """Create new Geo""" if not geo: return None return Geo(ip_address=geo.get("ipAddress"), latitude=geo.get("latitude"), longitude=geo.get("longitude"), country_code=geo.get("countryCode"), state_code=geo.get("stateCode"), city=geo.get("city"), zip=geo.get("zip"))
def valid_delivery_request(request, target_location_hint, valid_geo_request_context): """ :param request: (delivery_api_client.Model.delivery_request.DeliveryRequest) request :param target_location_hint: (str) Target location hint :param valid_geo_request_context: (callable) function that checks if request geo is valid :return: (delivery_api_client.Model.delivery_request.DeliveryRequest) updated copy of request """ request_copy = deepcopy(request) context = request_copy.context or Context() context.geo = valid_geo_request_context(context.geo or Geo()) request_copy.context = context request_copy.id = valid_visitor_id(request_copy.id, target_location_hint) request_copy.request_id = request_copy.request_id or create_uuid() return request_copy
def test_valid_geo_request_context_geo_targeting_disabled(self): artifact = deepcopy(ARTIFACT_BLANK) artifact["geoTargetingEnabled"] = False geo_provider = GeoProvider(self.config, artifact) with patch.object( geo_provider.pool_manager, "request", return_value=self.mock_geo_response) as mock_http_call: geo_input = Geo(ip_address="12.21.1.40") result = geo_provider.valid_geo_request_context(geo_input) time.sleep(1) self.assertEqual(result, geo_input) self.assertEqual(mock_http_call.call_count, 0)
def test_geo_invalid_ip_address(self): with self.assertRaises(ValueError) as err: Geo(ip_address="277.0.0.1") self.assertEqual( str(err.exception), r"""Invalid value for `ip_address`, must be a follow pattern or equal to `/((^\s*((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9] |25[0-5]))\s*$)|(^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4} |((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((: [0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([ 0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.( 25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f] {1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f] {1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25 [0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f] {1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa- f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9] ?\d)){3}))|:)))(%.+)?\s*$))/`""")
def test_valid_geo_request_non_200_response(self): artifact = deepcopy(ARTIFACT_BLANK) artifact["geoTargetingEnabled"] = True geo_provider = GeoProvider(self.config, artifact) mock_bad_response = HTTPResponse(body="Bad Request", status=BAD_REQUEST) with patch.object(geo_provider.pool_manager, "request", return_value=mock_bad_response) as mock_http_call: geo_input = Geo(ip_address="12.21.1.40") result = geo_provider.valid_geo_request_context(geo_input) time.sleep(1) self.assertEqual(result, None) self.assertEqual(mock_http_call.call_count, 1) self.assertEqual(mock_http_call.call_args[0][1], "https://assets.adobetarget.com/v1/geo") self.assertEqual( mock_http_call.call_args[1].get("headers").get( HTTP_HEADER_FORWARDED_FOR), "12.21.1.40")
def _map_geo_values(value_fn, initial=None): """ :param value_fn: (callable) function to lookup value by key :param initial: (delivery_api_client.Model.geo.Geo) initial geo object to start reduce with :return: (delivery_api_client.Model.geo.Geo) geo object """ if not initial: initial = Geo() def geo_mapping_accumulator(result, geo_mapping): """ :param result: (delivery_api_client.Model.geo.Geo) geo object :param geo_mapping: (dict) mapping object for handling specific geo fields :return: (delivery_api_client.Model.geo.Geo) updated result """ value = value_fn(geo_mapping.get("header_name")) if value: value = geo_mapping.get("parse_value")(value) if geo_mapping.get( "parse_value") else value setattr(result, geo_mapping.get("value_key"), value) return result return reduce(geo_mapping_accumulator, GEO_MAPPINGS, initial)
def test_create_or_update_geo_object_empty_geo_data(self): existing_geo = Geo(latitude=37.773972, longitude=-122.431297) result = create_or_update_geo_object(geo_data=None, existing_geo_context=existing_geo) expected = Geo(**{"latitude": 37.773972, "longitude": -122.431297}) self.assertEqual(result, expected)
def test_create_or_update_geo_object_no_existing_and_empty_geo_data(self): result = create_or_update_geo_object() expected = Geo() self.assertEqual(result, expected)