workflows: allow templates as global action timeout (#53548) #617
|
@ -2641,6 +2641,17 @@ def test_workflows_global_actions_timeout_triggers(pub):
|
|||
resp = resp.form.submit('submit').follow()
|
||||
assert Workflow.get(workflow.id).global_actions[0].triggers[0].timeout == '-2'
|
||||
|
||||
resp = resp.click(
|
||||
href='triggers/%s/' % Workflow.get(workflow.id).global_actions[0].triggers[0].id, index=0
|
||||
)
|
||||
resp.form['timeout'] = 'xxx'
|
||||
resp = resp.form.submit('submit')
|
||||
assert 'wrong format' in resp.text
|
||||
|
||||
resp.form['timeout'] = '{{ xxx }}'
|
||||
resp = resp.form.submit('submit').follow()
|
||||
assert Workflow.get(workflow.id).global_actions[0].triggers[0].timeout == '{{ xxx }}'
|
||||
|
||||
|
||||
def test_workflows_global_actions_webservice_trigger(pub):
|
||||
create_superuser(pub)
|
||||
|
|
|
@ -4497,6 +4497,46 @@ def test_global_timeouts(pub, formdef_class):
|
|||
assert formdef.data_class().get(formdata1.id).get_criticality_level_object().name == 'yellow'
|
||||
formdata1.store()
|
||||
|
||||
# template as timeout value
|
||||
trigger.anchor = 'latest-arrival'
|
||||
trigger.anchor_status_latest = 'wf-accepted'
|
||||
trigger.timeout = '{{ form_option_days }}'
|
||||
workflow.store()
|
||||
|
||||
# * invalid value
|
||||
pub.loggederror_class.wipe()
|
||||
formdata1.jump_status('accepted')
|
||||
formdata1.evolution[-1].time = time.localtime(time.time() - 1 * 86400)
|
||||
formdata1.store()
|
||||
pub.apply_global_action_timeouts()
|
||||
assert formdef.data_class().get(formdata1.id).get_criticality_level_object().name == 'green'
|
||||
formdata1.store()
|
||||
assert pub.loggederror_class.count() == 1
|
||||
assert [x.summary for x in pub.loggederror_class.select()][
|
||||
0
|
||||
] == "[TIMEOUTS] ValueError: invalid literal for int() with base 10: ''"
|
||||
|
||||
# * ok value but too short for timeout
|
||||
pub.loggederror_class.wipe()
|
||||
workflow.variables_formdef = WorkflowVariablesFieldsFormDef(workflow=workflow)
|
||||
workflow.variables_formdef.fields = [
|
||||
StringField(id='5', label='Days', varname='days'),
|
||||
]
|
||||
workflow.store()
|
||||
formdef.workflow_options = {'days': '2'}
|
||||
formdef.store()
|
||||
formdata1.store()
|
||||
pub.apply_global_action_timeouts()
|
||||
assert formdef.data_class().get(formdata1.id).get_criticality_level_object().name == 'green'
|
||||
assert pub.loggederror_class.count() == 0
|
||||
|
||||
# * ok value, and timeout is triggered
|
||||
formdata1.evolution[-1].time = time.localtime(time.time() - 4 * 86400)
|
||||
formdata1.store()
|
||||
pub.apply_global_action_timeouts()
|
||||
assert formdef.data_class().get(formdata1.id).get_criticality_level_object().name == 'yellow'
|
||||
assert pub.loggederror_class.count() == 0
|
||||
|
||||
|
||||
def test_global_timeouts_latest_arrival(pub):
|
||||
FormDef.wipe()
|
||||
|
|
|
@ -22,6 +22,7 @@ import glob
|
|||
import itertools
|
||||
import os
|
||||
import random
|
||||
import re
|
||||
import time
|
||||
import uuid
|
||||
import xml.etree.ElementTree as ET
|
||||
|
@ -48,12 +49,12 @@ from .qommon import _, ezt, get_cfg, misc, pgettext_lazy, template
|
|||
from .qommon.errors import UnknownReferencedErrorMixin
|
||||
from .qommon.form import (
|
||||
CheckboxWidget,
|
||||
ComputedExpressionWidget,
|
||||
ConditionWidget,
|
||||
Form,
|
||||
SingleSelectWidget,
|
||||
SingleSelectWidgetWithOther,
|
||||
StringWidget,
|
||||
ValidatedStringWidget,
|
||||
WidgetList,
|
||||
WidgetListOfRoles,
|
||||
)
|
||||
|
@ -1761,19 +1762,26 @@ class WorkflowGlobalActionTimeoutTrigger(WorkflowGlobalActionTrigger):
|
|||
},
|
||||
)
|
||||
|
||||
def validate_timeout(value):
|
||||
if Template.is_template_string(value):
|
||||
return ComputedExpressionWidget.validate_template(value)
|
||||
match = re.match(r'^-?\d+$', value or '')
|
||||
if not match or not match.group() == value:
|
||||
raise ValueError(_('wrong format'))
|
||||
|
||||
form.add(
|
||||
ValidatedStringWidget,
|
||||
StringWidget,
|
||||
'timeout',
|
||||
title=_('Delay (in days)'),
|
||||
value=self.timeout,
|
||||
regex=r'^-?\d+$',
|
||||
validation_function=validate_timeout,
|
||||
required=True,
|
||||
hint=_(
|
||||
'''
|
||||
Number of days relative to the reference date. If the
|
||||
reference date is computed from an expression, a negative
|
||||
delay is accepted to trigger the action before the
|
||||
date.'''
|
||||
date. This can be a template.'''
|
||||
),
|
||||
)
|
||||
|
||||
|
@ -1868,7 +1876,21 @@ class WorkflowGlobalActionTimeoutTrigger(WorkflowGlobalActionTrigger):
|
|||
if not anchor_date:
|
||||
return False
|
||||
|
||||
anchor_date = anchor_date + datetime.timedelta(days=int(self.timeout))
|
||||
timeout = self.timeout
|
||||
if Template.is_template_string(self.timeout):
|
||||
variables = get_publisher().substitutions.get_context_variables(mode='lazy')
|
||||
timeout = Template(self.timeout, autoescape=False).render(variables)
|
||||
try:
|
||||
int(timeout)
|
||||
except ValueError as e:
|
||||
# get the variables in the locals() namespace so they are
|
||||
# displayed within the trace.
|
||||
expression = self.timeout # noqa pylint: disable=unused-variable
|
||||
# noqa pylint: disable=unused-variable
|
||||
get_publisher().record_error(exception=e, context='[TIMEOUTS]', notify=True)
|
||||
return False
|
||||
|
||||
anchor_date = anchor_date + datetime.timedelta(days=int(timeout))
|
||||
|
||||
if not is_aware(anchor_date):
|
||||
anchor_date = make_aware(anchor_date, is_dst=True)
|
||||
|
|
Loading…
Reference in New Issue