Skip to content

Commit 45c99e4

Browse files
9856 Replace graphene with Strawberry (#15141)
* 9856 base strawberry integration * 9856 user and group * 9856 user and circuits base * 9856 extras and mixins * 9856 fk * 9856 update strawberry version * 9856 update imports * 9856 compatability fixes * 9856 compatability fixes * 9856 update strawberry types * 9856 update strawberry types * 9856 core schema * 9856 dcim schema * 9856 extras schema * 9856 ipam and tenant schema * 9856 virtualization, vpn, wireless schema * 9856 fix old decorator * 9856 cleanup * 9856 cleanup * 9856 fixes to circuits type specifiers * 9856 fixes to circuits type specifiers * 9856 update types * 9856 GFK working * 9856 GFK working * 9856 _name * 9856 misc fixes * 9856 type updates * 9856 _name to types * 9856 update types * 9856 update types * 9856 update types * 9856 update types * 9856 update types * 9856 update types * 9856 update types * 9856 update types * 9856 update types * 9856 GraphQLView * 9856 GraphQLView * 9856 fix OrganizationalObjectType * 9856 single item query for schema * 9856 circuits graphql tests working * 9856 test fixes * 9856 test fixes * 9856 test fixes * 9856 test fix vpn * 9856 test fixes * 9856 test fixes * 9856 test fixes * 9856 circuits test sans DjangoModelType * 9856 core test sans DjangoModelType * 9856 temp checkin * 9856 fix extas FK * 9856 fix tenancy FK * 9856 fix virtualization FK * 9856 fix vpn FK * 9856 fix wireless FK * 9856 fix ipam FK * 9856 fix partial dcim FK * 9856 fix dcim FK * 9856 fix virtualization FK * 9856 fix tests / remove debug code * 9856 fix test imagefield * 9856 cleanup graphene * 9856 fix plugin schema * 9856 fix requirements * 9856 fix requirements * 9856 fix docs * 9856 fix docs * 9856 temp fix tests * 9856 first filterset * 9856 first filterset * 9856 fix tests * 9856 fix tests * 9856 working auto filter generation * 9856 filter types * 9856 filter types * 9856 filter types * 9856 fix graphiql test * 9856 fix counter fields and merge feature * 9856 temp fix tests * 9856 fix tests * 9856 fix tenancy, ipam filter definitions * 9856 cleanup * 9856 cleanup * 9856 cleanup * 9856 review changes * 9856 review changes * 9856 review changes * 9856 fix base-requirements * 9856 add wrapper to graphiql * 9856 remove old graphiql debug toolbar * 9856 review changes * 9856 update strawberry * 9856 remove superfluous check --------- Co-authored-by: Jeremy Stretch <[email protected]>
1 parent 423c981 commit 45c99e4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

70 files changed

+97902
-2410
lines changed

base_requirements.txt

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,6 @@ django-debug-toolbar
1414
# https://github.com/carltongibson/django-filter/blob/main/CHANGES.rst
1515
django-filter
1616

17-
# Django debug toolbar extension with support for GraphiQL
18-
# https://github.com/flavors/django-graphiql-debug-toolbar/blob/main/CHANGES.rst
19-
django-graphiql-debug-toolbar
20-
2117
# HTMX utilities for Django
2218
# https://django-htmx.readthedocs.io/en/latest/changelog.html
2319
django-htmx
@@ -75,11 +71,6 @@ drf-spectacular-sidecar
7571
# https://github.com/kurtmckee/feedparser/blob/develop/CHANGELOG.rst
7672
feedparser
7773

78-
# Django wrapper for Graphene (GraphQL support)
79-
# https://github.com/graphql-python/graphene-django/releases
80-
# Pinned to v3.0.0 for GraphiQL UI issue (see #12762)
81-
graphene_django==3.0.0
82-
8374
# WSGI HTTP server
8475
# https://docs.gunicorn.org/en/latest/news.html
8576
gunicorn
@@ -136,8 +127,16 @@ social-auth-core
136127
# https://github.com/python-social-auth/social-app-django/blob/master/CHANGELOG.md
137128
social-auth-app-django
138129

130+
# Strawberry GraphQL
131+
# https://github.com/strawberry-graphql/strawberry/blob/main/CHANGELOG.md
132+
strawberry-graphql
133+
134+
# Strawberry GraphQL Django extension
135+
# https://github.com/strawberry-graphql/strawberry-django/blob/main/CHANGELOG.md
136+
strawberry-graphql-django
137+
139138
# SVG image rendering (used for rack elevations)
140-
# hhttps://github.com/mozman/svgwrite/blob/master/NEWS.rst
139+
# https://github.com/mozman/svgwrite/blob/master/NEWS.rst
141140
svgwrite
142141

143142
# Tabular dataset library (for table-based exports)

docs/plugins/development/graphql-api.md

Lines changed: 22 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,32 @@ A plugin can extend NetBox's GraphQL API by registering its own schema class. By
88

99
```python
1010
# graphql.py
11-
import graphene
12-
from netbox.graphql.types import NetBoxObjectType
13-
from netbox.graphql.fields import ObjectField, ObjectListField
14-
from . import filtersets, models
11+
from typing import List
12+
import strawberry
13+
import strawberry_django
1514

16-
class MyModelType(NetBoxObjectType):
15+
from . import models
1716

18-
class Meta:
19-
model = models.MyModel
20-
fields = '__all__'
21-
filterset_class = filtersets.MyModelFilterSet
2217

23-
class MyQuery(graphene.ObjectType):
24-
mymodel = ObjectField(MyModelType)
25-
mymodel_list = ObjectListField(MyModelType)
18+
@strawberry_django.type(
19+
models.MyModel,
20+
fields='__all__',
21+
)
22+
class MyModelType:
23+
pass
2624

27-
schema = MyQuery
25+
26+
@strawberry.type
27+
class MyQuery:
28+
@strawberry.field
29+
def dummymodel(self, id: int) -> DummyModelType:
30+
return None
31+
dummymodel_list: List[DummyModelType] = strawberry_django.field()
32+
33+
34+
schema = [
35+
MyQuery,
36+
]
2837
```
2938

3039
## GraphQL Objects
@@ -38,15 +47,3 @@ NetBox provides two object type classes for use by plugins.
3847
::: netbox.graphql.types.NetBoxObjectType
3948
options:
4049
members: false
41-
42-
## GraphQL Fields
43-
44-
NetBox provides two field classes for use by plugins.
45-
46-
::: netbox.graphql.fields.ObjectField
47-
options:
48-
members: false
49-
50-
::: netbox.graphql.fields.ObjectListField
51-
options:
52-
members: false

netbox/circuits/graphql/filters.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import strawberry
2+
import strawberry_django
3+
from circuits import filtersets, models
4+
5+
from netbox.graphql.filter_mixins import autotype_decorator, BaseFilterMixin
6+
7+
__all__ = (
8+
'CircuitTerminationFilter',
9+
'CircuitFilter',
10+
'CircuitTypeFilter',
11+
'ProviderFilter',
12+
'ProviderAccountFilter',
13+
'ProviderNetworkFilter',
14+
)
15+
16+
17+
@strawberry_django.filter(models.CircuitTermination, lookups=True)
18+
@autotype_decorator(filtersets.CircuitTerminationFilterSet)
19+
class CircuitTerminationFilter(BaseFilterMixin):
20+
pass
21+
22+
23+
@strawberry_django.filter(models.Circuit, lookups=True)
24+
@autotype_decorator(filtersets.CircuitFilterSet)
25+
class CircuitFilter(BaseFilterMixin):
26+
pass
27+
28+
29+
@strawberry_django.filter(models.CircuitType, lookups=True)
30+
@autotype_decorator(filtersets.CircuitTypeFilterSet)
31+
class CircuitTypeFilter(BaseFilterMixin):
32+
pass
33+
34+
35+
@strawberry_django.filter(models.Provider, lookups=True)
36+
@autotype_decorator(filtersets.ProviderFilterSet)
37+
class ProviderFilter(BaseFilterMixin):
38+
pass
39+
40+
41+
@strawberry_django.filter(models.ProviderAccount, lookups=True)
42+
@autotype_decorator(filtersets.ProviderAccountFilterSet)
43+
class ProviderAccountFilter(BaseFilterMixin):
44+
pass
45+
46+
47+
@strawberry_django.filter(models.ProviderNetwork, lookups=True)
48+
@autotype_decorator(filtersets.ProviderNetworkFilterSet)
49+
class ProviderNetworkFilter(BaseFilterMixin):
50+
pass

netbox/circuits/graphql/schema.py

Lines changed: 35 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,40 @@
1-
import graphene
1+
from typing import List
2+
3+
import strawberry
4+
import strawberry_django
25

36
from circuits import models
4-
from netbox.graphql.fields import ObjectField, ObjectListField
57
from .types import *
6-
from utilities.graphql_optimizer import gql_query_optimizer
7-
8-
9-
class CircuitsQuery(graphene.ObjectType):
10-
circuit = ObjectField(CircuitType)
11-
circuit_list = ObjectListField(CircuitType)
12-
13-
def resolve_circuit_list(root, info, **kwargs):
14-
return gql_query_optimizer(models.Circuit.objects.all(), info)
15-
16-
circuit_termination = ObjectField(CircuitTerminationType)
17-
circuit_termination_list = ObjectListField(CircuitTerminationType)
18-
19-
def resolve_circuit_termination_list(root, info, **kwargs):
20-
return gql_query_optimizer(models.CircuitTermination.objects.all(), info)
21-
22-
circuit_type = ObjectField(CircuitTypeType)
23-
circuit_type_list = ObjectListField(CircuitTypeType)
24-
25-
def resolve_circuit_type_list(root, info, **kwargs):
26-
return gql_query_optimizer(models.CircuitType.objects.all(), info)
27-
28-
provider = ObjectField(ProviderType)
29-
provider_list = ObjectListField(ProviderType)
30-
31-
def resolve_provider_list(root, info, **kwargs):
32-
return gql_query_optimizer(models.Provider.objects.all(), info)
33-
34-
provider_account = ObjectField(ProviderAccountType)
35-
provider_account_list = ObjectListField(ProviderAccountType)
368

37-
provider_network = ObjectField(ProviderNetworkType)
38-
provider_network_list = ObjectListField(ProviderNetworkType)
399

40-
def resolve_provider_network_list(root, info, **kwargs):
41-
return gql_query_optimizer(models.ProviderNetwork.objects.all(), info)
10+
@strawberry.type
11+
class CircuitsQuery:
12+
@strawberry.field
13+
def circuit(self, id: int) -> CircuitType:
14+
return models.Circuit.objects.get(pk=id)
15+
circuit_list: List[CircuitType] = strawberry_django.field()
16+
17+
@strawberry.field
18+
def circuit_termination(self, id: int) -> CircuitTerminationType:
19+
return models.CircuitTermination.objects.get(pk=id)
20+
circuit_termination_list: List[CircuitTerminationType] = strawberry_django.field()
21+
22+
@strawberry.field
23+
def circuit_type(self, id: int) -> CircuitTypeType:
24+
return models.CircuitType.objects.get(pk=id)
25+
circuit_type_list: List[CircuitTypeType] = strawberry_django.field()
26+
27+
@strawberry.field
28+
def provider(self, id: int) -> ProviderType:
29+
return models.Provider.objects.get(pk=id)
30+
provider_list: List[ProviderType] = strawberry_django.field()
31+
32+
@strawberry.field
33+
def provider_account(self, id: int) -> ProviderAccountType:
34+
return models.ProviderAccount.objects.get(pk=id)
35+
provider_account_list: List[ProviderAccountType] = strawberry_django.field()
36+
37+
@strawberry.field
38+
def provider_network(self, id: int) -> ProviderNetworkType:
39+
return models.ProviderNetwork.objects.get(pk=id)
40+
provider_network_list: List[ProviderNetworkType] = strawberry_django.field()

netbox/circuits/graphql/types.py

Lines changed: 84 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
1-
import graphene
1+
from typing import Annotated, List
22

3-
from circuits import filtersets, models
3+
import strawberry
4+
import strawberry_django
5+
6+
from circuits import models
47
from dcim.graphql.mixins import CabledObjectMixin
5-
from extras.graphql.mixins import CustomFieldsMixin, TagsMixin, ContactsMixin
6-
from netbox.graphql.types import ObjectType, OrganizationalObjectType, NetBoxObjectType
8+
from extras.graphql.mixins import ContactsMixin, CustomFieldsMixin, TagsMixin
9+
from netbox.graphql.types import NetBoxObjectType, ObjectType, OrganizationalObjectType
10+
from tenancy.graphql.types import TenantType
11+
from .filters import *
712

813
__all__ = (
914
'CircuitTerminationType',
@@ -15,48 +20,93 @@
1520
)
1621

1722

18-
class CircuitTerminationType(CustomFieldsMixin, TagsMixin, CabledObjectMixin, ObjectType):
23+
@strawberry_django.type(
24+
models.Provider,
25+
fields='__all__',
26+
filters=ProviderFilter
27+
)
28+
class ProviderType(NetBoxObjectType, ContactsMixin):
1929

20-
class Meta:
21-
model = models.CircuitTermination
22-
fields = '__all__'
23-
filterset_class = filtersets.CircuitTerminationFilterSet
30+
@strawberry_django.field
31+
def networks(self) -> List[Annotated["ProviderNetworkType", strawberry.lazy('circuits.graphql.types')]]:
32+
return self.networks.all()
2433

34+
@strawberry_django.field
35+
def circuits(self) -> List[Annotated["CircuitType", strawberry.lazy('circuits.graphql.types')]]:
36+
return self.circuits.all()
2537

26-
class CircuitType(NetBoxObjectType, ContactsMixin):
27-
class Meta:
28-
model = models.Circuit
29-
fields = '__all__'
30-
filterset_class = filtersets.CircuitFilterSet
38+
@strawberry_django.field
39+
def asns(self) -> List[Annotated["ASNType", strawberry.lazy('ipam.graphql.types')]]:
40+
return self.asns.all()
3141

42+
@strawberry_django.field
43+
def accounts(self) -> List[Annotated["ProviderAccountType", strawberry.lazy('circuits.graphql.types')]]:
44+
return self.accounts.all()
3245

33-
class CircuitTypeType(OrganizationalObjectType):
3446

35-
class Meta:
36-
model = models.CircuitType
37-
fields = '__all__'
38-
filterset_class = filtersets.CircuitTypeFilterSet
47+
@strawberry_django.type(
48+
models.ProviderAccount,
49+
fields='__all__',
50+
filters=ProviderAccountFilter
51+
)
52+
class ProviderAccountType(NetBoxObjectType):
53+
provider: Annotated["ProviderType", strawberry.lazy('circuits.graphql.types')]
3954

55+
@strawberry_django.field
56+
def circuits(self) -> List[Annotated["CircuitType", strawberry.lazy('circuits.graphql.types')]]:
57+
return self.circuits.all()
4058

41-
class ProviderType(NetBoxObjectType, ContactsMixin):
4259

43-
class Meta:
44-
model = models.Provider
45-
fields = '__all__'
46-
filterset_class = filtersets.ProviderFilterSet
60+
@strawberry_django.type(
61+
models.ProviderNetwork,
62+
fields='__all__',
63+
filters=ProviderNetworkFilter
64+
)
65+
class ProviderNetworkType(NetBoxObjectType):
66+
provider: Annotated["ProviderType", strawberry.lazy('circuits.graphql.types')]
4767

68+
@strawberry_django.field
69+
def circuit_terminations(self) -> List[Annotated["CircuitTerminationType", strawberry.lazy('circuits.graphql.types')]]:
70+
return self.circuit_terminations.all()
4871

49-
class ProviderAccountType(NetBoxObjectType):
5072

51-
class Meta:
52-
model = models.ProviderAccount
53-
fields = '__all__'
54-
filterset_class = filtersets.ProviderAccountFilterSet
73+
@strawberry_django.type(
74+
models.CircuitTermination,
75+
fields='__all__',
76+
filters=CircuitTerminationFilter
77+
)
78+
class CircuitTerminationType(CustomFieldsMixin, TagsMixin, CabledObjectMixin, ObjectType):
79+
circuit: Annotated["CircuitType", strawberry.lazy('circuits.graphql.types')]
80+
provider_network: Annotated["ProviderNetworkType", strawberry.lazy('circuits.graphql.types')] | None
81+
site: Annotated["SiteType", strawberry.lazy('dcim.graphql.types')] | None
5582

5683

57-
class ProviderNetworkType(NetBoxObjectType):
84+
@strawberry_django.type(
85+
models.CircuitType,
86+
fields='__all__',
87+
filters=CircuitTypeFilter
88+
)
89+
class CircuitTypeType(OrganizationalObjectType):
90+
color: str
91+
92+
@strawberry_django.field
93+
def circuits(self) -> List[Annotated["CircuitType", strawberry.lazy('circuits.graphql.types')]]:
94+
return self.circuits.all()
95+
5896

59-
class Meta:
60-
model = models.ProviderNetwork
61-
fields = '__all__'
62-
filterset_class = filtersets.ProviderNetworkFilterSet
97+
@strawberry_django.type(
98+
models.Circuit,
99+
fields='__all__',
100+
filters=CircuitFilter
101+
)
102+
class CircuitType(NetBoxObjectType, ContactsMixin):
103+
provider: ProviderType
104+
provider_account: ProviderAccountType | None
105+
termination_a: CircuitTerminationType | None
106+
termination_z: CircuitTerminationType | None
107+
type: CircuitTypeType
108+
tenant: TenantType | None
109+
110+
@strawberry_django.field
111+
def terminations(self) -> List[CircuitTerminationType]:
112+
return self.terminations.all()

netbox/core/graphql/filters.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import strawberry_django
2+
3+
from core import filtersets, models
4+
from netbox.graphql.filter_mixins import autotype_decorator, BaseFilterMixin
5+
6+
__all__ = (
7+
'DataFileFilter',
8+
'DataSourceFilter',
9+
)
10+
11+
12+
@strawberry_django.filter(models.DataFile, lookups=True)
13+
@autotype_decorator(filtersets.DataFileFilterSet)
14+
class DataFileFilter(BaseFilterMixin):
15+
pass
16+
17+
18+
@strawberry_django.filter(models.DataSource, lookups=True)
19+
@autotype_decorator(filtersets.DataSourceFilterSet)
20+
class DataSourceFilter(BaseFilterMixin):
21+
pass

0 commit comments

Comments
 (0)