diff --git a/crc/services/workflow_processor.py b/crc/services/workflow_processor.py index 63113aa7..165d3313 100644 --- a/crc/services/workflow_processor.py +++ b/crc/services/workflow_processor.py @@ -33,27 +33,27 @@ class CustomBpmnScriptEngine(BpmnScriptEngine): def execute(self, task: SpiffTask, script, data): """ - Assume that the script read in from the BPMN file is a fully qualified python class. Instantiate - that class, pass in any data available to the current task so that it might act on it. - Assume that the class implements the "do_task" method. - - This allows us to reference custom code from the BPMN diagram. + Functions in two modes. + 1. If the command is proceeded by #! then this is assumed to be a python script, and will + attempt to load that python module and execute the do_task method on that script. Scripts + must be located in the scripts package and they must extend the script.py class. + 2. If not proceeded by the #! this will attempt to execute the script directly and assumes it is + valid Python. """ # Shlex splits the whole string while respecting double quoted strings within - commands = shlex.split(script) - failedOnce = False - prevError = '' - if commands[0] != '#!': + if not script.startswith('#!'): try: - super().execute(task,script,data) + super().execute(task, script, data) except SyntaxError as e: - failedOnce = True - prevError = script - app.logger.warning('We experienced a syntax error, but we are going to try the old method on ' - '"%s"'%script) + raise ApiError.from_task('syntax_error', + f'If you are running a pre-defined script, please' + f' proceed the script with "#!", otherwise this is assumed to be' + f' pure python: {script}, {e.msg}', task=task) else: - commands = commands[1:] + self.run_predefined_script(task, script[2:], data) # strip off the first two characters. + def run_predefined_script(self, task: SpiffTask, script, data): + commands = shlex.split(script) path_and_command = commands[0].rsplit(".", 1) if len(path_and_command) == 1: module_name = "crc.scripts." + self.camel_to_snake(path_and_command[0]) @@ -83,10 +83,6 @@ class CustomBpmnScriptEngine(BpmnScriptEngine): else: klass().do_task(task, study_id, workflow_id, *commands[1:]) except ModuleNotFoundError: - if failedOnce: - raise ApiError.from_task("invalid_script", - "Script had a syntax error: '%s'" % (prevError), - task=task) raise ApiError.from_task("invalid_script", "Unable to locate Script: '%s:%s'" % (module_name, class_name), task=task) diff --git a/crc/static/bpmn/research_rampup/research_rampup.bpmn b/crc/static/bpmn/research_rampup/research_rampup.bpmn index 5a4bb1bc..4a04eb6d 100644 --- a/crc/static/bpmn/research_rampup/research_rampup.bpmn +++ b/crc/static/bpmn/research_rampup/research_rampup.bpmn @@ -755,7 +755,7 @@ Notify the Area Monitor for This step is internal to the system and do not require and user interaction Flow_0j4rs82 Flow_07ge8uf - RequestApproval ApprvlApprvr1 ApprvlApprvr2 + #!RequestApproval ApprvlApprvr1 ApprvlApprvr2 #### Script Task diff --git a/tests/workflow/test_workflow_spec_validation_api.py b/tests/workflow/test_workflow_spec_validation_api.py index 597c4aa1..00406f5b 100644 --- a/tests/workflow/test_workflow_spec_validation_api.py +++ b/tests/workflow/test_workflow_spec_validation_api.py @@ -66,7 +66,6 @@ class TestWorkflowSpecValidation(BaseTest): errors.extend(ApiErrorSchema(many=True).load(json_data)) self.assertEqual(0, len(errors), json.dumps(errors)) - def test_invalid_expression(self): self.load_example_data() errors = self.validate_workflow("invalid_expression") @@ -103,12 +102,10 @@ class TestWorkflowSpecValidation(BaseTest): errors = self.validate_workflow("invalid_script2") self.assertEqual(2, len(errors)) self.assertEqual("error_loading_workflow", errors[0]['code']) - self.assertTrue("syntax error" in errors[0]['message']) self.assertEqual("Invalid_Script_Task", errors[0]['task_id']) self.assertEqual("An Invalid Script Reference", errors[0]['task_name']) self.assertEqual("invalid_script2.bpmn", errors[0]['file_name']) - def test_repeating_sections_correctly_populated(self): self.load_example_data() spec_model = self.load_test_spec('repeat_form')