Skip to content

Commit 26a4425

Browse files
committed
Add US Enrichment Business API with ETag support
Ports the US Business Enrichment API from smartystreets-dotnet-sdk 13.0.0 and fills in example coverage for the rest of the Enrichment client. - New Business.Summary endpoint (send_business_lookup) accepting either a smartykey or address components, returning BusinessSummaryResponse with a list of BusinessEntry records. - New Business.Detail endpoint (send_business_detail_lookup) hitting /business/{business_id} and returning a scalar BusinessDetailResponse with ~153 attribute fields. - First-class ETag round trip: set request_etag on the Lookup to send an Etag request header; 200 responses capture the server Etag into response_etag; 304 responses raise NotModifiedError carrying the refreshed Etag. Case-insensitive header extraction via a new Response.find_header helper. - Extract LookupBase so Lookup and BusinessDetailLookup share include/exclude/custom-parameter/etag state, matching the dotnet EnrichmentLookupBase split. - Tighten validation: reject whitespace-only smartykey/street/freeform on the standard Lookup and whitespace-only business_id on BusinessDetailLookup. - Refactor the Enrichment client for idiomatic style: collapse the duplicated send_lookup/send_business_lookup bodies into one function parameterized by response class, and consolidate build_request into decomposed helpers (_url_components, _address_parameters, _common_parameters, _apply_etag_header) so a Request is populated in a single pass instead of being mutated across build_request and a follow-up _apply_common_request_fields call. - Add examples for every public send_* method on the Enrichment client: business, etag, geo-reference, risk, secondary, secondary-count, and the generic dataset/data_subset pass-through.
1 parent 6d97388 commit 26a4425

23 files changed

