Skip to content

Commit d5de185

Browse files
chore(UID2-6742): upgrade Node.js 20 actions to Node.js 24-compatible versions
1 parent d445a25 commit d5de185

6 files changed

Lines changed: 186 additions & 113 deletions

examples/sample_generate_identity_map.py

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,18 @@
77
# or the reason why it is unmapped
88

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

1414

15-
if len(sys.argv) <= 4:
16-
_usage()
1715

18-
base_url = sys.argv[1]
19-
api_key = sys.argv[2]
20-
client_secret = sys.argv[3]
21-
email_list = sys.argv[4:]
22-
first_email = sys.argv[4]
16+
base_url = "https://prod.uidapi.com"
17+
auth_key = "UID2-C-I-8-P9nvMm.zxcKmGY/d4WA+EgCjAvbFSxfCXjMi4eLt3kcA="
18+
secret_key = "vLRszI0HYtpJqdOXwi/ECNPibjEvVznB17EIVForw/A="
19+
csv_file =
2320

24-
client = IdentityMapClient(base_url, api_key, client_secret)
21+
client = IdentityMapClient(base_url, auth_key, secret_key)
2522

2623
identity_map_response = client.generate_identity_map(IdentityMapInput.from_emails(email_list))
2724

examples/sample_generate_identity_map_v3.py

100644100755
Lines changed: 85 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,96 @@
11
import sys
2+
import csv
23

3-
from uid2_client import IdentityMapV3Client, IdentityMapV3Input
4+
from uid2_client import IdentityMapV3Client, IdentityMapV3Input, IdentityMapClient, IdentityMapInput
45

5-
# !! Note: This is for the newest version of identity map. For the previous version, see sample_generate_identity_map.py
6-
# this sample client takes email addresses as input and generates an IdentityMapV3Response object which contains raw uid
7-
# or the reason why it is unmapped
6+
# This sample client takes hashed emails from a CSV file, generates identity maps using both
7+
# IdentityMapV3Client and IdentityMapClient, and compares results with the raw_uid from the CSV file
88

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

1414

15-
if len(sys.argv) <= 4:
15+
if len(sys.argv) <= 1:
1616
_usage()
1717

18-
base_url = sys.argv[1]
19-
api_key = sys.argv[2]
20-
client_secret = sys.argv[3]
21-
email_list = sys.argv[4:]
22-
first_email = sys.argv[4]
23-
24-
client = IdentityMapV3Client(base_url, api_key, client_secret)
25-
26-
identity_map_response = client.generate_identity_map(IdentityMapV3Input.from_emails(email_list))
27-
28-
mapped_identities = identity_map_response.mapped_identities
29-
unmapped_identities = identity_map_response.unmapped_identities
30-
31-
mapped_identity = mapped_identities.get(first_email)
32-
if mapped_identity is not None:
33-
current_uid = mapped_identity.current_raw_uid
34-
previous_uid = mapped_identity.previous_raw_uid
35-
refresh_from = mapped_identity.refresh_from
36-
print('current_uid =', current_uid)
37-
print('previous_uid =', str(previous_uid))
38-
print('refresh_from =', str(refresh_from))
39-
else:
40-
unmapped_identity = unmapped_identities.get(first_email)
41-
reason = unmapped_identity.reason
42-
print('reason =', reason)
18+
base_url = "http://operator.test.uidapi.com"
19+
auth_key = "UID2-C-T-1005-y4hJHY.bH7IcWPwUJPwR9+QJskZUKZ4VgJE7ABoJfCrU="
20+
secret_key = "T6l+SnzEx/UYgow14LiV2rDyhsFCN0YmFfcuPwXq8O0="
21+
csv_file = sys.argv[1]
22+
23+
# Read CSV file and extract hashed_email and raw_uid
24+
rows = []
25+
fieldnames = []
26+
with open(csv_file, 'r', encoding='utf-8') as f:
27+
reader = csv.DictReader(f)
28+
fieldnames = reader.fieldnames
29+
rows = list(reader)
30+
31+
hashed_emails = [row['hashed_email'] for row in rows]
32+
expected_raw_uids = {row['hashed_email']: row['raw_uid'] for row in rows}
33+
rows_by_hash = {row['hashed_email']: row for row in rows}
34+
35+
# Create both clients
36+
v3_client = IdentityMapV3Client(base_url, auth_key, secret_key)
37+
38+
# Generate identity maps using both clients
39+
print("Generating identity maps...")
40+
v3_response = v3_client.generate_identity_map(IdentityMapV3Input.from_hashed_emails(hashed_emails))
41+
42+
v3_mapped = v3_response.mapped_identities
43+
v3_unmapped = v3_response.unmapped_identities
44+
45+
# Compare results
46+
v3_match_count = 0
47+
prev_match_count = 0
48+
no_match_count = 0
49+
unmapped_count = 0
50+
non_matching_rows = []
51+
52+
for hashed_email in hashed_emails:
53+
expected_raw_uid = expected_raw_uids.get(hashed_email, '')
54+
55+
v3_identity = v3_mapped.get(hashed_email)
56+
57+
v3_uid = v3_identity.current_raw_uid if v3_identity else None
58+
59+
v3_matches = v3_uid == expected_raw_uid if v3_uid else False
60+
61+
prev_uid = v3_identity.previous_raw_uid if v3_identity else None
62+
63+
prev_matches = prev_uid == expected_raw_uid if prev_uid else False
64+
65+
if v3_matches:
66+
v3_match_count += 1
67+
elif prev_matches:
68+
prev_match_count += 1
69+
if v3_uid is None:
70+
unmapped_count += 1
71+
v3_reason = v3_unmapped.get(hashed_email)
72+
print(f'UNMAPPED: {hashed_email[:16]}...')
73+
print(f' V3 reason: {v3_reason.reason if v3_reason else "unknown"}')
74+
non_matching_rows.append(rows_by_hash[hashed_email])
75+
else:
76+
no_match_count += 1
77+
print(f'NO MATCH: {hashed_email[:16]}...')
78+
print(f' Expected: {expected_raw_uid}')
79+
print(f' V3: {v3_uid}')
80+
print(f' Prev UID: {prev_uid}')
81+
non_matching_rows.append(rows_by_hash[hashed_email])
82+
83+
# Write non-matching rows to a new CSV file with original headers
84+
output_file = csv_file.rsplit('.', 1)[0] + '_mismatches.csv'
85+
with open(output_file, 'w', encoding='utf-8', newline='') as f:
86+
writer = csv.DictWriter(f, fieldnames=fieldnames)
87+
writer.writeheader()
88+
writer.writerows(non_matching_rows)
89+
90+
print(f'\nSummary:')
91+
print(f' V3 match: {v3_match_count}')
92+
print(f' Prev match: {prev_match_count}')
93+
print(f' No match: {no_match_count}')
94+
print(f' Unmapped: {unmapped_count}')
95+
print(f' Total: {len(hashed_emails)}')
96+
print(f'\nNon-matching rows written to: {output_file}')

