Skip to content

Commit 37f7f15

Browse files
Add geo information to prebid openrtb requests (#101)
1 parent 4ae1152 commit 37f7f15

File tree

2 files changed

+84
-3
lines changed

2 files changed

+84
-3
lines changed

crates/common/src/geo.rs

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,76 @@ use crate::constants::{
1111
HEADER_X_GEO_INFO_AVAILABLE, HEADER_X_GEO_METRO_CODE,
1212
};
1313

14+
/// Geographic information extracted from a request.
15+
///
16+
/// Contains all available geographic data from Fastly's geolocation service,
17+
/// including city, country, continent, coordinates, and DMA/metro code.
18+
#[derive(Debug, Clone, serde::Serialize)]
19+
pub struct GeoInfo {
20+
/// City name
21+
pub city: String,
22+
/// Two-letter country code (e.g., "US", "GB")
23+
pub country: String,
24+
/// Continent name
25+
pub continent: String,
26+
/// Latitude coordinate
27+
pub latitude: f64,
28+
/// Longitude coordinate
29+
pub longitude: f64,
30+
/// DMA (Designated Market Area) / metro code
31+
pub metro_code: i64,
32+
/// Region code
33+
pub region: Option<String>,
34+
}
35+
36+
impl GeoInfo {
37+
/// Creates a new `GeoInfo` from a request by performing a geo lookup.
38+
///
39+
/// This constructor performs a geo lookup based on the client's IP address and returns
40+
/// all available geographic data in a structured format. It does not modify the request
41+
/// or set headers.
42+
///
43+
/// # Arguments
44+
///
45+
/// * `req` - The request to extract geographic information from
46+
///
47+
/// # Returns
48+
///
49+
/// `Some(GeoInfo)` if geo data is available, `None` otherwise
50+
///
51+
/// # Example
52+
///
53+
/// ```ignore
54+
/// if let Some(geo_info) = GeoInfo::from_request(&req) {
55+
/// println!("User is in {} ({})", geo_info.city, geo_info.country);
56+
/// println!("Coordinates: {}", geo_info.coordinates_string());
57+
/// }
58+
/// ```
59+
pub fn from_request(req: &Request) -> Option<Self> {
60+
req.get_client_ip_addr()
61+
.and_then(geo_lookup)
62+
.map(|geo| GeoInfo {
63+
city: geo.city().to_string(),
64+
country: geo.country_code().to_string(),
65+
continent: format!("{:?}", geo.continent()),
66+
latitude: geo.latitude(),
67+
longitude: geo.longitude(),
68+
metro_code: geo.metro_code(),
69+
region: geo.region().map(str::to_string),
70+
})
71+
}
72+
73+
/// Returns coordinates as a formatted string "latitude,longitude"
74+
pub fn coordinates_string(&self) -> String {
75+
format!("{},{}", self.latitude, self.longitude)
76+
}
77+
78+
/// Checks if a valid metro code is available (non-zero)
79+
pub fn has_metro_code(&self) -> bool {
80+
self.metro_code > 0
81+
}
82+
}
83+
1484
/// Extracts the DMA (Designated Market Area) code from the request's geolocation data.
1585
///
1686
/// This function:

crates/common/src/prebid_proxy.rs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use serde_json::{json, Value};
1212
use crate::backend::ensure_backend_from_url;
1313
use crate::constants::{HEADER_SYNTHETIC_FRESH, HEADER_SYNTHETIC_TRUSTED_SERVER};
1414
use crate::error::TrustedServerError;
15+
use crate::geo::GeoInfo;
1516
use crate::settings::Settings;
1617
use crate::synthetic::{generate_synthetic_id, get_or_generate_synthetic_id};
1718

@@ -162,9 +163,19 @@ fn enhance_openrtb_request(
162163
}
163164

164165
// Add geo information if available
165-
// Note: get_dma_code requires a mutable reference, but we only have immutable
166-
// For now, we'll skip DMA code in the enhancement
167-
// TODO: Refactor get_dma_code to accept immutable reference
166+
if let Some(geo_info) = GeoInfo::from_request(req) {
167+
let geo_obj = json!({
168+
"type": 2, // 2 = IP address location
169+
"country": geo_info.country, // Note: OpenRTB expects ISO 3166-1 alpha-3, but Fastly provides alpha-2
170+
"city": geo_info.city,
171+
"region": geo_info.region,
172+
});
173+
174+
if !request["device"].is_object() {
175+
request["device"] = json!({});
176+
}
177+
request["device"]["geo"] = geo_obj.clone();
178+
}
168179

169180
// Add site information if missing
170181
if !request["site"].is_object() {

0 commit comments

Comments
 (0)