unit tests are passing with the new spec tables

This commit is contained in:
jasquat 2023-03-03 10:08:14 -05:00
parent 8d67e8cc87
commit 0c6e9a63ba
10 changed files with 160 additions and 20 deletions

View File

@ -0,0 +1,28 @@
"""empty message
Revision ID: 567d22ded3af
Revises: 2fe2830f45e1
Create Date: 2023-03-03 08:38:25.855923
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '567d22ded3af'
down_revision = '2fe2830f45e1'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_unique_constraint('bpmn_process_definition_relationship_unique', 'bpmn_process_definition_relationship', ['bpmn_process_definition_parent_id', 'bpmn_process_definition_child_id'])
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint('bpmn_process_definition_relationship_unique', 'bpmn_process_definition_relationship', type_='unique')
# ### end Alembic commands ###

View File

@ -0,0 +1,30 @@
"""empty message
Revision ID: 6315ff2525b0
Revises: 567d22ded3af
Create Date: 2023-03-03 09:19:28.098057
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '6315ff2525b0'
down_revision = '567d22ded3af'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('process_instance', sa.Column('bpmn_process_definition_id', sa.Integer(), nullable=False))
op.create_foreign_key(None, 'process_instance', 'bpmn_process_definition', ['bpmn_process_definition_id'], ['id'])
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(None, 'process_instance', type_='foreignkey')
op.drop_column('process_instance', 'bpmn_process_definition_id')
# ### end Alembic commands ###

View File

