From 2c1d8b62cb4aa8e00b204dae9116167d13d3c28a Mon Sep 17 00:00:00 2001 From: jasquat Date: Mon, 12 Dec 2022 15:08:09 -0500 Subject: [PATCH 01/14] allow viewing the diagram for a specific process identifier --- src/spiffworkflow_backend/api.yml | 6 +++++ .../models/spec_reference.py | 4 +++ src/spiffworkflow_backend/models/task.py | 3 +++ .../routes/process_api_blueprint.py | 25 ++++++++++++++----- .../services/git_service.py | 7 ++++-- .../services/process_instance_service.py | 6 +++++ 6 files changed, 43 insertions(+), 8 deletions(-) diff --git a/src/spiffworkflow_backend/api.yml b/src/spiffworkflow_backend/api.yml index 764ba543..84ada234 100755 --- a/src/spiffworkflow_backend/api.yml +++ b/src/spiffworkflow_backend/api.yml @@ -699,6 +699,12 @@ paths: description: The unique id of an existing process instance. schema: type: integer + - name: process_identifier + in: query + required: false + description: The identifier of the process to use for the diagram. Useful for displaying the diagram for a call activity. + schema: + type: string get: tags: - Process Instances diff --git a/src/spiffworkflow_backend/models/spec_reference.py b/src/spiffworkflow_backend/models/spec_reference.py index 1e85f722..ac2e3200 100644 --- a/src/spiffworkflow_backend/models/spec_reference.py +++ b/src/spiffworkflow_backend/models/spec_reference.py @@ -8,6 +8,10 @@ from marshmallow import INCLUDE from sqlalchemy import UniqueConstraint +class SpecReferenceNotFoundError(Exception): + pass + + @dataclass() class SpecReference: """File Reference Information. diff --git a/src/spiffworkflow_backend/models/task.py b/src/spiffworkflow_backend/models/task.py index 52bb1171..019f4b64 100644 --- a/src/spiffworkflow_backend/models/task.py +++ b/src/spiffworkflow_backend/models/task.py @@ -118,6 +118,7 @@ class Task: form_schema: Union[str, None] = None, form_ui_schema: Union[str, None] = None, parent: Optional[str] = None, + call_activity_process_identifier: Optional[str] = None, ): """__init__.""" self.id = id @@ -129,6 +130,7 @@ class Task: self.documentation = documentation self.lane = lane self.parent = parent + self.call_activity_process_identifier = call_activity_process_identifier self.data = data if self.data is None: @@ -187,6 +189,7 @@ class Task: "form_schema": self.form_schema, "form_ui_schema": self.form_ui_schema, "parent": self.parent, + "call_activity_process_identifier": self.call_activity_process_identifier, } @classmethod diff --git a/src/spiffworkflow_backend/routes/process_api_blueprint.py b/src/spiffworkflow_backend/routes/process_api_blueprint.py index c29cf214..3a73098f 100644 --- a/src/spiffworkflow_backend/routes/process_api_blueprint.py +++ b/src/spiffworkflow_backend/routes/process_api_blueprint.py @@ -65,7 +65,7 @@ from spiffworkflow_backend.models.process_model import ProcessModelInfo from spiffworkflow_backend.models.process_model import ProcessModelInfoSchema from spiffworkflow_backend.models.secret_model import SecretModel from spiffworkflow_backend.models.secret_model import SecretModelSchema -from spiffworkflow_backend.models.spec_reference import SpecReferenceCache +from spiffworkflow_backend.models.spec_reference import SpecReferenceCache, SpecReferenceNotFoundError from spiffworkflow_backend.models.spec_reference import SpecReferenceSchema from spiffworkflow_backend.models.spiff_logging import SpiffLoggingModel from spiffworkflow_backend.models.spiff_step_details import SpiffStepDetailsModel @@ -1073,25 +1073,38 @@ def process_instance_report_column_list() -> flask.wrappers.Response: def process_instance_show( - modified_process_model_identifier: str, process_instance_id: int + modified_process_model_identifier: str, process_instance_id: int, process_identifier: Optional[str] = None ) -> flask.wrappers.Response: """Create_process_instance.""" process_model_identifier = modified_process_model_identifier.replace(":", "/") process_instance = find_process_instance_by_id_or_raise(process_instance_id) current_version_control_revision = GitService.get_current_revision() - process_model = get_process_model(process_model_identifier) - if process_model.primary_file_name: + process_model_with_diagram = None + name_of_file_with_diagram = None + if process_identifier: + spec_reference = SpecReferenceCache.query.filter_by(identifier=process_identifier).first() + if spec_reference is None: + raise SpecReferenceNotFoundError(f"Could not find given process identifier in the cache: {process_identifier}") + + process_model_with_diagram = ProcessModelService.get_process_model(spec_reference.process_model_id) + name_of_file_with_diagram = spec_reference.file_name + else: + process_model_with_diagram = get_process_model(process_model_identifier) + if process_model_with_diagram.primary_file_name: + name_of_file_with_diagram = process_model_with_diagram.primary_file_name + + if process_model_with_diagram and name_of_file_with_diagram: if ( process_instance.bpmn_version_control_identifier == current_version_control_revision ): bpmn_xml_file_contents = SpecFileService.get_data( - process_model, process_model.primary_file_name + process_model_with_diagram, name_of_file_with_diagram ).decode("utf-8") else: bpmn_xml_file_contents = GitService.get_instance_file_contents_for_revision( - process_model, process_instance.bpmn_version_control_identifier + process_model_with_diagram, process_instance.bpmn_version_control_identifier, file_name=name_of_file_with_diagram ) process_instance.bpmn_xml_file_contents = bpmn_xml_file_contents diff --git a/src/spiffworkflow_backend/services/git_service.py b/src/spiffworkflow_backend/services/git_service.py index f972b672..519510e7 100644 --- a/src/spiffworkflow_backend/services/git_service.py +++ b/src/spiffworkflow_backend/services/git_service.py @@ -46,18 +46,21 @@ class GitService: @classmethod def get_instance_file_contents_for_revision( - cls, process_model: ProcessModelInfo, revision: str + cls, process_model: ProcessModelInfo, revision: str, file_name: Optional[str] = None ) -> str: """Get_instance_file_contents_for_revision.""" bpmn_spec_absolute_dir = current_app.config["BPMN_SPEC_ABSOLUTE_DIR"] process_model_relative_path = FileSystemService.process_model_relative_path( process_model ) + file_name_to_use = file_name + if file_name_to_use is None: + file_name_to_use = process_model.primary_file_name with FileSystemService.cd(bpmn_spec_absolute_dir): shell_command = [ "git", "show", - f"{revision}:{process_model_relative_path}/{process_model.primary_file_name}", + f"{revision}:{process_model_relative_path}/{file_name_to_use}", ] return cls.run_shell_command_to_get_stdout(shell_command) diff --git a/src/spiffworkflow_backend/services/process_instance_service.py b/src/spiffworkflow_backend/services/process_instance_service.py index 46bd252b..38c71715 100644 --- a/src/spiffworkflow_backend/services/process_instance_service.py +++ b/src/spiffworkflow_backend/services/process_instance_service.py @@ -302,6 +302,11 @@ class ProcessInstanceService: else: lane = None + if hasattr(spiff_task.task_spec, "spec"): + call_activity_process_identifier = spiff_task.task_spec.spec + else: + call_activity_process_identifier = None + parent_id = None if spiff_task.parent: parent_id = spiff_task.parent.id @@ -319,6 +324,7 @@ class ProcessInstanceService: process_name=spiff_task.task_spec._wf_spec.description, properties=props, parent=parent_id, + call_activity_process_identifier=call_activity_process_identifier, ) return task From 5e54256a2e7fa2f26c8d96d143a917536bd7a344 Mon Sep 17 00:00:00 2001 From: jasquat Date: Mon, 12 Dec 2022 15:33:31 -0500 Subject: [PATCH 02/14] added test to get the diagram for a given process instance call activity --- .../integration/test_process_api.py | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/tests/spiffworkflow_backend/integration/test_process_api.py b/tests/spiffworkflow_backend/integration/test_process_api.py index 0070c5c9..25cd3da2 100644 --- a/tests/spiffworkflow_backend/integration/test_process_api.py +++ b/tests/spiffworkflow_backend/integration/test_process_api.py @@ -1167,6 +1167,57 @@ class TestProcessApi(BaseTest): xml_file_contents = f_open.read() assert show_response.json["bpmn_xml_file_contents"] == xml_file_contents + def test_process_instance_show_with_specified_process_identifier( + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, + ) -> None: + process_model_id = "call_activity_nested" + process_model_identifier = self.create_group_and_model_with_bpmn( + client=client, + user=with_super_admin_user, + process_group_id="test_group_two", + process_model_id=process_model_id, + bpmn_file_location="call_activity_nested", + ) + spec_reference = SpecReferenceCache.query.filter_by(identifier="Level2b").first() + assert spec_reference + modified_process_model_identifier = ( + self.modify_process_identifier_for_path_param(process_model_identifier) + ) + headers = self.logged_in_headers(with_super_admin_user) + create_response = self.create_process_instance_from_process_model_id( + client, process_model_identifier, headers + ) + assert create_response.json is not None + assert create_response.status_code == 201 + process_instance_id = create_response.json["id"] + client.post( + f"/v1.0/process-instances/{modified_process_model_identifier}/{process_instance_id}/run", + headers=self.logged_in_headers(with_super_admin_user), + ) + show_response = client.get( + f"/v1.0/process-instances/{modified_process_model_identifier}/{process_instance_id}?process_identifier={spec_reference.identifier}", + headers=self.logged_in_headers(with_super_admin_user), + ) + assert show_response.json is not None + assert show_response.status_code == 200 + file_system_root = FileSystemService.root_path() + process_instance_file_path = ( + f"{file_system_root}/{process_model_identifier}/{process_model_id}.bpmn" + ) + with open(process_instance_file_path) as f_open: + xml_file_contents = f_open.read() + assert show_response.json["bpmn_xml_file_contents"] != xml_file_contents + spec_reference_file_path = ( + os.path.join(file_system_root, spec_reference.relative_path) + ) + with open(spec_reference_file_path) as f_open: + xml_file_contents = f_open.read() + assert show_response.json["bpmn_xml_file_contents"] == xml_file_contents + def test_message_start_when_starting_process_instance( self, app: Flask, From 6ae471d0e0c916f81fa63ca142cf401ba8e504de Mon Sep 17 00:00:00 2001 From: jasquat Date: Mon, 12 Dec 2022 15:36:03 -0500 Subject: [PATCH 03/14] pyl --- .../models/spec_reference.py | 2 +- .../routes/process_api_blueprint.py | 23 ++++++++++++++----- .../services/git_service.py | 5 +++- .../integration/test_process_api.py | 9 +++++--- .../unit/test_git_service.py | 8 +++++-- 5 files changed, 34 insertions(+), 13 deletions(-) diff --git a/src/spiffworkflow_backend/models/spec_reference.py b/src/spiffworkflow_backend/models/spec_reference.py index ac2e3200..50b73fba 100644 --- a/src/spiffworkflow_backend/models/spec_reference.py +++ b/src/spiffworkflow_backend/models/spec_reference.py @@ -9,7 +9,7 @@ from sqlalchemy import UniqueConstraint class SpecReferenceNotFoundError(Exception): - pass + """SpecReferenceNotFoundError.""" @dataclass() diff --git a/src/spiffworkflow_backend/routes/process_api_blueprint.py b/src/spiffworkflow_backend/routes/process_api_blueprint.py index 3a73098f..0093c62e 100644 --- a/src/spiffworkflow_backend/routes/process_api_blueprint.py +++ b/src/spiffworkflow_backend/routes/process_api_blueprint.py @@ -65,7 +65,8 @@ from spiffworkflow_backend.models.process_model import ProcessModelInfo from spiffworkflow_backend.models.process_model import ProcessModelInfoSchema from spiffworkflow_backend.models.secret_model import SecretModel from spiffworkflow_backend.models.secret_model import SecretModelSchema -from spiffworkflow_backend.models.spec_reference import SpecReferenceCache, SpecReferenceNotFoundError +from spiffworkflow_backend.models.spec_reference import SpecReferenceCache +from spiffworkflow_backend.models.spec_reference import SpecReferenceNotFoundError from spiffworkflow_backend.models.spec_reference import SpecReferenceSchema from spiffworkflow_backend.models.spiff_logging import SpiffLoggingModel from spiffworkflow_backend.models.spiff_step_details import SpiffStepDetailsModel @@ -1073,7 +1074,9 @@ def process_instance_report_column_list() -> flask.wrappers.Response: def process_instance_show( - modified_process_model_identifier: str, process_instance_id: int, process_identifier: Optional[str] = None + modified_process_model_identifier: str, + process_instance_id: int, + process_identifier: Optional[str] = None, ) -> flask.wrappers.Response: """Create_process_instance.""" process_model_identifier = modified_process_model_identifier.replace(":", "/") @@ -1083,11 +1086,17 @@ def process_instance_show( process_model_with_diagram = None name_of_file_with_diagram = None if process_identifier: - spec_reference = SpecReferenceCache.query.filter_by(identifier=process_identifier).first() + spec_reference = SpecReferenceCache.query.filter_by( + identifier=process_identifier + ).first() if spec_reference is None: - raise SpecReferenceNotFoundError(f"Could not find given process identifier in the cache: {process_identifier}") + raise SpecReferenceNotFoundError( + f"Could not find given process identifier in the cache: {process_identifier}" + ) - process_model_with_diagram = ProcessModelService.get_process_model(spec_reference.process_model_id) + process_model_with_diagram = ProcessModelService.get_process_model( + spec_reference.process_model_id + ) name_of_file_with_diagram = spec_reference.file_name else: process_model_with_diagram = get_process_model(process_model_identifier) @@ -1104,7 +1113,9 @@ def process_instance_show( ).decode("utf-8") else: bpmn_xml_file_contents = GitService.get_instance_file_contents_for_revision( - process_model_with_diagram, process_instance.bpmn_version_control_identifier, file_name=name_of_file_with_diagram + process_model_with_diagram, + process_instance.bpmn_version_control_identifier, + file_name=name_of_file_with_diagram, ) process_instance.bpmn_xml_file_contents = bpmn_xml_file_contents diff --git a/src/spiffworkflow_backend/services/git_service.py b/src/spiffworkflow_backend/services/git_service.py index 519510e7..152aab1c 100644 --- a/src/spiffworkflow_backend/services/git_service.py +++ b/src/spiffworkflow_backend/services/git_service.py @@ -46,7 +46,10 @@ class GitService: @classmethod def get_instance_file_contents_for_revision( - cls, process_model: ProcessModelInfo, revision: str, file_name: Optional[str] = None + cls, + process_model: ProcessModelInfo, + revision: str, + file_name: Optional[str] = None, ) -> str: """Get_instance_file_contents_for_revision.""" bpmn_spec_absolute_dir = current_app.config["BPMN_SPEC_ABSOLUTE_DIR"] diff --git a/tests/spiffworkflow_backend/integration/test_process_api.py b/tests/spiffworkflow_backend/integration/test_process_api.py index 25cd3da2..4a0100d3 100644 --- a/tests/spiffworkflow_backend/integration/test_process_api.py +++ b/tests/spiffworkflow_backend/integration/test_process_api.py @@ -1174,6 +1174,7 @@ class TestProcessApi(BaseTest): with_db_and_bpmn_file_cleanup: None, with_super_admin_user: UserModel, ) -> None: + """Test_process_instance_show_with_specified_process_identifier.""" process_model_id = "call_activity_nested" process_model_identifier = self.create_group_and_model_with_bpmn( client=client, @@ -1182,7 +1183,9 @@ class TestProcessApi(BaseTest): process_model_id=process_model_id, bpmn_file_location="call_activity_nested", ) - spec_reference = SpecReferenceCache.query.filter_by(identifier="Level2b").first() + spec_reference = SpecReferenceCache.query.filter_by( + identifier="Level2b" + ).first() assert spec_reference modified_process_model_identifier = ( self.modify_process_identifier_for_path_param(process_model_identifier) @@ -1211,8 +1214,8 @@ class TestProcessApi(BaseTest): with open(process_instance_file_path) as f_open: xml_file_contents = f_open.read() assert show_response.json["bpmn_xml_file_contents"] != xml_file_contents - spec_reference_file_path = ( - os.path.join(file_system_root, spec_reference.relative_path) + spec_reference_file_path = os.path.join( + file_system_root, spec_reference.relative_path ) with open(spec_reference_file_path) as f_open: xml_file_contents = f_open.read() diff --git a/tests/spiffworkflow_backend/unit/test_git_service.py b/tests/spiffworkflow_backend/unit/test_git_service.py index ad3c814f..ed1e24e1 100644 --- a/tests/spiffworkflow_backend/unit/test_git_service.py +++ b/tests/spiffworkflow_backend/unit/test_git_service.py @@ -7,6 +7,7 @@ from spiffworkflow_backend.services.git_service import GitService class TestGitService(BaseTest): + """TestGitService.""" def test_strips_output_of_stdout_from_command( self, @@ -14,5 +15,8 @@ class TestGitService(BaseTest): client: FlaskClient, with_db_and_bpmn_file_cleanup: None, ) -> None: - output = GitService.run_shell_command_to_get_stdout(["echo", ' This output should not end in space or newline \n']) - assert output == 'This output should not end in space or newline' + """Test_strips_output_of_stdout_from_command.""" + output = GitService.run_shell_command_to_get_stdout( + ["echo", " This output should not end in space or newline \n"] + ) + assert output == "This output should not end in space or newline" From ebbb593e7f4723e09ffd554eb43c415493f6bf58 Mon Sep 17 00:00:00 2001 From: burnettk Date: Tue, 13 Dec 2022 08:34:08 -0500 Subject: [PATCH 04/14] lint --- tests/spiffworkflow_backend/unit/test_git_service.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/spiffworkflow_backend/unit/test_git_service.py b/tests/spiffworkflow_backend/unit/test_git_service.py index ad3c814f..ed1e24e1 100644 --- a/tests/spiffworkflow_backend/unit/test_git_service.py +++ b/tests/spiffworkflow_backend/unit/test_git_service.py @@ -7,6 +7,7 @@ from spiffworkflow_backend.services.git_service import GitService class TestGitService(BaseTest): + """TestGitService.""" def test_strips_output_of_stdout_from_command( self, @@ -14,5 +15,8 @@ class TestGitService(BaseTest): client: FlaskClient, with_db_and_bpmn_file_cleanup: None, ) -> None: - output = GitService.run_shell_command_to_get_stdout(["echo", ' This output should not end in space or newline \n']) - assert output == 'This output should not end in space or newline' + """Test_strips_output_of_stdout_from_command.""" + output = GitService.run_shell_command_to_get_stdout( + ["echo", " This output should not end in space or newline \n"] + ) + assert output == "This output should not end in space or newline" From 4c119caa0e3cdda03e721a3417e9cc7e5752cafe Mon Sep 17 00:00:00 2001 From: jasquat Date: Tue, 13 Dec 2022 14:16:28 -0500 Subject: [PATCH 05/14] some fixes to ensure we display the correct task data for the diagram elements w/ burnettk --- .../models/process_instance_metadata.py | 1 - src/spiffworkflow_backend/models/task.py | 10 +++++----- .../services/process_instance_service.py | 2 +- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/spiffworkflow_backend/models/process_instance_metadata.py b/src/spiffworkflow_backend/models/process_instance_metadata.py index c9003594..ff86c9ac 100644 --- a/src/spiffworkflow_backend/models/process_instance_metadata.py +++ b/src/spiffworkflow_backend/models/process_instance_metadata.py @@ -1,4 +1,3 @@ -"""Spiff_step_details.""" from dataclasses import dataclass from flask_bpmn.models.db import db diff --git a/src/spiffworkflow_backend/models/task.py b/src/spiffworkflow_backend/models/task.py index 019f4b64..60deda84 100644 --- a/src/spiffworkflow_backend/models/task.py +++ b/src/spiffworkflow_backend/models/task.py @@ -108,7 +108,7 @@ class Task: multi_instance_type: Union[MultiInstanceType, None] = None, multi_instance_count: str = "", multi_instance_index: str = "", - process_name: str = "", + process_identifier: str = "", properties: Union[dict, None] = None, process_instance_id: Union[int, None] = None, process_instance_status: Union[str, None] = None, @@ -153,7 +153,7 @@ class Task: self.multi_instance_index = ( multi_instance_index # And the index of the currently repeating task. ) - self.process_name = process_name + self.process_identifier = process_identifier self.properties = properties # Arbitrary extension properties from BPMN editor. if self.properties is None: @@ -179,7 +179,7 @@ class Task: "multi_instance_type": multi_instance_type, "multi_instance_count": self.multi_instance_count, "multi_instance_index": self.multi_instance_index, - "process_name": self.process_name, + "process_identifier": self.process_identifier, "properties": self.properties, "process_instance_id": self.process_instance_id, "process_instance_status": self.process_instance_status, @@ -285,7 +285,7 @@ class TaskSchema(Schema): "multi_instance_type", "multi_instance_count", "multi_instance_index", - "process_name", + "process_identifier", "properties", "process_instance_id", "form_schema", @@ -296,7 +296,7 @@ class TaskSchema(Schema): documentation = marshmallow.fields.String(required=False, allow_none=True) # form = marshmallow.fields.Nested(FormSchema, required=False, allow_none=True) title = marshmallow.fields.String(required=False, allow_none=True) - process_name = marshmallow.fields.String(required=False, allow_none=True) + process_identifier = marshmallow.fields.String(required=False, allow_none=True) lane = marshmallow.fields.String(required=False, allow_none=True) @marshmallow.post_load diff --git a/src/spiffworkflow_backend/services/process_instance_service.py b/src/spiffworkflow_backend/services/process_instance_service.py index 38c71715..5b2781a2 100644 --- a/src/spiffworkflow_backend/services/process_instance_service.py +++ b/src/spiffworkflow_backend/services/process_instance_service.py @@ -321,7 +321,7 @@ class ProcessInstanceService: multi_instance_type=mi_type, multi_instance_count=info["mi_count"], multi_instance_index=info["mi_index"], - process_name=spiff_task.task_spec._wf_spec.description, + process_identifier=spiff_task.task_spec._wf_spec.name, properties=props, parent=parent_id, call_activity_process_identifier=call_activity_process_identifier, From f15794a2ea7efaa6f538cc8d0aff1f2a30b8e472 Mon Sep 17 00:00:00 2001 From: jasquat Date: Tue, 13 Dec 2022 16:04:37 -0500 Subject: [PATCH 06/14] store subprocesses for spiff steps as well and do not save file as primary if one is already set w/ burnettk --- src/spiffworkflow_backend/routes/process_api_blueprint.py | 3 ++- .../services/process_instance_processor.py | 5 ++++- src/spiffworkflow_backend/services/spec_file_service.py | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/spiffworkflow_backend/routes/process_api_blueprint.py b/src/spiffworkflow_backend/routes/process_api_blueprint.py index 0093c62e..2a81463c 100644 --- a/src/spiffworkflow_backend/routes/process_api_blueprint.py +++ b/src/spiffworkflow_backend/routes/process_api_blueprint.py @@ -1433,7 +1433,8 @@ def process_instance_task_list( ) if step_detail is not None and process_instance.bpmn_json is not None: bpmn_json = json.loads(process_instance.bpmn_json) - bpmn_json["tasks"] = step_detail.task_json + bpmn_json["tasks"] = step_detail.task_json['tasks'] + bpmn_json["subprocesses"] = step_detail.task_json['subprocesses'] process_instance.bpmn_json = json.dumps(bpmn_json) processor = ProcessInstanceProcessor(process_instance) diff --git a/src/spiffworkflow_backend/services/process_instance_processor.py b/src/spiffworkflow_backend/services/process_instance_processor.py index ffe69fd7..535f8282 100644 --- a/src/spiffworkflow_backend/services/process_instance_processor.py +++ b/src/spiffworkflow_backend/services/process_instance_processor.py @@ -551,7 +551,10 @@ class ProcessInstanceProcessor: """SaveSpiffStepDetails.""" bpmn_json = self.serialize() wf_json = json.loads(bpmn_json) - task_json = wf_json["tasks"] + task_json = { + "tasks": wf_json["tasks"], + "subprocesses": wf_json["subprocesses"] + } return { "process_instance_id": self.process_instance_model.id, diff --git a/src/spiffworkflow_backend/services/spec_file_service.py b/src/spiffworkflow_backend/services/spec_file_service.py index c69f41c3..a31bc370 100644 --- a/src/spiffworkflow_backend/services/spec_file_service.py +++ b/src/spiffworkflow_backend/services/spec_file_service.py @@ -167,7 +167,7 @@ class SpecFileService(FileSystemService): for ref in references: # If no valid primary process is defined, default to the first process in the # updated file. - if not primary_process_ref and ref.type == "process" and ref.is_executable: + if not process_model_info.primary_file_name and not primary_process_ref and ref.type == "process" and ref.is_executable: ref.is_primary = True if ref.is_primary: From 36c509532a6223f06b6abeb665fbee39794e3c91 Mon Sep 17 00:00:00 2001 From: jasquat Date: Tue, 13 Dec 2022 16:44:46 -0500 Subject: [PATCH 07/14] pyl and fix test w/ burnettk --- .../models/process_instance_metadata.py | 1 + .../models/spiff_step_details.py | 2 +- .../routes/process_api_blueprint.py | 4 ++-- .../services/process_instance_processor.py | 5 +---- .../services/spec_file_service.py | 21 ++++++++++++------- 5 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/spiffworkflow_backend/models/process_instance_metadata.py b/src/spiffworkflow_backend/models/process_instance_metadata.py index ff86c9ac..f2e4c222 100644 --- a/src/spiffworkflow_backend/models/process_instance_metadata.py +++ b/src/spiffworkflow_backend/models/process_instance_metadata.py @@ -1,3 +1,4 @@ +"""Process_instance_metadata.""" from dataclasses import dataclass from flask_bpmn.models.db import db diff --git a/src/spiffworkflow_backend/models/spiff_step_details.py b/src/spiffworkflow_backend/models/spiff_step_details.py index 91d70116..9afb5d07 100644 --- a/src/spiffworkflow_backend/models/spiff_step_details.py +++ b/src/spiffworkflow_backend/models/spiff_step_details.py @@ -21,7 +21,7 @@ class SpiffStepDetailsModel(SpiffworkflowBaseDBModel): ForeignKey(ProcessInstanceModel.id), nullable=False # type: ignore ) spiff_step: int = db.Column(db.Integer, nullable=False) - task_json: str = deferred(db.Column(db.JSON, nullable=False)) # type: ignore + task_json: dict = deferred(db.Column(db.JSON, nullable=False)) # type: ignore timestamp: float = db.Column(db.DECIMAL(17, 6), nullable=False) completed_by_user_id: int = db.Column(db.Integer, nullable=True) lane_assignment_id: Optional[int] = db.Column( diff --git a/src/spiffworkflow_backend/routes/process_api_blueprint.py b/src/spiffworkflow_backend/routes/process_api_blueprint.py index 2a81463c..026ed35d 100644 --- a/src/spiffworkflow_backend/routes/process_api_blueprint.py +++ b/src/spiffworkflow_backend/routes/process_api_blueprint.py @@ -1433,8 +1433,8 @@ def process_instance_task_list( ) if step_detail is not None and process_instance.bpmn_json is not None: bpmn_json = json.loads(process_instance.bpmn_json) - bpmn_json["tasks"] = step_detail.task_json['tasks'] - bpmn_json["subprocesses"] = step_detail.task_json['subprocesses'] + bpmn_json["tasks"] = step_detail.task_json["tasks"] + bpmn_json["subprocesses"] = step_detail.task_json["subprocesses"] process_instance.bpmn_json = json.dumps(bpmn_json) processor = ProcessInstanceProcessor(process_instance) diff --git a/src/spiffworkflow_backend/services/process_instance_processor.py b/src/spiffworkflow_backend/services/process_instance_processor.py index 535f8282..5edc526c 100644 --- a/src/spiffworkflow_backend/services/process_instance_processor.py +++ b/src/spiffworkflow_backend/services/process_instance_processor.py @@ -551,10 +551,7 @@ class ProcessInstanceProcessor: """SaveSpiffStepDetails.""" bpmn_json = self.serialize() wf_json = json.loads(bpmn_json) - task_json = { - "tasks": wf_json["tasks"], - "subprocesses": wf_json["subprocesses"] - } + task_json = {"tasks": wf_json["tasks"], "subprocesses": wf_json["subprocesses"]} return { "process_instance_id": self.process_instance_model.id, diff --git a/src/spiffworkflow_backend/services/spec_file_service.py b/src/spiffworkflow_backend/services/spec_file_service.py index a31bc370..72f59d1f 100644 --- a/src/spiffworkflow_backend/services/spec_file_service.py +++ b/src/spiffworkflow_backend/services/spec_file_service.py @@ -167,17 +167,22 @@ class SpecFileService(FileSystemService): for ref in references: # If no valid primary process is defined, default to the first process in the # updated file. - if not process_model_info.primary_file_name and not primary_process_ref and ref.type == "process" and ref.is_executable: + if not primary_process_ref and ref.type == "process" and ref.is_executable: ref.is_primary = True if ref.is_primary: - ProcessModelService.update_process_model( - process_model_info, - { - "primary_process_id": ref.identifier, - "primary_file_name": file_name, - }, - ) + update_hash = {} + if not process_model_info.primary_file_name: + update_hash["primary_process_id"] = ref.identifier + update_hash["primary_file_name"] = file_name + elif file_name == process_model_info.primary_file_name: + update_hash["primary_process_id"] = ref.identifier + + if len(update_hash) > 0: + ProcessModelService.update_process_model( + process_model_info, + update_hash, + ) SpecFileService.update_caches(ref) return file From 7e39994c91edf275373bb5026f4c989b645f7a9a Mon Sep 17 00:00:00 2001 From: burnettk Date: Tue, 13 Dec 2022 23:21:32 -0500 Subject: [PATCH 08/14] in postgres you cannot order by a non-grouped column without doing an aggregate --- src/spiffworkflow_backend/routes/process_api_blueprint.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/spiffworkflow_backend/routes/process_api_blueprint.py b/src/spiffworkflow_backend/routes/process_api_blueprint.py index 026ed35d..ea7b675f 100644 --- a/src/spiffworkflow_backend/routes/process_api_blueprint.py +++ b/src/spiffworkflow_backend/routes/process_api_blueprint.py @@ -1025,11 +1025,11 @@ def process_instance_list( elif attribute in instance_metadata_aliases: if order_by_option.startswith("-"): order_by_query_array.append( - instance_metadata_aliases[attribute].value.desc() + func.max(instance_metadata_aliases[attribute].value).desc() ) else: order_by_query_array.append( - instance_metadata_aliases[attribute].value.asc() + func.max(instance_metadata_aliases[attribute].value).asc() ) process_instances = ( From 562ec8efd319924068e027d4b79854fb8ed81ea4 Mon Sep 17 00:00:00 2001 From: jasquat Date: Wed, 14 Dec 2022 11:00:32 -0500 Subject: [PATCH 09/14] added permission file for staging w/ burnettk --- .../config/permissions/staging.yml | 165 ++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100644 src/spiffworkflow_backend/config/permissions/staging.yml diff --git a/src/spiffworkflow_backend/config/permissions/staging.yml b/src/spiffworkflow_backend/config/permissions/staging.yml new file mode 100644 index 00000000..90c157bf --- /dev/null +++ b/src/spiffworkflow_backend/config/permissions/staging.yml @@ -0,0 +1,165 @@ +default_group: everybody + +groups: + admin: + users: + [ + admin, + jakub, + kb, + alex, + dan, + mike, + jason, + j, + jarrad, + elizabeth, + jon, + natalia, + ] + + Finance Team: + users: + [ + jakub, + alex, + dan, + mike, + jason, + j, + amir, + jarrad, + elizabeth, + jon, + natalia, + sasha, + fin, + fin1, + ] + + demo: + users: + [ + core, + fin, + fin1, + harmeet, + sasha, + manuchehr, + lead, + lead1 + ] + + core-contributor: + users: + [ + core, + harmeet, + ] + +permissions: + admin: + groups: [admin] + users: [] + allowed_permissions: [read] + uri: /* + admin-process-instances: + groups: [admin] + users: [] + allowed_permissions: [create, read, update, delete] + uri: /process-instances/* + + tasks-crud: + groups: [everybody] + users: [] + allowed_permissions: [create, read, update, delete] + uri: /v1.0/tasks/* + + service-tasks: + groups: [everybody] + users: [] + allowed_permissions: [read] + uri: /v1.0/service-tasks + + + # read all for everybody + read-all-process-groups: + groups: [everybody] + users: [] + allowed_permissions: [read] + uri: /v1.0/process-groups/* + read-all-process-models: + groups: [everybody] + users: [] + allowed_permissions: [read] + uri: /v1.0/process-models/* + read-all-process-instance: + groups: [everybody] + users: [] + allowed_permissions: [read] + uri: /v1.0/process-instances/* + read-process-instance-reports: + groups: [everybody] + users: [] + allowed_permissions: [read] + uri: /v1.0/process-instances/reports/* + processes-read: + groups: [everybody] + users: [] + allowed_permissions: [read] + uri: /v1.0/processes + + + manage-procurement-admin-instances: + groups: ["Project Lead"] + users: [] + allowed_permissions: [create, read, update, delete] + uri: /v1.0/process-instances/manage-procurement:* + manage-procurement-admin-instances-slash: + groups: ["Project Lead"] + users: [] + allowed_permissions: [create, read, update, delete] + uri: /v1.0/process-instances/manage-procurement/* + manage-procurement-admin-instance-logs: + groups: ["Project Lead"] + users: [] + allowed_permissions: [read] + uri: /v1.0/logs/manage-procurement:* + manage-procurement-admin-instance-logs-slash: + groups: ["Project Lead"] + users: [] + allowed_permissions: [read] + uri: /v1.0/logs/manage-procurement/* + + manage-revenue-streams-instances: + groups: ["core-contributor", "demo"] + users: [] + allowed_permissions: [create, read] + uri: /v1.0/process-instances/manage-revenue-streams:product-revenue-streams:customer-contracts-trade-terms/* + manage-revenue-streams-instance-logs: + groups: ["core-contributor", "demo"] + users: [] + allowed_permissions: [read] + uri: /v1.0/logs/manage-revenue-streams:product-revenue-streams:customer-contracts-trade-terms/* + + manage-procurement-invoice-instances: + groups: ["core-contributor", "demo"] + users: [] + allowed_permissions: [create, read] + uri: /v1.0/process-instances/manage-procurement:procurement:core-contributor-invoice-management:* + manage-procurement-invoice-instance-logs: + groups: ["core-contributor", "demo"] + users: [] + allowed_permissions: [read] + uri: /v1.0/logs/manage-procurement:procurement:core-contributor-invoice-management:* + + manage-procurement-instances: + groups: ["core-contributor", "demo"] + users: [] + allowed_permissions: [create, read] + uri: /v1.0/process-instances/manage-procurement:vendor-lifecycle-management:* + manage-procurement-instance-logs: + groups: ["core-contributor", "demo"] + users: [] + allowed_permissions: [read] + uri: /v1.0/logs/manage-procurement:vendor-lifecycle-management:* From 900f4524933818ccac3a33a7103d8d6d2fe6310e Mon Sep 17 00:00:00 2001 From: jasquat Date: Wed, 14 Dec 2022 14:35:08 -0500 Subject: [PATCH 10/14] load the correct perm file on staging w/ burnettk --- src/spiffworkflow_backend/config/staging.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/spiffworkflow_backend/config/staging.py b/src/spiffworkflow_backend/config/staging.py index 5f0fec4c..9cc24705 100644 --- a/src/spiffworkflow_backend/config/staging.py +++ b/src/spiffworkflow_backend/config/staging.py @@ -4,3 +4,4 @@ from os import environ GIT_BRANCH = environ.get("GIT_BRANCH_TO_PUBLISH_TO", default="staging") GIT_BRANCH_TO_PUBLISH_TO = environ.get("GIT_BRANCH_TO_PUBLISH_TO", default="main") GIT_COMMIT_ON_SAVE = False +SPIFFWORKFLOW_BACKEND_PERMISSIONS_FILE_NAME = "staging.yml" From 511d31b902095c7cda9d950b435e6ddbb0b2538c Mon Sep 17 00:00:00 2001 From: jasquat Date: Wed, 14 Dec 2022 15:03:22 -0500 Subject: [PATCH 11/14] fixed perms for readonly for staging w/ burnettk --- .../config/permissions/development.yml | 19 +++++++++++++++++-- .../config/permissions/staging.yml | 2 +- .../services/process_model_service.py | 2 +- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/spiffworkflow_backend/config/permissions/development.yml b/src/spiffworkflow_backend/config/permissions/development.yml index 419c925f..99790fed 100644 --- a/src/spiffworkflow_backend/config/permissions/development.yml +++ b/src/spiffworkflow_backend/config/permissions/development.yml @@ -17,7 +17,6 @@ groups: dan, mike, jason, - j, jarrad, elizabeth, jon, @@ -32,7 +31,6 @@ groups: dan, mike, jason, - j, amir, jarrad, elizabeth, @@ -63,6 +61,12 @@ groups: harmeet, ] + admin-ro: + users: + [ + j, + ] + permissions: admin: groups: [admin] @@ -70,6 +74,17 @@ permissions: allowed_permissions: [create, read, update, delete] uri: /* + admin-readonly: + groups: [admin-ro] + users: [] + allowed_permissions: [read] + uri: /* + admin-process-instances-for-readonly: + groups: [admin-ro] + users: [] + allowed_permissions: [create, read, update, delete] + uri: /v1.0/process-instances/* + tasks-crud: groups: [everybody] users: [] diff --git a/src/spiffworkflow_backend/config/permissions/staging.yml b/src/spiffworkflow_backend/config/permissions/staging.yml index 90c157bf..982b945c 100644 --- a/src/spiffworkflow_backend/config/permissions/staging.yml +++ b/src/spiffworkflow_backend/config/permissions/staging.yml @@ -67,7 +67,7 @@ permissions: groups: [admin] users: [] allowed_permissions: [create, read, update, delete] - uri: /process-instances/* + uri: /v1.0/process-instances/* tasks-crud: groups: [everybody] diff --git a/src/spiffworkflow_backend/services/process_model_service.py b/src/spiffworkflow_backend/services/process_model_service.py index 964981a8..67be986e 100644 --- a/src/spiffworkflow_backend/services/process_model_service.py +++ b/src/spiffworkflow_backend/services/process_model_service.py @@ -223,7 +223,7 @@ class ProcessModelService(FileSystemService): user = UserService.current_user() new_process_model_list = [] for process_model in process_models: - uri = f"/v1.0/process-models/{process_model.id.replace('/', ':')}/process-instances" + uri = f"/v1.0/process-instances/{process_model.id.replace('/', ':')}" result = AuthorizationService.user_has_permission( user=user, permission="create", target_uri=uri ) From 7e520dbc01bab8b0dc196b364d3a7d810867ff32 Mon Sep 17 00:00:00 2001 From: jasquat Date: Wed, 14 Dec 2022 16:32:07 -0500 Subject: [PATCH 12/14] remove assert statements from actual code w/ burnettk --- .../routes/process_api_blueprint.py | 1 - src/spiffworkflow_backend/routes/user.py | 9 +++++---- .../services/authentication_service.py | 4 ++++ .../services/service_task_service.py | 1 - 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/spiffworkflow_backend/routes/process_api_blueprint.py b/src/spiffworkflow_backend/routes/process_api_blueprint.py index ea7b675f..74e5a7e7 100644 --- a/src/spiffworkflow_backend/routes/process_api_blueprint.py +++ b/src/spiffworkflow_backend/routes/process_api_blueprint.py @@ -1898,7 +1898,6 @@ def secret_list( def add_secret(body: Dict) -> Response: """Add secret.""" secret_model = SecretService().add_secret(body["key"], body["value"], g.user.id) - assert secret_model # noqa: S101 return Response( json.dumps(SecretModelSchema().dump(secret_model)), status=201, diff --git a/src/spiffworkflow_backend/routes/user.py b/src/spiffworkflow_backend/routes/user.py index 2bbbc137..ad98fbbc 100644 --- a/src/spiffworkflow_backend/routes/user.py +++ b/src/spiffworkflow_backend/routes/user.py @@ -16,8 +16,9 @@ from flask_bpmn.api.api_error import ApiError from werkzeug.wrappers import Response from spiffworkflow_backend.models.user import UserModel +from spiffworkflow_backend.services.authentication_service import AuthenticationService from spiffworkflow_backend.services.authentication_service import ( - AuthenticationService, + MissingAccessTokenError, ) from spiffworkflow_backend.services.authorization_service import AuthorizationService from spiffworkflow_backend.services.user_service import UserService @@ -268,10 +269,10 @@ def login_api_return(code: str, state: str, session_state: str) -> str: code, "/v1.0/login_api_return" ) access_token: str = auth_token_object["access_token"] - assert access_token # noqa: S101 + if access_token is None: + raise MissingAccessTokenError("Cannot find the access token for the request") + return access_token - # return redirect("localhost:7000/v1.0/ui") - # return {'uid': 'user_1'} def logout(id_token: str, redirect_url: Optional[str]) -> Response: diff --git a/src/spiffworkflow_backend/services/authentication_service.py b/src/spiffworkflow_backend/services/authentication_service.py index f4bd357b..95c1eaa8 100644 --- a/src/spiffworkflow_backend/services/authentication_service.py +++ b/src/spiffworkflow_backend/services/authentication_service.py @@ -16,6 +16,10 @@ from werkzeug.wrappers import Response from spiffworkflow_backend.models.refresh_token import RefreshTokenModel +class MissingAccessTokenError(Exception): + """MissingAccessTokenError.""" + + class AuthenticationProviderTypes(enum.Enum): """AuthenticationServiceProviders.""" diff --git a/src/spiffworkflow_backend/services/service_task_service.py b/src/spiffworkflow_backend/services/service_task_service.py index 15e25a75..6fec8b79 100644 --- a/src/spiffworkflow_backend/services/service_task_service.py +++ b/src/spiffworkflow_backend/services/service_task_service.py @@ -31,7 +31,6 @@ class ServiceTaskDelegate: if value.startswith(secret_prefix): key = value.removeprefix(secret_prefix) secret = SecretService().get_secret(key) - assert secret # noqa: S101 return secret.value file_prefix = "file:" From ec01b27984c60d43a437125b1ae110f009a2c7b4 Mon Sep 17 00:00:00 2001 From: jasquat Date: Wed, 14 Dec 2022 17:09:43 -0500 Subject: [PATCH 13/14] some fixes for ci w/ burnettk --- bin/build_and_run_with_docker_compose | 2 +- docker-compose.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bin/build_and_run_with_docker_compose b/bin/build_and_run_with_docker_compose index 4356d974..2dfa896e 100755 --- a/bin/build_and_run_with_docker_compose +++ b/bin/build_and_run_with_docker_compose @@ -9,7 +9,7 @@ set -o errtrace -o errexit -o nounset -o pipefail if [[ -z "${BPMN_SPEC_ABSOLUTE_DIR:-}" ]]; then script_dir="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" - export BPMN_SPEC_ABSOLUTE_DIR="$script_dir/../../sample-process-models" + export BPMN_SPEC_ABSOLUTE_DIR="$script_dir/../../../sample-process-models" fi if [[ -z "${SPIFFWORKFLOW_BACKEND_DOCKER_COMPOSE_PROFILE:-}" ]]; then diff --git a/docker-compose.yml b/docker-compose.yml index 1cbe9dcb..410cbb7a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -68,7 +68,7 @@ services: - "7000:7000" network_mode: host volumes: - - ${BPMN_SPEC_ABSOLUTE_DIR:-./../sample-process-models}:/app/process_models + - ${BPMN_SPEC_ABSOLUTE_DIR:-../../sample-process-models}:/app/process_models - ./log:/app/log healthcheck: test: curl localhost:7000/v1.0/status --fail @@ -82,7 +82,7 @@ services: profiles: - debug volumes: - - ${BPMN_SPEC_ABSOLUTE_DIR:-./../sample-process-models}:/app/process_models + - ${BPMN_SPEC_ABSOLUTE_DIR:-../../sample-process-models}:/app/process_models - ./:/app command: /app/bin/boot_in_docker_debug_mode From 9af47b207b894f210fe0ecc44e6150ebe92d6f40 Mon Sep 17 00:00:00 2001 From: Jon Herron Date: Wed, 14 Dec 2022 18:23:38 -0500 Subject: [PATCH 14/14] Fix api endpoints for script unit tests --- src/spiffworkflow_backend/api.yml | 20 ++++--------------- .../routes/process_api_blueprint.py | 6 +++--- 2 files changed, 7 insertions(+), 19 deletions(-) diff --git a/src/spiffworkflow_backend/api.yml b/src/spiffworkflow_backend/api.yml index 84ada234..c559ba98 100755 --- a/src/spiffworkflow_backend/api.yml +++ b/src/spiffworkflow_backend/api.yml @@ -610,15 +610,9 @@ paths: items: $ref: "#/components/schemas/Workflow" - /process-models/{process_group_id}/{process_model_id}/script-unit-tests: + /process-models/{modified_process_model_identifier}/script-unit-tests: parameters: - - name: process_group_id - in: path - required: true - description: The unique id of an existing process group - schema: - type: string - - name: process_model_id + - name: modified_process_model_identifier in: path required: true description: The unique id of an existing process model. @@ -637,15 +631,9 @@ paths: schema: $ref: "#/components/schemas/Workflow" - /process-models/{process_group_id}/{process_model_id}/script-unit-tests/run: + /process-models/{modified_process_model_identifier}/script-unit-tests/run: parameters: - - name: process_group_id - in: path - required: true - description: The unique id of an existing process group - schema: - type: string - - name: process_model_id + - name: modified_process_model_identifier in: path required: true description: The unique id of an existing process model. diff --git a/src/spiffworkflow_backend/routes/process_api_blueprint.py b/src/spiffworkflow_backend/routes/process_api_blueprint.py index 74e5a7e7..9a616836 100644 --- a/src/spiffworkflow_backend/routes/process_api_blueprint.py +++ b/src/spiffworkflow_backend/routes/process_api_blueprint.py @@ -1613,7 +1613,7 @@ def task_submit( def script_unit_test_create( - process_group_id: str, process_model_id: str, body: Dict[str, Union[str, bool, int]] + modified_process_model_identifier: str, body: Dict[str, Union[str, bool, int]] ) -> flask.wrappers.Response: """Script_unit_test_create.""" bpmn_task_identifier = _get_required_parameter_or_raise( @@ -1624,7 +1624,7 @@ def script_unit_test_create( "expected_output_json", body ) - process_model_identifier = f"{process_group_id}/{process_model_id}" + process_model_identifier = modified_process_model_identifier.replace(":", "/") process_model = get_process_model(process_model_identifier) file = SpecFileService.get_files(process_model, process_model.primary_file_name)[0] if file is None: @@ -1702,7 +1702,7 @@ def script_unit_test_create( def script_unit_test_run( - process_group_id: str, process_model_id: str, body: Dict[str, Union[str, bool, int]] + modified_process_model_identifier: str, body: Dict[str, Union[str, bool, int]] ) -> flask.wrappers.Response: """Script_unit_test_run.""" # FIXME: We should probably clear this somewhere else but this works