Compare commits
2 Commits
4df0449045
...
e9766d6c10
Author | SHA1 | Date |
---|---|---|
Serghei Mihai | e9766d6c10 | |
Serghei Mihai | 84a3a28d69 |
|
@ -275,3 +275,54 @@ def test_afterjobs_base_directory(pub):
|
|||
get_app(pub).get('/api/jobs/', status=403)
|
||||
# base directory is 404
|
||||
get_app(pub).get(sign_url('/api/jobs/?orig=coucou', '1234'), status=404)
|
||||
|
||||
|
||||
def test_preview_json_payload(pub, admin_user):
|
||||
get_app(pub).get('/api/preview-json-payload', status=403)
|
||||
app = login(get_app(pub))
|
||||
resp = app.get('/api/preview-json-payload')
|
||||
|
||||
assert resp.pyquery('div.payload-preview').length == 1
|
||||
assert '<h2>Payload preview</h2>' in resp.text
|
||||
resp.pyquery('div.payload-preview').text() == '{}'
|
||||
params = {
|
||||
'request$post_data$added_elements': 1,
|
||||
'request$post_data$element1key': 'user/first_name',
|
||||
'request$post_data$element1value$value_template': 'Foo',
|
||||
'request$post_data$element1value$value_python': '',
|
||||
'request$post_data$element2key': 'user/last_name',
|
||||
'request$post_data$element2value$value_template': 'Bar',
|
||||
'request$post_data$element2value$value_python': '',
|
||||
'request$post_data$element3key': 'user/0',
|
||||
}
|
||||
resp = app.get('/api/preview-json-payload', params=params)
|
||||
resp.pyquery('div.payload-preview div.errornotice').length == 0
|
||||
assert resp.pyquery('div.payload-preview').text() == '{"user": {"first_name": "Foo","last_name": "Bar",}}'
|
||||
|
||||
params.update(
|
||||
{
|
||||
'request$post_data$element3value$value_template': 'value',
|
||||
'request$post_data$element3value$value_python': '',
|
||||
}
|
||||
)
|
||||
resp = app.get('/api/preview-json-payload', params=params)
|
||||
|
||||
assert resp.pyquery('div.payload-preview div.errornotice').length == 1
|
||||
assert 'Unable to generate payload' in resp.pyquery('div.payload-preview div.errornotice').text()
|
||||
assert (
|
||||
'Following error occured: there is a mix between lists and dicts'
|
||||
in resp.pyquery('div.payload-preview div.errornotice').text()
|
||||
)
|
||||
|
||||
params = {
|
||||
'post_data$element1key': '0',
|
||||
'post_data$element1value$value_template': 'Foo',
|
||||
'post_data$element1value$value_python': '',
|
||||
'post_data$element2key': '1',
|
||||
'post_data$element2value$value_template': '{{ form_name }}',
|
||||
'post_data$element2value$value_python': '',
|
||||
'post_data$element3key': '2',
|
||||
'post_data$element3value$value_template': '',
|
||||
}
|
||||
resp = app.get('/api/preview-json-payload', params=params)
|
||||
assert resp.pyquery('div.payload-preview').text() == '["Foo",{{ form_name }},"",]'
|
||||
|
|
|
@ -297,13 +297,13 @@ def test_webservice_with_unflattened_payload_keys(http_requests, pub):
|
|||
wscall.request = {
|
||||
'method': 'POST',
|
||||
'url': 'http://remote.example.net/json',
|
||||
'post_data': {'foo/0': 'first', 'foo/1': 'second', 'bar': 'example'},
|
||||
'post_data': {'foo/0': 'first', 'foo/1': 'second', 'bar': 'example', 'foo/2': ''},
|
||||
}
|
||||
wscall.store()
|
||||
|
||||
wscall.call()
|
||||
assert http_requests.get_last('url') == 'http://remote.example.net/json'
|
||||
assert http_requests.get_last('body') == '{"bar": "example", "foo": ["first", "second"]}'
|
||||
assert http_requests.get_last('body') == '{"bar": "example", "foo": ["first", "second", ""]}'
|
||||
assert http_requests.count() == 1
|
||||
|
||||
wscall.request = {
|
||||
|
|
69
wcs/api.py
69
wcs/api.py
|
@ -18,6 +18,7 @@ import base64
|
|||
import copy
|
||||
import datetime
|
||||
import json
|
||||
import re
|
||||
import urllib.parse
|
||||
|
||||
from django.http import HttpResponse, HttpResponseBadRequest, JsonResponse
|
||||
|
@ -26,6 +27,7 @@ from django.utils.timezone import localtime, make_naive
|
|||
from quixote import get_publisher, get_request, get_response, get_session, redirect
|
||||
from quixote.directory import Directory
|
||||
from quixote.errors import MethodNotAllowedError, RequestError
|
||||
from quixote.html import TemplateIO, htmltext
|
||||
|
||||
import wcs.qommon.storage as st
|
||||
from wcs.admin.settings import UserFieldsFormDef
|
||||
|
@ -56,6 +58,7 @@ from wcs.sql_criterias import (
|
|||
StrictNotEqual,
|
||||
)
|
||||
from wcs.workflows import ContentSnapshotPart
|
||||
from wcs.wscalls import UnflattenKeysException, unflatten_keys
|
||||
|
||||
from .backoffice.data_management import CardPage as BackofficeCardPage
|
||||
from .backoffice.management import FormPage as BackofficeFormPage
|
||||
|
@ -1406,6 +1409,7 @@ class ApiDirectory(Directory):
|
|||
'geojson',
|
||||
'jobs',
|
||||
('card-file-by-token', 'card_file_by_token'),
|
||||
('preview-json-payload', 'preview_json_payload'),
|
||||
('sign-url-token', 'sign_url_token'),
|
||||
]
|
||||
|
||||
|
@ -1433,6 +1437,71 @@ class ApiDirectory(Directory):
|
|||
get_response().set_content_type('application/json')
|
||||
return json.dumps({'err': 0, 'data': list_roles})
|
||||
|
||||
def preview_json_payload(self):
|
||||
if not (get_request().user and get_request().user.can_go_in_admin()):
|
||||
raise AccessForbiddenError('user has no access to backoffice')
|
||||
|
||||
def parse_payload():
|
||||
payload = {}
|
||||
for param, value in get_request().form.items():
|
||||
# skip elements which are not part of payload
|
||||
if 'post_data$element' not in param or param.endswith('value_python'):
|
||||
continue
|
||||
prefix, order, field = re.split(r'(\d)(?!\d)', param) # noqa pylint: disable=unused-variable
|
||||
# skip elements that aren't ordered
|
||||
if not order:
|
||||
continue
|
||||
|
||||
if order not in payload:
|
||||
payload[order] = []
|
||||
|
||||
if field == 'key':
|
||||
# skip empty keys
|
||||
if not value:
|
||||
continue
|
||||
# insert key on first position
|
||||
payload[order].insert(0, value)
|
||||
else:
|
||||
payload[order].append(value)
|
||||
return dict([v for v in payload.values() if len(v) > 1])
|
||||
|
||||
def format_payload(o, html=htmltext('')):
|
||||
if isinstance(o, (list, tuple)):
|
||||
html += htmltext('[<span class="payload-preview--obj">')
|
||||
for item in o:
|
||||
html = format_payload(item, html)
|
||||
html += htmltext('</span>]')
|
||||
elif isinstance(o, dict):
|
||||
html += htmltext('{<span class="payload-preview--obj">')
|
||||
for k, v in o.items():
|
||||
html += htmltext('<span class="payload-preview--key">"%s"</span>: ' % k)
|
||||
html = format_payload(v, html)
|
||||
html += htmltext('</span>}')
|
||||
else:
|
||||
# check if it's empty string, a template with text around or just text
|
||||
if not o or re.sub('^({[{|%]).+([%|}]})$', '', o):
|
||||
# and put double quotes
|
||||
o = '"%s"' % o
|
||||
html += htmltext('<span class="payload-preview--value">%s</span>' % o)
|
||||
html += htmltext('<span class="payload-preview--item-separator">,</span>')
|
||||
return html
|
||||
|
||||
payload = parse_payload()
|
||||
r = TemplateIO(html=True)
|
||||
r += htmltext('<h2>%s</h2>') % _('Payload preview')
|
||||
r += htmltext('<div class="payload-preview">')
|
||||
try:
|
||||
unflattened_payload = unflatten_keys(payload)
|
||||
r += format_payload(unflattened_payload)
|
||||
except UnflattenKeysException as e:
|
||||
r += htmltext('<div class="errornotice"><p>%s</p><p>%s %s</p></div>') % (
|
||||
_('Unable to generate payload.'),
|
||||
_('Following error occured: '),
|
||||
e,
|
||||
)
|
||||
r += htmltext('</div>')
|
||||
return r.getvalue()
|
||||
|
||||
def _q_traverse(self, path):
|
||||
get_request().is_json_marker = True
|
||||
return super()._q_traverse(path)
|
||||
|
|
|
@ -3131,3 +3131,31 @@ ul.objects-list.single-links li a.link-action-icon.duplicate {
|
|||
content: "\f24d"; /* clone */
|
||||
}
|
||||
}
|
||||
|
||||
span.payload-preview {
|
||||
&--value {
|
||||
font-family: Monospace;
|
||||
& + & {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
&--value + &--key::before {
|
||||
content: '\a';
|
||||
white-space: pre;
|
||||
}
|
||||
&--obj {
|
||||
margin-left: 1em;
|
||||
display: block;
|
||||
}
|
||||
&--item-separator + &--key::before, &--item-separator + &--value::before {
|
||||
content: '\a';
|
||||
white-space: pre;
|
||||
}
|
||||
&--obj + &--key::before, &--obj + &--value::before {
|
||||
content: ',\a';
|
||||
white-space: pre;
|
||||
}
|
||||
&--item-separator:last-child {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -305,7 +305,7 @@ $(function() {
|
|||
|
||||
$('div.WidgetDict[data-widget-name*="post_data"] input[type="text"]').on('change', function() {
|
||||
var $widget = $(this).parents('div.WidgetDict');
|
||||
var url = '/preview-json-payload?' + $('input', $widget).serialize();
|
||||
var url = '/api/preview-json-payload?' + $('input', $widget).serialize();
|
||||
var preview_button_id = 'payload-preview-button';
|
||||
var preview_button_selector = 'a#' + preview_button_id;
|
||||
if ($widget.find(preview_button_selector).length) {
|
||||
|
|
45
wcs/root.py
45
wcs/root.py
|
@ -21,11 +21,8 @@ from importlib import import_module
|
|||
|
||||
from quixote import get_publisher, get_request, get_response, get_session, get_session_manager, redirect
|
||||
from quixote.directory import Directory
|
||||
from quixote.html import TemplateIO, htmltext
|
||||
from quixote.util import StaticDirectory
|
||||
|
||||
from wcs.wscalls import UnflattenKeysException, unflatten_keys
|
||||
|
||||
from . import portfolio
|
||||
from .api import ApiDirectory
|
||||
from .categories import Category
|
||||
|
@ -216,7 +213,6 @@ class RootDirectory(Directory):
|
|||
'fargo',
|
||||
'static',
|
||||
'actions',
|
||||
('preview-json-payload', 'preview_json_payload'),
|
||||
('r', 'tiny_redirect'),
|
||||
]
|
||||
|
||||
|
@ -304,47 +300,6 @@ class RootDirectory(Directory):
|
|||
get_response().set_content_type('text/plain')
|
||||
return json.dumps(results, cls=misc.JSONEncoder)
|
||||
|
||||
def preview_json_payload(self):
|
||||
def parse_payload():
|
||||
payload = {}
|
||||
for param, value in get_request().form.items():
|
||||
# skip elements which are not part of payload
|
||||
if 'post_data$element' not in param or param.endswith('value_python'):
|
||||
continue
|
||||
prefix, order, field = re.split(r'(\d)(?!\d)', param) # noqa pylint: disable=unused-variable
|
||||
# skip elements that aren't ordered
|
||||
if not order:
|
||||
continue
|
||||
|
||||
if order not in payload:
|
||||
payload[order] = []
|
||||
|
||||
if field == 'key':
|
||||
# skip empty keys
|
||||
if not value:
|
||||
continue
|
||||
# insert key on first position
|
||||
payload[order].insert(0, value)
|
||||
else:
|
||||
payload[order].append(value)
|
||||
return dict([v for v in payload.values() if len(v) > 1])
|
||||
|
||||
r = TemplateIO(html=True)
|
||||
payload = parse_payload()
|
||||
r += htmltext('<h2>%s</h2>') % _('Payload preview')
|
||||
r += htmltext('<div class="payload-preview">')
|
||||
try:
|
||||
dump = json.dumps(unflatten_keys(payload), ensure_ascii=False, indent=2)
|
||||
r += htmltext('<pre>%s</pre>') % dump.replace('"{{', '{{').replace('}}"', '}}')
|
||||
except UnflattenKeysException as e:
|
||||
r += htmltext('<div class="errornotice"><p>%s</p><p>%s<code>%s</code></p></div>') % (
|
||||
_('Unable to generate payload.'),
|
||||
_('Following error occured: '),
|
||||
e,
|
||||
)
|
||||
r += htmltext('</div>')
|
||||
return r.getvalue()
|
||||
|
||||
def feed_substitution_parts(self):
|
||||
get_publisher().substitutions.feed(get_session())
|
||||
get_publisher().substitutions.feed(get_request().user)
|
||||
|
|
Loading…
Reference in New Issue