Merge branch 'main' into move-group-or-model

This commit is contained in:
mike cullerton 2022-11-21 15:56:44 -05:00
commit e36bf1b548
29 changed files with 805 additions and 715 deletions

View File

@ -16,7 +16,10 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
include: include:
- { python: "3.11", os: "ubuntu-latest", session: "safety" } # FIXME: https://github.com/mysql/mysql-connector-python/pull/86
# put back when poetry update protobuf mysql-connector-python updates protobuf
# right now mysql is forcing protobuf to version 3
# - { python: "3.11", os: "ubuntu-latest", session: "safety" }
- { python: "3.11", os: "ubuntu-latest", session: "mypy" } - { python: "3.11", os: "ubuntu-latest", session: "mypy" }
- { python: "3.10", os: "ubuntu-latest", session: "mypy" } - { python: "3.10", os: "ubuntu-latest", session: "mypy" }
- { python: "3.9", os: "ubuntu-latest", session: "mypy" } - { python: "3.9", os: "ubuntu-latest", session: "mypy" }

View File

@ -1,9 +1,10 @@
import logging import logging
from logging.config import fileConfig from logging.config import fileConfig
from alembic import context
from flask import current_app from flask import current_app
from alembic import context
# this is the Alembic Config object, which provides # this is the Alembic Config object, which provides
# access to the values within the .ini file in use. # access to the values within the .ini file in use.
config = context.config config = context.config
@ -11,17 +12,17 @@ config = context.config
# Interpret the config file for Python logging. # Interpret the config file for Python logging.
# This line sets up loggers basically. # This line sets up loggers basically.
fileConfig(config.config_file_name) fileConfig(config.config_file_name)
logger = logging.getLogger("alembic.env") logger = logging.getLogger('alembic.env')
# add your model's MetaData object here # add your model's MetaData object here
# for 'autogenerate' support # for 'autogenerate' support
# from myapp import mymodel # from myapp import mymodel
# target_metadata = mymodel.Base.metadata # target_metadata = mymodel.Base.metadata
config.set_main_option( config.set_main_option(
"sqlalchemy.url", 'sqlalchemy.url',
str(current_app.extensions["migrate"].db.get_engine().url).replace("%", "%%"), str(current_app.extensions['migrate'].db.get_engine().url).replace(
) '%', '%%'))
target_metadata = current_app.extensions["migrate"].db.metadata target_metadata = current_app.extensions['migrate'].db.metadata
# other values from the config, defined by the needs of env.py, # other values from the config, defined by the needs of env.py,
# can be acquired: # can be acquired:
@ -42,7 +43,9 @@ def run_migrations_offline():
""" """
url = config.get_main_option("sqlalchemy.url") 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(): with context.begin_transaction():
context.run_migrations() context.run_migrations()
@ -60,20 +63,20 @@ def run_migrations_online():
# when there are no changes to the schema # when there are no changes to the schema
# reference: http://alembic.zzzcomputing.com/en/latest/cookbook.html # reference: http://alembic.zzzcomputing.com/en/latest/cookbook.html
def process_revision_directives(context, revision, directives): def process_revision_directives(context, revision, directives):
if getattr(config.cmd_opts, "autogenerate", False): if getattr(config.cmd_opts, 'autogenerate', False):
script = directives[0] script = directives[0]
if script.upgrade_ops.is_empty(): if script.upgrade_ops.is_empty():
directives[:] = [] 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: with connectable.connect() as connection:
context.configure( context.configure(
connection=connection, connection=connection,
target_metadata=target_metadata, target_metadata=target_metadata,
process_revision_directives=process_revision_directives, process_revision_directives=process_revision_directives,
**current_app.extensions["migrate"].configure_args **current_app.extensions['migrate'].configure_args
) )
with context.begin_transaction(): with context.begin_transaction():

View File

