From d12e606c8409d1981155c1793e190e4a248320f5 Mon Sep 17 00:00:00 2001 From: jasquat Date: Mon, 28 Nov 2022 15:26:50 -0500 Subject: [PATCH] added script to save process instance metadata and fixed permissions issue w/ burnettk cullerton --- bin/run_pyl | 7 +++ .../{568f5fe2c9f8_.py => ff1c1628337c_.py} | 43 +++++++++------ .../config/permissions/development.yml | 51 ++++++++---------- .../terraform_deployed_environment.yml | 51 ++++++++---------- .../load_database_models.py | 3 ++ .../models/process_instance.py | 4 ++ .../models/process_instance_metadata.py | 30 +++++++++++ .../models/spiff_step_details.py | 5 +- .../scripts/save_process_instance_metadata.py | 35 +++++++++++++ .../save_process_instance_metadata.bpmn | 52 +++++++++++++++++++ .../test_save_process_instance_metadata.py | 42 +++++++++++++++ .../src/components/ProcessModelListTiles.tsx | 1 + 12 files changed, 248 insertions(+), 76 deletions(-) rename spiffworkflow-backend/migrations/versions/{568f5fe2c9f8_.py => ff1c1628337c_.py} (95%) create mode 100644 spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance_metadata.py create mode 100644 spiffworkflow-backend/src/spiffworkflow_backend/scripts/save_process_instance_metadata.py create mode 100644 spiffworkflow-backend/tests/data/save_process_instance_metadata/save_process_instance_metadata.bpmn create mode 100644 spiffworkflow-backend/tests/spiffworkflow_backend/scripts/test_save_process_instance_metadata.py diff --git a/bin/run_pyl b/bin/run_pyl index af82c6a6..d3abd7fc 100755 --- a/bin/run_pyl +++ b/bin/run_pyl @@ -20,6 +20,12 @@ function get_python_dirs() { (git ls-tree -r HEAD --name-only | grep -E '\.py$' | awk -F '/' '{print $1}' | sort | uniq | grep -v '\.' | grep -Ev '^(bin|migrations)$') || echo '' } +function run_fix_docstrings() { + if command -v fix_python_docstrings >/dev/null ; then + fix_python_docstrings $(get_top_level_directories_containing_python_files) + fi +} + function run_autoflake() { if ! command -v autoflake8 >/dev/null ; then pip install autoflake8 @@ -51,6 +57,7 @@ done for python_project in "${python_projects[@]}" ; do pushd "$python_project" + run_fix_docstrings || run_fix_docstrings run_autoflake || run_autoflake popd done diff --git a/spiffworkflow-backend/migrations/versions/568f5fe2c9f8_.py b/spiffworkflow-backend/migrations/versions/ff1c1628337c_.py similarity index 95% rename from spiffworkflow-backend/migrations/versions/568f5fe2c9f8_.py rename to spiffworkflow-backend/migrations/versions/ff1c1628337c_.py index bfd7fc2b..d8da6d3c 100644 --- a/spiffworkflow-backend/migrations/versions/568f5fe2c9f8_.py +++ b/spiffworkflow-backend/migrations/versions/ff1c1628337c_.py @@ -1,8 +1,8 @@ """empty message -Revision ID: 568f5fe2c9f8 +Revision ID: ff1c1628337c Revises: -Create Date: 2022-11-24 12:11:46.669020 +Create Date: 2022-11-28 15:08:52.014254 """ from alembic import op @@ -10,7 +10,7 @@ import sqlalchemy as sa # revision identifiers, used by Alembic. -revision = '568f5fe2c9f8' +revision = 'ff1c1628337c' down_revision = None branch_labels = None depends_on = None @@ -166,17 +166,6 @@ def upgrade(): sa.PrimaryKeyConstraint('id'), sa.UniqueConstraint('key') ) - op.create_table('spiff_step_details', - sa.Column('id', sa.Integer(), nullable=False), - sa.Column('process_instance_id', sa.Integer(), nullable=False), - sa.Column('spiff_step', sa.Integer(), nullable=False), - sa.Column('task_json', sa.JSON(), nullable=False), - sa.Column('timestamp', sa.DECIMAL(precision=17, scale=6), nullable=False), - sa.Column('completed_by_user_id', sa.Integer(), nullable=True), - sa.Column('lane_assignment_id', sa.Integer(), nullable=True), - sa.ForeignKeyConstraint(['lane_assignment_id'], ['group.id'], ), - sa.PrimaryKeyConstraint('id') - ) op.create_table('user_group_assignment', sa.Column('id', sa.Integer(), nullable=False), sa.Column('user_id', sa.Integer(), nullable=False), @@ -249,6 +238,29 @@ def upgrade(): sa.PrimaryKeyConstraint('id'), sa.UniqueConstraint('principal_id', 'permission_target_id', 'permission', name='permission_assignment_uniq') ) + op.create_table('process_instance_metadata', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('process_instance_id', sa.Integer(), nullable=False), + sa.Column('key', sa.String(length=255), nullable=False), + sa.Column('value', sa.String(length=255), nullable=False), + sa.Column('updated_at_in_seconds', sa.Integer(), nullable=False), + sa.Column('created_at_in_seconds', sa.Integer(), nullable=False), + sa.ForeignKeyConstraint(['process_instance_id'], ['process_instance.id'], ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('process_instance_id', 'key', name='process_instance_metadata_unique') + ) + op.create_table('spiff_step_details', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('process_instance_id', sa.Integer(), nullable=False), + sa.Column('spiff_step', sa.Integer(), nullable=False), + sa.Column('task_json', sa.JSON(), nullable=False), + sa.Column('timestamp', sa.DECIMAL(precision=17, scale=6), nullable=False), + sa.Column('completed_by_user_id', sa.Integer(), nullable=True), + sa.Column('lane_assignment_id', sa.Integer(), nullable=True), + sa.ForeignKeyConstraint(['lane_assignment_id'], ['group.id'], ), + sa.ForeignKeyConstraint(['process_instance_id'], ['process_instance.id'], ), + sa.PrimaryKeyConstraint('id') + ) op.create_table('active_task_user', sa.Column('id', sa.Integer(), nullable=False), sa.Column('active_task_id', sa.Integer(), nullable=False), @@ -282,6 +294,8 @@ def downgrade(): op.drop_index(op.f('ix_active_task_user_user_id'), table_name='active_task_user') op.drop_index(op.f('ix_active_task_user_active_task_id'), table_name='active_task_user') op.drop_table('active_task_user') + op.drop_table('spiff_step_details') + op.drop_table('process_instance_metadata') op.drop_table('permission_assignment') op.drop_table('message_instance') op.drop_index(op.f('ix_message_correlation_value'), table_name='message_correlation') @@ -291,7 +305,6 @@ def downgrade(): op.drop_table('message_correlation') op.drop_table('active_task') op.drop_table('user_group_assignment') - op.drop_table('spiff_step_details') op.drop_table('secret') op.drop_table('refresh_token') op.drop_index(op.f('ix_process_instance_report_identifier'), table_name='process_instance_report') diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/development.yml b/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/development.yml index 91207f86..e17e3f11 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/development.yml +++ b/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/development.yml @@ -136,47 +136,38 @@ permissions: allowed_permissions: [create, read, update, delete] uri: /v1.0/process-groups/manage-procurement:procurement:* - demo-models-instantiate-vendor-block: - groups: ["demo"] + manage-revenue-streams-instantiate: + groups: ["core-contributor", "demo"] users: [] allowed_permissions: [create] - uri: /v1.0/process-models/manage-procurement:vendor-lifecycle-management:vendor-md-maintenance:vendor-md-block/process-instances - demo-models-instantiate-vendor-change: - groups: ["demo"] + uri: /v1.0/process-models/manage-revenue-streams:product-revenue-streams:customer-contracts-trade-terms/* + manage-revenue-streams-instances: + groups: ["core-contributor", "demo"] users: [] - allowed_permissions: [create] - uri: /v1.0/process-models/manage-procurement:vendor-lifecycle-management:vendor-md-maintenance:vendor-md-change/process-instances - demo-models-instantiate-vendor-creation: - groups: ["demo"] - users: [] - allowed_permissions: [create] - uri: /v1.0/process-models/manage-procurement:vendor-lifecycle-management:vendor-md-maintenance:vendor-md-creation/process-instances - demo-models-instantiate-vendor-core-invoice_appoval: - groups: ["demo"] - users: [] - allowed_permissions: [create] - uri: /v1.0/process-models/manage-procurement:procurement:core-contributor-invoice-management:cc-invoice-approval/process-instances - demo-models-customer-contracts: - groups: ["demo"] - users: [] - allowed_permissions: [create] - uri: /v1.0/process-models/manage-revenue-streams:product-revenue-streams:customer-contracts-trade-terms/process-instances + allowed_permissions: [create, read] + uri: /v1.0/process-instances/manage-revenue-streams:product-revenue-streams:customer-contracts-trade-terms/* - core-admin-models-instantiate: - groups: ["core-contributor"] + manage-procurement-invoice-instantiate: + groups: ["core-contributor", "demo"] users: [] allowed_permissions: [create] - uri: /v1.0/process-models/manage-procurement:procurement:core-contributor-invoice-management:cc-invoice-approval/process-instances - core-admin-instances: - groups: ["core-contributor"] + uri: /v1.0/process-models/manage-procurement:procurement:core-contributor-invoice-management:* + 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:* - core-admin-instances-slash: - groups: ["core-contributor"] + + manage-procurement-instantiate: + groups: ["core-contributor", "demo"] + users: [] + allowed_permissions: [create] + uri: /v1.0/process-models/manage-procurement:vendor-lifecycle-management:* + manage-procurement-instances: + groups: ["core-contributor", "demo"] users: [] allowed_permissions: [create, read] - uri: /v1.0/process-instances/manage-procurement:procurement:core-contributor-invoice-management/* + uri: /v1.0/process-instances/manage-procurement:vendor-lifecycle-management:* core1-admin-models-instantiate: groups: ["core-contributor"] diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/terraform_deployed_environment.yml b/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/terraform_deployed_environment.yml index c1a0579c..e60946b3 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/terraform_deployed_environment.yml +++ b/spiffworkflow-backend/src/spiffworkflow_backend/config/permissions/terraform_deployed_environment.yml @@ -136,44 +136,35 @@ permissions: allowed_permissions: [create, read, update, delete] uri: /v1.0/process-groups/manage-procurement:procurement:* - demo-models-instantiate-vendor-block: - groups: ["demo"] + manage-revenue-streams-instantiate: + groups: ["core-contributor", "demo"] users: [] allowed_permissions: [create] - uri: /v1.0/process-models/manage-procurement:vendor-lifecycle-management:vendor-md-maintenance:vendor-md-block/process-instances - demo-models-instantiate-vendor-change: - groups: ["demo"] + uri: /v1.0/process-models/manage-revenue-streams:product-revenue-streams:customer-contracts-trade-terms/* + manage-revenue-streams-instances: + groups: ["core-contributor", "demo"] users: [] - allowed_permissions: [create] - uri: /v1.0/process-models/manage-procurement:vendor-lifecycle-management:vendor-md-maintenance:vendor-md-change/process-instances - demo-models-instantiate-vendor-creation: - groups: ["demo"] - users: [] - allowed_permissions: [create] - uri: /v1.0/process-models/manage-procurement:vendor-lifecycle-management:vendor-md-maintenance:vendor-md-creation/process-instances - demo-models-instantiate-vendor-core-invoice_appoval: - groups: ["demo"] - users: [] - allowed_permissions: [create] - uri: /v1.0/process-models/manage-procurement:procurement:core-contributor-invoice-management:cc-invoice-approval/process-instances - demo-models-customer-contracts: - groups: ["demo"] - users: [] - allowed_permissions: [create] - uri: /v1.0/process-models/manage-revenue-streams:product-revenue-streams:customer-contracts-trade-terms/process-instances + allowed_permissions: [create, read] + uri: /v1.0/process-instances/manage-revenue-streams:product-revenue-streams:customer-contracts-trade-terms/* - core-admin-models-instantiate: - groups: ["core-contributor"] + manage-procurement-invoice-instantiate: + groups: ["core-contributor", "demo"] users: [] allowed_permissions: [create] - uri: /v1.0/process-models/manage-procurement:procurement:core-contributor-invoice-management:cc-invoice-approval/process-instances - core-admin-instances: - groups: ["core-contributor"] + uri: /v1.0/process-models/manage-procurement:procurement:core-contributor-invoice-management:* + 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:* - core-admin-instances-slash: - groups: ["core-contributor"] + + manage-procurement-instantiate: + groups: ["core-contributor", "demo"] + users: [] + allowed_permissions: [create] + uri: /v1.0/process-models/manage-procurement:vendor-lifecycle-management:* + manage-procurement-instances: + groups: ["core-contributor", "demo"] users: [] allowed_permissions: [create, read] - uri: /v1.0/process-instances/manage-procurement:procurement:core-contributor-invoice-management/* + uri: /v1.0/process-instances/manage-procurement:vendor-lifecycle-management:* diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/load_database_models.py b/spiffworkflow-backend/src/spiffworkflow_backend/load_database_models.py index 97c99000..71adb57c 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/load_database_models.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/load_database_models.py @@ -51,5 +51,8 @@ from spiffworkflow_backend.models.spiff_step_details import ( ) # noqa: F401 from spiffworkflow_backend.models.user import UserModel # noqa: F401 from spiffworkflow_backend.models.group import GroupModel # noqa: F401 +from spiffworkflow_backend.models.process_instance_metadata import ( + ProcessInstanceMetadataModel, +) # noqa: F401 add_listeners() diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance.py b/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance.py index f06eb953..e6a5f684 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance.py @@ -22,6 +22,10 @@ from spiffworkflow_backend.models.task import TaskSchema from spiffworkflow_backend.models.user import UserModel +class ProcessInstanceNotFoundError(Exception): + """ProcessInstanceNotFoundError.""" + + class NavigationItemSchema(Schema): """NavigationItemSchema.""" diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance_metadata.py b/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance_metadata.py new file mode 100644 index 00000000..5a4d4ca5 --- /dev/null +++ b/spiffworkflow-backend/src/spiffworkflow_backend/models/process_instance_metadata.py @@ -0,0 +1,30 @@ +"""Spiff_step_details.""" +from dataclasses import dataclass + +from flask_bpmn.models.db import db +from flask_bpmn.models.db import SpiffworkflowBaseDBModel +from sqlalchemy import ForeignKey + +from spiffworkflow_backend.models.process_instance import ProcessInstanceModel + + +@dataclass +class ProcessInstanceMetadataModel(SpiffworkflowBaseDBModel): + """ProcessInstanceMetadataModel.""" + + __tablename__ = "process_instance_metadata" + __table_args__ = ( + db.UniqueConstraint( + "process_instance_id", "key", name="process_instance_metadata_unique" + ), + ) + + id: int = db.Column(db.Integer, primary_key=True) + process_instance_id: int = db.Column( + ForeignKey(ProcessInstanceModel.id), nullable=False # type: ignore + ) + key: str = db.Column(db.String(255), nullable=False) + value: str = db.Column(db.String(255), nullable=False) + + updated_at_in_seconds: int = db.Column(db.Integer, nullable=False) + created_at_in_seconds: int = db.Column(db.Integer, nullable=False) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/models/spiff_step_details.py b/spiffworkflow-backend/src/spiffworkflow_backend/models/spiff_step_details.py index e00e7cac..91d70116 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/models/spiff_step_details.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/models/spiff_step_details.py @@ -8,6 +8,7 @@ from sqlalchemy import ForeignKey from sqlalchemy.orm import deferred from spiffworkflow_backend.models.group import GroupModel +from spiffworkflow_backend.models.process_instance import ProcessInstanceModel @dataclass @@ -16,7 +17,9 @@ class SpiffStepDetailsModel(SpiffworkflowBaseDBModel): __tablename__ = "spiff_step_details" id: int = db.Column(db.Integer, primary_key=True) - process_instance_id: int = db.Column(db.Integer, nullable=False) + process_instance_id: int = db.Column( + 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 timestamp: float = db.Column(db.DECIMAL(17, 6), nullable=False) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/scripts/save_process_instance_metadata.py b/spiffworkflow-backend/src/spiffworkflow_backend/scripts/save_process_instance_metadata.py new file mode 100644 index 00000000..7176f6a5 --- /dev/null +++ b/spiffworkflow-backend/src/spiffworkflow_backend/scripts/save_process_instance_metadata.py @@ -0,0 +1,35 @@ +"""Get_env.""" +from typing import Any +from flask_bpmn.models.db import db + +from spiffworkflow_backend.models.process_instance_metadata import ProcessInstanceMetadataModel +from spiffworkflow_backend.models.script_attributes_context import ( + ScriptAttributesContext, +) +from spiffworkflow_backend.scripts.script import Script + + +class SaveProcessInstanceMetadata(Script): + """SaveProcessInstanceMetadata.""" + + def get_description(self) -> str: + """Get_description.""" + return """Save a given dict as process instance metadata (useful for creating reports).""" + + def run( + self, + script_attributes_context: ScriptAttributesContext, + *args: Any, + **kwargs: Any, + ) -> Any: + """Run.""" + metadata_dict = args[0] + for key, value in metadata_dict.items(): + pim = ProcessInstanceMetadataModel.query.filter_by( + process_instance_id=script_attributes_context.process_instance_id, key=key).first() + if pim is None: + pim = ProcessInstanceMetadataModel( + process_instance_id=script_attributes_context.process_instance_id, key=key) + pim.value = value + db.session.add(pim) + db.session.commit() diff --git a/spiffworkflow-backend/tests/data/save_process_instance_metadata/save_process_instance_metadata.bpmn b/spiffworkflow-backend/tests/data/save_process_instance_metadata/save_process_instance_metadata.bpmn new file mode 100644 index 00000000..2c72b08d --- /dev/null +++ b/spiffworkflow-backend/tests/data/save_process_instance_metadata/save_process_instance_metadata.bpmn @@ -0,0 +1,52 @@ + + + + + Flow_1j4jzft + + + + Flow_01xr2ac + + + Flow_1j4jzft + Flow_10xyk22 + save_process_instance_metadata({"key1": "value1"}) + + + + Flow_10xyk22 + Flow_01xr2ac + save_process_instance_metadata({"key2": "value2", "key3": "value3"}) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/scripts/test_save_process_instance_metadata.py b/spiffworkflow-backend/tests/spiffworkflow_backend/scripts/test_save_process_instance_metadata.py new file mode 100644 index 00000000..62823f15 --- /dev/null +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/scripts/test_save_process_instance_metadata.py @@ -0,0 +1,42 @@ +"""Test_get_localtime.""" +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.process_instance_metadata import ProcessInstanceMetadataModel +from spiffworkflow_backend.models.user import UserModel +from spiffworkflow_backend.services.process_instance_processor import ( + ProcessInstanceProcessor, +) + + +class TestSaveProcessInstanceMetadata(BaseTest): + """TestSaveProcessInstanceMetadata.""" + + def test_can_save_process_instance_metadata( + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, + ) -> None: + """Test_can_save_process_instance_metadata.""" + initiator_user = self.find_or_create_user("initiator_user") + self.create_process_group( + client, with_super_admin_user, "test_group", "test_group" + ) + process_model = load_test_spec( + process_model_id="save_process_instance_metadata/save_process_instance_metadata", + bpmn_file_name="save_process_instance_metadata.bpmn", + process_model_source_directory="save_process_instance_metadata", + ) + 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) + + process_instance_metadata = ProcessInstanceMetadataModel.query.filter_by( + process_instance_id=process_instance.id).all() + assert len(process_instance_metadata) == 3 diff --git a/spiffworkflow-frontend/src/components/ProcessModelListTiles.tsx b/spiffworkflow-frontend/src/components/ProcessModelListTiles.tsx index 3022fadc..4787fe94 100644 --- a/spiffworkflow-frontend/src/components/ProcessModelListTiles.tsx +++ b/spiffworkflow-frontend/src/components/ProcessModelListTiles.tsx @@ -81,6 +81,7 @@ export default function ProcessModelListTiles({