0001-api-handle-multiple-objects-returned-in-get-or-creat.patch
src/authentic2/api_exceptions.py | ||
---|---|---|
1 |
# authentic2 - versatile identity manager |
|
2 |
# Copyright (C) 2010-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.utils.translation import ugettext_lazy as _ |
|
18 | ||
19 |
from rest_framework.exceptions import APIException |
|
20 |
from rest_framework import status |
|
21 | ||
22 | ||
23 |
class Conflict(APIException): |
|
24 |
status_code = status.HTTP_409_CONFLICT |
|
25 |
default_detail = _('Cannot process request because of conflicting resources.') |
|
26 |
default_code = 'conflict' |
src/authentic2/api_mixins.py | ||
---|---|---|
1 | 1 |
# authentic2 - versatile identity manager |
2 |
# Copyright (C) 2010-2018 Entr'ouvert
|
|
2 |
# Copyright (C) 2010-2020 Entr'ouvert
|
|
3 | 3 |
# |
4 | 4 |
# This program is free software: you can redistribute it and/or modify it |
5 | 5 |
# under the terms of the GNU Affero General Public License as published |
... | ... | |
20 | 20 |
from rest_framework.exceptions import ValidationError |
21 | 21 |
from rest_framework.utils import model_meta |
22 | 22 | |
23 |
from .api_exceptions import Conflict |
|
24 | ||
23 | 25 | |
24 | 26 |
class GetOrCreateMixinView(object): |
25 | 27 |
_lookup_object = None |
... | ... | |
49 | 51 |
return ModelClass.objects.get(**kwargs) |
50 | 52 |
except ModelClass.DoesNotExist: |
51 | 53 |
return None |
54 |
except ModelClass.MultipleObjectsReturned: |
|
55 |
raise Conflict('retrieved several instances of model %s for key attributes %s' % ( |
|
56 |
ModelClass.__name__, kwargs)) |
|
52 | 57 | |
53 | 58 |
def _validate_get_keys(self, keys): |
54 | 59 |
ModelClass = self._get_model_class() |
tests/test_api.py | ||
---|---|---|
1420 | 1420 |
assert User.objects.get(id=id).last_name == 'Doe' |
1421 | 1421 | |
1422 | 1422 | |
1423 |
def test_api_users_get_or_create_email_not_unique(settings, app, admin): |
|
1424 |
settings.A2_EMAIL_IS_UNIQUE = False |
|
1425 |
User = get_user_model() |
|
1426 |
OU = get_ou_model() |
|
1427 |
ou1 = OU.objects.create(name='OU1', slug='ou1', email_is_unique=True) |
|
1428 |
ou2 = OU.objects.create(name='OU2', slug='ou2', email_is_unique=False) |
|
1429 | ||
1430 |
app.authorization = ('Basic', (admin.username, admin.username)) |
|
1431 |
payload = { |
|
1432 |
'email': 'john.doe@example.net', |
|
1433 |
'first_name': 'John', |
|
1434 |
'last_name': 'Doe', |
|
1435 |
'ou': 'ou1' |
|
1436 |
} |
|
1437 |
# 1. create |
|
1438 |
resp = app.post_json('/api/users/?get_or_create=email', params=payload, status=201) |
|
1439 |
id_user = resp.json['id'] |
|
1440 |
assert User.objects.get(id=id_user).first_name == 'John' |
|
1441 |
assert User.objects.get(id=id_user).last_name == 'Doe' |
|
1442 |
assert User.objects.get(id=id_user).ou == ou1 |
|
1443 |
# 2. get |
|
1444 |
resp = app.post_json('/api/users/?get_or_create=email', params=payload, status=200) |
|
1445 |
# 3. explicitly create in a different OU |
|
1446 |
payload['ou'] = 'ou2' |
|
1447 |
resp = app.post_json('/api/users/', params=payload, status=201) |
|
1448 |
id_user2 = resp.json['id'] |
|
1449 |
assert id_user2 != id_user |
|
1450 |
assert User.objects.get(id=id_user2).first_name == 'John' |
|
1451 |
assert User.objects.get(id=id_user2).last_name == 'Doe' |
|
1452 |
assert User.objects.get(id=id_user2).ou == ou2 |
|
1453 |
# 4. fail to retrieve a single instance for an ambiguous get-or-create key |
|
1454 |
resp = app.post_json('/api/users/?get_or_create=email', params=payload, status=409) |
|
1455 |
assert resp.json['errors'] == "retrieved several instances of model User for key attributes {'email': 'john.doe@example.net'}" |
|
1456 | ||
1457 | ||
1423 | 1458 |
def test_api_users_get_or_create_multi_key(settings, app, admin): |
1424 | 1459 |
app.authorization = ('Basic', (admin.username, admin.username)) |
1425 | 1460 |
# test missing first_name |
1426 |
- |