Skip to content

Commit 87505e0

Browse files
authored
Merge pull request #20632 from netbox-community/20603-graphql-api-v2
#20603: Split GraphQL API into v1 & v2
2 parents 77c08b7 + 7d82493 commit 87505e0

File tree

5 files changed

+80
-8
lines changed

5 files changed

+80
-8
lines changed

docs/configuration/graphql-api.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
# GraphQL API Parameters
22

3+
## GRAPHQL_DEFAULT_VERSION
4+
5+
!!! note "This parameter was introduced in NetBox v4.5."
6+
7+
Default: `1`
8+
9+
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/`.
10+
11+
---
12+
313
## GRAPHQL_ENABLED
414

515
!!! tip "Dynamic Configuration Parameter"

netbox/netbox/graphql/schema.py

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import strawberry
22
from django.conf import settings
33
from strawberry_django.optimizer import DjangoOptimizerExtension
4-
from strawberry.extensions import MaxAliasesLimiter # , SchemaExtension
4+
from strawberry.extensions import MaxAliasesLimiter
55
from strawberry.schema.config import StrawberryConfig
66

77
from circuits.graphql.schema import CircuitsQuery
@@ -16,9 +16,17 @@
1616
from vpn.graphql.schema import VPNQuery
1717
from wireless.graphql.schema import WirelessQuery
1818

19+
__all__ = (
20+
'Query',
21+
'QueryV1',
22+
'QueryV2',
23+
'schema_v1',
24+
'schema_v2',
25+
)
26+
1927

2028
@strawberry.type
21-
class Query(
29+
class QueryV1(
2230
UsersQuery,
2331
CircuitsQuery,
2432
CoreQuery,
@@ -31,11 +39,44 @@ class Query(
3139
WirelessQuery,
3240
*registry['plugins']['graphql_schemas'], # Append plugin schemas
3341
):
42+
"""Query class for GraphQL API v1"""
3443
pass
3544

3645

37-
schema = strawberry.Schema(
38-
query=Query,
46+
@strawberry.type
47+
class QueryV2(
48+
UsersQuery,
49+
CircuitsQuery,
50+
CoreQuery,
51+
DCIMQuery,
52+
ExtrasQuery,
53+
IPAMQuery,
54+
TenancyQuery,
55+
VirtualizationQuery,
56+
VPNQuery,
57+
WirelessQuery,
58+
*registry['plugins']['graphql_schemas'], # Append plugin schemas
59+
):
60+
"""Query class for GraphQL API v2"""
61+
pass
62+
63+
64+
# Expose a default Query class for the configured default GraphQL version
65+
class Query(QueryV2 if settings.GRAPHQL_DEFAULT_VERSION == 2 else QueryV1):
66+
pass
67+
68+
69+
# Generate schemas for both versions of the GraphQL API
70+
schema_v1 = strawberry.Schema(
71+
query=QueryV1,
72+
config=StrawberryConfig(auto_camel_case=False),
73+
extensions=[
74+
DjangoOptimizerExtension(prefetch_custom_queryset=True),
75+
MaxAliasesLimiter(max_alias_count=settings.GRAPHQL_MAX_ALIASES),
76+
]
77+
)
78+
schema_v2 = strawberry.Schema(
79+
query=QueryV2,
3980
config=StrawberryConfig(auto_camel_case=False),
4081
extensions=[
4182
DjangoOptimizerExtension(prefetch_custom_queryset=True),

netbox/netbox/graphql/utils.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
from django.conf import settings
2+
3+
from netbox.graphql.schema import schema_v1, schema_v2
4+
5+
__all__ = (
6+
'get_default_schema',
7+
)
8+
9+
10+
def get_default_schema():
11+
"""
12+
Returns the GraphQL schema corresponding to the value of the NETBOX_GRAPHQL_DEFAULT_SCHEMA setting.
13+
"""
14+
if settings.GRAPHQL_DEFAULT_VERSION == 2:
15+
return schema_v2
16+
return schema_v1

netbox/netbox/settings.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@
137137
EXEMPT_VIEW_PERMISSIONS = getattr(configuration, 'EXEMPT_VIEW_PERMISSIONS', [])
138138
FIELD_CHOICES = getattr(configuration, 'FIELD_CHOICES', {})
139139
FILE_UPLOAD_MAX_MEMORY_SIZE = getattr(configuration, 'FILE_UPLOAD_MAX_MEMORY_SIZE', 2621440)
140+
GRAPHQL_DEFAULT_VERSION = getattr(configuration, 'GRAPHQL_DEFAULT_VERSION', 1)
140141
GRAPHQL_MAX_ALIASES = getattr(configuration, 'GRAPHQL_MAX_ALIASES', 10)
141142
HOSTNAME = getattr(configuration, 'HOSTNAME', platform.node())
142143
HTTP_PROXIES = getattr(configuration, 'HTTP_PROXIES', {})

netbox/netbox/urls.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66

77
from account.views import LoginView, LogoutView
88
from netbox.api.views import APIRootView, StatusView
9-
from netbox.graphql.schema import schema
9+
from netbox.graphql.schema import schema_v1, schema_v2
10+
from netbox.graphql.utils import get_default_schema
1011
from netbox.graphql.views import NetBoxGraphQLView
1112
from netbox.plugins.urls import plugin_patterns, plugin_api_patterns
1213
from netbox.views import HomeView, MediaView, StaticMediaFailureView, SearchView, htmx
@@ -40,7 +41,7 @@
4041
# HTMX views
4142
path('htmx/object-selector/', htmx.ObjectSelectorView.as_view(), name='htmx_object_selector'),
4243

43-
# API
44+
# REST API
4445
path('api/', APIRootView.as_view(), name='api-root'),
4546
path('api/circuits/', include('circuits.api.urls')),
4647
path('api/core/', include('core.api.urls')),
@@ -54,6 +55,7 @@
5455
path('api/wireless/', include('wireless.api.urls')),
5556
path('api/status/', StatusView.as_view(), name='api-status'),
5657

58+
# REST API schema
5759
path(
5860
"api/schema/",
5961
cache_page(timeout=86400, key_prefix=f"api_schema_{settings.RELEASE.version}")(
@@ -64,8 +66,10 @@
6466
path('api/schema/swagger-ui/', SpectacularSwaggerView.as_view(url_name='schema'), name='api_docs'),
6567
path('api/schema/redoc/', SpectacularRedocView.as_view(url_name='schema'), name='api_redocs'),
6668

67-
# GraphQL
68-
path('graphql/', NetBoxGraphQLView.as_view(schema=schema), name='graphql'),
69+
# GraphQL API
70+
path('graphql/', NetBoxGraphQLView.as_view(schema=get_default_schema()), name='graphql'),
71+
path('graphql/v1/', NetBoxGraphQLView.as_view(schema=schema_v1), name='graphql_v1'),
72+
path('graphql/v2/', NetBoxGraphQLView.as_view(schema=schema_v2), name='graphql_v2'),
6973

7074
# Serving static media in Django to pipe it through LoginRequiredMiddleware
7175
path('media/<path:path>', MediaView.as_view(), name='media'),

0 commit comments

Comments
 (0)