Просмотр исходного кода

Merge pull request #20632 from netbox-community/20603-graphql-api-v2

#20603: Split GraphQL API into v1 & v2
bctiemann 3 месяцев назад
Родитель
Сommit
87505e0bb9

+ 10 - 0
docs/configuration/graphql-api.md

@@ -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"

+ 45 - 4
netbox/netbox/graphql/schema.py

@@ -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),

+ 16 - 0
netbox/netbox/graphql/utils.py

@@ -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

+ 1 - 0
netbox/netbox/settings.py

@@ -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', {})

+ 8 - 4
netbox/netbox/urls.py

@@ -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'),