Lines changed: 1292 additions & 95 deletions
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import os
2+
3+
from smartystreets_python_sdk import BasicAuthCredentials, ClientBuilder
4+
5+
6+
def run():
7+
auth_id = os.environ['SMARTY_AUTH_ID']
8+
auth_token = os.environ['SMARTY_AUTH_TOKEN']
9+
10+
credentials = BasicAuthCredentials(auth_id, auth_token)
11+
client = ClientBuilder(credentials).build_us_enrichment_api_client()
12+
13+
smarty_key = "1962995076"
14+
15+
try:
16+
summary_results = client.send_business_lookup(smarty_key)
17+
except Exception as err:
18+
print(err)
19+
return
20+
21+
if not summary_results:
22+
print("No response returned for SmartyKey {}".format(smarty_key))
23+
return
24+
25+
summary = summary_results[0]
26+
if not summary.businesses:
27+
print("SmartyKey {} has no business tenants".format(smarty_key))
28+
return
29+
30+
print("Summary results for SmartyKey: {}".format(smarty_key))
31+
for biz in summary.businesses:
32+
print(" - {} (ID: {})".format(biz.company_name, biz.business_id))
33+
34+
first = summary.businesses[0]
35+
print("\nFetching details for business: {} (ID: {})".format(first.company_name, first.business_id))
36+
37+
try:
38+
detail_result = client.send_business_detail_lookup(first.business_id)
39+
except Exception as err:
40+
print(err)
41+
return
42+
43+
if detail_result is None:
44+
print("\nNo detail result returned")
45+
return
46+
47+
print("\nDetail result:")
48+
print_result(detail_result)
49+
50+
51+
def print_result(obj):
52+
for key, value in vars(obj).items():
53+
if value is None:
54+
continue
55+
if key == 'attributes':
56+
print_result(value)
57+
continue
58+
print("{}: {}".format(key, value))
59+
print()
60+
61+
62+
if __name__ == "__main__":
63+
run()
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
import os
2+
3+
from smartystreets_python_sdk import BasicAuthCredentials, ClientBuilder
4+
from smartystreets_python_sdk.exceptions import NotModifiedError
5+
from smartystreets_python_sdk.us_enrichment import BusinessDetailLookup, BusinessLookup
6+
7+
8+
def run():
9+
auth_id = os.environ['SMARTY_AUTH_ID']
10+
auth_token = os.environ['SMARTY_AUTH_TOKEN']
11+
12+
credentials = BasicAuthCredentials(auth_id, auth_token)
13+
client = ClientBuilder(credentials).build_us_enrichment_api_client()
14+
15+
smarty_key = "1962995076"
16+
17+
business_id = exercise_summary_etag(client, smarty_key)
18+
if business_id is None:
19+
return
20+
21+
exercise_detail_etag(client, business_id)
22+
23+
24+
def exercise_summary_etag(client, smarty_key):
25+
print("=== Business.Summary ETag round trip ===")
26+
27+
first = BusinessLookup(smarty_key)
28+
try:
29+
client.send_business_lookup(first)
30+
except Exception as ex:
31+
print(" Initial Summary call failed: {}".format(ex))
32+
return None
33+
34+
initial_results = first.result
35+
captured_etag = first.response_etag
36+
print(" Call 1 (no Etag): captured Etag={}, results={}".format(
37+
display(captured_etag), len(initial_results or [])))
38+
39+
if not captured_etag:
40+
print(" Server did not return an Etag header; skipping conditional calls.")
41+
return first_business_id(initial_results)
42+
43+
second = BusinessLookup(smarty_key)
44+
second.request_etag = captured_etag
45+
try:
46+
client.send_business_lookup(second)
47+
print(" Call 2 (matching Etag): 200 — server did NOT honor the conditional. Results={}, Etag={}".format(
48+
len(second.result or []), display(second.response_etag)))
49+
except NotModifiedError as ex:
50+
print(" Call 2 (matching Etag): 304 NotModifiedError — caller treats this as cache-valid. Refreshed Etag={}".format(
51+
display(ex.response_etag)))
52+
except Exception as ex:
53+
print(" Call 2 unexpected failure: {}: {}".format(type(ex).__name__, ex))
54+
return None
55+
56+
third = BusinessLookup(smarty_key)
57+
third.request_etag = captured_etag + "X"
58+
try:
59+
client.send_business_lookup(third)
60+
print(" Call 3 (mutated Etag): 200 as expected. Results={}, Etag={}".format(
61+
len(third.result or []), display(third.response_etag)))
62+
except NotModifiedError:
63+
print(" Call 3 (mutated Etag): 304 — UNEXPECTED. Server treated a different Etag as matching.")
64+
except Exception as ex:
65+
print(" Call 3 unexpected failure: {}: {}".format(type(ex).__name__, ex))
66+
67+
return first_business_id(initial_results)
68+
69+
70+
def exercise_detail_etag(client, business_id):
71+
print()
72+
print("=== Business.Detail ETag round trip (businessId: {}) ===".format(business_id))
73+
74+
first = BusinessDetailLookup(business_id)
75+
try:
76+
client.send_business_detail_lookup(first)
77+
except Exception as ex:
78+
print(" Initial Detail call failed: {}".format(ex))
79+
return
80+
81+
initial = first.result
82+
captured_etag = first.response_etag
83+
initial_id = initial.business_id if initial is not None else "<null>"
84+
print(" Call 1 (no Etag): captured Etag={}, businessId={}".format(
85+
display(captured_etag), initial_id))
86+
87+
if not captured_etag:
88+
print(" Server did not return an Etag header; skipping conditional calls.")
89+
return
90+
91+
second = BusinessDetailLookup(business_id)
92+
second.request_etag = captured_etag
93+
try:
94+
client.send_business_detail_lookup(second)
95+
second_id = second.result.business_id if second.result is not None else "<null>"
96+
print(" Call 2 (matching Etag): 200 — server did NOT honor the conditional. businessId={}, Etag={}".format(
97+
second_id, display(second.response_etag)))
98+
except NotModifiedError as ex:
99+
print(" Call 2 (matching Etag): 304 NotModifiedError — caller treats this as cache-valid. Refreshed Etag={}".format(
100+
display(ex.response_etag)))
101+
except Exception as ex:
102+
print(" Call 2 unexpected failure: {}: {}".format(type(ex).__name__, ex))
103+
return
104+
105+
third = BusinessDetailLookup(business_id)
106+
third.request_etag = captured_etag + "X"
107+
try:
108+
client.send_business_detail_lookup(third)
109+
third_id = third.result.business_id if third.result is not None else "<null>"
110+
print(" Call 3 (mutated Etag): 200 as expected. businessId={}, Etag={}".format(
111+
third_id, display(third.response_etag)))
112+
except NotModifiedError:
113+
print(" Call 3 (mutated Etag): 304 — UNEXPECTED. Server treated a different Etag as matching.")
114+
except Exception as ex:
115+
print(" Call 3 unexpected failure: {}: {}".format(type(ex).__name__, ex))
116+
117+
118+
def first_business_id(results):
119+
if not results:
120+
return None
121+
businesses = results[0].businesses
122+
if not businesses:
123+
return None
124+
return businesses[0].business_id
125+
126+
127+
def display(s):
128+
return "<none>" if not s else s
129+
130+
131+
if __name__ == "__main__":
132+
run()
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import os
2+
3+
from smartystreets_python_sdk import BasicAuthCredentials, ClientBuilder
4+
from smartystreets_python_sdk.us_enrichment.lookup import Lookup as EnrichmentLookup
5+
6+
7+
def run():
8+
# We recommend storing your secret keys in environment variables instead---it's safer!
9+
auth_id = os.environ['SMARTY_AUTH_ID']
10+
auth_token = os.environ['SMARTY_AUTH_TOKEN']
11+
12+
credentials = BasicAuthCredentials(auth_id, auth_token)
13+
client = ClientBuilder(credentials).build_us_enrichment_api_client()
14+
15+
# send_generic_lookup routes a Lookup to any dataset/dataSubset that the
16+
# Enrichment API exposes, including new endpoints that may not yet have a
17+
# dedicated helper on the client.
18+
smarty_key = "87844267"
19+
dataset = "property"
20+
data_subset = "principal"
21+
22+
lookup = EnrichmentLookup()
23+
freeform_lookup = EnrichmentLookup()
24+
25+
lookup.smartykey = smarty_key
26+
# lookup.street = "1708 Watson Way"
27+
# lookup.city = "Vineyard"
28+
# lookup.state = "UT"
29+
# lookup.zipcode = "84059"
30+
lookup.features = "financial"
31+
32+
# Uncomment to narrow the response with include/exclude attributes
33+
# lookup.add_include_attribute('assessed_value')
34+
# lookup.add_exclude_attribute('tax_billed_amount')
35+
36+
freeform_lookup.freeform = "1708 Watson Way Vineyard UT 84059"
37+
38+
try:
39+
# Send a lookup by SmartyKey alone
40+
# results = client.send_generic_lookup(smarty_key, dataset, data_subset)
41+
# Or send it by address components
42+
results = client.send_generic_lookup(lookup, dataset, data_subset)
43+
# Or send it by freeform address
44+
# results = client.send_generic_lookup(freeform_lookup, dataset, data_subset)
45+
except Exception as err:
46+
print(err)
47+
return
48+
49+
if not results:
50+
print("No results found. This means the SmartyKey or address is likely not valid, or does not have data in this dataset")
51+
return
52+
53+
top_result = results[0]
54+
55+
print("Here is your result!")
56+
print(top_result)
57+
58+
59+
if __name__ == "__main__":
60+
run()
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import os
2+
3+
from smartystreets_python_sdk import BasicAuthCredentials, ClientBuilder
4+
from smartystreets_python_sdk.us_enrichment.lookup import GeoReferenceLookup
5+
6+
7+
def run():
8+
# We recommend storing your secret keys in environment variables instead---it's safer!
9+
auth_id = os.environ['SMARTY_AUTH_ID']
10+
auth_token = os.environ['SMARTY_AUTH_TOKEN']
11+
12+
credentials = BasicAuthCredentials(auth_id, auth_token)
13+
client = ClientBuilder(credentials).build_us_enrichment_api_client()
14+
15+
smarty_key = "87844267"
16+
17+
lookup = GeoReferenceLookup()
18+
freeform_lookup = GeoReferenceLookup()
19+
20+
lookup.smartykey = smarty_key
21+
# lookup.street = "1708 Watson Way"
22+
# lookup.city = "Vineyard"
23+
# lookup.state = "UT"
24+
# lookup.zipcode = "84059"
25+
26+
# Uncomment to narrow the response with include/exclude attributes
27+
# lookup.add_include_attribute('census_block')
28+
# lookup.add_exclude_attribute('place')
29+
30+
# Uncomment to add a custom parameter
31+
# lookup.add_custom_parameter("parameter", "value")
32+
33+
freeform_lookup.freeform = "1708 Watson Way Vineyard UT 84059"
34+
35+
try:
36+
# Send a lookup by SmartyKey alone
37+
# results = client.send_geo_reference_lookup(smarty_key)
38+
# Or send it by address components
39+
results = client.send_geo_reference_lookup(lookup)
40+
# Or send it by freeform address
41+
# results = client.send_geo_reference_lookup(freeform_lookup)
42+
43+
# Or run it through the generic endpoint instead
44+
# results = client.send_generic_lookup(smarty_key, 'geo-reference', None)
45+
except Exception as err:
46+
print(err)
47+
return
48+
49+
if not results:
50+
print("No results found. This means the SmartyKey or address is likely not valid, or does not have data in this dataset")
51+
return
52+
53+
top_result = results[0]
54+
55+
print("Here is your result!")
56+
print(top_result)
57+
58+
59+
if __name__ == "__main__":
60+
run()
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import os
2+
3+
from smartystreets_python_sdk import BasicAuthCredentials, ClientBuilder
4+
from smartystreets_python_sdk.us_enrichment.lookup import RiskLookup
5+
6+
7+
def run():
8+
# We recommend storing your secret keys in environment variables instead---it's safer!
9+
auth_id = os.environ['SMARTY_AUTH_ID']
10+
auth_token = os.environ['SMARTY_AUTH_TOKEN']
11+
12+
credentials = BasicAuthCredentials(auth_id, auth_token)
13+
client = ClientBuilder(credentials).build_us_enrichment_api_client()
14+
15+
smarty_key = "87844267"
16+
17+
lookup = RiskLookup()
18+
freeform_lookup = RiskLookup()
19+
20+
lookup.smartykey = smarty_key
21+
# lookup.street = "1708 Watson Way"
22+
# lookup.city = "Vineyard"
23+
# lookup.state = "UT"
24+
# lookup.zipcode = "84059"
25+
26+
# Uncomment to narrow the response with include/exclude attributes
27+
# lookup.add_include_attribute('fema_flood_zone')
28+
# lookup.add_exclude_attribute('fire_station_distance')
29+
30+
# Uncomment to add a custom parameter
31+
# lookup.add_custom_parameter("parameter", "value")
32+
33+
freeform_lookup.freeform = "1708 Watson Way Vineyard UT 84059"
34+
35+
try:
36+
# Send a lookup by SmartyKey alone
37+
# results = client.send_risk_lookup(smarty_key)
38+
# Or send it by address components
39+
results = client.send_risk_lookup(lookup)
40+
# Or send it by freeform address
41+
# results = client.send_risk_lookup(freeform_lookup)
42+
43+
# Or run it through the generic endpoint instead
44+
# results = client.send_generic_lookup(smarty_key, 'risk', None)
45+
except Exception as err:
46+
print(err)
47+
return
48+
49+
if not results:
50+
print("No results found. This means the SmartyKey or address is likely not valid, or does not have data in this dataset")
51+
return
52+
53+
top_result = results[0]
54+
55+
print("Here is your result!")
56+
print(top_result)
57+
58+
59+
if __name__ == "__main__":
60+
run()

0 commit comments

Comments
 (0)