mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-11-04 15:15:42 +00:00 
			
		
		
		
	[FR] Update to OpenAPI from CoreAPI (#4178)
* [FR] Update to OpenAPI from CoreAPI Fixes #3226 * factor request function out * add schema export task * add api-docs * add action to check if diff occured * also wait for docstyle * use full command * add envs for inventree * update inventree before running * use relative path * remove schema action * remove tags to fit 3.0 parsers * fix url base name for reloads * revert change in plugin resolver * remove unused tags * add rapidoc too * declare api regex * fix as suggested by @martonmiklos in https://github.com/inventree/InvenTree/pull/4178#discussion_r1167279443 * set inventree logo * remove Rapidoc
This commit is contained in:
		@@ -26,6 +26,7 @@ from sentry_sdk.integrations.django import DjangoIntegration
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
from . import config
 | 
					from . import config
 | 
				
			||||||
from .config import get_boolean_setting, get_custom_file, get_setting
 | 
					from .config import get_boolean_setting, get_custom_file, get_setting
 | 
				
			||||||
 | 
					from .version import inventreeApiVersion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
INVENTREE_NEWS_URL = 'https://inventree.org/news/feed.atom'
 | 
					INVENTREE_NEWS_URL = 'https://inventree.org/news/feed.atom'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -233,6 +234,7 @@ INSTALLED_APPS = [
 | 
				
			|||||||
    'django_otp.plugins.otp_static',        # Backup codes
 | 
					    'django_otp.plugins.otp_static',        # Backup codes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    'allauth_2fa',                          # MFA flow for allauth
 | 
					    'allauth_2fa',                          # MFA flow for allauth
 | 
				
			||||||
 | 
					    'drf_spectacular',                      # API documentation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    'django_ical',                          # For exporting calendars
 | 
					    'django_ical',                          # For exporting calendars
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
@@ -356,7 +358,7 @@ REST_FRAMEWORK = {
 | 
				
			|||||||
        'rest_framework.permissions.DjangoModelPermissions',
 | 
					        'rest_framework.permissions.DjangoModelPermissions',
 | 
				
			||||||
        'InvenTree.permissions.RolePermission',
 | 
					        'InvenTree.permissions.RolePermission',
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema',
 | 
					    'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
 | 
				
			||||||
    'DEFAULT_METADATA_CLASS': 'InvenTree.metadata.InvenTreeMetadata',
 | 
					    'DEFAULT_METADATA_CLASS': 'InvenTree.metadata.InvenTreeMetadata',
 | 
				
			||||||
    'DEFAULT_RENDERER_CLASSES': [
 | 
					    'DEFAULT_RENDERER_CLASSES': [
 | 
				
			||||||
        'rest_framework.renderers.JSONRenderer',
 | 
					        'rest_framework.renderers.JSONRenderer',
 | 
				
			||||||
@@ -367,6 +369,15 @@ if DEBUG:
 | 
				
			|||||||
    # Enable browsable API if in DEBUG mode
 | 
					    # Enable browsable API if in DEBUG mode
 | 
				
			||||||
    REST_FRAMEWORK['DEFAULT_RENDERER_CLASSES'].append('rest_framework.renderers.BrowsableAPIRenderer')
 | 
					    REST_FRAMEWORK['DEFAULT_RENDERER_CLASSES'].append('rest_framework.renderers.BrowsableAPIRenderer')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SPECTACULAR_SETTINGS = {
 | 
				
			||||||
 | 
					    'TITLE': 'InvenTree API',
 | 
				
			||||||
 | 
					    'DESCRIPTION': 'API for InvenTree - the intuitive open source inventory management system',
 | 
				
			||||||
 | 
					    'LICENSE': {'MIT': 'https://github.com/inventree/InvenTree/blob/master/LICENSE'},
 | 
				
			||||||
 | 
					    'EXTERNAL_DOCS': {'docs': 'https://docs.inventree.org', 'web': 'https://inventree.org'},
 | 
				
			||||||
 | 
					    'VERSION': inventreeApiVersion(),
 | 
				
			||||||
 | 
					    'SERVE_INCLUDE_SCHEMA': False,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
WSGI_APPLICATION = 'InvenTree.wsgi.application'
 | 
					WSGI_APPLICATION = 'InvenTree.wsgi.application'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
"""
 | 
					"""
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,7 +9,7 @@ from django.contrib import admin
 | 
				
			|||||||
from django.urls import include, path, re_path
 | 
					from django.urls import include, path, re_path
 | 
				
			||||||
from django.views.generic.base import RedirectView
 | 
					from django.views.generic.base import RedirectView
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from rest_framework.documentation import include_docs_urls
 | 
					from drf_spectacular.views import SpectacularAPIView, SpectacularRedocView
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from build.api import build_api_urls
 | 
					from build.api import build_api_urls
 | 
				
			||||||
from build.urls import build_urls
 | 
					from build.urls import build_urls
 | 
				
			||||||
@@ -62,9 +62,12 @@ apipatterns = [
 | 
				
			|||||||
    # Plugin endpoints
 | 
					    # Plugin endpoints
 | 
				
			||||||
    path('', include(plugin_api_urls)),
 | 
					    path('', include(plugin_api_urls)),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Webhook endpoints
 | 
					    # Common endpoints enpoint
 | 
				
			||||||
    path('', include(common_api_urls)),
 | 
					    path('', include(common_api_urls)),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # OpenAPI Schema
 | 
				
			||||||
 | 
					    re_path('schema/', SpectacularAPIView.as_view(custom_settings={'SCHEMA_PATH_PREFIX': '/api/'}), name='schema'),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # InvenTree information endpoint
 | 
					    # InvenTree information endpoint
 | 
				
			||||||
    path('', InfoView.as_view(), name='api-inventree-info'),
 | 
					    path('', InfoView.as_view(), name='api-inventree-info'),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -136,7 +139,7 @@ backendpatterns = [
 | 
				
			|||||||
    re_path(r'^auth/?', auth_request),
 | 
					    re_path(r'^auth/?', auth_request),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    re_path(r'^api/', include(apipatterns)),
 | 
					    re_path(r'^api/', include(apipatterns)),
 | 
				
			||||||
    re_path(r'^api-doc/', include_docs_urls(title='InvenTree API')),
 | 
					    re_path(r'^api-doc/', SpectacularRedocView.as_view(url_name='schema'), name='api-doc'),
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
frontendpatterns = [
 | 
					frontendpatterns = [
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,7 +9,6 @@ import subprocess
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import django
 | 
					import django
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import common.models
 | 
					 | 
				
			||||||
from InvenTree.api_version import INVENTREE_API_VERSION
 | 
					from InvenTree.api_version import INVENTREE_API_VERSION
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# InvenTree software version
 | 
					# InvenTree software version
 | 
				
			||||||
@@ -18,11 +17,15 @@ INVENTREE_SW_VERSION = "0.12.0 dev"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
def inventreeInstanceName():
 | 
					def inventreeInstanceName():
 | 
				
			||||||
    """Returns the InstanceName settings for the current database."""
 | 
					    """Returns the InstanceName settings for the current database."""
 | 
				
			||||||
 | 
					    import common.models
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return common.models.InvenTreeSetting.get_setting("INVENTREE_INSTANCE", "")
 | 
					    return common.models.InvenTreeSetting.get_setting("INVENTREE_INSTANCE", "")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def inventreeInstanceTitle():
 | 
					def inventreeInstanceTitle():
 | 
				
			||||||
    """Returns the InstanceTitle for the current database."""
 | 
					    """Returns the InstanceTitle for the current database."""
 | 
				
			||||||
 | 
					    import common.models
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if common.models.InvenTreeSetting.get_setting("INVENTREE_INSTANCE_TITLE", False):
 | 
					    if common.models.InvenTreeSetting.get_setting("INVENTREE_INSTANCE_TITLE", False):
 | 
				
			||||||
        return common.models.InvenTreeSetting.get_setting("INVENTREE_INSTANCE", "")
 | 
					        return common.models.InvenTreeSetting.get_setting("INVENTREE_INSTANCE", "")
 | 
				
			||||||
    else:
 | 
					    else:
 | 
				
			||||||
@@ -66,6 +69,7 @@ def isInvenTreeUpToDate():
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    A background task periodically queries GitHub for latest version, and stores it to the database as "_INVENTREE_LATEST_VERSION"
 | 
					    A background task periodically queries GitHub for latest version, and stores it to the database as "_INVENTREE_LATEST_VERSION"
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
 | 
					    import common.models
 | 
				
			||||||
    latest = common.models.InvenTreeSetting.get_setting('_INVENTREE_LATEST_VERSION', backup_value=None, create=False)
 | 
					    latest = common.models.InvenTreeSetting.get_setting('_INVENTREE_LATEST_VERSION', backup_value=None, create=False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # No record for "latest" version - we must assume we are up to date!
 | 
					    # No record for "latest" version - we must assume we are up to date!
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,6 +27,7 @@ django-user-sessions                    # user sessions in DB
 | 
				
			|||||||
django-weasyprint                       # django weasyprint integration
 | 
					django-weasyprint                       # django weasyprint integration
 | 
				
			||||||
djangorestframework                     # DRF framework
 | 
					djangorestframework                     # DRF framework
 | 
				
			||||||
django-xforwardedfor-middleware         # IP forwarding metadata
 | 
					django-xforwardedfor-middleware         # IP forwarding metadata
 | 
				
			||||||
 | 
					drf-spectacular                         # DRF API documentation
 | 
				
			||||||
feedparser                              # RSS newsfeed parser
 | 
					feedparser                              # RSS newsfeed parser
 | 
				
			||||||
gunicorn                                # Gunicorn web server
 | 
					gunicorn                                # Gunicorn web server
 | 
				
			||||||
pdf2image                               # PDF to image conversion
 | 
					pdf2image                               # PDF to image conversion
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,6 +8,8 @@ arrow==1.2.3
 | 
				
			|||||||
    # via django-q
 | 
					    # via django-q
 | 
				
			||||||
asgiref==3.6.0
 | 
					asgiref==3.6.0
 | 
				
			||||||
    # via django
 | 
					    # via django
 | 
				
			||||||
 | 
					attrs==22.2.0
 | 
				
			||||||
 | 
					    # via jsonschema
 | 
				
			||||||
babel==2.12.1
 | 
					babel==2.12.1
 | 
				
			||||||
    # via py-moneyed
 | 
					    # via py-moneyed
 | 
				
			||||||
bleach[css]==6.0.0
 | 
					bleach[css]==6.0.0
 | 
				
			||||||
@@ -70,6 +72,7 @@ django==3.2.18
 | 
				
			|||||||
    #   django-weasyprint
 | 
					    #   django-weasyprint
 | 
				
			||||||
    #   django-xforwardedfor-middleware
 | 
					    #   django-xforwardedfor-middleware
 | 
				
			||||||
    #   djangorestframework
 | 
					    #   djangorestframework
 | 
				
			||||||
 | 
					    #   drf-spectacular
 | 
				
			||||||
django-allauth==0.54.0
 | 
					django-allauth==0.54.0
 | 
				
			||||||
    # via
 | 
					    # via
 | 
				
			||||||
    #   -r requirements.in
 | 
					    #   -r requirements.in
 | 
				
			||||||
@@ -129,6 +132,10 @@ django-weasyprint==2.2.0
 | 
				
			|||||||
django-xforwardedfor-middleware==2.0
 | 
					django-xforwardedfor-middleware==2.0
 | 
				
			||||||
    # via -r requirements.in
 | 
					    # via -r requirements.in
 | 
				
			||||||
djangorestframework==3.14.0
 | 
					djangorestframework==3.14.0
 | 
				
			||||||
 | 
					    # via
 | 
				
			||||||
 | 
					    #   -r requirements.in
 | 
				
			||||||
 | 
					    #   drf-spectacular
 | 
				
			||||||
 | 
					drf-spectacular==0.25.1
 | 
				
			||||||
    # via -r requirements.in
 | 
					    # via -r requirements.in
 | 
				
			||||||
et-xmlfile==1.1.0
 | 
					et-xmlfile==1.1.0
 | 
				
			||||||
    # via openpyxl
 | 
					    # via openpyxl
 | 
				
			||||||
@@ -146,10 +153,14 @@ idna==3.4
 | 
				
			|||||||
    # via requests
 | 
					    # via requests
 | 
				
			||||||
importlib-metadata==6.1.0
 | 
					importlib-metadata==6.1.0
 | 
				
			||||||
    # via markdown
 | 
					    # via markdown
 | 
				
			||||||
 | 
					inflection==0.5.1
 | 
				
			||||||
 | 
					    # via drf-spectacular
 | 
				
			||||||
itypes==1.2.0
 | 
					itypes==1.2.0
 | 
				
			||||||
    # via coreapi
 | 
					    # via coreapi
 | 
				
			||||||
jinja2==3.1.2
 | 
					jinja2==3.1.2
 | 
				
			||||||
    # via coreschema
 | 
					    # via coreschema
 | 
				
			||||||
 | 
					jsonschema==4.17.3
 | 
				
			||||||
 | 
					    # via drf-spectacular
 | 
				
			||||||
markdown==3.4.3
 | 
					markdown==3.4.3
 | 
				
			||||||
    # via django-markdownify
 | 
					    # via django-markdownify
 | 
				
			||||||
markuppy==1.14
 | 
					markuppy==1.14
 | 
				
			||||||
@@ -186,6 +197,8 @@ pyphen==0.14.0
 | 
				
			|||||||
    # via weasyprint
 | 
					    # via weasyprint
 | 
				
			||||||
pypng==0.20220715.0
 | 
					pypng==0.20220715.0
 | 
				
			||||||
    # via qrcode
 | 
					    # via qrcode
 | 
				
			||||||
 | 
					pyrsistent==0.19.3
 | 
				
			||||||
 | 
					    # via jsonschema
 | 
				
			||||||
python-barcode[images]==0.14.0
 | 
					python-barcode[images]==0.14.0
 | 
				
			||||||
    # via -r requirements.in
 | 
					    # via -r requirements.in
 | 
				
			||||||
python-dateutil==2.8.2
 | 
					python-dateutil==2.8.2
 | 
				
			||||||
@@ -204,7 +217,9 @@ pytz==2023.3
 | 
				
			|||||||
    #   djangorestframework
 | 
					    #   djangorestframework
 | 
				
			||||||
    #   icalendar
 | 
					    #   icalendar
 | 
				
			||||||
pyyaml==6.0
 | 
					pyyaml==6.0
 | 
				
			||||||
    # via tablib
 | 
					    # via
 | 
				
			||||||
 | 
					    #   drf-spectacular
 | 
				
			||||||
 | 
					    #   tablib
 | 
				
			||||||
qrcode[pil]==7.4.2
 | 
					qrcode[pil]==7.4.2
 | 
				
			||||||
    # via
 | 
					    # via
 | 
				
			||||||
    #   -r requirements.in
 | 
					    #   -r requirements.in
 | 
				
			||||||
@@ -252,7 +267,9 @@ tinycss2==1.1.1
 | 
				
			|||||||
typing-extensions==4.5.0
 | 
					typing-extensions==4.5.0
 | 
				
			||||||
    # via qrcode
 | 
					    # via qrcode
 | 
				
			||||||
uritemplate==4.1.1
 | 
					uritemplate==4.1.1
 | 
				
			||||||
    # via coreapi
 | 
					    # via
 | 
				
			||||||
 | 
					    #   coreapi
 | 
				
			||||||
 | 
					    #   drf-spectacular
 | 
				
			||||||
urllib3==1.26.15
 | 
					urllib3==1.26.15
 | 
				
			||||||
    # via
 | 
					    # via
 | 
				
			||||||
    #   requests
 | 
					    #   requests
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										34
									
								
								tasks.py
									
									
									
									
									
								
							
							
						
						
									
										34
									
								
								tasks.py
									
									
									
									
									
								
							@@ -88,6 +88,22 @@ def manage(c, cmd, pty: bool = False):
 | 
				
			|||||||
    ), pty=pty)
 | 
					    ), pty=pty)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def check_file_existance(filename: str, overwrite: bool = False):
 | 
				
			||||||
 | 
					    """Checks if a file exists and asks the user if it should be overwritten.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Args:
 | 
				
			||||||
 | 
					        filename (str): Name of the file to check.
 | 
				
			||||||
 | 
					        overwrite (bool, optional): Overwrite the file without asking. Defaults to False.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    if Path(filename).is_file() and overwrite is False:
 | 
				
			||||||
 | 
					        response = input("Warning: file already exists. Do you want to overwrite? [y/N]: ")
 | 
				
			||||||
 | 
					        response = str(response).strip().lower()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if response not in ['y', 'yes']:
 | 
				
			||||||
 | 
					            print("Cancelled export operation")
 | 
				
			||||||
 | 
					            sys.exit(1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Install tasks
 | 
					# Install tasks
 | 
				
			||||||
@task
 | 
					@task
 | 
				
			||||||
def plugins(c):
 | 
					def plugins(c):
 | 
				
			||||||
@@ -305,13 +321,7 @@ def export_records(c, filename='data.json', overwrite=False, include_permissions
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    print(f"Exporting database records to file '{filename}'")
 | 
					    print(f"Exporting database records to file '{filename}'")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if Path(filename).is_file() and overwrite is False:
 | 
					    check_file_existance(filename, overwrite)
 | 
				
			||||||
        response = input("Warning: file already exists. Do you want to overwrite? [y/N]: ")
 | 
					 | 
				
			||||||
        response = str(response).strip().lower()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if response not in ['y', 'yes']:
 | 
					 | 
				
			||||||
            print("Cancelled export operation")
 | 
					 | 
				
			||||||
            sys.exit(1)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    tmpfile = f"{filename}.tmp"
 | 
					    tmpfile = f"{filename}.tmp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -621,3 +631,13 @@ def coverage(c):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    # Generate coverage report
 | 
					    # Generate coverage report
 | 
				
			||||||
    c.run('coverage html -i')
 | 
					    c.run('coverage html -i')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@task(help={
 | 
				
			||||||
 | 
					    'filename': "Output filename (default = 'schema.yml')",
 | 
				
			||||||
 | 
					    'overwrite': "Overwrite existing files without asking first (default = off/False)",
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					def schema(c, filename='schema.yml', overwrite=False):
 | 
				
			||||||
 | 
					    """Export current API schema."""
 | 
				
			||||||
 | 
					    check_file_existance(filename, overwrite)
 | 
				
			||||||
 | 
					    manage(c, f'spectacular --file {filename}')
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user