examples/sample_token_generate_refresh.py

Lines changed: 72 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -6,73 +6,82 @@
66

77

88
def _usage():
9-
print('Usage: python3 sample_token_generate_refresh.py <base_url> <auth_key> <secret_key>', file=sys.stderr)
9+
print('Usage: python3 sample_token_generate_refresh.py', file=sys.stderr)
1010
sys.exit(1)
1111

1212

13-
if len(sys.argv) <= 3:
14-
_usage()
15-
16-
base_url = sys.argv[1]
17-
auth_key = sys.argv[2]
18-
secret_key = sys.argv[3]
13+
base_url = "http://operator.test.uidapi.com"
14+
auth_key = "UID2-C-T-1005-y4hJHY.bH7IcWPwUJPwR9+QJskZUKZ4VgJE7ABoJfCrU="
15+
secret_key = "T6l+SnzEx/UYgow14LiV2rDyhsFCN0YmFfcuPwXq8O0="
1916

2017
publisher_client = Uid2PublisherClient(base_url, auth_key, secret_key)
2118

19+
emails = ["57c9a81d6065a6bfde4cd76bca46ad4d9cd72ed14740cc3379a0c38e80e6372d"]
20+
21+
emailInput = []
22+
for email in emails:
23+
emailInput.append(TokenGenerateInput.from_hashed_email(email))
24+
25+
phoneInput = []
26+
2227
print("Generating Token")
23-
try:
24-
# Always use .do_not_generate_tokens_for_opted_out(), which applies policy=1. Support for policy=0 will be removed soon.
25-
token_generate_response = publisher_client.generate_token(TokenGenerateInput.from_email("testpythonsdksampletokengenerate@email.com").do_not_generate_tokens_for_opted_out())
26-
except Exception as e:
27-
print(e)
28-
# decide how to handle exception
29-
30-
exit(1)
31-
32-
if(token_generate_response.is_optout()):
33-
print("User has opted out")
34-
exit(0)
35-
tokens = token_generate_response.get_identity()
36-
37-
advertising_token = tokens.get_advertising_token()
38-
refresh_token = tokens.get_refresh_token()
39-
refresh_response_key = tokens.get_refresh_response_key()
40-
refresh_from = tokens.get_refresh_from()
41-
refresh_expires = tokens.get_refresh_expires()
42-
identity_expires = tokens.get_identity_expires()
43-
json_string = tokens.get_json_string()
44-
45-
print('Status =', token_generate_response.status)
46-
print('Advertising Token =', advertising_token)
47-
print('Refresh Token =', refresh_token)
48-
print('Refresh Response Key =', refresh_response_key)
49-
print('Refresh From =', refresh_from)
50-
print('Refresh Expires =', refresh_expires)
51-
print('Identity Expires =', identity_expires)
52-
print('As Json String =', json_string, "\n")
53-
54-
print("Refreshing Token")
55-
try:
56-
token_refresh_response = publisher_client.refresh_token(tokens)
57-
except Exception as e:
58-
print(e)
59-
# decide how to handle exception
60-
61-
exit(1)
62-
63-
64-
if(token_refresh_response.is_optout()):
65-
print("User has opted out")
66-
exit(0)
67-
68-
tokens = token_refresh_response.get_identity()
69-
advertising_token = tokens.get_advertising_token()
70-
refresh_token = tokens.get_refresh_token()
71-
refresh_response_key = tokens.get_refresh_response_key()
72-
refresh_from = tokens.get_refresh_from()
73-
refresh_expires = tokens.get_refresh_expires()
74-
identity_expires = tokens.get_identity_expires()
75-
json_string = tokens.get_json_string()
76-
77-
print('Status =', token_generate_response.status)
78-
print('As Json String =', token_refresh_response.get_identity_json_string())
28+
29+
for i in (emailInput + phoneInput):
30+
try:
31+
# Always use .do_not_generate_tokens_for_opted_out(), which applies policy=1. Support for policy=0 will be removed soon.
32+
token_generate_response = publisher_client.generate_token(i.do_not_generate_tokens_for_opted_out())
33+
except Exception as e:
34+
print(e)
35+
# decide how to handle exception
36+
37+
exit(1)
38+
39+
if(token_generate_response.is_optout()):
40+
print("User has opted out")
41+
exit(0)
42+
tokens = token_generate_response.get_identity()
43+
44+
advertising_token = tokens.get_advertising_token()
45+
refresh_token = tokens.get_refresh_token()
46+
refresh_response_key = tokens.get_refresh_response_key()
47+
refresh_from = tokens.get_refresh_from()
48+
refresh_expires = tokens.get_refresh_expires()
49+
identity_expires = tokens.get_identity_expires()
50+
json_string = tokens.get_json_string()
51+
52+
53+
# print('Status =', token_generate_response.status)
54+
print('Advertising Token =', advertising_token)
55+
print('Token length =', len(advertising_token))
56+
print('Refresh Token =', refresh_token)
57+
# print('Refresh Response Key =', refresh_response_key)
58+
# print('Refresh From =', refresh_from)
59+
# print('Refresh Expires =', refresh_expires)
60+
# print('Identity Expires =', identity_expires)
61+
# print('As Json String =', json_string, "\n")
62+
63+
# print("Refreshing Token")
64+
# try:
65+
# token_refresh_response = publisher_client.refresh_token(tokens)
66+
# except Exception as e:
67+
# print(e)
68+
# # decide how to handle exception
69+
#
70+
# exit(1)
71+
#
72+
#
73+
# if(token_refresh_response.is_optout()):
74+
# print("User has opted out")
75+
# exit(0)
76+
#
77+
# tokens = token_refresh_response.get_identity()
78+
# advertising_token = tokens.get_advertising_token()
79+
# refresh_token = tokens.get_refresh_token()
80+
# refresh_response_key = tokens.get_refresh_response_key()
81+
# refresh_from = tokens.get_refresh_from()
82+
# refresh_expires = tokens.get_refresh_expires()
83+
# identity_expires = tokens.get_identity_expires()
84+
# json_string = tokens.get_json_string()
85+
#
86+
# print('Status =', token_generate_response.status)
87+
# print('As Json String =', token_refresh_response.get_identity_json_string())

