From e5a38874f65c2e2988aa2918aabd41de90d16e4a Mon Sep 17 00:00:00 2001 From: Dan Date: Tue, 29 Dec 2020 18:05:13 -0500 Subject: [PATCH] A hard or soft reset should also cause a 'cancel_notify' which will kick off any CANCEL events. If this happens during a hard or soft reset, and an error is thrown trying to fulfil a cancel event, the reset should still fire. Sending emails still had a number of issues correctly parsing it's arguments. This is corrected. --- crc/api/workflow.py | 13 ++++- crc/scripts/email.py | 76 +++++++++--------------------- crc/services/workflow_processor.py | 6 +++ 3 files changed, 40 insertions(+), 55 deletions(-) diff --git a/crc/api/workflow.py b/crc/api/workflow.py index 8462d6ec..2b94986f 100644 --- a/crc/api/workflow.py +++ b/crc/api/workflow.py @@ -102,7 +102,16 @@ def get_workflow(workflow_id, soft_reset=False, hard_reset=False, do_engine_step Read Only will return the workflow in a read only state, without running any engine tasks or logging any events. """ workflow_model: WorkflowModel = session.query(WorkflowModel).filter_by(id=workflow_id).first() - processor = WorkflowProcessor(workflow_model, soft_reset=soft_reset, hard_reset=hard_reset) + processor = WorkflowProcessor(workflow_model) + if soft_reset or hard_reset: + try: + processor.cancel_notify() + except Exception as e: + raise e + finally: + # In the event of a reset, ALWAYS allow the reset, even if the cancel_notify fails for some reason. + processor = WorkflowProcessor(workflow_model, soft_reset=soft_reset, hard_reset=hard_reset) + if do_engine_steps: processor.do_engine_steps() processor.save() @@ -148,7 +157,7 @@ def set_current_task(workflow_id, task_id): "currently set to COMPLETE or READY.") # If we have an interrupt task, run it. - processor.bpmn_workflow.cancel_notify() + processor.cancel_notify() # Only reset the token if the task doesn't already have it. if spiff_task.state == spiff_task.COMPLETED: diff --git a/crc/scripts/email.py b/crc/scripts/email.py index 62b6aebc..79f5dc5e 100644 --- a/crc/scripts/email.py +++ b/crc/scripts/email.py @@ -16,12 +16,12 @@ class Email(Script): def get_description(self): return """ -Creates an email, using the provided arguments (a list of UIDs)" -Each argument will be used to look up personal information needed for -the email creation. - +Creates an email, using the provided arguments. The first argument is the subject of the email, +all subsequent arguments should be email addresses in quotes, or variables containing an email address or a list +of email addresses." +The "documentation" should contain markdown that will become the body of the email message. Example: -Email Subject ApprvlApprvr1 PIComputingID +email ("My Subject", "dhf8r@virginia.edu", pi.email) """ def do_task_validate_only(self, task, *args, **kwargs): @@ -29,15 +29,13 @@ Email Subject ApprvlApprvr1 PIComputingID self.get_email_recipients(task, args) self.get_content(task) - def do_task(self, task, *args, **kwargs): - args = [arg for arg in args if type(arg) == str or type(arg) == list] + def do_task(self, task, study_id, workflow_id, *args, **kwargs): + if len(args) < 1: + raise ApiError(code="missing_argument", + message="Email script requires a subject and at least one email address as arguments") subject = args[0] - recipients = None - try: - recipients = self.get_email_recipients(task, args) - except ApiError: - raise + recipients = self.get_email_recipients(task, args) content, content_html = self.get_content(task) if recipients: send_mail( @@ -59,58 +57,30 @@ Email Subject ApprvlApprvr1 PIComputingID def get_email_recipients(self, task, args): emails = [] - if len(args[1]) < 1: + if len(args) < 2: raise ApiError(code="missing_argument", message="Email script requires at least one email address as an argument. " "Multiple email addresses are accepted.") - if isinstance(args[1], str): - if self.check_valid_email(args[1]): - emails.append(args[1]) + # Every argument following the subject should be an email, or a list of emails. + for arg in args[1:]: + if isinstance(arg, str): + emails_to_check = [arg] + elif isinstance(arg, list): + emails_to_check = arg else: raise ApiError(code="invalid_argument", - message="The email address you provided could not be parsed. " - "The value you provided is '%s" % args[1]) + message=f"Email script requires a valid email address, but received '{arg}'") - if isinstance(args[1], list): - for address in args[1]: - if self.check_valid_email(address): - emails.append(address) + for e in emails_to_check: + if self.check_valid_email(e): + emails.append(e) else: raise ApiError(code="invalid_argument", message="The email address you provided could not be parsed. " - "The value you provided is '%s" % address) + "The value you provided is '%s" % e) - if len(emails) > 0: - return emails - else: - raise ApiError(code="invalid_argument", - message="Email script requires a valid email address.") - - # def get_users_info(self, task, args): - # if len(args) < 1: - # raise ApiError(code="missing_argument", - # message="Email script requires at least one argument. The " - # "name of the variable in the task data that contains user" - # "id to process. Multiple arguments are accepted.") - # emails = [] - # for arg in args: - # try: - # uid = task.workflow.script_engine.evaluate_expression(task, arg) - # except Exception as e: - # app.logger.error(f'Workflow engines could not parse {arg}', exc_info=True) - # continue - # user_info = LdapService.user_info(uid) - # email = user_info.email_address - # emails.append(user_info.email_address) - # if not isinstance(email, str): - # raise ApiError(code="invalid_argument", - # message="The Email script requires at least 1 UID argument. The " - # "name of the variable in the task data that contains subject and" - # " user ids to process. This must point to an array or a string, but " - # "it currently points to a %s " % emails.__class__.__name__) - # - # return emails + return emails def get_subject(self, task, args): # subject = '' diff --git a/crc/services/workflow_processor.py b/crc/services/workflow_processor.py index 624de5bc..b4fb5f5c 100644 --- a/crc/services/workflow_processor.py +++ b/crc/services/workflow_processor.py @@ -377,6 +377,12 @@ class WorkflowProcessor(object): except WorkflowTaskExecException as we: raise ApiError.from_task("task_error", str(we), we.task) + def cancel_notify(self): + try: + self.bpmn_workflow.cancel_notify() + except WorkflowTaskExecException as we: + raise ApiError.from_task("task_error", str(we), we.task) + def serialize(self): return self._serializer.serialize_workflow(self.bpmn_workflow,include_spec=True)