mirror of
https://github.com/sartography/cr-connect-workflow.git
synced 2025-02-22 12:48:25 +00:00
Add some additional checks in the workflow service so we assure the data we are creating during validation can be serialized and deserialized just as it would be through the API. (Was hanging us up on dates)
Assure that if we generate a default value for a date in the task data, it is stored as an ISO String. remove any unserializable data from the task_data when an error is encountered, rather than just dropping all the task_data. This case seems to happen a lot and it leaves us with nothing to go on.
This commit is contained in:
parent
448b7b2773
commit
b02731df17
@ -51,18 +51,30 @@ class ApiError(Exception):
|
||||
if "task" in task.data:
|
||||
task.data.pop("task")
|
||||
|
||||
# In the unlikely event that the API error can't be serialized, try removing the task_data, as it may
|
||||
# contain some invalid data that we can't return, so we can at least get the erro rmessage.
|
||||
instance.task_data = task.data
|
||||
try:
|
||||
json.dumps(ApiErrorSchema().dump(instance))
|
||||
except TypeError as te:
|
||||
instance.task_data = {
|
||||
'task_data_hidden': 'We were unable to serialize the task data when reporting this error'}
|
||||
# Assure that there is nothing in the json data that can't be serialized.
|
||||
instance.task_data = ApiError.remove_unserializeable_from_dict(task.data)
|
||||
|
||||
app.logger.error(message, exc_info=True)
|
||||
return instance
|
||||
|
||||
@staticmethod
|
||||
def remove_unserializeable_from_dict(my_dict):
|
||||
keys_to_delete = []
|
||||
for key, value in my_dict.items():
|
||||
if not ApiError.is_jsonable(value):
|
||||
keys_to_delete.append(key)
|
||||
for key in keys_to_delete:
|
||||
del my_dict[key]
|
||||
return my_dict
|
||||
|
||||
@staticmethod
|
||||
def is_jsonable(x):
|
||||
try:
|
||||
json.dumps(x)
|
||||
return True
|
||||
except (TypeError, OverflowError):
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def from_task_spec(cls, code, message, task_spec, status_code=400):
|
||||
"""Constructs an API Error with details pulled from the current task."""
|
||||
@ -89,6 +101,8 @@ class ApiError(Exception):
|
||||
return ApiError.from_task_spec(code, message, exp.sender)
|
||||
|
||||
|
||||
|
||||
|
||||
class ApiErrorSchema(ma.Schema):
|
||||
class Meta:
|
||||
fields = ("code", "message", "workflow_name", "file_name", "task_name", "task_id",
|
||||
|
@ -1,4 +1,5 @@
|
||||
import copy
|
||||
import json
|
||||
import string
|
||||
from datetime import datetime
|
||||
import random
|
||||
@ -277,7 +278,11 @@ class WorkflowService(object):
|
||||
form_data[field.id] = WorkflowService.get_random_data_for_field(field, task)
|
||||
if task.data is None:
|
||||
task.data = {}
|
||||
task.data.update(form_data)
|
||||
|
||||
# jsonify, and de-jsonify the data to mimic how data will be returned from the front end for forms and assures
|
||||
# we aren't generating something that can't be serialized.
|
||||
form_data_string = json.dumps(form_data)
|
||||
task.data.update(json.loads(form_data_string))
|
||||
|
||||
@staticmethod
|
||||
def check_field_id(id):
|
||||
@ -435,6 +440,8 @@ class WorkflowService(object):
|
||||
if default == 'true' or default == 't':
|
||||
return True
|
||||
return False
|
||||
elif field.type == 'date' and isinstance(default, datetime):
|
||||
return default.isoformat()
|
||||
else:
|
||||
return default
|
||||
|
||||
@ -484,8 +491,6 @@ class WorkflowService(object):
|
||||
return [random_value]
|
||||
else:
|
||||
return random_value
|
||||
|
||||
|
||||
elif field.type == "long":
|
||||
return random.randint(1, 1000)
|
||||
elif field.type == 'boolean':
|
||||
|
65
tests/data/date_value_expression/date_value_expression.bpmn
Normal file
65
tests/data/date_value_expression/date_value_expression.bpmn
Normal file
@ -0,0 +1,65 @@
|
||||
<?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:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:modeler="http://camunda.org/schema/modeler/1.0" id="Definitions_1717350" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="4.10.0" modeler:executionPlatform="Camunda Platform" modeler:executionPlatformVersion="7.15.0">
|
||||
<bpmn:process id="Process_1y3o9tq" isExecutable="true">
|
||||
<bpmn:startEvent id="StartEvent_1">
|
||||
<bpmn:outgoing>Flow_0ecke9e</bpmn:outgoing>
|
||||
</bpmn:startEvent>
|
||||
<bpmn:sequenceFlow id="Flow_0ecke9e" sourceRef="StartEvent_1" targetRef="Date_Value_Expression" />
|
||||
<bpmn:userTask id="Date_Value_Expression" name="Date Form" camunda:formKey="My Form">
|
||||
<bpmn:extensionElements>
|
||||
<camunda:formData>
|
||||
<camunda:formField id="my_date" label="my Date" type="date">
|
||||
<camunda:properties>
|
||||
<camunda:property id="value_expression" value="datetime.datetime.now()" />
|
||||
</camunda:properties>
|
||||
<camunda:validation>
|
||||
<camunda:constraint name="required" config="True" />
|
||||
</camunda:validation>
|
||||
</camunda:formField>
|
||||
</camunda:formData>
|
||||
</bpmn:extensionElements>
|
||||
<bpmn:incoming>Flow_0ecke9e</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_04yzu4r</bpmn:outgoing>
|
||||
</bpmn:userTask>
|
||||
<bpmn:endEvent id="Event_06knzzw">
|
||||
<bpmn:documentation>The Date is {{my_date}}</bpmn:documentation>
|
||||
<bpmn:incoming>Flow_1hz2cs6</bpmn:incoming>
|
||||
</bpmn:endEvent>
|
||||
<bpmn:sequenceFlow id="Flow_04yzu4r" sourceRef="Date_Value_Expression" targetRef="Activity_0irsthq" />
|
||||
<bpmn:sequenceFlow id="Flow_1hz2cs6" sourceRef="Activity_0irsthq" targetRef="Event_06knzzw" />
|
||||
<bpmn:scriptTask id="Activity_0irsthq" name="Date Script">
|
||||
<bpmn:incoming>Flow_04yzu4r</bpmn:incoming>
|
||||
<bpmn:outgoing>Flow_1hz2cs6</bpmn:outgoing>
|
||||
<bpmn:script>dateparser.parse(my_date)
|
||||
</bpmn:script>
|
||||
</bpmn:scriptTask>
|
||||
</bpmn:process>
|
||||
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
|
||||
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1y3o9tq">
|
||||
<bpmndi:BPMNEdge id="Flow_0ecke9e_di" bpmnElement="Flow_0ecke9e">
|
||||
<di:waypoint x="215" y="117" />
|
||||
<di:waypoint x="270" y="117" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_04yzu4r_di" bpmnElement="Flow_04yzu4r">
|
||||
<di:waypoint x="370" y="117" />
|
||||
<di:waypoint x="420" y="117" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNEdge id="Flow_1hz2cs6_di" bpmnElement="Flow_1hz2cs6">
|
||||
<di:waypoint x="520" y="117" />
|
||||
<di:waypoint x="542" y="117" />
|
||||
</bpmndi:BPMNEdge>
|
||||
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
|
||||
<dc:Bounds x="179" y="99" width="36" height="36" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_0myo4ou_di" bpmnElement="Date_Value_Expression">
|
||||
<dc:Bounds x="270" y="77" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Event_06knzzw_di" bpmnElement="Event_06knzzw">
|
||||
<dc:Bounds x="542" y="99" width="36" height="36" />
|
||||
</bpmndi:BPMNShape>
|
||||
<bpmndi:BPMNShape id="Activity_01ztf01_di" bpmnElement="Activity_0irsthq">
|
||||
<dc:Bounds x="420" y="77" width="100" height="80" />
|
||||
</bpmndi:BPMNShape>
|
||||
</bpmndi:BPMNPlane>
|
||||
</bpmndi:BPMNDiagram>
|
||||
</bpmn:definitions>
|
@ -186,3 +186,12 @@ class TestWorkflowSpecValidation(BaseTest):
|
||||
api_error = json_data[0]
|
||||
self.assertEqual('disabled_workflow', api_error['code'])
|
||||
self.assertEqual('This workflow is disabled. This is my mocked disable message.', api_error['message'])
|
||||
|
||||
|
||||
def test_date_generation_during_validation(self):
|
||||
# We hit a bug where the date was generated as a part of a value_expression during validation, but
|
||||
# it wasn't converted to an ISO String as it would be if submitted through the API.
|
||||
# subsequent attempts to work with the expected date_string failed, because it was already a date.
|
||||
# This can't happen in the front end code base, but it was breaking validation.
|
||||
errors = self.validate_workflow("date_value_expression")
|
||||
self.assertEqual(0, len(errors))
|
||||
|
Loading…
x
Reference in New Issue
Block a user