Merge pull request #388 from sartography/bug/472_date_time

fixes 472 - a bug with the datetime validation
This commit is contained in:
Dan Funk 2021-10-07 11:13:53 -04:00 committed by GitHub
commit 208ca92fb2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 104 additions and 11 deletions

View File

@ -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",

View File

@ -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':

View 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>

View File

@ -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))