@ -0,0 +1,32 @@
"""empty message
Revision ID: def2cbb0ca6b
Revises: 6315ff2525b0
Create Date: 2023-03-03 09:23:19.480250
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import mysql
# revision identifiers, used by Alembic.
revision = 'def2cbb0ca6b'
down_revision = '6315ff2525b0'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.alter_column('process_instance', 'bpmn_process_definition_id',
existing_type=mysql.INTEGER(),
nullable=True)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.alter_column('process_instance', 'bpmn_process_definition_id',
existing_type=mysql.INTEGER(),
nullable=False)
# ### end Alembic commands ###

View File

@ -21,6 +21,7 @@ class BpmnProcessDefinitionModel(SpiffworkflowBaseDBModel):
properties_json: str = db.Column(db.JSON, nullable=False)
# process or subprocess
# FIXME: will probably ignore for now since we do not strictly need it
type: str = db.Column(db.String(32), nullable=False, index=True)
# TODO: remove these from process_instance

View File

@ -1,4 +1,5 @@
from __future__ import annotations
from sqlalchemy import UniqueConstraint
from sqlalchemy.orm import deferred
from sqlalchemy import ForeignKey
from spiffworkflow_backend.models.bpmn_process_definition import BpmnProcessDefinitionModel
@ -9,6 +10,12 @@ from spiffworkflow_backend.models.db import SpiffworkflowBaseDBModel
class BpmnProcessDefinitionRelationshipModel(SpiffworkflowBaseDBModel):
__tablename__ = "bpmn_process_definition_relationship"
__table_args__ = (
UniqueConstraint(
"bpmn_process_definition_parent_id", "bpmn_process_definition_child_id", name="bpmn_process_definition_relationship_unique"
),
)
id: int = db.Column(db.Integer, primary_key=True)
bpmn_process_definition_parent_id: int = db.Column(ForeignKey(BpmnProcessDefinitionModel.id), nullable=False) # type: ignore
bpmn_process_definition_child_id: int = db.Column(ForeignKey(BpmnProcessDefinitionModel.id), nullable=False) # type: ignore

View File

@ -1,5 +1,6 @@
"""Process_instance."""
from __future__ import annotations
from spiffworkflow_backend.models.bpmn_process_definition import BpmnProcessDefinitionModel
from typing import Any
from typing import cast
@ -74,6 +75,9 @@ class ProcessInstanceModel(SpiffworkflowBaseDBModel):
)
process_instance_data = relationship("ProcessInstanceDataModel", cascade="delete")
bpmn_process_definition_id: int = db.Column(ForeignKey(BpmnProcessDefinitionModel.id), nullable=True) # type: ignore
bpmn_process_definition = relationship(BpmnProcessDefinitionModel)
active_human_tasks = relationship(
"HumanTaskModel",
primaryjoin=(

View File

@ -1,5 +1,7 @@
"""Process_instance_processor."""
import _strptime # type: ignore
import _strptime
from spiffworkflow_backend.models import serialized_bpmn_definition # type: ignore
from spiffworkflow_backend.models.bpmn_process_definition_relationship import BpmnProcessDefinitionRelationshipModel # noqa: F401
import decimal
import json
import logging
@ -55,6 +57,7 @@ from SpiffWorkflow.util.deep_merge import DeepMerge # type: ignore
from sqlalchemy import text
from spiffworkflow_backend.exceptions.api_error import ApiError
from spiffworkflow_backend.models import bpmn_process_definition_relationship
from spiffworkflow_backend.models.bpmn_process_definition import BpmnProcessDefinitionModel
from spiffworkflow_backend.models.db import db
from spiffworkflow_backend.models.file import File
@ -526,9 +529,32 @@ class ProcessInstanceProcessor:
def _get_full_bpmn_json(cls, process_instance_model: ProcessInstanceModel) -> dict:
if process_instance_model.serialized_bpmn_definition_id is None:
return {}
serialized_bpmn_definition = process_instance_model.serialized_bpmn_definition
# serialized_bpmn_definition = process_instance_model.serialized_bpmn_definition
# print(f"serialized_bpmn_definition.static_json: {serialized_bpmn_definition.static_json}")
# loaded_json: dict = json.loads(serialized_bpmn_definition.static_json) # or "{}")
serialized_bpmn_definition = {}
bpmn_process_definition = BpmnProcessDefinitionModel.query.filter_by(id=process_instance_model.bpmn_process_definition_id).first()
task_definitions = TaskDefinitionModel.query.filter_by(bpmn_process_definition_id=process_instance_model.bpmn_process_definition_id).all()
if bpmn_process_definition is not None:
serialized_bpmn_definition = {"serializer_version": cls.SERIALIZER_VERSION, "spec": {}, "subprocess_specs": {}}
bpmn_process_definition_dict = json.loads(bpmn_process_definition.properties_json)
bpmn_process_definition_dict['task_specs'] = {}
for task_definition in task_definitions:
bpmn_process_definition_dict['task_specs'][task_definition.bpmn_identifier] = json.loads(task_definition.properties_json)
serialized_bpmn_definition['spec'] = bpmn_process_definition_dict
bpmn_process_subprocess_definitions = BpmnProcessDefinitionRelationshipModel.query.filter_by(bpmn_process_definition_parent_id=bpmn_process_definition.id).all()
for bpmn_process_subprocess_definition in bpmn_process_subprocess_definitions:
subprocess_task_definitions = TaskDefinitionModel.query.filter_by(bpmn_process_definition_id=bpmn_process_subprocess_definition.id).all()
bpmn_process_subprocess_dict = json.loads(bpmn_process_definition.properties_json)
bpmn_process_subprocess_dict['task_specs'] = {}
for subprocess_task_definition in subprocess_task_definitions:
bpmn_process_subprocess_dict['task_specs'][subprocess_task_definition.bpmn_identifier] = json.loads(subprocess_task_definition.properties_json)
serialized_bpmn_definition['subprocess_specs'][bpmn_process_subprocess_definition.bpmn_identifier] = bpmn_process_subprocess_dict
loaded_json: dict = serialized_bpmn_definition
process_instance_data = process_instance_model.process_instance_data
loaded_json: dict = json.loads(serialized_bpmn_definition.static_json or "{}")
loaded_json.update(json.loads(process_instance_data.runtime_json))
return loaded_json
@ -909,19 +935,15 @@ class ProcessInstanceProcessor:
self.process_instance_model.process_instance_data = process_instance_data
def _store_bpmn_process_definitions(self, process_bpmn_properties: dict) -> None:
# for process_bpmn_identifier, process_bpmn_properties in bpmn_spec_dict.items():
print(f"process_bpmn_properties: {process_bpmn_properties}")
def _store_bpmn_process_definition(self, process_bpmn_properties: dict, bpmn_process_definition_parent: Optional[BpmnProcessDefinitionModel] = None) -> BpmnProcessDefinitionModel:
process_bpmn_identifier = process_bpmn_properties['name']
new_hash_digest = sha256(
json.dumps(process_bpmn_properties, sort_keys=True).encode("utf8")
).hexdigest()
bpmn_process_definition = BpmnProcessDefinitionModel.query.filter_by(
bpmn_process_definition: Optional[BpmnProcessDefinitionModel] = BpmnProcessDefinitionModel.query.filter_by(
hash=new_hash_digest
).first()
if bpmn_process_definition is None:
# print(f"process_bpmn_identifier: {process_bpmn_identifier}")
print(f"process_bpmn_properties: {process_bpmn_properties}")
task_specs = process_bpmn_properties.pop("task_specs")
bpmn_process_definition = BpmnProcessDefinitionModel(
hash=new_hash_digest, bpmn_identifier=process_bpmn_identifier, properties_json=json.dumps(process_bpmn_properties), type="process"
@ -937,6 +959,19 @@ class ProcessInstanceProcessor:
)
db.session.add(task_definition)
if bpmn_process_definition_parent:
bpmn_process_definition_relationship = BpmnProcessDefinitionRelationshipModel.query.filter_by(
bpmn_process_definition_parent_id=bpmn_process_definition_parent.id,
bpmn_process_definition_child_id=bpmn_process_definition.id,
).first()
if bpmn_process_definition_relationship is None:
bpmn_process_definition_relationship = BpmnProcessDefinitionRelationshipModel(
bpmn_process_definition_parent_id=bpmn_process_definition_parent.id,
bpmn_process_definition_child_id=bpmn_process_definition.id,
)
db.session.add(bpmn_process_definition_relationship)
return bpmn_process_definition
def _add_bpmn_json_records_new(self) -> None:
"""Adds serialized_bpmn_definition and process_instance_data records to the db session.
@ -952,7 +987,11 @@ class ProcessInstanceProcessor:
else:
process_instance_data_dict[bpmn_key] = bpmn_dict[bpmn_key]
self._store_bpmn_process_definitions(bpmn_spec_dict['spec'])
bpmn_process_definition_parent = self._store_bpmn_process_definition(bpmn_spec_dict['spec'])
for process_bpmn_properties in bpmn_spec_dict['subprocess_specs'].values():
self._store_bpmn_process_definition(process_bpmn_properties, bpmn_process_definition_parent)
self.process_instance_model.bpmn_process_definition = bpmn_process_definition_parent
# # FIXME: always save new hash until we get updated Spiff without loopresettask
# # if self.process_instance_model.serialized_bpmn_definition_id is None:

