def main(): t = Terminal() payload_str = get_env_var('EVENT_PAYLOAD') print(f'{t.cyan}Starting the slack notifier{t.normal}') payload = json.loads(payload_str) pr_number = payload.get('pull_request', {}).get('number') # Get the PR information in order to get information like metadata org_name = 'demisto' repo_name = 'content' gh = Github(get_env_var('CONTENTBOT_GH_ADMIN_TOKEN'), verify=False) content_repo = gh.get_repo(f'{org_name}/{repo_name}') pr = content_repo.get_pull(pr_number) metadata_files = [file for file in pr.get_files() if file.filename.endswith('_metadata.json')] # Build all blocks of the message header = create_pr_title(pr) pull_request_segment = create_pull_request_segment(pr) packs_segment = create_packs_segment(metadata_files) blocks = header + pull_request_segment + packs_segment print(f'{t.yellow}Finished preparing message: \n{pformat(blocks)}{t.normal}') # Send message slack_token = get_env_var('CORTEX_XSOAR_SLACK_TOKEN') client = WebClient(token=slack_token) slack_post_message(client, blocks, pr) print(f'{t.cyan}Slack message sent successfully{t.normal}')
def test_no_env_var(self): """ Scenario: Try getting an environment variable Given - Using the 'get_env_var' function When - The environment variable does not exist - No 'default_val' argument was passed when the function was called Then - Ensure a 'EnvVariableError' exception is raised """ with pytest.raises(EnvVariableError): get_env_var('MADE_UP_ENV_VARIABLE')
def test_empty_env_var(self, monkeypatch): """ Scenario: Try getting an environment variable Given - Using the 'get_env_var' function When - The environment variable's value is an empty string - No 'default_val' argument was passed when the function was called Then - Ensure a 'EnvVariableError' exception is raised """ monkeypatch.setenv('MADE_UP_ENV_VARIABLE', '') with pytest.raises(EnvVariableError): get_env_var('MADE_UP_ENV_VARIABLE')
def test_existing_env_var(self, monkeypatch): """ Scenario: Try getting an environment variable Given - Using the 'get_env_var' function When - The environment variable's value is 'LEROY JENKINS' - No 'default_val' argument was passed when the function was called Then - Ensure 'LEROY JENKINS' is returned from the function """ monkeypatch.setenv('MADE_UP_ENV_VARIABLE', 'LEROY JENKINS') env_var_val = get_env_var('MADE_UP_ENV_VARIABLE') assert env_var_val == 'LEROY JENKINS'
def test_no_env_var_with_default(self): """ Scenario: Try getting an environment variable Given - Using the 'get_env_var' function When - The environment variable does not exist - The 'default_val' argument was passed with a value of 'TIMOTHY' Then - Ensure 'TIMOTHY' is returned from the function """ default_val = 'TIMOTHY' env_var_val = get_env_var('MADE_UP_ENV_VARIABLE', default_val) assert env_var_val == default_val
def test_existing_env_var_with_default(self, monkeypatch): """ Scenario: Try getting an environment variable Given - Using the 'get_env_var' function When - The environment variable's value is 'LEROY JENKINS' - The 'default_val' argument was passed with a value of 'TIMOTHY' Then - Ensure 'LEROY JENKINS' is returned from the function """ monkeypatch.setenv('MADE_UP_ENV_VARIABLE', 'LEROY JENKINS') default_val = 'TIMOTHY' env_var_val = get_env_var('MADE_UP_ENV_VARIABLE', default_val) assert env_var_val == 'LEROY JENKINS'
def test_env_vars(): """test if the critical env variables are available in the environment""" CELERY_BROKER_URL = get_env_var('CELERY_BROKER_URL') DB_URL = get_env_var('SQLALCHEMY_DATABASE_URI') DB_URL_TEST = get_env_var('SQLALCHEMY_DATABASE_URI_TEST') SECRET_KEY = get_env_var('MPORTER_SECRET') MAILGUN_KEY = get_env_var('MAILGUN_KEY') MAILGUN_SANDBOX = get_env_var('MAILGUN_SANDBOX') assert CELERY_BROKER_URL is not None assert DB_URL is not None assert DB_URL_TEST is not None assert SECRET_KEY is not None assert MAILGUN_KEY is not None assert MAILGUN_SANDBOX is not None
def update_handler(event, context): """ Save Original Email and Update Email Content Using WorkMail Lambda Integration Parameters ---------- email_summary: dict, required Amazon WorkMail Message Summary Input Format For more information, see https://docs.aws.amazon.com/workmail/latest/adminguide/lambda.html { "summaryVersion": "2019-07-28", # AWS WorkMail Message Summary Version "envelope": { "mailFrom" : { "address" : "*****@*****.**" # String containing from email address }, "recipients" : [ # List of all recipient email addresses { "address" : "*****@*****.**" }, { "address" : "*****@*****.**" } ] }, "sender" : { "address" : "*****@*****.**" # String containing sender email address }, "subject" : "Hello From Amazon WorkMail!", # String containing email subject (Truncated to first 256 chars)" "messageId": "00000000-0000-0000-0000-000000000000", # String containing message id for retrieval using workmail flow API "invocationId": "00000000000000000000000000000000", # String containing the id of this lambda invocation. Useful for detecting retries and avoiding duplication "flowDirection": "INBOUND", # String indicating direction of email flow. Value is either "INBOUND" or "OUTBOUND" "truncated": false # boolean indicating if any field in message was truncated due to size limitations } context: object, required Lambda Context runtime methods and attributes. See https://docs.aws.amazon.com/lambda/latest/dg/python-context-object.html Returns ------- Amazon WorkMail Sync Lambda Response Format For more information, see https://docs.aws.amazon.com/workmail/latest/adminguide/lambda.html#synchronous-schema return { 'actions': [ # Required, should contain at least 1 list element { 'action' : { # Required 'type': 'string', # Required. Can be "BOUNCE", "DROP" or "DEFAULT" 'parameters': { <various> } # Optional. For bounce, <various> can be {"bounceMessage": "message that goes in bounce mail"} }, 'recipients': list of strings, # Optional. Indicates list of recipients for which this action applies 'default': boolean # Optional. Indicates whether this action applies to all recipients } ]} """ logger.info(f"Received event: {event}") email_from = event['envelope']['mailFrom'] recipients = event['envelope']['recipients'] message_id = event['messageId'] key = str(uuid.uuid4()) # Determine if the message is internal if utils.extract_domains([email_from ]) == utils.extract_domains(recipients): internal_message = True else: internal_message = False update_internal_msg = ( utils.get_env_var('UPDATE_INTERNAL_MESSAGES') == 'True') update_external_msg = ( utils.get_env_var('UPDATE_EXTERNAL_MESSAGES') == 'True') save_and_update_msg = False if internal_message: if update_internal_msg: save_and_update_msg = True else: if update_external_msg: save_and_update_msg = True try: # 1. Download email downloaded_email = utils.download_email(message_id) # 2. Save and update original email if save_and_update_msg: saved_bucket = utils.get_env_var('SAVED_EMAIL_BUCKET') updated_bucket = utils.get_env_var('UPDATED_EMAIL_BUCKET') # 3. Save the orginal, unmodified, email message source utils.save_email(saved_bucket, downloaded_email.as_bytes(), key + ".eml") # 4. Save the event data (metadata) about the message so we know the envelope details that aren't in the message source utils.save_email(saved_bucket, json.dumps(event), key + ".json") # 5. Update the email with the desired modifications updated_email = utils.update_email(downloaded_email, event['subject'], event['flowDirection'], key) logger.info("Providing modified message for WorkMail") utils.update_workmail(message_id, updated_bucket, updated_email, key) else: logger.info("Preserving original message for WorkMail") except ClientError as e: if e.response['Error']['Code'] == 'MessageFrozen': # Redirect emails are not eligible for update, handle it gracefully. logger.info( f"Message {message_id} is not eligible for update. This is usually the case for a redirected email" ) else: logger.error(e.response['Error']['Message']) if e.response['Error']['Code'] == 'ResourceNotFoundException': logger.error( f"Message {message_id} does not exist. Messages in transit are no longer accessible after 1 day" ) elif e.response['Error']['Code'] == 'InvalidContentLocation': logger.error( 'WorkMail could not access the updated email content. See https://docs.aws.amazon.com/workmail/latest/adminguide/update-with-lambda.html' ) raise (e) # Resume normal email flow return { 'actions': [{ 'action': { 'type': 'DEFAULT' }, 'allRecipients': 'true' }] }
def main(): """Handles External PRs (PRs from forks) Performs the following operations: 1. If the external PR's base branch is master we create a new branch and set it as the base branch of the PR. 2. Labels the PR with the "Contribution" label. (Adds the "Hackathon" label where applicable.) 3. Assigns a Reviewer. 4. Creates a welcome comment Will use the following env vars: - CONTENTBOT_GH_ADMIN_TOKEN: token to use to update the PR - EVENT_PAYLOAD: json data from the pull_request event """ t = Terminal() payload_str = get_env_var('EVENT_PAYLOAD') if not payload_str: raise ValueError('EVENT_PAYLOAD env variable not set or empty') payload = json.loads(payload_str) print(f'{t.cyan}Processing PR started{t.normal}') org_name = 'demisto' repo_name = 'content' gh = Github(get_env_var('CONTENTBOT_GH_ADMIN_TOKEN'), verify=False) content_repo = gh.get_repo(f'{org_name}/{repo_name}') pr_number = payload.get('pull_request', {}).get('number') pr = content_repo.get_pull(pr_number) # Add 'Contribution' Label to PR contribution_label = 'Contribution' pr.add_to_labels(contribution_label) print(f'{t.cyan}Added "Contribution" label to the PR{t.normal}') # check base branch is master if pr.base.ref == 'master': print(f'{t.cyan}Determining name for new base branch{t.normal}') branch_prefix = 'contrib/' new_branch_name = f'{branch_prefix}{pr.head.label.replace(":", "_")}' existant_branches = content_repo.get_git_matching_refs( f'heads/{branch_prefix}') potential_conflicting_branch_names = [ branch.ref.lstrip('refs/heads/') for branch in existant_branches ] # make sure new branch name does not conflict with existing branch name while new_branch_name in potential_conflicting_branch_names: # append or increment digit if not new_branch_name[-1].isdigit(): new_branch_name += '-1' else: digit = str(int(new_branch_name[-1]) + 1) new_branch_name = f'{new_branch_name[:-1]}{digit}' master_branch_commit_sha = content_repo.get_branch('master').commit.sha # create new branch print(f'{t.cyan}Creating new branch "{new_branch_name}"{t.normal}') content_repo.create_git_ref(f'refs/heads/{new_branch_name}', master_branch_commit_sha) # update base branch of the PR pr.edit(base=new_branch_name) print( f'{t.cyan}Updated base branch of PR "{pr_number}" to "{new_branch_name}"{t.normal}' ) # assign reviewers / request review from reviewer_to_assign = determine_reviewer(REVIEWERS, content_repo) pr.add_to_assignees(reviewer_to_assign) pr.create_review_request(reviewers=[reviewer_to_assign]) print(f'{t.cyan}Assigned user "{reviewer_to_assign}" to the PR{t.normal}') print( f'{t.cyan}Requested review from user "{reviewer_to_assign}"{t.normal}') # create welcome comment body = WELCOME_MSG.format(selected_reviewer=reviewer_to_assign) pr.create_issue_comment(body) print(f'{t.cyan}Created welcome comment{t.normal}')
# 'AR', # 'PH', # 'CO', # 'MY', # 'VE', # 'TH', # 'PK', # ] country_code = args.country_code print('Country:', country_code) # Choose Number of Nodes To Distribute Credentials: e.g. jobarray=0-4, cpu_per_task=20, credentials = 90 (<100) SLURM_JOB_ID = get_env_var('SLURM_JOB_ID',0) SLURM_ARRAY_TASK_ID = get_env_var('SLURM_ARRAY_TASK_ID',0) SLURM_ARRAY_TASK_COUNT = get_env_var('SLURM_ARRAY_TASK_COUNT',1) SLURM_JOB_CPUS_PER_NODE = get_env_var('SLURM_JOB_CPUS_PER_NODE',mp.cpu_count()) # + if 'samuel' in socket.gethostname().lower(): path_to_data='../../data' else: path_to_data='/scratch/spf248/twitter/data' path_to_keys = os.path.join(path_to_data,'keys','twitter') path_to_users = os.path.join(path_to_data,'users') path_to_locations = os.path.join(path_to_data,'locations','profiles') path_to_friends = os.path.join(path_to_data,'friends','API',country_code) os.makedirs(path_to_friends, exist_ok=True)
from utils import get_env_var SEND_MAIL_HOUR = 8 CELERY_BROKER_URL = get_env_var('RABBITMQ_BIGWIG_URL') DB_URL = get_env_var('DATABASE_URL') DB_URL_TEST = get_env_var('HEROKU_POSTGRESQL_GRAY_URL') SECRET_KEY = get_env_var('MPORTER_SECRET') MAILGUN_KEY = get_env_var('MAILGUN_API_KEY') MAILGUN_SANDBOX = get_env_var('MAILGUN_DOMAIN') MAILGUN_URL = 'https://api.mailgun.net/v2/{0}/messages'.format(MAILGUN_SANDBOX) MAILGUN_TESTMAIL_ADDR = 'postmaster@{}'.format(MAILGUN_SANDBOX)
def main(): """Creates Internal PRs from Merged External PRs Performs the following operations: 1. Creates new PR. A) Uses body of merged external PR as the body of the new PR. B) Uses base branch of merged external PR as head branch of the new PR to master. C) Adds 'docs-approved' label if it was on the merged external PR. D) Requests review from the same users as on the merged external PR. E) Assigns the same users as on the merged external PR. Will use the following env vars: - CONTENTBOT_GH_ADMIN_TOKEN: token to use to update the PR - EVENT_PAYLOAD: json data from the pull_request event """ t = Terminal() payload_str = get_env_var('EVENT_PAYLOAD') if not payload_str: raise ValueError('EVENT_PAYLOAD env variable not set or empty') payload = json.loads(payload_str) print(f'{t.cyan}Creation of Internal PR started{t.normal}') org_name = 'demisto' repo_name = 'content' gh = Github(get_env_var('CONTENTBOT_GH_ADMIN_TOKEN'), verify=False) content_repo = gh.get_repo(f'{org_name}/{repo_name}') pr_number = payload.get('pull_request', {}).get('number') merged_pr = content_repo.get_pull(pr_number) merged_pr_url = merged_pr.html_url body = f'## Original External PR\r\n[external pull request]({merged_pr_url})\r\n\r\n' title = merged_pr.title body += merged_pr.body base_branch = 'master' head_branch = merged_pr.base.ref pr = content_repo.create_pull(title=title, body=body, base=base_branch, head=head_branch, draft=False) print(f'{t.cyan}Internal PR Created - {pr.html_url}{t.normal}') labels = [label.name for label in merged_pr.labels] docs_approved_label = 'docs-approved' if docs_approved_label in labels: pr.add_to_labels(docs_approved_label) print(f'{t.cyan}"docs-approved" label added{t.normal}') merged_by = merged_pr.merged_by.login reviewers, _ = merged_pr.get_review_requests() reviewers_logins = [reviewer.login for reviewer in reviewers] # request reviews from the same people as in the merged PR new_pr_reviewers = [merged_by] if merged_by else reviewers_logins pr.create_review_request(reviewers=new_pr_reviewers) print(f'{t.cyan}Requested review from {new_pr_reviewers}{t.normal}') # assign same users as in the merged PR assignees = [assignee.login for assignee in merged_pr.assignees] pr.add_to_assignees(*assignees) print(f'{t.cyan}Assigned users {assignees}{t.normal}') # remove branch protections print(f'{t.cyan}Removing protection from branch "{head_branch}"{t.normal}') contrib_branch = content_repo.get_branch(head_branch) contrib_branch.remove_protection() contrib_branch.remove_required_status_checks() contrib_branch.remove_required_pull_request_reviews()
from os import environ from utils import get_env_var import asyncio from crawler import Crawler if __name__ == '__main__': try: token = environ['API_KEY'] except KeyError: exit(1) pg_user = get_env_var('POSTGRES_USER', 'postgres') pg_password = get_env_var('POSTGRES_PASSWORD', '') pg_database = get_env_var('POSTGRES_DB', 'postgres') pg_host = get_env_var('PGHOST', 'localhost') crawler = Crawler(token, pg_user=pg_user, pg_password=pg_password, pg_database=pg_database, pg_host=pg_host) print('Кроулер запущен') asyncio.get_event_loop().run_until_complete(crawler.run())
def restricted_mailboxes_handler(email_summary, context): """ Restricted Mailboxes for Amazon WorkMail Parameters ---------- email_summary: dict, required Amazon WorkMail Message Summary Input Format For more information, see https://docs.aws.amazon.com/workmail/latest/adminguide/lambda.html { "summaryVersion": "2019-07-28", # AWS WorkMail Message Summary Version "envelope": { "mailFrom" : { "address" : "*****@*****.**" # String containing from email address }, "recipients" : [ # List of all recipient email addresses { "address" : "*****@*****.**" }, { "address" : "*****@*****.**" } ] }, "sender" : { "address" : "*****@*****.**" # String containing sender email address }, "subject" : "Hello From Amazon WorkMail!", # String containing email subject (Truncated to first 256 chars)" "messageId": "00000000-0000-0000-0000-000000000000", # String containing message id for retrieval using workmail flow API "invocationId": "00000000000000000000000000000000", # String containing the id of this lambda invocation. Useful for detecting retries and avoiding duplication "flowDirection": "INBOUND", # String indicating direction of email flow. Value is either "INBOUND" or "OUTBOUND" "truncated": false # boolean indicating if any field in message was truncated due to size limitations } context: object, required Lambda Context runtime methods and attributes Attributes ---------- context.aws_request_id: str Lambda request ID context.client_context: object Additional context when invoked through AWS Mobile SDK context.function_name: str Lambda function name context.function_version: str Function version identifier context.get_remaining_time_in_millis: function Time in milliseconds before function times out context.identity: Cognito identity provider context when invoked through AWS Mobile SDK context.invoked_function_arn: str Function ARN context.log_group_name: str Cloudwatch Log group name context.log_stream_name: str Cloudwatch Log stream name context.memory_limit_in_mb: int Function memory https://docs.aws.amazon.com/lambda/latest/dg/python-context-object.html Returns ------- Amazon WorkMail Sync Lambda Response Format For more information, see https://docs.aws.amazon.com/workmail/latest/adminguide/lambda.html#synchronous-schema return { 'actions': [ # Required, should contain at least 1 list element { 'action' : { # Required 'type': 'string', # Required. Can be "BOUNCE", "DROP" or "DEFAULT" 'parameters': { <various> } # Optional. For bounce, <various> can be {"bounceMessage": "message that goes in bounce mail"} }, 'recipients': list of strings, # Optional. Indicates list of recipients for which this action applies 'default': boolean # Optional. Indicates whether this action applies to all recipients } ]} """ logger.info(email_summary) organization_id = utils.get_env_var('WORKMAIL_ORGANIZATION_ID') restricted_group = utils.get_env_var('RESTRICTED_GROUP_NAME') report_mailbox_address = os.getenv('REPORT_MAILBOX_ADDRESS') sender = email_summary['envelope']['mailFrom'] recipients = email_summary['envelope']['recipients'] flow_direction = email_summary['flowDirection'] if flow_direction == 'INBOUND': # 1. First check if sender is an external email address is_sender_external = utils.filter_external([sender], organization_id) is not None if is_sender_external: # 2. Then filter out restricted recipients a.k.a the ones that have restricted mailboxes from all email recipients restricted_recipients = utils.filter_restricted(recipients, organization_id, restricted_group) if restricted_recipients: # 3. Bounce this email for all restricted recipients and allow for the rest logger.info(f"Received email from external source {sender} to restricted mailboxes {restricted_recipients}; bouncing! ") additional_recipients = [] # Tip: You may add any additional recipients you would like to send copy of this email if report_mailbox_address: additional_recipients.append(report_mailbox_address) return { 'actions': [ { 'recipients': restricted_recipients, # Bounce this email for restricted_recipients 'action' : { 'type': 'BOUNCE' } }, { 'recipients': additional_recipients, # For any additional recipients and; 'allRecipients': True, # for all the remaining recipients (i.e. except the ones in bounce action) 'action' : { 'type': 'DEFAULT' } # let the email be sent normally } ]} elif flow_direction == 'OUTBOUND': # 1. First check if sender is restricted a.k.a sender has an restricted mailbox is_sender_restricted = utils.filter_restricted([sender], organization_id, restricted_group) is not None if is_sender_restricted: # 2. Then filter out external email addresses from all email recipients external_recipients = utils.filter_external(recipients, organization_id) if external_recipients: # 3. Finally bounce this email for all external recipients and allow for the rest logger.info(f"Restricted mailbox {sender} attempted to send to external recipient {external_recipients}; bouncing!") additional_recipients = [] # Tip: You may add any additional recipients you would like to send copy of this email if report_mailbox_address: additional_recipients.append(report_mailbox_address) return { 'actions': [ { 'recipients': external_recipients, # Bounce this email for external recipients 'action' : { 'type': 'BOUNCE', 'parameters': { 'bounceMessage': "Sending e-mails to external domains is against company policy." } }, }, { 'recipients': additional_recipients, # For any additional recipients and; 'allRecipients': True, # for all the remaining recipients (i.e. except the in bounce action) 'action' : { 'type': 'DEFAULT' } # let the email be sent normally } ]} else: error_msg = f"Received invalid flow direction:{flow_direction} in message summary" logger.error(error_msg) return { 'actions': [ { 'allRecipients': True, # For all recipients 'action' : { 'type' : 'DEFAULT' } # let the email be sent normally } ]}
def test_existed_var(self): """This function tests if the config file for the translation server exists""" self.assertIsInstance(utils.get_env_var("FLASKKEY"), str, "get_env_var() could not find a flask Key")
def translate_handler(event, context): """ Translate Email Content Using WorkMail Lambda Integration Parameters ---------- email_summary: dict, required Amazon WorkMail Message Summary Input Format For more information, see https://docs.aws.amazon.com/workmail/latest/adminguide/lambda.html { "summaryVersion": "2019-07-28", # AWS WorkMail Message Summary Version "envelope": { "mailFrom" : { "address" : "*****@*****.**" # String containing from email address }, "recipients" : [ # List of all recipient email addresses { "address" : "*****@*****.**" }, { "address" : "*****@*****.**" } ] }, "sender" : { "address" : "*****@*****.**" # String containing sender email address }, "subject" : "Hello From Amazon WorkMail!", # String containing email subject (Truncated to first 256 chars)" "messageId": "00000000-0000-0000-0000-000000000000", # String containing message id for retrieval using workmail flow API "invocationId": "00000000000000000000000000000000", # String containing the id of this lambda invocation. Useful for detecting retries and avoiding duplication "flowDirection": "INBOUND", # String indicating direction of email flow. Value is either "INBOUND" or "OUTBOUND" "truncated": false # boolean indicating if any field in message was truncated due to size limitations } context: object, required Lambda Context runtime methods and attributes. See https://docs.aws.amazon.com/lambda/latest/dg/python-context-object.html Returns ------- Amazon WorkMail Sync Lambda Response Format For more information, see https://docs.aws.amazon.com/workmail/latest/adminguide/lambda.html#synchronous-schema return { 'actions': [ # Required, should contain at least 1 list element { 'action' : { # Required 'type': 'string', # Required. Can be "BOUNCE", "DROP" or "DEFAULT" 'parameters': { <various> } # Optional. For bounce, <various> can be {"bounceMessage": "message that goes in bounce mail"} }, 'recipients': list of strings, # Optional. Indicates list of recipients for which this action applies 'default': boolean # Optional. Indicates whether this action applies to all recipients } ]} """ logger.info(f"Received event: {event}") message_id = event['messageId'] try: # 1. Download email downloaded_email = utils.download_email(message_id) # 2. Detect email language text_body = utils.extract_text_body(downloaded_email) # Use first 100 characters of email body and email subject to detect email source language email_language = translate_helper.detect_language( f"{event['subject']} {text_body[:100]}") if email_language != utils.get_env_var('DESTINATION_LANGUAGE'): # 3. Translate email translated_email = utils.translate_email(downloaded_email, event['subject'], email_language, text_body) # 4. Send translated email back to WorkMail utils.update_workmail(message_id, translated_email) else: logger.info('Email is already in destination language') except ClientError as e: if e.response['Error']['Code'] == 'MessageFrozen': # Redirect emails are not eligible for update, handle it gracefully. logger.info( f"Message {message_id} is not eligible for update. This is usually the case for a redirected email" ) else: logger.error(e.response['Error']['Message']) if e.response['Error']['Code'] == 'ResourceNotFoundException': logger.error( f"Message {message_id} does not exist. Messages in transit are no longer accessible after 1 day" ) elif e.response['Error']['Code'] == 'InvalidContentLocation': logger.error( 'WorkMail could not access the updated email content. See https://docs.aws.amazon.com/workmail/latest/adminguide/update-with-lambda.html' ) raise (e) # Resume normal email flow return { 'actions': [{ 'action': { 'type': 'DEFAULT' }, 'allRecipients': 'true' }] }