look for any migration detail for the pi and target hash to get the git revision w/ burnettk (#1959)

Co-authored-by: jasquat <jasquat@users.noreply.github.com>
This commit is contained in:
jasquat 2024-07-19 12:35:02 -04:00 committed by GitHub
parent 038fbeedbb
commit 57c7b12e38
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 211 additions and 7 deletions

View File

@ -27,6 +27,7 @@ from SpiffWorkflow.bpmn.util.diff import migrate_workflow
from SpiffWorkflow.task import Task as SpiffTask # type: ignore
from SpiffWorkflow.util.deep_merge import DeepMerge # type: ignore
from SpiffWorkflow.util.task import TaskState # type: ignore
from sqlalchemy import or_
from spiffworkflow_backend.background_processing.celery_tasks.process_instance_task_producer import (
queue_process_instance_if_appropriate,
@ -262,9 +263,7 @@ class ProcessInstanceService:
bpmn_process_instance=processor.bpmn_process_instance,
store_process_instance_events=False,
)
git_revision_to_use = cls.get_appropriate_git_revision(
process_instance, initial_bpmn_process_hash, target_bpmn_process_hash
)
git_revision_to_use = cls.get_appropriate_git_revision(process_instance, target_bpmn_process_hash)
process_instance.bpmn_version_control_identifier = git_revision_to_use
db.session.add(process_instance)
@ -303,16 +302,21 @@ class ProcessInstanceService:
def get_appropriate_git_revision(
cls,
process_instance: ProcessInstanceModel,
initial_bpmn_process_hash: str | None,
target_bpmn_process_hash: str | None,
) -> str | None:
# if target_bpmn_process_hash is set and there's an old migration event, then assume this is a revert
# and that we should ensure that items like git revision remain consistent
git_revision_to_use = None
if target_bpmn_process_hash is not None:
# NOTE: there is a potential bug where there could be many git revisions for a bpmn process hash
# so this could pick up a different git revision than the revert event passed in.
# This is an edge case though and the git revision is close enough and more informational
old_migration_event = (
ProcessInstanceMigrationDetailModel.query.filter_by(
initial_bpmn_process_hash=target_bpmn_process_hash, target_bpmn_process_hash=initial_bpmn_process_hash
ProcessInstanceMigrationDetailModel.query.filter(
or_(
ProcessInstanceMigrationDetailModel.initial_bpmn_process_hash == target_bpmn_process_hash,
ProcessInstanceMigrationDetailModel.target_bpmn_process_hash == target_bpmn_process_hash,
)
)
.join(ProcessInstanceEventModel)
.filter(ProcessInstanceEventModel.process_instance_id == process_instance.id)
@ -320,7 +324,10 @@ class ProcessInstanceService:
.first()
)
if old_migration_event is not None:
git_revision_to_use = old_migration_event.initial_git_revision
if old_migration_event.initial_bpmn_process_hash == target_bpmn_process_hash:
git_revision_to_use = old_migration_event.initial_git_revision
elif old_migration_event.target_bpmn_process_hash == target_bpmn_process_hash:
git_revision_to_use = old_migration_event.target_bpmn_process_hash
if git_revision_to_use is None:
try:
git_revision_to_use = GitService.get_current_revision()

View File

@ -0,0 +1,95 @@
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" id="Definitions_96f6665" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="3.0.0-dev">
<bpmn:process id="Process_migration_test_wlm607w" isExecutable="true">
<bpmn:startEvent id="StartEvent_top">
<bpmn:outgoing>Flow_17db3yp</bpmn:outgoing>
</bpmn:startEvent>
<bpmn:sequenceFlow id="Flow_17db3yp" sourceRef="StartEvent_top" targetRef="subprocess_one" />
<bpmn:endEvent id="EndEvent_1">
<bpmn:incoming>Flow_0m0he21</bpmn:incoming>
</bpmn:endEvent>
<bpmn:sequenceFlow id="Flow_0m0he21" sourceRef="subprocess_one" targetRef="EndEvent_1" />
<bpmn:subProcess id="subprocess_one">
<bpmn:incoming>Flow_17db3yp</bpmn:incoming>
<bpmn:outgoing>Flow_0m0he21</bpmn:outgoing>
<bpmn:startEvent id="StartEvent_sub">
<bpmn:outgoing>Flow_01eckoj</bpmn:outgoing>
</bpmn:startEvent>
<bpmn:sequenceFlow id="Flow_01eckoj" sourceRef="StartEvent_sub" targetRef="manual_task_one" />
<bpmn:endEvent id="Event_0fx2psf">
<bpmn:incoming>Flow_0s3wg15</bpmn:incoming>
</bpmn:endEvent>
<bpmn:sequenceFlow id="Flow_0s7769x" sourceRef="manual_task_one" targetRef="manual_task_two" />
<bpmn:manualTask id="manual_task_one" name="Manual Task One">
<bpmn:incoming>Flow_01eckoj</bpmn:incoming>
<bpmn:outgoing>Flow_0s7769x</bpmn:outgoing>
</bpmn:manualTask>
<bpmn:sequenceFlow id="Flow_17fvyk2" sourceRef="manual_task_two" targetRef="manual_task_three" />
<bpmn:manualTask id="manual_task_two">
<bpmn:incoming>Flow_0s7769x</bpmn:incoming>
<bpmn:outgoing>Flow_17fvyk2</bpmn:outgoing>
</bpmn:manualTask>
<bpmn:sequenceFlow id="Flow_0s3wg15" sourceRef="manual_task_three" targetRef="Event_0fx2psf" />
<bpmn:manualTask id="manual_task_three">
<bpmn:incoming>Flow_17fvyk2</bpmn:incoming>
<bpmn:outgoing>Flow_0s3wg15</bpmn:outgoing>
</bpmn:manualTask>
</bpmn:subProcess>
</bpmn:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_migration_test_wlm607w">
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_top">
<dc:Bounds x="179" y="159" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_14za570_di" bpmnElement="EndEvent_1">
<dc:Bounds x="462" y="159" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_115q6jv_di" bpmnElement="subprocess_one">
<dc:Bounds x="280" y="137" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="Flow_17db3yp_di" bpmnElement="Flow_17db3yp">
<di:waypoint x="215" y="177" />
<di:waypoint x="280" y="177" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0m0he21_di" bpmnElement="Flow_0m0he21">
<di:waypoint x="380" y="177" />
<di:waypoint x="462" y="177" />
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
<bpmndi:BPMNDiagram id="BPMNDiagram_0fjhef4">
<bpmndi:BPMNPlane id="BPMNPlane_1rqwee3" bpmnElement="subprocess_one">
<bpmndi:BPMNShape id="Event_0bneyqp_di" bpmnElement="StartEvent_sub">
<dc:Bounds x="452" y="302" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_0fx2psf_di" bpmnElement="Event_0fx2psf">
<dc:Bounds x="932" y="302" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_1lv4tyw_di" bpmnElement="manual_task_one">
<dc:Bounds x="540" y="280" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0938qt9_di" bpmnElement="manual_task_two">
<dc:Bounds x="670" y="280" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_05r8cox_di" bpmnElement="manual_task_three">
<dc:Bounds x="790" y="280" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="Flow_01eckoj_di" bpmnElement="Flow_01eckoj">
<di:waypoint x="488" y="320" />
<di:waypoint x="540" y="320" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0s7769x_di" bpmnElement="Flow_0s7769x">
<di:waypoint x="640" y="320" />
<di:waypoint x="670" y="320" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_17fvyk2_di" bpmnElement="Flow_17fvyk2">
<di:waypoint x="770" y="320" />
<di:waypoint x="790" y="320" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0s3wg15_di" bpmnElement="Flow_0s3wg15">
<di:waypoint x="890" y="320" />
<di:waypoint x="932" y="320" />
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions>

View File

@ -345,6 +345,108 @@ class TestProcessInstanceService(BaseTest):
assert pi_migration_details.target_git_revision == "rev1"
assert process_instance.bpmn_version_control_identifier == "rev1"
def test_it_can_migrate_a_process_instance_multiple_times_and_revert(
self,
app: Flask,
mocker: MockerFixture,
with_db_and_bpmn_file_cleanup: None,
) -> None:
initiator_user = self.find_or_create_user("initiator_user")
process_model = load_test_spec(
process_model_id="test_group/migration-test-with-subprocess",
process_model_source_directory="migration-test-with-subprocess",
bpmn_file_name="migration-initial.bpmn",
)
mock_get_current_revision = mocker.patch.object(GitService, "get_current_revision")
# Set the return value for the first call
process_instance = self.create_process_instance_from_process_model(
process_model=process_model, user=initiator_user, bpmn_version_control_identifier="rev1"
)
processor = ProcessInstanceProcessor(process_instance)
processor.do_engine_steps(save=True, execution_strategy_name="greedy")
initial_bpmn_process_hash = process_instance.bpmn_process_definition.full_process_model_hash
assert initial_bpmn_process_hash is not None
spiff_task = processor.__class__.get_task_by_bpmn_identifier("manual_task_two", processor.bpmn_process_instance)
assert spiff_task is None
new_file_path = os.path.join(
app.instance_path,
"..",
"..",
"tests",
"data",
"migration-test-with-subprocess",
"migration-new.bpmn",
)
with open(new_file_path) as f:
new_contents = f.read().encode()
SpecFileService.update_file(
process_model_info=process_model,
file_name="migration-initial.bpmn",
binary_data=new_contents,
update_process_cache_only=True,
)
process_instance = ProcessInstanceModel.query.filter_by(id=process_instance.id).first()
mock_get_current_revision.return_value = "rev2"
ProcessInstanceService.migrate_process_instance(process_instance, user=initiator_user)
process_instance = ProcessInstanceModel.query.filter_by(id=process_instance.id).first()
assert process_instance.bpmn_version_control_identifier == "rev2"
processor = ProcessInstanceProcessor(process_instance)
processor.do_engine_steps(save=True, execution_strategy_name="greedy")
target_bpmn_process_hash_one = process_instance.bpmn_process_definition.full_process_model_hash
assert target_bpmn_process_hash_one is not None
spiff_task = processor.__class__.get_task_by_bpmn_identifier("manual_task_two", processor.bpmn_process_instance)
assert spiff_task is not None
new_file_path = os.path.join(
app.instance_path,
"..",
"..",
"tests",
"data",
"migration-test-with-subprocess",
"migration-new-2.bpmn",
)
with open(new_file_path) as f:
new_contents = f.read().encode()
SpecFileService.update_file(
process_model_info=process_model,
file_name="migration-initial.bpmn",
binary_data=new_contents,
update_process_cache_only=True,
)
process_instance = ProcessInstanceModel.query.filter_by(id=process_instance.id).first()
mock_get_current_revision.return_value = "rev3"
ProcessInstanceService.migrate_process_instance(process_instance, user=initiator_user)
process_instance = ProcessInstanceModel.query.filter_by(id=process_instance.id).first()
assert process_instance.bpmn_version_control_identifier == "rev3"
processor = ProcessInstanceProcessor(process_instance)
processor.do_engine_steps(save=True, execution_strategy_name="greedy")
target_bpmn_process_hash_two = process_instance.bpmn_process_definition.full_process_model_hash
assert target_bpmn_process_hash_two is not None
spiff_task = processor.__class__.get_task_by_bpmn_identifier("manual_task_three", processor.bpmn_process_instance)
assert spiff_task is not None
ProcessInstanceService.migrate_process_instance(
process_instance, user=initiator_user, target_bpmn_process_hash=initial_bpmn_process_hash
)
processor = ProcessInstanceProcessor(process_instance)
spiff_task = processor.__class__.get_task_by_bpmn_identifier("manual_task_two", processor.bpmn_process_instance)
assert spiff_task is None
human_task_one = process_instance.active_human_tasks[0]
assert human_task_one.task_model.task_definition.bpmn_identifier == "manual_task_one"
self.complete_next_manual_task(processor)
assert process_instance.status == ProcessInstanceStatus.complete.value
process_instance = ProcessInstanceModel.query.filter_by(id=process_instance.id).first()
assert process_instance.bpmn_version_control_identifier == "rev1"
def test_it_can_check_if_a_process_instance_can_be_migrated(
self,
app: Flask,