Skip to content

Commit 24ab854

Browse files
committed
Support for /identity/buckets
1 parent cec164b commit 24ab854

File tree

6 files changed

+124
-5
lines changed

6 files changed

+124
-5
lines changed
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
# or the reason why it is unmapped
88

99
def _usage():
10-
print('Usage: python3 sample_identity_map_client.py <base_url> <api_key> <client_secret> <email_1> <email_2> ... <email_n>'
10+
print('Usage: python3 sample_generate_identity_map.py <base_url> <api_key> <client_secret> <email_1> <email_2> ... <email_n>'
1111
, file=sys.stderr)
1212
sys.exit(1)
1313

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import datetime
2+
import sys
3+
4+
from uid2_client import IdentityMapClient
5+
6+
7+
# this sample client takes date time as input and generates an IdentityBucketsResponse object which contains
8+
# a list of buckets
9+
10+
def _usage():
11+
print('Usage: python3 sample_get_identity_buckets.py <base_url> <api_key> <client_secret> <year> <month> <day> <hour> <minute> <second>'
12+
, file=sys.stderr)
13+
sys.exit(1)
14+
15+
16+
if len(sys.argv) <= 9:
17+
_usage()
18+
19+
base_url = sys.argv[1]
20+
api_key = sys.argv[2]
21+
client_secret = sys.argv[3]
22+
year = int(sys.argv[4])
23+
month = int(sys.argv[5])
24+
day = int(sys.argv[6])
25+
hour = int(sys.argv[7])
26+
minute = int(sys.argv[8])
27+
second = int(sys.argv[9])
28+
29+
client = IdentityMapClient(base_url, api_key, client_secret)
30+
31+
identity_buckets_response = client.get_identity_buckets(datetime.datetime(year, month, day, hour, minute, second))
32+
33+
bucket = identity_buckets_response.buckets[0]
34+
print("The bucket id of the first bucket: ", bucket.get_bucket_id())
35+
print("The last updated timestamp of the first bucket: ", bucket.get_last_updated())

tests/test_identity_map_client.py

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import datetime
12
import os
23
import unittest
34

@@ -131,24 +132,28 @@ def test_identity_map_hashed_phones(self):
131132

132133
self.assert_unmapped(response, "optout", hashed_opted_out_phone)
133134

134-
def test_identity_map_bad_url(self):
135+
def test_identity_map_client_bad_url(self):
135136
identity_map_input = IdentityMapInput.from_emails(
136137
["hopefully-not-opted-out@example.com", "somethingelse@example.com", "optout@example.com"])
137138
client = IdentityMapClient("https://operator-bad-url.uidapi.com", os.getenv("UID2_API_KEY"), os.getenv("UID2_SECRET_KEY"))
138139
self.assertRaises(requests.exceptions.ConnectionError, client.generate_identity_map, identity_map_input)
140+
self.assertRaises(requests.exceptions.ConnectionError, client.get_identity_buckets, datetime.datetime.now())
139141

140-
def test_identity_map_bad_api_key(self):
142+
def test_identity_map_client_bad_api_key(self):
141143
identity_map_input = IdentityMapInput.from_emails(
142144
["hopefully-not-opted-out@example.com", "somethingelse@example.com", "optout@example.com"])
143145
client = IdentityMapClient(os.getenv("UID2_BASE_URL"), "bad-api-key", os.getenv("UID2_SECRET_KEY"))
144146
self.assertRaises(requests.exceptions.HTTPError, client.generate_identity_map,identity_map_input)
147+
self.assertRaises(requests.exceptions.HTTPError, client.get_identity_buckets, datetime.datetime.now())
145148

146-
def test_identity_map_bad_secret(self):
149+
def test_identity_map_client_bad_secret(self):
147150
identity_map_input = IdentityMapInput.from_emails(
148151
["hopefully-not-opted-out@example.com", "somethingelse@example.com", "optout@example.com"])
149152
client = IdentityMapClient(os.getenv("UID2_BASE_URL"), os.getenv("UID2_API_KEY"), "wJ0hP19QU4hmpB64Y3fV2dAed8t/mupw3sjN5jNRFzg=")
150153
self.assertRaises(requests.exceptions.HTTPError, client.generate_identity_map,
151154
identity_map_input)
155+
self.assertRaises(requests.exceptions.HTTPError, client.get_identity_buckets,
156+
datetime.datetime.now())
152157

153158
def assert_mapped(self, response, dii):
154159
mapped_identity = response.mapped_identities.get(dii)
@@ -166,6 +171,20 @@ def assert_unmapped(self, response, reason, dii):
166171
mapped_identity = response.mapped_identities.get(dii)
167172
self.assertIsNone(mapped_identity)
168173

174+
def test_identity_buckets(self):
175+
response = self.identity_map_client.get_identity_buckets(datetime.datetime.now() - datetime.timedelta(days=2))
176+
self.assertTrue(len(response.buckets) > 0)
177+
self.assertTrue(response.is_success)
178+
179+
def test_identity_buckets_empty_response(self):
180+
response = self.identity_map_client.get_identity_buckets(datetime.datetime.now() + datetime.timedelta(days=1))
181+
self.assertTrue(len(response.buckets) == 0)
182+
self.assertTrue(response.is_success)
183+
184+
def test_identity_buckets_invalid_timestamp(self):
185+
self.assertRaises(TypeError, self.identity_map_client.get_identity_buckets,
186+
"1234567890")
187+
169188

170189
if __name__ == '__main__':
171190
unittest.main()
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import json
2+
3+
4+
class IdentityBucketsResponse:
5+
def __init__(self, response):
6+
self._buckets = []
7+
response_json = json.loads(response)
8+
self._status = response_json["status"]
9+
10+
if not self.is_success():
11+
raise ValueError("Got unexpected identity buckets status: " + self._status)
12+
13+
body = response_json["body"]
14+
15+
for bucket in body:
16+
self._buckets.append(Bucket.from_json(bucket))
17+
18+
def is_success(self):
19+
return self._status == "success"
20+
21+
@property
22+
def buckets(self):
23+
return self._buckets
24+
25+
@property
26+
def status(self):
27+
return self._status
28+
29+
30+
class Bucket:
31+
def __init__(self, bucket_id, last_updated):
32+
self._bucket_id = bucket_id
33+
self._last_updated = last_updated
34+
35+
def get_bucket_id(self):
36+
return self._bucket_id
37+
38+
def get_last_updated(self):
39+
return self._last_updated
40+
41+
@staticmethod
42+
def from_json(json_obj):
43+
return Bucket(
44+
json_obj.get("bucket_id"),
45+
json_obj.get("last_updated")
46+
)

uid2_client/identity_map_client.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import base64
22
import datetime as dt
3+
import json
34
from datetime import timezone
45

6+
from .Identity_buckets_response import IdentityBucketsResponse
57
from .identity_map_response import IdentityMapResponse
68

7-
from uid2_client import auth_headers, make_v2_request, post, parse_v2_response
9+
from uid2_client import auth_headers, make_v2_request, post, parse_v2_response, get_datetime_iso_format
810

911

1012
class IdentityMapClient:
@@ -39,3 +41,11 @@ def generate_identity_map(self, identity_map_input):
3941
resp.raise_for_status()
4042
resp_body = parse_v2_response(self._client_secret, resp.text, nonce)
4143
return IdentityMapResponse(resp_body, identity_map_input)
44+
45+
def get_identity_buckets(self, since_timestamp):
46+
req, nonce = make_v2_request(self._client_secret, dt.datetime.now(tz=timezone.utc),
47+
json.dumps({"since_timestamp": get_datetime_iso_format(since_timestamp)}).encode())
48+
resp = post(self._base_url, '/v2/identity/buckets', headers=auth_headers(self._api_key), data=req)
49+
resp.raise_for_status()
50+
resp_body = parse_v2_response(self._client_secret, resp.text, nonce)
51+
return IdentityBucketsResponse(resp_body)

uid2_client/input_util.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import datetime
12
import hashlib
23
import base64
34

@@ -119,3 +120,11 @@ def normalize_and_hash_phone(phone):
119120
if not is_phone_number_normalized(phone):
120121
raise ValueError("phone number is not normalized: " + phone)
121122
return get_base64_encoded_hash(phone)
123+
124+
125+
def get_datetime_iso_format(timestamp):
126+
if isinstance(timestamp, datetime.datetime):
127+
return timestamp.isoformat()
128+
else:
129+
raise TypeError("timestamp is not in datetime format")
130+

0 commit comments

Comments
 (0)