Skip to content

Commit 065ae94

Browse files
[API-7661] Adds support for warnings in Events API response (#110)
1 parent 0cd0bec commit 065ae94

File tree

5 files changed

+182
-93
lines changed

5 files changed

+182
-93
lines changed

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,14 @@ except sift.client.ApiException:
119119
# request failed
120120
pass
121121

122+
# To include `warnings` field to Events API response via calling `track()` method, set it by the `include_warnings` param:
123+
try:
124+
response = client.track("$transaction", properties, include_warnings=True)
125+
# ...
126+
except sift.client.ApiException:
127+
# request failed
128+
pass
129+
122130
# Request a score for the user with user_id 23056
123131
try:
124132
response = client.score(user_id)

sift/client.py

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,8 @@ def track(
9797
abuse_types=None,
9898
timeout=None,
9999
version=None,
100-
include_score_percentiles=False):
100+
include_score_percentiles=False,
101+
include_warnings=False):
101102
"""Track an event and associated properties to the Sift Science client.
102103
This call is blocking. Check out https://siftscience.com/resources/references/events-api
103104
for more information on what types of events you can send and fields you can add to the
@@ -137,6 +138,10 @@ def track(
137138
include_score_percentiles(optional) : Whether to add new parameter in the query parameter.
138139
if include_score_percentiles is true then add a new parameter called fields in the query parameter
139140
141+
include_warnings(optional) : Whether the API response should include `warnings` field.
142+
if include_warnings is True `warnings` field returns the amount of validation warnings
143+
along with their descriptions. They are not critical enough to reject the whole request,
144+
but important enough to be fixed.
140145
Returns:
141146
A sift.client.Response object if the track call succeeded, otherwise
142147
raises an ApiException.
@@ -179,8 +184,12 @@ def track(
179184
if force_workflow_run:
180185
params['force_workflow_run'] = 'true'
181186

182-
if include_score_percentiles:
183-
params['fields'] = 'SCORE_PERCENTILES'
187+
include_fields = Client._get_fields_param(include_score_percentiles,
188+
include_warnings)
189+
if include_fields:
190+
params['fields'] = ",".join(include_fields)
191+
192+
184193
try:
185194
response = self.session.post(
186195
path,
@@ -1120,6 +1129,16 @@ def _verification_resend_url(self):
11201129
def _verification_check_url(self):
11211130
return (API_URL_VERIFICATION + 'check')
11221131

1132+
@staticmethod
1133+
def _get_fields_param(include_score_percentiles, include_warnings):
1134+
return [
1135+
field for include, field in [
1136+
(include_score_percentiles, 'SCORE_PERCENTILES'),
1137+
(include_warnings, 'WARNINGS')
1138+
] if include
1139+
]
1140+
1141+
11231142
class Response(object):
11241143
HTTP_CODES_WITHOUT_BODY = [204, 304]
11251144

test_integration_app/events_api/test_events_api.py

Lines changed: 97 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -445,122 +445,131 @@ def create_content_review(self):
445445

446446
def create_order(self):
447447
# Sample $create_order event
448+
order_properties = self.build_create_order_event()
449+
return self.client.track("$create_order", order_properties)
450+
451+
def create_order_with_warnings(self):
452+
# Sample $create_order event
453+
order_properties = self.build_create_order_event()
454+
return self.client.track("$create_order", order_properties, include_warnings=True)
455+
456+
def build_create_order_event(self):
448457
order_properties = {
449458
# Required Fields
450-
"$user_id" : self.user_id,
459+
"$user_id": self.user_id,
451460
# Supported Fields
452-
"$session_id" : "gigtleqddo84l8cm15qe4il",
453-
"$order_id" : "ORDER-28168441",
454-
"$user_email" : self.user_email,
455-
"$verification_phone_number" : "+123456789012",
456-
"$amount" : 115940000, # $115.94
457-
"$currency_code" : "USD",
458-
"$billing_address" : {
459-
"$name" : "Bill Jones",
460-
"$phone" : "1-415-555-6041",
461-
"$address_1" : "2100 Main Street",
462-
"$address_2" : "Apt 3B",
463-
"$city" : "New London",
464-
"$region" : "New Hampshire",
465-
"$country" : "US",
466-
"$zipcode" : "03257"
461+
"$session_id": "gigtleqddo84l8cm15qe4il",
462+
"$order_id": "ORDER-28168441",
463+
"$user_email": self.user_email,
464+
"$verification_phone_number": "+123456789012",
465+
"$amount": 115940000, # $115.94
466+
"$currency_code": "USD",
467+
"$billing_address": {
468+
"$name": "Bill Jones",
469+
"$phone": "1-415-555-6041",
470+
"$address_1": "2100 Main Street",
471+
"$address_2": "Apt 3B",
472+
"$city": "New London",
473+
"$region": "New Hampshire",
474+
"$country": "US",
475+
"$zipcode": "03257"
467476
},
468-
"$payment_methods" : [
477+
"$payment_methods": [
469478
{
470-
"$payment_type" : "$credit_card",
471-
"$payment_gateway" : "$braintree",
472-
"$card_bin" : "542486",
473-
"$card_last4" : "4444"
479+
"$payment_type": "$credit_card",
480+
"$payment_gateway": "$braintree",
481+
"$card_bin": "542486",
482+
"$card_last4": "4444"
474483
}
475484
],
476-
"$ordered_from" : {
477-
"$store_id" : "123",
478-
"$store_address" : {
479-
"$name" : "Bill Jones",
480-
"$phone" : "1-415-555-6040",
481-
"$address_1" : "2100 Main Street",
482-
"$address_2" : "Apt 3B",
483-
"$city" : "New London",
484-
"$region" : "New Hampshire",
485-
"$country" : "US",
486-
"$zipcode" : "03257"
485+
"$ordered_from": {
486+
"$store_id": "123",
487+
"$store_address": {
488+
"$name": "Bill Jones",
489+
"$phone": "1-415-555-6040",
490+
"$address_1": "2100 Main Street",
491+
"$address_2": "Apt 3B",
492+
"$city": "New London",
493+
"$region": "New Hampshire",
494+
"$country": "US",
495+
"$zipcode": "03257"
487496
}
488497
},
489-
"$brand_name" : "sift",
490-
"$site_domain" : "sift.com",
491-
"$site_country" : "US",
492-
"$shipping_address" : {
493-
"$name" : "Bill Jones",
494-
"$phone" : "1-415-555-6041",
495-
"$address_1" : "2100 Main Street",
496-
"$address_2" : "Apt 3B",
497-
"$city" : "New London",
498-
"$region" : "New Hampshire",
499-
"$country" : "US",
500-
"$zipcode" : "03257"
498+
"$brand_name": "sift",
499+
"$site_domain": "sift.com",
500+
"$site_country": "US",
501+
"$shipping_address": {
502+
"$name": "Bill Jones",
503+
"$phone": "1-415-555-6041",
504+
"$address_1": "2100 Main Street",
505+
"$address_2": "Apt 3B",
506+
"$city": "New London",
507+
"$region": "New Hampshire",
508+
"$country": "US",
509+
"$zipcode": "03257"
501510
},
502-
"$expedited_shipping" : True,
503-
"$shipping_method" : "$physical",
504-
"$shipping_carrier" : "UPS",
511+
"$expedited_shipping": True,
512+
"$shipping_method": "$physical",
513+
"$shipping_carrier": "UPS",
505514
"$shipping_tracking_numbers": ["1Z204E380338943508", "1Z204E380338943509"],
506-
"$items" : [
515+
"$items": [
507516
{
508-
"$item_id" : "12344321",
509-
"$product_title" : "Microwavable Kettle Corn: Original Flavor",
510-
"$price" : 4990000, # $4.99
511-
"$upc" : "097564307560",
512-
"$sku" : "03586005",
513-
"$brand" : "Peters Kettle Corn",
514-
"$manufacturer" : "Peters Kettle Corn",
515-
"$category" : "Food and Grocery",
516-
"$tags" : ["Popcorn", "Snacks", "On Sale"],
517-
"$quantity" : 4
517+
"$item_id": "12344321",
518+
"$product_title": "Microwavable Kettle Corn: Original Flavor",
519+
"$price": 4990000, # $4.99
520+
"$upc": "097564307560",
521+
"$sku": "03586005",
522+
"$brand": "Peters Kettle Corn",
523+
"$manufacturer": "Peters Kettle Corn",
524+
"$category": "Food and Grocery",
525+
"$tags": ["Popcorn", "Snacks", "On Sale"],
526+
"$quantity": 4
518527
},
519528
{
520-
"$item_id" : "B004834GQO",
521-
"$product_title" : "The Slanket Blanket-Texas Tea",
522-
"$price" : 39990000, # $39.99
523-
"$upc" : "6786211451001",
524-
"$sku" : "004834GQ",
525-
"$brand" : "Slanket",
526-
"$manufacturer" : "Slanket",
527-
"$category" : "Blankets & Throws",
528-
"$tags" : ["Awesome", "Wintertime specials"],
529-
"$color" : "Texas Tea",
530-
"$quantity" : 2
529+
"$item_id": "B004834GQO",
530+
"$product_title": "The Slanket Blanket-Texas Tea",
531+
"$price": 39990000, # $39.99
532+
"$upc": "6786211451001",
533+
"$sku": "004834GQ",
534+
"$brand": "Slanket",
535+
"$manufacturer": "Slanket",
536+
"$category": "Blankets & Throws",
537+
"$tags": ["Awesome", "Wintertime specials"],
538+
"$color": "Texas Tea",
539+
"$quantity": 2
531540
}
532541
],
533542
# For marketplaces, use $seller_user_id to identify the seller
534-
"$seller_user_id" : "slinkys_emporium",
543+
"$seller_user_id": "slinkys_emporium",
535544

536-
"$promotions" : [
545+
"$promotions": [
537546
{
538-
"$promotion_id" : "FirstTimeBuyer",
539-
"$status" : "$success",
540-
"$description" : "$5 off",
541-
"$discount" : {
542-
"$amount" : 5000000, # $5.00
543-
"$currency_code" : "USD",
544-
"$minimum_purchase_amount" : 25000000 # $25.00
547+
"$promotion_id": "FirstTimeBuyer",
548+
"$status": "$success",
549+
"$description": "$5 off",
550+
"$discount": {
551+
"$amount": 5000000, # $5.00
552+
"$currency_code": "USD",
553+
"$minimum_purchase_amount": 25000000 # $25.00
545554
}
546555
}
547556
],
548557

549558
# Sample Custom Fields
550-
"digital_wallet" : "apple_pay", # "google_wallet", etc.
551-
"coupon_code" : "dollarMadness",
552-
"shipping_choice" : "FedEx Ground Courier",
553-
"is_first_time_buyer" : False,
559+
"digital_wallet": "apple_pay", # "google_wallet", etc.
560+
"coupon_code": "dollarMadness",
561+
"shipping_choice": "FedEx Ground Courier",
562+
"is_first_time_buyer": False,
554563

555564
# Send this information from a BROWSER client.
556-
"$browser" : {
557-
"$user_agent" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36",
558-
"$accept_language" : "en-US",
559-
"$content_language" : "en-GB"
565+
"$browser": {
566+
"$user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36",
567+
"$accept_language": "en-US",
568+
"$content_language": "en-GB"
560569
}
561570
}
562-
return self.client.track("$create_order", order_properties)
563-
571+
return order_properties
572+
564573
def flag_content(self):
565574
# Sample $flag_content event
566575
flag_content_properties = {

test_integration_app/main.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,17 @@ def isOK(self, response):
1414
return ((response.status == 0) and ((response.http_status_code == 200) or (response.http_status_code == 201)))
1515
else:
1616
return ((response.http_status_code == 200) or (response.http_status_code == 201))
17-
17+
18+
def is_ok_with_warnings(self, response):
19+
return self.isOK(response) and \
20+
hasattr(response, 'body') and \
21+
len(response.body['warnings']) > 0
22+
23+
def is_ok_without_warnings(self, response):
24+
return self.isOK(response) and \
25+
hasattr(response, 'body') and \
26+
'warnings' not in response.body
27+
1828
def runAllMethods():
1929
objUtils = Utils()
2030
objEvents = test_events_api.EventsAPI()
@@ -24,7 +34,7 @@ def runAllMethods():
2434
objVerification = test_verification_api.VerificationAPI()
2535
objPSPMerchant = test_psp_merchant_api.PSPMerchantAPI()
2636

27-
#Events APIs
37+
# Events APIs
2838
assert (objUtils.isOK(objEvents.add_item_to_cart()) == True)
2939
assert (objUtils.isOK(objEvents.add_promotion()) == True)
3040
assert (objUtils.isOK(objEvents.chargeback()) == True)
@@ -55,6 +65,11 @@ def runAllMethods():
5565
assert (objUtils.isOK(objEvents.update_order()) == True)
5666
assert (objUtils.isOK(objEvents.update_password()) == True)
5767
assert (objUtils.isOK(objEvents.verification()) == True)
68+
69+
# Testing include warnings query param
70+
assert (objUtils.is_ok_without_warnings(objEvents.create_order()) == True)
71+
assert (objUtils.is_ok_with_warnings(objEvents.create_order_with_warnings()) == True)
72+
5873
print("Events API Tested")
5974

6075
# Decision APIs

tests/test_client.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1499,6 +1499,44 @@ def test_get_user_score_include_score_percentiles_ok(self):
14991499
assert (response.body['scores']['payment_abuse']['score'] == 0.97)
15001500
assert ('latest_decisions' in response.body)
15011501

1502+
def test_warnings_added_as_fields_param(self):
1503+
event = '$transaction'
1504+
mock_response = mock.Mock()
1505+
mock_response.content = '{"status": 0, "error_message": "OK"}'
1506+
mock_response.json.return_value = json.loads(mock_response.content)
1507+
mock_response.status_code = 200
1508+
with mock.patch.object(self.sift_client.session, 'post') as mock_post:
1509+
mock_post.return_value = mock_response
1510+
response = self.sift_client.track(event, valid_transaction_properties(),
1511+
include_warnings=True)
1512+
mock_post.assert_called_with(
1513+
'https://api.siftscience.com/v205/events',
1514+
data=mock.ANY,
1515+
headers=mock.ANY,
1516+
timeout=mock.ANY,
1517+
params={'fields': 'WARNINGS'})
1518+
self.assertIsInstance(response, sift.client.Response)
1519+
1520+
def test_warnings_and_score_percentiles_added_as_fields_param(self):
1521+
event = '$transaction'
1522+
mock_response = mock.Mock()
1523+
mock_response.content = '{"status": 0, "error_message": "OK"}'
1524+
mock_response.json.return_value = json.loads(mock_response.content)
1525+
mock_response.status_code = 200
1526+
with mock.patch.object(self.sift_client.session, 'post') as mock_post:
1527+
mock_post.return_value = mock_response
1528+
response = self.sift_client.track(event, valid_transaction_properties(),
1529+
include_score_percentiles=True,
1530+
include_warnings=True)
1531+
mock_post.assert_called_with(
1532+
'https://api.siftscience.com/v205/events',
1533+
data=mock.ANY,
1534+
headers=mock.ANY,
1535+
timeout=mock.ANY,
1536+
params={'fields': 'SCORE_PERCENTILES,WARNINGS'})
1537+
self.assertIsInstance(response, sift.client.Response)
1538+
1539+
15021540
def main():
15031541
unittest.main()
15041542

0 commit comments

Comments
 (0)