Merge pull request #388 from sartography/bug/472_date_time
fixes 472 - a bug with the datetime validation
This commit is contained in:
commit
208ca92fb2
|
@ -51,18 +51,30 @@ class ApiError(Exception):
|
||||||
if "task" in task.data:
|
if "task" in task.data:
|
||||||
task.data.pop("task")
|
task.data.pop("task")
|
||||||
|
|
||||||
# In the unlikely event that the API error can't be serialized, try removing the task_data, as it may
|
# Assure that there is nothing in the json data that can't be serialized.
|
||||||
# contain some invalid data that we can't return, so we can at least get the erro rmessage.
|
instance.task_data = ApiError.remove_unserializeable_from_dict(task.data)
|
||||||
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'}
|
|
||||||
|
|
||||||
app.logger.error(message, exc_info=True)
|
app.logger.error(message, exc_info=True)
|
||||||
return instance
|
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
|
@classmethod
|
||||||
def from_task_spec(cls, code, message, task_spec, status_code=400):
|
def from_task_spec(cls, code, message, task_spec, status_code=400):
|
||||||
"""Constructs an API Error with details pulled from the current task."""
|
"""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)
|
return ApiError.from_task_spec(code, message, exp.sender)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ApiErrorSchema(ma.Schema):
|
class ApiErrorSchema(ma.Schema):
|
||||||
class Meta:
|
class Meta:
|
||||||
fields = ("code", "message", "workflow_name", "file_name", "task_name", "task_id",
|
fields = ("code", "message", "workflow_name", "file_name", "task_name", "task_id",
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import copy
|
import copy
|
||||||
|
import json
|
||||||
import string
|
import string
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import random
|
import random
|
||||||
|
@ -277,7 +278,11 @@ class WorkflowService(object):
|
||||||
form_data[field.id] = WorkflowService.get_random_data_for_field(field, task)
|
form_data[field.id] = WorkflowService.get_random_data_for_field(field, task)
|
||||||
if task.data is None:
|
if task.data is None:
|
||||||
task.data = {}
|
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
|
@staticmethod
|
||||||
def check_field_id(id):
|
def check_field_id(id):
|
||||||
|
@ -435,6 +440,8 @@ class WorkflowService(object):
|
||||||
if default == 'true' or default == 't':
|
if default == 'true' or default == 't':
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
elif field.type == 'date' and isinstance(default, datetime):
|
||||||
|
return default.isoformat()
|
||||||
else:
|
else:
|
||||||
return default
|
return default
|
||||||
|
|
||||||
|
@ -484,8 +491,6 @@ class WorkflowService(object):
|
||||||
return [random_value]
|
return [random_value]
|
||||||
else:
|
else:
|
||||||
return random_value
|
return random_value
|
||||||
|
|
||||||
|
|
||||||
elif field.type == "long":
|
elif field.type == "long":
|
||||||
return random.randint(1, 1000)
|
return random.randint(1, 1000)
|
||||||
elif field.type == 'boolean':
|
elif field.type == 'boolean':
|
||||||
|
|
|
@ -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]
|
api_error = json_data[0]
|
||||||
self.assertEqual('disabled_workflow', api_error['code'])
|
self.assertEqual('disabled_workflow', api_error['code'])
|
||||||
self.assertEqual('This workflow is disabled. This is my mocked disable message.', api_error['message'])
|
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…
Reference in New Issue