This commit is contained in:
burnettk 2022-11-15 10:11:26 -05:00
parent 2b300d0d4e
commit fbc15c97fe
10 changed files with 102 additions and 89 deletions

View File

@ -1,5 +1,3 @@
from __future__ import with_statement
import logging import logging
from logging.config import fileConfig from logging.config import fileConfig

View File

@ -375,7 +375,7 @@ paths:
/processes: /processes:
get: get:
operationId: spiffworkflow_backend.routes.process_api_blueprint.process_list operationId: spiffworkflow_backend.routes.process_api_blueprint.process_list
summary: Return a list of all processes (not just primary process of a process model) summary: Return a list of all processes (not just primary process of a process model)
useful for finding processes for call activites. useful for finding processes for call activites.
tags: tags:
- Process Models - Process Models

View File

@ -63,8 +63,6 @@ CONTENT_TYPES = {
} }
@dataclass(order=True) @dataclass(order=True)
class File: class File:
"""File.""" """File."""
@ -131,6 +129,3 @@ class FileSchema(Schema):
references = marshmallow.fields.List( references = marshmallow.fields.List(
marshmallow.fields.Nested("SpecReferenceSchema") marshmallow.fields.Nested("SpecReferenceSchema")
) )

View File

@ -18,21 +18,21 @@ class SpecReference:
""" """
identifier: str # The id of the process or decision. "Process_1234" identifier: str # The id of the process or decision. "Process_1234"
display_name: str # The name of the process or decision. "Invoice Submission" display_name: str # The name of the process or decision. "Invoice Submission"
process_model_id: str process_model_id: str
type: str # can be 'process' or 'decision' type: str # can be 'process' or 'decision'
file_name: str # The name of the file where this process or decision is defined. file_name: str # The name of the file where this process or decision is defined.
relative_path: str # The path to the file. relative_path: str # The path to the file.
has_lanes: bool # If this is a process, whether it has lanes or not. 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_executable: bool # Whether this process or decision is designated as executable.
is_primary: bool # Whether this is the primary process of a process model 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. 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. 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. start_messages: list # The names of any messages that would start this process.
class SpecReferenceCache(SpiffworkflowBaseDBModel): class SpecReferenceCache(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."""
__tablename__ = "spec_reference_cache" __tablename__ = "spec_reference_cache"
@ -49,16 +49,18 @@ class SpecReferenceCache(SpiffworkflowBaseDBModel):
@classmethod @classmethod
def from_spec_reference(cls, ref): def from_spec_reference(cls, ref):
"""From_spec_reference."""
return cls( return cls(
identifier=ref.identifier, identifier=ref.identifier,
display_name=ref.display_name, display_name=ref.display_name,
process_model_id=ref.process_model_id, process_model_id=ref.process_model_id,
type=ref.type, type=ref.type,
file_name=ref.file_name, file_name=ref.file_name,
has_lanes=ref.has_lanes, has_lanes=ref.has_lanes,
is_executable=ref.is_executable, is_executable=ref.is_executable,
is_primary=ref.is_primary, is_primary=ref.is_primary,
relative_path=ref.relative_path,) relative_path=ref.relative_path,
)
class SpecReferenceSchema(Schema): class SpecReferenceSchema(Schema):
@ -68,8 +70,15 @@ class SpecReferenceSchema(Schema):
"""Meta.""" """Meta."""
model = SpecReference model = SpecReference
fields = ["identifier", "display_name", fields = [
"process_group_id", "process_model_id", "identifier",
"type", "file_name", "has_lanes", "display_name",
"is_executable", "is_primary"] "process_group_id",
unknown = INCLUDE "process_model_id",
"type",
"file_name",
"has_lanes",
"is_executable",
"is_primary",
]
unknown = INCLUDE

View File

@ -59,7 +59,8 @@ from spiffworkflow_backend.models.process_model import ProcessModelInfo
from spiffworkflow_backend.models.process_model import ProcessModelInfoSchema from spiffworkflow_backend.models.process_model import ProcessModelInfoSchema
from spiffworkflow_backend.models.secret_model import SecretModel from spiffworkflow_backend.models.secret_model import SecretModel
from spiffworkflow_backend.models.secret_model import SecretModelSchema from spiffworkflow_backend.models.secret_model import SecretModelSchema
from spiffworkflow_backend.models.spec_reference import SpecReferenceCache, SpecReferenceSchema from spiffworkflow_backend.models.spec_reference import SpecReferenceCache
from spiffworkflow_backend.models.spec_reference import SpecReferenceSchema
from spiffworkflow_backend.models.spiff_logging import SpiffLoggingModel from spiffworkflow_backend.models.spiff_logging import SpiffLoggingModel
from spiffworkflow_backend.models.spiff_step_details import SpiffStepDetailsModel from spiffworkflow_backend.models.spiff_step_details import SpiffStepDetailsModel
from spiffworkflow_backend.models.user import UserModel from spiffworkflow_backend.models.user import UserModel
@ -340,8 +341,8 @@ def process_model_list(
def process_list() -> any: def process_list() -> any:
"""Returns a list of all known processes - this includes processes that are not the """Returns a list of all known processes - this includes processes that are not the
primary process - helpful for finding possible call activities. """ primary process - helpful for finding possible call activities."""
references = SpecReferenceCache.query.filter_by(type = "process") references = SpecReferenceCache.query.filter_by(type="process")
return SpecReferenceSchema(many=True).dump(references) return SpecReferenceSchema(many=True).dump(references)

View File

@ -67,7 +67,6 @@ from SpiffWorkflow.util.deep_merge import DeepMerge # type: ignore
from spiffworkflow_backend.models.active_task import ActiveTaskModel from spiffworkflow_backend.models.active_task import ActiveTaskModel
from spiffworkflow_backend.models.active_task_user import ActiveTaskUserModel from spiffworkflow_backend.models.active_task_user import ActiveTaskUserModel
from spiffworkflow_backend.models.spec_reference import SpecReferenceCache
from spiffworkflow_backend.models.file import File from spiffworkflow_backend.models.file import File
from spiffworkflow_backend.models.file import FileType from spiffworkflow_backend.models.file import FileType
from spiffworkflow_backend.models.group import GroupModel from spiffworkflow_backend.models.group import GroupModel
@ -86,6 +85,7 @@ from spiffworkflow_backend.models.process_model import ProcessModelInfo
from spiffworkflow_backend.models.script_attributes_context import ( from spiffworkflow_backend.models.script_attributes_context import (
ScriptAttributesContext, ScriptAttributesContext,
) )
from spiffworkflow_backend.models.spec_reference import SpecReferenceCache
from spiffworkflow_backend.models.spiff_step_details import SpiffStepDetailsModel from spiffworkflow_backend.models.spiff_step_details import SpiffStepDetailsModel
from spiffworkflow_backend.models.user import UserModel from spiffworkflow_backend.models.user import UserModel
from spiffworkflow_backend.models.user import UserModelSchema from spiffworkflow_backend.models.user import UserModelSchema
@ -671,8 +671,9 @@ class ProcessInstanceProcessor:
return parser return parser
@staticmethod @staticmethod
def backfill_missing_spec_reference_records(bpmn_process_identifier: str) -> Optional[str]: def backfill_missing_spec_reference_records(
bpmn_process_identifier: str,
) -> Optional[str]:
"""Backfill_missing_spec_reference_records.""" """Backfill_missing_spec_reference_records."""
process_models = ProcessModelService().get_process_models() process_models = ProcessModelService().get_process_models()
for process_model in process_models: for process_model in process_models:
@ -695,11 +696,15 @@ class ProcessInstanceProcessor:
"bpmn_file_full_path_from_bpmn_process_identifier: bpmn_process_identifier is unexpectedly None" "bpmn_file_full_path_from_bpmn_process_identifier: bpmn_process_identifier is unexpectedly None"
) )
spec_reference = SpecReferenceCache.query.filter_by(identifier=bpmn_process_identifier).first() spec_reference = SpecReferenceCache.query.filter_by(
identifier=bpmn_process_identifier
).first()
bpmn_file_full_path = None bpmn_file_full_path = None
if spec_reference is None: if spec_reference is None:
bpmn_file_full_path = ProcessInstanceProcessor.backfill_missing_spec_reference_records( bpmn_file_full_path = (
bpmn_process_identifier ProcessInstanceProcessor.backfill_missing_spec_reference_records(
bpmn_process_identifier
)
) )
else: else:
bpmn_file_full_path = os.path.join( bpmn_file_full_path = os.path.join(

View File

@ -8,10 +8,9 @@ from typing import Optional
from flask_bpmn.models.db import db from flask_bpmn.models.db import db
from SpiffWorkflow.bpmn.parser.ValidationException import ValidationException # type: ignore from SpiffWorkflow.bpmn.parser.ValidationException import ValidationException # type: ignore
from spiffworkflow_backend.models.spec_reference import SpecReferenceCache
from spiffworkflow_backend.models.file import File from spiffworkflow_backend.models.file import File
from spiffworkflow_backend.models.file import SpecReference
from spiffworkflow_backend.models.file import FileType from spiffworkflow_backend.models.file import FileType
from spiffworkflow_backend.models.file import SpecReference
from spiffworkflow_backend.models.message_correlation_property import ( from spiffworkflow_backend.models.message_correlation_property import (
MessageCorrelationPropertyModel, MessageCorrelationPropertyModel,
) )
@ -20,6 +19,7 @@ from spiffworkflow_backend.models.message_triggerable_process_model import (
MessageTriggerableProcessModel, MessageTriggerableProcessModel,
) )
from spiffworkflow_backend.models.process_model import ProcessModelInfo from spiffworkflow_backend.models.process_model import ProcessModelInfo
from spiffworkflow_backend.models.spec_reference import SpecReferenceCache
from spiffworkflow_backend.services.custom_parser import MyCustomParser from spiffworkflow_backend.services.custom_parser import MyCustomParser
from spiffworkflow_backend.services.file_system_service import FileSystemService from spiffworkflow_backend.services.file_system_service import FileSystemService
from spiffworkflow_backend.services.process_model_service import ProcessModelService from spiffworkflow_backend.services.process_model_service import ProcessModelService
@ -55,7 +55,7 @@ class SpecFileService(FileSystemService):
@staticmethod @staticmethod
def reference_map(references: list[SpecReference]) -> dict[str, SpecReference]: def reference_map(references: list[SpecReference]) -> dict[str, SpecReference]:
""" Creates a dict with provided references organized by id. """ """Creates a dict with provided references organized by id."""
ref_map = {} ref_map = {}
for ref in references: for ref in references:
ref_map[ref.identifier] = ref ref_map[ref.identifier] = ref
@ -63,13 +63,15 @@ class SpecFileService(FileSystemService):
@staticmethod @staticmethod
def get_references(process_models: List[ProcessModelInfo]) -> list[SpecReference]: def get_references(process_models: List[ProcessModelInfo]) -> list[SpecReference]:
"""Returns all references -- process_ids, and decision ids, across all process models provided""" """Returns all references -- process_ids, and decision ids, across all process models provided."""
references = [] references = []
for process_model in process_models: for process_model in process_models:
references.extend(SpecFileService.get_references_for_process(process_model)) references.extend(SpecFileService.get_references_for_process(process_model))
@staticmethod @staticmethod
def get_references_for_process(process_model_info: ProcessModelInfo) -> list[SpecReference]: def get_references_for_process(
process_model_info: ProcessModelInfo,
) -> list[SpecReference]:
"""Get_references_for_process.""" """Get_references_for_process."""
files = SpecFileService.get_files(process_model_info) files = SpecFileService.get_files(process_model_info)
references = [] references = []
@ -80,7 +82,9 @@ class SpecFileService(FileSystemService):
return references return references
@staticmethod @staticmethod
def get_references_for_file(file: File, process_model_info: ProcessModelInfo) -> list[SpecReference]: def get_references_for_file(
file: File, process_model_info: ProcessModelInfo
) -> list[SpecReference]:
"""Uses spiffworkflow to parse BPMN and DMN files to determine how they can be externally referenced. """Uses spiffworkflow to parse BPMN and DMN files to determine how they can be externally referenced.
Returns a list of Reference objects that contain the type of reference, the id, the name. Returns a list of Reference objects that contain the type of reference, the id, the name.
@ -116,9 +120,11 @@ class SpecFileService(FileSystemService):
for sub_parser in sub_parsers: for sub_parser in sub_parsers:
if parser_type == "process": if parser_type == "process":
has_lanes = sub_parser.has_lanes() has_lanes = sub_parser.has_lanes()
executable = sub_parser.process_executable sub_parser.process_executable
start_messages = sub_parser.start_messages() start_messages = sub_parser.start_messages()
is_primary = sub_parser.get_id() == process_model_info.primary_process_id is_primary = (
sub_parser.get_id() == process_model_info.primary_process_id
)
references.append( references.append(
SpecReference( SpecReference(
@ -133,7 +139,7 @@ class SpecFileService(FileSystemService):
messages=messages, messages=messages,
is_primary=is_primary, is_primary=is_primary,
correlations=correlations, correlations=correlations,
start_messages=start_messages start_messages=start_messages,
) )
) )
return references return references
@ -166,11 +172,12 @@ class SpecFileService(FileSystemService):
if ref.is_primary: if ref.is_primary:
ProcessModelService().update_spec( ProcessModelService().update_spec(
process_model_info, { process_model_info,
{
"primary_process_id": ref.identifier, "primary_process_id": ref.identifier,
"primary_file_name": file_name, "primary_file_name": file_name,
"is_review": ref.has_lanes, "is_review": ref.has_lanes,
} },
) )
SpecFileService.update_process_cache(ref) SpecFileService.update_process_cache(ref)
SpecFileService.update_message_cache(ref) SpecFileService.update_message_cache(ref)
@ -229,7 +236,10 @@ class SpecFileService(FileSystemService):
@staticmethod @staticmethod
def update_process_cache(ref: SpecReference) -> None: def update_process_cache(ref: SpecReference) -> None:
process_id_lookup = SpecReferenceCache.query.filter_by(identifier=ref.identifier).first() """Update_process_cache."""
process_id_lookup = SpecReferenceCache.query.filter_by(
identifier=ref.identifier
).first()
if process_id_lookup is None: if process_id_lookup is None:
process_id_lookup = SpecReferenceCache.from_spec_reference(ref) process_id_lookup = SpecReferenceCache.from_spec_reference(ref)
db.session.add(process_id_lookup) db.session.add(process_id_lookup)
@ -246,9 +256,7 @@ class SpecFileService(FileSystemService):
f"{process_id_lookup.relative_path}. It cannot be reused." f"{process_id_lookup.relative_path}. It cannot be reused."
) )
else: else:
process_id_lookup.relative_path = ( process_id_lookup.relative_path = ref.relative_path
ref.relative_path
)
db.session.add(process_id_lookup) db.session.add(process_id_lookup)
db.session.commit() db.session.commit()
@ -269,7 +277,7 @@ class SpecFileService(FileSystemService):
@staticmethod @staticmethod
def update_message_trigger_cache( def update_message_trigger_cache(
ref: SpecReference, process_model_info: ProcessModelInfo ref: SpecReference, process_model_info: ProcessModelInfo
) -> None: ) -> None:
"""Assure we know which messages can trigger the start of a process.""" """Assure we know which messages can trigger the start of a process."""
for message_model_identifier in ref.start_messages: for message_model_identifier in ref.start_messages:

View File

@ -9,8 +9,6 @@ import pytest
from flask.app import Flask from flask.app import Flask
from flask.testing import FlaskClient from flask.testing import FlaskClient
from flask_bpmn.models.db import db from flask_bpmn.models.db import db
from spiffworkflow_backend.models.spec_reference import SpecReferenceCache
from tests.spiffworkflow_backend.helpers.base_test import BaseTest from tests.spiffworkflow_backend.helpers.base_test import BaseTest
from tests.spiffworkflow_backend.helpers.test_data import load_test_spec from tests.spiffworkflow_backend.helpers.test_data import load_test_spec
@ -27,6 +25,7 @@ from spiffworkflow_backend.models.process_instance_report import (
) )
from spiffworkflow_backend.models.process_model import NotificationType from spiffworkflow_backend.models.process_model import NotificationType
from spiffworkflow_backend.models.process_model import ProcessModelInfoSchema from spiffworkflow_backend.models.process_model import ProcessModelInfoSchema
from spiffworkflow_backend.models.spec_reference import SpecReferenceCache
from spiffworkflow_backend.models.user import UserModel from spiffworkflow_backend.models.user import UserModel
from spiffworkflow_backend.services.authorization_service import AuthorizationService from spiffworkflow_backend.services.authorization_service import AuthorizationService
from spiffworkflow_backend.services.file_system_service import FileSystemService from spiffworkflow_backend.services.file_system_service import FileSystemService
@ -441,45 +440,49 @@ class TestProcessApi(BaseTest):
assert response.json["pagination"]["total"] == 5 assert response.json["pagination"]["total"] == 5
assert response.json["pagination"]["pages"] == 2 assert response.json["pagination"]["pages"] == 2
def test_process_list(self, def test_process_list(
app: Flask, self,
client: FlaskClient, app: Flask,
with_db_and_bpmn_file_cleanup: None, client: FlaskClient,
with_super_admin_user: UserModel, with_db_and_bpmn_file_cleanup: None,
): with_super_admin_user: UserModel,
):
"""It should be possible to get a list of all processes known to the system.""" """It should be possible to get a list of all processes known to the system."""
load_test_spec( load_test_spec(
"test_group_one/simple_form", "test_group_one/simple_form",
process_model_source_directory='simple_form', process_model_source_directory="simple_form",
bpmn_file_name='simple_form' bpmn_file_name="simple_form",
) )
# When adding a process model with one Process, no decisions, and some json files, only one process is recorded. # When adding a process model with one Process, no decisions, and some json files, only one process is recorded.
assert(len(SpecReferenceCache.query.all()) == 1) assert len(SpecReferenceCache.query.all()) == 1
self.create_group_and_model_with_bpmn( self.create_group_and_model_with_bpmn(
client=client, client=client,
user=with_super_admin_user, user=with_super_admin_user,
process_group_id='test_group_two', process_group_id="test_group_two",
process_model_id='call_activity_nested', process_model_id="call_activity_nested",
bpmn_file_location='call_activity_nested' bpmn_file_location="call_activity_nested",
) )
# When adding a process model with 4 processes and a decision, 5 new records will be in the Cache # When adding a process model with 4 processes and a decision, 5 new records will be in the Cache
assert(len(SpecReferenceCache.query.all()) == 6) assert len(SpecReferenceCache.query.all()) == 6
# get the results # get the results
response = client.get("/v1.0/processes", headers=self.logged_in_headers(with_super_admin_user), response = client.get(
"/v1.0/processes",
headers=self.logged_in_headers(with_super_admin_user),
) )
assert response.json is not None assert response.json is not None
# We should get 5 back, as one of the items in the cache is a decision. # We should get 5 back, as one of the items in the cache is a decision.
assert len(response.json) == 5 assert len(response.json) == 5
simple_form = next(p for p in response.json if p['identifier'] == 'Proccess_WithForm') simple_form = next(
assert(simple_form['display_name'] == 'Process With Form') p for p in response.json if p["identifier"] == "Proccess_WithForm"
assert(simple_form['process_model_id'] == 'test_group_one/simple_form') )
assert(simple_form['has_lanes'] == False) assert simple_form["display_name"] == "Process With Form"
assert(simple_form['is_executable'] == True) assert simple_form["process_model_id"] == "test_group_one/simple_form"
assert(simple_form['is_primary'] == True) assert simple_form["has_lanes"] == False
assert simple_form["is_executable"] == True
assert simple_form["is_primary"] == True
def test_process_group_add( def test_process_group_add(
self, self,

View File

@ -5,8 +5,8 @@ from flask_bpmn.models.db import db
from tests.spiffworkflow_backend.helpers.base_test import BaseTest from tests.spiffworkflow_backend.helpers.base_test import BaseTest
from tests.spiffworkflow_backend.helpers.test_data import load_test_spec from tests.spiffworkflow_backend.helpers.test_data import load_test_spec
from spiffworkflow_backend.models.spec_reference import SpecReferenceCache
from spiffworkflow_backend.models.process_model import ProcessModelInfo from spiffworkflow_backend.models.process_model import ProcessModelInfo
from spiffworkflow_backend.models.spec_reference import SpecReferenceCache
from spiffworkflow_backend.models.user import UserModel from spiffworkflow_backend.models.user import UserModel
from spiffworkflow_backend.services.process_instance_processor import ( from spiffworkflow_backend.services.process_instance_processor import (
ProcessInstanceProcessor, ProcessInstanceProcessor,

View File

@ -69,10 +69,7 @@ class TestSpecFileService(BaseTest):
) )
bpmn_process_id_lookups = SpecReferenceCache.query.all() bpmn_process_id_lookups = SpecReferenceCache.query.all()
assert len(bpmn_process_id_lookups) == 1 assert len(bpmn_process_id_lookups) == 1
assert ( assert bpmn_process_id_lookups[0].identifier == bpmn_process_identifier
bpmn_process_id_lookups[0].identifier
== bpmn_process_identifier
)
assert ( assert (
bpmn_process_id_lookups[0].relative_path bpmn_process_id_lookups[0].relative_path
== self.call_activity_nested_relative_file_path == self.call_activity_nested_relative_file_path
@ -115,10 +112,7 @@ class TestSpecFileService(BaseTest):
bpmn_process_id_lookups = SpecReferenceCache.query.all() bpmn_process_id_lookups = SpecReferenceCache.query.all()
assert len(bpmn_process_id_lookups) == 1 assert len(bpmn_process_id_lookups) == 1
assert ( assert bpmn_process_id_lookups[0].identifier == bpmn_process_identifier
bpmn_process_id_lookups[0].identifier
== bpmn_process_identifier
)
assert ( assert (
bpmn_process_id_lookups[0].relative_path bpmn_process_id_lookups[0].relative_path
== self.call_activity_nested_relative_file_path == self.call_activity_nested_relative_file_path