Skip to content

Commit 4df6d6b

Browse files
authored
Ryan/enrichment search (#43)
* Add address search capability to us_enrichment_api - Add address fields (street, city, state, zipcode, freeform) to EnrichmentLookup - Use /lookup/search/{dataset} path when smarty_key is not provided - Add validation to require at least one address field for address searches - Add ValidationError variant to SmartyError - Add example demonstrating structured and freeform address search - Add tests for address search functionality * More settings.
1 parent 937e659 commit 4df6d6b

File tree

7 files changed

+236
-6
lines changed

7 files changed

+236
-6
lines changed

.claude/settings.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,9 @@
88
"Bash(make *)",
99
"Bash(cargo *)"
1010
]
11+
},
12+
"attribution": {
13+
"commit": "",
14+
"pr": ""
1115
}
1216
}

Makefile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ us_zipcode_api:
5353
us_enrichment_api:
5454
RUST_LOG=trace cargo run --example us_enrichment_api
5555

56+
us_enrichment_address_search_api:
57+
RUST_LOG=trace cargo run --example us_enrichment_address_search_api
58+
5659
examples: international_autocomplete_api international_postal_code_api international_street_api logger us_autocomplete_pro_api us_extract_api us_reverse_geo_api us_street_api us_zipcode_api
5760

58-
.PHONY: clean test dependencies package examples clippy international_autocomplete_api international_postal_code_api international_street_api logger us_autocomplete_pro_api us_extract_api us_reverse_geo_api us_street_api us_zipcode_api us_enrichment_api
61+
.PHONY: clean test dependencies package examples clippy international_autocomplete_api international_postal_code_api international_street_api logger us_autocomplete_pro_api us_extract_api us_reverse_geo_api us_street_api us_zipcode_api us_enrichment_api us_enrichment_address_search_api
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
use smarty_rust_sdk::sdk::authentication::BasicAuthCredential;
2+
use smarty_rust_sdk::sdk::options::OptionsBuilder;
3+
4+
use smarty_rust_sdk::us_enrichment_api::client::*;
5+
use smarty_rust_sdk::us_enrichment_api::lookup::EnrichmentLookup;
6+
use smarty_rust_sdk::us_enrichment_api::principal::*;
7+
8+
use std::error::Error;
9+
10+
#[tokio::main]
11+
async fn main() -> Result<(), Box<dyn Error>> {
12+
// Example using structured address fields
13+
structured_address_search().await?;
14+
15+
// Example using freeform address
16+
freeform_address_search().await?;
17+
18+
Ok(())
19+
}
20+
21+
async fn structured_address_search() -> Result<(), Box<dyn Error>> {
22+
println!("=== Structured Address Search ===");
23+
24+
let mut lookup = EnrichmentLookup::<PrincipalResponse> {
25+
street: "56 Union Ave".to_string(),
26+
city: "Somerville".to_string(),
27+
state: "NJ".to_string(),
28+
zipcode: "08876".to_string(),
29+
..Default::default()
30+
};
31+
32+
let authentication = BasicAuthCredential::new(
33+
std::env::var("SMARTY_AUTH_ID").expect("Missing SMARTY_AUTH_ID env variable"),
34+
std::env::var("SMARTY_AUTH_TOKEN").expect("Missing SMARTY_AUTH_TOKEN env variable"),
35+
);
36+
37+
let options = OptionsBuilder::new(Some(authentication))
38+
.with_logging()
39+
.build();
40+
41+
let client = USEnrichmentClient::new(options)?;
42+
43+
client.send(&mut lookup).await?;
44+
45+
println!("{}", serde_json::to_string_pretty(&lookup.results)?);
46+
47+
Ok(())
48+
}
49+
50+
async fn freeform_address_search() -> Result<(), Box<dyn Error>> {
51+
println!("\n=== Freeform Address Search ===");
52+
53+
let mut lookup = EnrichmentLookup::<PrincipalResponse> {
54+
freeform: "56 Union Ave, Somerville, NJ 08876".to_string(),
55+
..Default::default()
56+
};
57+
58+
let authentication = BasicAuthCredential::new(
59+
std::env::var("SMARTY_AUTH_ID").expect("Missing SMARTY_AUTH_ID env variable"),
60+
std::env::var("SMARTY_AUTH_TOKEN").expect("Missing SMARTY_AUTH_TOKEN env variable"),
61+
);
62+
63+
let options = OptionsBuilder::new(Some(authentication))
64+
.with_logging()
65+
.build();
66+
67+
let client = USEnrichmentClient::new(options)?;
68+
69+
client.send(&mut lookup).await?;
70+
71+
println!("{}", serde_json::to_string_pretty(&lookup.results)?);
72+
73+
Ok(())
74+
}

