Projet

Général

Profil

0002-data_transfer-use-ValidationError-instead-of-DataImp.patch

Benjamin Dauvergne, 15 avril 2020 11:42

Télécharger (12,1 ko)

Voir les différences:

Subject: [PATCH 2/4] data_transfer: use ValidationError instead of
 DataImportError (#41342)

 src/authentic2/data_transfer.py      | 53 ++++++++++++++++++----------
 src/authentic2/manager/views.py      | 12 +++----
 tests/test_data_transfer.py          | 16 +++++----
 tests/test_import_export_site_cmd.py |  9 +++--
 4 files changed, 54 insertions(+), 36 deletions(-)
src/authentic2/data_transfer.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 functools import wraps
18

  
19
from django.core.exceptions import ValidationError
17 20
from django.contrib.contenttypes.models import ContentType
21
from django.utils.translation import ugettext_lazy as _
18 22

  
19 23
from django_rbac.models import Operation
20 24
from django_rbac.utils import (
21 25
    get_ou_model, get_role_model, get_role_parenting_model, get_permission_model)
26

  
22 27
from authentic2.a2_rbac.models import RoleAttribute
28
from authentic2.utils.lazy import lazy_join
23 29

  
24 30

  
25 31
def update_model(obj, d):
......
128 134
        self.role_attributes_update = role_attributes_update
129 135

  
130 136

  
131
class DataImportError(Exception):
132
    pass
133

  
134

  
135 137
class RoleDeserializer(object):
136 138
    def __init__(self, d, import_context):
137 139
        self._import_context = import_context
......
151 153
            else:
152 154
                self._role_d[key] = value
153 155

  
156
    def wraps_validationerror(func):
157
        @wraps(func)
158
        def f(self, *args, **kwargs):
159
            try:
160
                return func(self, *args, **kwargs)
161
            except ValidationError as e:
162
                raise ValidationError(_('Role "%s": %s') % (
163
                    self._role_d.get('name', self._role_d.get('slug')),
164
                    lazy_join(', ', [v.message for v in e.error_list])))
165
        return f
166

  
167
    @wraps_validationerror
154 168
    def deserialize(self):
155 169
        ou_d = self._role_d['ou']
156 170
        has_ou = bool(ou_d)
157 171
        ou = None if not has_ou else search_ou(ou_d)
158 172
        if has_ou and not ou:
159
            raise DataImportError(
160
                "Can't import role because missing Organizational Unit: %s" % ou_d)
173
            raise ValidationError(_("Can't import role because missing Organizational Unit: %s") % ou_d)
161 174

  
162 175
        kwargs = self._role_d.copy()
163 176
        kwargs.pop('ou', None)
......
172 185
            update_model(self._obj, kwargs)
173 186
        else:  # Create role
174 187
            if 'uuid' in kwargs and not kwargs['uuid']:
175
                raise DataImportError("Cannot import role '%s' with empty uuid"
176
                                      % kwargs.get('name'))
188
                raise ValidationError(_("Cannot import role '%s' with empty uuid") % kwargs.get('name'))
177 189
            self._obj = get_role_model().objects.create(**kwargs)
178 190
            status = 'created'
179 191

  
......
184 196
        self._obj.get_admin_role()
185 197
        return self._obj, status
186 198

  
199
    @wraps_validationerror
187 200
    def attributes(self):
188 201
        """ Update attributes (delete everything then create)
189 202
        """
......
199 212

  
200 213
        return created, deleted
201 214

  
215
    @wraps_validationerror
202 216
    def parentings(self):
203 217
        """ Update parentings (delete everything then create)
204 218
        """
......
212 226
            for parent_d in self._parents:
213 227
                parent = search_role(parent_d)
214 228
                if not parent:
215
                    raise DataImportError("Could not find role: %s" % parent_d)
229
                    raise ValidationError(_("Could not find parent role: %s") % parent_d)
216 230
                created.append(Parenting.objects.create(
217 231
                    child=self._obj, direct=True, parent=parent))
218 232

  
219 233
        return created, deleted
220 234

  
235
    @wraps_validationerror
221 236
    def permissions(self):
222 237
        """ Update permissions (delete everything then create)
223 238
        """
......
300 315
    result = ImportResult()
301 316

  
302 317
    if not isinstance(json_d, dict):
303
        raise DataImportError('Export file is invalid: not a dictionnary')
318
        raise ValidationError(_('Import file is invalid: not a dictionnary'))
304 319

  
305 320
    if import_context.import_ous:
306 321
        for ou_d in json_d.get('ous', []):
307 322
            result.update_ous(*import_ou(ou_d))
308 323

  
309 324
    if import_context.import_roles:
310
        roles_ds = [RoleDeserializer(role_d, import_context) for role_d in json_d.get('roles', [])
311
                    if not role_d['slug'].startswith('_')]
325
        roles_ds = []
326
        for role_d in json_d.get('roles', []):
327
            # ignore internal roles
328
            if role_d['slug'].startswith('_'):
329
                continue
330
            roles_ds.append(RoleDeserializer(role_d, import_context))
312 331

  
313 332
        for ds in roles_ds:
314 333
            result.update_roles(*ds.deserialize())
......
326 345
                result.update_permissions(*ds.permissions())
327 346

  
328 347
        if import_context.ou_delete_orphans:
329
            raise DataImportError(
330
                "Unsupported context value for ou_delete_orphans : %s" % (
331
                    import_context.ou_delete_orphans))
348
            raise ValidationError(_("Unsupported context value for ou_delete_orphans : %s") % (
349
                import_context.ou_delete_orphans))
332 350

  
333 351
        if import_context.role_delete_orphans:
334 352
            # FIXME : delete each role that is in DB but not in the export
335
            raise DataImportError(
336
                "Unsupported context value for role_delete_orphans : %s" % (
337
                    import_context.role_delete_orphans))
353
            raise ValidationError(_("Unsupported context value for role_delete_orphans : %s") % (
354
                import_context.role_delete_orphans))
338 355

  
339 356
    return result
src/authentic2/manager/views.py
17 17
import json
18 18
import inspect
19 19

  
20
from django.core.exceptions import PermissionDenied
20
from django.core.exceptions import PermissionDenied, ValidationError
21 21
from django.db import transaction
22 22
from django.views.generic.base import ContextMixin
23 23
from django.views.generic import (FormView, UpdateView, CreateView, DeleteView, TemplateView,
......
40 40

  
41 41
from django_rbac.utils import get_ou_model
42 42

  
43
from authentic2.data_transfer import export_site, import_site, DataImportError, ImportContext
43
from authentic2.data_transfer import export_site, import_site, ImportContext
44 44
from authentic2.forms.profile import modelform_factory
45 45
from authentic2.utils import redirect, batch_queryset
46 46
from authentic2.decorators import json as json_view
......
715 715

  
716 716
    def form_valid(self, form):
717 717
        try:
718
            json_site = json.loads(force_text(
719
                    self.request.FILES['site_json'].read()))
718
            json_site = json.loads(
719
                force_text(self.request.FILES['site_json'].read()))
720 720
        except ValueError:
721 721
            form.add_error('site_json', _('File is not in the expected JSON format.'))
722 722
            return self.form_invalid(form)
......
724 724
        try:
725 725
            with transaction.atomic():
726 726
                import_site(json_site, ImportContext())
727
        except DataImportError as e:
728
            form.add_error('site_json', six.text_type(e))
727
        except ValidationError as e:
728
            form.add_error('site_json', e)
729 729
            return self.form_invalid(form)
730 730

  
731 731
        return super(SiteImportView, self).form_valid(form)
tests/test_data_transfer.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_rbac.utils import get_role_model, get_ou_model
18 17
import pytest
19 18

  
19
from django.core.exceptions import ValidationError
20

  
21
from django_rbac.utils import get_role_model, get_ou_model
22

  
20 23
from authentic2.a2_rbac.models import RoleParenting
21 24
from authentic2.data_transfer import (
22 25
    export_site,
23 26
    ExportContext,
24
    DataImportError,
25 27
    export_roles,
26 28
    import_site,
27 29
    export_ous,
......
159 161
        'uuid': get_hex_uuid(), 'name': 'some role', 'description': 'role description',
160 162
        'slug': 'some-role', 'ou': {'slug': 'some-ou'}, 'service': None},
161 163
        ImportContext())
162
    with pytest.raises(DataImportError):
164
    with pytest.raises(ValidationError):
163 165
        rd.deserialize()
164 166

  
165 167

  
......
255 257
        'uuid': get_hex_uuid(), 'ou': None, 'service': None}
