#20603: Split GraphQL API into v1 & v2

This commit is contained in:
Jeremy Stretch 2025-10-20 11:00:23 -04:00
parent 77c08b7bf9
commit 7d82493052
5 changed files with 80 additions and 8 deletions

View File

@ -1,5 +1,15 @@
# GraphQL API Parameters
## GRAPHQL_DEFAULT_VERSION
!!! note "This parameter was introduced in NetBox v4.5."
Default: `1`
Designates the default version of the GraphQL API served by `/graphql/`. To access a specific version, append the version number to the URL, e.g. `/graphql/v2/`.
---
## GRAPHQL_ENABLED
!!! tip "Dynamic Configuration Parameter"

View File

@ -1,7 +1,7 @@
import strawberry
from django.conf import settings
from strawberry_django.optimizer import DjangoOptimizerExtension
from strawberry.extensions import MaxAliasesLimiter # , SchemaExtension
from strawberry.extensions import MaxAliasesLimiter
from strawberry.schema.config import StrawberryConfig
from circuits.graphql.schema import CircuitsQuery
@ -16,9 +16,17 @@ from virtualization.graphql.schema import VirtualizationQuery
from vpn.graphql.schema import VPNQuery
from wireless.graphql.schema import WirelessQuery
__all__ = (
'Query',
'QueryV1',
'QueryV2',
'schema_v1',
'schema_v2',
)
@strawberry.type
class Query(
class QueryV1(
UsersQuery,
CircuitsQuery,
CoreQuery,
@ -31,11 +39,44 @@ class Query(
WirelessQuery,
*registry['plugins']['graphql_schemas'], # Append plugin schemas
):
"""Query class for GraphQL API v1"""
pass
schema = strawberry.Schema(
query=Query,
@strawberry.type
class QueryV2(
UsersQuery,
CircuitsQuery,
CoreQuery,
DCIMQuery,
ExtrasQuery,
IPAMQuery,
TenancyQuery,
VirtualizationQuery,
VPNQuery,
WirelessQuery,
*registry['plugins']['graphql_schemas'], # Append plugin schemas
):
"""Query class for GraphQL API v2"""
pass
# Expose a default Query class for the configured default GraphQL version
class Query(QueryV2 if settings.GRAPHQL_DEFAULT_VERSION == 2 else QueryV1):
pass
# Generate schemas for both versions of the GraphQL API
schema_v1 = strawberry.Schema(
query=QueryV1,
config=StrawberryConfig(auto_camel_case=False),
extensions=[
DjangoOptimizerExtension(prefetch_custom_queryset=True),
MaxAliasesLimiter(max_alias_count=settings.GRAPHQL_MAX_ALIASES),
]
)
schema_v2 = strawberry.Schema(
query=QueryV2,
config=StrawberryConfig(auto_camel_case=False),
extensions=[
DjangoOptimizerExtension(prefetch_custom_queryset=True),

View File

@ -0,0 +1,16 @@
from django.conf import settings
from netbox.graphql.schema import schema_v1, schema_v2
__all__ = (
'get_default_schema',
)
def get_default_schema():
"""
Returns the GraphQL schema corresponding to the value of the NETBOX_GRAPHQL_DEFAULT_SCHEMA setting.
"""
if settings.GRAPHQL_DEFAULT_VERSION == 2:
return schema_v2
return schema_v1

View File

@ -137,6 +137,7 @@ EVENTS_PIPELINE = getattr(configuration, 'EVENTS_PIPELINE', [
EXEMPT_VIEW_PERMISSIONS = getattr(configuration, 'EXEMPT_VIEW_PERMISSIONS', [])
FIELD_CHOICES = getattr(configuration, 'FIELD_CHOICES', {})
FILE_UPLOAD_MAX_MEMORY_SIZE = getattr(configuration, 'FILE_UPLOAD_MAX_MEMORY_SIZE', 2621440)
GRAPHQL_DEFAULT_VERSION = getattr(configuration, 'GRAPHQL_DEFAULT_VERSION', 1)
GRAPHQL_MAX_ALIASES = getattr(configuration, 'GRAPHQL_MAX_ALIASES', 10)
HOSTNAME = getattr(configuration, 'HOSTNAME', platform.node())
HTTP_PROXIES = getattr(configuration, 'HTTP_PROXIES', {})

View File

@ -6,7 +6,8 @@ from drf_spectacular.views import SpectacularAPIView, SpectacularRedocView, Spec
from account.views import LoginView, LogoutView
from netbox.api.views import APIRootView, StatusView
from netbox.graphql.schema import schema
from netbox.graphql.schema import schema_v1, schema_v2
from netbox.graphql.utils import get_default_schema
from netbox.graphql.views import NetBoxGraphQLView
from netbox.plugins.urls import plugin_patterns, plugin_api_patterns
from netbox.views import HomeView, MediaView, StaticMediaFailureView, SearchView, htmx
@ -40,7 +41,7 @@ _patterns = [
# HTMX views
path('htmx/object-selector/', htmx.ObjectSelectorView.as_view(), name='htmx_object_selector'),
# API
# REST API
path('api/', APIRootView.as_view(), name='api-root'),
path('api/circuits/', include('circuits.api.urls')),
path('api/core/', include('core.api.urls')),
@ -54,6 +55,7 @@ _patterns = [
path('api/wireless/', include('wireless.api.urls')),
path('api/status/', StatusView.as_view(), name='api-status'),
# REST API schema
path(
"api/schema/",
cache_page(timeout=86400, key_prefix=f"api_schema_{settings.RELEASE.version}")(
@ -64,8 +66,10 @@ _patterns = [
path('api/schema/swagger-ui/', SpectacularSwaggerView.as_view(url_name='schema'), name='api_docs'),
path('api/schema/redoc/', SpectacularRedocView.as_view(url_name='schema'), name='api_redocs'),
# GraphQL
path('graphql/', NetBoxGraphQLView.as_view(schema=schema), name='graphql'),
# GraphQL API
path('graphql/', NetBoxGraphQLView.as_view(schema=get_default_schema()), name='graphql'),
path('graphql/v1/', NetBoxGraphQLView.as_view(schema=schema_v1), name='graphql_v1'),
path('graphql/v2/', NetBoxGraphQLView.as_view(schema=schema_v2), name='graphql_v2'),
# Serving static media in Django to pipe it through LoginRequiredMiddleware
path('media/<path:path>', MediaView.as_view(), name='media'),