smarty-rust-sdk/src/sdk/error.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,6 @@ pub enum SmartyError {
1212
Parse(#[from] url::ParseError),
1313
#[error("http error")]
1414
HttpError { code: StatusCode, detail: String },
15+
#[error("validation error: {0}")]
16+
ValidationError(String),
1517
}

smarty-rust-sdk/src/us_enrichment_api/client.rs

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::borrow::Cow;
2+
13
use crate::sdk::client::Client;
24
use crate::sdk::error::SmartyError;
35
use crate::sdk::options::Options;
@@ -26,12 +28,23 @@ impl USEnrichmentClient {
2628
&self,
2729
lookup: &mut EnrichmentLookup<R>,
2830
) -> Result<(), SmartyError> {
31+
// Validate that address search has at least one address field
32+
if lookup.is_address_search() && !lookup.has_address_fields() {
33+
return Err(SmartyError::ValidationError(
34+
"address search requires at least one address field (street, city, state, zipcode, or freeform)".to_string()
35+
));
36+
}
37+
2938
let mut url = self.client.url.clone();
30-
url = url.join(&format!(
31-
"/lookup/{}/{}",
32-
lookup.smarty_key,
33-
R::lookup_type()
34-
))?;
39+
40+
// Use "search" path for address-based lookups, smarty_key for key-based lookups
41+
let key_or_search: Cow<str> = if lookup.is_address_search() {
42+
"search".into()
43+
} else {
44+
lookup.smarty_key.to_string().into()
45+
};
46+
47+
url = url.join(&format!("/lookup/{}/{}", key_or_search, R::lookup_type()))?;
3548

3649
let mut req = self.client.reqwest_client.request(Method::GET, url);
3750

smarty-rust-sdk/src/us_enrichment_api/lookup.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@ pub struct EnrichmentLookup<R: EnrichmentResponse> {
99
pub etag: String,
1010
pub features: String,
1111
pub results: Vec<R>,
12+
13+
// Address search fields (used when smarty_key is not provided)
14+
pub street: String,
15+
pub city: String,
16+
pub state: String,
17+
pub zipcode: String,
18+
pub freeform: String,
1219
}
1320

1421
impl<R: EnrichmentResponse> EnrichmentLookup<R> {
@@ -21,9 +28,28 @@ impl<R: EnrichmentResponse> EnrichmentLookup<R> {
2128
has_param("include".to_string(), self.include),
2229
has_param("exclude".to_string(), self.exclude),
2330
has_param("features".to_string(), self.features),
31+
has_param("street".to_string(), self.street),
32+
has_param("city".to_string(), self.city),
33+
has_param("state".to_string(), self.state),
34+
has_param("zipcode".to_string(), self.zipcode),
35+
has_param("freeform".to_string(), self.freeform),
2436
]
2537
.into_iter()
2638
.flatten()
2739
.collect()
2840
}
41+
42+
/// Returns true if this is an address search lookup (no smarty_key provided)
43+
pub fn is_address_search(&self) -> bool {
44+
self.smarty_key == 0
45+
}
46+
47+
/// Returns true if any address fields are populated
48+
pub fn has_address_fields(&self) -> bool {
49+
!self.street.is_empty()
50+
|| !self.city.is_empty()
51+
|| !self.state.is_empty()
52+
|| !self.zipcode.is_empty()
53+
|| !self.freeform.is_empty()
54+
}
2955
}

smarty-rust-sdk/src/us_enrichment_api/mod.rs

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,112 @@ mod tests {
5555

5656
assert_eq!(lookup.into_param_array(), expected_params);
5757
}
58+
59+
#[test]
60+
fn address_search_lookup_test() {
61+
let lookup: EnrichmentLookup<PrincipalResponse> = EnrichmentLookup {
62+
street: "123 Main St".to_string(),
63+
city: "Phoenix".to_string(),
64+
state: "AZ".to_string(),
65+
zipcode: "85001".to_string(),
66+
..Default::default()
67+
};
68+
69+
assert!(lookup.is_address_search());
70+
71+
let expected_params = vec![
72+
("street".to_string(), "123 Main St".to_string()),
73+
("city".to_string(), "Phoenix".to_string()),
74+
("state".to_string(), "AZ".to_string()),
75+
("zipcode".to_string(), "85001".to_string()),
76+
];
77+
78+
assert_eq!(lookup.into_param_array(), expected_params);
79+
}
80+
81+
#[test]
82+
fn freeform_address_search_test() {
83+
let lookup: EnrichmentLookup<PrincipalResponse> = EnrichmentLookup {
84+
freeform: "123 Main St, Phoenix, AZ 85001".to_string(),
85+
..Default::default()
86+
};
87+
88+
assert!(lookup.is_address_search());
89+
90+
let expected_params = vec![
91+
("freeform".to_string(), "123 Main St, Phoenix, AZ 85001".to_string()),
92+
];
93+
94+
assert_eq!(lookup.into_param_array(), expected_params);
95+
}
96+
97+
#[test]
98+
fn smarty_key_lookup_is_not_address_search() {
99+
let lookup: EnrichmentLookup<PrincipalResponse> = EnrichmentLookup {
100+
smarty_key: 123456789,
101+
..Default::default()
102+
};
103+
104+
assert!(!lookup.is_address_search());
105+
}
106+
107+
#[test]
108+
fn has_address_fields_returns_true_when_any_field_set() {
109+
let lookup_street: EnrichmentLookup<PrincipalResponse> = EnrichmentLookup {
110+
street: "123 Main St".to_string(),
111+
..Default::default()
112+
};
113+
assert!(lookup_street.has_address_fields());
114+
115+
let lookup_city: EnrichmentLookup<PrincipalResponse> = EnrichmentLookup {
116+
city: "Phoenix".to_string(),
117+
..Default::default()
118+
};
119+
assert!(lookup_city.has_address_fields());
120+
121+
let lookup_state: EnrichmentLookup<PrincipalResponse> = EnrichmentLookup {
122+
state: "AZ".to_string(),
123+
..Default::default()
124+
};
125+
assert!(lookup_state.has_address_fields());
126+
127+
let lookup_zipcode: EnrichmentLookup<PrincipalResponse> = EnrichmentLookup {
128+
zipcode: "85001".to_string(),
129+
..Default::default()
130+
};
131+
assert!(lookup_zipcode.has_address_fields());
132+
133+
let lookup_freeform: EnrichmentLookup<PrincipalResponse> = EnrichmentLookup {
134+
freeform: "123 Main St, Phoenix, AZ".to_string(),
135+
..Default::default()
136+
};
137+
assert!(lookup_freeform.has_address_fields());
138+
}
139+
140+
#[test]
141+
fn has_address_fields_returns_false_when_no_fields_set() {
142+
let lookup: EnrichmentLookup<PrincipalResponse> = EnrichmentLookup {
143+
smarty_key: 123456789,
144+
..Default::default()
145+
};
146+
assert!(!lookup.has_address_fields());
147+
148+
let empty_lookup: EnrichmentLookup<PrincipalResponse> = EnrichmentLookup::default();
149+
assert!(!empty_lookup.has_address_fields());
150+
}
151+
152+
#[test]
153+
fn smarty_key_takes_precedence_over_address_fields() {
154+
// When both smarty_key and address fields are provided, smarty_key lookup is used
155+
let lookup: EnrichmentLookup<PrincipalResponse> = EnrichmentLookup {
156+
smarty_key: 123456789,
157+
street: "123 Main St".to_string(),
158+
city: "Phoenix".to_string(),
159+
..Default::default()
160+
};
161+
162+
assert!(!lookup.is_address_search());
163+
assert!(lookup.has_address_fields());
164+
}
165+
58166
}

0 commit comments

Comments
 (0)