0001-misc-add-a-jsonp-endpoint-for-datasources-use-it-for.patch
tests/test_api.py | ||
---|---|---|
1438 | 1438 |
secret, orig = get_secret_and_orig('https://api.example.com/endpoint/') |
1439 | 1439 |
assert secret == '1234' |
1440 | 1440 |
assert orig == 'example.net' |
1441 | ||
1442 |
def test_datasources_jsonp(pub): |
|
1443 |
NamedDataSource.wipe() |
|
1444 |
data_source = NamedDataSource(name='foobar') |
|
1445 |
source = [{'id': '1', 'text': 'foo', 'more': 'XXX'}, |
|
1446 |
{'id': '2', 'text': 'bar', 'more': 'YYY'}] |
|
1447 |
data_source.data_source = {'type': 'formula', 'value': repr(source)} |
|
1448 |
data_source.store() |
|
1449 | ||
1450 |
get_app(pub).get('/api/datasources/xxx', status=404) |
|
1451 |
get_app(pub).get('/api/datasources/xxx/', status=404) |
|
1452 |
resp = get_app(pub).get('/api/datasources/foobar/') |
|
1453 |
assert len(resp.json['data']) == 2 |
|
1454 |
resp = get_app(pub).get('/api/datasources/foobar/?q=fo') |
|
1455 |
resp_data = resp.body |
|
1456 |
assert len(resp.json['data']) == 1 |
|
1457 |
resp = get_app(pub).get('/api/datasources/foobar/?q=fo&callback=cb123') |
|
1458 |
assert resp_data in resp.body |
|
1459 |
assert resp.body.startswith('cb123(') |
|
1460 | ||
1461 |
# test custom handling of jsonp sources (redirect) |
|
1462 |
data_source.data_source = {'type': 'jsonp', 'value': 'http://remote.example.net/json'} |
|
1463 |
data_source.store() |
|
1464 |
resp = get_app(pub).get('/api/datasources/foobar/?q=fo&callback=cb123') |
|
1465 |
assert resp.location == 'http://remote.example.net/json?q=fo&callback=cb123' |
tests/test_form_pages.py | ||
---|---|---|
3080 | 3080 |
assert not hasattr(formdata.data['0'], 'metadata') |
3081 | 3081 |
assert not '0_structured' in formdata.data |
3082 | 3082 | |
3083 | ||
3084 | 3083 |
def test_form_string_field_autocomplete(pub): |
3085 | 3084 |
formdef = create_formdef() |
3086 | 3085 |
formdef.fields = [fields.StringField(id='0', label='string', type='string', required=False)] |
... | ... | |
3105 | 3104 |
assert ').autocomplete({' in resp.body |
3106 | 3105 |
assert 'http://example.net' in resp.body |
3107 | 3106 | |
3107 |
# named data source |
|
3108 |
NamedDataSource.wipe() |
|
3109 |
data_source = NamedDataSource(name='foobar') |
|
3110 |
data_source.data_source = {'type': 'formula', 'value': repr([])} |
|
3111 |
data_source.store() |
|
3112 |
formdef.fields[0].data_source = {'type': 'foobar', 'value': ''} |
|
3113 |
formdef.store() |
|
3114 |
resp = get_app(pub).get('/test/') |
|
3115 |
assert ').autocomplete({' in resp.body |
|
3116 |
assert '/api/datasources/foobar/' in resp.body |
|
3117 | ||
3108 | 3118 |
def test_form_workflow_trigger(pub): |
3109 | 3119 |
user = create_user(pub) |
3110 | 3120 |
wcs/api.py | ||
---|---|---|
19 | 19 |
import urllib2 |
20 | 20 |
import sys |
21 | 21 | |
22 |
from quixote import get_request, get_publisher, get_response |
|
22 |
from quixote import get_request, get_publisher, get_response, redirect
|
|
23 | 23 |
from quixote.directory import Directory |
24 | 24 |
from qommon import misc |
25 | 25 |
from qommon.errors import (AccessForbiddenError, QueryError, TraversalError, |
26 | 26 |
UnknownNameIdAccessForbiddenError) |
27 | 27 | |
28 | 28 |
from wcs.categories import Category |
29 |
from wcs.data_sources import NamedDataSource |
|
29 | 30 |
from wcs.formdef import FormDef |
30 | 31 |
from wcs.roles import Role, logged_users_role |
31 | 32 |
from wcs.forms.common import FormStatusPage |
... | ... | |
569 | 570 |
return json.dumps(data) |
570 | 571 | |
571 | 572 | |
573 |
class ApiDataSourceDirectory(Directory): |
|
574 |
_q_exports = [''] |
|
575 | ||
576 |
def __init__(self, datasource): |
|
577 |
self.datasource = datasource |
|
578 | ||
579 |
def _q_index(self): |
|
580 |
dtype = self.datasource.data_source.get('type') |
|
581 |
if dtype == 'jsonp': |
|
582 |
# redirect to the source |
|
583 |
url = self.datasource.data_source.get('value') |
|
584 |
if not '?' in url: |
|
585 |
url += '?' |
|
586 |
url += get_request().get_query() |
|
587 |
return redirect(url) |
|
588 |
query = get_request().form.get('q', '').lower() |
|
589 |
items = [x[-1] for x in self.datasource.get_items() if query in x[1].lower()] |
|
590 |
return misc.json_response({'data': items}) |
|
591 | ||
592 | ||
593 |
class ApiDataSourcesDirectory(Directory): |
|
594 |
def _q_lookup(self, component): |
|
595 |
try: |
|
596 |
return ApiDataSourceDirectory(NamedDataSource.get_by_slug(component)) |
|
597 |
except KeyError: |
|
598 |
raise TraversalError() |
|
599 | ||
600 | ||
572 | 601 |
class ApiDirectory(Directory): |
573 | 602 |
_q_exports = ['forms', 'roles', ('reverse-geocoding', 'reverse_geocoding'), |
574 |
'formdefs', 'categories', 'user', 'users', 'code'] |
|
603 |
'formdefs', 'categories', 'user', 'users', 'code', |
|
604 |
'datasources'] |
|
575 | 605 | |
576 | 606 |
forms = ApiFormsDirectory() |
577 | 607 |
formdefs = ApiFormdefsDirectory() |
... | ... | |
579 | 609 |
user = ApiUserDirectory() |
580 | 610 |
users = ApiUsersDirectory() |
581 | 611 |
code = ApiTrackingCodeDirectory() |
612 |
datasources = ApiDataSourcesDirectory() |
|
582 | 613 | |
583 | 614 |
def reverse_geocoding(self): |
584 | 615 |
try: |
wcs/data_sources.py | ||
---|---|---|
39 | 39 | |
40 | 40 |
class DataSourceSelectionWidget(CompositeWidget): |
41 | 41 |
def __init__(self, name, value=None, allow_jsonp=True, |
42 |
allow_named_sources=True, **kwargs): |
|
42 |
allow_named_sources=True, require_jsonp=False, **kwargs):
|
|
43 | 43 |
CompositeWidget.__init__(self, name, value, **kwargs) |
44 | 44 | |
45 | 45 |
if not value: |
46 | 46 |
value = {} |
47 | 47 | |
48 |
options = [('none', _('None')), |
|
49 |
('formula', _('Python Expression')), |
|
50 |
('json', _('JSON URL'))] |
|
48 |
options = [('none', _('None'))] |
|
49 |
if not require_jsonp: |
|
50 |
options.append(('formula', _('Python Expression'))) |
|
51 |
options.append(('json', _('JSON URL'))) |
|
51 | 52 |
if allow_jsonp: |
52 | 53 |
options.append(('jsonp', _('JSONP URL'))) |
53 | 54 |
if allow_named_sources: |
... | ... | |
245 | 246 |
def get_substitution_variables(cls): |
246 | 247 |
return {'data_source': DataSourcesSubstitutionProxy()} |
247 | 248 | |
249 |
def get_items(self): |
|
250 |
return get_items(self.data_source) |
|
251 | ||
248 | 252 | |
249 | 253 |
class DataSourcesSubstitutionProxy(object): |
250 | 254 |
def __getattr__(self, attr): |
wcs/fields.py | ||
---|---|---|
564 | 564 |
if real_data_source.get('type') == 'jsonp': |
565 | 565 |
kwargs['url'] = real_data_source.get('value') |
566 | 566 |
self.widget_class = AutocompleteStringWidget |
567 |
elif self.data_source.get('type') not in ('none', 'formula', 'json'): |
|
568 |
# named data source |
|
569 |
kwargs['url'] = root_url = get_publisher().get_root_url() + 'api/datasources/%s/' % self.data_source.get('type') |
|
570 |
self.widget_class = AutocompleteStringWidget |
|
567 | 571 | |
568 | 572 |
def fill_admin_form(self, form): |
569 | 573 |
WidgetField.fill_admin_form(self, form) |
... | ... | |
573 | 577 |
value=self.validation, advanced=(not self.validation)) |
574 | 578 |
form.add(data_sources.DataSourceSelectionWidget, 'data_source', |
575 | 579 |
value=self.data_source, |
580 |
require_jsonp=True, |
|
576 | 581 |
title=_('Data Source'), |
577 | 582 |
hint=_('This will allow autocompletion from an external source.'), |
578 | 583 |
advanced=is_datasource_advanced(self.data_source), |
wcs/qommon/form.py | ||
---|---|---|
1972 | 1972 |
url = None |
1973 | 1973 | |
1974 | 1974 |
def __init__(self, *args, **kwargs): |
1975 |
self.url = kwargs.pop('url', None) |
|
1975 | 1976 |
WcsExtraStringWidget.__init__(self, *args, **kwargs) |
1976 |
if kwargs.get('url'): |
|
1977 |
self.url = kwargs.get('url') |
|
1978 | 1977 | |
1979 | 1978 |
def render_content(self): |
1980 | 1979 |
get_response().add_javascript(['jquery.js', 'jquery-ui.js']) |
1981 |
- |