Expose API version descriptors for admins via API (#5865)

* Added endpoint to expose API version descriptors for admins

* Refactored tests and moved them to seperate file

* Switched to raw string

* add coverage exclusion

* ignore imports (possible fix to multiline imports)

* Add OpenApi Schema

* removed changes regarding coverage

* moved new functions to `version`
to keep api_version a pure static file

* clean up diff
This commit is contained in:
Matthias Mair 2023-11-06 23:36:15 +01:00 committed by GitHub
parent 0af08da6f8
commit c34e14baec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 126 additions and 8 deletions

View File

@ -6,7 +6,8 @@ from django.http import JsonResponse
from django.utils.translation import gettext_lazy as _
from django_q.models import OrmQ
from rest_framework import permissions
from drf_spectacular.utils import OpenApiResponse, extend_schema
from rest_framework import permissions, serializers
from rest_framework.response import Response
from rest_framework.serializers import ValidationError
from rest_framework.views import APIView
@ -21,8 +22,9 @@ from plugin.serializers import MetadataSerializer
from users.models import ApiToken
from .email import is_email_configured
from .mixins import RetrieveUpdateAPI
from .mixins import ListAPI, RetrieveUpdateAPI
from .status import check_system_health, is_worker_running
from .version import inventreeApiText
from .views import AjaxView
@ -57,6 +59,34 @@ class VersionView(APIView):
})
class VersionSerializer(serializers.Serializer):
"""Serializer for a single version."""
version = serializers.CharField()
date = serializers.CharField()
gh = serializers.CharField()
text = serializers.CharField()
latest = serializers.BooleanField()
class Meta:
"""Meta class for VersionSerializer."""
fields = ['version', 'date', 'gh', 'text', 'latest']
class VersionApiSerializer(serializers.Serializer):
"""Serializer for the version api endpoint."""
VersionSerializer(many=True)
class VersionTextView(ListAPI):
"""Simple JSON endpoint for InvenTree version text."""
permission_classes = [permissions.IsAdminUser]
@extend_schema(responses={200: OpenApiResponse(response=VersionApiSerializer)})
def list(self, request, *args, **kwargs):
"""Return information about the InvenTree server."""
return JsonResponse(inventreeApiText())
class InfoView(AjaxView):
"""Simple JSON endpoint for InvenTree information.

View File

@ -3,10 +3,9 @@
# InvenTree API version
INVENTREE_API_VERSION = 148
"""Increment this API version number whenever there is a significant change to the API that any clients need to know about."""
"""
Increment this API version number whenever there is a significant change to the API that any clients need to know about
INVENTREE_API_TEXT = """
v148 -> 2023-11-06 : https://github.com/inventree/InvenTree/pull/5872
- Allow "quantity" to be specified when installing an item into another item
@ -115,7 +114,7 @@ v117 -> 2023-05-22 : https://github.com/inventree/InvenTree/pull/4854
v116 -> 2023-05-18 : https://github.com/inventree/InvenTree/pull/4823
- Updates to part parameter implementation, to use physical units
v115 - > 2023-05-18 : https://github.com/inventree/InvenTree/pull/4846
v115 -> 2023-05-18 : https://github.com/inventree/InvenTree/pull/4846
- Adds ability to partially scrap a build output
v114 -> 2023-05-16 : https://github.com/inventree/InvenTree/pull/4825

View File

@ -0,0 +1,41 @@
"""Tests for api_version."""
from django.urls import reverse
from InvenTree.api_version import INVENTREE_API_VERSION
from InvenTree.unit_test import InvenTreeAPITestCase
from InvenTree.version import inventreeApiText, parse_version_text
class ApiVersionTests(InvenTreeAPITestCase):
"""Tests for api_version functions and APIs."""
def test_api(self):
"""Test that the API text is correct."""
url = reverse('api-version-text')
response = self.client.get(url, format='json')
data = response.json()
self.assertEqual(len(data), 10)
def test_inventree_api_text(self):
"""Test that the inventreeApiText function works expected."""
# Normal run
resp = inventreeApiText()
self.assertEqual(len(resp), 10)
# More responses
resp = inventreeApiText(20)
self.assertEqual(len(resp), 20)
# Specific version
resp = inventreeApiText(start_version=5)
self.assertEqual(list(resp)[0], 'v5')
def test_parse_version_text(self):
"""Test that api version text is correctly parsed."""
resp = parse_version_text()
# Check that all texts are parsed
self.assertEqual(len(resp), INVENTREE_API_VERSION - 1)

View File

@ -36,7 +36,8 @@ from plugin.urls import get_plugin_urls
from stock.urls import stock_urls
from web.urls import urlpatterns as platform_urls
from .api import APISearchView, InfoView, NotFoundView, VersionView
from .api import (APISearchView, InfoView, NotFoundView, VersionTextView,
VersionView)
from .magic_login import GetSimpleLoginView
from .social_auth_urls import (EmailListView, EmailPrimaryView,
EmailRemoveView, EmailVerifyView,
@ -79,6 +80,7 @@ apipatterns = [
re_path('schema/', SpectacularAPIView.as_view(custom_settings={'SCHEMA_PATH_PREFIX': '/api/'}), name='schema'),
# InvenTree information endpoints
path("version-text", VersionTextView.as_view(), name="api-version-text"), # version text
path('version/', VersionView.as_view(), name='api-version'), # version info
path('', InfoView.as_view(), name='api-inventree-info'), # server info

View File

@ -16,7 +16,7 @@ from django.conf import settings
from dulwich.repo import NotGitRepository, Repo
from .api_version import INVENTREE_API_VERSION
from .api_version import INVENTREE_API_TEXT, INVENTREE_API_VERSION
# InvenTree software version
INVENTREE_SW_VERSION = "0.13.0 dev"
@ -142,6 +142,52 @@ def inventreeApiVersion():
return INVENTREE_API_VERSION
def parse_version_text():
"""Parse the version text to structured data."""
patched_data = INVENTREE_API_TEXT.split("\n\n")
# Remove first newline on latest version
patched_data[0] = patched_data[0].replace("\n", "", 1)
version_data = {}
for version in patched_data:
data = version.split("\n")
version_split = data[0].split(' -> ')
version_detail = version_split[1].split(':', 1) if len(version_split) > 1 else ['', ]
new_data = {
"version": version_split[0].strip(),
"date": version_detail[0].strip(),
"gh": version_detail[1].strip() if len(version_detail) > 1 else None,
"text": data[1:],
"latest": False,
}
version_data[new_data["version"]] = new_data
return version_data
INVENTREE_API_TEXT_DATA = parse_version_text()
"""Pre-processed API version text."""
def inventreeApiText(versions: int = 10, start_version: int = 0):
"""Returns API version descriptors.
Args:
versions: Number of versions to return. Default: 10
start_version: first version to report. Defaults to return the latest {versions} versions.
"""
version_data = INVENTREE_API_TEXT_DATA
# Define the range of versions to return
if start_version == 0:
start_version = INVENTREE_API_VERSION - versions
return {
f"v{a}": version_data.get(f"v{a}", None)
for a in range(start_version, start_version + versions)
}
def inventreeDjangoVersion():
"""Returns the version of Django library."""
return django.get_version()