@ -0,0 +1,322 @@
"""empty message
Revision ID: 70223f5c7b98
Revises:
Create Date: 2022-11-20 19:54:45.061376
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '70223f5c7b98'
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('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),
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'),
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_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('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('spiff_step_details',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('process_instance_id', sa.Integer(), nullable=False),
sa.Column('spiff_step', sa.Integer(), nullable=False),
sa.Column('task_json', sa.JSON(), nullable=False),
sa.Column('timestamp', sa.DECIMAL(precision=17, scale=6), nullable=False),
sa.Column('completed_by_user_id', sa.Integer(), nullable=True),
sa.Column('lane_assignment_id', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['lane_assignment_id'], ['group.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_table('user_group_assignment',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('user_id', sa.Integer(), nullable=False),
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('spiff_step_details')
op.drop_table('secret')
op.drop_table('refresh_token')
op.drop_index(op.f('ix_process_instance_report_identifier'), table_name='process_instance_report')
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_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 ###

View File

@ -1,579 +0,0 @@
"""empty message
Revision ID: b7790c9c8174
Revises:
Create Date: 2022-11-15 14:11:47.309399
"""
import sqlalchemy as sa
from alembic import op
# revision identifiers, used by Alembic.
revision = "b7790c9c8174"
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("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),
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"),
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_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(
"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(
"spiff_step_details",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("process_instance_id", sa.Integer(), nullable=False),
sa.Column("spiff_step", sa.Integer(), nullable=False),
sa.Column("task_json", sa.JSON(), nullable=False),
sa.Column("timestamp", sa.DECIMAL(precision=17, scale=6), nullable=False),
sa.Column("completed_by_user_id", sa.Integer(), nullable=True),
sa.Column("lane_assignment_id", sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(
["lane_assignment_id"],
["group.id"],
),
sa.PrimaryKeyConstraint("id"),
)
op.create_table(
"user_group_assignment",
sa.Column("id", sa.Integer(), nullable=False),
sa.Column("user_id", sa.Integer(), nullable=False),
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("spiff_step_details")
op.drop_table("secret")
op.drop_table("refresh_token")
op.drop_index(
op.f("ix_process_instance_report_identifier"),
table_name="process_instance_report",
)
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_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 ###

View File

@ -477,6 +477,17 @@ six = ">=1.9.0"
gmpy = ["gmpy"] gmpy = ["gmpy"]
gmpy2 = ["gmpy2"] gmpy2 = ["gmpy2"]
[[package]]
name = "exceptiongroup"
version = "1.0.4"
description = "Backport of PEP 654 (exception groups)"
category = "main"
optional = false
python-versions = ">=3.7"
[package.extras]
test = ["pytest (>=6)"]
[[package]] [[package]]
name = "filelock" name = "filelock"
version = "3.8.0" version = "3.8.0"
@ -1240,14 +1251,6 @@ category = "main"
optional = false optional = false
python-versions = ">=3.6" python-versions = ">=3.6"
[[package]]
name = "py"
version = "1.11.0"
description = "library with cross-python path, ini-parsing, io, code, log facilities"
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[[package]] [[package]]
name = "pyasn1" name = "pyasn1"
version = "0.4.8" version = "0.4.8"
@ -1332,7 +1335,7 @@ python-versions = ">=3.7"
[[package]] [[package]]
name = "pytest" name = "pytest"
version = "7.1.3" version = "7.2.0"
description = "pytest: simple powerful testing with Python" description = "pytest: simple powerful testing with Python"
category = "main" category = "main"
optional = false optional = false
@ -1341,11 +1344,11 @@ python-versions = ">=3.7"
[package.dependencies] [package.dependencies]
attrs = ">=19.2.0" attrs = ">=19.2.0"
colorama = {version = "*", markers = "sys_platform == \"win32\""} colorama = {version = "*", markers = "sys_platform == \"win32\""}
exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""}
iniconfig = "*" iniconfig = "*"
packaging = "*" packaging = "*"
pluggy = ">=0.12,<2.0" pluggy = ">=0.12,<2.0"
py = ">=1.8.2" tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""}
tomli = ">=1.0.0"
[package.extras] [package.extras]
testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"]
@ -1876,7 +1879,7 @@ lxml = "*"
type = "git" type = "git"
url = "https://github.com/sartography/SpiffWorkflow" url = "https://github.com/sartography/SpiffWorkflow"
reference = "main" reference = "main"
resolved_reference = "eea53c912984d21a064330c3b3334ac219cb8e18" resolved_reference = "46f410a2852baeedc8f9ac5165347ce6d4470594"
[[package]] [[package]]
name = "SQLAlchemy" name = "SQLAlchemy"
@ -2259,7 +2262,7 @@ testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools"
[metadata] [metadata]
lock-version = "1.1" lock-version = "1.1"
python-versions = ">=3.9,<3.12" python-versions = ">=3.9,<3.12"
content-hash = "a6d3882a3ab142b82201b83ee8a0552fd16112c4540e2a1dbcb5c38599b917c1" content-hash = "e171edf387c3a4569b52e14acff436a3448f93ffe0977d3491d77822e168bd8d"
[metadata.files] [metadata.files]
alabaster = [ alabaster = [
@ -2484,6 +2487,10 @@ ecdsa = [
{file = "ecdsa-0.18.0-py2.py3-none-any.whl", hash = "sha256:80600258e7ed2f16b9aa1d7c295bd70194109ad5a30fdee0eaeefef1d4c559dd"}, {file = "ecdsa-0.18.0-py2.py3-none-any.whl", hash = "sha256:80600258e7ed2f16b9aa1d7c295bd70194109ad5a30fdee0eaeefef1d4c559dd"},
{file = "ecdsa-0.18.0.tar.gz", hash = "sha256:190348041559e21b22a1d65cee485282ca11a6f81d503fddb84d5017e9ed1e49"}, {file = "ecdsa-0.18.0.tar.gz", hash = "sha256:190348041559e21b22a1d65cee485282ca11a6f81d503fddb84d5017e9ed1e49"},
] ]
exceptiongroup = [
{file = "exceptiongroup-1.0.4-py3-none-any.whl", hash = "sha256:542adf9dea4055530d6e1279602fa5cb11dab2395fa650b8674eaec35fc4a828"},
{file = "exceptiongroup-1.0.4.tar.gz", hash = "sha256:bd14967b79cd9bdb54d97323216f8fdf533e278df937aa2a90089e7d6e06e5ec"},
]
filelock = [ filelock = [
{file = "filelock-3.8.0-py3-none-any.whl", hash = "sha256:617eb4e5eedc82fc5f47b6d61e4d11cb837c56cb4544e39081099fa17ad109d4"}, {file = "filelock-3.8.0-py3-none-any.whl", hash = "sha256:617eb4e5eedc82fc5f47b6d61e4d11cb837c56cb4544e39081099fa17ad109d4"},
{file = "filelock-3.8.0.tar.gz", hash = "sha256:55447caa666f2198c5b6b13a26d2084d26fa5b115c00d065664b2124680c4edc"}, {file = "filelock-3.8.0.tar.gz", hash = "sha256:55447caa666f2198c5b6b13a26d2084d26fa5b115c00d065664b2124680c4edc"},
@ -3051,10 +3058,6 @@ psycopg2 = [
{file = "psycopg2-2.9.4-cp39-cp39-win_amd64.whl", hash = "sha256:849bd868ae3369932127f0771c08d1109b254f08d48dc42493c3d1b87cb2d308"}, {file = "psycopg2-2.9.4-cp39-cp39-win_amd64.whl", hash = "sha256:849bd868ae3369932127f0771c08d1109b254f08d48dc42493c3d1b87cb2d308"},
{file = "psycopg2-2.9.4.tar.gz", hash = "sha256:d529926254e093a1b669f692a3aa50069bc71faf5b0ecd91686a78f62767d52f"}, {file = "psycopg2-2.9.4.tar.gz", hash = "sha256:d529926254e093a1b669f692a3aa50069bc71faf5b0ecd91686a78f62767d52f"},
] ]
py = [
{file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"},
{file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"},
]
pyasn1 = [ pyasn1 = [
{file = "pyasn1-0.4.8-py2.4.egg", hash = "sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3"}, {file = "pyasn1-0.4.8-py2.4.egg", hash = "sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3"},
{file = "pyasn1-0.4.8-py2.5.egg", hash = "sha256:0458773cfe65b153891ac249bcf1b5f8f320b7c2ce462151f8fa74de8934becf"}, {file = "pyasn1-0.4.8-py2.5.egg", hash = "sha256:0458773cfe65b153891ac249bcf1b5f8f320b7c2ce462151f8fa74de8934becf"},
@ -3118,8 +3121,8 @@ pyrsistent = [
{file = "pyrsistent-0.18.1.tar.gz", hash = "sha256:d4d61f8b993a7255ba714df3aca52700f8125289f84f704cf80916517c46eb96"}, {file = "pyrsistent-0.18.1.tar.gz", hash = "sha256:d4d61f8b993a7255ba714df3aca52700f8125289f84f704cf80916517c46eb96"},
] ]
pytest = [ pytest = [
{file = "pytest-7.1.3-py3-none-any.whl", hash = "sha256:1377bda3466d70b55e3f5cecfa55bb7cfcf219c7964629b967c37cf0bda818b7"}, {file = "pytest-7.2.0-py3-none-any.whl", hash = "sha256:892f933d339f068883b6fd5a459f03d85bfcb355e4981e146d2c7616c21fef71"},
{file = "pytest-7.1.3.tar.gz", hash = "sha256:4f365fec2dff9c1162f834d9f18af1ba13062db0c708bf7b946f8a5c76180c39"}, {file = "pytest-7.2.0.tar.gz", hash = "sha256:c4014eb40e10f11f355ad4e3c2fb2c6c6d1919c73f3b5a433de4708202cade59"},
] ]
pytest-flask = [ pytest-flask = [
{file = "pytest-flask-1.2.0.tar.gz", hash = "sha256:46fde652f77777bf02dc91205aec4ce20cdf2acbbbd66a918ab91f5c14693d3d"}, {file = "pytest-flask-1.2.0.tar.gz", hash = "sha256:46fde652f77777bf02dc91205aec4ce20cdf2acbbbd66a918ab91f5c14693d3d"},

View File

@ -33,7 +33,7 @@ sentry-sdk = "^1.10"
sphinx-autoapi = "^2.0" sphinx-autoapi = "^2.0"
flask-bpmn = {git = "https://github.com/sartography/flask-bpmn", rev = "main"} flask-bpmn = {git = "https://github.com/sartography/flask-bpmn", rev = "main"}
# flask-bpmn = {develop = true, path = "../flask-bpmn"} # flask-bpmn = {develop = true, path = "../flask-bpmn"}
mysql-connector-python = "^8.0.29" mysql-connector-python = "*"
pytest-flask = "^1.2.0" pytest-flask = "^1.2.0"
pytest-flask-sqlalchemy = "^1.1.0" pytest-flask-sqlalchemy = "^1.1.0"
psycopg2 = "^2.9.3" psycopg2 = "^2.9.3"
@ -75,7 +75,7 @@ types-dateparser = "^1.1.4.1"
[tool.poetry.dev-dependencies] [tool.poetry.dev-dependencies]
pytest = "^7.1.2" pytest = "*"
coverage = {extras = ["toml"], version = "^6.1"} coverage = {extras = ["toml"], version = "^6.1"}
safety = "^2.3.1" safety = "^2.3.1"
mypy = ">=0.961" mypy = ">=0.961"
@ -103,7 +103,6 @@ sphinx-click = "^4.3.0"
Pygments = "^2.10.0" Pygments = "^2.10.0"
pyupgrade = "^3.1.0" pyupgrade = "^3.1.0"
furo = ">=2021.11.12" furo = ">=2021.11.12"
MonkeyType = "^22.2.0"
[tool.poetry.scripts] [tool.poetry.scripts]
spiffworkflow-backend = "spiffworkflow_backend.__main__:main" spiffworkflow-backend = "spiffworkflow_backend.__main__:main"

View File

@ -67,9 +67,9 @@ def start_scheduler(
seconds=10, seconds=10,
) )
scheduler.add_job( scheduler.add_job(
BackgroundProcessingService(app).run, BackgroundProcessingService(app).process_waiting_process_instances,
"interval", "interval",
seconds=30, seconds=10,
) )
scheduler.start() scheduler.start()

View File

@ -1,5 +1,4 @@
"""Acceptance_test_fixtures.""" """Acceptance_test_fixtures."""
import json
import time import time
from flask import current_app from flask import current_app
@ -8,13 +7,15 @@ from tests.spiffworkflow_backend.helpers.base_test import BaseTest
from spiffworkflow_backend.models.process_instance import ProcessInstanceModel from spiffworkflow_backend.models.process_instance import ProcessInstanceModel
from spiffworkflow_backend.models.process_instance import ProcessInstanceStatus from spiffworkflow_backend.models.process_instance import ProcessInstanceStatus
from spiffworkflow_backend.services.process_instance_service import (
ProcessInstanceService,
)
def load_acceptance_test_fixtures() -> list[ProcessInstanceModel]: def load_acceptance_test_fixtures() -> list[ProcessInstanceModel]:
"""Load_fixtures.""" """Load_fixtures."""
current_app.logger.debug("load_acceptance_test_fixtures() start") current_app.logger.debug("load_acceptance_test_fixtures() start")
test_process_group_id = "" test_process_model_id = "misc/acceptance-tests-group-one/acceptance-tests-model-1"
test_process_model_id = "acceptance-tests-group-one/acceptance-tests-model-1"
user = BaseTest.find_or_create_user() user = BaseTest.find_or_create_user()
statuses = ProcessInstanceStatus.list() statuses = ProcessInstanceStatus.list()
current_time = round(time.time()) current_time = round(time.time())
@ -28,16 +29,13 @@ def load_acceptance_test_fixtures() -> list[ProcessInstanceModel]:
# suspended - 6 hours ago # suspended - 6 hours ago
process_instances = [] process_instances = []
for i in range(len(statuses)): for i in range(len(statuses)):
process_instance = ProcessInstanceModel(
status=statuses[i], process_instance = ProcessInstanceService.create_process_instance(
process_initiator=user, test_process_model_id, user
process_model_identifier=test_process_model_id,
process_group_identifier=test_process_group_id,
updated_at_in_seconds=round(time.time()),
start_in_seconds=current_time - (3600 * i),
end_in_seconds=current_time - (3600 * i - 20),
bpmn_json=json.dumps({"i": i}),
) )
process_instance.status = statuses[i]
process_instance.start_in_seconds = current_time - (3600 * i)
process_instance.end_in_seconds = current_time - (3600 * i - 20)
db.session.add(process_instance) db.session.add(process_instance)
process_instances.append(process_instance) process_instances.append(process_instance)

View File

@ -14,7 +14,7 @@ class BackgroundProcessingService:
"""__init__.""" """__init__."""
self.app = app self.app = app
def run(self) -> None: def process_waiting_process_instances(self) -> None:
"""Since this runs in a scheduler, we need to specify the app context as well.""" """Since this runs in a scheduler, we need to specify the app context as well."""
with self.app.app_context(): with self.app.app_context():
ProcessInstanceService.do_waiting() ProcessInstanceService.do_waiting()

View File

@ -359,21 +359,8 @@ class ProcessInstanceProcessor:
subprocesses=subprocesses, subprocesses=subprocesses,
) )
self.bpmn_process_instance.script_engine = self._script_engine self.bpmn_process_instance.script_engine = self._script_engine
self.add_user_info_to_process_instance(self.bpmn_process_instance) self.add_user_info_to_process_instance(self.bpmn_process_instance)
if self.PROCESS_INSTANCE_ID_KEY not in self.bpmn_process_instance.data:
if not process_instance_model.id:
db.session.add(process_instance_model)
# If the model is new, and has no id, save it, write it into the process_instance model
# and save it again. In this way, the workflow process is always aware of the
# database model to which it is associated, and scripts running within the model
# can then load data as needed.
self.bpmn_process_instance.data[
ProcessInstanceProcessor.PROCESS_INSTANCE_ID_KEY
] = process_instance_model.id
self.save()
except MissingSpecError as ke: except MissingSpecError as ke:
raise ApiError( raise ApiError(
error_code="unexpected_process_instance_structure", error_code="unexpected_process_instance_structure",

View File

@ -323,14 +323,9 @@ class ProcessInstanceService:
"""Serialize_flat_with_task_data.""" """Serialize_flat_with_task_data."""
results = {} results = {}
try: try:
original_status = process_instance.status
processor = ProcessInstanceProcessor(process_instance) processor = ProcessInstanceProcessor(process_instance)
process_instance.data = processor.get_current_data() process_instance.data = processor.get_current_data()
results = process_instance.serialized_flat results = process_instance.serialized_flat
# this process seems to mutate the status of the process_instance which
# can result in different results than expected from process_instance_list,
# so set the status back to the expected value
results["status"] = original_status
except ApiError: except ApiError:
results = process_instance.serialized results = process_instance.serialized
return results return results

View File

@ -2,6 +2,7 @@
import json import json
import os import os
import shutil import shutil
from glob import glob
from typing import Any from typing import Any
from typing import List from typing import List
from typing import Optional from typing import Optional
@ -181,17 +182,20 @@ class ProcessModelService(FileSystemService):
self, process_group_id: Optional[str] = None self, process_group_id: Optional[str] = None
) -> List[ProcessModelInfo]: ) -> List[ProcessModelInfo]:
"""Get process models.""" """Get process models."""
process_groups = []
if process_group_id is None:
process_groups = self.get_process_groups()
else:
process_group = self.get_process_group(process_group_id)
if process_group is not None:
process_groups.append(process_group)
process_models = [] process_models = []
for process_group in process_groups: root_path = FileSystemService.root_path()
process_models.extend(process_group.process_models) if process_group_id:
awesome_id = process_group_id.replace("/", os.sep)
root_path = os.path.join(root_path, awesome_id)
process_model_glob = os.path.join(root_path, "**", "process_model.json")
for file in glob(process_model_glob, recursive=True):
process_model_relative_path = os.path.relpath(
file, start=FileSystemService.root_path()
)
process_model = self.get_process_model_from_relative_path(
os.path.dirname(process_model_relative_path)
)
process_models.append(process_model)
process_models.sort() process_models.sort()
return process_models return process_models

View File

@ -355,6 +355,41 @@ class TestProcessApi(BaseTest):
assert response.json["primary_process_id"] == "superduper" assert response.json["primary_process_id"] == "superduper"
assert response.json["is_review"] is False assert response.json["is_review"] is False
def test_process_model_list_all(
self,
app: Flask,
client: FlaskClient,
with_db_and_bpmn_file_cleanup: None,
with_super_admin_user: UserModel,
) -> None:
"""Test_process_model_list_all."""
group_id = "test_group/test_sub_group"
self.create_process_group(client, with_super_admin_user, group_id)
# add 5 models to the group
for i in range(5):
process_model_identifier = f"{group_id}/test_model_{i}"
model_display_name = f"Test Model {i}"
model_description = f"Test Model {i} Description"
self.create_process_model_with_api(
client,
process_model_id=process_model_identifier,
process_model_display_name=model_display_name,
process_model_description=model_description,
user=with_super_admin_user,
)
# get all models
response = client.get(
"/v1.0/process-models?per_page=1000",
headers=self.logged_in_headers(with_super_admin_user),
)
assert response.json is not None
assert len(response.json["results"]) == 5
assert response.json["pagination"]["count"] == 5
assert response.json["pagination"]["total"] == 5
assert response.json["pagination"]["pages"] == 1
def test_process_model_list( def test_process_model_list(
self, self,
app: Flask, app: Flask,

View File

@ -37,7 +37,8 @@ describe('process-groups', () => {
cy.contains(groupId).should('not.exist'); cy.contains(groupId).should('not.exist');
}); });
it('can paginate items', () => { // process groups no longer has pagination post-tiles
cy.basicPaginationTest(); // it('can paginate items', () => {
}); // cy.basicPaginationTest();
// });
}); });

View File

@ -3,9 +3,9 @@ import { DATE_FORMAT, PROCESS_STATUSES } from '../../src/config';
const filterByDate = (fromDate) => { const filterByDate = (fromDate) => {
cy.get('#date-picker-start-from').clear().type(format(fromDate, DATE_FORMAT)); cy.get('#date-picker-start-from').clear().type(format(fromDate, DATE_FORMAT));
cy.contains('Start date from').click(); cy.contains('Start date to').click();
cy.get('#date-picker-end-from').clear().type(format(fromDate, DATE_FORMAT)); cy.get('#date-picker-end-from').clear().type(format(fromDate, DATE_FORMAT));
cy.contains('End date from').click(); cy.contains('End date to').click();
cy.getBySel('filter-button').click(); cy.getBySel('filter-button').click();
}; };
@ -53,9 +53,9 @@ const updateBpmnPythonScriptWithMonaco = (
cy.get('.monaco-editor textarea:first') cy.get('.monaco-editor textarea:first')
.click() .click()
.focused() // change subject to currently focused element .focused() // change subject to currently focused element
// .type('{ctrl}a') // had been doing it this way, but it turns out to be flaky relative to clear()
.clear() .clear()
.type(pythonScript, { delay: 30 }); // long delay to ensure cypress isn't competing with monaco auto complete stuff
.type(pythonScript, { delay: 120 });
cy.contains('Close').click(); cy.contains('Close').click();
// wait for a little bit for the xml to get set before saving // wait for a little bit for the xml to get set before saving
@ -119,28 +119,28 @@ describe('process-instances', () => {
cy.runPrimaryBpmnFile(); cy.runPrimaryBpmnFile();
}); });
it('can create a new instance and can modify with monaco text editor', () => { // it('can create a new instance and can modify with monaco text editor', () => {
// leave off the ending double quote since manco adds it // // leave off the ending double quote since manco adds it
const originalPythonScript = 'person = "Kevin'; // const originalPythonScript = 'person = "Kevin';
const newPythonScript = 'person = "Mike'; // const newPythonScript = 'person = "Mike';
//
const bpmnFile = 'process_model_one.bpmn'; // const bpmnFile = 'process_model_one.bpmn';
//
// Change bpmn // // Change bpmn
cy.getBySel('files-accordion').click(); // cy.getBySel('files-accordion').click();
cy.getBySel(`edit-file-${bpmnFile.replace('.', '-')}`).click(); // cy.getBySel(`edit-file-${bpmnFile.replace('.', '-')}`).click();
cy.contains(`Process Model File: ${bpmnFile}`); // cy.contains(`Process Model File: ${bpmnFile}`);
updateBpmnPythonScriptWithMonaco(newPythonScript); // updateBpmnPythonScriptWithMonaco(newPythonScript);
cy.contains('acceptance-tests-model-1').click(); // cy.contains('acceptance-tests-model-1').click();
cy.runPrimaryBpmnFile(); // cy.runPrimaryBpmnFile();
//
cy.getBySel('files-accordion').click(); // cy.getBySel('files-accordion').click();
cy.getBySel(`edit-file-${bpmnFile.replace('.', '-')}`).click(); // cy.getBySel(`edit-file-${bpmnFile.replace('.', '-')}`).click();
cy.contains(`Process Model File: ${bpmnFile}`); // cy.contains(`Process Model File: ${bpmnFile}`);
updateBpmnPythonScriptWithMonaco(originalPythonScript); // updateBpmnPythonScriptWithMonaco(originalPythonScript);
cy.contains('acceptance-tests-model-1').click(); // cy.contains('acceptance-tests-model-1').click();
cy.runPrimaryBpmnFile(); // cy.runPrimaryBpmnFile();
}); // });
it('can paginate items', () => { it('can paginate items', () => {
// make sure we have some process instances // make sure we have some process instances
@ -174,13 +174,12 @@ describe('process-instances', () => {
if (!['all', 'waiting'].includes(processStatus)) { if (!['all', 'waiting'].includes(processStatus)) {
cy.get(statusSelect).click(); cy.get(statusSelect).click();
cy.get(statusSelect).contains(processStatus).click(); cy.get(statusSelect).contains(processStatus).click();
// close the dropdown again
cy.get(statusSelect).click();
cy.getBySel('filter-button').click(); cy.getBySel('filter-button').click();
// FIXME: wait a little bit for the useEffects to be able to fully set processInstanceFilters
cy.wait(1000);
cy.url().should('include', `status=${processStatus}`);
cy.assertAtLeastOneItemInPaginatedResults(); cy.assertAtLeastOneItemInPaginatedResults();
cy.getBySel(`process-instance-status-${processStatus}`).contains( cy.getBySel(`process-instance-status-${processStatus}`);
processStatus
);
// there should really only be one, but in CI there are sometimes more // there should really only be one, but in CI there are sometimes more
cy.get('div[aria-label="Clear all selected items"]:first').click(); cy.get('div[aria-label="Clear all selected items"]:first').click();
} }

View File

@ -29,17 +29,20 @@ describe('process-models', () => {
newModelDisplayName newModelDisplayName
); );
cy.contains('Delete').click(); // go back to process model show by clicking on the breadcrumb
cy.contains(modelId).click();
cy.getBySel('delete-process-model-button').click();
cy.contains('Are you sure'); cy.contains('Are you sure');
cy.getBySel('modal-confirmation-dialog').find('.cds--btn--danger').click(); cy.getBySel('modal-confirmation-dialog').find('.cds--btn--danger').click();
cy.url().should('include', `process-groups/${groupId}`); cy.url().should('include', `process-groups/${groupId}`);
cy.contains(modelId).should('not.exist'); cy.contains(modelId).should('not.exist');
}); });
it('can create new bpmn, dmn, and json files', () => { it.only('can create new bpmn, dmn, and json files', () => {
const uuid = () => Cypress._.random(0, 1e6); const uuid = () => Cypress._.random(0, 1e6);
const id = uuid(); const id = uuid();
const groupId = 'acceptance-tests-group-one'; const groupId = 'misc/acceptance-tests-group-one';
const groupDisplayName = 'Acceptance Tests Group One'; const groupDisplayName = 'Acceptance Tests Group One';
const modelDisplayName = `Test Model 2 ${id}`; const modelDisplayName = `Test Model 2 ${id}`;
const modelId = `test-model-2-${id}`; const modelId = `test-model-2-${id}`;
@ -48,13 +51,13 @@ describe('process-models', () => {
const dmnFileName = `dmn_test_file_${id}`; const dmnFileName = `dmn_test_file_${id}`;
const jsonFileName = `json_test_file_${id}`; const jsonFileName = `json_test_file_${id}`;
cy.contains('Misc').click();
cy.contains(groupDisplayName).click(); cy.contains(groupDisplayName).click();
cy.createModel(groupId, modelId, modelDisplayName); cy.createModel(groupId, modelId, modelDisplayName);
cy.contains(groupId).click(); cy.contains(groupId).click();
cy.contains(modelId).click(); cy.contains(modelId).click();
cy.url().should('include', `process-models/${groupId}:${modelId}`); cy.url().should('include', `process-models/${groupId}:${modelId}`);
cy.contains(`Process Model: ${modelDisplayName}`); cy.contains(`Process Model: ${modelDisplayName}`);
cy.getBySel('files-accordion').click();
cy.contains(`${bpmnFileName}.bpmn`).should('not.exist'); cy.contains(`${bpmnFileName}.bpmn`).should('not.exist');
cy.contains(`${dmnFileName}.dmn`).should('not.exist'); cy.contains(`${dmnFileName}.dmn`).should('not.exist');
cy.contains(`${jsonFileName}.json`).should('not.exist'); cy.contains(`${jsonFileName}.json`).should('not.exist');
@ -73,7 +76,7 @@ describe('process-models', () => {
cy.contains(`Process Model File: ${bpmnFileName}`); cy.contains(`Process Model File: ${bpmnFileName}`);
cy.contains(modelId).click(); cy.contains(modelId).click();
cy.contains(`Process Model: ${modelDisplayName}`); cy.contains(`Process Model: ${modelDisplayName}`);
cy.getBySel('files-accordion').click(); // cy.getBySel('files-accordion').click();
cy.contains(`${bpmnFileName}.bpmn`).should('exist'); cy.contains(`${bpmnFileName}.bpmn`).should('exist');
// add new dmn file // add new dmn file
@ -81,13 +84,17 @@ describe('process-models', () => {
cy.contains(/^Process Model File$/); cy.contains(/^Process Model File$/);
cy.get('g[data-element-id=decision_1]').click().should('exist'); cy.get('g[data-element-id=decision_1]').click().should('exist');
cy.contains('General').click(); cy.contains('General').click();
cy.get('#bio-properties-panel-id')
.clear()
.type('decision_acceptance_test_1');
cy.contains('General').click();
cy.contains('Save').click(); cy.contains('Save').click();
cy.get('input[name=file_name]').type(dmnFileName); cy.get('input[name=file_name]').type(dmnFileName);
cy.contains('Save Changes').click(); cy.contains('Save Changes').click();
cy.contains(`Process Model File: ${dmnFileName}`); cy.contains(`Process Model File: ${dmnFileName}`);
cy.contains(modelId).click(); cy.contains(modelId).click();
cy.contains(`Process Model: ${modelDisplayName}`); cy.contains(`Process Model: ${modelDisplayName}`);
cy.getBySel('files-accordion').click(); // cy.getBySel('files-accordion').click();
cy.contains(`${dmnFileName}.dmn`).should('exist'); cy.contains(`${dmnFileName}.dmn`).should('exist');
// add new json file // add new json file
@ -103,11 +110,10 @@ describe('process-models', () => {
cy.wait(500); cy.wait(500);
cy.contains(modelId).click(); cy.contains(modelId).click();
cy.contains(`Process Model: ${modelDisplayName}`); cy.contains(`Process Model: ${modelDisplayName}`);
cy.getBySel('files-accordion').click(); // cy.getBySel('files-accordion').click();
cy.contains(`${jsonFileName}.json`).should('exist'); cy.contains(`${jsonFileName}.json`).should('exist');
cy.contains('Edit process model').click(); cy.getBySel('delete-process-model-button').click();
cy.contains('Delete').click();
cy.contains('Are you sure'); cy.contains('Are you sure');
cy.getBySel('modal-confirmation-dialog').find('.cds--btn--danger').click(); cy.getBySel('modal-confirmation-dialog').find('.cds--btn--danger').click();
cy.url().should('include', `process-groups/${groupId}`); cy.url().should('include', `process-groups/${groupId}`);
@ -131,7 +137,6 @@ describe('process-models', () => {
cy.url().should('include', `process-models/${groupId}:${modelId}`); cy.url().should('include', `process-models/${groupId}:${modelId}`);
cy.contains(`Process Model: ${modelDisplayName}`); cy.contains(`Process Model: ${modelDisplayName}`);
cy.getBySel('files-accordion').click();
cy.getBySel('upload-file-button').click(); cy.getBySel('upload-file-button').click();
cy.contains('Add file').selectFile( cy.contains('Add file').selectFile(
'cypress/fixtures/test_bpmn_file_upload.bpmn' 'cypress/fixtures/test_bpmn_file_upload.bpmn'
@ -142,7 +147,7 @@ describe('process-models', () => {
.click(); .click();
cy.runPrimaryBpmnFile(); cy.runPrimaryBpmnFile();
cy.getBySel('process-instance-list-link').click(); // cy.getBySel('process-instance-list-link').click();
cy.getBySel('process-instance-show-link').click(); cy.getBySel('process-instance-show-link').click();
cy.getBySel('process-instance-delete').click(); cy.getBySel('process-instance-delete').click();
cy.contains('Are you sure'); cy.contains('Are you sure');
@ -151,8 +156,7 @@ describe('process-models', () => {
// in breadcrumb // in breadcrumb
cy.contains(modelId).click(); cy.contains(modelId).click();
cy.contains('Edit process model').click(); cy.getBySel('delete-process-model-button').click();
cy.contains('Delete').click();
cy.contains('Are you sure'); cy.contains('Are you sure');
cy.getBySel('modal-confirmation-dialog').find('.cds--btn--danger').click(); cy.getBySel('modal-confirmation-dialog').find('.cds--btn--danger').click();
cy.url().should('include', `process-groups/${groupId}`); cy.url().should('include', `process-groups/${groupId}`);

View File

@ -76,7 +76,10 @@ Cypress.Commands.add('createModel', (groupId, modelId, modelDisplayName) => {
cy.get('input[name=id]').should('have.value', modelId); cy.get('input[name=id]').should('have.value', modelId);
cy.contains('Submit').click(); cy.contains('Submit').click();
cy.url().should('include', `process-models/${groupId}:${modelId}`); cy.url().should(
'include',
`process-models/${cy.modifyProcessModelPath(groupId)}:${modelId}`
);
cy.contains(`Process Model: ${modelDisplayName}`); cy.contains(`Process Model: ${modelDisplayName}`);
}); });
@ -93,6 +96,8 @@ Cypress.Commands.add(
'navigateToProcessModel', 'navigateToProcessModel',
(groupDisplayName, modelDisplayName, modelIdentifier) => { (groupDisplayName, modelDisplayName, modelIdentifier) => {
cy.navigateToAdmin(); cy.navigateToAdmin();
cy.contains('Misc').click();
cy.contains(`Process Group: Misc`);
cy.contains(groupDisplayName).click(); cy.contains(groupDisplayName).click();
cy.contains(`Process Group: ${groupDisplayName}`); cy.contains(`Process Group: ${groupDisplayName}`);
// https://stackoverflow.com/q/51254946/6090676 // https://stackoverflow.com/q/51254946/6090676

View File

@ -164,7 +164,7 @@ export default function NavigationBar() {
href="/admin/process-instances/reports" href="/admin/process-instances/reports"
isCurrentPage={isActivePage('/admin/process-instances/reports')} isCurrentPage={isActivePage('/admin/process-instances/reports')}
> >
Reports Perspectives
</HeaderMenuItem> </HeaderMenuItem>
</> </>
); );

View File

@ -24,7 +24,6 @@ export default function ProcessModelForm({
useState<boolean>(false); useState<boolean>(false);
const [displayNameInvalid, setDisplayNameInvalid] = useState<boolean>(false); const [displayNameInvalid, setDisplayNameInvalid] = useState<boolean>(false);
const navigate = useNavigate(); const navigate = useNavigate();
const modifiedProcessModelPath = modifyProcessModelPath(processModel.id);
const navigateToProcessModel = (result: ProcessModel) => { const navigateToProcessModel = (result: ProcessModel) => {
if ('id' in result) { if ('id' in result) {
@ -53,7 +52,9 @@ export default function ProcessModelForm({
if (hasErrors) { if (hasErrors) {
return; return;
} }
const path = `/process-models/${modifiedProcessModelPath}`; const path = `/process-models/${modifyProcessModelPath(
processGroupId || ''
)}`;
let httpMethod = 'POST'; let httpMethod = 'POST';
if (mode === 'edit') { if (mode === 'edit') {
httpMethod = 'PUT'; httpMethod = 'PUT';
@ -64,7 +65,7 @@ export default function ProcessModelForm({
}; };
if (mode === 'new') { if (mode === 'new') {
Object.assign(postBody, { Object.assign(postBody, {
id: `${processGroupId}:${processModel.id}`, id: `${processGroupId}/${processModel.id}`,
}); });
} }

View File

@ -102,3 +102,11 @@ export interface PermissionCheckResult {
export interface PermissionCheckResponseBody { export interface PermissionCheckResponseBody {
results: PermissionCheckResult; results: PermissionCheckResult;
} }
export interface FormField {
id: string;
title: string;
required: boolean;
type: string;
enum: string[];
}

View File

@ -21,6 +21,7 @@ import ErrorContext from '../contexts/ErrorContext';
import ProcessInstanceLogList from './ProcessInstanceLogList'; import ProcessInstanceLogList from './ProcessInstanceLogList';
import MessageInstanceList from './MessageInstanceList'; import MessageInstanceList from './MessageInstanceList';
import Configuration from './Configuration'; import Configuration from './Configuration';
import JsonSchemaFormBuilder from './JsonSchemaFormBuilder';
export default function AdminRoutes() { export default function AdminRoutes() {
const location = useLocation(); const location = useLocation();
@ -108,6 +109,10 @@ export default function AdminRoutes() {
<Route path="process-instances" element={<ProcessInstanceList />} /> <Route path="process-instances" element={<ProcessInstanceList />} />
<Route path="messages" element={<MessageInstanceList />} /> <Route path="messages" element={<MessageInstanceList />} />
<Route path="configuration/*" element={<Configuration />} /> <Route path="configuration/*" element={<Configuration />} />
<Route
path="process-models/:process_model_id/form-builder"
element={<JsonSchemaFormBuilder />}
/>
</Routes> </Routes>
); );
} }

View File

@ -0,0 +1,250 @@
import { useEffect, useState } from 'react';
// @ts-ignore
import { Button, Select, SelectItem, TextInput } from '@carbon/react';
import { useParams } from 'react-router-dom';
import { FormField } from '../interfaces';
import { modifyProcessModelPath, slugifyString } from '../helpers';
import HttpService from '../services/HttpService';
export default function JsonSchemaFormBuilder() {
const params = useParams();
const formFieldTypes = ['textbox', 'checkbox', 'select'];
const [formTitle, setFormTitle] = useState<string>('');
const [formDescription, setFormDescription] = useState<string>('');
const [formId, setFormId] = useState<string>('');
const [formFields, setFormFields] = useState<FormField[]>([]);
const [showNewFormField, setShowNewFormField] = useState<boolean>(false);
const [formFieldSelectOptions, setFormFieldSelectOptions] =
useState<string>('');
const [formIdHasBeenUpdatedByUser, setFormIdHasBeenUpdatedByUser] =
useState<boolean>(false);
const [formFieldIdHasBeenUpdatedByUser, setFormFieldIdHasBeenUpdatedByUser] =
useState<boolean>(false);
const [showFormFieldSelectTextField, setShowFormFieldSelectTextField] =
useState<boolean>(false);
const [formFieldId, setFormFieldId] = useState<string>('');
const [formFieldTitle, setFormFieldTitle] = useState<string>('');
const [formFieldType, setFormFieldType] = useState<string>('');
const modifiedProcessModelId = modifyProcessModelPath(
`${params.process_model_id}`
);
useEffect(() => {}, []);
const renderFormJson = () => {
const formJson = {
title: formTitle,
description: formDescription,
properties: {},
required: [],
};
formFields.forEach((formField: FormField) => {
let jsonSchemaFieldType = 'string';
if (formField.type === 'checkbox') {
jsonSchemaFieldType = 'boolean';
}
const formJsonObject: any = {
type: jsonSchemaFieldType,
title: formField.title,
};
if (formField.type === 'select') {
formJsonObject.enum = formField.enum;
}
(formJson.properties as any)[formField.id] = formJsonObject;
});
return JSON.stringify(formJson, null, 2);
};
const renderFormUiJson = () => {
const uiOrder = formFields.map((formField: FormField) => {
return formField.id;
});
return JSON.stringify({ 'ui:order': uiOrder }, null, 2);
};
const onFormFieldTitleChange = (newFormFieldTitle: string) => {
console.log('newFormFieldTitle', newFormFieldTitle);
console.log(
'setFormFieldIdHasBeenUpdatedByUser',
formFieldIdHasBeenUpdatedByUser
);
if (!formFieldIdHasBeenUpdatedByUser) {
setFormFieldId(slugifyString(newFormFieldTitle));
}
setFormFieldTitle(newFormFieldTitle);
};
const onFormTitleChange = (newFormTitle: string) => {
if (!formIdHasBeenUpdatedByUser) {
setFormId(slugifyString(newFormTitle));
}
setFormTitle(newFormTitle);
};
const addFormField = () => {
const newFormField: FormField = {
id: formFieldId,
title: formFieldTitle,
required: false,
type: formFieldType,
enum: formFieldSelectOptions.split(','),
};
setFormFieldIdHasBeenUpdatedByUser(false);
setShowNewFormField(false);
setFormFields([...formFields, newFormField]);
};
const handleFormFieldTypeChange = (event: any) => {
setFormFieldType(event.srcElement.value);
if (event.srcElement.value === 'select') {
setShowFormFieldSelectTextField(true);
} else {
setShowFormFieldSelectTextField(false);
}
};
const newFormFieldComponent = () => {
if (showNewFormField) {
return (
<>
<TextInput
id="form-field-title"
name="title"
labelText="Title"
value={formFieldTitle}
onChange={(event: any) => {
onFormFieldTitleChange(event.srcElement.value);
}}
/>
<TextInput
id="json-form-field-id"
name="id"
labelText="ID"
value={formFieldId}
onChange={(event: any) => {
setFormFieldIdHasBeenUpdatedByUser(true);
setFormFieldId(event.srcElement.value);
}}
/>
<Select
id="form-field-type"
labelText="Type"
onChange={handleFormFieldTypeChange}
>
{formFieldTypes.map((fft: string) => {
return <SelectItem text={fft} value={fft} />;
})}
</Select>
{showFormFieldSelectTextField ? (
<TextInput
id="json-form-field-select-options"
name="select-options"
labelText="Select Options"
onChange={(event: any) => {
setFormFieldSelectOptions(event.srcElement.value);
}}
/>
) : null}
<Button onClick={addFormField}>Add Field</Button>
</>
);
}
return null;
};
const formFieldArea = () => {
if (formFields.length > 0) {
return formFields.map((formField: FormField) => {
return <p>Form Field: {formField.id}</p>;
});
}
return null;
};
const handleSaveCallback = (result: any) => {
console.log('result', result);
};
const uploadFile = (file: File) => {
const url = `/process-models/${modifiedProcessModelId}/files`;
const httpMethod = 'POST';
const formData = new FormData();
formData.append('file', file);
formData.append('fileName', file.name);
HttpService.makeCallToBackend({
path: url,
successCallback: handleSaveCallback,
httpMethod,
postBody: formData,
});
};
const saveFile = () => {
const formJsonFileName = `${formId}-schema.json`;
const formUiJsonFileName = `${formId}-uischema.json`;
uploadFile(new File([renderFormJson()], formJsonFileName));
uploadFile(new File([renderFormUiJson()], formUiJsonFileName));
};
const jsonFormArea = () => {
return (
<>
<Button onClick={saveFile}>Save</Button>
<TextInput
id="json-form-title"
name="title"
labelText="Title"
value={formTitle}
onChange={(event: any) => {
onFormTitleChange(event.srcElement.value);
}}
/>
<TextInput
id="json-form-id"
name="id"
labelText="ID"
value={formId}
onChange={(event: any) => {
setFormIdHasBeenUpdatedByUser(true);
setFormId(event.srcElement.value);
}}
/>
<TextInput
id="form-description"
name="description"
labelText="Description"
value={formDescription}
onChange={(event: any) => {
setFormDescription(event.srcElement.value);
}}
/>
<Button
onClick={() => {
setFormFieldId('');
setFormFieldTitle('');
setFormFieldType('');
setFormFieldSelectOptions('');
setShowFormFieldSelectTextField(false);
setShowNewFormField(true);
}}
>
New Field
</Button>
{formFieldArea()}
{newFormFieldComponent()}
</>
);
};
return <>{jsonFormArea()}</>;
}

View File

@ -111,6 +111,7 @@ export default function ProcessGroupShow() {
]} ]}
/> />
<h1>Process Group: {processGroup.display_name}</h1> <h1>Process Group: {processGroup.display_name}</h1>
<p className="process-description">{processGroup.description}</p>
<ul> <ul>
<Stack orientation="horizontal" gap={3}> <Stack orientation="horizontal" gap={3}>
<Can I="POST" a={targetUris.processGroupListPath} ability={ability}> <Can I="POST" a={targetUris.processGroupListPath} ability={ability}>

View File

@ -44,9 +44,9 @@ export default function ProcessInstanceReportList() {
const headerStuff = ( const headerStuff = (
<> <>
<h1>Process Instance Reports</h1> <h1>Process Instance Perspectives</h1>
<Button href="/admin/process-instances/reports/new"> <Button href="/admin/process-instances/reports/new">
Add a process instance report Add a process instance perspective
</Button> </Button>
</> </>
); );
@ -61,7 +61,7 @@ export default function ProcessInstanceReportList() {
return ( return (
<main> <main>
{headerStuff} {headerStuff}
<p>No reports found</p> <p>No perspectives found</p>
</main> </main>
); );
} }

View File

@ -56,7 +56,7 @@ export default function ProcessInstanceReportNew() {
return ( return (
<> <>
<ProcessBreadcrumb /> <ProcessBreadcrumb />
<h1>Add Process Model</h1> <h1>Add Process Instance Perspective</h1>
<form onSubmit={addProcessInstanceReport}> <form onSubmit={addProcessInstanceReport}>
<label htmlFor="identifier"> <label htmlFor="identifier">
identifier: identifier:

View File

@ -80,11 +80,11 @@ export default function ProcessInstanceReport() {
processGroupId={params.process_group_id} processGroupId={params.process_group_id}
linkProcessModel linkProcessModel
/> />
<h1>Process Instance Report: {params.report_identifier}</h1> <h1>Process Instance Perspective: {params.report_identifier}</h1>
<Button <Button
href={`/admin/process-instances/reports/${params.report_identifier}/edit`} href={`/admin/process-instances/reports/${params.report_identifier}/edit`}
> >
Edit process instance report Edit process instance perspective
</Button> </Button>
<PaginationForTable <PaginationForTable
page={page} page={page}

View File

@ -3,6 +3,7 @@ import { useParams } from 'react-router-dom';
import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
import { ProcessModel } from '../interfaces'; import { ProcessModel } from '../interfaces';
import ProcessModelForm from '../components/ProcessModelForm'; import ProcessModelForm from '../components/ProcessModelForm';
import { unModifyProcessModelPath } from '../helpers';
export default function ProcessModelNew() { export default function ProcessModelNew() {
const params = useParams(); const params = useParams();
@ -28,7 +29,7 @@ export default function ProcessModelNew() {
<h1>Add Process Model</h1> <h1>Add Process Model</h1>
<ProcessModelForm <ProcessModelForm
mode="new" mode="new"
processGroupId={params.process_group_id} processGroupId={unModifyProcessModelPath(params.process_group_id || '')}
processModel={processModel} processModel={processModel}
setProcessModel={setProcessModel} setProcessModel={setProcessModel}
/> />

View File

@ -7,6 +7,7 @@ import {
TrashCan, TrashCan,
Favorite, Favorite,
Edit, Edit,
ArrowRight,
// @ts-ignore // @ts-ignore
} from '@carbon/icons-react'; } from '@carbon/icons-react';
import { import {
@ -514,6 +515,35 @@ export default function ProcessModelShow() {
); );
}; };
const processInstanceListTableButton = () => {
if (processModel) {
return (
<Grid fullWidth>
<Column
sm={{ span: 1, offset: 3 }}
md={{ span: 1, offset: 7 }}
lg={{ span: 1, offset: 15 }}
>
<Button
data-qa="process-instance-list-link"
kind="ghost"
renderIcon={ArrowRight}
iconDescription="Go to Filterable List"
hasIconOnly
size="lg"
onClick={() =>
navigate(
`/admin/process-instances?process_model_identifier=${processModel.id}`
)
}
/>
</Column>
</Grid>
);
}
return null;
};
if (processModel) { if (processModel) {
return ( return (
<> <>
@ -535,6 +565,7 @@ export default function ProcessModelShow() {
<Can I="DELETE" a={targetUris.processModelShowPath} ability={ability}> <Can I="DELETE" a={targetUris.processModelShowPath} ability={ability}>
<ButtonWithConfirmation <ButtonWithConfirmation
kind="ghost" kind="ghost"
data-qa="delete-process-model-button"
renderIcon={TrashCan} renderIcon={TrashCan}
iconDescription="Delete Process Model" iconDescription="Delete Process Model"
hasIconOnly hasIconOnly
@ -570,6 +601,7 @@ export default function ProcessModelShow() {
{processInstanceRunResultTag()} {processInstanceRunResultTag()}
<br /> <br />
<Can I="GET" a={targetUris.processInstanceListPath} ability={ability}> <Can I="GET" a={targetUris.processInstanceListPath} ability={ability}>
{processInstanceListTableButton()}
<ProcessInstanceListTable <ProcessInstanceListTable
filtersEnabled={false} filtersEnabled={false}
processModelFullIdentifier={processModel.id} processModelFullIdentifier={processModel.id}

View File

@ -175,6 +175,19 @@ export default function ReactFormEditor() {
<Button onClick={saveFile} variant="danger" data-qa="file-save-button"> <Button onClick={saveFile} variant="danger" data-qa="file-save-button">
Save Save
</Button> </Button>
{params.file_name ? null : (
<Button
onClick={() =>
navigate(
`/admin/process-models/${params.process_model_id}/form-builder`
)
}
variant="danger"
data-qa="form-builder-button"
>
Form Builder
</Button>
)}
{params.file_name ? ( {params.file_name ? (
<ButtonWithConfirmation <ButtonWithConfirmation
description={`Delete file ${params.file_name}?`} description={`Delete file ${params.file_name}?`}