From d740518e7594a6cac1e066a294e50e615a4e5db1 Mon Sep 17 00:00:00 2001 From: Dan Date: Mon, 14 Nov 2022 15:23:37 -0500 Subject: [PATCH 1/6] Mostly a name change from BpmnProcessIdLookup to SpecReferenceCache. I landed on this unfortunate name because: 1. It's not just processes, it contains the list of all DMN Decisions as well. 2. It is closely linked to the SpecReference object that can be generated by looking through all the Spec files to find the processes and decisions they contain. 3. It is a cache of information, the file system is the source of truth. Seems likely we will cache more things in the future -- so setting things up this way made sense. --- .gitignore | 1 + spiffworkflow-backend/migrations/env.py | 27 +- .../migrations/versions/3e2a826ac720_.py | 318 ++++++++++ .../migrations/versions/fd00c59e1f60_.py | 550 ------------------ .../load_database_models.py | 4 +- .../models/bpmn_process_id_lookup.py | 14 - .../models/spec_reference.py | 56 ++ .../services/process_instance_processor.py | 16 +- .../services/spec_file_service.py | 62 +- .../helpers/example_data.py | 2 +- .../unit/test_process_model.py | 4 +- .../unit/test_spec_file_service.py | 34 +- spiffworkflow-frontend/package-lock.json | 4 +- 13 files changed, 455 insertions(+), 637 deletions(-) create mode 100644 spiffworkflow-backend/migrations/versions/3e2a826ac720_.py delete mode 100644 spiffworkflow-backend/migrations/versions/fd00c59e1f60_.py delete mode 100644 spiffworkflow-backend/src/spiffworkflow_backend/models/bpmn_process_id_lookup.py create mode 100644 spiffworkflow-backend/src/spiffworkflow_backend/models/spec_reference.py diff --git a/.gitignore b/.gitignore index 73f59e5ed..f509b359e 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ pyrightconfig.json +.idea/ \ No newline at end of file diff --git a/spiffworkflow-backend/migrations/env.py b/spiffworkflow-backend/migrations/env.py index 4bd0316c2..68feded2a 100644 --- a/spiffworkflow-backend/migrations/env.py +++ b/spiffworkflow-backend/migrations/env.py @@ -1,9 +1,12 @@ +from __future__ import with_statement + import logging from logging.config import fileConfig -from alembic import context from flask import current_app +from alembic import context + # this is the Alembic Config object, which provides # access to the values within the .ini file in use. config = context.config @@ -11,17 +14,17 @@ config = context.config # Interpret the config file for Python logging. # This line sets up loggers basically. fileConfig(config.config_file_name) -logger = logging.getLogger("alembic.env") +logger = logging.getLogger('alembic.env') # add your model's MetaData object here # for 'autogenerate' support # from myapp import mymodel # target_metadata = mymodel.Base.metadata config.set_main_option( - "sqlalchemy.url", - str(current_app.extensions["migrate"].db.get_engine().url).replace("%", "%%"), -) -target_metadata = current_app.extensions["migrate"].db.metadata + 'sqlalchemy.url', + str(current_app.extensions['migrate'].db.get_engine().url).replace( + '%', '%%')) +target_metadata = current_app.extensions['migrate'].db.metadata # other values from the config, defined by the needs of env.py, # can be acquired: @@ -42,7 +45,9 @@ def run_migrations_offline(): """ url = config.get_main_option("sqlalchemy.url") - context.configure(url=url, target_metadata=target_metadata, literal_binds=True) + context.configure( + url=url, target_metadata=target_metadata, literal_binds=True + ) with context.begin_transaction(): context.run_migrations() @@ -60,20 +65,20 @@ def run_migrations_online(): # when there are no changes to the schema # reference: http://alembic.zzzcomputing.com/en/latest/cookbook.html def process_revision_directives(context, revision, directives): - if getattr(config.cmd_opts, "autogenerate", False): + if getattr(config.cmd_opts, 'autogenerate', False): script = directives[0] if script.upgrade_ops.is_empty(): directives[:] = [] - logger.info("No changes in schema detected.") + logger.info('No changes in schema detected.') - connectable = current_app.extensions["migrate"].db.get_engine() + connectable = current_app.extensions['migrate'].db.get_engine() with connectable.connect() as connection: context.configure( connection=connection, target_metadata=target_metadata, process_revision_directives=process_revision_directives, - **current_app.extensions["migrate"].configure_args + **current_app.extensions['migrate'].configure_args ) with context.begin_transaction(): diff --git a/spiffworkflow-backend/migrations/versions/3e2a826ac720_.py b/spiffworkflow-backend/migrations/versions/3e2a826ac720_.py new file mode 100644 index 000000000..c9e17c1cb --- /dev/null +++ b/spiffworkflow-backend/migrations/versions/3e2a826ac720_.py @@ -0,0 +1,318 @@ +"""empty message + +Revision ID: 3e2a826ac720 +Revises: +Create Date: 2022-11-14 14:59:43.265173 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '3e2a826ac720' +down_revision = None +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('group', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('name', sa.String(length=255), nullable=True), + sa.Column('identifier', sa.String(length=255), nullable=True), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('message_model', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('identifier', sa.String(length=50), nullable=True), + sa.Column('name', sa.String(length=50), nullable=True), + sa.PrimaryKeyConstraint('id') + ) + op.create_index(op.f('ix_message_model_identifier'), 'message_model', ['identifier'], unique=True) + op.create_index(op.f('ix_message_model_name'), 'message_model', ['name'], unique=True) + op.create_table('permission_target', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('uri', sa.String(length=255), nullable=False), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('uri') + ) + op.create_table('spec_reference_cache', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('identifier', sa.String(length=255), nullable=True), + sa.Column('display_name', sa.String(length=255), nullable=True), + sa.Column('type', sa.String(length=255), nullable=True), + sa.Column('file_name', sa.String(length=255), nullable=True), + sa.Column('relative_path', sa.String(length=255), nullable=True), + sa.Column('has_lanes', sa.Boolean(), nullable=True), + sa.Column('is_executable', sa.Boolean(), nullable=True), + sa.Column('is_primary', sa.Boolean(), nullable=True), + sa.PrimaryKeyConstraint('id') + ) + op.create_index(op.f('ix_spec_reference_cache_display_name'), 'spec_reference_cache', ['display_name'], unique=False) + op.create_index(op.f('ix_spec_reference_cache_identifier'), 'spec_reference_cache', ['identifier'], unique=True) + op.create_index(op.f('ix_spec_reference_cache_type'), 'spec_reference_cache', ['type'], unique=False) + op.create_table('spiff_logging', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('process_instance_id', sa.Integer(), nullable=False), + sa.Column('bpmn_process_identifier', sa.String(length=255), nullable=False), + sa.Column('bpmn_task_identifier', sa.String(length=255), nullable=False), + sa.Column('bpmn_task_name', sa.String(length=255), nullable=True), + sa.Column('bpmn_task_type', sa.String(length=255), nullable=True), + sa.Column('spiff_task_guid', sa.String(length=50), nullable=False), + sa.Column('timestamp', sa.DECIMAL(precision=17, scale=6), nullable=False), + sa.Column('message', sa.String(length=255), nullable=True), + sa.Column('current_user_id', sa.Integer(), nullable=True), + sa.Column('spiff_step', sa.Integer(), nullable=False), + sa.PrimaryKeyConstraint('id') + ) + 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.PrimaryKeyConstraint('id') + ) + op.create_table('user', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('username', sa.String(length=255), nullable=False), + sa.Column('uid', sa.String(length=50), nullable=True), + sa.Column('service', sa.String(length=50), nullable=False), + sa.Column('service_id', sa.String(length=255), nullable=False), + sa.Column('name', sa.String(length=255), nullable=True), + sa.Column('email', sa.String(length=255), nullable=True), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('service', 'service_id', name='service_key'), + sa.UniqueConstraint('uid'), + sa.UniqueConstraint('username') + ) + op.create_table('message_correlation_property', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('identifier', sa.String(length=50), nullable=True), + sa.Column('message_model_id', sa.Integer(), nullable=False), + sa.Column('updated_at_in_seconds', sa.Integer(), nullable=True), + sa.Column('created_at_in_seconds', sa.Integer(), nullable=True), + sa.ForeignKeyConstraint(['message_model_id'], ['message_model.id'], ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('identifier', 'message_model_id', name='message_correlation_property_unique') + ) + op.create_index(op.f('ix_message_correlation_property_identifier'), 'message_correlation_property', ['identifier'], unique=False) + op.create_table('message_triggerable_process_model', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('message_model_id', sa.Integer(), nullable=False), + sa.Column('process_model_identifier', sa.String(length=50), nullable=False), + sa.Column('process_group_identifier', sa.String(length=50), nullable=False), + sa.Column('updated_at_in_seconds', sa.Integer(), nullable=True), + sa.Column('created_at_in_seconds', sa.Integer(), nullable=True), + sa.ForeignKeyConstraint(['message_model_id'], ['message_model.id'], ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('message_model_id') + ) + op.create_index(op.f('ix_message_triggerable_process_model_process_group_identifier'), 'message_triggerable_process_model', ['process_group_identifier'], unique=False) + op.create_index(op.f('ix_message_triggerable_process_model_process_model_identifier'), 'message_triggerable_process_model', ['process_model_identifier'], unique=False) + op.create_table('principal', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('user_id', sa.Integer(), nullable=True), + sa.Column('group_id', sa.Integer(), nullable=True), + sa.CheckConstraint('NOT(user_id IS NULL AND group_id IS NULL)'), + sa.ForeignKeyConstraint(['group_id'], ['group.id'], ), + sa.ForeignKeyConstraint(['user_id'], ['user.id'], ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('group_id'), + sa.UniqueConstraint('user_id') + ) + op.create_table('process_instance', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('process_model_identifier', sa.String(length=255), nullable=False), + sa.Column('process_group_identifier', sa.String(length=50), nullable=False), + sa.Column('process_initiator_id', sa.Integer(), nullable=False), + sa.Column('bpmn_json', sa.JSON(), nullable=True), + sa.Column('start_in_seconds', sa.Integer(), nullable=True), + sa.Column('end_in_seconds', sa.Integer(), nullable=True), + sa.Column('updated_at_in_seconds', sa.Integer(), nullable=True), + sa.Column('created_at_in_seconds', sa.Integer(), nullable=True), + sa.Column('status', sa.String(length=50), nullable=True), + sa.Column('bpmn_version_control_type', sa.String(length=50), nullable=True), + sa.Column('bpmn_version_control_identifier', sa.String(length=255), nullable=True), + sa.Column('spiff_step', sa.Integer(), nullable=True), + sa.ForeignKeyConstraint(['process_initiator_id'], ['user.id'], ), + sa.PrimaryKeyConstraint('id') + ) + op.create_index(op.f('ix_process_instance_process_group_identifier'), 'process_instance', ['process_group_identifier'], unique=False) + op.create_index(op.f('ix_process_instance_process_model_identifier'), 'process_instance', ['process_model_identifier'], unique=False) + op.create_table('process_instance_report', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('identifier', sa.String(length=50), nullable=False), + sa.Column('report_metadata', sa.JSON(), nullable=True), + sa.Column('created_by_id', sa.Integer(), nullable=False), + sa.Column('created_at_in_seconds', sa.Integer(), nullable=True), + sa.Column('updated_at_in_seconds', sa.Integer(), nullable=True), + sa.ForeignKeyConstraint(['created_by_id'], ['user.id'], ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('created_by_id', 'identifier', name='process_instance_report_unique') + ) + op.create_index(op.f('ix_process_instance_report_created_by_id'), 'process_instance_report', ['created_by_id'], unique=False) + op.create_index(op.f('ix_process_instance_report_identifier'), 'process_instance_report', ['identifier'], unique=False) + op.create_table('refresh_token', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('user_id', sa.Integer(), nullable=False), + sa.Column('token', sa.String(length=1024), nullable=False), + sa.ForeignKeyConstraint(['user_id'], ['user.id'], ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('user_id') + ) + op.create_table('secret', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('key', sa.String(length=50), nullable=False), + sa.Column('value', sa.Text(), nullable=False), + sa.Column('user_id', sa.Integer(), nullable=False), + sa.Column('updated_at_in_seconds', sa.Integer(), nullable=True), + sa.Column('created_at_in_seconds', sa.Integer(), nullable=True), + sa.ForeignKeyConstraint(['user_id'], ['user.id'], ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('key') + ) + op.create_table('user_group_assignment', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('user_id', sa.Integer(), nullable=False), + sa.Column('group_id', sa.Integer(), nullable=False), + sa.ForeignKeyConstraint(['group_id'], ['group.id'], ), + sa.ForeignKeyConstraint(['user_id'], ['user.id'], ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('user_id', 'group_id', name='user_group_assignment_unique') + ) + op.create_table('active_task', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('process_instance_id', sa.Integer(), nullable=False), + sa.Column('actual_owner_id', sa.Integer(), nullable=True), + sa.Column('lane_assignment_id', sa.Integer(), nullable=True), + sa.Column('form_file_name', sa.String(length=50), nullable=True), + sa.Column('ui_form_file_name', sa.String(length=50), nullable=True), + sa.Column('updated_at_in_seconds', sa.Integer(), nullable=True), + sa.Column('created_at_in_seconds', sa.Integer(), nullable=True), + sa.Column('task_id', sa.String(length=50), nullable=True), + sa.Column('task_name', sa.String(length=50), nullable=True), + sa.Column('task_title', sa.String(length=50), nullable=True), + sa.Column('task_type', sa.String(length=50), nullable=True), + sa.Column('task_status', sa.String(length=50), nullable=True), + sa.Column('process_model_display_name', sa.String(length=255), nullable=True), + sa.ForeignKeyConstraint(['actual_owner_id'], ['user.id'], ), + sa.ForeignKeyConstraint(['lane_assignment_id'], ['group.id'], ), + sa.ForeignKeyConstraint(['process_instance_id'], ['process_instance.id'], ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('task_id', 'process_instance_id', name='active_task_unique') + ) + op.create_table('message_correlation', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('process_instance_id', sa.Integer(), nullable=False), + sa.Column('message_correlation_property_id', sa.Integer(), nullable=False), + sa.Column('name', sa.String(length=255), nullable=False), + sa.Column('value', sa.String(length=255), nullable=False), + sa.Column('updated_at_in_seconds', sa.Integer(), nullable=True), + sa.Column('created_at_in_seconds', sa.Integer(), nullable=True), + sa.ForeignKeyConstraint(['message_correlation_property_id'], ['message_correlation_property.id'], ), + sa.ForeignKeyConstraint(['process_instance_id'], ['process_instance.id'], ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('process_instance_id', 'message_correlation_property_id', 'name', name='message_instance_id_name_unique') + ) + op.create_index(op.f('ix_message_correlation_message_correlation_property_id'), 'message_correlation', ['message_correlation_property_id'], unique=False) + op.create_index(op.f('ix_message_correlation_name'), 'message_correlation', ['name'], unique=False) + op.create_index(op.f('ix_message_correlation_process_instance_id'), 'message_correlation', ['process_instance_id'], unique=False) + op.create_index(op.f('ix_message_correlation_value'), 'message_correlation', ['value'], unique=False) + op.create_table('message_instance', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('process_instance_id', sa.Integer(), nullable=False), + sa.Column('message_model_id', sa.Integer(), nullable=False), + sa.Column('message_type', sa.String(length=20), nullable=False), + sa.Column('payload', sa.JSON(), nullable=True), + sa.Column('status', sa.String(length=20), nullable=False), + sa.Column('failure_cause', sa.Text(), nullable=True), + sa.Column('updated_at_in_seconds', sa.Integer(), nullable=True), + sa.Column('created_at_in_seconds', sa.Integer(), nullable=True), + sa.ForeignKeyConstraint(['message_model_id'], ['message_model.id'], ), + sa.ForeignKeyConstraint(['process_instance_id'], ['process_instance.id'], ), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('permission_assignment', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('principal_id', sa.Integer(), nullable=False), + sa.Column('permission_target_id', sa.Integer(), nullable=False), + sa.Column('grant_type', sa.String(length=50), nullable=False), + sa.Column('permission', sa.String(length=50), nullable=False), + sa.ForeignKeyConstraint(['permission_target_id'], ['permission_target.id'], ), + sa.ForeignKeyConstraint(['principal_id'], ['principal.id'], ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('principal_id', 'permission_target_id', 'permission', name='permission_assignment_uniq') + ) + op.create_table('active_task_user', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('active_task_id', sa.Integer(), nullable=False), + sa.Column('user_id', sa.Integer(), nullable=False), + sa.ForeignKeyConstraint(['active_task_id'], ['active_task.id'], ), + sa.ForeignKeyConstraint(['user_id'], ['user.id'], ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('active_task_id', 'user_id', name='active_task_user_unique') + ) + op.create_index(op.f('ix_active_task_user_active_task_id'), 'active_task_user', ['active_task_id'], unique=False) + op.create_index(op.f('ix_active_task_user_user_id'), 'active_task_user', ['user_id'], unique=False) + op.create_table('message_correlation_message_instance', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('message_instance_id', sa.Integer(), nullable=False), + sa.Column('message_correlation_id', sa.Integer(), nullable=False), + sa.ForeignKeyConstraint(['message_correlation_id'], ['message_correlation.id'], ), + sa.ForeignKeyConstraint(['message_instance_id'], ['message_instance.id'], ), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('message_instance_id', 'message_correlation_id', name='message_correlation_message_instance_unique') + ) + op.create_index(op.f('ix_message_correlation_message_instance_message_correlation_id'), 'message_correlation_message_instance', ['message_correlation_id'], unique=False) + op.create_index(op.f('ix_message_correlation_message_instance_message_instance_id'), 'message_correlation_message_instance', ['message_instance_id'], unique=False) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_index(op.f('ix_message_correlation_message_instance_message_instance_id'), table_name='message_correlation_message_instance') + op.drop_index(op.f('ix_message_correlation_message_instance_message_correlation_id'), table_name='message_correlation_message_instance') + op.drop_table('message_correlation_message_instance') + 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('permission_assignment') + op.drop_table('message_instance') + op.drop_index(op.f('ix_message_correlation_value'), table_name='message_correlation') + op.drop_index(op.f('ix_message_correlation_process_instance_id'), table_name='message_correlation') + op.drop_index(op.f('ix_message_correlation_name'), table_name='message_correlation') + op.drop_index(op.f('ix_message_correlation_message_correlation_property_id'), table_name='message_correlation') + op.drop_table('message_correlation') + op.drop_table('active_task') + op.drop_table('user_group_assignment') + op.drop_table('secret') + op.drop_table('refresh_token') + op.drop_index(op.f('ix_process_instance_report_identifier'), table_name='process_instance_report') + op.drop_index(op.f('ix_process_instance_report_created_by_id'), table_name='process_instance_report') + op.drop_table('process_instance_report') + op.drop_index(op.f('ix_process_instance_process_model_identifier'), table_name='process_instance') + op.drop_index(op.f('ix_process_instance_process_group_identifier'), table_name='process_instance') + op.drop_table('process_instance') + op.drop_table('principal') + op.drop_index(op.f('ix_message_triggerable_process_model_process_model_identifier'), table_name='message_triggerable_process_model') + op.drop_index(op.f('ix_message_triggerable_process_model_process_group_identifier'), table_name='message_triggerable_process_model') + op.drop_table('message_triggerable_process_model') + op.drop_index(op.f('ix_message_correlation_property_identifier'), table_name='message_correlation_property') + op.drop_table('message_correlation_property') + op.drop_table('user') + op.drop_table('spiff_step_details') + op.drop_table('spiff_logging') + op.drop_index(op.f('ix_spec_reference_cache_type'), table_name='spec_reference_cache') + op.drop_index(op.f('ix_spec_reference_cache_identifier'), table_name='spec_reference_cache') + op.drop_index(op.f('ix_spec_reference_cache_display_name'), table_name='spec_reference_cache') + op.drop_table('spec_reference_cache') + op.drop_table('permission_target') + op.drop_index(op.f('ix_message_model_name'), table_name='message_model') + op.drop_index(op.f('ix_message_model_identifier'), table_name='message_model') + op.drop_table('message_model') + op.drop_table('group') + # ### end Alembic commands ### diff --git a/spiffworkflow-backend/migrations/versions/fd00c59e1f60_.py b/spiffworkflow-backend/migrations/versions/fd00c59e1f60_.py deleted file mode 100644 index 55f1ca3f6..000000000 --- a/spiffworkflow-backend/migrations/versions/fd00c59e1f60_.py +++ /dev/null @@ -1,550 +0,0 @@ -"""empty message - -Revision ID: fd00c59e1f60 -Revises: -Create Date: 2022-11-09 14:04:14.169379 - -""" -import sqlalchemy as sa -from alembic import op - - -# revision identifiers, used by Alembic. -revision = "fd00c59e1f60" -down_revision = None -branch_labels = None -depends_on = None - - -def upgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.create_table( - "bpmn_process_id_lookup", - sa.Column("id", sa.Integer(), nullable=False), - sa.Column("bpmn_process_identifier", sa.String(length=255), nullable=True), - sa.Column("bpmn_file_relative_path", sa.String(length=255), nullable=True), - sa.Column("display_name", sa.String(length=255), nullable=True), - sa.PrimaryKeyConstraint("id"), - ) - op.create_index( - op.f("ix_bpmn_process_id_lookup_bpmn_process_identifier"), - "bpmn_process_id_lookup", - ["bpmn_process_identifier"], - unique=True, - ) - op.create_table( - "group", - sa.Column("id", sa.Integer(), nullable=False), - sa.Column("name", sa.String(length=255), nullable=True), - sa.Column("identifier", sa.String(length=255), nullable=True), - sa.PrimaryKeyConstraint("id"), - ) - op.create_table( - "message_model", - sa.Column("id", sa.Integer(), nullable=False), - sa.Column("identifier", sa.String(length=50), nullable=True), - sa.Column("name", sa.String(length=50), nullable=True), - sa.PrimaryKeyConstraint("id"), - ) - op.create_index( - op.f("ix_message_model_identifier"), - "message_model", - ["identifier"], - unique=True, - ) - op.create_index( - op.f("ix_message_model_name"), "message_model", ["name"], unique=True - ) - op.create_table( - "permission_target", - sa.Column("id", sa.Integer(), nullable=False), - sa.Column("uri", sa.String(length=255), nullable=False), - sa.PrimaryKeyConstraint("id"), - sa.UniqueConstraint("uri"), - ) - op.create_table( - "spiff_logging", - sa.Column("id", sa.Integer(), nullable=False), - sa.Column("process_instance_id", sa.Integer(), nullable=False), - sa.Column("bpmn_process_identifier", sa.String(length=255), nullable=False), - sa.Column("bpmn_task_identifier", sa.String(length=255), nullable=False), - sa.Column("bpmn_task_name", sa.String(length=255), nullable=True), - sa.Column("bpmn_task_type", sa.String(length=255), nullable=True), - sa.Column("spiff_task_guid", sa.String(length=50), nullable=False), - sa.Column("timestamp", sa.DECIMAL(precision=17, scale=6), nullable=False), - sa.Column("message", sa.String(length=255), nullable=True), - sa.Column("current_user_id", sa.Integer(), nullable=True), - sa.Column("spiff_step", sa.Integer(), nullable=False), - sa.PrimaryKeyConstraint("id"), - ) - 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.PrimaryKeyConstraint("id"), - ) - op.create_table( - "user", - sa.Column("id", sa.Integer(), nullable=False), - sa.Column("username", sa.String(length=255), nullable=False), - sa.Column("uid", sa.String(length=50), nullable=True), - sa.Column("service", sa.String(length=50), nullable=False), - sa.Column("service_id", sa.String(length=255), nullable=False), - sa.Column("name", sa.String(length=255), nullable=True), - sa.Column("email", sa.String(length=255), nullable=True), - sa.PrimaryKeyConstraint("id"), - sa.UniqueConstraint("service", "service_id", name="service_key"), - sa.UniqueConstraint("uid"), - sa.UniqueConstraint("username"), - ) - op.create_table( - "message_correlation_property", - sa.Column("id", sa.Integer(), nullable=False), - sa.Column("identifier", sa.String(length=50), nullable=True), - sa.Column("message_model_id", sa.Integer(), nullable=False), - sa.Column("updated_at_in_seconds", sa.Integer(), nullable=True), - sa.Column("created_at_in_seconds", sa.Integer(), nullable=True), - sa.ForeignKeyConstraint( - ["message_model_id"], - ["message_model.id"], - ), - sa.PrimaryKeyConstraint("id"), - sa.UniqueConstraint( - "identifier", "message_model_id", name="message_correlation_property_unique" - ), - ) - op.create_index( - op.f("ix_message_correlation_property_identifier"), - "message_correlation_property", - ["identifier"], - unique=False, - ) - op.create_table( - "message_triggerable_process_model", - sa.Column("id", sa.Integer(), nullable=False), - sa.Column("message_model_id", sa.Integer(), nullable=False), - sa.Column("process_model_identifier", sa.String(length=50), nullable=False), - sa.Column("process_group_identifier", sa.String(length=50), nullable=False), - sa.Column("updated_at_in_seconds", sa.Integer(), nullable=True), - sa.Column("created_at_in_seconds", sa.Integer(), nullable=True), - sa.ForeignKeyConstraint( - ["message_model_id"], - ["message_model.id"], - ), - sa.PrimaryKeyConstraint("id"), - sa.UniqueConstraint("message_model_id"), - ) - op.create_index( - op.f("ix_message_triggerable_process_model_process_group_identifier"), - "message_triggerable_process_model", - ["process_group_identifier"], - unique=False, - ) - op.create_index( - op.f("ix_message_triggerable_process_model_process_model_identifier"), - "message_triggerable_process_model", - ["process_model_identifier"], - unique=False, - ) - op.create_table( - "principal", - sa.Column("id", sa.Integer(), nullable=False), - sa.Column("user_id", sa.Integer(), nullable=True), - sa.Column("group_id", sa.Integer(), nullable=True), - sa.CheckConstraint("NOT(user_id IS NULL AND group_id IS NULL)"), - sa.ForeignKeyConstraint( - ["group_id"], - ["group.id"], - ), - sa.ForeignKeyConstraint( - ["user_id"], - ["user.id"], - ), - sa.PrimaryKeyConstraint("id"), - sa.UniqueConstraint("group_id"), - sa.UniqueConstraint("user_id"), - ) - op.create_table( - "process_instance", - sa.Column("id", sa.Integer(), nullable=False), - sa.Column("process_model_identifier", sa.String(length=255), nullable=False), - sa.Column("process_group_identifier", sa.String(length=50), nullable=False), - sa.Column("process_initiator_id", sa.Integer(), nullable=False), - sa.Column("bpmn_json", sa.JSON(), nullable=True), - sa.Column("start_in_seconds", sa.Integer(), nullable=True), - sa.Column("end_in_seconds", sa.Integer(), nullable=True), - sa.Column("updated_at_in_seconds", sa.Integer(), nullable=True), - sa.Column("created_at_in_seconds", sa.Integer(), nullable=True), - sa.Column("status", sa.String(length=50), nullable=True), - sa.Column("bpmn_version_control_type", sa.String(length=50), nullable=True), - sa.Column( - "bpmn_version_control_identifier", sa.String(length=255), nullable=True - ), - sa.Column("spiff_step", sa.Integer(), nullable=True), - sa.ForeignKeyConstraint( - ["process_initiator_id"], - ["user.id"], - ), - sa.PrimaryKeyConstraint("id"), - ) - op.create_index( - op.f("ix_process_instance_process_group_identifier"), - "process_instance", - ["process_group_identifier"], - unique=False, - ) - op.create_index( - op.f("ix_process_instance_process_model_identifier"), - "process_instance", - ["process_model_identifier"], - unique=False, - ) - op.create_table( - "process_instance_report", - sa.Column("id", sa.Integer(), nullable=False), - sa.Column("identifier", sa.String(length=50), nullable=False), - sa.Column("report_metadata", sa.JSON(), nullable=True), - sa.Column("created_by_id", sa.Integer(), nullable=False), - sa.Column("created_at_in_seconds", sa.Integer(), nullable=True), - sa.Column("updated_at_in_seconds", sa.Integer(), nullable=True), - sa.ForeignKeyConstraint( - ["created_by_id"], - ["user.id"], - ), - sa.PrimaryKeyConstraint("id"), - sa.UniqueConstraint( - "created_by_id", "identifier", name="process_instance_report_unique" - ), - ) - op.create_index( - op.f("ix_process_instance_report_created_by_id"), - "process_instance_report", - ["created_by_id"], - unique=False, - ) - op.create_index( - op.f("ix_process_instance_report_identifier"), - "process_instance_report", - ["identifier"], - unique=False, - ) - op.create_table( - "refresh_token", - sa.Column("id", sa.Integer(), nullable=False), - sa.Column("user_id", sa.Integer(), nullable=False), - sa.Column("token", sa.String(length=1024), nullable=False), - sa.ForeignKeyConstraint( - ["user_id"], - ["user.id"], - ), - sa.PrimaryKeyConstraint("id"), - sa.UniqueConstraint("user_id"), - ) - op.create_table( - "secret", - sa.Column("id", sa.Integer(), nullable=False), - sa.Column("key", sa.String(length=50), nullable=False), - sa.Column("value", sa.Text(), nullable=False), - sa.Column("user_id", sa.Integer(), nullable=False), - sa.Column("updated_at_in_seconds", sa.Integer(), nullable=True), - sa.Column("created_at_in_seconds", sa.Integer(), nullable=True), - sa.ForeignKeyConstraint( - ["user_id"], - ["user.id"], - ), - sa.PrimaryKeyConstraint("id"), - sa.UniqueConstraint("key"), - ) - op.create_table( - "user_group_assignment", - sa.Column("id", sa.Integer(), nullable=False), - sa.Column("user_id", sa.Integer(), nullable=False), - sa.Column("group_id", sa.Integer(), nullable=False), - sa.ForeignKeyConstraint( - ["group_id"], - ["group.id"], - ), - sa.ForeignKeyConstraint( - ["user_id"], - ["user.id"], - ), - sa.PrimaryKeyConstraint("id"), - sa.UniqueConstraint("user_id", "group_id", name="user_group_assignment_unique"), - ) - op.create_table( - "active_task", - sa.Column("id", sa.Integer(), nullable=False), - sa.Column("process_instance_id", sa.Integer(), nullable=False), - sa.Column("actual_owner_id", sa.Integer(), nullable=True), - sa.Column("lane_assignment_id", sa.Integer(), nullable=True), - sa.Column("form_file_name", sa.String(length=50), nullable=True), - sa.Column("ui_form_file_name", sa.String(length=50), nullable=True), - sa.Column("updated_at_in_seconds", sa.Integer(), nullable=True), - sa.Column("created_at_in_seconds", sa.Integer(), nullable=True), - sa.Column("task_id", sa.String(length=50), nullable=True), - sa.Column("task_name", sa.String(length=50), nullable=True), - sa.Column("task_title", sa.String(length=50), nullable=True), - sa.Column("task_type", sa.String(length=50), nullable=True), - sa.Column("task_status", sa.String(length=50), nullable=True), - sa.Column("process_model_display_name", sa.String(length=255), nullable=True), - sa.ForeignKeyConstraint( - ["actual_owner_id"], - ["user.id"], - ), - sa.ForeignKeyConstraint( - ["lane_assignment_id"], - ["group.id"], - ), - sa.ForeignKeyConstraint( - ["process_instance_id"], - ["process_instance.id"], - ), - sa.PrimaryKeyConstraint("id"), - sa.UniqueConstraint( - "task_id", "process_instance_id", name="active_task_unique" - ), - ) - op.create_table( - "message_correlation", - sa.Column("id", sa.Integer(), nullable=False), - sa.Column("process_instance_id", sa.Integer(), nullable=False), - sa.Column("message_correlation_property_id", sa.Integer(), nullable=False), - sa.Column("name", sa.String(length=255), nullable=False), - sa.Column("value", sa.String(length=255), nullable=False), - sa.Column("updated_at_in_seconds", sa.Integer(), nullable=True), - sa.Column("created_at_in_seconds", sa.Integer(), nullable=True), - sa.ForeignKeyConstraint( - ["message_correlation_property_id"], - ["message_correlation_property.id"], - ), - sa.ForeignKeyConstraint( - ["process_instance_id"], - ["process_instance.id"], - ), - sa.PrimaryKeyConstraint("id"), - sa.UniqueConstraint( - "process_instance_id", - "message_correlation_property_id", - "name", - name="message_instance_id_name_unique", - ), - ) - op.create_index( - op.f("ix_message_correlation_message_correlation_property_id"), - "message_correlation", - ["message_correlation_property_id"], - unique=False, - ) - op.create_index( - op.f("ix_message_correlation_name"), - "message_correlation", - ["name"], - unique=False, - ) - op.create_index( - op.f("ix_message_correlation_process_instance_id"), - "message_correlation", - ["process_instance_id"], - unique=False, - ) - op.create_index( - op.f("ix_message_correlation_value"), - "message_correlation", - ["value"], - unique=False, - ) - op.create_table( - "message_instance", - sa.Column("id", sa.Integer(), nullable=False), - sa.Column("process_instance_id", sa.Integer(), nullable=False), - sa.Column("message_model_id", sa.Integer(), nullable=False), - sa.Column("message_type", sa.String(length=20), nullable=False), - sa.Column("payload", sa.JSON(), nullable=True), - sa.Column("status", sa.String(length=20), nullable=False), - sa.Column("failure_cause", sa.Text(), nullable=True), - sa.Column("updated_at_in_seconds", sa.Integer(), nullable=True), - sa.Column("created_at_in_seconds", sa.Integer(), nullable=True), - sa.ForeignKeyConstraint( - ["message_model_id"], - ["message_model.id"], - ), - sa.ForeignKeyConstraint( - ["process_instance_id"], - ["process_instance.id"], - ), - sa.PrimaryKeyConstraint("id"), - ) - op.create_table( - "permission_assignment", - sa.Column("id", sa.Integer(), nullable=False), - sa.Column("principal_id", sa.Integer(), nullable=False), - sa.Column("permission_target_id", sa.Integer(), nullable=False), - sa.Column("grant_type", sa.String(length=50), nullable=False), - sa.Column("permission", sa.String(length=50), nullable=False), - sa.ForeignKeyConstraint( - ["permission_target_id"], - ["permission_target.id"], - ), - sa.ForeignKeyConstraint( - ["principal_id"], - ["principal.id"], - ), - sa.PrimaryKeyConstraint("id"), - sa.UniqueConstraint( - "principal_id", - "permission_target_id", - "permission", - name="permission_assignment_uniq", - ), - ) - op.create_table( - "active_task_user", - sa.Column("id", sa.Integer(), nullable=False), - sa.Column("active_task_id", sa.Integer(), nullable=False), - sa.Column("user_id", sa.Integer(), nullable=False), - sa.ForeignKeyConstraint( - ["active_task_id"], - ["active_task.id"], - ), - sa.ForeignKeyConstraint( - ["user_id"], - ["user.id"], - ), - sa.PrimaryKeyConstraint("id"), - sa.UniqueConstraint( - "active_task_id", "user_id", name="active_task_user_unique" - ), - ) - op.create_index( - op.f("ix_active_task_user_active_task_id"), - "active_task_user", - ["active_task_id"], - unique=False, - ) - op.create_index( - op.f("ix_active_task_user_user_id"), - "active_task_user", - ["user_id"], - unique=False, - ) - op.create_table( - "message_correlation_message_instance", - sa.Column("id", sa.Integer(), nullable=False), - sa.Column("message_instance_id", sa.Integer(), nullable=False), - sa.Column("message_correlation_id", sa.Integer(), nullable=False), - sa.ForeignKeyConstraint( - ["message_correlation_id"], - ["message_correlation.id"], - ), - sa.ForeignKeyConstraint( - ["message_instance_id"], - ["message_instance.id"], - ), - sa.PrimaryKeyConstraint("id"), - sa.UniqueConstraint( - "message_instance_id", - "message_correlation_id", - name="message_correlation_message_instance_unique", - ), - ) - op.create_index( - op.f("ix_message_correlation_message_instance_message_correlation_id"), - "message_correlation_message_instance", - ["message_correlation_id"], - unique=False, - ) - op.create_index( - op.f("ix_message_correlation_message_instance_message_instance_id"), - "message_correlation_message_instance", - ["message_instance_id"], - unique=False, - ) - # ### end Alembic commands ### - - -def downgrade(): - # ### commands auto generated by Alembic - please adjust! ### - op.drop_index( - op.f("ix_message_correlation_message_instance_message_instance_id"), - table_name="message_correlation_message_instance", - ) - op.drop_index( - op.f("ix_message_correlation_message_instance_message_correlation_id"), - table_name="message_correlation_message_instance", - ) - op.drop_table("message_correlation_message_instance") - 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("permission_assignment") - op.drop_table("message_instance") - op.drop_index( - op.f("ix_message_correlation_value"), table_name="message_correlation" - ) - op.drop_index( - op.f("ix_message_correlation_process_instance_id"), - table_name="message_correlation", - ) - op.drop_index(op.f("ix_message_correlation_name"), table_name="message_correlation") - op.drop_index( - op.f("ix_message_correlation_message_correlation_property_id"), - table_name="message_correlation", - ) - op.drop_table("message_correlation") - op.drop_table("active_task") - op.drop_table("user_group_assignment") - op.drop_table("secret") - op.drop_table("refresh_token") - op.drop_index( - op.f("ix_process_instance_report_identifier"), - table_name="process_instance_report", - ) - op.drop_index( - op.f("ix_process_instance_report_created_by_id"), - table_name="process_instance_report", - ) - op.drop_table("process_instance_report") - op.drop_index( - op.f("ix_process_instance_process_model_identifier"), - table_name="process_instance", - ) - op.drop_index( - op.f("ix_process_instance_process_group_identifier"), - table_name="process_instance", - ) - op.drop_table("process_instance") - op.drop_table("principal") - op.drop_index( - op.f("ix_message_triggerable_process_model_process_model_identifier"), - table_name="message_triggerable_process_model", - ) - op.drop_index( - op.f("ix_message_triggerable_process_model_process_group_identifier"), - table_name="message_triggerable_process_model", - ) - op.drop_table("message_triggerable_process_model") - op.drop_index( - op.f("ix_message_correlation_property_identifier"), - table_name="message_correlation_property", - ) - op.drop_table("message_correlation_property") - op.drop_table("user") - op.drop_table("spiff_step_details") - op.drop_table("spiff_logging") - op.drop_table("permission_target") - op.drop_index(op.f("ix_message_model_name"), table_name="message_model") - op.drop_index(op.f("ix_message_model_identifier"), table_name="message_model") - op.drop_table("message_model") - op.drop_table("group") - op.drop_index( - op.f("ix_bpmn_process_id_lookup_bpmn_process_identifier"), - table_name="bpmn_process_id_lookup", - ) - op.drop_table("bpmn_process_id_lookup") - # ### end Alembic commands ### diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/load_database_models.py b/spiffworkflow-backend/src/spiffworkflow_backend/load_database_models.py index 14dcac0dc..97c990004 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/load_database_models.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/load_database_models.py @@ -18,8 +18,8 @@ from spiffworkflow_backend.models.principal import PrincipalModel # noqa: F401 from spiffworkflow_backend.models.active_task import ActiveTaskModel # noqa: F401 -from spiffworkflow_backend.models.bpmn_process_id_lookup import ( - BpmnProcessIdLookup, +from spiffworkflow_backend.models.spec_reference import ( + SpecReferenceCache, ) # noqa: F401 from spiffworkflow_backend.models.message_correlation_property import ( MessageCorrelationPropertyModel, diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/models/bpmn_process_id_lookup.py b/spiffworkflow-backend/src/spiffworkflow_backend/models/bpmn_process_id_lookup.py deleted file mode 100644 index 133901c16..000000000 --- a/spiffworkflow-backend/src/spiffworkflow_backend/models/bpmn_process_id_lookup.py +++ /dev/null @@ -1,14 +0,0 @@ -"""Message_model.""" -from flask_bpmn.models.db import db -from flask_bpmn.models.db import SpiffworkflowBaseDBModel - - -class BpmnProcessIdLookup(SpiffworkflowBaseDBModel): - """BpmnProcessIdLookup.""" - - __tablename__ = "bpmn_process_id_lookup" - - id = db.Column(db.Integer, primary_key=True) - bpmn_process_identifier = db.Column(db.String(255), unique=True, index=True) - display_name = db.Column(db.String(255), unique=True, index=True) - bpmn_file_relative_path = db.Column(db.String(255)) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/models/spec_reference.py b/spiffworkflow-backend/src/spiffworkflow_backend/models/spec_reference.py new file mode 100644 index 000000000..e3fbabe7d --- /dev/null +++ b/spiffworkflow-backend/src/spiffworkflow_backend/models/spec_reference.py @@ -0,0 +1,56 @@ +"""Message_model.""" +from dataclasses import dataclass + +from flask_bpmn.models.db import db +from flask_bpmn.models.db import SpiffworkflowBaseDBModel +from flask_marshmallow import Schema +from marshmallow import INCLUDE + + +@dataclass() +class SpecReference: + """File Reference Information. + + Includes items such as the process id and name for a BPMN, + or the Decision id and Decision name for a DMN file. There may be more than + one reference that points to a particular file - if for instance, there are + three executable processes in a collaboration within a BPMN Diagram. + """ + + identifier: str # The id of the process or decision. "Process_1234" + display_name: str # The name of the process or decision. "Invoice Submission" + type: str # can be 'process' or 'decision' + file_name: str # The name of the file where this process or decision is defined. + relative_path: str # The path to the file. + has_lanes: bool # If this is a process, whether it has lanes or not. + is_executable: bool # Whether this process or decision is designated as executable. + is_primary: bool # Whether this is the primary process of a process model + messages: dict # Any messages defined in the same file where this process is defined. + correlations: dict # Any correlations defined in the same file with this process. + start_messages: list # The names of any messages that would start this process. + + +class SpecReferenceCache(SpiffworkflowBaseDBModel): + """A cache of information about all the Processes and Decisions defined in all files. """ + + __tablename__ = "spec_reference_cache" + + id = db.Column(db.Integer, primary_key=True) + identifier = db.Column(db.String(255), unique=True, index=True) + display_name = db.Column(db.String(255), index=True) + type = db.Column(db.String(255), index=True) # either 'process' or 'decision' + file_name = db.Column(db.String(255)) + relative_path = db.Column(db.String(255)) + has_lanes = db.Column(db.Boolean()) + is_executable = db.Column(db.Boolean()) # either 'process' or 'decision' + is_primary = db.Column(db.Boolean()) + +class SpecReferenceSchema(Schema): + """FileSchema.""" + + class Meta: + """Meta.""" + + model = SpecReference + fields = ["identifier", "display_name", "type"] + unknown = INCLUDE \ No newline at end of file diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py index 6c472e2a6..35bb60f23 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py @@ -68,7 +68,7 @@ from SpiffWorkflow.util.deep_merge import DeepMerge # type: ignore from spiffworkflow_backend.models.active_task import ActiveTaskModel from spiffworkflow_backend.models.active_task_user import ActiveTaskUserModel -from spiffworkflow_backend.models.bpmn_process_id_lookup import BpmnProcessIdLookup +from spiffworkflow_backend.models.spec_reference import SpecReferenceCache from spiffworkflow_backend.models.file import File from spiffworkflow_backend.models.file import FileType from spiffworkflow_backend.models.group import GroupModel @@ -674,9 +674,9 @@ class ProcessInstanceProcessor: return parser @staticmethod - def backfill_missing_bpmn_process_id_lookup_records(bpmn_process_identifier: str) -> Optional[str]: + def backfill_missing_spec_reference_records(bpmn_process_identifier: str) -> Optional[str]: - """Backfill_missing_bpmn_process_id_lookup_records.""" + """Backfill_missing_spec_reference_records.""" process_models = ProcessModelService().get_process_models() for process_model in process_models: refs = SpecFileService.reference_map(SpecFileService.get_references_for_process(process_model)) @@ -698,18 +698,16 @@ class ProcessInstanceProcessor: "bpmn_file_full_path_from_bpmn_process_identifier: bpmn_process_identifier is unexpectedly None" ) - bpmn_process_id_lookup = BpmnProcessIdLookup.query.filter_by( - bpmn_process_identifier=bpmn_process_identifier - ).first() + spec_reference = SpecReferenceCache.query.filter_by(identifier=bpmn_process_identifier).first() bpmn_file_full_path = None - if bpmn_process_id_lookup is None: - bpmn_file_full_path = ProcessInstanceProcessor.backfill_missing_bpmn_process_id_lookup_records( + if spec_reference is None: + bpmn_file_full_path = ProcessInstanceProcessor.backfill_missing_spec_reference_records( bpmn_process_identifier ) else: bpmn_file_full_path = os.path.join( FileSystemService.root_path(), - bpmn_process_id_lookup.bpmn_file_relative_path, + spec_reference.relative_path, ) if bpmn_file_full_path is None: raise ( diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/spec_file_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/spec_file_service.py index a3c44a1ec..49c29b36a 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/spec_file_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/spec_file_service.py @@ -15,9 +15,9 @@ from lxml.etree import _Element # type: ignore from lxml.etree import Element as EtreeElement from SpiffWorkflow.bpmn.parser.ValidationException import ValidationException # type: ignore -from spiffworkflow_backend.models.bpmn_process_id_lookup import BpmnProcessIdLookup +from spiffworkflow_backend.models.spec_reference import SpecReferenceCache from spiffworkflow_backend.models.file import File -from spiffworkflow_backend.models.file import FileReference +from spiffworkflow_backend.models.file import SpecReference from spiffworkflow_backend.models.file import FileType from spiffworkflow_backend.models.message_correlation_property import ( MessageCorrelationPropertyModel, @@ -61,22 +61,22 @@ class SpecFileService(FileSystemService): return files @staticmethod - def reference_map(references: list[FileReference]) -> dict[str, FileReference]: + def reference_map(references: list[SpecReference]) -> dict[str, SpecReference]: """ Creates a dict with provided references organized by id. """ ref_map = {} for ref in references: - ref_map[ref.id] = ref + ref_map[ref.identifier] = ref return ref_map @staticmethod - def get_references(process_models: List[ProcessModelInfo]) -> list[FileReference]: + def get_references(process_models: List[ProcessModelInfo]) -> list[SpecReference]: """Returns all references -- process_ids, and decision ids, across all process models provided""" references = [] for process_model in process_models: references.extend(SpecFileService.get_references_for_process(process_model)) @staticmethod - def get_references_for_process(process_model_info: ProcessModelInfo) -> list[FileReference]: + def get_references_for_process(process_model_info: ProcessModelInfo) -> list[SpecReference]: files = SpecFileService.get_files(process_model_info) references = [] for file in files: @@ -84,7 +84,7 @@ class SpecFileService(FileSystemService): return references @staticmethod - def get_references_for_file(file: File, process_model_info: ProcessModelInfo) -> list[FileReference]: + def get_references_for_file(file: File, process_model_info: ProcessModelInfo) -> list[SpecReference]: """Uses spiffworkflow to parse BPMN and DMN files to determine how they can be externally referenced. Returns a list of Reference objects that contain the type of reference, the id, the name. @@ -93,14 +93,15 @@ class SpecFileService(FileSystemService): name = {str} 'Level 3' type = {str} 'process' / 'decision' """ - references: list[FileReference] = [] + references: list[SpecReference] = [] full_file_path = SpecFileService.file_path(process_model_info, file.name) file_path = os.path.join(process_model_info.id, file.name) parser = MyCustomParser() parser_type = None sub_parser = None has_lanes = False - executable = True + is_executable = True + is_primary = False messages = {} correlations = {} start_messages = [] @@ -121,10 +122,11 @@ class SpecFileService(FileSystemService): has_lanes = sub_parser.has_lanes() executable = sub_parser.process_executable start_messages = sub_parser.start_messages() - references.append(FileReference( - id=sub_parser.get_id(), name=sub_parser.get_name(), type=parser_type, - file_name=file.name, file_path=file_path, has_lanes=has_lanes, - executable=executable, messages=messages, + is_primary = sub_parser.get_id() == process_model_info.primary_process_id + references.append(SpecReference( + identifier=sub_parser.get_id(), display_name=sub_parser.get_name(), type=parser_type, + file_name=file.name, relative_path=file_path, has_lanes=has_lanes, + is_executable=is_executable, messages=messages, is_primary=is_primary, correlations=correlations, start_messages=start_messages )) return references @@ -162,7 +164,7 @@ class SpecFileService(FileSystemService): if ref.type == "process": ProcessModelService().update_spec( process_model_info, { - "primary_process_id": ref.id, + "primary_process_id": ref.identifier, "primary_file_name": file_name, "is_review": ref.has_lanes, } @@ -231,30 +233,32 @@ class SpecFileService(FileSystemService): @staticmethod - def update_process_cache(ref: FileReference) -> None: - process_id_lookup = BpmnProcessIdLookup.query.filter_by(bpmn_process_identifier=ref.id).first() + def update_process_cache(ref: SpecReference) -> None: + process_id_lookup = SpecReferenceCache.query.filter_by(identifier=ref.identifier).first() if process_id_lookup is None: - process_id_lookup = BpmnProcessIdLookup( - bpmn_process_identifier=ref.id, - display_name=ref.name, - bpmn_file_relative_path=ref.file_path, + process_id_lookup = SpecReferenceCache( + identifier=ref.identifier, + display_name=ref.display_name, + relative_path=ref.relative_path, + type=ref.type, + is_executable=ref.is_executable ) db.session.add(process_id_lookup) else: - if ref.file_path != process_id_lookup.bpmn_file_relative_path: + if ref.relative_path != process_id_lookup.relative_path: full_bpmn_file_path = SpecFileService.full_path_from_relative_path( - process_id_lookup.bpmn_file_relative_path + process_id_lookup.relative_path ) # if the old relative bpmn file no longer exists, then assume things were moved around # on the file system. Otherwise, assume it is a duplicate process id and error. if os.path.isfile(full_bpmn_file_path): raise ValidationException( - f"Process id ({ref.id}) has already been used for " - f"{process_id_lookup.bpmn_file_relative_path}. It cannot be reused." + f"Process id ({ref.identifier}) has already been used for " + f"{process_id_lookup.relative_path}. It cannot be reused." ) else: - process_id_lookup.bpmn_file_relative_path = ( - ref.file_path + process_id_lookup.relative_path = ( + ref.relative_path ) db.session.add(process_id_lookup) db.session.commit() @@ -262,7 +266,7 @@ class SpecFileService(FileSystemService): @staticmethod - def update_message_cache(ref: FileReference) -> None: + def update_message_cache(ref: SpecReference) -> None: """Assure we have a record in the database of all possible message ids and names.""" for message_model_identifier in ref.messages.keys(): message_model = MessageModel.query.filter_by(identifier=message_model_identifier).first() @@ -274,7 +278,7 @@ class SpecFileService(FileSystemService): db.session.commit() @staticmethod - def update_message_trigger_cache(ref: FileReference, process_model_info: ProcessModelInfo) -> None: + def update_message_trigger_cache(ref: SpecReference, process_model_info: ProcessModelInfo) -> None: """assure we know which messages can trigger the start of a process.""" for message_model_identifier in ref.start_messages: message_model = MessageModel.query.filter_by( @@ -313,7 +317,7 @@ class SpecFileService(FileSystemService): ) @staticmethod - def update_correlation_cache(ref: FileReference) -> None: + def update_correlation_cache(ref: SpecReference) -> None: for correlation_identifier in ref.correlations.keys(): correlation_property_retrieval_expressions = \ ref.correlations[correlation_identifier]['retrieval_expressions'] diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/helpers/example_data.py b/spiffworkflow-backend/tests/spiffworkflow_backend/helpers/example_data.py index b5261a26b..8a19a4c43 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/helpers/example_data.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/helpers/example_data.py @@ -86,7 +86,7 @@ class ExampleDataLoader: ) if is_primary: references = SpecFileService.get_references_for_file(file_info, spec) - spec.primary_process_id = references[0].id + spec.primary_process_id = references[0].identifier spec.primary_file_name = filename ProcessModelService().save_process_model(spec) finally: diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_model.py b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_model.py index 5b5b9f256..0d0939547 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_model.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_model.py @@ -5,7 +5,7 @@ from flask_bpmn.models.db import db from tests.spiffworkflow_backend.helpers.base_test import BaseTest from tests.spiffworkflow_backend.helpers.test_data import load_test_spec -from spiffworkflow_backend.models.bpmn_process_id_lookup import BpmnProcessIdLookup +from spiffworkflow_backend.models.spec_reference import SpecReferenceCache from spiffworkflow_backend.models.process_model import ProcessModelInfo from spiffworkflow_backend.models.user import UserModel from spiffworkflow_backend.services.process_instance_processor import ( @@ -116,7 +116,7 @@ class TestProcessModel(BaseTest): # delete all of the id lookup items to force to processor to find the correct # process model when running the process - db.session.query(BpmnProcessIdLookup).delete() + db.session.query(SpecReferenceCache).delete() db.session.commit() processor = ProcessInstanceProcessor(process_instance) processor.do_engine_steps(save=True) diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_spec_file_service.py b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_spec_file_service.py index 004d98593..391c86fc5 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_spec_file_service.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_spec_file_service.py @@ -10,7 +10,7 @@ from SpiffWorkflow.dmn.parser.BpmnDmnParser import BpmnDmnParser # type: ignore from tests.spiffworkflow_backend.helpers.base_test import BaseTest from tests.spiffworkflow_backend.helpers.test_data import load_test_spec -from spiffworkflow_backend.models.bpmn_process_id_lookup import BpmnProcessIdLookup +from spiffworkflow_backend.models.spec_reference import SpecReferenceCache from spiffworkflow_backend.models.user import UserModel from spiffworkflow_backend.services.process_model_service import ProcessModelService from spiffworkflow_backend.services.spec_file_service import SpecFileService @@ -45,11 +45,11 @@ class TestSpecFileService(BaseTest): bpmn_file_name=self.bpmn_file_name, bpmn_file_location="call_activity_nested", ) - bpmn_process_id_lookups = BpmnProcessIdLookup.query.all() + bpmn_process_id_lookups = SpecReferenceCache.query.all() assert len(bpmn_process_id_lookups) == 1 - assert bpmn_process_id_lookups[0].bpmn_process_identifier == "Level1" + assert bpmn_process_id_lookups[0].identifier == "Level1" assert ( - bpmn_process_id_lookups[0].bpmn_file_relative_path + bpmn_process_id_lookups[0].relative_path == self.call_activity_nested_relative_file_path ) @@ -70,14 +70,14 @@ class TestSpecFileService(BaseTest): bpmn_file_name=self.bpmn_file_name, bpmn_file_location=self.process_model_id, ) - bpmn_process_id_lookups = BpmnProcessIdLookup.query.all() + bpmn_process_id_lookups = SpecReferenceCache.query.all() assert len(bpmn_process_id_lookups) == 1 assert ( - bpmn_process_id_lookups[0].bpmn_process_identifier + bpmn_process_id_lookups[0].identifier == bpmn_process_identifier ) assert ( - bpmn_process_id_lookups[0].bpmn_file_relative_path + bpmn_process_id_lookups[0].relative_path == self.call_activity_nested_relative_file_path ) with pytest.raises(ValidationException) as exception: @@ -99,9 +99,9 @@ class TestSpecFileService(BaseTest): ) -> None: """Test_updates_relative_file_path_when_appropriate.""" bpmn_process_identifier = "Level1" - process_id_lookup = BpmnProcessIdLookup( - bpmn_process_identifier=bpmn_process_identifier, - bpmn_file_relative_path=self.call_activity_nested_relative_file_path, + process_id_lookup = SpecReferenceCache( + identifier=bpmn_process_identifier, + relative_path=self.call_activity_nested_relative_file_path, ) db.session.add(process_id_lookup) db.session.commit() @@ -115,14 +115,14 @@ class TestSpecFileService(BaseTest): bpmn_file_location=self.process_model_id, ) - bpmn_process_id_lookups = BpmnProcessIdLookup.query.all() + bpmn_process_id_lookups = SpecReferenceCache.query.all() assert len(bpmn_process_id_lookups) == 1 assert ( - bpmn_process_id_lookups[0].bpmn_process_identifier + bpmn_process_id_lookups[0].identifier == bpmn_process_identifier ) assert ( - bpmn_process_id_lookups[0].bpmn_file_relative_path + bpmn_process_id_lookups[0].relative_path == self.call_activity_nested_relative_file_path ) @@ -166,13 +166,13 @@ class TestSpecFileService(BaseTest): file = next(filter(lambda f: f.name == "call_activity_level_3.bpmn", files)) ca_3 = SpecFileService.get_references_for_file(file, process_model_info) assert len(ca_3) == 1 - assert ca_3[0].name == "Level 3" - assert ca_3[0].id == "Level3" + assert ca_3[0].display_name == "Level 3" + assert ca_3[0].identifier == "Level3" assert ca_3[0].type == "process" file = next(filter(lambda f: f.name == "level2c.dmn", files)) dmn1 = SpecFileService.get_references_for_file(file, process_model_info) assert len(dmn1) == 1 - assert dmn1[0].name == "Decision 1" - assert dmn1[0].id == "Decision_0vrtcmk" + assert dmn1[0].display_name == "Decision 1" + assert dmn1[0].identifier == "Decision_0vrtcmk" assert dmn1[0].type == "decision" diff --git a/spiffworkflow-frontend/package-lock.json b/spiffworkflow-frontend/package-lock.json index 79b2bbb52..e890810f1 100644 --- a/spiffworkflow-frontend/package-lock.json +++ b/spiffworkflow-frontend/package-lock.json @@ -7485,7 +7485,7 @@ }, "node_modules/bpmn-js-spiffworkflow": { "version": "0.0.8", - "resolved": "git+ssh://git@github.com/sartography/bpmn-js-spiffworkflow.git#09fa713bb0bb1b9d4f97684afc46bc3711e11770", + "resolved": "git+ssh://git@github.com/sartography/bpmn-js-spiffworkflow.git#c90359945c98034c76a65fcbe8709f8ddeaf949a", "license": "MIT", "dependencies": { "inherits": "^2.0.4", @@ -35755,7 +35755,7 @@ } }, "bpmn-js-spiffworkflow": { - "version": "git+ssh://git@github.com/sartography/bpmn-js-spiffworkflow.git#09fa713bb0bb1b9d4f97684afc46bc3711e11770", + "version": "git+ssh://git@github.com/sartography/bpmn-js-spiffworkflow.git#c90359945c98034c76a65fcbe8709f8ddeaf949a", "from": "bpmn-js-spiffworkflow@sartography/bpmn-js-spiffworkflow#main", "requires": { "inherits": "^2.0.4", From 88a40c73ea9504c40e3134719b7a9519bf2341f9 Mon Sep 17 00:00:00 2001 From: Dan Date: Mon, 14 Nov 2022 20:31:20 -0500 Subject: [PATCH 2/6] Assure that the list of cached Spec References includes all Process Instances and DMNs (even those that are not primary) --- .../src/spiffworkflow_backend/models/file.py | 32 ++------------ .../services/spec_file_service.py | 44 +++++++++---------- .../integration/test_process_api.py | 30 +++++++++++++ 3 files changed, 53 insertions(+), 53 deletions(-) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/models/file.py b/spiffworkflow-backend/src/spiffworkflow_backend/models/file.py index dbb092df7..a6e87552c 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/models/file.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/models/file.py @@ -9,6 +9,7 @@ from marshmallow import INCLUDE from marshmallow import Schema from spiffworkflow_backend.helpers.spiff_enum import SpiffEnum +from spiffworkflow_backend.models.spec_reference import SpecReference class FileType(SpiffEnum): @@ -62,25 +63,6 @@ CONTENT_TYPES = { } -@dataclass() -class FileReference: - """File Reference Information. - - Includes items such as the process id and name for a BPMN, - or the Decision id and Decision name for a DMN file. There may be more than - one reference that points to a particular file. - """ - - id: str - name: str - type: str # can be 'process', 'decision', or just 'file' - file_name: str - file_path: str - has_lanes: bool - executable: bool - messages: dict - correlations: dict - start_messages: list @dataclass(order=True) class File: @@ -93,7 +75,7 @@ class File: type: str last_modified: datetime size: int - references: Optional[list[FileReference]] = None + references: Optional[list[SpecReference]] = None file_contents: Optional[bytes] = None process_model_id: Optional[str] = None process_group_id: Optional[str] = None @@ -146,16 +128,8 @@ class FileSchema(Schema): ] unknown = INCLUDE references = marshmallow.fields.List( - marshmallow.fields.Nested("FileReferenceSchema") + marshmallow.fields.Nested("SpecReferenceSchema") ) -class FileReferenceSchema(Schema): - """FileSchema.""" - class Meta: - """Meta.""" - - model = FileReference - fields = ["id", "name", "type"] - unknown = INCLUDE diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/spec_file_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/spec_file_service.py index 49c29b36a..0c1778f01 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/spec_file_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/spec_file_service.py @@ -144,36 +144,32 @@ class SpecFileService(FileSystemService): ) -> File: """Update_file.""" SpecFileService.assert_valid_file_name(file_name) - # file_path = SpecFileService.file_path(process_model_info, file_name) file_path = os.path.join( FileSystemService.root_path(), process_model_info.id, file_name ) SpecFileService.write_file_data_to_system(file_path, binary_data) file = SpecFileService.to_file_object(file_name, file_path) - if file.type == FileType.bpmn.value: - set_primary_file = False - if ( - process_model_info.primary_file_name is None - or file_name == process_model_info.primary_file_name - ): - # If no primary process exists, make this primary process. - set_primary_file = True - references = SpecFileService.get_references_for_file(file, process_model_info) - for ref in references: - if ref.type == "process": - ProcessModelService().update_spec( - process_model_info, { - "primary_process_id": ref.identifier, - "primary_file_name": file_name, - "is_review": ref.has_lanes, - } - ) - SpecFileService.update_process_cache(ref) - SpecFileService.update_message_cache(ref) - SpecFileService.update_message_trigger_cache(ref, process_model_info) - SpecFileService.update_correlation_cache(ref) - break + references = SpecFileService.get_references_for_file(file, process_model_info) + primary_process_ref = next((ref for ref in references if ref.is_primary), None) + 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": + ref.is_primary = True + + if ref.is_primary: + ProcessModelService().update_spec( + process_model_info, { + "primary_process_id": ref.identifier, + "primary_file_name": file_name, + "is_review": ref.has_lanes, + } + ) + SpecFileService.update_process_cache(ref) + SpecFileService.update_message_cache(ref) + SpecFileService.update_message_trigger_cache(ref, process_model_info) + SpecFileService.update_correlation_cache(ref) return file 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 5960500f8..fb1453ab9 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py @@ -9,6 +9,8 @@ import pytest from flask.app import Flask from flask.testing import FlaskClient from flask_bpmn.models.db import db + +from spiffworkflow_backend.models.spec_reference import SpecReferenceCache from tests.spiffworkflow_backend.helpers.base_test import BaseTest from tests.spiffworkflow_backend.helpers.test_data import load_test_spec @@ -439,6 +441,34 @@ class TestProcessApi(BaseTest): assert response.json["pagination"]["total"] == 5 assert response.json["pagination"]["pages"] == 2 + def test_process_list(self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, + ): + """It should be possible to get a list of all processes known to the system.""" + + load_test_spec( + "test_group_one/simple_form", + process_model_source_directory='simple_form', + bpmn_file_name='simple_form' + ) + # When adding a process model with one Process, no decisions, and some json files, only one process is recorded. + assert(len(SpecReferenceCache.query.all()) == 1) + + self.create_group_and_model_with_bpmn( + client=client, + user=with_super_admin_user, + process_group_id='test_group_two', + process_model_id='call_activity_nested', + bpmn_file_location='call_activity_nested' + ) + # When adding a process model with 4 processes and a decision, 5 new records will be in the Cache + assert(len(SpecReferenceCache.query.all()) == 6) + + + def test_process_group_add( self, app: Flask, From 22d11862368e829b918fa8a932e918704a6f704f Mon Sep 17 00:00:00 2001 From: Dan Date: Mon, 14 Nov 2022 21:54:13 -0500 Subject: [PATCH 3/6] Adding a very simple api endpoint that just returns a list of every process known to the system. --- .../{3e2a826ac720_.py => 7d1662ea1227_.py} | 7 ++-- .../src/spiffworkflow_backend/api.yml | 38 ++++++++++++++++++- .../models/spec_reference.py | 7 +++- .../routes/process_api_blueprint.py | 9 ++++- .../services/spec_file_service.py | 12 ++++-- .../integration/test_process_api.py | 12 ++++++ 6 files changed, 76 insertions(+), 9 deletions(-) rename spiffworkflow-backend/migrations/versions/{3e2a826ac720_.py => 7d1662ea1227_.py} (99%) diff --git a/spiffworkflow-backend/migrations/versions/3e2a826ac720_.py b/spiffworkflow-backend/migrations/versions/7d1662ea1227_.py similarity index 99% rename from spiffworkflow-backend/migrations/versions/3e2a826ac720_.py rename to spiffworkflow-backend/migrations/versions/7d1662ea1227_.py index c9e17c1cb..a00fc0180 100644 --- a/spiffworkflow-backend/migrations/versions/3e2a826ac720_.py +++ b/spiffworkflow-backend/migrations/versions/7d1662ea1227_.py @@ -1,8 +1,8 @@ """empty message -Revision ID: 3e2a826ac720 +Revision ID: 7d1662ea1227 Revises: -Create Date: 2022-11-14 14:59:43.265173 +Create Date: 2022-11-14 21:48:34.469311 """ from alembic import op @@ -10,7 +10,7 @@ import sqlalchemy as sa # revision identifiers, used by Alembic. -revision = '3e2a826ac720' +revision = '7d1662ea1227' down_revision = None branch_labels = None depends_on = None @@ -42,6 +42,7 @@ def upgrade(): sa.Column('id', sa.Integer(), nullable=False), sa.Column('identifier', sa.String(length=255), nullable=True), sa.Column('display_name', sa.String(length=255), nullable=True), + sa.Column('process_model_id', sa.String(length=255), nullable=True), sa.Column('type', sa.String(length=255), nullable=True), sa.Column('file_name', sa.String(length=255), nullable=True), sa.Column('relative_path', sa.String(length=255), nullable=True), diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/api.yml b/spiffworkflow-backend/src/spiffworkflow_backend/api.yml index a87d5a2ff..97f12b7d4 100755 --- a/spiffworkflow-backend/src/spiffworkflow_backend/api.yml +++ b/spiffworkflow-backend/src/spiffworkflow_backend/api.yml @@ -371,6 +371,23 @@ paths: application/json: schema: $ref: "#/components/schemas/OkTrue" + # process_model_list + /processes: + get: + operationId: spiffworkflow_backend.routes.process_api_blueprint.process_list + summary: Return a list of all processes (not just primary process of a process model) + useful for finding processes for call activites. + tags: + - Process Models + responses: + "200": + description: Successfully return the requested processes + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/Process" /process-instances: parameters: @@ -1530,7 +1547,26 @@ components: type: string x-nullable: true example: Some Value - + Process: + properties: + identifier: + type: string + display_name: + type: string + process_group_id: + type: string + process_model_id: + type: string + type: + type: string + file_name: + type: string + has_lanes: + type: boolean + is_executable: + type: boolean + is_primary: + type: boolean ProcessModel: properties: id: diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/models/spec_reference.py b/spiffworkflow-backend/src/spiffworkflow_backend/models/spec_reference.py index e3fbabe7d..816025efd 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/models/spec_reference.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/models/spec_reference.py @@ -19,6 +19,7 @@ class SpecReference: identifier: str # The id of the process or decision. "Process_1234" display_name: str # The name of the process or decision. "Invoice Submission" + process_model_id: str type: str # can be 'process' or 'decision' file_name: str # The name of the file where this process or decision is defined. relative_path: str # The path to the file. @@ -38,6 +39,7 @@ class SpecReferenceCache(SpiffworkflowBaseDBModel): id = db.Column(db.Integer, primary_key=True) identifier = db.Column(db.String(255), unique=True, index=True) display_name = db.Column(db.String(255), index=True) + process_model_id = db.Column(db.String(255)) type = db.Column(db.String(255), index=True) # either 'process' or 'decision' file_name = db.Column(db.String(255)) relative_path = db.Column(db.String(255)) @@ -52,5 +54,8 @@ class SpecReferenceSchema(Schema): """Meta.""" model = SpecReference - fields = ["identifier", "display_name", "type"] + fields = ["identifier", "display_name", + "process_group_id", "process_model_id", + "type", "file_name", "has_lanes", + "is_executable", "is_primary"] unknown = INCLUDE \ No newline at end of file diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py index a813480fd..f13a25728 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py @@ -59,6 +59,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, SpecReferenceSchema from spiffworkflow_backend.models.spiff_logging import SpiffLoggingModel from spiffworkflow_backend.models.spiff_step_details import SpiffStepDetailsModel from spiffworkflow_backend.models.user import UserModel @@ -336,10 +337,16 @@ def process_model_list( "pages": pages, }, } - return Response(json.dumps(response_json), status=200, mimetype="application/json") +def process_list() -> any: + """Returns a list of all known processes - this includes processes that are not the + primary process - helpful for finding possible call activities. """ + references = SpecReferenceCache.query.filter_by(type = "process") + return SpecReferenceSchema(many=True).dump(references) + + def get_file(modified_process_model_id: str, file_name: str) -> Any: """Get_file.""" process_model_identifier = modified_process_model_id.replace(":", "/") diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/spec_file_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/spec_file_service.py index 0c1778f01..9b9cb5182 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/spec_file_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/spec_file_service.py @@ -124,7 +124,9 @@ class SpecFileService(FileSystemService): start_messages = sub_parser.start_messages() is_primary = sub_parser.get_id() == process_model_info.primary_process_id references.append(SpecReference( - identifier=sub_parser.get_id(), display_name=sub_parser.get_name(), type=parser_type, + identifier=sub_parser.get_id(), display_name=sub_parser.get_name(), + process_model_id=process_model_info.id, + type=parser_type, file_name=file.name, relative_path=file_path, has_lanes=has_lanes, is_executable=is_executable, messages=messages, is_primary=is_primary, correlations=correlations, start_messages=start_messages @@ -235,9 +237,13 @@ class SpecFileService(FileSystemService): process_id_lookup = SpecReferenceCache( identifier=ref.identifier, display_name=ref.display_name, - relative_path=ref.relative_path, + process_model_id=ref.process_model_id, type=ref.type, - is_executable=ref.is_executable + file_name=ref.file_name, + has_lanes=ref.has_lanes, + is_executable=ref.is_executable, + is_primary=ref.is_primary, + relative_path=ref.relative_path, ) db.session.add(process_id_lookup) else: 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 fb1453ab9..bc638e406 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py @@ -467,6 +467,18 @@ class TestProcessApi(BaseTest): # When adding a process model with 4 processes and a decision, 5 new records will be in the Cache assert(len(SpecReferenceCache.query.all()) == 6) + # get the results + response = client.get("/v1.0/processes", headers=self.logged_in_headers(with_super_admin_user), + ) + assert response.json is not None + # 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') + assert(simple_form['display_name'] == 'Process With Form') + assert(simple_form['process_model_id'] == 'test_group_one/simple_form') + assert(simple_form['has_lanes'] == False) + assert(simple_form['is_executable'] == True) + assert(simple_form['is_primary'] == True) def test_process_group_add( From 96f8cf1fd27982b791f6d6cb0d0febb120b7c5d5 Mon Sep 17 00:00:00 2001 From: Dan Date: Mon, 14 Nov 2022 22:01:38 -0500 Subject: [PATCH 4/6] A little quick code cleanup. --- .../spiffworkflow_backend/models/spec_reference.py | 14 ++++++++++++++ .../services/spec_file_service.py | 12 +----------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/models/spec_reference.py b/spiffworkflow-backend/src/spiffworkflow_backend/models/spec_reference.py index 816025efd..525641366 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/models/spec_reference.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/models/spec_reference.py @@ -47,6 +47,20 @@ class SpecReferenceCache(SpiffworkflowBaseDBModel): is_executable = db.Column(db.Boolean()) # either 'process' or 'decision' is_primary = db.Column(db.Boolean()) + @classmethod + def from_spec_reference(cls, ref): + return cls( + identifier=ref.identifier, + display_name=ref.display_name, + process_model_id=ref.process_model_id, + type=ref.type, + file_name=ref.file_name, + has_lanes=ref.has_lanes, + is_executable=ref.is_executable, + is_primary=ref.is_primary, + relative_path=ref.relative_path,) + + class SpecReferenceSchema(Schema): """FileSchema.""" diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/spec_file_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/spec_file_service.py index 9b9cb5182..4029444f3 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/spec_file_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/spec_file_service.py @@ -234,17 +234,7 @@ class SpecFileService(FileSystemService): def update_process_cache(ref: SpecReference) -> None: process_id_lookup = SpecReferenceCache.query.filter_by(identifier=ref.identifier).first() if process_id_lookup is None: - process_id_lookup = SpecReferenceCache( - identifier=ref.identifier, - display_name=ref.display_name, - process_model_id=ref.process_model_id, - type=ref.type, - file_name=ref.file_name, - has_lanes=ref.has_lanes, - is_executable=ref.is_executable, - is_primary=ref.is_primary, - relative_path=ref.relative_path, - ) + process_id_lookup = SpecReferenceCache.from_spec_reference(ref) db.session.add(process_id_lookup) else: if ref.relative_path != process_id_lookup.relative_path: From d748f019e8d94e0c72f1089326c5f9c74285e796 Mon Sep 17 00:00:00 2001 From: burnettk Date: Tue, 15 Nov 2022 10:11:26 -0500 Subject: [PATCH 5/6] run_pyl --- spiffworkflow-backend/migrations/env.py | 2 - .../src/spiffworkflow_backend/api.yml | 2 +- .../src/spiffworkflow_backend/models/file.py | 5 -- .../models/spec_reference.py | 57 +++++++++++-------- .../routes/process_api_blueprint.py | 7 ++- .../services/process_instance_processor.py | 17 ++++-- .../services/spec_file_service.py | 40 +++++++------ .../integration/test_process_api.py | 49 ++++++++-------- .../unit/test_process_model.py | 2 +- .../unit/test_spec_file_service.py | 10 +--- 10 files changed, 102 insertions(+), 89 deletions(-) diff --git a/spiffworkflow-backend/migrations/env.py b/spiffworkflow-backend/migrations/env.py index 68feded2a..630e381ad 100644 --- a/spiffworkflow-backend/migrations/env.py +++ b/spiffworkflow-backend/migrations/env.py @@ -1,5 +1,3 @@ -from __future__ import with_statement - import logging from logging.config import fileConfig diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/api.yml b/spiffworkflow-backend/src/spiffworkflow_backend/api.yml index 064b7e06a..dfea2ce81 100755 --- a/spiffworkflow-backend/src/spiffworkflow_backend/api.yml +++ b/spiffworkflow-backend/src/spiffworkflow_backend/api.yml @@ -375,7 +375,7 @@ paths: /processes: get: operationId: spiffworkflow_backend.routes.process_api_blueprint.process_list - summary: Return a list of all processes (not just primary process of a process model) + summary: Return a list of all processes (not just primary process of a process model) useful for finding processes for call activites. tags: - Process Models diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/models/file.py b/spiffworkflow-backend/src/spiffworkflow_backend/models/file.py index 7075e417f..449c145d9 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/models/file.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/models/file.py @@ -63,8 +63,6 @@ CONTENT_TYPES = { } - - @dataclass(order=True) class File: """File.""" @@ -131,6 +129,3 @@ class FileSchema(Schema): references = marshmallow.fields.List( marshmallow.fields.Nested("SpecReferenceSchema") ) - - - diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/models/spec_reference.py b/spiffworkflow-backend/src/spiffworkflow_backend/models/spec_reference.py index 525641366..810cbc440 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/models/spec_reference.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/models/spec_reference.py @@ -18,21 +18,21 @@ class SpecReference: """ identifier: str # The id of the process or decision. "Process_1234" - display_name: str # The name of the process or decision. "Invoice Submission" + display_name: str # The name of the process or decision. "Invoice Submission" process_model_id: str type: str # can be 'process' or 'decision' - file_name: str # The name of the file where this process or decision is defined. - relative_path: str # The path to the file. - has_lanes: bool # If this is a process, whether it has lanes or not. - is_executable: bool # Whether this process or decision is designated as executable. - is_primary: bool # Whether this is the primary process of a process model - messages: dict # Any messages defined in the same file where this process is defined. - correlations: dict # Any correlations defined in the same file with this process. - start_messages: list # The names of any messages that would start this process. + file_name: str # The name of the file where this process or decision is defined. + relative_path: str # The path to the file. + has_lanes: bool # If this is a process, whether it has lanes or not. + is_executable: bool # Whether this process or decision is designated as executable. + is_primary: bool # Whether this is the primary process of a process model + messages: dict # Any messages defined in the same file where this process is defined. + correlations: dict # Any correlations defined in the same file with this process. + start_messages: list # The names of any messages that would start this process. class SpecReferenceCache(SpiffworkflowBaseDBModel): - """A cache of information about all the Processes and Decisions defined in all files. """ + """A cache of information about all the Processes and Decisions defined in all files.""" __tablename__ = "spec_reference_cache" @@ -49,16 +49,18 @@ class SpecReferenceCache(SpiffworkflowBaseDBModel): @classmethod def from_spec_reference(cls, ref): + """From_spec_reference.""" return cls( - identifier=ref.identifier, - display_name=ref.display_name, - process_model_id=ref.process_model_id, - type=ref.type, - file_name=ref.file_name, - has_lanes=ref.has_lanes, - is_executable=ref.is_executable, - is_primary=ref.is_primary, - relative_path=ref.relative_path,) + identifier=ref.identifier, + display_name=ref.display_name, + process_model_id=ref.process_model_id, + type=ref.type, + file_name=ref.file_name, + has_lanes=ref.has_lanes, + is_executable=ref.is_executable, + is_primary=ref.is_primary, + relative_path=ref.relative_path, + ) class SpecReferenceSchema(Schema): @@ -68,8 +70,15 @@ class SpecReferenceSchema(Schema): """Meta.""" model = SpecReference - fields = ["identifier", "display_name", - "process_group_id", "process_model_id", - "type", "file_name", "has_lanes", - "is_executable", "is_primary"] - unknown = INCLUDE \ No newline at end of file + fields = [ + "identifier", + "display_name", + "process_group_id", + "process_model_id", + "type", + "file_name", + "has_lanes", + "is_executable", + "is_primary", + ] + unknown = INCLUDE diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py index c2a7c1385..e61d00794 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py @@ -59,7 +59,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, SpecReferenceSchema +from spiffworkflow_backend.models.spec_reference import SpecReferenceCache +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 from spiffworkflow_backend.models.user import UserModel @@ -340,8 +341,8 @@ def process_model_list( def process_list() -> any: """Returns a list of all known processes - this includes processes that are not the - primary process - helpful for finding possible call activities. """ - references = SpecReferenceCache.query.filter_by(type = "process") + primary process - helpful for finding possible call activities.""" + references = SpecReferenceCache.query.filter_by(type="process") return SpecReferenceSchema(many=True).dump(references) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py index 70ae93192..f13ab8d30 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/process_instance_processor.py @@ -67,7 +67,6 @@ from SpiffWorkflow.util.deep_merge import DeepMerge # type: ignore from spiffworkflow_backend.models.active_task import ActiveTaskModel from spiffworkflow_backend.models.active_task_user import ActiveTaskUserModel -from spiffworkflow_backend.models.spec_reference import SpecReferenceCache from spiffworkflow_backend.models.file import File from spiffworkflow_backend.models.file import FileType from spiffworkflow_backend.models.group import GroupModel @@ -86,6 +85,7 @@ from spiffworkflow_backend.models.process_model import ProcessModelInfo from spiffworkflow_backend.models.script_attributes_context import ( ScriptAttributesContext, ) +from spiffworkflow_backend.models.spec_reference import SpecReferenceCache from spiffworkflow_backend.models.spiff_step_details import SpiffStepDetailsModel from spiffworkflow_backend.models.user import UserModel from spiffworkflow_backend.models.user import UserModelSchema @@ -671,8 +671,9 @@ class ProcessInstanceProcessor: return parser @staticmethod - def backfill_missing_spec_reference_records(bpmn_process_identifier: str) -> Optional[str]: - + def backfill_missing_spec_reference_records( + bpmn_process_identifier: str, + ) -> Optional[str]: """Backfill_missing_spec_reference_records.""" process_models = ProcessModelService().get_process_models() for process_model in process_models: @@ -695,11 +696,15 @@ class ProcessInstanceProcessor: "bpmn_file_full_path_from_bpmn_process_identifier: bpmn_process_identifier is unexpectedly None" ) - spec_reference = SpecReferenceCache.query.filter_by(identifier=bpmn_process_identifier).first() + spec_reference = SpecReferenceCache.query.filter_by( + identifier=bpmn_process_identifier + ).first() bpmn_file_full_path = None if spec_reference is None: - bpmn_file_full_path = ProcessInstanceProcessor.backfill_missing_spec_reference_records( - bpmn_process_identifier + bpmn_file_full_path = ( + ProcessInstanceProcessor.backfill_missing_spec_reference_records( + bpmn_process_identifier + ) ) else: bpmn_file_full_path = os.path.join( diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/spec_file_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/spec_file_service.py index 24a05d128..e8e334f21 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/spec_file_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/spec_file_service.py @@ -8,10 +8,9 @@ from typing import Optional from flask_bpmn.models.db import db from SpiffWorkflow.bpmn.parser.ValidationException import ValidationException # type: ignore -from spiffworkflow_backend.models.spec_reference import SpecReferenceCache from spiffworkflow_backend.models.file import File -from spiffworkflow_backend.models.file import SpecReference from spiffworkflow_backend.models.file import FileType +from spiffworkflow_backend.models.file import SpecReference from spiffworkflow_backend.models.message_correlation_property import ( MessageCorrelationPropertyModel, ) @@ -20,6 +19,7 @@ from spiffworkflow_backend.models.message_triggerable_process_model import ( MessageTriggerableProcessModel, ) from spiffworkflow_backend.models.process_model import ProcessModelInfo +from spiffworkflow_backend.models.spec_reference import SpecReferenceCache from spiffworkflow_backend.services.custom_parser import MyCustomParser from spiffworkflow_backend.services.file_system_service import FileSystemService from spiffworkflow_backend.services.process_model_service import ProcessModelService @@ -55,7 +55,7 @@ class SpecFileService(FileSystemService): @staticmethod def reference_map(references: list[SpecReference]) -> dict[str, SpecReference]: - """ Creates a dict with provided references organized by id. """ + """Creates a dict with provided references organized by id.""" ref_map = {} for ref in references: ref_map[ref.identifier] = ref @@ -63,13 +63,15 @@ class SpecFileService(FileSystemService): @staticmethod def get_references(process_models: List[ProcessModelInfo]) -> list[SpecReference]: - """Returns all references -- process_ids, and decision ids, across all process models provided""" + """Returns all references -- process_ids, and decision ids, across all process models provided.""" references = [] for process_model in process_models: references.extend(SpecFileService.get_references_for_process(process_model)) @staticmethod - def get_references_for_process(process_model_info: ProcessModelInfo) -> list[SpecReference]: + def get_references_for_process( + process_model_info: ProcessModelInfo, + ) -> list[SpecReference]: """Get_references_for_process.""" files = SpecFileService.get_files(process_model_info) references = [] @@ -80,7 +82,9 @@ class SpecFileService(FileSystemService): return references @staticmethod - def get_references_for_file(file: File, process_model_info: ProcessModelInfo) -> list[SpecReference]: + def get_references_for_file( + file: File, process_model_info: ProcessModelInfo + ) -> list[SpecReference]: """Uses spiffworkflow to parse BPMN and DMN files to determine how they can be externally referenced. Returns a list of Reference objects that contain the type of reference, the id, the name. @@ -116,9 +120,11 @@ class SpecFileService(FileSystemService): for sub_parser in sub_parsers: if parser_type == "process": has_lanes = sub_parser.has_lanes() - executable = sub_parser.process_executable + sub_parser.process_executable start_messages = sub_parser.start_messages() - is_primary = sub_parser.get_id() == process_model_info.primary_process_id + is_primary = ( + sub_parser.get_id() == process_model_info.primary_process_id + ) references.append( SpecReference( @@ -133,7 +139,7 @@ class SpecFileService(FileSystemService): messages=messages, is_primary=is_primary, correlations=correlations, - start_messages=start_messages + start_messages=start_messages, ) ) return references @@ -166,11 +172,12 @@ class SpecFileService(FileSystemService): if ref.is_primary: ProcessModelService().update_spec( - process_model_info, { + process_model_info, + { "primary_process_id": ref.identifier, "primary_file_name": file_name, "is_review": ref.has_lanes, - } + }, ) SpecFileService.update_process_cache(ref) SpecFileService.update_message_cache(ref) @@ -229,7 +236,10 @@ class SpecFileService(FileSystemService): @staticmethod def update_process_cache(ref: SpecReference) -> None: - process_id_lookup = SpecReferenceCache.query.filter_by(identifier=ref.identifier).first() + """Update_process_cache.""" + process_id_lookup = SpecReferenceCache.query.filter_by( + identifier=ref.identifier + ).first() if process_id_lookup is None: process_id_lookup = SpecReferenceCache.from_spec_reference(ref) db.session.add(process_id_lookup) @@ -246,9 +256,7 @@ class SpecFileService(FileSystemService): f"{process_id_lookup.relative_path}. It cannot be reused." ) else: - process_id_lookup.relative_path = ( - ref.relative_path - ) + process_id_lookup.relative_path = ref.relative_path db.session.add(process_id_lookup) db.session.commit() @@ -269,7 +277,7 @@ class SpecFileService(FileSystemService): @staticmethod def update_message_trigger_cache( - ref: SpecReference, process_model_info: ProcessModelInfo + ref: SpecReference, process_model_info: ProcessModelInfo ) -> None: """Assure we know which messages can trigger the start of a process.""" for message_model_identifier in ref.start_messages: 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 cad84bb4e..b02f1eb08 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py @@ -9,8 +9,6 @@ import pytest from flask.app import Flask from flask.testing import FlaskClient from flask_bpmn.models.db import db - -from spiffworkflow_backend.models.spec_reference import SpecReferenceCache from tests.spiffworkflow_backend.helpers.base_test import BaseTest from tests.spiffworkflow_backend.helpers.test_data import load_test_spec @@ -27,6 +25,7 @@ from spiffworkflow_backend.models.process_instance_report import ( ) from spiffworkflow_backend.models.process_model import NotificationType from spiffworkflow_backend.models.process_model import ProcessModelInfoSchema +from spiffworkflow_backend.models.spec_reference import SpecReferenceCache from spiffworkflow_backend.models.user import UserModel from spiffworkflow_backend.services.authorization_service import AuthorizationService from spiffworkflow_backend.services.file_system_service import FileSystemService @@ -441,45 +440,49 @@ class TestProcessApi(BaseTest): assert response.json["pagination"]["total"] == 5 assert response.json["pagination"]["pages"] == 2 - def test_process_list(self, - app: Flask, - client: FlaskClient, - with_db_and_bpmn_file_cleanup: None, - with_super_admin_user: UserModel, - ): + def test_process_list( + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + with_super_admin_user: UserModel, + ): """It should be possible to get a list of all processes known to the system.""" load_test_spec( "test_group_one/simple_form", - process_model_source_directory='simple_form', - bpmn_file_name='simple_form' + process_model_source_directory="simple_form", + bpmn_file_name="simple_form", ) # When adding a process model with one Process, no decisions, and some json files, only one process is recorded. - assert(len(SpecReferenceCache.query.all()) == 1) + assert len(SpecReferenceCache.query.all()) == 1 self.create_group_and_model_with_bpmn( client=client, user=with_super_admin_user, - process_group_id='test_group_two', - process_model_id='call_activity_nested', - bpmn_file_location='call_activity_nested' + process_group_id="test_group_two", + process_model_id="call_activity_nested", + bpmn_file_location="call_activity_nested", ) # When adding a process model with 4 processes and a decision, 5 new records will be in the Cache - assert(len(SpecReferenceCache.query.all()) == 6) + assert len(SpecReferenceCache.query.all()) == 6 # get the results - response = client.get("/v1.0/processes", headers=self.logged_in_headers(with_super_admin_user), + response = client.get( + "/v1.0/processes", + headers=self.logged_in_headers(with_super_admin_user), ) assert response.json is not None # 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') - assert(simple_form['display_name'] == 'Process With Form') - assert(simple_form['process_model_id'] == 'test_group_one/simple_form') - assert(simple_form['has_lanes'] == False) - assert(simple_form['is_executable'] == True) - assert(simple_form['is_primary'] == True) - + simple_form = next( + p for p in response.json if p["identifier"] == "Proccess_WithForm" + ) + assert simple_form["display_name"] == "Process With Form" + assert simple_form["process_model_id"] == "test_group_one/simple_form" + assert simple_form["has_lanes"] == False + assert simple_form["is_executable"] == True + assert simple_form["is_primary"] == True def test_process_group_add( self, diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_model.py b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_model.py index 0d0939547..09421bc71 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_model.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_process_model.py @@ -5,8 +5,8 @@ from flask_bpmn.models.db import db from tests.spiffworkflow_backend.helpers.base_test import BaseTest from tests.spiffworkflow_backend.helpers.test_data import load_test_spec -from spiffworkflow_backend.models.spec_reference import SpecReferenceCache from spiffworkflow_backend.models.process_model import ProcessModelInfo +from spiffworkflow_backend.models.spec_reference import SpecReferenceCache from spiffworkflow_backend.models.user import UserModel from spiffworkflow_backend.services.process_instance_processor import ( ProcessInstanceProcessor, diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_spec_file_service.py b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_spec_file_service.py index 2e318936b..deec4a2d0 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_spec_file_service.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_spec_file_service.py @@ -69,10 +69,7 @@ class TestSpecFileService(BaseTest): ) bpmn_process_id_lookups = SpecReferenceCache.query.all() assert len(bpmn_process_id_lookups) == 1 - assert ( - bpmn_process_id_lookups[0].identifier - == bpmn_process_identifier - ) + assert bpmn_process_id_lookups[0].identifier == bpmn_process_identifier assert ( bpmn_process_id_lookups[0].relative_path == self.call_activity_nested_relative_file_path @@ -115,10 +112,7 @@ class TestSpecFileService(BaseTest): bpmn_process_id_lookups = SpecReferenceCache.query.all() assert len(bpmn_process_id_lookups) == 1 - assert ( - bpmn_process_id_lookups[0].identifier - == bpmn_process_identifier - ) + assert bpmn_process_id_lookups[0].identifier == bpmn_process_identifier assert ( bpmn_process_id_lookups[0].relative_path == self.call_activity_nested_relative_file_path From b63693514a89de165ab81d28a6acbf45d7f8780a Mon Sep 17 00:00:00 2001 From: burnettk Date: Tue, 15 Nov 2022 10:28:18 -0500 Subject: [PATCH 6/6] fix mypy typing stuff. w/ jasquat --- .../src/spiffworkflow_backend/models/spec_reference.py | 6 +++--- .../routes/process_api_blueprint.py | 9 ++++++--- .../spiffworkflow_backend/services/spec_file_service.py | 7 ------- .../integration/test_process_api.py | 9 ++++----- 4 files changed, 13 insertions(+), 18 deletions(-) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/models/spec_reference.py b/spiffworkflow-backend/src/spiffworkflow_backend/models/spec_reference.py index 810cbc440..3c3848993 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/models/spec_reference.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/models/spec_reference.py @@ -3,7 +3,7 @@ from dataclasses import dataclass from flask_bpmn.models.db import db from flask_bpmn.models.db import SpiffworkflowBaseDBModel -from flask_marshmallow import Schema +from flask_marshmallow import Schema # type: ignore from marshmallow import INCLUDE @@ -48,7 +48,7 @@ class SpecReferenceCache(SpiffworkflowBaseDBModel): is_primary = db.Column(db.Boolean()) @classmethod - def from_spec_reference(cls, ref): + def from_spec_reference(cls, ref: SpecReference) -> "SpecReferenceCache": """From_spec_reference.""" return cls( identifier=ref.identifier, @@ -63,7 +63,7 @@ class SpecReferenceCache(SpiffworkflowBaseDBModel): ) -class SpecReferenceSchema(Schema): +class SpecReferenceSchema(Schema): # type: ignore """FileSchema.""" class Meta: diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py index e61d00794..1722635cf 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/routes/process_api_blueprint.py @@ -339,9 +339,12 @@ def process_model_list( return Response(json.dumps(response_json), status=200, mimetype="application/json") -def process_list() -> any: - """Returns a list of all known processes - this includes processes that are not the - primary process - helpful for finding possible call activities.""" +def process_list() -> Any: + """Returns a list of all known processes. + + This includes processes that are not the + primary process - helpful for finding possible call activities. + """ references = SpecReferenceCache.query.filter_by(type="process") return SpecReferenceSchema(many=True).dump(references) diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/spec_file_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/spec_file_service.py index e8e334f21..8b629de99 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/spec_file_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/spec_file_service.py @@ -61,13 +61,6 @@ class SpecFileService(FileSystemService): ref_map[ref.identifier] = ref return ref_map - @staticmethod - def get_references(process_models: List[ProcessModelInfo]) -> list[SpecReference]: - """Returns all references -- process_ids, and decision ids, across all process models provided.""" - references = [] - for process_model in process_models: - references.extend(SpecFileService.get_references_for_process(process_model)) - @staticmethod def get_references_for_process( process_model_info: ProcessModelInfo, 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 b02f1eb08..85ee4a058 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/integration/test_process_api.py @@ -446,9 +446,8 @@ class TestProcessApi(BaseTest): client: FlaskClient, with_db_and_bpmn_file_cleanup: None, with_super_admin_user: UserModel, - ): + ) -> None: """It should be possible to get a list of all processes known to the system.""" - load_test_spec( "test_group_one/simple_form", process_model_source_directory="simple_form", @@ -480,9 +479,9 @@ class TestProcessApi(BaseTest): ) assert simple_form["display_name"] == "Process With Form" assert simple_form["process_model_id"] == "test_group_one/simple_form" - assert simple_form["has_lanes"] == False - assert simple_form["is_executable"] == True - assert simple_form["is_primary"] == True + assert simple_form["has_lanes"] is False + assert simple_form["is_executable"] is True + assert simple_form["is_primary"] is True def test_process_group_add( self,