Projet

Général

Profil

0001-misc-add-new-widget-to-validate-workflow-expressions.patch

Frédéric Péters, 25 mai 2016 10:45

Télécharger (10,8 ko)

Voir les différences:

Subject: [PATCH] misc: add new widget to validate workflow expressions
 (#11042)

 tests/test_widgets.py               | 21 +++++++++++++++++
 wcs/qommon/form.py                  | 45 +++++++++++++++++++++++++++++++++++++
 wcs/qommon/static/css/dc2/admin.css |  1 +
 wcs/wf/geolocate.py                 |  8 +++----
 wcs/wf/jump.py                      |  2 +-
 wcs/wf/wscall.py                    |  3 ++-
 wcs/workflows.py                    | 27 ++--------------------
 7 files changed, 76 insertions(+), 31 deletions(-)
tests/test_widgets.py
382 382
            options=[('apple', 'Apple'), ('pear', 'Pear'), ('peach', 'Peach')])
383 383
    mock_form_submission(req, widget, {'test$choice': ['__other'], 'test$other': 'Apricot'})
384 384
    assert widget.parse() == 'Apricot'
385

  
386
def test_computed_expression_widget():
387
    widget = ComputedExpressionWidget('test')
388
    form = MockHtmlForm(widget)
389
    mock_form_submission(req, widget, {'test': 'hello world'})
390
    assert widget.parse() == 'hello world'
391
    assert not widget.has_error()
392

  
393
    widget = ComputedExpressionWidget('test')
394
    mock_form_submission(req, widget, {'test': '=hello world'})
395
    assert widget.has_error()
396
    assert widget.get_error().startswith('syntax error')
397

  
398
    widget = ComputedExpressionWidget('test')
399
    mock_form_submission(req, widget, {'test': '[form_var_xxx]'})
400
    assert not widget.has_error()
401

  
402
    widget = ComputedExpressionWidget('test')
403
    mock_form_submission(req, widget, {'test': '[end]'})
404
    assert widget.has_error()
405
    assert widget.get_error().startswith('error in template')
wcs/qommon/form.py
59 59
from strftime import strftime
60 60
from publisher import get_cfg
61 61
from wcs import file_validation
62
from . import ezt
62 63

  
63 64
QuixoteForm = Form
64 65

  
......
2234 2235
        self.value = self.get('choice')
2235 2236
        if self.value == '__other':
2236 2237
            self.value = self.get('other')
2238

  
2239

  
2240
class ComputedExpressionWidget(StringWidget):
2241
    '''StringWidget that checks the entered value is a correct workflow
2242
       expression.'''
2243

  
2244
    @classmethod
2245
    def validate_ezt(cls, template):
2246
        processor = ezt.Template(compress_whitespace=False)
2247
        try:
2248
            processor.parse(template or '')
2249
        except ezt.EZTException as e:
2250
            parts = []
2251
            parts.append({
2252
                ezt.ArgCountSyntaxError: _('wrong number of arguments'),
2253
                ezt.UnknownReference: _('unknown reference'),
2254
                ezt.NeedSequenceError: _('sequence required'),
2255
                ezt.UnclosedBlocksError: _('unclosed block'),
2256
                ezt.UnmatchedEndError: _('unmatched [end]'),
2257
                ezt.BaseUnavailableError: _('unavailable base location'),
2258
                ezt.BadFormatConstantError: _('bad format constant'),
2259
                ezt.UnknownFormatConstantError: _('unknown format constant'),
2260
                }.get(e.__class__))
2261
            if e.line:
2262
                parts.append(_('at line %(line)d and column %(column)d') % {
2263
                    'line': e.line+1,
2264
                    'column': e.column+1})
2265
            raise ValueError(_('error in template (%s)') % ' '.join(parts))
2266

  
2267
    def _parse(self, request):
2268
        StringWidget._parse(self, request)
2269
        if self.value:
2270
            if self.value.startswith('='):
2271
                # python expression
2272
                try:
2273
                    compile(self.value[1:], '<string>', 'eval')
2274
                except SyntaxError as e:
2275
                    self.set_error(_('syntax error (%s)') % e)
2276
            else:
2277
                # ezt expression
2278
                try:
2279
                    self.validate_ezt(self.value)
2280
                except ValueError as e:
2281
                    self.set_error(str(e))
wcs/qommon/static/css/dc2/admin.css
1170 1170
	width: 25%;
1171 1171
}
1172 1172

  
1173
div.WidgetDict div.content div + div.ComputedExpressionWidget,
1173 1174
div.WidgetDict div.content div + div.StringWidget {
1174 1175
	width: 70%;
1175 1176
	padding-left: 1em;
wcs/wf/geolocate.py
27 27
from quixote import get_publisher
28 28

  
29 29
from qommon import get_logger
30
from qommon.form import RadiobuttonsWidget, StringWidget, CheckboxWidget
30
from qommon.form import RadiobuttonsWidget, ComputedExpressionWidget, CheckboxWidget
31 31
from qommon.misc import http_get_page
32 32
from wcs.workflows import WorkflowStatusItem, register_item_class
33 33

  
......
60 60
                    value=self.method,
61 61
                    attrs={'data-dynamic-display-parent': 'true'})
62 62
        if 'address_string' in parameters:
