Skip to content

Commit c8ca610

Browse files
committed
Rework update noop
1 parent 1a0c31d commit c8ca610

File tree

9 files changed

+47
-62
lines changed

9 files changed

+47
-62
lines changed

CHANGES/6896.feature

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Modified the update Domain API so that it does not dispatch a task if no change is needed.
1+
Modified the API so that updates do not dispatch a task if no change is needed.

CHANGES/6897.feature

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Modified the update Remote API so that it does not dispatch a task if no change is needed.
1+
Modified the API so that updates do not dispatch a task if no change is needed.

pulpcore/app/util.py

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -678,16 +678,3 @@ def normalize_http_status(status):
678678
return "5xx"
679679
else:
680680
return ""
681-
682-
683-
def resource_modified(resource, request, **kwargs):
684-
"""Check if the request data has values different from the current resource instance."""
685-
partial = kwargs.pop("partial", False)
686-
entity = resource.get_object()
687-
serializer = resource.get_serializer(entity, data=request.data, partial=partial)
688-
serializer.is_valid(raise_exception=True)
689-
690-
for request_field, request_field_value in serializer.validated_data.items():
691-
if getattr(entity, request_field) != request_field_value:
692-
return True
693-
return False

pulpcore/app/viewsets/base.py

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from rest_framework.decorators import action
1414
from rest_framework.generics import get_object_or_404
1515
from rest_framework.response import Response
16-
from pulpcore.openapi import PulpAutoSchema
16+
from pulpcore.openapi import PulpAutoSchema, InheritSerializer
1717
from rest_framework.serializers import ValidationError as DRFValidationError, ListField, CharField
1818

1919
from pulpcore.app import tasks
@@ -482,27 +482,31 @@ class AsyncUpdateMixin(AsyncReservedObjectMixin):
482482
ALLOW_NON_BLOCKING_UPDATE = True
483483

484484
@extend_schema(
485-
description="Trigger an asynchronous update task",
486-
responses={202: AsyncOperationResponseSerializer},
485+
description="Update the entity and trigger an asynchronous task if necessary",
486+
responses={200: InheritSerializer, 202: AsyncOperationResponseSerializer},
487487
)
488488
def update(self, request, pk, **kwargs):
489489
partial = kwargs.pop("partial", False)
490490
instance = self.get_object()
491491
serializer = self.get_serializer(instance, data=request.data, partial=partial)
492492
serializer.is_valid(raise_exception=True)
493-
app_label = instance._meta.app_label
494-
task = dispatch(
495-
tasks.base.ageneral_update,
496-
exclusive_resources=self.async_reserved_resources(instance),
497-
args=(pk, app_label, serializer.__class__.__name__),
498-
kwargs={"data": request.data, "partial": partial},
499-
immediate=self.ALLOW_NON_BLOCKING_UPDATE,
500-
)
501-
return OperationPostponedResponse(task, request)
493+
494+
if all(getattr(instance, key) == value for key, value in serializer.validated_data.items()):
495+
return Response(serializer.data)
496+
else:
497+
app_label = instance._meta.app_label
498+
task = dispatch(
499+
tasks.base.ageneral_update,
500+
exclusive_resources=self.async_reserved_resources(instance),
501+
args=(pk, app_label, serializer.__class__.__name__),
502+
kwargs={"data": request.data, "partial": partial},
503+
immediate=self.ALLOW_NON_BLOCKING_UPDATE,
504+
)
505+
return OperationPostponedResponse(task, request)
502506

503507
@extend_schema(
504-
description="Trigger an asynchronous partial update task",
505-
responses={202: AsyncOperationResponseSerializer},
508+
description="Update the entity partially and trigger an asynchronous task if necessary",
509+
responses={200: InheritSerializer, 202: AsyncOperationResponseSerializer},
506510
)
507511
def partial_update(self, request, *args, **kwargs):
508512
kwargs["partial"] = True

pulpcore/app/viewsets/domain.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from gettext import gettext as _
22

33
from drf_spectacular.utils import extend_schema
4-
from rest_framework import mixins, response
4+
from rest_framework import mixins
55
from rest_framework.decorators import action
66
from rest_framework.exceptions import ValidationError
77

