unique bpmn process ids (#927)

* remove SpecReferenceCache

* make sure strings are sometimes unique

* lint

* more random

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

---------

Co-authored-by: burnettk <burnettk@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
This commit is contained in:
Kevin Burnett 2024-01-30 12:48:44 -08:00 committed by GitHub
parent 4cb1f33ac9
commit 711da2b048
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 17 additions and 92 deletions

View File

@ -23,9 +23,6 @@ from spiffworkflow_backend.models.principal import PrincipalModel # noqa: F401
from spiffworkflow_backend.models.human_task import HumanTaskModel # noqa: F401 from spiffworkflow_backend.models.human_task import HumanTaskModel # noqa: F401
from spiffworkflow_backend.models.spec_reference import (
SpecReferenceCache,
) # noqa: F401
from spiffworkflow_backend.models.cache_generation import ( from spiffworkflow_backend.models.cache_generation import (
CacheGenerationModel, CacheGenerationModel,
) # noqa: F401 ) # noqa: F401

View File

@ -59,7 +59,6 @@ class Reference:
return os.path.join(self.relative_location, self.file_name).replace("/", os.sep) return os.path.join(self.relative_location, self.file_name).replace("/", os.sep)
# SpecReferenceCache
class ReferenceCacheModel(SpiffworkflowBaseDBModel): class ReferenceCacheModel(SpiffworkflowBaseDBModel):
"""A cache of information about all the Processes and Decisions defined in all files.""" """A cache of information about all the Processes and Decisions defined in all files."""

View File

@ -1,85 +0,0 @@
from dataclasses import dataclass
from flask_marshmallow import Schema # type: ignore
from marshmallow import INCLUDE
from sqlalchemy import UniqueConstraint
from spiffworkflow_backend.models.db import SpiffworkflowBaseDBModel
from spiffworkflow_backend.models.db import db
class SpecReferenceNotFoundError(Exception):
pass
@dataclass()
class SpecReference:
"""File Reference Information.
Includes items such as the process id and name for a BPMN,
or the Decision id and Decision name for a DMN file. There may be more than
one reference that points to a particular file - if for instance, there are
three executable processes in a collaboration within a BPMN Diagram.
"""
identifier: str # The id of the process or decision. "Process_1234"
display_name: str # The name of the process or decision. "Invoice Submission"
process_model_id: str
type: str # can be 'process' or 'decision'
file_name: str # The name of the file where this process or decision is defined.
relative_path: str # The path to the file.
has_lanes: bool # If this is a process, whether it has lanes or not.
is_executable: bool # Whether this process or decision is designated as executable.
is_primary: bool # Whether this is the primary process of a process model
messages: dict # Any messages defined in the same file where this process is defined.
correlations: dict # Any correlations defined in the same file with this process.
start_messages: list # The names of any messages that would start this process.
called_element_ids: list # The element ids of any called elements
class SpecReferenceCache(SpiffworkflowBaseDBModel):
"""A cache of information about all the Processes and Decisions defined in all files."""
__tablename__ = "spec_reference_cache"
__table_args__ = (UniqueConstraint("identifier", "type", name="_identifier_type_unique"),)
id = db.Column(db.Integer, primary_key=True)
identifier = db.Column(db.String(255), index=True)
display_name = db.Column(db.String(255), index=True)
process_model_id = db.Column(db.String(255), index=True)
type = db.Column(db.String(255), index=True) # either 'process' or 'decision'
file_name = db.Column(db.String(255))
relative_path = db.Column(db.String(255))
has_lanes = db.Column(db.Boolean())
is_executable = db.Column(db.Boolean())
is_primary = db.Column(db.Boolean())
@classmethod
def from_spec_reference(cls, ref: SpecReference) -> "SpecReferenceCache":
return cls(
identifier=ref.identifier,
display_name=ref.display_name,
process_model_id=ref.process_model_id,
type=ref.type,
file_name=ref.file_name,
has_lanes=ref.has_lanes,
is_executable=ref.is_executable,
is_primary=ref.is_primary,
relative_path=ref.relative_path,
)
class SpecReferenceSchema(Schema): # type: ignore
class Meta:
model = SpecReference
fields = [
"identifier",
"display_name",
"process_group_id",
"process_model_id",
"type",
"file_name",
"has_lanes",
"is_executable",
"is_primary",
]
unknown = INCLUDE

View File

@ -1,7 +1,9 @@
"""APIs for dealing with process groups, process models, and process instances.""" """APIs for dealing with process groups, process models, and process instances."""
import json import json
import os import os
import random
import re import re
import string
from hashlib import sha256 from hashlib import sha256
from typing import Any from typing import Any
@ -83,6 +85,15 @@ def process_model_create(
with open(template_file) as f: with open(template_file) as f:
contents = f.read() contents = f.read()
process_model_id_for_bpmn_file = process_model_info.id.split("/")[-1] process_model_id_for_bpmn_file = process_model_info.id.split("/")[-1]
# convert dashes to underscores for process id
underscored_process_id = process_model_id_for_bpmn_file.replace("-", "_")
# make process id unique by adding random string to add
fuzz = "".join(random.SystemRandom().choice(string.ascii_lowercase + string.digits) for _ in range(7))
process_id_with_fuzz = f"Process_{underscored_process_id}_{fuzz}"
contents = contents.replace("Process_replace_me_just_for_template", process_id_with_fuzz)
SpecFileService.update_file(process_model_info, f"{process_model_id_for_bpmn_file}.bpmn", contents.encode()) SpecFileService.update_file(process_model_info, f"{process_model_id_for_bpmn_file}.bpmn", contents.encode())
_commit_and_push_to_git(f"User: {g.user.username} created process model {process_model_info.id}") _commit_and_push_to_git(f"User: {g.user.username} created process model {process_model_info.id}")

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?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:spiffworkflow="http://spiffworkflow.org/bpmn/schema/1.0/core" 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: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:spiffworkflow="http://spiffworkflow.org/bpmn/schema/1.0/core" 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_ynixgwm" isExecutable="true"> <bpmn:process id="Process_replace_me_just_for_template" isExecutable="true">
<bpmn:startEvent id="StartEvent_1"> <bpmn:startEvent id="StartEvent_1">
<bpmn:outgoing>Flow_17db3yp</bpmn:outgoing> <bpmn:outgoing>Flow_17db3yp</bpmn:outgoing>
</bpmn:startEvent> </bpmn:startEvent>
@ -27,7 +27,7 @@ You can also change the text you are reading here by updating the *Instructions*
</bpmn:manualTask> </bpmn:manualTask>
</bpmn:process> </bpmn:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1"> <bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_ynixgwm"> <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_replace_me_just_for_template">
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1"> <bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
<dc:Bounds x="179" y="159" width="36" height="36" /> <dc:Bounds x="179" y="159" width="36" height="36" />
</bpmndi:BPMNShape> </bpmndi:BPMNShape>

View File

@ -166,7 +166,7 @@ class TestNestedGroups(BaseTest):
data=json.dumps(ProcessGroupSchema().dump(process_group_c)), data=json.dumps(ProcessGroupSchema().dump(process_group_c)),
) )
def test_process_model_create( def test_process_model_create_nested(
self, self,
app: Flask, app: Flask,
client: FlaskClient, client: FlaskClient,

View File

@ -173,6 +173,9 @@ class TestProcessApi(BaseTest):
assert model_display_name == process_model.display_name assert model_display_name == process_model.display_name
assert 0 == process_model.display_order assert 0 == process_model.display_order
assert 1 == len(ProcessModelService.get_process_groups()) assert 1 == len(ProcessModelService.get_process_groups())
assert process_model.primary_file_name == f"{process_model_id}.bpmn"
assert process_model.primary_process_id
assert process_model.primary_process_id.startswith(f"Process_{process_model_id}")
# add bpmn file to the model # add bpmn file to the model
bpmn_file_name = "sample.bpmn" bpmn_file_name = "sample.bpmn"