256 258
    rd = RoleDeserializer(child_role_dict, ImportContext())
257 259
    rd.deserialize()
258
    with pytest.raises(DataImportError) as excinfo:
260
    with pytest.raises(ValidationError) as excinfo:
259 261
        rd.parentings()
260 262

  
261
    assert "Could not find role" in str(excinfo.value)
263
    assert "Could not find parent role" in str(excinfo.value)
262 264

  
263 265

  
264 266
def test_role_deserializer_permissions(db):
......
473 475
def test_import_roles_role_delete_orphans(db):
474 476
    roles = [{
475 477
        'name': 'some role', 'description': 'some role description', 'slug': '_some-role'}]
476
    with pytest.raises(DataImportError):
478
    with pytest.raises(ValidationError):
477 479
        import_site({'roles': roles}, ImportContext(role_delete_orphans=True))
478 480

  
479 481

  
......
512 514
    import_site(d, ImportContext(import_roles=False, import_ous=False))
513 515
    assert Role.objects.exclude(slug__startswith='_').count() == 0
514 516
    assert OU.objects.exclude(slug='default').count() == 0
515
    with pytest.raises(DataImportError) as e:
517
    with pytest.raises(ValidationError) as e:
516 518
        import_site(d, ImportContext(import_roles=True, import_ous=False))
517 519
    assert 'missing Organizational' in e.value.args[0]
518 520
    assert Role.objects.exclude(slug__startswith='_').count() == 0
tests/test_import_export_site_cmd.py
17 17
import random
18 18
import json
19 19

  
20
from django.core.exceptions import ValidationError
21

  
20 22
from django.utils import six
21 23
from django.utils.six.moves import builtins as __builtin__
22 24
from django.core import management
......
122 124

  
123 125

  
124 126
def test_import_site_cmd_unhandled_context_option(db, monkeypatch, capsys, json_fixture):
125
    from authentic2.data_transfer import DataImportError
126

  
127 127
    content = {
128 128
        'roles': [
129 129
            {
......
138 138

  
139 139
    Role.objects.create(uuid='dqfewrvesvews2532', slug='role-slug', name='role-name')
140 140

  
141
    with pytest.raises(DataImportError):
141
    with pytest.raises(ValidationError):
142 142
        management.call_command(
143 143
            'import_site', '-o', 'role-delete-orphans', json_fixture(content))
144 144

  
......
205 205

  
206 206

  
207 207
def test_import_site_empty_uuids(db, monkeypatch, json_fixture):
208
    from authentic2.data_transfer import DataImportError
209
    with pytest.raises(DataImportError):
208
    with pytest.raises(ValidationError):
210 209
        management.call_command('import_site', json_fixture({
211 210
            'roles': [
212 211
                {
213
-