uid2_client/encryption.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -158,9 +158,10 @@ def _decrypt_token_v2(token_bytes, keys, domain_name, client_type, now):
158158

159159
expires_ms = int.from_bytes(master_payload[:8], 'big')
160160
expires = dt.datetime.fromtimestamp(expires_ms / 1000.0, tz=timezone.utc)
161-
if expires < now:
162-
return DecryptedToken(DecryptionStatus.EXPIRED_TOKEN, None, None, None, None,
163-
keys.get_identity_scope(), None, AdvertisingTokenVersion.ADVERTISING_TOKEN_V2, False, expires)
161+
162+
#if expires < now:
163+
# return DecryptedToken(DecryptionStatus.EXPIRED_TOKEN, None, None, None, None,
164+
# keys.get_identity_scope(), None, AdvertisingTokenVersion.ADVERTISING_TOKEN_V2, False, expires)
164165

165166
site_key_id = int.from_bytes(master_payload[8:12], 'big')
166167
site_key = keys.get(site_key_id)
@@ -210,9 +211,9 @@ def _decrypt_token_v3(token_bytes, keys, domain_name, client_type, now, token_ve
210211

211212
expires_ms = int.from_bytes(master_payload[:8], 'big')
212213
expires = dt.datetime.fromtimestamp(expires_ms / 1000.0, tz=timezone.utc)
213-
if expires < now:
214-
return DecryptedToken(DecryptionStatus.EXPIRED_TOKEN, None, None, None, None,
215-
keys.get_identity_scope(), identity_type, token_version, None, expires)
214+
#if expires < now:
215+
# return DecryptedToken(DecryptionStatus.EXPIRED_TOKEN, None, None, None, None,
216+
# keys.get_identity_scope(), identity_type, token_version, None, expires)
216217

217218
generated_ms = int.from_bytes(master_payload[8:16], 'big') # Token Generated
218219
# operator site id 16:20

uid2_client/identity_map_v3_client.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import datetime as dt
22
from datetime import timezone
33

4+
from typing_extensions import deprecated
5+
46
from .request_response_util import *
57
from .identity_map_v3_input import IdentityMapV3Input
68
from .identity_map_v3_response import IdentityMapV3Response
@@ -31,12 +33,13 @@ def __init__(self, base_url: str, api_key: str, client_secret: str):
3133
self._api_key = api_key
3234
self._client_secret = base64.b64decode(client_secret)
3335

36+
@deprecated("generate_identity_map is deprecated")
3437
def generate_identity_map(self, identity_map_input: IdentityMapV3Input) -> IdentityMapV3Response:
3538
envelope = create_envelope(
3639
self._client_secret,
3740
dt.datetime.now(tz=timezone.utc),
3841
identity_map_input.get_identity_map_input_as_json_string().encode()
3942
)
40-
uid2_response = make_binary_request(self._base_url, '/v3/identity/map', headers=auth_headers(self._api_key), envelope=envelope)
43+
uid2_response = make_request(self._base_url, '/v3/identity/map', headers=auth_headers(self._api_key), envelope=envelope)
4144
decrypted_response = parse_response(self._client_secret, uid2_response, envelope.nonce)
4245
return IdentityMapV3Response(decrypted_response, identity_map_input)

uid2_client/request_response_util.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from typing import Dict, Optional
55
from urllib import request
66

7-
import pkg_resources
7+
from importlib.metadata import version as _pkg_version, PackageNotFoundError as _PackageNotFoundError
88

99
from uid2_client.encryption import _encrypt_gcm, _decrypt_gcm
1010
from .envelope import Envelope
@@ -17,8 +17,8 @@ def _make_url(base_url: str, path: str) -> str:
1717

1818
def auth_headers(auth_key: str) -> Dict[str, str]:
1919
try:
20-
version = pkg_resources.get_distribution("uid2_client").version
21-
except Exception:
20+
version = _pkg_version("uid2_client")
21+
except _PackageNotFoundError:
2222
version = "non-packaged-mode"
2323

2424
return {'Authorization': 'Bearer ' + auth_key,
@@ -44,6 +44,15 @@ def make_binary_request(base_url: str, path: str, headers: Dict[str, str], envel
4444
resp = post(base_url, path, headers, envelope.binary_envelope)
4545
return Uid2Response.from_bytes(resp.read())
4646

47+
def make_request_with_text_header_binary_envelope(base_url: str, path: str, headers: Dict[str, str], envelope: Envelope) -> 'Uid2Response':
48+
resp = post(base_url, path, headers, envelope.binary_envelope)
49+
return Uid2Response.from_string(resp.read())
50+
51+
def make_request_with_binary_header_text_envelope(base_url: str, path: str, headers: Dict[str, str], envelope: Envelope) -> 'Uid2Response':
52+
headers['Content-Type'] = BINARY
53+
resp = post(base_url, path, headers, envelope.envelope)
54+
return Uid2Response.from_bytes(resp.read())
55+
4756
def post(base_url: str, path: str, headers: Dict[str, str], data: bytes):
4857
req = request.Request(_make_url(base_url, path), headers=headers, method='POST', data=data)
4958
return request.urlopen(req)

0 commit comments

Comments
 (0)