mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +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:
parent
b0f6021002
commit
9d5522c18c
@ -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}')
|
||||||
|
Loading…
Reference in New Issue
Block a user