From 855c12fa2c1f0fd5512d02a469c1a72430d22adc Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Wed, 6 Apr 2016 17:50:21 +0200 Subject: [PATCH 1/2] fix non determinism when importing wscall workflow actions (#10477) --- tests/test_workflow_import.py | 3 ++- tests/test_workflows.py | 18 ++++++++++++++++++ wcs/wf/wscall.py | 7 ++++--- wcs/workflows.py | 20 ++++++++++++++++++++ 4 files changed, 44 insertions(+), 4 deletions(-) diff --git a/tests/test_workflow_import.py b/tests/test_workflow_import.py index af79690..704d03c 100644 --- a/tests/test_workflow_import.py +++ b/tests/test_workflow_import.py @@ -2,6 +2,7 @@ import sys import shutil import StringIO import xml.etree.ElementTree as ET +from collections import OrderedDict from quixote import cleanup from wcs import publisher @@ -306,7 +307,7 @@ def test_wscall_action(): wscall.varname = 'varname' wscall.post = False wscall.request_signature_key = 'key' - wscall.post_data = {'one': '1', 'two': '=2', 'good:name': 'ok'} + wscall.post_data = OrderedDict((('one', '1'), ('two', '=2'), ('good:name', 'ok'))) st1.items.append(wscall) wscall.parent = st1 diff --git a/tests/test_workflows.py b/tests/test_workflows.py index e508998..b28cba4 100644 --- a/tests/test_workflows.py +++ b/tests/test_workflows.py @@ -1216,3 +1216,21 @@ def test_global_timeouts(pub): pub.apply_global_action_timeouts() assert formdef.data_class().get(formdata1.id).get_criticality_level_object().name == 'green' formdata1.store() + + +def test_workflow_get_json_export_dict(pub): + workflow = Workflow(name='wf roles') + st2 = workflow.add_status('Status2', 'st2') + st2.forced_endpoint = True + workflow.roles['_other'] = 'Other Function' + root = workflow.get_json_export_dict() + assert set(root.keys()) >= set(['statuses', 'name', 'functions']) + + assert root['name'] == 'wf roles' + assert set(root['statuses'].keys()) == set(['st1', 'st2']) + assert all(set(status.keys()) >= set(['name', 'forced_endpoint']) for status in + root['statuses'].values()) + assert root['statuses']['st1']['name'] == 'Status1' + assert root['statuses']['st1']['forced_endpoint'] is False + assert root['statuses']['st2']['name'] == 'Status2' + assert root['statuses']['st2']['forced_endpoint'] is True diff --git a/wcs/wf/wscall.py b/wcs/wf/wscall.py index a35e53b..d51c843 100644 --- a/wcs/wf/wscall.py +++ b/wcs/wf/wscall.py @@ -23,6 +23,7 @@ import collections import mimetypes from StringIO import StringIO +from collections import OrderedDict from quixote.html import TemplateIO, htmltext from qommon.errors import ConnectionError from qommon.form import * @@ -157,7 +158,7 @@ class WebserviceCallStatusItem(WorkflowStatusItem): if 'post_data' in parameters: form.add(WidgetDict, '%spost_data' % prefix, title=_('Post data'), - value=self.post_data or {}, + value=dict(self.post_data or {}), attrs={ 'data-dynamic-display-child-of': '%smethod' % prefix, 'data-dynamic-display-value': methods.get('POST'), @@ -232,7 +233,7 @@ class WebserviceCallStatusItem(WorkflowStatusItem): # if self.post_data exists, post_data is a dict built from it if self.method == 'POST' and self.post_data: - post_data = {} + post_data = OrderedDict() for (key, value) in self.post_data.items(): try: post_data[key] = self.compute(value, raises=True) @@ -382,7 +383,7 @@ class WebserviceCallStatusItem(WorkflowStatusItem): def post_data_init_with_xml(self, elem, charset, include_id=False): if elem is None: return - self.post_data = {} + self.post_data = OrderedDict() for item in elem.findall('item'): key = item.find('name').text.encode(charset) value = item.find('value').text.encode(charset) diff --git a/wcs/workflows.py b/wcs/workflows.py index 454930e..a9771dd 100644 --- a/wcs/workflows.py +++ b/wcs/workflows.py @@ -692,6 +692,26 @@ class Workflow(StorableObject): return workflow + def get_json_export_dict(self, include_id=False): + charset = get_publisher().site_charset + root = {} + root['name'] = unicode(self.name, charset) + if include_id and self.id: + root['id'] = str(self.id) + if self.last_modification_time: + root['last_modification_time'] = time.strftime('%Y-%m-%dT%H:%M:%S', + self.last_modification_time) + roles = root['functions'] = {} + for role, label in self.roles.iteritems(): + roles[role] = unicode(label, charset) + statuses = root['statuses'] = {} + for status in self.possible_status: + statuses[status.id] = { + 'name': unicode(status.name, charset), + 'forced_endpoint': status.forced_endpoint, + } + return root + class XmlSerialisable(object): node_name = None -- 2.1.4