mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-11-23 23:49:08 -06:00
Merge branch 'dev' into feature-ai
This commit is contained in:
@@ -1041,7 +1041,7 @@ class DocumentSerializer(
|
||||
request.version if request else settings.REST_FRAMEWORK["DEFAULT_VERSION"],
|
||||
)
|
||||
|
||||
if api_version < 9:
|
||||
if api_version < 9 and "created" in self.fields:
|
||||
# provide created as a datetime for backwards compatibility
|
||||
from django.utils import timezone
|
||||
|
||||
|
||||
@@ -172,6 +172,35 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase):
|
||||
results = response.data["results"]
|
||||
self.assertEqual(len(results[0]), 0)
|
||||
|
||||
def test_document_fields_api_version_8_respects_created(self):
|
||||
Document.objects.create(
|
||||
title="legacy",
|
||||
checksum="123",
|
||||
mime_type="application/pdf",
|
||||
created=date(2024, 1, 15),
|
||||
)
|
||||
|
||||
response = self.client.get(
|
||||
"/api/documents/?fields=id",
|
||||
headers={"Accept": "application/json; version=8"},
|
||||
format="json",
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
results = response.data["results"]
|
||||
self.assertIn("id", results[0])
|
||||
self.assertNotIn("created", results[0])
|
||||
|
||||
response = self.client.get(
|
||||
"/api/documents/?fields=id,created",
|
||||
headers={"Accept": "application/json; version=8"},
|
||||
format="json",
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
results = response.data["results"]
|
||||
self.assertIn("id", results[0])
|
||||
self.assertIn("created", results[0])
|
||||
self.assertRegex(results[0]["created"], r"^2024-01-15T00:00:00.*$")
|
||||
|
||||
def test_document_legacy_created_format(self):
|
||||
"""
|
||||
GIVEN:
|
||||
@@ -2250,6 +2279,23 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase):
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertListEqual(response.data, ["test", "test2"])
|
||||
|
||||
def test_get_log_with_limit(self):
|
||||
log_data = "test1\ntest2\ntest3\n"
|
||||
with (Path(settings.LOGGING_DIR) / "paperless.log").open("w") as f:
|
||||
f.write(log_data)
|
||||
response = self.client.get("/api/logs/paperless/", {"limit": 2})
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertListEqual(response.data, ["test2", "test3"])
|
||||
|
||||
def test_get_log_with_invalid_limit(self):
|
||||
log_data = "test1\ntest2\n"
|
||||
with (Path(settings.LOGGING_DIR) / "paperless.log").open("w") as f:
|
||||
f.write(log_data)
|
||||
response = self.client.get("/api/logs/paperless/", {"limit": "abc"})
|
||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
response = self.client.get("/api/logs/paperless/", {"limit": -5})
|
||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
def test_invalid_regex_other_algorithm(self):
|
||||
for endpoint in ["correspondents", "tags", "document_types"]:
|
||||
response = self.client.post(
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
from django.core.cache import cache
|
||||
from pytest_httpx import HTTPXMock
|
||||
from rest_framework import status
|
||||
from rest_framework.test import APIClient
|
||||
@@ -8,6 +9,9 @@ from paperless import version
|
||||
class TestApiRemoteVersion:
|
||||
ENDPOINT = "/api/remote_version/"
|
||||
|
||||
def setup_method(self):
|
||||
cache.clear()
|
||||
|
||||
def test_remote_version_enabled_no_update_prefix(
|
||||
self,
|
||||
rest_api_client: APIClient,
|
||||
|
||||
@@ -7,6 +7,7 @@ import re
|
||||
import tempfile
|
||||
import zipfile
|
||||
from collections import defaultdict
|
||||
from collections import deque
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from time import mktime
|
||||
@@ -53,6 +54,7 @@ from django.utils.timezone import make_aware
|
||||
from django.utils.translation import get_language
|
||||
from django.views import View
|
||||
from django.views.decorators.cache import cache_control
|
||||
from django.views.decorators.cache import cache_page
|
||||
from django.views.decorators.csrf import ensure_csrf_cookie
|
||||
from django.views.decorators.http import condition
|
||||
from django.views.decorators.http import last_modified
|
||||
@@ -73,6 +75,7 @@ from rest_framework import parsers
|
||||
from rest_framework import serializers
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.exceptions import NotFound
|
||||
from rest_framework.exceptions import ValidationError
|
||||
from rest_framework.filters import OrderingFilter
|
||||
from rest_framework.filters import SearchFilter
|
||||
from rest_framework.generics import GenericAPIView
|
||||
@@ -1489,6 +1492,13 @@ class UnifiedSearchViewSet(DocumentViewSet):
|
||||
type=OpenApiTypes.STR,
|
||||
location=OpenApiParameter.PATH,
|
||||
),
|
||||
OpenApiParameter(
|
||||
name="limit",
|
||||
type=OpenApiTypes.INT,
|
||||
location=OpenApiParameter.QUERY,
|
||||
description="Return only the last N entries from the log file",
|
||||
required=False,
|
||||
),
|
||||
],
|
||||
responses={
|
||||
(200, "application/json"): serializers.ListSerializer(
|
||||
@@ -1520,8 +1530,22 @@ class LogViewSet(ViewSet):
|
||||
if not log_file.is_file():
|
||||
raise Http404
|
||||
|
||||
limit_param = request.query_params.get("limit")
|
||||
if limit_param is not None:
|
||||
try:
|
||||
limit = int(limit_param)
|
||||
except (TypeError, ValueError):
|
||||
raise ValidationError({"limit": "Must be a positive integer"})
|
||||
if limit < 1:
|
||||
raise ValidationError({"limit": "Must be a positive integer"})
|
||||
else:
|
||||
limit = None
|
||||
|
||||
with log_file.open() as f:
|
||||
lines = [line.rstrip() for line in f.readlines()]
|
||||
if limit is None:
|
||||
lines = [line.rstrip() for line in f.readlines()]
|
||||
else:
|
||||
lines = [line.rstrip() for line in deque(f, maxlen=limit)]
|
||||
|
||||
return Response(lines)
|
||||
|
||||
@@ -2533,6 +2557,7 @@ class UiSettingsView(GenericAPIView):
|
||||
)
|
||||
|
||||
|
||||
@method_decorator(cache_page(60 * 15), name="dispatch")
|
||||
@extend_schema_view(
|
||||
get=extend_schema(
|
||||
description="Get the current version of the Paperless-NGX server",
|
||||
|
||||
@@ -53,6 +53,15 @@ class TestUrlCanary:
|
||||
Verify certain URLs are still available so testing is valid still
|
||||
"""
|
||||
|
||||
# Wikimedia rejects requests without a browser-like User-Agent header and returns 403.
|
||||
_WIKIMEDIA_HEADERS = {
|
||||
"User-Agent": (
|
||||
"Mozilla/5.0 (X11; Linux x86_64) "
|
||||
"AppleWebKit/537.36 (KHTML, like Gecko) "
|
||||
"Chrome/123.0.0.0 Safari/537.36"
|
||||
),
|
||||
}
|
||||
|
||||
def test_online_image_exception_on_not_available(self):
|
||||
"""
|
||||
GIVEN:
|
||||
@@ -70,6 +79,7 @@ class TestUrlCanary:
|
||||
with pytest.raises(httpx.HTTPStatusError) as exec_info:
|
||||
resp = httpx.get(
|
||||
"https://upload.wikimedia.org/wikipedia/en/f/f7/nonexistent.png",
|
||||
headers=self._WIKIMEDIA_HEADERS,
|
||||
)
|
||||
resp.raise_for_status()
|
||||
|
||||
@@ -90,7 +100,10 @@ class TestUrlCanary:
|
||||
"""
|
||||
|
||||
# Now check the URL used in samples/sample.html
|
||||
resp = httpx.get("https://upload.wikimedia.org/wikipedia/en/f/f7/RickRoll.png")
|
||||
resp = httpx.get(
|
||||
"https://upload.wikimedia.org/wikipedia/en/f/f7/RickRoll.png",
|
||||
headers=self._WIKIMEDIA_HEADERS,
|
||||
)
|
||||
resp.raise_for_status()
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user