0001-query-check-slug-and-name-unicity-in-forms-43002.patch
passerelle/apps/arcgis/forms.py | ||
---|---|---|
1 |
# passerelle - uniform access to multiple data sources and services |
|
2 |
# Copyright (C) 2020 Entr'ouvert |
|
3 |
# |
|
4 |
# This program is free software: you can redistribute it and/or modify it |
|
5 |
# under the terms of the GNU Affero General Public License as published |
|
6 |
# by the Free Software Foundation, either version 3 of the License, or |
|
7 |
# (at your option) any later version. |
|
8 |
# |
|
9 |
# This program is distributed in the hope that it will be useful, |
|
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 |
# GNU Affero General Public License for more details. |
|
13 |
# |
|
14 |
# You should have received a copy of the GNU Affero General Public License |
|
15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
16 | ||
17 |
from django import forms |
|
18 | ||
19 |
from passerelle.base.forms import BaseQueryFormMixin |
|
20 |
from . import models |
|
21 | ||
22 | ||
23 |
class QueryForm(BaseQueryFormMixin, forms.ModelForm): |
|
24 |
class Meta: |
|
25 |
model = models.Query |
|
26 |
fields = '__all__' |
|
27 |
exclude = ['resource'] |
passerelle/apps/arcgis/views.py | ||
---|---|---|
14 | 14 |
# You should have received a copy of the GNU Affero General Public License |
15 | 15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
16 | 16 | |
17 |
from django import forms |
|
18 | 17 |
from django.views.generic import UpdateView, CreateView, DeleteView |
19 | 18 | |
20 | 19 |
from passerelle.base.mixins import ResourceChildViewMixin |
21 | 20 | |
22 | 21 |
from . import models |
23 | ||
24 | ||
25 |
class QueryForm(forms.ModelForm): |
|
26 |
class Meta: |
|
27 |
model = models.Query |
|
28 |
fields = '__all__' |
|
29 |
exclude = ['resource'] |
|
22 |
from .forms import QueryForm |
|
30 | 23 | |
31 | 24 | |
32 | 25 |
class QueryNew(ResourceChildViewMixin, CreateView): |
33 | 26 |
model = models.Query |
34 | 27 |
form_class = QueryForm |
35 | 28 | |
36 |
def form_valid(self, form): |
|
37 |
form.instance.resource = self.resource |
|
38 |
return super(QueryNew, self).form_valid(form) |
|
29 |
def get_form_kwargs(self): |
|
30 |
kwargs = super(QueryNew, self).get_form_kwargs() |
|
31 |
kwargs['instance'] = self.model(resource=self.resource) |
|
32 |
return kwargs |
|
39 | 33 | |
40 | 34 |
def get_changed_url(self): |
41 | 35 |
return self.object.get_absolute_url() |
passerelle/apps/opendatasoft/forms.py | ||
---|---|---|
1 |
# passerelle - uniform access to multiple data sources and services |
|
2 |
# Copyright (C) 2020 Entr'ouvert |
|
3 |
# |
|
4 |
# This program is free software: you can redistribute it and/or modify it |
|
5 |
# under the terms of the GNU Affero General Public License as published |
|
6 |
# by the Free Software Foundation, either version 3 of the License, or |
|
7 |
# (at your option) any later version. |
|
8 |
# |
|
9 |
# This program is distributed in the hope that it will be useful, |
|
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 |
# GNU Affero General Public License for more details. |
|
13 |
# |
|
14 |
# You should have received a copy of the GNU Affero General Public License |
|
15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
16 | ||
17 |
from django import forms |
|
18 | ||
19 |
from passerelle.base.forms import BaseQueryFormMixin |
|
20 |
from . import models |
|
21 | ||
22 | ||
23 |
class QueryForm(BaseQueryFormMixin, forms.ModelForm): |
|
24 |
class Meta: |
|
25 |
model = models.Query |
|
26 |
fields = '__all__' |
|
27 |
exclude = ['resource'] |
passerelle/apps/opendatasoft/views.py | ||
---|---|---|
14 | 14 |
# You should have received a copy of the GNU Affero General Public License |
15 | 15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
16 | 16 | |
17 |
from django import forms |
|
18 | 17 |
from django.views.generic import UpdateView, CreateView, DeleteView |
19 | 18 | |
20 | 19 |
from passerelle.base.mixins import ResourceChildViewMixin |
21 | 20 | |
22 | 21 |
from . import models |
23 | ||
24 | ||
25 |
class QueryForm(forms.ModelForm): |
|
26 |
class Meta: |
|
27 |
model = models.Query |
|
28 |
fields = '__all__' |
|
29 |
exclude = ['resource'] |
|
22 |
from .forms import QueryForm |
|
30 | 23 | |
31 | 24 | |
32 | 25 |
class QueryNew(ResourceChildViewMixin, CreateView): |
... | ... | |
34 | 27 |
form_class = QueryForm |
35 | 28 |
template_name = "passerelle/manage/resource_child_form.html" |
36 | 29 | |
37 |
def form_valid(self, form): |
|
38 |
form.instance.resource = self.resource |
|
39 |
return super(QueryNew, self).form_valid(form) |
|
30 |
def get_form_kwargs(self): |
|
31 |
kwargs = super(QueryNew, self).get_form_kwargs() |
|
32 |
kwargs['instance'] = self.model(resource=self.resource) |
|
33 |
return kwargs |
|
40 | 34 | |
41 | 35 | |
42 | 36 |
class QueryEdit(ResourceChildViewMixin, UpdateView): |
passerelle/apps/opengis/forms.py | ||
---|---|---|
19 | 19 |
from django import forms |
20 | 20 |
from django.utils.translation import ugettext_lazy as _ |
21 | 21 | |
22 |
from passerelle.base.forms import BaseQueryFormMixin |
|
22 | 23 |
from . import models |
23 | 24 | |
24 | 25 | |
25 |
class QueryForm(forms.ModelForm): |
|
26 |
class QueryForm(BaseQueryFormMixin, forms.ModelForm):
|
|
26 | 27 |
class Meta: |
27 | 28 |
model = models.Query |
28 | 29 |
fields = '__all__' |
passerelle/apps/opengis/views.py | ||
---|---|---|
26 | 26 |
model = models.Query |
27 | 27 |
form_class = QueryForm |
28 | 28 | |
29 |
def form_valid(self, form): |
|
30 |
form.instance.resource = self.resource |
|
31 |
return super(QueryNew, self).form_valid(form) |
|
29 |
def get_form_kwargs(self): |
|
30 |
kwargs = super(QueryNew, self).get_form_kwargs() |
|
31 |
kwargs['instance'] = self.model(resource=self.resource) |
|
32 |
return kwargs |
|
32 | 33 | |
33 | 34 |
def get_changed_url(self): |
34 | 35 |
return self.object.get_absolute_url() |
passerelle/base/forms.py | ||
---|---|---|
33 | 33 |
class ImportSiteForm(forms.Form): |
34 | 34 |
site_json = forms.FileField(label=_('Site Export File')) |
35 | 35 |
import_users = forms.BooleanField(label=_('Import users and access rights'), required=False) |
36 | ||
37 | ||
38 |
class BaseQueryFormMixin(object): |
|
39 |
def clean_slug(self): |
|
40 |
slug = self.cleaned_data['slug'] |
|
41 | ||
42 |
queryset = self.instance.resource.queries.filter(slug=slug) |
|
43 |
if self.instance.pk: |
|
44 |
queryset = queryset.exclude(pk=self.instance.pk) |
|
45 |
if queryset.exists(): |
|
46 |
raise forms.ValidationError(_('A query with this slug already exists')) |
|
47 | ||
48 |
return slug |
|
49 | ||
50 |
def clean_name(self): |
|
51 |
name = self.cleaned_data['name'] |
|
52 | ||
53 |
queryset = self.instance.resource.queries.filter(name=name) |
|
54 |
if self.instance.pk: |
|
55 |
queryset = queryset.exclude(pk=self.instance.pk) |
|
56 |
if queryset.exists(): |
|
57 |
raise forms.ValidationError(_('A query with this name already exists')) |
|
58 | ||
59 |
return name |
tests/test_arcgis.py | ||
---|---|---|
6 | 6 |
import mock |
7 | 7 |
import utils |
8 | 8 | |
9 |
from django.core.exceptions import ValidationError
|
|
9 |
from django.contrib.auth.models import User
|
|
10 | 10 |
from django.contrib.contenttypes.models import ContentType |
11 |
from django.core.exceptions import ValidationError |
|
11 | 12 | |
12 | 13 |
from passerelle.apps.arcgis.models import ArcGIS, validate_where, SqlFormatter, Query |
13 | 14 |
from passerelle.base.models import ApiUser, AccessRight |
14 | 15 |
from passerelle.utils import import_site |
15 | 16 | |
17 |
from test_manager import login |
|
18 | ||
19 |
pytestmark = pytest.mark.django_db |
|
20 | ||
16 | 21 |
# from http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/fold/serv/MapServer/1 |
17 | 22 |
STATES = '''{ |
18 | 23 |
"fieldAliases" : { |
... | ... | |
80 | 85 | |
81 | 86 | |
82 | 87 |
@pytest.fixture |
83 |
def arcgis(db): |
|
88 |
def admin_user(): |
|
89 |
return User.objects.create_superuser('admin', email=None, password='admin') |
|
90 | ||
91 | ||
92 |
@pytest.fixture |
|
93 |
def arcgis(): |
|
84 | 94 |
return ArcGIS.objects.create(slug='test', |
85 | 95 |
base_url='https://arcgis.example.net/') |
86 | 96 | |
... | ... | |
417 | 427 |
assert '<span class="param-name">adress</span>' in resp.text |
418 | 428 | |
419 | 429 | |
430 |
def test_arcgis_query_unicity(admin_user, app, arcgis): |
|
431 |
query = Query.objects.create( |
|
432 |
resource=arcgis, |
|
433 |
name='Test Query', |
|
434 |
slug='test-query', |
|
435 |
) |
|
436 | ||
437 |
arcgis2 = ArcGIS.objects.create( |
|
438 |
slug='test2', |
|
439 |
base_url='https://arcgis.example.net/') |
|
440 |
Query.objects.create( |
|
441 |
resource=arcgis2, |
|
442 |
name='Foo Bar', |
|
443 |
slug='foo-bar', |
|
444 |
) |
|
445 | ||
446 |
app = login(app) |
|
447 |
resp = app.get('/manage/arcgis/%s/query/new/' % arcgis.slug) |
|
448 |
resp.form['slug'] = query.slug |
|
449 |
resp.form['name'] = 'Foo Bar' |
|
450 |
resp.form['service'] = 'Foo' |
|
451 |
resp = resp.form.submit() |
|
452 |
assert resp.status_code == 200 |
|
453 |
assert Query.objects.filter(resource=arcgis).count() == 1 |
|
454 |
assert 'A query with this slug already exists' in resp.text |
|
455 |
resp.form['slug'] = 'foo-bar' |
|
456 |
resp.form['name'] = query.name |
|
457 |
resp.form['service'] = 'Foo' |
|
458 |
resp = resp.form.submit() |
|
459 |
assert Query.objects.filter(resource=arcgis).count() == 1 |
|
460 |
assert resp.status_code == 200 |
|
461 |
assert 'A query with this name already exists' in resp.text |
|
462 |
resp.form['slug'] = 'foo-bar' |
|
463 |
resp.form['name'] = 'Foo Bar' |
|
464 |
resp.form['service'] = 'Foo' |
|
465 |
resp = resp.form.submit() |
|
466 |
assert resp.status_code == 302 |
|
467 |
assert Query.objects.filter(resource=arcgis).count() == 2 |
|
468 |
new_query = Query.objects.latest('pk') |
|
469 |
assert new_query.resource == arcgis |
|
470 | ||
471 |
resp = app.get('/manage/arcgis/%s/query/%s/' % (arcgis.slug, new_query.pk)) |
|
472 |
resp.form['slug'] = query.slug |
|
473 |
resp.form['name'] = 'Foo Bar' |
|
474 |
resp = resp.form.submit() |
|
475 |
assert resp.status_code == 200 |
|
476 |
assert 'A query with this slug already exists' in resp.text |
|
477 |
resp.form['slug'] = 'foo-bar' |
|
478 |
resp.form['name'] = query.name |
|
479 |
resp = resp.form.submit() |
|
480 |
assert resp.status_code == 200 |
|
481 |
assert 'A query with this name already exists' in resp.text |
|
482 |
resp.form['slug'] = 'foo-bar' |
|
483 |
resp.form['name'] = 'Foo Bar' |
|
484 |
resp = resp.form.submit() |
|
485 |
assert resp.status_code == 302 |
|
486 | ||
487 | ||
420 | 488 |
def test_export_import(query): |
421 | 489 |
assert ArcGIS.objects.count() == 1 |
422 | 490 |
assert Query.objects.count() == 1 |
tests/test_opendatasoft.py | ||
---|---|---|
21 | 21 | |
22 | 22 |
import utils |
23 | 23 | |
24 |
from django.contrib.auth.models import User |
|
25 | ||
24 | 26 |
from passerelle.apps.opendatasoft.models import OpenDataSoft, Query |
25 | 27 |
from passerelle.utils import import_site |
26 | 28 | |
27 |
from test_manager import login, admin_user |
|
29 |
from test_manager import login |
|
30 | ||
31 |
pytestmark = pytest.mark.django_db |
|
28 | 32 | |
29 | 33 | |
30 | 34 |
FAKED_CONTENT_Q_SEARCH = json.dumps({ |
... | ... | |
155 | 159 | |
156 | 160 | |
157 | 161 |
@pytest.fixture |
158 |
def connector(db): |
|
162 |
def admin_user(): |
|
163 |
return User.objects.create_superuser('admin', email=None, password='admin') |
|
164 | ||
165 | ||
166 |
@pytest.fixture |
|
167 |
def connector(): |
|
159 | 168 |
return utils.setup_access_rights(OpenDataSoft.objects.create( |
160 | 169 |
slug='my_connector', |
161 | 170 |
api_key='my_secret', |
... | ... | |
289 | 298 |
resp = app.get(endpoint, params=params, status=200) |
290 | 299 |
assert len(resp.json['data']) == 1 |
291 | 300 |
assert resp.json['data'][0]['text'] == "19 RUE DE L'AUBEPINE Lipsheim" |
301 | ||
302 | ||
303 |
def test_opendatasoft_query_unicity(admin_user, app, connector, query): |
|
304 |
connector2 = OpenDataSoft.objects.create( |
|
305 |
slug='my_connector2', |
|
306 |
api_key='my_secret', |
|
307 |
) |
|
308 |
Query.objects.create( |
|
309 |
resource=connector2, |
|
310 |
name='Foo Bar', |
|
311 |
slug='foo-bar', |
|
312 |
) |
|
313 | ||
314 |
app = login(app) |
|
315 |
resp = app.get('/manage/opendatasoft/%s/query/new/' % connector.slug) |
|
316 |
resp.form['slug'] = query.slug |
|
317 |
resp.form['name'] = 'Foo Bar' |
|
318 |
resp.form['dataset'] = 'my-dataset' |
|
319 |
resp = resp.form.submit() |
|
320 |
assert resp.status_code == 200 |
|
321 |
assert Query.objects.filter(resource=connector).count() == 1 |
|
322 |
assert 'A query with this slug already exists' in resp.text |
|
323 |
resp.form['slug'] = 'foo-bar' |
|
324 |
resp.form['name'] = query.name |
|
325 |
resp.form['dataset'] = 'my-dataset' |
|
326 |
resp = resp.form.submit() |
|
327 |
assert Query.objects.filter(resource=connector).count() == 1 |
|
328 |
assert resp.status_code == 200 |
|
329 |
assert 'A query with this name already exists' in resp.text |
|
330 |
resp.form['slug'] = 'foo-bar' |
|
331 |
resp.form['name'] = 'Foo Bar' |
|
332 |
resp.form['dataset'] = 'my-dataset' |
|
333 |
resp = resp.form.submit() |
|
334 |
assert resp.status_code == 302 |
|
335 |
assert Query.objects.filter(resource=connector).count() == 2 |
|
336 |
new_query = Query.objects.latest('pk') |
|
337 |
assert new_query.resource == connector |
|
338 | ||
339 |
resp = app.get('/manage/opendatasoft/%s/query/%s/' % (connector.slug, new_query.pk)) |
|
340 |
resp.form['slug'] = query.slug |
|
341 |
resp.form['name'] = 'Foo Bar' |
|
342 |
resp = resp.form.submit() |
|
343 |
assert resp.status_code == 200 |
|
344 |
assert 'A query with this slug already exists' in resp.text |
|
345 |
resp.form['slug'] = 'foo-bar' |
|
346 |
resp.form['name'] = query.name |
|
347 |
resp = resp.form.submit() |
|
348 |
assert resp.status_code == 200 |
|
349 |
assert 'A query with this name already exists' in resp.text |
|
350 |
resp.form['slug'] = 'foo-bar' |
|
351 |
resp.form['name'] = 'Foo Bar' |
|
352 |
resp = resp.form.submit() |
|
353 |
assert resp.status_code == 302 |
tests/test_opengis.py | ||
---|---|---|
2 | 2 |
import mock |
3 | 3 |
import pytest |
4 | 4 | |
5 |
from django.contrib.auth.models import User |
|
5 | 6 |
from django.core.management import call_command |
6 | 7 | |
7 | 8 |
from passerelle.apps.opengis.models import OpenGIS, Query, FeatureCache |
8 | 9 |
from passerelle.base.models import Job |
9 | 10 |
from passerelle.utils import import_site |
10 | 11 | |
12 |
from test_manager import login |
|
13 | ||
11 | 14 |
import utils |
12 | 15 | |
16 |
pytestmark = pytest.mark.django_db |
|
17 | ||
13 | 18 |
FAKE_FEATURE_INFO = '''<?xml version="1.0" encoding="UTF-8"?> |
14 | 19 |
<msGMLOutput |
15 | 20 |
xmlns:gml="http://www.opengis.net/gml" |
... | ... | |
314 | 319 | |
315 | 320 | |
316 | 321 |
@pytest.fixture |
317 |
def connector(db): |
|
322 |
def admin_user(): |
|
323 |
return User.objects.create_superuser('admin', email=None, password='admin') |
|
324 | ||
325 | ||
326 |
@pytest.fixture |
|
327 |
def connector(): |
|
318 | 328 |
return utils.setup_access_rights(OpenGIS.objects.create( |
319 | 329 |
slug='test', |
320 | 330 |
wms_service_url='http://example.net/wms', |
... | ... | |
805 | 815 |
assert 'extra parameter' in resp.json['err_desc'] |
806 | 816 | |
807 | 817 | |
818 |
def test_opengis_query_unicity(admin_user, app, connector, query): |
|
819 |
connector2 = OpenGIS.objects.create( |
|
820 |
slug='test2', |
|
821 |
wms_service_url='http://example.net/wms', |
|
822 |
wfs_service_url='http://example.net/wfs') |
|
823 |
Query.objects.create( |
|
824 |
resource=connector2, |
|
825 |
name='Foo Bar', |
|
826 |
slug='foo-bar', |
|
827 |
) |
|
828 | ||
829 |
app = login(app) |
|
830 |
resp = app.get('/manage/opengis/%s/query/new/' % connector.slug) |
|
831 |
resp.form['slug'] = query.slug |
|
832 |
resp.form['name'] = 'Foo Bar' |
|
833 |
resp.form['typename'] = 'foo' |
|
834 |
resp = resp.form.submit() |
|
835 |
assert resp.status_code == 200 |
|
836 |
assert Query.objects.filter(resource=connector).count() == 1 |
|
837 |
assert 'A query with this slug already exists' in resp.text |
|
838 |
resp.form['slug'] = 'foo-bar' |
|
839 |
resp.form['name'] = query.name |
|
840 |
resp.form['typename'] = 'foo' |
|
841 |
resp = resp.form.submit() |
|
842 |
assert Query.objects.filter(resource=connector).count() == 1 |
|
843 |
assert resp.status_code == 200 |
|
844 |
assert 'A query with this name already exists' in resp.text |
|
845 |
resp.form['slug'] = 'foo-bar' |
|
846 |
resp.form['name'] = 'Foo Bar' |
|
847 |
resp.form['typename'] = 'foo' |
|
848 |
resp = resp.form.submit() |
|
849 |
assert resp.status_code == 302 |
|
850 |
assert Query.objects.filter(resource=connector).count() == 2 |
|
851 |
new_query = Query.objects.latest('pk') |
|
852 |
assert new_query.resource == connector |
|
853 | ||
854 |
resp = app.get('/manage/opengis/%s/query/%s/' % (connector.slug, new_query.pk)) |
|
855 |
resp.form['slug'] = query.slug |
|
856 |
resp.form['name'] = 'Foo Bar' |
|
857 |
resp = resp.form.submit() |
|
858 |
assert resp.status_code == 200 |
|
859 |
assert 'A query with this slug already exists' in resp.text |
|
860 |
resp.form['slug'] = 'foo-bar' |
|
861 |
resp.form['name'] = query.name |
|
862 |
resp = resp.form.submit() |
|
863 |
assert resp.status_code == 200 |
|
864 |
assert 'A query with this name already exists' in resp.text |
|
865 |
resp.form['slug'] = 'foo-bar' |
|
866 |
resp.form['name'] = 'Foo Bar' |
|
867 |
resp = resp.form.submit() |
|
868 |
assert resp.status_code == 302 |
|
869 | ||
870 | ||
808 | 871 |
def test_opengis_export_import(query): |
809 | 872 |
assert OpenGIS.objects.count() == 1 |
810 | 873 |
assert Query.objects.count() == 1 |
811 |
- |