View File

@ -24,14 +24,14 @@
</bpmn:correlationPropertyRetrievalExpression>
</bpmn:correlationProperty>
<bpmn:process id="test_dot_notation" name="Test Dot Notation" isExecutable="true">
<bpmn:startEvent id="start" name="Start">
<bpmn:startEvent id="startHere" name="StartHere">
<bpmn:outgoing>Flow_0dbnzbi</bpmn:outgoing>
</bpmn:startEvent>
<bpmn:sequenceFlow id="Flow_0dbnzbi" sourceRef="start" targetRef="get_data" />
<bpmn:endEvent id="end" name="End">
<bpmn:sequenceFlow id="Flow_0dbnzbi" sourceRef="startHere" targetRef="get_data" />
<bpmn:endEvent id="endHere" name="End">
<bpmn:incoming>Flow_0nt355i</bpmn:incoming>
</bpmn:endEvent>
<bpmn:sequenceFlow id="Flow_0nt355i" sourceRef="get_data" targetRef="end" />
<bpmn:sequenceFlow id="Flow_0nt355i" sourceRef="get_data" targetRef="endHere" />
<bpmn:userTask id="get_data" name="Get Data">
<bpmn:extensionElements>
<spiffworkflow:properties>
@ -53,13 +53,13 @@
<di:waypoint x="360" y="230" />
<di:waypoint x="412" y="230" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="Event_1uf4njx_di" bpmnElement="start">
<bpmndi:BPMNShape id="Event_1uf4njx_di" bpmnElement="startHere">
<dc:Bounds x="172" y="212" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="178" y="255" width="24" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_00d0dwr_di" bpmnElement="end">
<bpmndi:BPMNShape id="Event_00d0dwr_di" bpmnElement="endHere">
<dc:Bounds x="412" y="212" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="420" y="255" width="20" height="14" />

View File

@ -1301,10 +1301,11 @@ class TestProcessApi(BaseTest):
assert create_response.json is not None
assert create_response.status_code == 201
process_instance_id = create_response.json["id"]
client.post(
run_response = client.post(
f"/v1.0/process-instances/{modified_process_model_identifier}/{process_instance_id}/run",
headers=self.logged_in_headers(with_super_admin_user),
)
assert run_response.status_code == 200
show_response = client.get(
f"/v1.0/process-instances/{modified_process_model_identifier}/{process_instance_id}?process_identifier={spec_reference.identifier}",
headers=self.logged_in_headers(with_super_admin_user),

View File

@ -13,9 +13,7 @@ from spiffworkflow_backend.services.process_instance_service import (
class TestDotNotation(BaseTest):
"""TestVariousBpmnConstructs."""
def test_dot_notation(
def test_dot_notation_in_message_path(
self,
app: Flask,
client: FlaskClient,