63
            form.add(StringWidget, '%saddress_string' % prefix, size=50,
63
            form.add(ComputedExpressionWidget, '%saddress_string' % prefix, size=50,
64 64
                    title=_('Address String'), value=self.address_string,
65 65
                    attrs={
66 66
                        'data-dynamic-display-child-of': '%smethod' % prefix,
67 67
                        'data-dynamic-display-value': methods.get('address_string'),
68 68
                    })
69 69
        if 'map_variable' in parameters:
70
            form.add(StringWidget, '%smap_variable' % prefix, size=50,
70
            form.add(ComputedExpressionWidget, '%smap_variable' % prefix, size=50,
71 71
                    title=_('Map Variable'), value=self.map_variable,
72 72
                    attrs={
73 73
                        'data-dynamic-display-child-of': '%smethod' % prefix,
74 74
                        'data-dynamic-display-value': methods.get('map_variable'),
75 75
                    })
76 76
        if 'photo_variable' in parameters:
77
            form.add(StringWidget, '%sphoto_variable' % prefix, size=50,
77
            form.add(ComputedExpressionWidget, '%sphoto_variable' % prefix, size=50,
78 78
                    title=_('Photo Variable'), value=self.photo_variable,
79 79
                    attrs={
80 80
                        'data-dynamic-display-child-of': '%smethod' % prefix,
wcs/wf/jump.py
166 166
                        'variables': ', '.join(timewords()),
167 167
                        'granularity': seconds2humanduration(self._granularity)}
168 168
            if str(self.timeout).startswith('='):
169
                form.add(StringWidget, '%stimeout' % prefix, title=_('Timeout'),
169
                form.add(ComputedExpressionWidget, '%stimeout' % prefix, title=_('Timeout'),
170 170
                            value=self.timeout, hint=_hint)
171 171
            else:
172 172
                form.add(StringWidget, '%stimeout' % prefix, title=_('Timeout'),
wcs/wf/wscall.py
147 147
                     title=_('URL'), value=self.url, size=80,
148 148
                     hint=_('Common substitution variables are available with the [variable] syntax.'))
149 149
        if 'request_signature_key' in parameters:
150
            form.add(StringWidget, '%srequest_signature_key' % prefix,
150
            form.add(ComputedExpressionWidget, '%srequest_signature_key' % prefix,
151 151
                    title=_('Request Signature Key'),
152 152
                    value=self.request_signature_key)
153 153
        methods = collections.OrderedDict(
......
170 170
            form.add(WidgetDict, '%spost_data' % prefix,
171 171
                    title=_('Post data'),
172 172
                    value=self.post_data or {},
173
                    element_value_type=ComputedExpressionWidget,
173 174
                    attrs={
174 175
                        'data-dynamic-display-child-of': '%smethod' % prefix,
175 176
                        'data-dynamic-display-value': methods.get('POST'),
wcs/workflows.py
1872 1872
                                        self.get_list_of_roles(include_logged_in_users=False)})
1873 1873
        if 'subject' in parameters:
1874 1874
            form.add(StringWidget, '%ssubject' % prefix, title=_('Subject'),
1875
                     validation_function=validate_ezt,
1875
                     validation_function=ComputedExpressionWidget.validate_ezt,
1876 1876
                     value=self.subject, size=40)
1877 1877
        if 'body' in parameters:
1878 1878
            form.add(TextWidget, '%sbody' % prefix, title=_('Body'),
1879 1879
                     value=self.body, cols=80, rows=10,
1880
                     validation_function=validate_ezt,
1880
                     validation_function=ComputedExpressionWidget.validate_ezt,
1881 1881
                     hint=_('Available variables: url, url_status, details, name, number, comment, field_NAME'))
1882 1882

  
1883

  
1884 1883
    def perform(self, formdata):
1885 1884
        if not self.to:
1886 1885
            return
......
2006 2005
    return fd.getvalue()
2007 2006

  
2008 2007

  
2009
def validate_ezt(template):
2010
    processor = ezt.Template(compress_whitespace=False)
2011
    try:
2012
        processor.parse(template or '')
2013
    except ezt.EZTException as e:
2014
        parts = []
2015
        parts.append({
2016
            ezt.ArgCountSyntaxError: _('wrong number of arguments'),
2017
            ezt.UnknownReference: _('unknown reference'),
2018
            ezt.NeedSequenceError: _('sequence required'),
2019
            ezt.UnclosedBlocksError: _('unclosed block'),
2020
            ezt.UnmatchedEndError: _('unmatched [end]'),
2021
            ezt.BaseUnavailableError: _('unavailable base location'),
2022
            ezt.BadFormatConstantError: _('bad format constant'),
2023
            ezt.UnknownFormatConstantError: _('unknown format constant'),
2024
            }.get(e.__class__))
2025
        if e.line:
2026
            parts.append(_('at line %(line)d and column %(column)d') % {
2027
                'line': e.line+1,
2028
                'column': e.column+1})
2029
        raise ValueError(_('error in template (%s)') % ' '.join(parts))
2030

  
2031 2008
class SendSMSWorkflowStatusItem(WorkflowStatusItem):
2032 2009
    description = N_('Send SMS')
2033 2010
    key = 'sendsms'
2034
-