Fix: handle versioned PaperlessTasks response

This commit is contained in:
shamoon 2025-03-08 16:38:12 -08:00
parent b746b6f2d6
commit 337092f345
7 changed files with 86 additions and 15 deletions

View File

@ -413,3 +413,11 @@ Initial API version.
list of strings. When creating or updating a custom field value of a list of strings. When creating or updating a custom field value of a
document for a select type custom field, the value should be the `id` of document for a select type custom field, the value should be the `id` of
the option whereas previously was the index of the option. the option whereas previously was the index of the option.
#### Version 8
- PaperlessTask objects now have a `task_name` field which replaces the old
`type` field. The `type` field is now used to represent the way the task
was created. Additionally, the tasks endpoint now returns different types
of tasks other than simply 'file' tasks. See the API schema for more
information.

View File

@ -3,7 +3,7 @@ const base_url = new URL(document.baseURI)
export const environment = { export const environment = {
production: true, production: true,
apiBaseUrl: document.baseURI + 'api/', apiBaseUrl: document.baseURI + 'api/',
apiVersion: '7', apiVersion: '8',
appTitle: 'Paperless-ngx', appTitle: 'Paperless-ngx',
version: '2.14.7', version: '2.14.7',
webSocketHost: window.location.host, webSocketHost: window.location.host,

View File

@ -5,7 +5,7 @@
export const environment = { export const environment = {
production: false, production: false,
apiBaseUrl: 'http://localhost:8000/api/', apiBaseUrl: 'http://localhost:8000/api/',
apiVersion: '7', apiVersion: '8',
appTitle: 'Paperless-ngx', appTitle: 'Paperless-ngx',
version: 'DEVELOPMENT', version: 'DEVELOPMENT',
webSocketHost: 'localhost:8000', webSocketHost: 'localhost:8000',

View File

@ -421,6 +421,15 @@ class OwnedObjectListSerializer(serializers.ListSerializer):
return super().to_representation(documents) return super().to_representation(documents)
class GetAPIVersionMixin:
def get_api_version(self):
return int(
self.context.get("request").version
if self.context.get("request")
else settings.REST_FRAMEWORK["DEFAULT_VERSION"],
)
class CorrespondentSerializer(MatchingModelSerializer, OwnedObjectSerializer): class CorrespondentSerializer(MatchingModelSerializer, OwnedObjectSerializer):
last_correspondence = serializers.DateTimeField(read_only=True, required=False) last_correspondence = serializers.DateTimeField(read_only=True, required=False)
@ -717,7 +726,7 @@ class ReadWriteSerializerMethodField(serializers.SerializerMethodField):
return {self.field_name: data} return {self.field_name: data}
class CustomFieldInstanceSerializer(serializers.ModelSerializer): class CustomFieldInstanceSerializer(serializers.ModelSerializer, GetAPIVersionMixin):
field = serializers.PrimaryKeyRelatedField(queryset=CustomField.objects.all()) field = serializers.PrimaryKeyRelatedField(queryset=CustomField.objects.all())
value = ReadWriteSerializerMethodField(allow_null=True) value = ReadWriteSerializerMethodField(allow_null=True)
@ -809,13 +818,6 @@ class CustomFieldInstanceSerializer(serializers.ModelSerializer):
return data return data
def get_api_version(self):
return int(
self.context.get("request").version
if self.context.get("request")
else settings.REST_FRAMEWORK["DEFAULT_VERSION"],
)
def to_internal_value(self, data): def to_internal_value(self, data):
ret = super().to_internal_value(data) ret = super().to_internal_value(data)
@ -1709,7 +1711,7 @@ class UiSettingsViewSerializer(serializers.ModelSerializer):
return ui_settings return ui_settings
class TasksViewSerializer(OwnedObjectSerializer): class TasksViewSerializer(OwnedObjectSerializer, GetAPIVersionMixin):
class Meta: class Meta:
model = PaperlessTask model = PaperlessTask
fields = ( fields = (
@ -1752,6 +1754,13 @@ class TasksViewSerializer(OwnedObjectSerializer):
return result return result
def to_representation(self, instance: PaperlessTask):
result = super().to_representation(instance)
if self.get_api_version() < 8:
# Older versions only returned file tasks (filtering handled in view) and had different naming scheme
result["type"] = "file"
return result
class RunTaskViewSerializer(serializers.Serializer): class RunTaskViewSerializer(serializers.Serializer):
task_name = serializers.ChoiceField( task_name = serializers.ChoiceField(

View File

@ -370,3 +370,45 @@ class TestTasks(DirectoriesMixin, APITestCase):
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
mock_check_sanity.assert_not_called() mock_check_sanity.assert_not_called()
def test_handle_older_api_version(self):
"""
GIVEN:
- A request from the API with version < 8
WHEN:
- Tasks are requested
THEN:
- Only consume file tasks are returned and the type is 'file'
"""
task1 = PaperlessTask.objects.create(
task_id=str(uuid.uuid4()),
task_file_name="task_one.pdf",
task_name=PaperlessTask.TaskName.CONSUME_FILE,
)
task2 = PaperlessTask.objects.create(
task_id=str(uuid.uuid4()),
task_name=PaperlessTask.TaskName.CHECK_SANITY,
type=PaperlessTask.TaskType.AUTO,
)
response = self.client.get(
self.ENDPOINT,
headers={"Accept": "application/json; version=7"},
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data), 1)
self.assertEqual(response.data[0]["task_id"], task1.task_id)
self.assertEqual(response.data[0]["type"], "file")
response = self.client.get(
self.ENDPOINT,
headers={"Accept": "application/json; version=8"},
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data), 2)
self.assertEqual(response.data[0]["task_id"], task2.task_id)
self.assertEqual(response.data[0]["type"], PaperlessTask.TaskType.AUTO)

View File

@ -2287,6 +2287,7 @@ class TasksViewSet(ReadOnlyModelViewSet):
ObjectOwnedOrGrantedPermissionsFilter, ObjectOwnedOrGrantedPermissionsFilter,
) )
filterset_class = PaperlessTaskFilterSet filterset_class = PaperlessTaskFilterSet
queryset = PaperlessTask.objects.all().order_by("-date_created")
TASK_AND_ARGS_BY_NAME = { TASK_AND_ARGS_BY_NAME = {
PaperlessTask.TaskName.INDEX_OPTIMIZE: (index_optimize, {}), PaperlessTask.TaskName.INDEX_OPTIMIZE: (index_optimize, {}),
@ -2301,11 +2302,22 @@ class TasksViewSet(ReadOnlyModelViewSet):
} }
def get_queryset(self): def get_queryset(self):
queryset = PaperlessTask.objects.all().order_by("-date_created")
task_id = self.request.query_params.get("task_id") task_id = self.request.query_params.get("task_id")
if task_id is not None: if task_id is not None:
queryset = PaperlessTask.objects.filter(task_id=task_id) return PaperlessTask.objects.filter(task_id=task_id)
return queryset return super().get_queryset()
def list(self, request, *args, **kwargs):
version = int(
request.version if request else settings.REST_FRAMEWORK["DEFAULT_VERSION"],
)
if version < 8:
# Previous API versions only had file tasks, the format is also different (handled in serializer)
self.queryset = PaperlessTask.objects.filter(
task_name=PaperlessTask.TaskName.CONSUME_FILE,
).order_by("-date_created")
return super().list(request, *args, **kwargs)
@action(methods=["post"], detail=False) @action(methods=["post"], detail=False)
def acknowledge(self, request): def acknowledge(self, request):

View File

@ -345,7 +345,7 @@ REST_FRAMEWORK = {
"DEFAULT_VERSION": "7", "DEFAULT_VERSION": "7",
# Make sure these are ordered and that the most recent version appears # Make sure these are ordered and that the most recent version appears
# last. See api.md#api-versioning when adding new versions. # last. See api.md#api-versioning when adding new versions.
"ALLOWED_VERSIONS": ["1", "2", "3", "4", "5", "6", "7"], "ALLOWED_VERSIONS": ["1", "2", "3", "4", "5", "6", "7", "8"],
# DRF Spectacular default schema # DRF Spectacular default schema
"DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema", "DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
} }