"""Lambda sr-fingerprinting-rules-migrator function module.""" import base64 import json import os import sentry_sdk from sentry_sdk.integrations.aws_lambda import AwsLambdaIntegration from src.connectors import ows_sound_recordings from .common import logger import config # initialize sentry sentry_dsn = os.environ.get( 'SENTRY_DSN', config.secrets_manager_client.get_cred('SENTRY_DSN')) if sentry_dsn: logger.info('Initializing with sentry') sentry_sdk.init( sentry_dsn, integrations=[AwsLambdaIntegration()] ) else: logger.info('Initializing without sentry') ALL_TERRITORIES = [ 'AD', 'AE', 'AF', 'AG', 'AI', 'AL', 'AM', 'AO', 'AQ', 'AR', 'AS', 'AT', 'AU', 'AW', 'AX', 'AZ', 'BA', 'BB', 'BD', 'BE', 'BF', 'BG', 'BH', 'BI', 'BJ', 'BL', 'BM', 'BN', 'BO', 'BQ', 'BR', 'BS', 'BT', 'BV', 'BW', 'BY', 'BZ', 'CA', 'CC', 'CD', 'CF', 'CG', 'CH', 'CI', 'CK', 'CL', 'CM', 'CN', 'CO', 'CR', 'CU', 'CV', 'CW', 'CX', 'CY', 'CZ', 'DE', 'DJ', 'DK', 'DM', 'DO', 'DZ', 'EC', 'EE', 'EG', 'EH', 'ER', 'ES', 'ET', 'FI', 'FJ', 'FK', 'FM', 'FO', 'FR', 'GA', 'GB', 'GD', 'GE', 'GF', 'GG', 'GH', 'GI', 'GL', 'GM', 'GN', 'GP', 'GQ', 'GR', 'GS', 'GT', 'GU', 'GW', 'GY', 'HK', 'HM', 'HN', 'HR', 'HT', 'HU', 'ID', 'IE', 'IL', 'IM', 'IN', 'IO', 'IQ', 'IR', 'IS', 'IT', 'JE', 'JM', 'JO', 'JP', 'KE', 'KG', 'KH', 'KI', 'KM', 'KN', 'KP', 'KR', 'KW', 'KY', 'KZ', 'LA', 'LB', 'LC', 'LI', 'LK', 'LR', 'LS', 'LT', 'LU', 'LV', 'LY', 'MA', 'MC', 'MD', 'ME', 'MF', 'MG', 'MH', 'MK', 'ML', 'MM', 'MN', 'MO', 'MP', 'MQ', 'MR', 'MS', 'MT', 'MU', 'MV', 'MW', 'MX', 'MY', 'MZ', 'NA', 'NC', 'NE', 'NF', 'NG', 'NI', 'NL', 'NO', 'NP', 'NR', 'NU', 'NZ', 'OM', 'PA', 'PE', 'PF', 'PG', 'PH', 'PK', 'PL', 'PM', 'PN', 'PR', 'PS', 'PT', 'PW', 'PY', 'QA', 'RE', 'RO', 'RS', 'RU', 'RW', 'SA', 'SB', 'SC', 'SD', 'SE', 'SG', 'SH', 'SI', 'SJ', 'SK', 'SL', 'SM', 'SN', 'SO', 'SR', 'SS', 'ST', 'SV', 'SX', 'SY', 'SZ', 'TC', 'TD', 'TF', 'TG', 'TH', 'TJ', 'TK', 'TL', 'TM', 'TN', 'TO', 'TR', 'TT', 'TV', 'TW', 'TZ', 'UA', 'UG', 'UM', 'US', 'UY', 'UZ', 'VA', 'VC', 'VE', 'VG', 'VI', 'VN', 'VU', 'WF', 'WS', 'YE', 'YT', 'ZA', 'ZM', 'ZW'] class Event: """Struct for parsed event data.""" def __init__(self, obj_type, obj_id, star_carveout, territoy_carveouts, start_date, service): """Set up Struct.""" self.obj_type = obj_type self.obj_id = obj_id self.star_carveout = star_carveout self.territory_carveouts = territoy_carveouts self.start_date = start_date self.service = service def _decode_records(records): # extract all records from event extracted_records = sum( [v for _, v in records['records'].items()], [] ) object_updates = [] for record in extracted_records: event_value = json.loads(base64.b64decode( record['value']).decode('utf-8')) key = event_value.get('payload').get('KEY') id_type = key.split(':')[0] obj_type = '' if id_type == 'vendor_id': obj_type = 'vendors' elif id_type == 'subaccount_id': obj_type = 'subaccounts' elif id_type == 'tuid': obj_type = 'tracks' else: raise Exception(f'Unhandled key "{key}"') obj_id = key.split(':')[1] object_update_events = [] values = json.loads(event_value.get('payload').get('VALUE')) for value in values: territory_carveouts = [] star_carveout = False start_date = None if 'start_date' in value: start_date = value['start_date'] service = value['service'] if 'service' in value else 'tiktok' star_carveout = value['star_carveout'] if value['territory_carveouts'] != '': territory_carveouts = \ value['territory_carveouts'].rstrip(',').split(',') object_update_events.append(Event( obj_type, obj_id, star_carveout, territory_carveouts, start_date, service )) object_updates.append({ 'obj_id': obj_id, 'obj_type': obj_type, 'events': object_update_events }) return object_updates def _carveouts_to_rules(territory_carveouts): res = [i for i in ALL_TERRITORIES if i not in territory_carveouts] return res def _transform_event_to_ows_sound_recordings_call(obj_type, object_updates): batched_updates = [] for object_update in object_updates: obj_id = object_update['obj_id'] transformed_rules = [] for event in object_update['events']: start_date = event.start_date service = event.service territory_carveouts = event.territory_carveouts star_carveout = event.star_carveout if (obj_type == 'vendors'): if (star_carveout): # Delete vendor monetization rules transformed_rules.append({ 'territories': [], 'policy': 'monetize', 'start': start_date, 'service': service }) elif (not star_carveout and len(territory_carveouts) == 0): # Add all territory monetization rule transformed_rules.append({ 'territories': ALL_TERRITORIES, 'policy': 'monetize', 'start': start_date, 'service': service }) elif (not star_carveout and (len(territory_carveouts) > 0)): # Sync vendor rules (inverse of carveouts) transformed_rules.append({ 'territories': _carveouts_to_rules( territory_carveouts), 'policy': 'monetize', 'start': start_date, 'service': service }) transformed_rules.append({ 'territories': territory_carveouts, 'policy': 'carveout', 'start': None, 'service': service }) else: # Subaccount / Track level rules if star_carveout: # Carveout everything transformed_rules.append({ 'territories': ALL_TERRITORIES, 'policy': 'carveout', 'start': None, 'service': service }) else: # Sync carveouts transformed_rules.append({ 'territories': territory_carveouts, 'policy': 'carveout', 'start': None, 'service': service }) batched_updates.append({ 'obj_id': obj_id, 'rules': transformed_rules }) return batched_updates def _batch_process_events(obj_type, object_updates): try: return ows_sound_recordings.post_rule( obj_type, _transform_event_to_ows_sound_recordings_call( obj_type, object_updates) ) except Exception as e: # Log exception and immediately continue with next rule logger.exception(str(e)) return { 'status': 'error', 'obj_type': obj_type, 'error': str(e) } def handler(raw_records, context): """Lambda entry point.""" try: object_updates = _decode_records(raw_records) except Exception as e: logger.exception(str(e)) raise e vendor_updates = [] subaccount_updates = [] track_updates = [] for object_update in object_updates: obj_type = object_update['obj_type'] if obj_type == 'vendors': vendor_updates.append(object_update) elif obj_type == 'subaccounts': subaccount_updates.append(object_update) elif obj_type == 'tracks': track_updates.append(object_update) batch_results = [] if vendor_updates: batch_results.append( _batch_process_events('vendors', vendor_updates)) if subaccount_updates: batch_results.append( _batch_process_events('subaccounts', subaccount_updates)) if track_updates: batch_results.append( _batch_process_events('tracks', track_updates)) return batch_results