diff --git a/poetry.lock b/poetry.lock index 4566e5a5..0fd209b3 100644 --- a/poetry.lock +++ b/poetry.lock @@ -95,7 +95,7 @@ python-versions = ">=3.5" dev = ["cloudpickle", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy (>=0.900,!=0.940)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "sphinx", "sphinx-notfound-page", "zope.interface"] docs = ["furo", "sphinx", "sphinx-notfound-page", "zope.interface"] tests = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "zope.interface"] -tests_no_zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins"] +tests-no-zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy (>=0.900,!=0.940)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins"] [[package]] name = "Babel" @@ -268,7 +268,7 @@ optional = false python-versions = ">=3.6.0" [package.extras] -unicode_backport = ["unicodedata2"] +unicode-backport = ["unicodedata2"] [[package]] name = "classify-imports" @@ -1512,7 +1512,7 @@ urllib3 = ">=1.21.1,<1.27" [package.extras] socks = ["PySocks (>=1.5.6,!=1.5.7)"] -use_chardet_on_py3 = ["chardet (>=3.0.2,<6)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "requests-toolbelt" @@ -1625,7 +1625,7 @@ falcon = ["falcon (>=1.4)"] fastapi = ["fastapi (>=0.79.0)"] flask = ["blinker (>=1.1)", "flask (>=0.11)"] httpx = ["httpx (>=0.16.0)"] -pure_eval = ["asttokens", "executing", "pure-eval"] +pure-eval = ["asttokens", "executing", "pure-eval"] pyspark = ["pyspark (>=2.4.4)"] quart = ["blinker (>=1.1)", "quart (>=0.16.1)"] rq = ["rq (>=0.6)"] @@ -1891,19 +1891,19 @@ aiomysql = ["aiomysql", "greenlet (!=0.4.17)"] aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing_extensions (!=3.10.0.1)"] asyncio = ["greenlet (!=0.4.17)"] asyncmy = ["asyncmy (>=0.2.3,!=0.2.4)", "greenlet (!=0.4.17)"] -mariadb_connector = ["mariadb (>=1.0.1,!=1.1.2)"] +mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2)"] mssql = ["pyodbc"] -mssql_pymssql = ["pymssql"] -mssql_pyodbc = ["pyodbc"] +mssql-pymssql = ["pymssql"] +mssql-pyodbc = ["pyodbc"] mypy = ["mypy (>=0.910)", "sqlalchemy2-stubs"] mysql = ["mysqlclient (>=1.4.0)", "mysqlclient (>=1.4.0,<2)"] -mysql_connector = ["mysql-connector-python"] +mysql-connector = ["mysql-connector-python"] oracle = ["cx_oracle (>=7)", "cx_oracle (>=7,<8)"] postgresql = ["psycopg2 (>=2.7)"] -postgresql_asyncpg = ["asyncpg", "greenlet (!=0.4.17)"] -postgresql_pg8000 = ["pg8000 (>=1.16.6,!=1.29.0)"] -postgresql_psycopg2binary = ["psycopg2-binary"] -postgresql_psycopg2cffi = ["psycopg2cffi"] +postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"] +postgresql-pg8000 = ["pg8000 (>=1.16.6,!=1.29.0)"] +postgresql-psycopg2binary = ["psycopg2-binary"] +postgresql-psycopg2cffi = ["psycopg2cffi"] pymysql = ["pymysql", "pymysql (<1)"] sqlcipher = ["sqlcipher3_binary"] @@ -3051,18 +3051,7 @@ py = [ {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, ] pyasn1 = [ - {file = "pyasn1-0.4.8-py2.4.egg", hash = "sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3"}, - {file = "pyasn1-0.4.8-py2.5.egg", hash = "sha256:0458773cfe65b153891ac249bcf1b5f8f320b7c2ce462151f8fa74de8934becf"}, - {file = "pyasn1-0.4.8-py2.6.egg", hash = "sha256:5c9414dcfede6e441f7e8f81b43b34e834731003427e5b09e4e00e3172a10f00"}, - {file = "pyasn1-0.4.8-py2.7.egg", hash = "sha256:6e7545f1a61025a4e58bb336952c5061697da694db1cae97b116e9c46abcf7c8"}, {file = "pyasn1-0.4.8-py2.py3-none-any.whl", hash = "sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d"}, - {file = "pyasn1-0.4.8-py3.1.egg", hash = "sha256:78fa6da68ed2727915c4767bb386ab32cdba863caa7dbe473eaae45f9959da86"}, - {file = "pyasn1-0.4.8-py3.2.egg", hash = "sha256:08c3c53b75eaa48d71cf8c710312316392ed40899cb34710d092e96745a358b7"}, - {file = "pyasn1-0.4.8-py3.3.egg", hash = "sha256:03840c999ba71680a131cfaee6fab142e1ed9bbd9c693e285cc6aca0d555e576"}, - {file = "pyasn1-0.4.8-py3.4.egg", hash = "sha256:7ab8a544af125fb704feadb008c99a88805126fb525280b2270bb25cc1d78a12"}, - {file = "pyasn1-0.4.8-py3.5.egg", hash = "sha256:e89bf84b5437b532b0803ba5c9a5e054d21fec423a89952a74f87fa2c9b7bce2"}, - {file = "pyasn1-0.4.8-py3.6.egg", hash = "sha256:014c0e9976956a08139dc0712ae195324a75e142284d5f87f1a87ee1b068a359"}, - {file = "pyasn1-0.4.8-py3.7.egg", hash = "sha256:99fcc3c8d804d1bc6d9a099921e39d827026409a58f2a720dcdb89374ea0c776"}, {file = "pyasn1-0.4.8.tar.gz", hash = "sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba"}, ] pycodestyle = [ diff --git a/src/spiffworkflow_backend/config/permissions/development.yml b/src/spiffworkflow_backend/config/permissions/development.yml index 957a654c..ccb70abd 100644 --- a/src/spiffworkflow_backend/config/permissions/development.yml +++ b/src/spiffworkflow_backend/config/permissions/development.yml @@ -21,7 +21,7 @@ groups: ] Finance Team: - users: [finance_user1] + users: [finance_user1, jason] Project Lead: users: diff --git a/src/spiffworkflow_backend/models/script_attributes_context.py b/src/spiffworkflow_backend/models/script_attributes_context.py new file mode 100644 index 00000000..6d250d33 --- /dev/null +++ b/src/spiffworkflow_backend/models/script_attributes_context.py @@ -0,0 +1,12 @@ +"""Script_attributes_context.""" +from dataclasses import dataclass +from SpiffWorkflow.task import Task as SpiffTask # type: ignore + + +@dataclass +class ScriptAttributesContext(): + """ScriptAttributesContext.""" + task: SpiffTask + environment_identifier: str + process_instance_id: int + process_model_identifier: str diff --git a/src/spiffworkflow_backend/routes/process_api_blueprint.py b/src/spiffworkflow_backend/routes/process_api_blueprint.py index 18ad9e10..dda516ae 100644 --- a/src/spiffworkflow_backend/routes/process_api_blueprint.py +++ b/src/spiffworkflow_backend/routes/process_api_blueprint.py @@ -1,5 +1,4 @@ """APIs for dealing with process groups, process models, and process instances.""" -import dataclasses import json import os import random @@ -749,7 +748,6 @@ def process_instance_delete( """Create_process_instance.""" process_instance = find_process_instance_by_id_or_raise(process_instance_id) - # import pdb; pdb.set_trace() # (Pdb) db.session.delete # > db.session.delete(process_instance) @@ -1352,13 +1350,6 @@ def get_spiff_task_from_process_instance( task_uuid = uuid.UUID(task_id) spiff_task = processor.bpmn_process_instance.get_task(task_uuid) - # FOR DEBUGGING: save this variable so we get it in sentry when something fails - active_task = ActiveTaskModel.query.filter_by(task_id=task_id).first() - if active_task: - task_json = dataclasses.asdict(active_task) - print(f"task_json: {task_json}") - ######## - if spiff_task is None: raise ( ApiError( diff --git a/src/spiffworkflow_backend/scripts/fact_service.py b/src/spiffworkflow_backend/scripts/fact_service.py index 387939ae..ee86a84a 100644 --- a/src/spiffworkflow_backend/scripts/fact_service.py +++ b/src/spiffworkflow_backend/scripts/fact_service.py @@ -1,8 +1,9 @@ """Fact_service.""" from typing import Any -from typing import Optional -from SpiffWorkflow.task import Task as SpiffTask # type: ignore +from spiffworkflow_backend.models.script_attributes_context import ( + ScriptAttributesContext, +) from spiffworkflow_backend.scripts.script import Script @@ -16,8 +17,7 @@ class FactService(Script): def run( self, - task: Optional[SpiffTask], - environment_identifier: str, + script_attributes_context: ScriptAttributesContext, *args: Any, **kwargs: Any ) -> Any: diff --git a/src/spiffworkflow_backend/scripts/get_env.py b/src/spiffworkflow_backend/scripts/get_env.py index 45b86179..cd586ae0 100644 --- a/src/spiffworkflow_backend/scripts/get_env.py +++ b/src/spiffworkflow_backend/scripts/get_env.py @@ -1,8 +1,9 @@ """Get_env.""" from typing import Any -from typing import Optional -from SpiffWorkflow.task import Task as SpiffTask # type: ignore +from spiffworkflow_backend.models.script_attributes_context import ( + ScriptAttributesContext, +) from spiffworkflow_backend.scripts.script import Script @@ -15,10 +16,9 @@ class GetEnv(Script): def run( self, - task: Optional[SpiffTask], - environment_identifier: str, + script_attributes_context: ScriptAttributesContext, *_args: Any, **kwargs: Any ) -> Any: """Run.""" - return environment_identifier + return script_attributes_context.environment_identifier diff --git a/src/spiffworkflow_backend/scripts/get_localtime.py b/src/spiffworkflow_backend/scripts/get_localtime.py index 019313da..16b9bf57 100644 --- a/src/spiffworkflow_backend/scripts/get_localtime.py +++ b/src/spiffworkflow_backend/scripts/get_localtime.py @@ -1,11 +1,12 @@ """Get_localtime.""" from datetime import datetime from typing import Any -from typing import Optional import pytz from flask_bpmn.api.api_error import ApiError -from SpiffWorkflow.task import Task as SpiffTask # type: ignore +from spiffworkflow_backend.models.script_attributes_context import ( + ScriptAttributesContext, +) from spiffworkflow_backend.scripts.script import Script @@ -19,8 +20,7 @@ class GetLocaltime(Script): def run( self, - task: Optional[SpiffTask], - environment_identifier: str, + script_attributes_context: ScriptAttributesContext, *args: Any, **kwargs: Any ) -> datetime: diff --git a/src/spiffworkflow_backend/scripts/get_process_info.py b/src/spiffworkflow_backend/scripts/get_process_info.py new file mode 100644 index 00000000..bfd1e3dd --- /dev/null +++ b/src/spiffworkflow_backend/scripts/get_process_info.py @@ -0,0 +1,25 @@ +"""Get_process_info.""" +from typing import Any +from spiffworkflow_backend.models.script_attributes_context import ScriptAttributesContext + +from spiffworkflow_backend.scripts.script import Script + + +class GetProcessInfo(Script): + """GetUser.""" + + def get_description(self) -> str: + """Get_description.""" + return """Returns a dictionary of information about the currently running process.""" + + def run( + self, + script_attributes_context: ScriptAttributesContext, + *_args: Any, + **kwargs: Any + ) -> Any: + """Run.""" + return { + 'process_instance_id': script_attributes_context.process_instance_id, + 'process_model_identifier': script_attributes_context.process_model_identifier, + } diff --git a/src/spiffworkflow_backend/scripts/get_user.py b/src/spiffworkflow_backend/scripts/get_user.py index db0561a4..f49e3fd8 100644 --- a/src/spiffworkflow_backend/scripts/get_user.py +++ b/src/spiffworkflow_backend/scripts/get_user.py @@ -1,9 +1,10 @@ """Get_env.""" from typing import Any -from typing import Optional from flask import g -from SpiffWorkflow.task import Task as SpiffTask # type: ignore +from spiffworkflow_backend.models.script_attributes_context import ( + ScriptAttributesContext, +) from spiffworkflow_backend.scripts.script import Script @@ -16,8 +17,7 @@ class GetUser(Script): def run( self, - task: Optional[SpiffTask], - environment_identifier: str, + script_attributes_context: ScriptAttributesContext, *_args: Any, **kwargs: Any ) -> Any: diff --git a/src/spiffworkflow_backend/scripts/script.py b/src/spiffworkflow_backend/scripts/script.py index f36e4ace..7ed745d6 100644 --- a/src/spiffworkflow_backend/scripts/script.py +++ b/src/spiffworkflow_backend/scripts/script.py @@ -9,7 +9,9 @@ from typing import Any from typing import Callable from flask_bpmn.api.api_error import ApiError -from SpiffWorkflow.task import Task as SpiffTask # type: ignore +from spiffworkflow_backend.models.script_attributes_context import ( + ScriptAttributesContext, +) # Generally speaking, having some global in a flask app is TERRIBLE. # This is here, because after loading the application this will never change under @@ -28,8 +30,7 @@ class Script: @abstractmethod def run( self, - task: SpiffTask, - environment_identifier: str, + script_attributes_context: ScriptAttributesContext, *args: Any, **kwargs: Any, ) -> Any: @@ -43,7 +44,7 @@ class Script: @staticmethod def generate_augmented_list( - task: SpiffTask, environment_identifier: str + script_attributes_context: ScriptAttributesContext, ) -> dict[str, Callable]: """This makes a dictionary of lambda functions that are closed over the class instance that they represent. @@ -56,7 +57,8 @@ class Script: """ def make_closure( - subclass: type[Script], task: SpiffTask, environment_identifier: str + subclass: type[Script], + script_attributes_context: ScriptAttributesContext, ) -> Callable: """Yes - this is black magic. @@ -70,8 +72,7 @@ class Script: instance = subclass() return lambda *ar, **kw: subclass.run( instance, - task, - environment_identifier, + script_attributes_context, *ar, **kw, ) @@ -81,7 +82,7 @@ class Script: for x in range(len(subclasses)): subclass = subclasses[x] execlist[subclass.__module__.split(".")[-1]] = make_closure( - subclass, task=task, environment_identifier=environment_identifier + subclass, script_attributes_context=script_attributes_context ) return execlist diff --git a/src/spiffworkflow_backend/services/process_instance_processor.py b/src/spiffworkflow_backend/services/process_instance_processor.py index 3c49ec7c..af378a38 100644 --- a/src/spiffworkflow_backend/services/process_instance_processor.py +++ b/src/spiffworkflow_backend/services/process_instance_processor.py @@ -73,6 +73,9 @@ from spiffworkflow_backend.models.message_instance import MessageModel from spiffworkflow_backend.models.process_instance import ProcessInstanceModel from spiffworkflow_backend.models.process_instance import ProcessInstanceStatus from spiffworkflow_backend.models.process_model import ProcessModelInfo +from spiffworkflow_backend.models.script_attributes_context import ( + ScriptAttributesContext, +) from spiffworkflow_backend.models.task_event import TaskAction from spiffworkflow_backend.models.task_event import TaskEventModel from spiffworkflow_backend.models.user import UserModel @@ -126,6 +129,10 @@ class PotentialOwnerUserNotFoundError(Exception): """PotentialOwnerUserNotFoundError.""" +class MissingProcessInfoError(Exception): + """MissingProcessInfoError.""" + + class CustomBpmnScriptEngine(PythonScriptEngine): # type: ignore """This is a custom script processor that can be easily injected into Spiff Workflow. @@ -139,9 +146,26 @@ class CustomBpmnScriptEngine(PythonScriptEngine): # type: ignore def __get_augment_methods(self, task: SpiffTask) -> Dict[str, Callable]: """__get_augment_methods.""" - return Script.generate_augmented_list( - task, current_app.config["ENV_IDENTIFIER"] + tld = current_app.config["THREAD_LOCAL_DATA"] + + if not hasattr(tld, "process_model_identifier"): + raise MissingProcessInfoError( + "Could not find process_model_identifier from app config" + ) + if not hasattr(tld, "process_instance_id"): + raise MissingProcessInfoError( + "Could not find process_instance_id from app config" + ) + + process_model_identifier = tld.process_model_identifier + process_instance_id = tld.process_instance_id + script_attributes_context = ScriptAttributesContext( + task=task, + environment_identifier=current_app.config["ENV_IDENTIFIER"], + process_instance_id=process_instance_id, + process_model_identifier=process_model_identifier, ) + return Script.generate_augmented_list(script_attributes_context) def evaluate(self, task: SpiffTask, expression: str) -> Any: """Evaluate.""" @@ -254,6 +278,12 @@ class ProcessInstanceProcessor: "THREAD_LOCAL_DATA" ].process_instance_id = process_instance_model.id + # we want this to be the fully qualified path to the process model including all group subcomponents + current_app.config["THREAD_LOCAL_DATA"].process_model_identifier = ( + f"{process_instance_model.process_group_identifier}/" + f"{process_instance_model.process_model_identifier}" + ) + self.process_instance_model = process_instance_model self.process_model_service = ProcessModelService() bpmn_process_spec = None diff --git a/tests/spiffworkflow_backend/scripts/test_get_localtime.py b/tests/spiffworkflow_backend/scripts/test_get_localtime.py index cb81ce61..a6eb4f12 100644 --- a/tests/spiffworkflow_backend/scripts/test_get_localtime.py +++ b/tests/spiffworkflow_backend/scripts/test_get_localtime.py @@ -4,6 +4,9 @@ import datetime import pytz from flask.app import Flask from flask.testing import FlaskClient +from spiffworkflow_backend.models.script_attributes_context import ( + ScriptAttributesContext, +) from spiffworkflow_backend.scripts.get_localtime import GetLocaltime from spiffworkflow_backend.services.process_instance_processor import ( ProcessInstanceProcessor, @@ -22,9 +25,16 @@ class TestGetLocaltime(BaseTest): """Test_get_localtime_script_directly.""" current_time = datetime.datetime.now() timezone = "US/Pacific" - result = GetLocaltime().run( + process_model_identifier = "test_process_model" + process_instance_id = 1 + script_attributes_context = ScriptAttributesContext( task=None, environment_identifier="testing", + process_instance_id=process_instance_id, + process_model_identifier=process_model_identifier, + ) + result = GetLocaltime().run( + script_attributes_context, datetime=current_time, timezone=timezone, )