From 29ada389046122a2ba39534f934dde53d6ef173a Mon Sep 17 00:00:00 2001 From: jasquat Date: Mon, 27 Feb 2023 14:28:19 -0500 Subject: [PATCH] added script to get process initiator w/ burnettk --- .../models/human_task.py | 4 +- .../src/spiffworkflow_backend/models/user.py | 8 +++ .../scripts/get_last_user_completing_task.py | 11 ++-- .../scripts/get_process_initiator_user.py | 36 +++++++++++ .../dynamic_enums_ask_for_color.bpmn | 4 +- .../tests/data/error/instructions_error.bpmn | 4 +- .../data/get_localtime/get_localtime.bpmn | 4 +- .../tests/data/manual_task/manual_task.bpmn | 4 +- .../tests/data/model_with_lanes/lanes.bpmn | 4 +- .../lanes_with_owner_dict.bpmn | 4 +- .../tests/data/simple_form/simple_form.bpmn | 1 + .../simple_form_with_error.bpmn | 4 +- .../data/simple_script/simple_script.bpmn | 4 +- .../integration/test_process_api.py | 2 +- .../test_get_last_user_completing_task.py | 6 +- .../test_get_process_initiator_user.py | 62 +++++++++++++++++++ 16 files changed, 133 insertions(+), 29 deletions(-) create mode 100644 spiffworkflow-backend/src/spiffworkflow_backend/scripts/get_process_initiator_user.py create mode 100644 spiffworkflow-backend/tests/spiffworkflow_backend/scripts/test_get_process_initiator_user.py diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/models/human_task.py b/spiffworkflow-backend/src/spiffworkflow_backend/models/human_task.py index 43415352e..5cc208b19 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/models/human_task.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/models/human_task.py @@ -34,7 +34,9 @@ class HumanTaskModel(SpiffworkflowBaseDBModel): lane_assignment_id: int | None = db.Column(ForeignKey(GroupModel.id)) completed_by_user_id: int = db.Column(ForeignKey(UserModel.id), nullable=True) # type: ignore - completed_by_user = relationship("UserModel", foreign_keys=[completed_by_user_id]) + completed_by_user = relationship( + "UserModel", foreign_keys=[completed_by_user_id], viewonly=True + ) actual_owner_id: int = db.Column(ForeignKey(UserModel.id)) # type: ignore # actual_owner: RelationshipProperty[UserModel] = relationship(UserModel) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/models/user.py b/spiffworkflow-backend/src/spiffworkflow_backend/models/user.py index 464bdc8b2..f32a35d79 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/models/user.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/models/user.py @@ -2,6 +2,7 @@ from __future__ import annotations from dataclasses import dataclass +from typing import Any import jwt import marshmallow @@ -82,6 +83,13 @@ class UserModel(SpiffworkflowBaseDBModel): # # return instance + def as_dict(self) -> dict[str, Any]: + # dump the user using our json encoder and then load it back up as a dict + # to remove unwanted field types + user_as_json_string = current_app.json.dumps(self) + user_dict: dict[str, Any] = current_app.json.loads(user_as_json_string) + return user_dict + class UserModelSchema(Schema): """UserModelSchema.""" diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/scripts/get_last_user_completing_task.py b/spiffworkflow-backend/src/spiffworkflow_backend/scripts/get_last_user_completing_task.py index eea757d64..8d63610bb 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/scripts/get_last_user_completing_task.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/scripts/get_last_user_completing_task.py @@ -1,8 +1,6 @@ """Get current user.""" from typing import Any -from flask import current_app - from spiffworkflow_backend.models.human_task import HumanTaskModel from spiffworkflow_backend.models.script_attributes_context import ( ScriptAttributesContext, @@ -38,14 +36,13 @@ class GetLastUserCompletingTask(Script): human_task = ( HumanTaskModel.query.filter_by( - bpmn_process_identifier=bpmn_process_identifier, task_name=task_name + process_instance_id=script_attributes_context.process_instance_id, + bpmn_process_identifier=bpmn_process_identifier, + task_name=task_name, ) .order_by(HumanTaskModel.id.desc()) # type: ignore .join(UserModel, UserModel.id == HumanTaskModel.completed_by_user_id) .first() ) - # dump the user using our json encoder and then load it back up as a dict - # to remove unwanted field types - user_as_json_string = current_app.json.dumps(human_task.completed_by_user) - return current_app.json.loads(user_as_json_string) + return human_task.completed_by_user.as_dict() diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/scripts/get_process_initiator_user.py b/spiffworkflow-backend/src/spiffworkflow_backend/scripts/get_process_initiator_user.py new file mode 100644 index 000000000..266fa57ba --- /dev/null +++ b/spiffworkflow-backend/src/spiffworkflow_backend/scripts/get_process_initiator_user.py @@ -0,0 +1,36 @@ +"""Get current user.""" +from typing import Any + +from spiffworkflow_backend.models.process_instance import ProcessInstanceModel +from spiffworkflow_backend.models.script_attributes_context import ( + ScriptAttributesContext, +) +from spiffworkflow_backend.models.user import UserModel +from spiffworkflow_backend.scripts.script import Script + + +class GetProcessInitiatorUser(Script): + @staticmethod + def requires_privileged_permissions() -> bool: + """We have deemed this function safe to run without elevated permissions.""" + return False + + def get_description(self) -> str: + return """Return the user that initiated the process instance.""" + + def run( + self, + script_attributes_context: ScriptAttributesContext, + *_args: Any, + **kwargs: Any, + ) -> Any: + """Run.""" + process_instance = ( + ProcessInstanceModel.query.filter_by( + id=script_attributes_context.process_instance_id + ) + .join(UserModel, UserModel.id == ProcessInstanceModel.process_initiator_id) + .first() + ) + + return process_instance.process_initiator.as_dict() diff --git a/spiffworkflow-backend/tests/data/dynamic_enum_select_fields/dynamic_enums_ask_for_color.bpmn b/spiffworkflow-backend/tests/data/dynamic_enum_select_fields/dynamic_enums_ask_for_color.bpmn index d4f1aa5d2..9b15cb09a 100644 --- a/spiffworkflow-backend/tests/data/dynamic_enum_select_fields/dynamic_enums_ask_for_color.bpmn +++ b/spiffworkflow-backend/tests/data/dynamic_enum_select_fields/dynamic_enums_ask_for_color.bpmn @@ -1,6 +1,6 @@ - + Flow_1my9ag5 @@ -28,7 +28,7 @@ form_ui_hidden_fields = ["veryImportantFieldButOnlySometimes", "building.floor"] - + diff --git a/spiffworkflow-backend/tests/data/error/instructions_error.bpmn b/spiffworkflow-backend/tests/data/error/instructions_error.bpmn index 24039bbbd..1db55f390 100644 --- a/spiffworkflow-backend/tests/data/error/instructions_error.bpmn +++ b/spiffworkflow-backend/tests/data/error/instructions_error.bpmn @@ -1,6 +1,6 @@ - + Flow_0smvjir @@ -21,7 +21,7 @@ Department: {{ department }} - + diff --git a/spiffworkflow-backend/tests/data/get_localtime/get_localtime.bpmn b/spiffworkflow-backend/tests/data/get_localtime/get_localtime.bpmn index 5660ba0bc..2efa2fa68 100644 --- a/spiffworkflow-backend/tests/data/get_localtime/get_localtime.bpmn +++ b/spiffworkflow-backend/tests/data/get_localtime/get_localtime.bpmn @@ -1,6 +1,6 @@ - + Flow_0ijucqh @@ -40,7 +40,7 @@ localtime = get_localtime(some_time, timezone) - + diff --git a/spiffworkflow-backend/tests/data/manual_task/manual_task.bpmn b/spiffworkflow-backend/tests/data/manual_task/manual_task.bpmn index aefbb376b..4f0fba72c 100644 --- a/spiffworkflow-backend/tests/data/manual_task/manual_task.bpmn +++ b/spiffworkflow-backend/tests/data/manual_task/manual_task.bpmn @@ -1,6 +1,6 @@ - + Flow_1xlck7g @@ -18,7 +18,7 @@ - + diff --git a/spiffworkflow-backend/tests/data/model_with_lanes/lanes.bpmn b/spiffworkflow-backend/tests/data/model_with_lanes/lanes.bpmn index ed6195ac5..b396bf714 100644 --- a/spiffworkflow-backend/tests/data/model_with_lanes/lanes.bpmn +++ b/spiffworkflow-backend/tests/data/model_with_lanes/lanes.bpmn @@ -1,9 +1,9 @@ - + - + StartEvent_1 diff --git a/spiffworkflow-backend/tests/data/model_with_lanes/lanes_with_owner_dict.bpmn b/spiffworkflow-backend/tests/data/model_with_lanes/lanes_with_owner_dict.bpmn index 0c2af8d48..9d0f2a307 100644 --- a/spiffworkflow-backend/tests/data/model_with_lanes/lanes_with_owner_dict.bpmn +++ b/spiffworkflow-backend/tests/data/model_with_lanes/lanes_with_owner_dict.bpmn @@ -1,9 +1,9 @@ - + - + StartEvent_1 diff --git a/spiffworkflow-backend/tests/data/simple_form/simple_form.bpmn b/spiffworkflow-backend/tests/data/simple_form/simple_form.bpmn index 9c3024181..a2f29fd3b 100644 --- a/spiffworkflow-backend/tests/data/simple_form/simple_form.bpmn +++ b/spiffworkflow-backend/tests/data/simple_form/simple_form.bpmn @@ -26,6 +26,7 @@ Department: {{ department }} + process_initiator_user = get_process_initiator_user() Flow_0smvjir Flow_1ly1khd diff --git a/spiffworkflow-backend/tests/data/simple_form_with_error/simple_form_with_error.bpmn b/spiffworkflow-backend/tests/data/simple_form_with_error/simple_form_with_error.bpmn index 351d53a65..43d3d1167 100644 --- a/spiffworkflow-backend/tests/data/simple_form_with_error/simple_form_with_error.bpmn +++ b/spiffworkflow-backend/tests/data/simple_form_with_error/simple_form_with_error.bpmn @@ -1,6 +1,6 @@ - + Flow_0smvjir @@ -31,7 +31,7 @@ Department: {{ department }} - + diff --git a/spiffworkflow-backend/tests/data/simple_script/simple_script.bpmn b/spiffworkflow-backend/tests/data/simple_script/simple_script.bpmn index 6e14807fa..f5efba61d 100644 --- a/spiffworkflow-backend/tests/data/simple_script/simple_script.bpmn +++ b/spiffworkflow-backend/tests/data/simple_script/simple_script.bpmn @@ -1,6 +1,6 @@ - + Flow_0r3ua0i @@ -48,7 +48,7 @@ b = 2 - + diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py b/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py index 29d1d33f4..cce940beb 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py @@ -583,7 +583,7 @@ class TestProcessApi(BaseTest): # We should get 5 back, as one of the items in the cache is a decision. assert len(response.json) == 5 simple_form = next( - p for p in response.json if p["identifier"] == "Proccess_WithForm" + p for p in response.json if p["identifier"] == "Process_WithForm" ) assert simple_form["display_name"] == "Process With Form" assert simple_form["process_model_id"] == "test_group_one/simple_form" diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/scripts/test_get_last_user_completing_task.py b/spiffworkflow-backend/tests/spiffworkflow_backend/scripts/test_get_last_user_completing_task.py index 661428d6b..d6533eaec 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/scripts/test_get_last_user_completing_task.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/scripts/test_get_last_user_completing_task.py @@ -1,12 +1,11 @@ """Test_get_localtime.""" -from spiffworkflow_backend.services.authorization_service import AuthorizationService -from tests.spiffworkflow_backend.helpers.test_data import load_test_spec - from flask.app import Flask from flask.testing import FlaskClient from tests.spiffworkflow_backend.helpers.base_test import BaseTest +from tests.spiffworkflow_backend.helpers.test_data import load_test_spec from spiffworkflow_backend.models.user import UserModel +from spiffworkflow_backend.services.authorization_service import AuthorizationService from spiffworkflow_backend.services.process_instance_processor import ( ProcessInstanceProcessor, ) @@ -16,7 +15,6 @@ from spiffworkflow_backend.services.process_instance_service import ( class TestGetLastUserCompletingTask(BaseTest): - def test_get_last_user_completing_task_script_works( self, app: Flask, diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/scripts/test_get_process_initiator_user.py b/spiffworkflow-backend/tests/spiffworkflow_backend/scripts/test_get_process_initiator_user.py new file mode 100644 index 000000000..5e7342278 --- /dev/null +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/scripts/test_get_process_initiator_user.py @@ -0,0 +1,62 @@ +"""Test_get_localtime.""" +from spiffworkflow_backend.services.authorization_service import AuthorizationService +from tests.spiffworkflow_backend.helpers.test_data import load_test_spec + +from flask.app import Flask +from flask.testing import FlaskClient +from tests.spiffworkflow_backend.helpers.base_test import BaseTest + +from spiffworkflow_backend.models.user import UserModel +from spiffworkflow_backend.services.process_instance_processor import ( + ProcessInstanceProcessor, +) +from spiffworkflow_backend.services.process_instance_service import ( + ProcessInstanceService, +) + + +class TestGetProcessInitiatorUser(BaseTest): + + def test_get_process_initiator_user( + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, + ) -> None: + """Test_sets_permission_correctly_on_human_task.""" + self.create_process_group( + client, with_super_admin_user, "test_group", "test_group" + ) + initiator_user = self.find_or_create_user("initiator_user") + assert initiator_user.principal is not None + AuthorizationService.import_permissions_from_yaml_file() + + process_model = load_test_spec( + process_model_id="misc/category_number_one/simple_form", + # bpmn_file_name="simp.bpmn", + process_model_source_directory="simple_form", + ) + process_instance = self.create_process_instance_from_process_model( + process_model=process_model, user=initiator_user + ) + processor = ProcessInstanceProcessor(process_instance) + processor.do_engine_steps(save=True) + + assert len(process_instance.active_human_tasks) == 1 + human_task = process_instance.active_human_tasks[0] + assert len(human_task.potential_owners) == 1 + assert human_task.potential_owners[0] == initiator_user + + spiff_task = processor.__class__.get_task_by_bpmn_identifier( + human_task.task_name, processor.bpmn_process_instance + ) + ProcessInstanceService.complete_form_task( + processor, spiff_task, {"name": "HEY"}, initiator_user, human_task + ) + + assert spiff_task is not None + assert ( + initiator_user.username + == spiff_task.get_data("process_initiator_user")["username"] + )