@@ -14,7 +14,6 @@
1414
AsyncOperationResponseSerializer,
1515
)
1616
from pulpcore.app.tasks import migrate_backend
17-
from pulpcore.app.util import resource_modified
1817
from pulpcore.app.viewsets import NamedModelViewSet, AsyncRemoveMixin, AsyncUpdateMixin, LabelsMixin
1918
from pulpcore.app.viewsets.base import NAME_FILTER_OPTIONS
2019
from pulpcore.app.viewsets.custom_filters import LabelFilter
@@ -110,19 +109,14 @@ class DomainViewSet(
110109

111110
@extend_schema(
112111
description="Trigger an asynchronous update task",
113-
responses={202: AsyncOperationResponseSerializer, 204: None},
112+
responses={200: DomainSerializer, 202: AsyncOperationResponseSerializer},
114113
)
115114
def update(self, request, pk, **kwargs):
116115
"""Prevent trying to update the default domain."""
117116
instance = self.get_object()
118117
if instance.name == "default":
119118
raise ValidationError(_("Default domain can not be updated."))
120119

121-
# check if the update operation is going to change anything and
122-
# if no actual change is needed return a 204 No Content.
123-
if not resource_modified(self, request, **kwargs):
124-
return response.Response(None, status=204)
125-
126120
return super().update(request, pk, **kwargs)
127121

128122
@extend_schema(

pulpcore/app/viewsets/repository.py

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from django.urls.base import Resolver404, resolve
66
from django_filters import Filter
77
from drf_spectacular.utils import extend_schema
8-
from rest_framework import mixins, response, serializers
8+
from rest_framework import mixins, serializers
99
from rest_framework.decorators import action
1010
from rest_framework.viewsets import GenericViewSet
1111
from urllib.parse import urlparse
@@ -43,7 +43,7 @@
4343
from pulpcore.app.viewsets.custom_filters import LabelFilter, WithContentFilter, WithContentInFilter
4444
from pulpcore.tasking.tasks import dispatch
4545
from pulpcore.filters import HyperlinkRelatedFilter
46-
from pulpcore.app.util import resolve_prn, resource_modified
46+
from pulpcore.app.util import resolve_prn
4747

4848

4949
class RepositoryContentFilter(Filter):
@@ -368,21 +368,6 @@ class RemoteViewSet(
368368
queryset = Remote.objects.all()
369369
filterset_class = RemoteFilter
370370

371-
@extend_schema(
372-
description="Trigger an asynchronous update task",
373-
responses={202: AsyncOperationResponseSerializer, 204: None},
374-
)
375-
def update(self, request, pk, **kwargs):
376-
"""
377-
Check if the update operation is going to change anything and if:
378-
- no actual change is needed return a 204 No Content
379-
- the request contains modifications, trigger an asynchronous update task
380-
"""
381-
if not resource_modified(self, request, **kwargs):
382-
return response.Response(None, status=204)
383-
384-
return super().update(request, pk, **kwargs)
385-
386371

387372
# We have to use GenericViewSet as NamedModelViewSet causes
388373
# get_viewset_for_model() to match multiple viewsets.

pulpcore/openapi/__init__.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@
2323
)
2424
from drf_spectacular.settings import spectacular_settings
2525
from drf_spectacular.types import OpenApiTypes
26-
from drf_spectacular.utils import OpenApiParameter, extend_schema_field
27-
from drf_spectacular.extensions import OpenApiAuthenticationExtension
26+
from drf_spectacular.utils import OpenApiParameter, extend_schema, extend_schema_field
27+
from drf_spectacular.extensions import OpenApiViewExtension, OpenApiAuthenticationExtension
2828
from rest_framework import mixins, serializers
2929
from rest_framework.exceptions import ParseError
3030
from rest_framework.request import Request
@@ -48,6 +48,10 @@
4848
extend_schema_field(OpenApiTypes.INT64)(serializers.IntegerField)
4949

5050

51+
class InheritSerializer(serializers.Serializer):
52+
"""This is a dummy tracer to allow mixins to refer to the natural serializer of a viewset."""
53+
54+
5155
class PulpAutoSchema(AutoSchema):
5256
"""Pulp Auto Schema."""
5357

@@ -238,6 +242,7 @@ def _get_response_bodies(self):
238242
"""
239243
Handle response status code.
240244
"""
245+
# DRF handles this most of the time. But it seems set_label still needs it.
241246
response = super()._get_response_bodies()
242247
if (
243248
self.method == "POST"
@@ -248,6 +253,15 @@ def _get_response_bodies(self):
248253

249254
return response
250255

256+
def _get_response_for_code(
257+
self, serializer, status_code, media_types=None, direction="response"
258+
):
259+
"""Hack to replace the InheritSerializer with the real deal."""
260+
if serializer == InheritSerializer:
261+
serializer = self._get_serializer()
262+
263+
return super()._get_response_for_code(serializer, status_code, media_types, direction)
264+
251265

252266
class PulpSchemaGenerator(SchemaGenerator):
253267
"""Pulp Schema Generator."""

pulpcore/tests/functional/api/test_crud_domains.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ def test_crud_domains(pulpcore_bindings, monitor_task):
4848
response = pulpcore_bindings.DomainsApi.partial_update_with_http_info(
4949
domain.pulp_href, update_body
5050
)
51-
assert response.status_code == 204
52-
assert response.data is None
51+
assert response.status_code == 200
52+
assert response.data == domain
5353

5454
# Delete the domain
5555
response = pulpcore_bindings.DomainsApi.delete(domain.pulp_href)

pulpcore/tests/functional/api/using_plugin/test_crud_repos.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -210,8 +210,8 @@ def _compare_results(data, received):
210210

211211
# An update request with no changes should return a 200 OK (without dispatching a task)
212212
response = file_bindings.RemotesFileApi.partial_update_with_http_info(remote.pulp_href, data)
213-
assert response.status_code == 204
214-
assert response.data is None
213+
assert response.status_code == 200
214+
_compare_results(data, response.data)
215215

216216
# Test that a password can be updated with a PUT request.
217217
temp_remote = file_remote_factory(
@@ -231,6 +231,7 @@ def _compare_results(data, received):
231231
assert exc.stdout.rstrip("\n") == "changed"
232232

233233
# Test that password doesn't get unset when not passed with a PUT request.
234+
# QUESTION: Why not? PUT is supposed to replace the whole entity in place.
234235
temp_remote = file_remote_factory(url="http://", password="new")
235236
href = temp_remote.pulp_href
236237
uuid = re.search(r"/api/v3/remotes/file/file/([\w-]+)/", href).group(1)
@@ -241,8 +242,8 @@ def _compare_results(data, received):
241242
# test a PUT request without a password
242243
remote_update = {"name": temp_remote.name, "url": "http://"}
243244
response = file_bindings.RemotesFileApi.update_with_http_info(href, remote_update)
244-
assert response.status_code == 204
245-
assert response.data is None
245+
assert response.status_code == 200
246+
_compare_results(remote_update, response.data)
246247
exc = run(["pulpcore-manager", "shell", "-c", shell_cmd], text=True, capture_output=True)
247248
assert exc.stdout.rstrip("\n") == "new"
248249

0 commit comments

Comments
 (0)