Merge pull request #98 from sartography/feature/add_some_xml_validations

Feature/add some xml validations
This commit is contained in:
jasquat 2023-01-10 14:31:18 -05:00 committed by GitHub
commit bd2ec3d148
14 changed files with 206 additions and 102 deletions

View File

@ -397,6 +397,12 @@ paths:
description: the modified process model id description: the modified process model id
schema: schema:
type: string type: string
- name: include_file_references
in: query
required: false
description: include all file references in the return
schema:
type: boolean
get: get:
operationId: spiffworkflow_backend.routes.process_models_controller.process_model_show operationId: spiffworkflow_backend.routes.process_models_controller.process_model_show
summary: Returns a single process model summary: Returns a single process model

View File

@ -15,6 +15,7 @@ from flask import jsonify
from flask import make_response from flask import make_response
from flask.wrappers import Response from flask.wrappers import Response
from flask_bpmn.api.api_error import ApiError from flask_bpmn.api.api_error import ApiError
from werkzeug.datastructures import FileStorage
from spiffworkflow_backend.interfaces import IdToProcessGroupMapping from spiffworkflow_backend.interfaces import IdToProcessGroupMapping
from spiffworkflow_backend.models.file import FileSchema from spiffworkflow_backend.models.file import FileSchema
@ -38,6 +39,9 @@ from spiffworkflow_backend.services.process_instance_report_service import (
ProcessInstanceReportService, ProcessInstanceReportService,
) )
from spiffworkflow_backend.services.process_model_service import ProcessModelService from spiffworkflow_backend.services.process_model_service import ProcessModelService
from spiffworkflow_backend.services.spec_file_service import (
ProcessModelFileInvalidError,
)
from spiffworkflow_backend.services.spec_file_service import SpecFileService from spiffworkflow_backend.services.spec_file_service import SpecFileService
@ -119,7 +123,9 @@ def process_model_update(
return ProcessModelInfoSchema().dump(process_model) return ProcessModelInfoSchema().dump(process_model)
def process_model_show(modified_process_model_identifier: str) -> Any: def process_model_show(
modified_process_model_identifier: str, include_file_references: bool = False
) -> Any:
"""Process_model_show.""" """Process_model_show."""
process_model_identifier = modified_process_model_identifier.replace(":", "/") process_model_identifier = modified_process_model_identifier.replace(":", "/")
process_model = _get_process_model(process_model_identifier) process_model = _get_process_model(process_model_identifier)
@ -128,8 +134,12 @@ def process_model_show(modified_process_model_identifier: str) -> Any:
key=lambda f: "" if f.name == process_model.primary_file_name else f.sort_index, key=lambda f: "" if f.name == process_model.primary_file_name else f.sort_index,
) )
process_model.files = files process_model.files = files
for file in process_model.files:
file.references = SpecFileService.get_references_for_file(file, process_model) if include_file_references:
for file in process_model.files:
file.references = SpecFileService.get_references_for_file(
file, process_model
)
process_model.parent_groups = ProcessModelService.get_parent_group_array( process_model.parent_groups = ProcessModelService.get_parent_group_array(
process_model.id process_model.id
@ -222,26 +232,11 @@ def process_model_file_update(
modified_process_model_identifier: str, file_name: str modified_process_model_identifier: str, file_name: str
) -> flask.wrappers.Response: ) -> flask.wrappers.Response:
"""Process_model_file_update.""" """Process_model_file_update."""
process_model_identifier = modified_process_model_identifier.replace(":", "/") message = f"User: {g.user.username} clicked save for"
process_model = _get_process_model(process_model_identifier) return _create_or_update_process_model_file(
modified_process_model_identifier, message, 200
request_file = _get_file_from_request()
request_file_contents = request_file.stream.read()
if not request_file_contents:
raise ApiError(
error_code="file_contents_empty",
message="Given request file does not have any content",
status_code=400,
)
SpecFileService.update_file(process_model, file_name, request_file_contents)
_commit_and_push_to_git(
f"User: {g.user.username} clicked save for"
f" {process_model_identifier}/{file_name}"
) )
return Response(json.dumps({"ok": True}), status=200, mimetype="application/json")
def process_model_file_delete( def process_model_file_delete(
modified_process_model_identifier: str, file_name: str modified_process_model_identifier: str, file_name: str
@ -271,28 +266,9 @@ def process_model_file_create(
modified_process_model_identifier: str, modified_process_model_identifier: str,
) -> flask.wrappers.Response: ) -> flask.wrappers.Response:
"""Process_model_file_create.""" """Process_model_file_create."""
process_model_identifier = modified_process_model_identifier.replace(":", "/") message = f"User: {g.user.username} added process model file"
process_model = _get_process_model(process_model_identifier) return _create_or_update_process_model_file(
request_file = _get_file_from_request() modified_process_model_identifier, message, 201
if not request_file.filename:
raise ApiError(
error_code="could_not_get_filename",
message="Could not get filename from request",
status_code=400,
)
file = SpecFileService.add_file(
process_model, request_file.filename, request_file.stream.read()
)
file_contents = SpecFileService.get_data(process_model, file.name)
file.file_contents = file_contents
file.process_model_id = process_model.id
_commit_and_push_to_git(
f"User: {g.user.username} added process model file"
f" {process_model_identifier}/{file.name}"
)
return Response(
json.dumps(FileSchema().dump(file)), status=201, mimetype="application/json"
) )
@ -462,9 +438,9 @@ def process_model_create_with_natural_language(
) )
def _get_file_from_request() -> Any: def _get_file_from_request() -> FileStorage:
"""Get_file_from_request.""" """Get_file_from_request."""
request_file = connexion.request.files.get("file") request_file: FileStorage = connexion.request.files.get("file")
if not request_file: if not request_file:
raise ApiError( raise ApiError(
error_code="no_file_given", error_code="no_file_given",
@ -502,3 +478,58 @@ def _get_process_group_from_modified_identifier(
status_code=400, status_code=400,
) )
return process_group return process_group
def _create_or_update_process_model_file(
modified_process_model_identifier: str,
message_for_git_commit: str,
http_status_to_return: int,
) -> flask.wrappers.Response:
"""_create_or_update_process_model_file."""
process_model_identifier = modified_process_model_identifier.replace(":", "/")
process_model = _get_process_model(process_model_identifier)
request_file = _get_file_from_request()
# for mypy
request_file_contents = request_file.stream.read()
if not request_file_contents:
raise ApiError(
error_code="file_contents_empty",
message="Given request file does not have any content",
status_code=400,
)
if not request_file.filename:
raise ApiError(
error_code="could_not_get_filename",
message="Could not get filename from request",
status_code=400,
)
file = None
try:
file = SpecFileService.update_file(
process_model, request_file.filename, request_file_contents
)
except ProcessModelFileInvalidError as exception:
raise (
ApiError(
error_code="process_model_file_invalid",
message=(
f"Invalid Process model file cannot be save: {request_file.name}."
f" Received error: {str(exception)}"
),
status_code=400,
)
) from exception
file_contents = SpecFileService.get_data(process_model, file.name)
file.file_contents = file_contents
file.process_model_id = process_model.id
_commit_and_push_to_git(
f"{message_for_git_commit} {process_model_identifier}/{file.name}"
)
return Response(
json.dumps(FileSchema().dump(file)),
status=http_status_to_return,
mimetype="application/json",
)

View File

@ -6,7 +6,8 @@ from typing import List
from typing import Optional 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 lxml import etree # type: ignore
from SpiffWorkflow.bpmn.parser.BpmnParser import BpmnValidator # type: ignore
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
@ -29,6 +30,10 @@ class ProcessModelFileNotFoundError(Exception):
"""ProcessModelFileNotFoundError.""" """ProcessModelFileNotFoundError."""
class ProcessModelFileInvalidError(Exception):
"""ProcessModelFileInvalidError."""
class SpecFileService(FileSystemService): class SpecFileService(FileSystemService):
"""SpecFileService.""" """SpecFileService."""
@ -44,7 +49,6 @@ class SpecFileService(FileSystemService):
extension_filter: str = "", extension_filter: str = "",
) -> List[File]: ) -> List[File]:
"""Return all files associated with a workflow specification.""" """Return all files associated with a workflow specification."""
# path = SpecFileService.workflow_path(process_model_info)
path = os.path.join( path = os.path.join(
FileSystemService.root_path(), process_model_info.id_for_file_path() FileSystemService.root_path(), process_model_info.id_for_file_path()
) )
@ -76,9 +80,22 @@ class SpecFileService(FileSystemService):
) )
return references return references
@staticmethod @classmethod
def get_references_for_file( def get_references_for_file(
file: File, process_model_info: ProcessModelInfo cls, file: File, process_model_info: ProcessModelInfo
) -> list[SpecReference]:
"""Get_references_for_file."""
full_file_path = SpecFileService.full_file_path(process_model_info, file.name)
file_contents: bytes = b""
with open(full_file_path) as f:
file_contents = f.read().encode()
return cls.get_references_for_file_contents(
process_model_info, file.name, file_contents
)
@classmethod
def get_references_for_file_contents(
cls, process_model_info: ProcessModelInfo, file_name: str, binary_data: bytes
) -> list[SpecReference]: ) -> 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.
@ -89,8 +106,8 @@ class SpecFileService(FileSystemService):
type = {str} 'process' / 'decision' type = {str} 'process' / 'decision'
""" """
references: list[SpecReference] = [] references: list[SpecReference] = []
full_file_path = SpecFileService.full_file_path(process_model_info, file.name) file_path = os.path.join(process_model_info.id_for_file_path(), file_name)
file_path = os.path.join(process_model_info.id_for_file_path(), file.name) file_type = FileSystemService.file_type(file_name)
parser = MyCustomParser() parser = MyCustomParser()
parser_type = None parser_type = None
sub_parser = None sub_parser = None
@ -100,14 +117,14 @@ class SpecFileService(FileSystemService):
messages = {} messages = {}
correlations = {} correlations = {}
start_messages = [] start_messages = []
if file.type == FileType.bpmn.value: if file_type.value == FileType.bpmn.value:
parser.add_bpmn_file(full_file_path) parser.add_bpmn_xml(etree.fromstring(binary_data))
parser_type = "process" parser_type = "process"
sub_parsers = list(parser.process_parsers.values()) sub_parsers = list(parser.process_parsers.values())
messages = parser.messages messages = parser.messages
correlations = parser.correlations correlations = parser.correlations
elif file.type == FileType.dmn.value: elif file_type.value == FileType.dmn.value:
parser.add_dmn_file(full_file_path) parser.add_dmn_xml(etree.fromstring(binary_data))
sub_parsers = list(parser.dmn_parsers.values()) sub_parsers = list(parser.dmn_parsers.values())
parser_type = "decision" parser_type = "decision"
else: else:
@ -127,7 +144,7 @@ class SpecFileService(FileSystemService):
display_name=sub_parser.get_name(), display_name=sub_parser.get_name(),
process_model_id=process_model_info.id, process_model_id=process_model_info.id,
type=parser_type, type=parser_type,
file_name=file.name, file_name=file_name,
relative_path=file_path, relative_path=file_path,
has_lanes=has_lanes, has_lanes=has_lanes,
is_executable=is_executable, is_executable=is_executable,
@ -147,23 +164,36 @@ class SpecFileService(FileSystemService):
# Same as update # Same as update
return SpecFileService.update_file(process_model_info, file_name, binary_data) return SpecFileService.update_file(process_model_info, file_name, binary_data)
@staticmethod @classmethod
def validate_bpmn_xml(cls, file_name: str, binary_data: bytes) -> None:
"""Validate_bpmn_xml."""
file_type = FileSystemService.file_type(file_name)
if file_type.value == FileType.bpmn.value:
validator = BpmnValidator()
parser = MyCustomParser(validator=validator)
try:
parser.add_bpmn_xml(etree.fromstring(binary_data), filename=file_name)
except etree.XMLSyntaxError as exception:
raise ProcessModelFileInvalidError(
f"Received error trying to parse bpmn xml: {str(exception)}"
) from exception
@classmethod
def update_file( def update_file(
process_model_info: ProcessModelInfo, file_name: str, binary_data: bytes cls, process_model_info: ProcessModelInfo, file_name: str, binary_data: bytes
) -> File: ) -> File:
"""Update_file.""" """Update_file."""
SpecFileService.assert_valid_file_name(file_name) SpecFileService.assert_valid_file_name(file_name)
full_file_path = SpecFileService.full_file_path(process_model_info, file_name) cls.validate_bpmn_xml(file_name, binary_data)
SpecFileService.write_file_data_to_system(full_file_path, binary_data)
file = SpecFileService.to_file_object(file_name, full_file_path)
references = SpecFileService.get_references_for_file(file, process_model_info) references = cls.get_references_for_file_contents(
process_model_info, file_name, binary_data
)
primary_process_ref = next( primary_process_ref = next(
(ref for ref in references if ref.is_primary and ref.is_executable), None (ref for ref in references if ref.is_primary and ref.is_executable), None
) )
SpecFileService.clear_caches_for_file(file_name, process_model_info) SpecFileService.clear_caches_for_file(file_name, process_model_info)
for ref in references: for ref in references:
# If no valid primary process is defined, default to the first process in the # If no valid primary process is defined, default to the first process in the
# updated file. # updated file.
@ -184,7 +214,11 @@ class SpecFileService(FileSystemService):
update_hash, update_hash,
) )
SpecFileService.update_caches(ref) SpecFileService.update_caches(ref)
return file
# make sure we save the file as the last thing we do to ensure validations have run
full_file_path = SpecFileService.full_file_path(process_model_info, file_name)
SpecFileService.write_file_data_to_system(full_file_path, binary_data)
return SpecFileService.to_file_object(file_name, full_file_path)
@staticmethod @staticmethod
def get_data(process_model_info: ProcessModelInfo, file_name: str) -> bytes: def get_data(process_model_info: ProcessModelInfo, file_name: str) -> bytes:
@ -282,7 +316,7 @@ class SpecFileService(FileSystemService):
# if the old relative bpmn file no longer exists, then assume things were moved around # if the old relative bpmn file no longer exists, then assume things were moved around
# on the file system. Otherwise, assume it is a duplicate process id and error. # on the file system. Otherwise, assume it is a duplicate process id and error.
if os.path.isfile(full_bpmn_file_path): if os.path.isfile(full_bpmn_file_path):
raise ValidationException( raise ProcessModelFileInvalidError(
f"Process id ({ref.identifier}) has already been used for " f"Process id ({ref.identifier}) has already been used for "
f"{process_id_lookup.relative_path}. It cannot be reused." f"{process_id_lookup.relative_path}. It cannot be reused."
) )
@ -314,7 +348,7 @@ class SpecFileService(FileSystemService):
identifier=message_model_identifier identifier=message_model_identifier
).first() ).first()
if message_model is None: if message_model is None:
raise ValidationException( raise ProcessModelFileInvalidError(
"Could not find message model with identifier" "Could not find message model with identifier"
f" '{message_model_identifier}'Required by a Start Event in :" f" '{message_model_identifier}'Required by a Start Event in :"
f" {ref.file_name}" f" {ref.file_name}"
@ -336,7 +370,7 @@ class SpecFileService(FileSystemService):
message_triggerable_process_model.process_model_identifier message_triggerable_process_model.process_model_identifier
!= ref.process_model_id != ref.process_model_id
): ):
raise ValidationException( raise ProcessModelFileInvalidError(
"Message model is already used to start process model" "Message model is already used to start process model"
f" {ref.process_model_id}" f" {ref.process_model_id}"
) )
@ -355,7 +389,7 @@ class SpecFileService(FileSystemService):
identifier=message_model_identifier identifier=message_model_identifier
).first() ).first()
if message_model is None: if message_model is None:
raise ValidationException( raise ProcessModelFileInvalidError(
"Could not find message model with identifier" "Could not find message model with identifier"
f" '{message_model_identifier}'specified by correlation" f" '{message_model_identifier}'specified by correlation"
f" property: {cpre}" f" property: {cpre}"

View File

@ -2,10 +2,10 @@
<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" xmlns:spiffworkflow="http://spiffworkflow.org/bpmn/schema/1.0/core" 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:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:spiffworkflow="http://spiffworkflow.org/bpmn/schema/1.0/core" id="Definitions_96f6665" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="3.0.0-dev">
<bpmn:correlationProperty id="message_correlation_property" name="Message Correlation Property"> <bpmn:correlationProperty id="message_correlation_property" name="Message Correlation Property">
<bpmn:correlationPropertyRetrievalExpression messageRef="message_send"> <bpmn:correlationPropertyRetrievalExpression messageRef="message_send">
<bpmn:formalExpression>to</bpmn:formalExpression> <bpmn:messagePath>to</bpmn:messagePath>
</bpmn:correlationPropertyRetrievalExpression> </bpmn:correlationPropertyRetrievalExpression>
<bpmn:correlationPropertyRetrievalExpression messageRef="message_response"> <bpmn:correlationPropertyRetrievalExpression messageRef="message_response">
<bpmn:formalExpression>from.name</bpmn:formalExpression> <bpmn:messagePath>from.name</bpmn:messagePath>
</bpmn:correlationPropertyRetrievalExpression> </bpmn:correlationPropertyRetrievalExpression>
</bpmn:correlationProperty> </bpmn:correlationProperty>
<bpmn:message id="message_send" name="Message Send"> <bpmn:message id="message_send" name="Message Send">
@ -20,7 +20,7 @@
</bpmn:message> </bpmn:message>
<bpmn:correlationProperty id="correlation_property_one" name="Correlation Property One"> <bpmn:correlationProperty id="correlation_property_one" name="Correlation Property One">
<bpmn:correlationPropertyRetrievalExpression messageRef="message_send"> <bpmn:correlationPropertyRetrievalExpression messageRef="message_send">
<bpmn:formalExpression>new</bpmn:formalExpression> <bpmn:messagePath>new</bpmn:messagePath>
</bpmn:correlationPropertyRetrievalExpression> </bpmn:correlationPropertyRetrievalExpression>
</bpmn:correlationProperty> </bpmn:correlationProperty>
<bpmn:process id="test_dot_notation" name="Test Dot Notation" isExecutable="true"> <bpmn:process id="test_dot_notation" name="Test Dot Notation" isExecutable="true">

View File

@ -12,18 +12,18 @@
</bpmn:collaboration> </bpmn:collaboration>
<bpmn:correlationProperty id="message_correlation_property_topica" name="Message Correlation Property TopicA"> <bpmn:correlationProperty id="message_correlation_property_topica" name="Message Correlation Property TopicA">
<bpmn:correlationPropertyRetrievalExpression messageRef="message_send"> <bpmn:correlationPropertyRetrievalExpression messageRef="message_send">
<bpmn:formalExpression>topica</bpmn:formalExpression> <bpmn:messagePath>topica</bpmn:messagePath>
</bpmn:correlationPropertyRetrievalExpression> </bpmn:correlationPropertyRetrievalExpression>
<bpmn:correlationPropertyRetrievalExpression messageRef="message_response"> <bpmn:correlationPropertyRetrievalExpression messageRef="message_response">
<bpmn:formalExpression>the_payload.topica</bpmn:formalExpression> <bpmn:messagePath>the_payload.topica</bpmn:messagePath>
</bpmn:correlationPropertyRetrievalExpression> </bpmn:correlationPropertyRetrievalExpression>
</bpmn:correlationProperty> </bpmn:correlationProperty>
<bpmn:correlationProperty id="message_correlation_property_topicb" name="Message Correlation Property TopicB"> <bpmn:correlationProperty id="message_correlation_property_topicb" name="Message Correlation Property TopicB">
<bpmn:correlationPropertyRetrievalExpression messageRef="message_send"> <bpmn:correlationPropertyRetrievalExpression messageRef="message_send">
<bpmn:formalExpression>topicb</bpmn:formalExpression> <bpmn:messagePath>topicb</bpmn:messagePath>
</bpmn:correlationPropertyRetrievalExpression> </bpmn:correlationPropertyRetrievalExpression>
<bpmn:correlationPropertyRetrievalExpression messageRef="message_response"> <bpmn:correlationPropertyRetrievalExpression messageRef="message_response">
<bpmn:formalExpression>the_payload.topicb</bpmn:formalExpression> <bpmn:messagePath>the_payload.topicb</bpmn:messagePath>
</bpmn:correlationPropertyRetrievalExpression> </bpmn:correlationPropertyRetrievalExpression>
</bpmn:correlationProperty> </bpmn:correlationProperty>
<bpmn:message id="message_send" name="Message Send"> <bpmn:message id="message_send" name="Message Send">

View File

@ -12,18 +12,18 @@
</bpmn:collaboration> </bpmn:collaboration>
<bpmn:correlationProperty id="message_correlation_property_topica" name="Message Correlation Property TopicA"> <bpmn:correlationProperty id="message_correlation_property_topica" name="Message Correlation Property TopicA">
<bpmn:correlationPropertyRetrievalExpression messageRef="message_send"> <bpmn:correlationPropertyRetrievalExpression messageRef="message_send">
<bpmn:formalExpression>topica</bpmn:formalExpression> <bpmn:messagePath>topica</bpmn:messagePath>
</bpmn:correlationPropertyRetrievalExpression> </bpmn:correlationPropertyRetrievalExpression>
<bpmn:correlationPropertyRetrievalExpression messageRef="message_response"> <bpmn:correlationPropertyRetrievalExpression messageRef="message_response">
<bpmn:formalExpression>the_payload.topica</bpmn:formalExpression> <bpmn:messagePath>the_payload.topica</bpmn:messagePath>
</bpmn:correlationPropertyRetrievalExpression> </bpmn:correlationPropertyRetrievalExpression>
</bpmn:correlationProperty> </bpmn:correlationProperty>
<bpmn:correlationProperty id="message_correlation_property_topicb" name="Message Correlation Property TopicB"> <bpmn:correlationProperty id="message_correlation_property_topicb" name="Message Correlation Property TopicB">
<bpmn:correlationPropertyRetrievalExpression messageRef="message_send"> <bpmn:correlationPropertyRetrievalExpression messageRef="message_send">
<bpmn:formalExpression>topicb</bpmn:formalExpression> <bpmn:messagePath>topicb</bpmn:messagePath>
</bpmn:correlationPropertyRetrievalExpression> </bpmn:correlationPropertyRetrievalExpression>
<bpmn:correlationPropertyRetrievalExpression messageRef="message_response"> <bpmn:correlationPropertyRetrievalExpression messageRef="message_response">
<bpmn:formalExpression>the_payload.topicb</bpmn:formalExpression> <bpmn:messagePath>the_payload.topicb</bpmn:messagePath>
</bpmn:correlationPropertyRetrievalExpression> </bpmn:correlationPropertyRetrievalExpression>
</bpmn:correlationProperty> </bpmn:correlationProperty>
<bpmn:process id="message_send_process" name="Message Send Process" isExecutable="true"> <bpmn:process id="message_send_process" name="Message Send Process" isExecutable="true">

View File

@ -12,18 +12,18 @@
</bpmn:collaboration> </bpmn:collaboration>
<bpmn:correlationProperty id="mcp_topica_one" name="MCP TopicA One"> <bpmn:correlationProperty id="mcp_topica_one" name="MCP TopicA One">
<bpmn:correlationPropertyRetrievalExpression messageRef="message_send_one"> <bpmn:correlationPropertyRetrievalExpression messageRef="message_send_one">
<bpmn:formalExpression>topica_one</bpmn:formalExpression> <bpmn:messagePath>topica_one</bpmn:messagePath>
</bpmn:correlationPropertyRetrievalExpression> </bpmn:correlationPropertyRetrievalExpression>
<bpmn:correlationPropertyRetrievalExpression messageRef="message_response_one"> <bpmn:correlationPropertyRetrievalExpression messageRef="message_response_one">
<bpmn:formalExpression>topica_one</bpmn:formalExpression> <bpmn:messagePath>topica_one</bpmn:messagePath>
</bpmn:correlationPropertyRetrievalExpression> </bpmn:correlationPropertyRetrievalExpression>
</bpmn:correlationProperty> </bpmn:correlationProperty>
<bpmn:correlationProperty id="mcp_topicb_one" name="MCP TopicB_one"> <bpmn:correlationProperty id="mcp_topicb_one" name="MCP TopicB_one">
<bpmn:correlationPropertyRetrievalExpression messageRef="message_send_one"> <bpmn:correlationPropertyRetrievalExpression messageRef="message_send_one">
<bpmn:formalExpression>topicb_one</bpmn:formalExpression> <bpmn:messagePath>topicb_one</bpmn:messagePath>
</bpmn:correlationPropertyRetrievalExpression> </bpmn:correlationPropertyRetrievalExpression>
<bpmn:correlationPropertyRetrievalExpression messageRef="message_response_one"> <bpmn:correlationPropertyRetrievalExpression messageRef="message_response_one">
<bpmn:formalExpression>topicb_one</bpmn:formalExpression> <bpmn:messagePath>topicb_one</bpmn:messagePath>
</bpmn:correlationPropertyRetrievalExpression> </bpmn:correlationPropertyRetrievalExpression>
</bpmn:correlationProperty> </bpmn:correlationProperty>
<bpmn:message id="message_send_one" name="Message Send One"> <bpmn:message id="message_send_one" name="Message Send One">

View File

@ -12,18 +12,18 @@
</bpmn:collaboration> </bpmn:collaboration>
<bpmn:correlationProperty id="mcp_topica_two" name="MCP TopicA Two"> <bpmn:correlationProperty id="mcp_topica_two" name="MCP TopicA Two">
<bpmn:correlationPropertyRetrievalExpression messageRef="message_send_two"> <bpmn:correlationPropertyRetrievalExpression messageRef="message_send_two">
<bpmn:formalExpression>topica_two</bpmn:formalExpression> <bpmn:messagePath>topica_two</bpmn:messagePath>
</bpmn:correlationPropertyRetrievalExpression> </bpmn:correlationPropertyRetrievalExpression>
<bpmn:correlationPropertyRetrievalExpression messageRef="message_response_two"> <bpmn:correlationPropertyRetrievalExpression messageRef="message_response_two">
<bpmn:formalExpression>topica_two</bpmn:formalExpression> <bpmn:messagePath>topica_two</bpmn:messagePath>
</bpmn:correlationPropertyRetrievalExpression> </bpmn:correlationPropertyRetrievalExpression>
</bpmn:correlationProperty> </bpmn:correlationProperty>
<bpmn:correlationProperty id="mcp_topicb_two" name="MCP TopicB_two"> <bpmn:correlationProperty id="mcp_topicb_two" name="MCP TopicB_two">
<bpmn:correlationPropertyRetrievalExpression messageRef="message_send_two"> <bpmn:correlationPropertyRetrievalExpression messageRef="message_send_two">
<bpmn:formalExpression>topicb_two</bpmn:formalExpression> <bpmn:messagePath>topicb_two</bpmn:messagePath>
</bpmn:correlationPropertyRetrievalExpression> </bpmn:correlationPropertyRetrievalExpression>
<bpmn:correlationPropertyRetrievalExpression messageRef="message_response_two"> <bpmn:correlationPropertyRetrievalExpression messageRef="message_response_two">
<bpmn:formalExpression>topicb_two</bpmn:formalExpression> <bpmn:messagePath>topicb_two</bpmn:messagePath>
</bpmn:correlationPropertyRetrievalExpression> </bpmn:correlationPropertyRetrievalExpression>
</bpmn:correlationProperty> </bpmn:correlationProperty>
<bpmn:message id="message_send_two" name="Message Send Two"> <bpmn:message id="message_send_two" name="Message Send Two">

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:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:spiffworkflow="http://spiffworkflow.org/bpmn/schema/1.0/core" 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:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:spiffworkflow="http://spiffworkflow.org/bpmn/schema/1.0/core" id="Definitions_96f6665" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="3.0.0-dev">
<bpmn:collaboration id="Collaboration_0oye1os" messages="[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object]"> <bpmn:collaboration id="Collaboration_0oye1os">
<bpmn:participant id="message_initiator" name="Message Initiator" processRef="message_send_process" /> <bpmn:participant id="message_initiator" name="Message Initiator" processRef="message_send_process" />
<bpmn:participant id="message-receiver-one" name="Message Receiver One" /> <bpmn:participant id="message-receiver-one" name="Message Receiver One" />
<bpmn:participant id="message-receiver-two" name="Message Receiver Two" /> <bpmn:participant id="message-receiver-two" name="Message Receiver Two" />
@ -19,18 +19,18 @@
</bpmn:collaboration> </bpmn:collaboration>
<bpmn:correlationProperty id="mcp_topica_one" name="MCP TopicA One"> <bpmn:correlationProperty id="mcp_topica_one" name="MCP TopicA One">
<bpmn:correlationPropertyRetrievalExpression messageRef="message_send_one"> <bpmn:correlationPropertyRetrievalExpression messageRef="message_send_one">
<bpmn:formalExpression>topica_one</bpmn:formalExpression> <bpmn:messagePath>topica_one</bpmn:messagePath>
</bpmn:correlationPropertyRetrievalExpression> </bpmn:correlationPropertyRetrievalExpression>
<bpmn:correlationPropertyRetrievalExpression messageRef="message_response_one"> <bpmn:correlationPropertyRetrievalExpression messageRef="message_response_one">
<bpmn:formalExpression>payload_var_one.topica_one</bpmn:formalExpression> <bpmn:messagePath>payload_var_one.topica_one</bpmn:messagePath>
</bpmn:correlationPropertyRetrievalExpression> </bpmn:correlationPropertyRetrievalExpression>
</bpmn:correlationProperty> </bpmn:correlationProperty>
<bpmn:correlationProperty id="mcp_topicb_one" name="MCP TopicB_one"> <bpmn:correlationProperty id="mcp_topicb_one" name="MCP TopicB_one">
<bpmn:correlationPropertyRetrievalExpression messageRef="message_send_one"> <bpmn:correlationPropertyRetrievalExpression messageRef="message_send_one">
<bpmn:formalExpression>topicb_one</bpmn:formalExpression> <bpmn:messagePath>topicb_one</bpmn:messagePath>
</bpmn:correlationPropertyRetrievalExpression> </bpmn:correlationPropertyRetrievalExpression>
<bpmn:correlationPropertyRetrievalExpression messageRef="message_response_one"> <bpmn:correlationPropertyRetrievalExpression messageRef="message_response_one">
<bpmn:formalExpression>payload_var_one.topicb</bpmn:formalExpression> <bpmn:messagePath>payload_var_one.topicb</bpmn:messagePath>
</bpmn:correlationPropertyRetrievalExpression> </bpmn:correlationPropertyRetrievalExpression>
</bpmn:correlationProperty> </bpmn:correlationProperty>
<bpmn:process id="message_send_process" name="Message Send Process" isExecutable="true"> <bpmn:process id="message_send_process" name="Message Send Process" isExecutable="true">
@ -117,18 +117,18 @@ del time</bpmn:script>
</bpmn:message> </bpmn:message>
<bpmn:correlationProperty id="mcp_topica_two" name="MCP Topica Two"> <bpmn:correlationProperty id="mcp_topica_two" name="MCP Topica Two">
<bpmn:correlationPropertyRetrievalExpression messageRef="message_send_two"> <bpmn:correlationPropertyRetrievalExpression messageRef="message_send_two">
<bpmn:formalExpression>topica_two</bpmn:formalExpression> <bpmn:messagePath>topica_two</bpmn:messagePath>
</bpmn:correlationPropertyRetrievalExpression> </bpmn:correlationPropertyRetrievalExpression>
<bpmn:correlationPropertyRetrievalExpression messageRef="message_response_two"> <bpmn:correlationPropertyRetrievalExpression messageRef="message_response_two">
<bpmn:formalExpression>topica_two</bpmn:formalExpression> <bpmn:messagePath>topica_two</bpmn:messagePath>
</bpmn:correlationPropertyRetrievalExpression> </bpmn:correlationPropertyRetrievalExpression>
</bpmn:correlationProperty> </bpmn:correlationProperty>
<bpmn:correlationProperty id="mcp_topicb_two" name="MCP Topicb Two"> <bpmn:correlationProperty id="mcp_topicb_two" name="MCP Topicb Two">
<bpmn:correlationPropertyRetrievalExpression messageRef="message_send_two"> <bpmn:correlationPropertyRetrievalExpression messageRef="message_send_two">
<bpmn:formalExpression>topicb_two</bpmn:formalExpression> <bpmn:messagePath>topicb_two</bpmn:messagePath>
</bpmn:correlationPropertyRetrievalExpression> </bpmn:correlationPropertyRetrievalExpression>
<bpmn:correlationPropertyRetrievalExpression messageRef="message_response_two"> <bpmn:correlationPropertyRetrievalExpression messageRef="message_response_two">
<bpmn:formalExpression>topicb_two</bpmn:formalExpression> <bpmn:messagePath>topicb_two</bpmn:messagePath>
</bpmn:correlationPropertyRetrievalExpression> </bpmn:correlationPropertyRetrievalExpression>
</bpmn:correlationProperty> </bpmn:correlationProperty>
<bpmndi:BPMNDiagram id="BPMNDiagram_1"> <bpmndi:BPMNDiagram id="BPMNDiagram_1">

View File

@ -860,7 +860,7 @@ class TestProcessApi(BaseTest):
assert response.status_code == 200 assert response.status_code == 200
assert response.json is not None assert response.json is not None
assert response.json["ok"] assert response.json["file_contents"] is not None
response = client.get( response = client.get(
f"/v1.0/process-models/{modified_process_model_id}/files/random_fact.svg", f"/v1.0/process-models/{modified_process_model_id}/files/random_fact.svg",

View File

@ -5,13 +5,15 @@ import pytest
from flask import Flask from flask 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.bpmn.parser.ValidationException import ValidationException # type: ignore
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.spec_reference import SpecReferenceCache
from spiffworkflow_backend.models.user import UserModel from spiffworkflow_backend.models.user import UserModel
from spiffworkflow_backend.services.process_model_service import ProcessModelService from spiffworkflow_backend.services.process_model_service import ProcessModelService
from spiffworkflow_backend.services.spec_file_service import (
ProcessModelFileInvalidError,
)
from spiffworkflow_backend.services.spec_file_service import SpecFileService from spiffworkflow_backend.services.spec_file_service import SpecFileService
@ -74,7 +76,7 @@ class TestSpecFileService(BaseTest):
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
) )
with pytest.raises(ValidationException) as exception: with pytest.raises(ProcessModelFileInvalidError) as exception:
load_test_spec( load_test_spec(
"call_activity_nested_duplicate", "call_activity_nested_duplicate",
process_model_source_directory="call_activity_duplicate", process_model_source_directory="call_activity_duplicate",
@ -85,6 +87,14 @@ class TestSpecFileService(BaseTest):
in str(exception.value) in str(exception.value)
) )
process_model = ProcessModelService.get_process_model(
"call_activity_nested_duplicate"
)
full_file_path = SpecFileService.full_file_path(
process_model, "call_activity_nested_duplicate.bpmn"
)
assert not os.path.isfile(full_file_path)
def test_updates_relative_file_path_when_appropriate( def test_updates_relative_file_path_when_appropriate(
self, self,
app: Flask, app: Flask,
@ -206,3 +216,23 @@ class TestSpecFileService(BaseTest):
assert dmn1[0].display_name == "Decision 1" assert dmn1[0].display_name == "Decision 1"
assert dmn1[0].identifier == "Decision_0vrtcmk" assert dmn1[0].identifier == "Decision_0vrtcmk"
assert dmn1[0].type == "decision" assert dmn1[0].type == "decision"
def test_validate_bpmn_xml_with_invalid_xml(
self,
app: Flask,
client: FlaskClient,
with_db_and_bpmn_file_cleanup: None,
) -> None:
"""Test_validate_bpmn_xml_with_invalid_xml."""
process_model = load_test_spec(
process_model_id="group/invalid_xml",
bpmn_file_name="script_error_with_task_data.bpmn",
process_model_source_directory="error",
)
with pytest.raises(ProcessModelFileInvalidError):
SpecFileService.update_file(
process_model, "bad_xml.bpmn", b"THIS_IS_NOT_VALID_XML"
)
full_file_path = SpecFileService.full_file_path(process_model, "bad_xml.bpmn")
assert not os.path.isfile(full_file_path)

View File

@ -37,7 +37,9 @@ export default function ProcessModelSearch({
const shouldFilterProcessModel = (options: any) => { const shouldFilterProcessModel = (options: any) => {
const processModel: ProcessModel = options.item; const processModel: ProcessModel = options.item;
const { inputValue } = options; const { inputValue } = options;
return getFullProcessModelLabel(processModel).includes(inputValue); return getFullProcessModelLabel(processModel)
.toLowerCase()
.includes((inputValue || '').toLowerCase());
}; };
return ( return (
<ComboBox <ComboBox

View File

@ -134,7 +134,7 @@ export default function ProcessModelEditDiagram() {
setProcessModel(result); setProcessModel(result);
}; };
HttpService.makeCallToBackend({ HttpService.makeCallToBackend({
path: `/${processModelPath}`, path: `/${processModelPath}?include_file_references=true`,
successCallback: processResult, successCallback: processResult,
}); });
}, [processModelPath]); }, [processModelPath]);
@ -966,7 +966,6 @@ export default function ProcessModelEditDiagram() {
{scriptEditorAndTests()} {scriptEditorAndTests()}
{markdownEditor()} {markdownEditor()}
{processModelSelector()} {processModelSelector()}
{`Processes length: ${processes.length}`}
<div id="diagram-container" /> <div id="diagram-container" />
</> </>
); );

View File

@ -374,6 +374,7 @@ export default function ProcessModelShow() {
const doFileUpload = (event: any) => { const doFileUpload = (event: any) => {
event.preventDefault(); event.preventDefault();
setErrorObject(null);
const url = `/process-models/${modifiedProcessModelId}/files`; const url = `/process-models/${modifiedProcessModelId}/files`;
const formData = new FormData(); const formData = new FormData();
formData.append('file', filesToUpload[0]); formData.append('file', filesToUpload[0]);
@ -383,6 +384,7 @@ export default function ProcessModelShow() {
successCallback: onUploadedCallback, successCallback: onUploadedCallback,
httpMethod: 'POST', httpMethod: 'POST',
postBody: formData, postBody: formData,
failureCallback: setErrorObject,
}); });
setFilesToUpload(null); setFilesToUpload(null);
}; };