Merge remote-tracking branch 'origin/main' into feature/log_filters

This commit is contained in:
jasquat 2023-04-17 16:29:58 -04:00
commit 971b69ffb3
16 changed files with 3032 additions and 2463 deletions

1
.gitignore vendored
View File

@ -5,3 +5,4 @@ t
.dccache
*~
version_info.json
.coverage*

View File

@ -19,3 +19,4 @@ node_modules
/bin/import_secrets.py
/src/spiffworkflow_backend/config/secrets.py
*null-ls_*
/local_wheels/*.whl

View File

@ -0,0 +1,15 @@
# Local Wheels
If you have any wheels you wish to test locally, copy them into this folder then run:
```
poetry add local_wheels/my.whl
```
Alternatively you can sideload it:
```
poetry run pip install local_wheels/*.whl
```
when you boot the backend.

View File

@ -14,15 +14,30 @@ config = context.config
fileConfig(config.config_file_name)
logger = logging.getLogger('alembic.env')
def get_engine():
try:
# this works with Flask-SQLAlchemy<3 and Alchemical
return current_app.extensions['migrate'].db.get_engine()
except TypeError:
# this works with Flask-SQLAlchemy>=3
return current_app.extensions['migrate'].db.engine
def get_engine_url():
try:
return get_engine().url.render_as_string(hide_password=False).replace(
'%', '%%')
except AttributeError:
return str(get_engine().url).replace('%', '%%')
# 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
config.set_main_option('sqlalchemy.url', get_engine_url())
target_db = current_app.extensions['migrate'].db
# other values from the config, defined by the needs of env.py,
# can be acquired:
@ -30,6 +45,12 @@ target_metadata = current_app.extensions['migrate'].db.metadata
# ... etc.
def get_metadata():
if hasattr(target_db, 'metadatas'):
return target_db.metadatas[None]
return target_db.metadata
def run_migrations_offline():
"""Run migrations in 'offline' mode.
@ -44,7 +65,7 @@ def run_migrations_offline():
"""
url = config.get_main_option("sqlalchemy.url")
context.configure(
url=url, target_metadata=target_metadata, literal_binds=True
url=url, target_metadata=get_metadata(), literal_binds=True
)
with context.begin_transaction():
@ -69,12 +90,12 @@ def run_migrations_online():
directives[:] = []
logger.info('No changes in schema detected.')
connectable = current_app.extensions['migrate'].db.get_engine()
connectable = get_engine()
with connectable.connect() as connection:
context.configure(
connection=connection,
target_metadata=target_metadata,
target_metadata=get_metadata(),
process_revision_directives=process_revision_directives,
**current_app.extensions['migrate'].configure_args
)

View File

@ -1,8 +1,8 @@
"""empty message
Revision ID: 0b5dd14bfbac
Revision ID: 44a8f46cc508
Revises:
Create Date: 2023-03-23 16:25:33.288500
Create Date: 2023-04-17 15:40:28.658588
"""
from alembic import op
@ -10,7 +10,7 @@ import sqlalchemy as sa
from sqlalchemy.dialects import mysql
# revision identifiers, used by Alembic.
revision = '0b5dd14bfbac'
revision = '44a8f46cc508'
down_revision = None
branch_labels = None
depends_on = None
@ -20,7 +20,8 @@ def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('bpmn_process_definition',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('hash', sa.String(length=255), nullable=False),
sa.Column('single_process_hash', sa.String(length=255), nullable=False),
sa.Column('full_process_model_hash', sa.String(length=255), nullable=True),
sa.Column('bpmn_identifier', sa.String(length=255), nullable=False),
sa.Column('bpmn_name', sa.String(length=255), nullable=True),
sa.Column('properties_json', sa.JSON(), nullable=False),
@ -29,10 +30,13 @@ def upgrade():
sa.Column('updated_at_in_seconds', sa.Integer(), nullable=True),
sa.Column('created_at_in_seconds', sa.Integer(), nullable=True),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('hash')
sa.UniqueConstraint('full_process_model_hash'),
sa.UniqueConstraint('full_process_model_hash', 'single_process_hash', name='process_hash_unique')
)
op.create_index(op.f('ix_bpmn_process_definition_bpmn_identifier'), 'bpmn_process_definition', ['bpmn_identifier'], unique=False)
op.create_index(op.f('ix_bpmn_process_definition_bpmn_name'), 'bpmn_process_definition', ['bpmn_name'], unique=False)
with op.batch_alter_table('bpmn_process_definition', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_bpmn_process_definition_bpmn_identifier'), ['bpmn_identifier'], unique=False)
batch_op.create_index(batch_op.f('ix_bpmn_process_definition_bpmn_name'), ['bpmn_name'], unique=False)
op.create_table('correlation_property_cache',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('name', sa.String(length=50), nullable=False),
@ -41,16 +45,20 @@ def upgrade():
sa.Column('retrieval_expression', sa.String(length=255), nullable=True),
sa.PrimaryKeyConstraint('id')
)
op.create_index(op.f('ix_correlation_property_cache_message_name'), 'correlation_property_cache', ['message_name'], unique=False)
op.create_index(op.f('ix_correlation_property_cache_name'), 'correlation_property_cache', ['name'], unique=False)
with op.batch_alter_table('correlation_property_cache', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_correlation_property_cache_message_name'), ['message_name'], unique=False)
batch_op.create_index(batch_op.f('ix_correlation_property_cache_name'), ['name'], unique=False)
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_index(op.f('ix_group_identifier'), 'group', ['identifier'], unique=False)
op.create_index(op.f('ix_group_name'), 'group', ['name'], unique=False)
with op.batch_alter_table('group', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_group_identifier'), ['identifier'], unique=False)
batch_op.create_index(batch_op.f('ix_group_name'), ['name'], unique=False)
op.create_table('json_data',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('hash', sa.String(length=255), nullable=False),
@ -66,8 +74,10 @@ def upgrade():
sa.Column('created_at_in_seconds', sa.Integer(), nullable=True),
sa.PrimaryKeyConstraint('id')
)
op.create_index(op.f('ix_message_triggerable_process_model_message_name'), 'message_triggerable_process_model', ['message_name'], unique=False)
op.create_index(op.f('ix_message_triggerable_process_model_process_model_identifier'), 'message_triggerable_process_model', ['process_model_identifier'], unique=False)
with op.batch_alter_table('message_triggerable_process_model', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_message_triggerable_process_model_message_name'), ['message_name'], unique=False)
batch_op.create_index(batch_op.f('ix_message_triggerable_process_model_process_model_identifier'), ['process_model_identifier'], unique=False)
op.create_table('permission_target',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('uri', sa.String(length=255), nullable=False),
@ -88,10 +98,12 @@ def upgrade():
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('identifier', 'type', name='_identifier_type_unique')
)
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=False)
op.create_index(op.f('ix_spec_reference_cache_process_model_id'), 'spec_reference_cache', ['process_model_id'], unique=False)
op.create_index(op.f('ix_spec_reference_cache_type'), 'spec_reference_cache', ['type'], unique=False)
with op.batch_alter_table('spec_reference_cache', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_spec_reference_cache_display_name'), ['display_name'], unique=False)
batch_op.create_index(batch_op.f('ix_spec_reference_cache_identifier'), ['identifier'], unique=False)
batch_op.create_index(batch_op.f('ix_spec_reference_cache_process_model_id'), ['process_model_id'], unique=False)
batch_op.create_index(batch_op.f('ix_spec_reference_cache_type'), ['type'], unique=False)
op.create_table('user',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('username', sa.String(length=255), nullable=False),
@ -108,9 +120,11 @@ def upgrade():
sa.UniqueConstraint('service', 'service_id', name='service_key'),
sa.UniqueConstraint('username')
)
op.create_index(op.f('ix_user_email'), 'user', ['email'], unique=False)
op.create_index(op.f('ix_user_service'), 'user', ['service'], unique=False)
op.create_index(op.f('ix_user_service_id'), 'user', ['service_id'], unique=False)
with op.batch_alter_table('user', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_user_email'), ['email'], unique=False)
batch_op.create_index(batch_op.f('ix_user_service'), ['service'], unique=False)
batch_op.create_index(batch_op.f('ix_user_service_id'), ['service_id'], unique=False)
op.create_table('bpmn_process',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('guid', sa.String(length=36), nullable=True),
@ -127,10 +141,12 @@ def upgrade():
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('guid')
)
op.create_index(op.f('ix_bpmn_process_bpmn_process_definition_id'), 'bpmn_process', ['bpmn_process_definition_id'], unique=False)
op.create_index(op.f('ix_bpmn_process_direct_parent_process_id'), 'bpmn_process', ['direct_parent_process_id'], unique=False)
op.create_index(op.f('ix_bpmn_process_json_data_hash'), 'bpmn_process', ['json_data_hash'], unique=False)
op.create_index(op.f('ix_bpmn_process_top_level_process_id'), 'bpmn_process', ['top_level_process_id'], unique=False)
with op.batch_alter_table('bpmn_process', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_bpmn_process_bpmn_process_definition_id'), ['bpmn_process_definition_id'], unique=False)
batch_op.create_index(batch_op.f('ix_bpmn_process_direct_parent_process_id'), ['direct_parent_process_id'], unique=False)
batch_op.create_index(batch_op.f('ix_bpmn_process_json_data_hash'), ['json_data_hash'], unique=False)
batch_op.create_index(batch_op.f('ix_bpmn_process_top_level_process_id'), ['top_level_process_id'], unique=False)
op.create_table('bpmn_process_definition_relationship',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('bpmn_process_definition_parent_id', sa.Integer(), nullable=False),
@ -140,8 +156,10 @@ def upgrade():
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('bpmn_process_definition_parent_id', 'bpmn_process_definition_child_id', name='bpmn_process_definition_relationship_unique')
)
op.create_index(op.f('ix_bpmn_process_definition_relationship_bpmn_process_definition_parent_id'), 'bpmn_process_definition_relationship', ['bpmn_process_definition_parent_id'], unique=False)
op.create_index(op.f('ix_bpmn_process_definition_relationship_bpmn_process_definition_child_id'), 'bpmn_process_definition_relationship', ['bpmn_process_definition_child_id'], unique=False)
with op.batch_alter_table('bpmn_process_definition_relationship', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_bpmn_process_definition_relationship_bpmn_process_definition_parent_id'), ['bpmn_process_definition_parent_id'], unique=False)
batch_op.create_index(batch_op.f('ix_bpmn_process_definition_relationship_bpmn_process_definition_child_id'), ['bpmn_process_definition_child_id'], unique=False)
op.create_table('principal',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('user_id', sa.Integer(), nullable=True),
@ -164,8 +182,10 @@ def upgrade():
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)
with op.batch_alter_table('process_instance_report', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_process_instance_report_created_by_id'), ['created_by_id'], unique=False)
batch_op.create_index(batch_op.f('ix_process_instance_report_identifier'), ['identifier'], unique=False)
op.create_table('refresh_token',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('user_id', sa.Integer(), nullable=False),
@ -185,7 +205,9 @@ def upgrade():
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('key')
)
op.create_index(op.f('ix_secret_user_id'), 'secret', ['user_id'], unique=False)
with op.batch_alter_table('secret', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_secret_user_id'), ['user_id'], unique=False)
op.create_table('task_definition',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('bpmn_process_definition_id', sa.Integer(), nullable=False),
@ -199,10 +221,12 @@ def upgrade():
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('bpmn_process_definition_id', 'bpmn_identifier', name='task_definition_unique')
)
op.create_index(op.f('ix_task_definition_bpmn_identifier'), 'task_definition', ['bpmn_identifier'], unique=False)
op.create_index(op.f('ix_task_definition_bpmn_name'), 'task_definition', ['bpmn_name'], unique=False)
op.create_index(op.f('ix_task_definition_bpmn_process_definition_id'), 'task_definition', ['bpmn_process_definition_id'], unique=False)
op.create_index(op.f('ix_task_definition_typename'), 'task_definition', ['typename'], unique=False)
with op.batch_alter_table('task_definition', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_task_definition_bpmn_identifier'), ['bpmn_identifier'], unique=False)
batch_op.create_index(batch_op.f('ix_task_definition_bpmn_name'), ['bpmn_name'], unique=False)
batch_op.create_index(batch_op.f('ix_task_definition_bpmn_process_definition_id'), ['bpmn_process_definition_id'], unique=False)
batch_op.create_index(batch_op.f('ix_task_definition_typename'), ['typename'], unique=False)
op.create_table('user_group_assignment',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('user_id', sa.Integer(), nullable=False),
@ -212,8 +236,10 @@ def upgrade():
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('user_id', 'group_id', name='user_group_assignment_unique')
)
op.create_index(op.f('ix_user_group_assignment_group_id'), 'user_group_assignment', ['group_id'], unique=False)
op.create_index(op.f('ix_user_group_assignment_user_id'), 'user_group_assignment', ['user_id'], unique=False)
with op.batch_alter_table('user_group_assignment', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_user_group_assignment_group_id'), ['group_id'], unique=False)
batch_op.create_index(batch_op.f('ix_user_group_assignment_user_id'), ['user_id'], unique=False)
op.create_table('user_group_assignment_waiting',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('username', sa.String(length=255), nullable=False),
@ -222,7 +248,9 @@ def upgrade():
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('username', 'group_id', name='user_group_assignment_staged_unique')
)
op.create_index(op.f('ix_user_group_assignment_waiting_group_id'), 'user_group_assignment_waiting', ['group_id'], unique=False)
with op.batch_alter_table('user_group_assignment_waiting', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_user_group_assignment_waiting_group_id'), ['group_id'], unique=False)
op.create_table('permission_assignment',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('principal_id', sa.Integer(), nullable=False),
@ -234,8 +262,10 @@ def upgrade():
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('principal_id', 'permission_target_id', 'permission', name='permission_assignment_uniq')
)
op.create_index(op.f('ix_permission_assignment_permission_target_id'), 'permission_assignment', ['permission_target_id'], unique=False)
op.create_index(op.f('ix_permission_assignment_principal_id'), 'permission_assignment', ['principal_id'], unique=False)
with op.batch_alter_table('permission_assignment', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_permission_assignment_permission_target_id'), ['permission_target_id'], unique=False)
batch_op.create_index(batch_op.f('ix_permission_assignment_principal_id'), ['principal_id'], unique=False)
op.create_table('process_instance',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('process_model_identifier', sa.String(length=255), nullable=False),
@ -256,14 +286,16 @@ def upgrade():
sa.ForeignKeyConstraint(['process_initiator_id'], ['user.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_index(op.f('ix_process_instance_bpmn_process_definition_id'), 'process_instance', ['bpmn_process_definition_id'], unique=False)
op.create_index(op.f('ix_process_instance_bpmn_process_id'), 'process_instance', ['bpmn_process_id'], unique=False)
op.create_index(op.f('ix_process_instance_end_in_seconds'), 'process_instance', ['end_in_seconds'], unique=False)
op.create_index(op.f('ix_process_instance_process_initiator_id'), 'process_instance', ['process_initiator_id'], unique=False)
op.create_index(op.f('ix_process_instance_process_model_display_name'), 'process_instance', ['process_model_display_name'], unique=False)
op.create_index(op.f('ix_process_instance_process_model_identifier'), 'process_instance', ['process_model_identifier'], unique=False)
op.create_index(op.f('ix_process_instance_start_in_seconds'), 'process_instance', ['start_in_seconds'], unique=False)
op.create_index(op.f('ix_process_instance_status'), 'process_instance', ['status'], unique=False)
with op.batch_alter_table('process_instance', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_process_instance_bpmn_process_definition_id'), ['bpmn_process_definition_id'], unique=False)
batch_op.create_index(batch_op.f('ix_process_instance_bpmn_process_id'), ['bpmn_process_id'], unique=False)
batch_op.create_index(batch_op.f('ix_process_instance_end_in_seconds'), ['end_in_seconds'], unique=False)
batch_op.create_index(batch_op.f('ix_process_instance_process_initiator_id'), ['process_initiator_id'], unique=False)
batch_op.create_index(batch_op.f('ix_process_instance_process_model_display_name'), ['process_model_display_name'], unique=False)
batch_op.create_index(batch_op.f('ix_process_instance_process_model_identifier'), ['process_model_identifier'], unique=False)
batch_op.create_index(batch_op.f('ix_process_instance_start_in_seconds'), ['start_in_seconds'], unique=False)
batch_op.create_index(batch_op.f('ix_process_instance_status'), ['status'], unique=False)
op.create_table('message_instance',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('process_instance_id', sa.Integer(), nullable=True),
@ -281,9 +313,11 @@ def upgrade():
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_index(op.f('ix_message_instance_process_instance_id'), 'message_instance', ['process_instance_id'], unique=False)
op.create_index(op.f('ix_message_instance_status'), 'message_instance', ['status'], unique=False)
op.create_index(op.f('ix_message_instance_user_id'), 'message_instance', ['user_id'], unique=False)
with op.batch_alter_table('message_instance', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_message_instance_process_instance_id'), ['process_instance_id'], unique=False)
batch_op.create_index(batch_op.f('ix_message_instance_status'), ['status'], unique=False)
batch_op.create_index(batch_op.f('ix_message_instance_user_id'), ['user_id'], unique=False)
op.create_table('process_instance_event',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('task_guid', sa.String(length=36), nullable=True),
@ -295,11 +329,13 @@ def upgrade():
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_index(op.f('ix_process_instance_event_event_type'), 'process_instance_event', ['event_type'], unique=False)
op.create_index(op.f('ix_process_instance_event_process_instance_id'), 'process_instance_event', ['process_instance_id'], unique=False)
op.create_index(op.f('ix_process_instance_event_task_guid'), 'process_instance_event', ['task_guid'], unique=False)
op.create_index(op.f('ix_process_instance_event_timestamp'), 'process_instance_event', ['timestamp'], unique=False)
op.create_index(op.f('ix_process_instance_event_user_id'), 'process_instance_event', ['user_id'], unique=False)
with op.batch_alter_table('process_instance_event', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_process_instance_event_event_type'), ['event_type'], unique=False)
batch_op.create_index(batch_op.f('ix_process_instance_event_process_instance_id'), ['process_instance_id'], unique=False)
batch_op.create_index(batch_op.f('ix_process_instance_event_task_guid'), ['task_guid'], unique=False)
batch_op.create_index(batch_op.f('ix_process_instance_event_timestamp'), ['timestamp'], unique=False)
batch_op.create_index(batch_op.f('ix_process_instance_event_user_id'), ['user_id'], unique=False)
op.create_table('process_instance_file_data',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('process_instance_id', sa.Integer(), nullable=False),
@ -314,8 +350,10 @@ def upgrade():
sa.ForeignKeyConstraint(['process_instance_id'], ['process_instance.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_index(op.f('ix_process_instance_file_data_digest'), 'process_instance_file_data', ['digest'], unique=False)
op.create_index(op.f('ix_process_instance_file_data_process_instance_id'), 'process_instance_file_data', ['process_instance_id'], unique=False)
with op.batch_alter_table('process_instance_file_data', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_process_instance_file_data_digest'), ['digest'], unique=False)
batch_op.create_index(batch_op.f('ix_process_instance_file_data_process_instance_id'), ['process_instance_id'], unique=False)
op.create_table('process_instance_metadata',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('process_instance_id', sa.Integer(), nullable=False),
@ -327,8 +365,10 @@ def upgrade():
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('process_instance_id', 'key', name='process_instance_metadata_unique')
)
op.create_index(op.f('ix_process_instance_metadata_key'), 'process_instance_metadata', ['key'], unique=False)
op.create_index(op.f('ix_process_instance_metadata_process_instance_id'), 'process_instance_metadata', ['process_instance_id'], unique=False)
with op.batch_alter_table('process_instance_metadata', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_process_instance_metadata_key'), ['key'], unique=False)
batch_op.create_index(batch_op.f('ix_process_instance_metadata_process_instance_id'), ['process_instance_id'], unique=False)
op.create_table('process_instance_queue',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('process_instance_id', sa.Integer(), nullable=False),
@ -343,9 +383,11 @@ def upgrade():
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('process_instance_id')
)
op.create_index(op.f('ix_process_instance_queue_locked_at_in_seconds'), 'process_instance_queue', ['locked_at_in_seconds'], unique=False)
op.create_index(op.f('ix_process_instance_queue_locked_by'), 'process_instance_queue', ['locked_by'], unique=False)
op.create_index(op.f('ix_process_instance_queue_status'), 'process_instance_queue', ['status'], unique=False)
with op.batch_alter_table('process_instance_queue', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_process_instance_queue_locked_at_in_seconds'), ['locked_at_in_seconds'], unique=False)
batch_op.create_index(batch_op.f('ix_process_instance_queue_locked_by'), ['locked_by'], unique=False)
batch_op.create_index(batch_op.f('ix_process_instance_queue_status'), ['status'], unique=False)
op.create_table('task',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('guid', sa.String(length=36), nullable=False),
@ -364,12 +406,14 @@ def upgrade():
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('guid')
)
op.create_index(op.f('ix_task_bpmn_process_id'), 'task', ['bpmn_process_id'], unique=False)
op.create_index(op.f('ix_task_json_data_hash'), 'task', ['json_data_hash'], unique=False)
op.create_index(op.f('ix_task_process_instance_id'), 'task', ['process_instance_id'], unique=False)
op.create_index(op.f('ix_task_python_env_data_hash'), 'task', ['python_env_data_hash'], unique=False)
op.create_index(op.f('ix_task_state'), 'task', ['state'], unique=False)
op.create_index(op.f('ix_task_task_definition_id'), 'task', ['task_definition_id'], unique=False)
with op.batch_alter_table('task', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_task_bpmn_process_id'), ['bpmn_process_id'], unique=False)
batch_op.create_index(batch_op.f('ix_task_json_data_hash'), ['json_data_hash'], unique=False)
batch_op.create_index(batch_op.f('ix_task_process_instance_id'), ['process_instance_id'], unique=False)
batch_op.create_index(batch_op.f('ix_task_python_env_data_hash'), ['python_env_data_hash'], unique=False)
batch_op.create_index(batch_op.f('ix_task_state'), ['state'], unique=False)
batch_op.create_index(batch_op.f('ix_task_task_definition_id'), ['task_definition_id'], unique=False)
op.create_table('human_task',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('process_instance_id', sa.Integer(), nullable=False),
@ -396,12 +440,14 @@ def upgrade():
sa.ForeignKeyConstraint(['task_model_id'], ['task.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_index(op.f('ix_human_task_actual_owner_id'), 'human_task', ['actual_owner_id'], unique=False)
op.create_index(op.f('ix_human_task_completed'), 'human_task', ['completed'], unique=False)
op.create_index(op.f('ix_human_task_completed_by_user_id'), 'human_task', ['completed_by_user_id'], unique=False)
op.create_index(op.f('ix_human_task_lane_assignment_id'), 'human_task', ['lane_assignment_id'], unique=False)
op.create_index(op.f('ix_human_task_process_instance_id'), 'human_task', ['process_instance_id'], unique=False)
op.create_index(op.f('ix_human_task_task_model_id'), 'human_task', ['task_model_id'], unique=False)
with op.batch_alter_table('human_task', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_human_task_actual_owner_id'), ['actual_owner_id'], unique=False)
batch_op.create_index(batch_op.f('ix_human_task_completed'), ['completed'], unique=False)
batch_op.create_index(batch_op.f('ix_human_task_completed_by_user_id'), ['completed_by_user_id'], unique=False)
batch_op.create_index(batch_op.f('ix_human_task_lane_assignment_id'), ['lane_assignment_id'], unique=False)
batch_op.create_index(batch_op.f('ix_human_task_process_instance_id'), ['process_instance_id'], unique=False)
batch_op.create_index(batch_op.f('ix_human_task_task_model_id'), ['task_model_id'], unique=False)
op.create_table('message_instance_correlation_rule',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('message_instance_id', sa.Integer(), nullable=False),
@ -413,8 +459,10 @@ def upgrade():
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('message_instance_id', 'name', name='message_instance_id_name_unique')
)
op.create_index(op.f('ix_message_instance_correlation_rule_message_instance_id'), 'message_instance_correlation_rule', ['message_instance_id'], unique=False)
op.create_index(op.f('ix_message_instance_correlation_rule_name'), 'message_instance_correlation_rule', ['name'], unique=False)
with op.batch_alter_table('message_instance_correlation_rule', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_message_instance_correlation_rule_message_instance_id'), ['message_instance_id'], unique=False)
batch_op.create_index(batch_op.f('ix_message_instance_correlation_rule_name'), ['name'], unique=False)
op.create_table('human_task_user',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('human_task_id', sa.Integer(), nullable=False),
@ -424,111 +472,161 @@ def upgrade():
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('human_task_id', 'user_id', name='human_task_user_unique')
)
op.create_index(op.f('ix_human_task_user_human_task_id'), 'human_task_user', ['human_task_id'], unique=False)
op.create_index(op.f('ix_human_task_user_user_id'), 'human_task_user', ['user_id'], unique=False)
with op.batch_alter_table('human_task_user', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_human_task_user_human_task_id'), ['human_task_id'], unique=False)
batch_op.create_index(batch_op.f('ix_human_task_user_user_id'), ['user_id'], unique=False)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_index(op.f('ix_human_task_user_user_id'), table_name='human_task_user')
op.drop_index(op.f('ix_human_task_user_human_task_id'), table_name='human_task_user')
with op.batch_alter_table('human_task_user', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_human_task_user_user_id'))
batch_op.drop_index(batch_op.f('ix_human_task_user_human_task_id'))
op.drop_table('human_task_user')
op.drop_index(op.f('ix_message_instance_correlation_rule_name'), table_name='message_instance_correlation_rule')
op.drop_index(op.f('ix_message_instance_correlation_rule_message_instance_id'), table_name='message_instance_correlation_rule')
with op.batch_alter_table('message_instance_correlation_rule', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_message_instance_correlation_rule_name'))
batch_op.drop_index(batch_op.f('ix_message_instance_correlation_rule_message_instance_id'))
op.drop_table('message_instance_correlation_rule')
op.drop_index(op.f('ix_human_task_task_model_id'), table_name='human_task')
op.drop_index(op.f('ix_human_task_process_instance_id'), table_name='human_task')
op.drop_index(op.f('ix_human_task_lane_assignment_id'), table_name='human_task')
op.drop_index(op.f('ix_human_task_completed_by_user_id'), table_name='human_task')
op.drop_index(op.f('ix_human_task_completed'), table_name='human_task')
op.drop_index(op.f('ix_human_task_actual_owner_id'), table_name='human_task')
with op.batch_alter_table('human_task', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_human_task_task_model_id'))
batch_op.drop_index(batch_op.f('ix_human_task_process_instance_id'))
batch_op.drop_index(batch_op.f('ix_human_task_lane_assignment_id'))
batch_op.drop_index(batch_op.f('ix_human_task_completed_by_user_id'))
batch_op.drop_index(batch_op.f('ix_human_task_completed'))
batch_op.drop_index(batch_op.f('ix_human_task_actual_owner_id'))
op.drop_table('human_task')
op.drop_index(op.f('ix_task_task_definition_id'), table_name='task')
op.drop_index(op.f('ix_task_state'), table_name='task')
op.drop_index(op.f('ix_task_python_env_data_hash'), table_name='task')
op.drop_index(op.f('ix_task_process_instance_id'), table_name='task')
op.drop_index(op.f('ix_task_json_data_hash'), table_name='task')
op.drop_index(op.f('ix_task_bpmn_process_id'), table_name='task')
with op.batch_alter_table('task', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_task_task_definition_id'))
batch_op.drop_index(batch_op.f('ix_task_state'))
batch_op.drop_index(batch_op.f('ix_task_python_env_data_hash'))
batch_op.drop_index(batch_op.f('ix_task_process_instance_id'))
batch_op.drop_index(batch_op.f('ix_task_json_data_hash'))
batch_op.drop_index(batch_op.f('ix_task_bpmn_process_id'))
op.drop_table('task')
op.drop_index(op.f('ix_process_instance_queue_status'), table_name='process_instance_queue')
op.drop_index(op.f('ix_process_instance_queue_locked_by'), table_name='process_instance_queue')
op.drop_index(op.f('ix_process_instance_queue_locked_at_in_seconds'), table_name='process_instance_queue')
with op.batch_alter_table('process_instance_queue', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_process_instance_queue_status'))
batch_op.drop_index(batch_op.f('ix_process_instance_queue_locked_by'))
batch_op.drop_index(batch_op.f('ix_process_instance_queue_locked_at_in_seconds'))
op.drop_table('process_instance_queue')
op.drop_index(op.f('ix_process_instance_metadata_process_instance_id'), table_name='process_instance_metadata')
op.drop_index(op.f('ix_process_instance_metadata_key'), table_name='process_instance_metadata')
with op.batch_alter_table('process_instance_metadata', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_process_instance_metadata_process_instance_id'))
batch_op.drop_index(batch_op.f('ix_process_instance_metadata_key'))
op.drop_table('process_instance_metadata')
op.drop_index(op.f('ix_process_instance_file_data_process_instance_id'), table_name='process_instance_file_data')
op.drop_index(op.f('ix_process_instance_file_data_digest'), table_name='process_instance_file_data')
with op.batch_alter_table('process_instance_file_data', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_process_instance_file_data_process_instance_id'))
batch_op.drop_index(batch_op.f('ix_process_instance_file_data_digest'))
op.drop_table('process_instance_file_data')
op.drop_index(op.f('ix_process_instance_event_user_id'), table_name='process_instance_event')
op.drop_index(op.f('ix_process_instance_event_timestamp'), table_name='process_instance_event')
op.drop_index(op.f('ix_process_instance_event_task_guid'), table_name='process_instance_event')
op.drop_index(op.f('ix_process_instance_event_process_instance_id'), table_name='process_instance_event')
op.drop_index(op.f('ix_process_instance_event_event_type'), table_name='process_instance_event')
with op.batch_alter_table('process_instance_event', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_process_instance_event_user_id'))
batch_op.drop_index(batch_op.f('ix_process_instance_event_timestamp'))
batch_op.drop_index(batch_op.f('ix_process_instance_event_task_guid'))
batch_op.drop_index(batch_op.f('ix_process_instance_event_process_instance_id'))
batch_op.drop_index(batch_op.f('ix_process_instance_event_event_type'))
op.drop_table('process_instance_event')
op.drop_index(op.f('ix_message_instance_user_id'), table_name='message_instance')
op.drop_index(op.f('ix_message_instance_status'), table_name='message_instance')
op.drop_index(op.f('ix_message_instance_process_instance_id'), table_name='message_instance')
with op.batch_alter_table('message_instance', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_message_instance_user_id'))
batch_op.drop_index(batch_op.f('ix_message_instance_status'))
batch_op.drop_index(batch_op.f('ix_message_instance_process_instance_id'))
op.drop_table('message_instance')
op.drop_index(op.f('ix_process_instance_status'), table_name='process_instance')
op.drop_index(op.f('ix_process_instance_start_in_seconds'), table_name='process_instance')
op.drop_index(op.f('ix_process_instance_process_model_identifier'), table_name='process_instance')
op.drop_index(op.f('ix_process_instance_process_model_display_name'), table_name='process_instance')
op.drop_index(op.f('ix_process_instance_process_initiator_id'), table_name='process_instance')
op.drop_index(op.f('ix_process_instance_end_in_seconds'), table_name='process_instance')
op.drop_index(op.f('ix_process_instance_bpmn_process_id'), table_name='process_instance')
op.drop_index(op.f('ix_process_instance_bpmn_process_definition_id'), table_name='process_instance')
with op.batch_alter_table('process_instance', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_process_instance_status'))
batch_op.drop_index(batch_op.f('ix_process_instance_start_in_seconds'))
batch_op.drop_index(batch_op.f('ix_process_instance_process_model_identifier'))
batch_op.drop_index(batch_op.f('ix_process_instance_process_model_display_name'))
batch_op.drop_index(batch_op.f('ix_process_instance_process_initiator_id'))
batch_op.drop_index(batch_op.f('ix_process_instance_end_in_seconds'))
batch_op.drop_index(batch_op.f('ix_process_instance_bpmn_process_id'))
batch_op.drop_index(batch_op.f('ix_process_instance_bpmn_process_definition_id'))
op.drop_table('process_instance')
op.drop_index(op.f('ix_permission_assignment_principal_id'), table_name='permission_assignment')
op.drop_index(op.f('ix_permission_assignment_permission_target_id'), table_name='permission_assignment')
with op.batch_alter_table('permission_assignment', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_permission_assignment_principal_id'))
batch_op.drop_index(batch_op.f('ix_permission_assignment_permission_target_id'))
op.drop_table('permission_assignment')
op.drop_index(op.f('ix_user_group_assignment_waiting_group_id'), table_name='user_group_assignment_waiting')
with op.batch_alter_table('user_group_assignment_waiting', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_user_group_assignment_waiting_group_id'))
op.drop_table('user_group_assignment_waiting')
op.drop_index(op.f('ix_user_group_assignment_user_id'), table_name='user_group_assignment')
op.drop_index(op.f('ix_user_group_assignment_group_id'), table_name='user_group_assignment')
with op.batch_alter_table('user_group_assignment', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_user_group_assignment_user_id'))
batch_op.drop_index(batch_op.f('ix_user_group_assignment_group_id'))
op.drop_table('user_group_assignment')
op.drop_index(op.f('ix_task_definition_typename'), table_name='task_definition')
op.drop_index(op.f('ix_task_definition_bpmn_process_definition_id'), table_name='task_definition')
op.drop_index(op.f('ix_task_definition_bpmn_name'), table_name='task_definition')
op.drop_index(op.f('ix_task_definition_bpmn_identifier'), table_name='task_definition')
with op.batch_alter_table('task_definition', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_task_definition_typename'))
batch_op.drop_index(batch_op.f('ix_task_definition_bpmn_process_definition_id'))
batch_op.drop_index(batch_op.f('ix_task_definition_bpmn_name'))
batch_op.drop_index(batch_op.f('ix_task_definition_bpmn_identifier'))
op.drop_table('task_definition')
op.drop_index(op.f('ix_secret_user_id'), table_name='secret')
with op.batch_alter_table('secret', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_secret_user_id'))
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')
with op.batch_alter_table('process_instance_report', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_process_instance_report_identifier'))
batch_op.drop_index(batch_op.f('ix_process_instance_report_created_by_id'))
op.drop_table('process_instance_report')
op.drop_table('principal')
op.drop_index(op.f('ix_bpmn_process_definition_relationship_bpmn_process_definition_child_id'), table_name='bpmn_process_definition_relationship')
op.drop_index(op.f('ix_bpmn_process_definition_relationship_bpmn_process_definition_parent_id'), table_name='bpmn_process_definition_relationship')
with op.batch_alter_table('bpmn_process_definition_relationship', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_bpmn_process_definition_relationship_bpmn_process_definition_child_id'))
batch_op.drop_index(batch_op.f('ix_bpmn_process_definition_relationship_bpmn_process_definition_parent_id'))
op.drop_table('bpmn_process_definition_relationship')
op.drop_index(op.f('ix_bpmn_process_top_level_process_id'), table_name='bpmn_process')
op.drop_index(op.f('ix_bpmn_process_json_data_hash'), table_name='bpmn_process')
op.drop_index(op.f('ix_bpmn_process_direct_parent_process_id'), table_name='bpmn_process')
op.drop_index(op.f('ix_bpmn_process_bpmn_process_definition_id'), table_name='bpmn_process')
with op.batch_alter_table('bpmn_process', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_bpmn_process_top_level_process_id'))
batch_op.drop_index(batch_op.f('ix_bpmn_process_json_data_hash'))
batch_op.drop_index(batch_op.f('ix_bpmn_process_direct_parent_process_id'))
batch_op.drop_index(batch_op.f('ix_bpmn_process_bpmn_process_definition_id'))
op.drop_table('bpmn_process')
op.drop_index(op.f('ix_user_service_id'), table_name='user')
op.drop_index(op.f('ix_user_service'), table_name='user')
op.drop_index(op.f('ix_user_email'), table_name='user')
with op.batch_alter_table('user', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_user_service_id'))
batch_op.drop_index(batch_op.f('ix_user_service'))
batch_op.drop_index(batch_op.f('ix_user_email'))
op.drop_table('user')
op.drop_index(op.f('ix_spec_reference_cache_type'), table_name='spec_reference_cache')
op.drop_index(op.f('ix_spec_reference_cache_process_model_id'), 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')
with op.batch_alter_table('spec_reference_cache', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_spec_reference_cache_type'))
batch_op.drop_index(batch_op.f('ix_spec_reference_cache_process_model_id'))
batch_op.drop_index(batch_op.f('ix_spec_reference_cache_identifier'))
batch_op.drop_index(batch_op.f('ix_spec_reference_cache_display_name'))
op.drop_table('spec_reference_cache')
op.drop_table('permission_target')
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_message_name'), table_name='message_triggerable_process_model')
with op.batch_alter_table('message_triggerable_process_model', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_message_triggerable_process_model_process_model_identifier'))
batch_op.drop_index(batch_op.f('ix_message_triggerable_process_model_message_name'))
op.drop_table('message_triggerable_process_model')
op.drop_table('json_data')
op.drop_index(op.f('ix_group_name'), table_name='group')
op.drop_index(op.f('ix_group_identifier'), table_name='group')
with op.batch_alter_table('group', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_group_name'))
batch_op.drop_index(batch_op.f('ix_group_identifier'))
op.drop_table('group')
op.drop_index(op.f('ix_correlation_property_cache_name'), table_name='correlation_property_cache')
op.drop_index(op.f('ix_correlation_property_cache_message_name'), table_name='correlation_property_cache')
with op.batch_alter_table('correlation_property_cache', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_correlation_property_cache_name'))
batch_op.drop_index(batch_op.f('ix_correlation_property_cache_message_name'))
op.drop_table('correlation_property_cache')
op.drop_index(op.f('ix_bpmn_process_definition_bpmn_name'), table_name='bpmn_process_definition')
op.drop_index(op.f('ix_bpmn_process_definition_bpmn_identifier'), table_name='bpmn_process_definition')
with op.batch_alter_table('bpmn_process_definition', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_bpmn_process_definition_bpmn_name'))
batch_op.drop_index(batch_op.f('ix_bpmn_process_definition_bpmn_identifier'))
op.drop_table('bpmn_process_definition')
# ### end Alembic commands ###

File diff suppressed because it is too large Load Diff

View File

@ -86,6 +86,7 @@ prometheus-flask-exporter = "^0.22.3"
safety = "^2.3.5"
sqlalchemy = "^2.0.7"
marshmallow-sqlalchemy = "^0.29.0"
spiff-element-units = "^0.1.0"
[tool.poetry.dev-dependencies]
pytest = "^7.1.2"

View File

@ -145,3 +145,11 @@ SPIFFWORKFLOW_BACKEND_ENGINE_STEP_DEFAULT_STRATEGY_WEB = environ.get(
# this is only used in CI. use SPIFFWORKFLOW_BACKEND_DATABASE_URI instead for real configuration
SPIFFWORKFLOW_BACKEND_DATABASE_PASSWORD = environ.get("SPIFFWORKFLOW_BACKEND_DATABASE_PASSWORD", default=None)
SPIFFWORKFLOW_BACKEND_FEATURE_ELEMENT_UNITS_ENABLED = (
environ.get("SPIFFWORKFLOW_BACKEND_FEATURE_ELEMENT_UNITS_ENABLED", default="false") == "true"
)
SPIFFWORKFLOW_BACKEND_ELEMENT_UNITS_CACHE_DIR = environ.get(
"SPIFFWORKFLOW_BACKEND_ELEMENT_UNITS_CACHE_DIR", default=None
)

View File

@ -2,6 +2,8 @@ from __future__ import annotations
from dataclasses import dataclass
from sqlalchemy import UniqueConstraint
from spiffworkflow_backend.models.db import db
from spiffworkflow_backend.models.db import SpiffworkflowBaseDBModel
@ -15,13 +17,25 @@ from spiffworkflow_backend.models.db import SpiffworkflowBaseDBModel
@dataclass
class BpmnProcessDefinitionModel(SpiffworkflowBaseDBModel):
__tablename__ = "bpmn_process_definition"
__table_args__ = (
UniqueConstraint(
"full_process_model_hash",
"single_process_hash",
name="process_hash_unique",
),
)
id: int = db.Column(db.Integer, primary_key=True)
# this is a sha256 hash of spec and serializer_version
# note that a call activity is its own row in this table, with its own hash,
# and therefore it only gets stored once per version, and can be reused
# by multiple calling processes.
hash: str = db.Column(db.String(255), nullable=False, unique=True)
single_process_hash: str = db.Column(db.String(255), nullable=False)
# only the top level parent will have this set
# it includes all subprocesses and call activities
full_process_model_hash: str | None = db.Column(db.String(255), nullable=True, unique=True)
bpmn_identifier: str = db.Column(db.String(255), nullable=False, index=True)
bpmn_name: str = db.Column(db.String(255), nullable=True, index=True)

View File

@ -634,19 +634,29 @@ def process_instance_task_list(
task_models = task_model_query.all()
if most_recent_tasks_only:
most_recent_tasks = {}
most_recent_subprocesses = set()
# if you have a loop and there is a subprocess, and you are going around for the second time,
# ignore the tasks in the "first loop" subprocess
relevant_subprocess_guids = {bpmn_process_guid, None}
bpmn_process_cache: dict[str, list[str]] = {}
for task_model in task_models:
bpmn_process_guid = task_model.bpmn_process_guid or "TOP"
row_key = f"{bpmn_process_guid}:::{task_model.bpmn_identifier}"
if task_model.bpmn_process_guid not in bpmn_process_cache:
bpmn_process = BpmnProcessModel.query.filter_by(guid=task_model.bpmn_process_guid).first()
full_bpmn_process_path = TaskService.full_bpmn_process_path(bpmn_process)
bpmn_process_cache[task_model.bpmn_process_guid] = full_bpmn_process_path
else:
full_bpmn_process_path = bpmn_process_cache[task_model.bpmn_process_guid]
row_key = f"{':::'.join(full_bpmn_process_path)}:::{task_model.bpmn_identifier}"
if row_key not in most_recent_tasks:
most_recent_tasks[row_key] = task_model
if task_model.typename in ["SubWorkflowTask", "CallActivity"]:
most_recent_subprocesses.add(task_model.guid)
relevant_subprocess_guids.add(task_model.guid)
task_models = [
task_model
for task_model in most_recent_tasks.values()
if task_model.bpmn_process_guid in most_recent_subprocesses or task_model.bpmn_process_guid is None
if task_model.bpmn_process_guid in relevant_subprocess_guids
]
if to_task_model is not None:

View File

@ -0,0 +1,57 @@
import json
from typing import Any
from typing import Dict
from typing import Optional
from flask import current_app
BpmnSpecDict = Dict[str, Any]
class ElementUnitsService:
"""Feature gated glue between the backend and spiff-element-units."""
@classmethod
def _cache_dir(cls) -> Optional[str]:
return current_app.config["SPIFFWORKFLOW_BACKEND_ELEMENT_UNITS_CACHE_DIR"] # type: ignore
@classmethod
def _enabled(cls) -> bool:
enabled = current_app.config["SPIFFWORKFLOW_BACKEND_FEATURE_ELEMENT_UNITS_ENABLED"]
return enabled and cls._cache_dir() is not None
@classmethod
def cache_element_units_for_workflow(cls, cache_key: str, bpmn_spec_dict: BpmnSpecDict) -> None:
if not cls._enabled():
return None
try:
# for now we are importing inside each of these functions, not sure the best
# way to do this in an overall feature flagged strategy but this gets things
# moving
import spiff_element_units # type: ignore
bpmn_spec_json = json.dumps(bpmn_spec_dict)
spiff_element_units.cache_element_units_for_workflow(cls._cache_dir(), cache_key, bpmn_spec_json)
except Exception as e:
current_app.logger.exception(e)
return None
@classmethod
def workflow_from_cached_element_unit(cls, cache_key: str, element_id: str) -> Optional[BpmnSpecDict]:
if not cls._enabled():
return None
try:
# for now we are importing inside each of these functions, not sure the best
# way to do this in an overall feature flagged strategy but this gets things
# moving
import spiff_element_units
bpmn_spec_json = spiff_element_units.workflow_from_cached_element_unit(
cls._cache_dir(), cache_key, element_id
)
return json.loads(bpmn_spec_json) # type: ignore
except Exception as e:
current_app.logger.exception(e)
return None

View File

@ -91,6 +91,9 @@ from spiffworkflow_backend.models.task_definition import TaskDefinitionModel
from spiffworkflow_backend.models.user import UserModel
from spiffworkflow_backend.scripts.script import Script
from spiffworkflow_backend.services.custom_parser import MyCustomParser
from spiffworkflow_backend.services.element_units_service import (
ElementUnitsService,
)
from spiffworkflow_backend.services.file_system_service import FileSystemService
from spiffworkflow_backend.services.process_instance_queue_service import ProcessInstanceQueueService
from spiffworkflow_backend.services.process_model_service import ProcessModelService
@ -673,6 +676,25 @@ class ProcessInstanceProcessor:
bpmn_definition_to_task_definitions_mappings,
)
#
# see if we have any cached element units and if so step on the spec and subprocess_specs.
# in the early stages of development this will return the full workflow when the feature
# flag is set to on. as time goes we will need to think about how this plays in with the
# bpmn definition tables more.
#
element_unit_process_dict = None
full_process_model_hash = bpmn_process_definition.full_process_model_hash
if full_process_model_hash is not None:
element_unit_process_dict = ElementUnitsService.workflow_from_cached_element_unit(
full_process_model_hash,
bpmn_process_definition.bpmn_identifier,
)
if element_unit_process_dict is not None:
spiff_bpmn_process_dict["spec"] = element_unit_process_dict["spec"]
spiff_bpmn_process_dict["subprocess_specs"] = element_unit_process_dict["subprocess_specs"]
bpmn_process = process_instance_model.bpmn_process
if bpmn_process is not None:
single_bpmn_process_dict = cls._get_bpmn_process_dict(bpmn_process, get_tasks=True)
@ -959,18 +981,31 @@ class ProcessInstanceProcessor:
process_bpmn_properties: dict,
bpmn_process_definition_parent: Optional[BpmnProcessDefinitionModel] = None,
store_bpmn_definition_mappings: bool = False,
full_bpmn_spec_dict: Optional[dict] = None,
) -> BpmnProcessDefinitionModel:
process_bpmn_identifier = process_bpmn_properties["name"]
process_bpmn_name = process_bpmn_properties["description"]
new_hash_digest = sha256(json.dumps(process_bpmn_properties, sort_keys=True).encode("utf8")).hexdigest()
bpmn_process_definition: Optional[BpmnProcessDefinitionModel] = BpmnProcessDefinitionModel.query.filter_by(
hash=new_hash_digest
).first()
bpmn_process_definition: Optional[BpmnProcessDefinitionModel] = None
single_process_hash = sha256(json.dumps(process_bpmn_properties, sort_keys=True).encode("utf8")).hexdigest()
full_process_model_hash = None
if full_bpmn_spec_dict is not None:
full_process_model_hash = sha256(
json.dumps(full_bpmn_spec_dict, sort_keys=True).encode("utf8")
).hexdigest()
bpmn_process_definition = BpmnProcessDefinitionModel.query.filter_by(
full_process_model_hash=full_process_model_hash
).first()
else:
bpmn_process_definition = BpmnProcessDefinitionModel.query.filter_by(
single_process_hash=single_process_hash
).first()
if bpmn_process_definition is None:
task_specs = process_bpmn_properties.pop("task_specs")
bpmn_process_definition = BpmnProcessDefinitionModel(
hash=new_hash_digest,
single_process_hash=single_process_hash,
full_process_model_hash=full_process_model_hash,
bpmn_identifier=process_bpmn_identifier,
bpmn_name=process_bpmn_name,
properties_json=process_bpmn_properties,
@ -1050,6 +1085,7 @@ class ProcessInstanceProcessor:
bpmn_process_definition_parent = self._store_bpmn_process_definition(
bpmn_spec_dict["spec"],
store_bpmn_definition_mappings=store_bpmn_definition_mappings,
full_bpmn_spec_dict=bpmn_spec_dict,
)
for process_bpmn_properties in bpmn_spec_dict["subprocess_specs"].values():
self._store_bpmn_process_definition(
@ -1059,6 +1095,26 @@ class ProcessInstanceProcessor:
)
self.process_instance_model.bpmn_process_definition = bpmn_process_definition_parent
#
# builds and caches the element units for the parent bpmn process defintion. these
# element units can then be queried using the same hash for later execution.
#
# TODO: this seems to be run each time a process instance is started, so element
# units will only be queried after a save/resume point. the hash used as the key
# can be anything, so possibly some hash of all files required to form the process
# definition and their hashes could be used? Not sure how that plays in with the
# bpmn_process_defintion hash though.
#
# TODO: first time through for an instance the bpmn_spec_dict seems to get mutated,
# so for now we don't seed the cache until the second instance. not immediately a
# problem and can be part of the larger discussion mentioned in the TODO above.
full_process_model_hash = bpmn_process_definition_parent.full_process_model_hash
if full_process_model_hash is not None and "task_specs" in bpmn_spec_dict["spec"]:
ElementUnitsService.cache_element_units_for_workflow(full_process_model_hash, bpmn_spec_dict)
def save(self) -> None:
"""Saves the current state of this processor to the database."""
self.process_instance_model.spiff_serializer_version = self.SERIALIZER_VERSION

View File

@ -622,7 +622,13 @@ class ProcessInstanceReportService:
group_model_join_conditions = [GroupModel.id == HumanTaskModel.lane_assignment_id]
if report_filter.user_group_identifier:
group_model_join_conditions.append(GroupModel.identifier == report_filter.user_group_identifier)
process_instance_query = process_instance_query.join(HumanTaskModel)
if report_filter.has_active_status:
process_instance_query = process_instance_query.filter(
HumanTaskModel.completed.is_(False) # type: ignore
)
process_instance_query = process_instance_query.join(GroupModel, and_(*group_model_join_conditions))
process_instance_query = process_instance_query.join(
UserGroupAssignmentModel,

View File

@ -511,6 +511,17 @@ class TaskService:
return (bpmn_processes + b, [parent_task_model] + t)
return (bpmn_processes, task_models)
@classmethod
def full_bpmn_process_path(cls, bpmn_process: BpmnProcessModel) -> list[str]:
"""Returns a list of bpmn process identifiers pointing the given bpmn_process."""
bpmn_process_identifiers: list[str] = [bpmn_process.bpmn_process_definition.bpmn_identifier]
if bpmn_process.direct_parent_process_id is not None:
parent_bpmn_process = BpmnProcessModel.query.filter_by(id=bpmn_process.direct_parent_process_id).first()
if parent_bpmn_process is not None:
# always prepend new identifiers since they come first in the path
bpmn_process_identifiers = cls.full_bpmn_process_path(parent_bpmn_process) + bpmn_process_identifiers
return bpmn_process_identifiers
@classmethod
def reset_task_model_dict(
cls,

View File

@ -0,0 +1,116 @@
{
"serializer_version": "spiff-element-units-integration",
"spec": {
"correlation_keys": {},
"data_objects": {},
"description": "No Tasks",
"file": "tests/data/process-models/test-cases/no-tasks/no-tasks.bpmn",
"io_specification": null,
"name": "no_tasks",
"task_specs": {
"End": {
"description": "",
"id": "no_tasks_3",
"inputs": [
"no_tasks.EndJoin"
],
"internal": false,
"lookahead": 2,
"manual": false,
"name": "End",
"outputs": [],
"typename": "Simple"
},
"Event_0qq9il3": {
"data_input_associations": [],
"data_output_associations": [],
"description": null,
"documentation": null,
"event_definition": {
"external": false,
"internal": false,
"typename": "NoneEventDefinition"
},
"extensions": {},
"id": "no_tasks_5",
"inputs": [
"StartEvent_1"
],
"internal": false,
"io_specification": null,
"lane": null,
"lookahead": 2,
"manual": false,
"name": "Event_0qq9il3",
"outputs": [
"no_tasks.EndJoin"
],
"position": {
"x": 272.0,
"y": 159.0
},
"typename": "EndEvent"
},
"Start": {
"description": "",
"id": "no_tasks_1",
"inputs": [],
"internal": false,
"lookahead": 2,
"manual": false,
"name": "Start",
"outputs": [
"StartEvent_1"
],
"typename": "StartTask"
},
"StartEvent_1": {
"data_input_associations": [],
"data_output_associations": [],
"description": null,
"documentation": null,
"event_definition": {
"external": false,
"internal": false,
"typename": "NoneEventDefinition"
},
"extensions": {},
"id": "no_tasks_4",
"inputs": [
"Start"
],
"internal": false,
"io_specification": null,
"lane": null,
"lookahead": 2,
"manual": false,
"name": "StartEvent_1",
"outputs": [
"Event_0qq9il3"
],
"position": {
"x": 179.0,
"y": 159.0
},
"typename": "StartEvent"
},
"no_tasks.EndJoin": {
"description": "",
"id": "no_tasks_2",
"inputs": [
"Event_0qq9il3"
],
"internal": false,
"lookahead": 2,
"manual": false,
"name": "no_tasks.EndJoin",
"outputs": [
"End"
],
"typename": "_EndJoin"
}
},
"typename": "BpmnProcessSpec"
},
"subprocess_specs": {}
}

View File

@ -0,0 +1,133 @@
import json
import os
import tempfile
from typing import Generator
import pytest
from flask.app import Flask
from tests.spiffworkflow_backend.helpers.base_test import BaseTest
from spiffworkflow_backend.services.element_units_service import BpmnSpecDict
from spiffworkflow_backend.services.element_units_service import ElementUnitsService
#
# we don't want to fully flex every aspect of the spiff-element-units
# library here, mainly just checking that our interaction with it is
# as expected.
#
@pytest.fixture()
def app_no_cache_dir(app: Flask) -> Generator[Flask, None, None]:
app.config["SPIFFWORKFLOW_BACKEND_ELEMENT_UNITS_CACHE_DIR"] = None
yield app
@pytest.fixture()
def app_some_cache_dir(app: Flask) -> Generator[Flask, None, None]:
app.config["SPIFFWORKFLOW_BACKEND_ELEMENT_UNITS_CACHE_DIR"] = "some_cache_dir"
yield app
@pytest.fixture()
def app_disabled(app: Flask) -> Generator[Flask, None, None]:
app.config["SPIFFWORKFLOW_BACKEND_FEATURE_ELEMENT_UNITS_ENABLED"] = False
yield app
@pytest.fixture()
def app_enabled(app_some_cache_dir: Flask) -> Generator[Flask, None, None]:
app_some_cache_dir.config["SPIFFWORKFLOW_BACKEND_FEATURE_ELEMENT_UNITS_ENABLED"] = True
yield app_some_cache_dir
@pytest.fixture()
def app_enabled_tmp_cache_dir(app_enabled: Flask) -> Generator[Flask, None, None]:
with tempfile.TemporaryDirectory() as tmpdirname:
app_enabled.config["SPIFFWORKFLOW_BACKEND_ELEMENT_UNITS_CACHE_DIR"] = tmpdirname
yield app_enabled
@pytest.fixture()
def example_specs_dict(app: Flask) -> Generator[BpmnSpecDict, None, None]:
path = os.path.join(app.instance_path, "..", "..", "tests", "data", "specs-json", "no-tasks.json")
with open(path) as f:
yield json.loads(f.read())
class TestElementUnitsService(BaseTest):
"""Tests the ElementUnitsService."""
def test_cache_dir_env_is_respected(
self,
app_some_cache_dir: Flask,
) -> None:
assert ElementUnitsService._cache_dir() == "some_cache_dir"
def test_feature_disabled_if_env_is_false(
self,
app_disabled: Flask,
) -> None:
assert not ElementUnitsService._enabled()
def test_feature_enabled_if_env_is_true(
self,
app_enabled: Flask,
) -> None:
assert ElementUnitsService._enabled()
def test_is_disabled_when_no_cache_dir(
self,
app_no_cache_dir: Flask,
) -> None:
assert not ElementUnitsService._enabled()
def test_ok_to_cache_when_disabled(
self,
app_disabled: Flask,
) -> None:
result = ElementUnitsService.cache_element_units_for_workflow("", {})
assert result is None
def test_ok_to_read_workflow_from_cached_element_unit_when_disabled(
self,
app_disabled: Flask,
) -> None:
result = ElementUnitsService.workflow_from_cached_element_unit("", "")
assert result is None
def test_can_write_to_cache(
self,
app_enabled_tmp_cache_dir: Flask,
example_specs_dict: BpmnSpecDict,
) -> None:
result = ElementUnitsService.cache_element_units_for_workflow("testing", example_specs_dict)
assert result is None
def test_can_write_to_cache_multiple_times(
self,
app_enabled_tmp_cache_dir: Flask,
example_specs_dict: BpmnSpecDict,
) -> None:
result = ElementUnitsService.cache_element_units_for_workflow("testing", example_specs_dict)
assert result is None
result = ElementUnitsService.cache_element_units_for_workflow("testing", example_specs_dict)
assert result is None
result = ElementUnitsService.cache_element_units_for_workflow("testing", example_specs_dict)
assert result is None
def test_can_read_element_unit_for_process_from_cache(
self,
app_enabled_tmp_cache_dir: Flask,
example_specs_dict: BpmnSpecDict,
) -> None:
ElementUnitsService.cache_element_units_for_workflow("testing", example_specs_dict)
cached_specs_dict = ElementUnitsService.workflow_from_cached_element_unit("testing", "no_tasks")
assert cached_specs_dict == example_specs_dict
def test_reading_element_unit_for_uncached_process_returns_none(
self,
app_enabled_tmp_cache_dir: Flask,
) -> None:
cached_specs_dict = ElementUnitsService.workflow_from_cached_element_unit("testing", "no_tasks")
assert cached_specs_dict is None