Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@

### Changed

- nothing changed
- Create instance with spécific uid
- raise 400 if user try to update uid with patch request

### Removed

Expand Down
21 changes: 18 additions & 3 deletions concrete_datastore/api/v1/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -365,9 +365,11 @@ def make_serializer_class(
class Meta:
model = concrete.models[meta_model.get_model_name().lower()]
fields = _fields

no_update_fields = [
"uid",
]
read_only_fields = (
['uid', 'created_by', 'admin', 'is_staff']
['created_by', 'admin', 'is_staff']
+ fk_read_only_fields
+ [f for f in _all_fields if f.startswith('resource_')]
)
Expand Down Expand Up @@ -427,7 +429,7 @@ class Meta:

attrs.update(custom_fields_attrs)

class _ModelSerializer(serializers.ModelSerializer):
class _ModelSerializer(UpdateMixin, serializers.ModelSerializer):
url = serializers.SerializerMethodField()
verbose_name = serializers.SerializerMethodField()
scopes = serializers.SerializerMethodField()
Expand Down Expand Up @@ -604,3 +606,16 @@ def __call__(self, value):
raise PasswordInsecureValidationError(
str(e), code='PASSWORD_INSECURE'
)


class UpdateMixin(serializers.ModelSerializer):
def get_extra_kwargs(self):
kwargs = super().get_extra_kwargs()
no_update_fields = getattr(self.Meta, "no_update_fields", None)

if self.instance and no_update_fields:
for field in no_update_fields:
kwargs.setdefault(field, {})
kwargs[field]["read_only"] = True

return kwargs
11 changes: 11 additions & 0 deletions concrete_datastore/api/v1/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2274,6 +2274,17 @@ def handle_divider_update(self, request, request_user, instance):

def update(self, request, *args, **kwargs):
instance = self.get_object()
update_uid = request.data.__contains__('uid')

if request.method == "PATCH" and update_uid is True:
return Response(
data={
"message": "The field 'uid' can't be updated",
"_errors": ["INVALID_QUERY"],
},
status=HTTP_400_BAD_REQUEST,
Comment thread
Theodrem marked this conversation as resolved.
Outdated
)

if isinstance(instance, UserModel):
request_user = request.user
at_least_admin = request_user.is_at_least_admin
Expand Down
138 changes: 138 additions & 0 deletions tests/tests_api_v1_1/test_api_v1_1_CRUD.py
Original file line number Diff line number Diff line change
Expand Up @@ -396,3 +396,141 @@ def test_create_with_trailing_space(self):
project = Project.objects.first()

self.assertEqual(project.name, project_name_to_post)

def test_create_with_specify_uid(self):
project_name_to_post = "project 1"
url_projects = '/api/v1.1/project/'

self.assertEqual(Project.objects.count(), 0)

resp = self.client.post(
url_projects,
{
"uid": "521fd822-12d2-49b1-9573-a1cde74e4d51",
"name": project_name_to_post,
"description": "description",
"skills": [],
"members": [],
},
HTTP_AUTHORIZATION='Token {}'.format(self.token),
)
self.assertEqual(
resp.status_code, status.HTTP_201_CREATED, msg=resp.content
)

self.assertEqual(Project.objects.count(), 1)
project = Project.objects.first()

self.assertEqual(project.name, project_name_to_post)
self.assertEqual(
str(project.pk), "521fd822-12d2-49b1-9573-a1cde74e4d51"
)

def test_create_with_specify_uid_wrong_format(self):
project_name_to_post = "project 1"
url_projects = '/api/v1.1/project/'

self.assertEqual(Project.objects.count(), 0)

resp = self.client.post(
url_projects,
{
"uid": "521fd822-12-a1cde74e4d51", # wrong format
"name": project_name_to_post,
"description": "description",
"skills": [],
"members": [],
},
HTTP_AUTHORIZATION='Token {}'.format(self.token),
)
self.assertEqual(resp.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(resp.data['uid'][0].title(), 'Must Be A Valid Uuid.')

def test_error_when_patch_uid_instance(self):
# We can't update uuid of instance
project_name_to_post = "project 1"
url_projects = '/api/v1.1/project/'

self.assertEqual(Project.objects.count(), 0)

resp = self.client.post(
url_projects,
{
"uid": "521fd822-12d2-49b1-9573-a1cde74e4d51",
"name": project_name_to_post,
"description": "description",
"skills": [],
"members": [],
},
HTTP_AUTHORIZATION='Token {}'.format(self.token),
)
self.assertEqual(
resp.status_code, status.HTTP_201_CREATED, msg=resp.content
)

self.assertEqual(Project.objects.count(), 1)
project = Project.objects.first()

url_to_patch = resp.data['url']
resp = self.client.patch(
url_to_patch,
{"name": "Project42"},
HTTP_AUTHORIZATION='Token {}'.format(self.token),
)
self.assertEqual(resp.status_code, status.HTTP_200_OK)

new_name = resp.data['name']
self.assertEqual(new_name, "Project42", msg=resp.content)

resp = self.client.patch(
url_to_patch,
{"uid": "1af2566f-6dad-4fb4-81f1-2cb4d67a713f"},
HTTP_AUTHORIZATION='Token {}'.format(self.token),
)
self.assertEqual(resp.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(resp.data['_errors'], ['INVALID_QUERY'])
self.assertEqual(
resp.data['message'], "The field 'uid' can't be updated"
)

def test_update_uid_with_put_request(self):
# We can't update uuid of instance
# The request must not fail but the uid field
# must not be modified
url_projects = '/api/v1.1/project/'
initial_uid = "521fd822-12d2-49b1-9573-a1cde74e4d51"

project_name_to_post = "project 1"

self.assertEqual(Project.objects.count(), 0)

resp = self.client.post(
url_projects,
{
"uid": initial_uid,
"name": project_name_to_post,
"description": "description",
"skills": [],
"members": [],
},
HTTP_AUTHORIZATION='Token {}'.format(self.token),
)

self.assertEqual(Project.objects.count(), 1)
project = User.objects.first()

url_to_put = resp.data['url']
new_project_name = "project new"
resp = self.client.put(
url_to_put,
{
"uid": "1af2566f-6dad-4fb4-81f1-2cb4d67a713f",
"name": new_project_name,
},
HTTP_AUTHORIZATION='Token {}'.format(self.token),
)
self.assertEqual(resp.status_code, status.HTTP_200_OK)
self.assertEqual(resp.data["name"], new_project_name, msg=resp.content)
self.assertEqual(
resp.data["uid"], initial_uid, msg=resp.content
) # Must not be modified