from internal.queries import http_query from internal.helpers import generate_schema_id, get_user_id, rds_query, get_email, make_iso_date_now from management.helpers import get_default_workspace, get_id_and_management_schema_for_alliance, has_some_role, \ add_role, \ add_alliance_management, user_and_company_for_email, email_and_company_for_user, get_roles, update_caw_meta, \ add_invitation, add_alliance_for_invitee, set_roles, remove_invitation, gen_pass, create_new_user, \ remove_alliance_for_invitee, update_alliance_updated, get_alliance_member, get_raw_alliances, add_event_log, \ get_caw_owner_id from management.commons import ROLE_OWNER, PENDING_MEMBER, PENDING_GUEST, ROLE_GUEST, ROLE_MEMBER, MANAGEMENT_ROLES, \ EVENT_LOG_CREATE_ALLIANCE, EVENT_LOG_RENAME_ALLIANCE, EVENT_LOG_JOIN_ALLIANCE, \ EVENT_LOG_IMPORT_INTO_ALLIANCE, EVENT_LOG_DELETE_DATA_FROM_ALLIANCE, EVENT_LOG_JOIN_ALLIANCE_DECLINED import logging log = logging.getLogger().getChild("management.alliance") def create_alliance(endpoint, req, event): """createAlliance(name: String!, description: String): Alliance""" try: management_schema = req['management_schema'] new_alliance_schema = generate_schema_id(event, marker='a') user_id = get_user_id(event) # create schema req['new_schema'] = new_alliance_schema req['user_id'] = user_id req['caw'] = 'alliance' req['email'] = get_email(event) http_query(endpoint, req, event) # add to commons.company_alliance add_alliance_management(management_schema, new_alliance_schema, req['name'], req['description']) # add manager user role add_role(user_id, new_alliance_schema, management_schema, ROLE_OWNER, 'alliance') add_event_log(new_alliance_schema, None, user_id, EVENT_LOG_CREATE_ALLIANCE, f"Created alliance {req['name']}") req['alliance_schema'] = new_alliance_schema req['alliance_management_schema'] = management_schema return get_alliance(endpoint, req, event) except Exception as e: log.exception("create_alliance FAILED", exc_info=e) raise RuntimeError("Alliance creation failed") def update_alliance(endpoint, req, event): alliance_schema, alliance_management_schema = req['alliance_schema'], req['alliance_management_schema'] user_id = get_user_id(event) # check for rights if has_some_role(user_id, alliance_schema, alliance_management_schema, ROLE_OWNER): update_caw_meta(alliance_schema, req) if 'name' in req and req['name'] is not None: add_event_log(alliance_schema, None, user_id, EVENT_LOG_RENAME_ALLIANCE, f"Renamed alliance to {req['name']}") else: raise RuntimeError('Access Denied') return get_alliance(endpoint, req, event) def delete_alliance(endpoint, req, event): try: user_id = get_user_id(event) alliance_schema, alliance_management_schema = req['alliance_schema'], req['alliance_management_schema'] # need to gather all the users, remove the alliances from their alliance tables if has_some_role(user_id, alliance_schema, alliance_management_schema, ROLE_OWNER): http_query(endpoint, req, event) return { '__typename': 'Alliance', 'id': req['alliance_schema'], 'status': 'deleting' } else: raise RuntimeError('Access Denied') except Exception as e: log.exception("DELETE ALLIANCE FAILED", exc_info=e) raise e def delete_alliance_collections(endpoint, req, event): try: collection_ids = [c.split('-')[-1] for c in req['collectionIds']] except Exception as e: log.exception("INVALID INPUT", exc_info=e) raise RuntimeError('input parameter contains unexpected data.') alliance_schema, alliance_management_schema = req['alliance_schema'], req['alliance_management_schema'] user_id = get_user_id(event) if not has_some_role(user_id, alliance_schema, alliance_management_schema, ROLE_OWNER, ROLE_MEMBER): raise RuntimeError('Access Denied: No management rights') # send async delete request for data, since that could take a while. dreq = {'async_source': 'appsync.deleteAllianceCollections', 'alliance_schema': alliance_schema, 'management_schema': alliance_management_schema, 'user_id': user_id, 'collection_ids': collection_ids, 'acceptedConsequences': req['acceptedConsequences'] if 'acceptedConsequences' in req else None } http_query(endpoint, dreq, event) update_alliance_updated(alliance_schema) sources_count = len(collection_ids) add_event_log(alliance_schema, None, user_id, EVENT_LOG_DELETE_DATA_FROM_ALLIANCE, f"Deleted {sources_count} data source{'s' if sources_count > 1 else ''} from alliance:") return get_alliance(endpoint, req, event) def invite_alliance_member(endpoint, req, event): """inviteAllianceMember(allianceId: ID!, userId: String!, role: String, message: String): Boolean""" user_id = get_user_id(event) invi_email = req['userId'] email = get_email(event) role = req.get('role', ROLE_GUEST) message = req.get('message', None) alliance_schema, alliance_management_schema = req['alliance_schema'], req['alliance_management_schema'] if role not in (ROLE_GUEST, ROLE_MEMBER): raise RuntimeError(f'Role must be one of: {ROLE_GUEST}, {ROLE_MEMBER}') if has_some_role(user_id, alliance_schema, alliance_management_schema, ROLE_OWNER): if email == invi_email: raise RuntimeError("No need to invite yourself.") alliance = get_alliance(endpoint, {'alliance_schema': alliance_schema, 'alliance_management_schema': alliance_management_schema}, event) alliance_name = alliance['name'] invi_user_id, invi_default_schema = user_and_company_for_email(invi_email) log.info('initial params validated') # send email email_req = { 'async_source': 'appsync.sendAllianceMemberEmail', 'template_type': 'invite_existing_user', 'inviter_email': email, 'recipient': invi_email, 'alliance_name': alliance_name, 'message': message } if invi_user_id and invi_default_schema: if has_some_role(invi_user_id, alliance_schema, alliance_management_schema, *MANAGEMENT_ROLES): raise RuntimeError("Can't invite existing alliance member again.") # registered user # send email email_req['template_type'] = 'invite_existing_user' else: temp_passwd = gen_pass(event) log.info('creating new user') invi_user_id, invi_default_schema = create_new_user(invi_email, temp_passwd) log.info(f'Created user {invi_user_id}') # send email email_req['template_type'] = 'invite_new_user' email_req['temp_passwd'] = temp_passwd log.info('database stuff') add_alliance_for_invitee(invi_default_schema, alliance_schema) add_role(invi_user_id, alliance_schema, alliance_management_schema, PENDING_MEMBER if role == ROLE_MEMBER else PENDING_GUEST, 'alliance') add_invitation(user_id, invi_email, role, invi_user_id=invi_user_id, into_alliance=alliance_schema, message=message) # send email log.info('firing off email to ') http_query(endpoint, email_req, event) return get_alliance_member(endpoint, {'memberId': invi_user_id, 'alliance_schema': alliance_schema, 'alliance_management_schema': alliance_management_schema, 'email': invi_email}, event) else: raise RuntimeError("Access Denied") def alliance_invitation_reply(endpoint, req, event): """allianceInvitationReply(allianceId: ID!, reply: Boolean): String""" user_id = get_user_id(event) reply = req.get('reply', False) default_workspace = get_default_workspace(event) alliance_schema, alliance_management_schema = req['alliance_schema'], req['alliance_management_schema'] current_roles = get_roles(user_id, alliance_schema, alliance_management_schema) pending_role = set(current_roles).intersection([PENDING_GUEST, PENDING_MEMBER]) if len(pending_role) == 1: pending_role = pending_role.pop() current_roles.remove(pending_role) if reply: # convert to real role current_roles.append(ROLE_MEMBER if pending_role == PENDING_MEMBER else ROLE_GUEST) add_event_log(alliance_schema, None, user_id, EVENT_LOG_JOIN_ALLIANCE, f"Joined alliance") else: # remove access # TODO: if many people from same company have access, check for that first. remove_alliance_for_invitee(default_workspace, alliance_schema) add_event_log(alliance_schema, None, user_id, EVENT_LOG_JOIN_ALLIANCE_DECLINED, f"Alliance join request declined") set_roles(user_id, alliance_schema, alliance_management_schema, current_roles, 'alliance') # remove invite email, _ = email_and_company_for_user(user_id) remove_invitation(email, into_alliance=alliance_schema) return "success" else: raise RuntimeError("No invitation found") def remove_alliance_member(endpoint, req, event): alliance_schema, alliance_management_schema = req['alliance_schema'], req['alliance_management_schema'] for user in rds_query(f"SELECT user_id FROM {alliance_management_schema}.user_roles WHERE user_id LIKE %(uid)s", {'uid': req['memberId'] + '%'}): member_user_id = user['user_id'] break else: member_user_id = None member_email, member_default_schema = email_and_company_for_user(member_user_id) if not (member_user_id and member_default_schema and member_email): raise RuntimeError('Unknown member') if has_some_role(member_user_id, alliance_schema, alliance_management_schema, ROLE_OWNER): raise RuntimeError("Can't remove owner of alliance") user_id = req['user_id'] if member_user_id != user_id and not has_some_role(user_id, alliance_schema, alliance_management_schema, ROLE_OWNER): raise RuntimeError('Access Denied: Not owner, can only remove yourself') # remove access for the user (done by precondition checker) # remove_alliance_for_invitee(member_default_schema, alliance_schema) # send async delete request for data, since that could take a while. req.update({ 'user_id': user_id, 'member_schema': member_default_schema, 'member_user_id': member_user_id}) http_query(endpoint, req, event) return get_alliance_member(endpoint, {'memberId': member_user_id, 'alliance_schema': alliance_schema, 'alliance_management_schema': alliance_management_schema, 'member_roles': []}, event) def get_alliance_analytics(endpoint, req, event): alliance_schema = req['alliance_schema'] collection_id = req['collectionId'] if 'collectionId' in req else None if not collection_id: res = rds_query(f"SELECT cc.id FROM {alliance_schema}.collection cc " f"WHERE cc.source = 'GLOBAL ANALYTICS' and cc.status = 'finished' ORDER BY cc.id DESC LIMIT 1") if res: collection_id = res[0]['id'] req['collectionId'] = str(collection_id) return http_query(endpoint, req, event) def get_alliances(endpoint, req, event): return [get_alliance(endpoint, { 'alliance_schema': alliance['id'], 'alliance_management_schema': alliance['management_company_id'] }, event) for alliance in get_raw_alliances(event)] def get_alliance(endpoint, req, event): alliance_schema = req['alliance_schema'] alliance_management_schema = req['alliance_management_schema'] meta_data = {meta_line['key']: meta_line['val'] for meta_line in rds_query(f"SELECT key,val FROM {alliance_schema}.meta_data " f"WHERE key IN ('name', 'description', 'created', 'creator', 'updated')")} for glob in rds_query(f"SELECT cc.id, cc.status FROM {alliance_schema}.collection cc " f"WHERE cc.source = 'GLOBAL ANALYTICS' ORDER BY cc.id DESC LIMIT 1"): status = glob['status'] break else: status = '' current_user_id = get_user_id(event) if 'creator' in meta_data: req['memberId'] = meta_data['creator'] req['alliance_owner_id'] = meta_data['creator'] return { '__typename': 'Alliance', 'id': req['alliance_schema'], 'name': meta_data.get('name', None), 'description': meta_data.get('description', None), 'status': 'pending' if has_some_role(current_user_id, alliance_schema, alliance_management_schema, PENDING_MEMBER, PENDING_GUEST) else status, 'dateCreated': meta_data.get('created', None), 'lastUpdated': meta_data.get('updated', meta_data.get('created', None)), 'createdBy': get_alliance_member(endpoint, req, event) } def get_alliance_collections(endpoint, req, event): alliance_schema, alliance_management_schema = req['alliance_schema'], req['alliance_management_schema'] aid = req['alliance_schema'] collections = rds_query(f"""WITH tree (id, name, status, source, "parentId", "dateCreated", "type", "totalProfiles") AS ( (SELECT DISTINCT C.id, name, status, source, parent_id AS "parentId", TO_CHAR(C.date_created, 'YYYY-MM-DD"T"HH24:MI:SS.US"Z"') AS "dateCreated", collection_type AS "type", (SELECT count(*) FROM {alliance_schema}.collection_fan CF WHERE CF.collection_id=C.id) AS "totalProfiles" FROM {alliance_schema}.collection C WHERE collection_type IN ('source')) ) SELECT format('{aid}-%s',id) id, name, status, "parentId" AS "parentId", "dateCreated", "type", "totalProfiles", source FROM tree""") alliance_owner_id = get_caw_owner_id(alliance_schema, alliance_management_schema) for collection in collections: member_user_id = collection['source'].split('/')[-1] collection['createdBy'] = get_alliance_member(endpoint, {'memberId': member_user_id, 'alliance_schema': alliance_schema, 'alliance_management_schema': alliance_management_schema, 'alliance_owner_id': alliance_owner_id}, event) return collections def get_alliance_segments(endpoint, req, event): alliance_schema, alliance_management_schema = req['alliance_schema'], req['alliance_management_schema'] aid = req['alliance_schema'] return rds_query(f"""WITH analytics as ( SELECT cc.id FROM {alliance_schema}.collection cc WHERE cc.source = 'GLOBAL ANALYTICS' and cc.status = 'finished' ORDER BY cc.id DESC LIMIT 1 ), tree (id, name, status, source, "parentId", "dateCreated", "type", "totalProfiles") AS ( (SELECT DISTINCT C.id, name, status, source, parent_id AS "parentId", TO_CHAR(C.date_created, 'YYYY-MM-DD"T"HH24:MI:SS.US"Z"') AS "dateCreated", collection_type AS "type", (SELECT count(*) FROM {alliance_schema}.collection_fan CF WHERE CF.collection_id=C.id) AS "totalProfiles" FROM {alliance_schema}.collection C WHERE collection_type IN ('segment') AND (parent_id IN (SELECT analytics.id FROM analytics) OR C.id IN (SELECT analytics.id FROM analytics)) ) ) SELECT format('{aid}-%s',id) id, name, status, format('{aid}-%s',"parentId") AS "parentId", "dateCreated", "type", "totalProfiles" FROM tree""") def get_alliance_members(endpoint, req, event): alliance_schema, alliance_management_schema = req['alliance_schema'], req['alliance_management_schema'] alliance_owner_id = get_caw_owner_id(alliance_schema, alliance_management_schema) return [get_alliance_member(endpoint, {'alliance_schema': alliance_schema, 'email': '(deprecated)', 'memberId': member['user_id'], 'member_roles': member['roles'], 'user_name': member['user_name'], 'company_name': member['company_name'], 'member_email': member['email'], 'alliance_owner_id': alliance_owner_id}, event) for member in rds_query(f"SELECT uc.user_id, uc.email, uc.default_schema, ur.roles, uc.user_name, " f"uc.company_name FROM commons.user_company uc " f"JOIN {alliance_management_schema}.user_roles ur ON uc.user_id = ur.user_id " f"WHERE ur.id = %(alliance_schema)s", {'alliance_schema': alliance_schema}) ] def get_alliance_totalprofiles(endpoint, req, event): alliance_schema = req['alliance_schema'] return rds_query(f"""SELECT count(*) count FROM {alliance_schema}.fan""")[0]['count'] def merge_profiles_into_alliance(endpoint, req, event): user_id = get_user_id(event) alliance_schema, alliance_management_schema = req['alliance_schema'], req['alliance_management_schema'] if has_some_role(user_id, alliance_schema, alliance_management_schema, ROLE_OWNER, ROLE_MEMBER): http_query(endpoint, req, event) update_alliance_updated(alliance_schema) sources_count = len(req['input']) if 'input' in req and req['input'] is not None else 0 add_event_log(alliance_schema, None, user_id, EVENT_LOG_IMPORT_INTO_ALLIANCE, f"Added {sources_count} data source{'s' if sources_count > 1 else ''} to alliance") return get_alliance(endpoint, {'alliance_schema': req['alliance_schema'], 'alliance_management_schema': req['alliance_management_schema']}, event) else: raise RuntimeError('Access denied.') def get_alliance_collection_event_log(endpoint, req, event): return [{ 'id': '0', 'collectionId': '0', 'status': 'test', 'timeStamp': make_iso_date_now() }] def send_alliance(endpoint, req, event): pass def check_alliance_channel_access(endpoint, req, event): get_id_and_management_schema_for_alliance(get_default_workspace(event), req['channelId'])