-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: adds 'create_recipients' function to the braze client (#25)
- Loading branch information
1 parent
d6c7ac7
commit fca08e6
Showing
5 changed files
with
208 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,6 +11,7 @@ | |
|
||
from braze.constants import ( | ||
GET_EXTERNAL_IDS_CHUNK_SIZE, | ||
MAX_NUM_IDENTIFY_USERS_ALIASES, | ||
REQUEST_TYPE_GET, | ||
REQUEST_TYPE_POST, | ||
TRACK_USER_COMPONENT_CHUNK_SIZE, | ||
|
@@ -209,6 +210,96 @@ def identify_users(self, aliases_to_identify): | |
|
||
return self._make_request(payload, BrazeAPIEndpoints.IDENTIFY_USERS, REQUEST_TYPE_POST) | ||
|
||
def create_recipients(self, alias_label, user_id_by_email, trigger_properties_by_email=None): | ||
""" | ||
Create a recipient object using the dictionary, `user_id_by_email` | ||
containing the user_email key and `lms_user_id` value. | ||
Identifies a list of given email addresess with any existing Braze alias records | ||
via the provided ``lms_user_id``. | ||
https://www.braze.com/docs/api/objects_filters/user_alias_object | ||
The user_alias objects requires a passed in alias_label. | ||
https://www.braze.com/docs/api/endpoints/user_data/post_user_identify/ | ||
The maximum email/user_id dictionary limit is 50, any length beyond 50 will raise an error. | ||
The trigger properties default to None and return as an empty dictionary if no individualized | ||
trigger property is set based on the email. | ||
Arguments: | ||
- `alias_label` (str): The alias label of the user | ||
- `user_id_by_email` (dict): A dictionary where the key is the user's email (str) | ||
and the value is the `lms_user_id` (int). | ||
- `trigger_properties_by_email` (dict) : A dictionary where the key is the user's email (str) | ||
and the value are the `trigger_properties` (dict) | ||
Default is None | ||
Raises: | ||
- `BrazeClientError`: if the number of entries in `user_id_by_email` exceeds 50. | ||
Returns: | ||
- Dict: A dictionary where the key is the `user_email` (str) and the value is the metadata | ||
relating to the braze recipient. | ||
Example: create_recipients( | ||
'alias_label'='Enterprise', | ||
'user_id_by_email'= { | ||
'[email protected]': 123, | ||
'[email protected]': 231, | ||
}, | ||
'trigger_properties_by_email'= { | ||
'[email protected]': { | ||
'foo':'bar' | ||
}, | ||
'[email protected]': {} | ||
}, | ||
) | ||
""" | ||
if len(user_id_by_email) > MAX_NUM_IDENTIFY_USERS_ALIASES: | ||
msg = "Max recipient limit reached." | ||
raise BrazeClientError(msg) | ||
|
||
if trigger_properties_by_email is None: | ||
trigger_properties_by_email = {} | ||
|
||
user_aliases_by_email = { | ||
email: { | ||
"alias_label": alias_label, | ||
"alias_name": email, | ||
} | ||
for email in user_id_by_email | ||
} | ||
# Identify the user alias in case it already exists. This is necessary so | ||
# we don't accidently create a duplicate Braze profile. | ||
self.identify_users([ | ||
{ | ||
'external_id': lms_user_id, | ||
'user_alias': user_aliases_by_email.get(email) | ||
} | ||
for email, lms_user_id in user_id_by_email.items() | ||
]) | ||
|
||
attributes_by_email = { | ||
email: { | ||
"user_alias": user_aliases_by_email.get(email), | ||
"email": email, | ||
"is_enterprise_learner": True, | ||
"_update_existing_only": False, | ||
} | ||
for email in user_id_by_email | ||
} | ||
|
||
return { | ||
email: { | ||
'external_user_id': lms_user_id, | ||
'attributes': attributes_by_email.get(email), | ||
# If a profile does not already exist, Braze will create a new profile before sending a message. | ||
'send_to_existing_only': False, | ||
'trigger_properties': trigger_properties_by_email.get(email, {}), | ||
} | ||
for email, lms_user_id in user_id_by_email.items() | ||
} | ||
|
||
def track_user( | ||
self, | ||
attributes=None, | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
""" | ||
Utility functions for tests | ||
""" | ||
import math | ||
import random | ||
import string | ||
|
||
|
||
def generate_emails_and_ids(num_emails): | ||
""" | ||
Generates random emails with random uuids used primarily to test length constraints | ||
""" | ||
emails_and_ids = { | ||
''.join(random.choices(string.ascii_uppercase + | ||
string.digits, k=8)) + '@gmail.com': math.floor(random.random() * 1000) | ||
for _ in range(num_emails) | ||
} | ||
return emails_and_ids |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,6 +11,7 @@ | |
from braze.client import BrazeClient | ||
from braze.constants import ( | ||
GET_EXTERNAL_IDS_CHUNK_SIZE, | ||
MAX_NUM_IDENTIFY_USERS_ALIASES, | ||
UNSUBSCRIBED_EMAILS_API_LIMIT, | ||
UNSUBSCRIBED_EMAILS_API_SORT_DIRECTION, | ||
BrazeAPIEndpoints, | ||
|
@@ -24,6 +25,7 @@ | |
BrazeRateLimitError, | ||
BrazeUnauthorizedError, | ||
) | ||
from test_utils.utils import generate_emails_and_ids | ||
|
||
|
||
@ddt.ddt | ||
|
@@ -142,6 +144,96 @@ def test_identify_users(self): | |
assert responses.calls[0].request.url == self.USERS_IDENTIFY_URL | ||
assert responses.calls[0].request.body == json.dumps(expected_body) | ||
|
||
@responses.activate | ||
def test_create_recipients_happy_path(self): | ||
""" | ||
Tests create recipients with multiple user emails | ||
""" | ||
responses.add( | ||
responses.POST, | ||
self.USERS_IDENTIFY_URL, | ||
json={'message': 'success'}, | ||
status=201 | ||
) | ||
|
||
mock_user_id_by_email = { | ||
"[email protected]": 12345, | ||
"[email protected]": 56789, | ||
} | ||
mock_trigger_properties_by_email = { | ||
"[email protected]": { | ||
'test_property_name': True | ||
}, | ||
"[email protected]": { | ||
'test_property_address': True | ||
}, | ||
} | ||
mock_expected_recipients = { | ||
email: { | ||
'external_user_id': lms_user_id, | ||
'attributes': { | ||
'user_alias': { | ||
'alias_label': 'Enterprise', | ||
'alias_name': email | ||
}, | ||
'email': email, | ||
'is_enterprise_learner': True, | ||
'_update_existing_only': False, | ||
}, | ||
'send_to_existing_only': False, | ||
'trigger_properties': mock_trigger_properties_by_email.get(email, {}) | ||
|
||
} | ||
for email, lms_user_id in mock_user_id_by_email.items() | ||
} | ||
recipients = self.client.create_recipients( | ||
alias_label='Enterprise', | ||
user_id_by_email=mock_user_id_by_email, | ||
trigger_properties_by_email=mock_trigger_properties_by_email, | ||
) | ||
|
||
assert len(recipients) == 2 | ||
assert recipients == mock_expected_recipients | ||
|
||
def test_create_recipients_exceed_max_emails(self): | ||
""" | ||
Tests the maximum number of emails allowed per identify_users call | ||
used within this function. | ||
""" | ||
mock_exceed_email_length = generate_emails_and_ids(MAX_NUM_IDENTIFY_USERS_ALIASES + 10) | ||
try: | ||
self.client.create_recipients( | ||
alias_label='Enterprise', | ||
user_id_by_email=mock_exceed_email_length, | ||
) | ||
except BrazeClientError as error: | ||
assert str(error) == "Max recipient limit reached." | ||
|
||
@responses.activate | ||
def test_create_recipients_none_type_trigger_properties(self): | ||
""" | ||
Tests that when trigger_properties_by_email is not a defined parameter, | ||
its output is transformed into an empty dictionary. | ||
""" | ||
responses.add( | ||
responses.POST, | ||
self.USERS_IDENTIFY_URL, | ||
json={'message': 'success'}, | ||
status=201 | ||
) | ||
mock_user_id_by_email = { | ||
"[email protected]": 12345, | ||
"[email protected]": 56789, | ||
} | ||
|
||
recipients = self.client.create_recipients( | ||
alias_label='Enterprise', | ||
user_id_by_email=mock_user_id_by_email, | ||
) | ||
|
||
for _, metadata in recipients.items(): | ||
assert metadata.get('trigger_properties') == {} | ||
|
||
def test_track_user_bad_args(self): | ||
""" | ||
Tests that arguments are validated. | ||
|