Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions netbox/circuits/graphql/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import strawberry
import strawberry_django
from strawberry.scalars import ID
from strawberry_django import FilterLookup, DateFilterLookup
from strawberry_django import BaseFilterLookup, FilterLookup, DateFilterLookup

from circuits import models
from core.graphql.filter_mixins import BaseObjectTypeFilterMixin, ChangeLogFilterMixin
Expand Down Expand Up @@ -52,7 +52,9 @@ class CircuitTerminationFilter(
circuit: Annotated['CircuitFilter', strawberry.lazy('circuits.graphql.filters')] | None = (
strawberry_django.filter_field()
)
term_side: Annotated['CircuitTerminationSideEnum', strawberry.lazy('circuits.graphql.enums')] | None = (
term_side: (
BaseFilterLookup[Annotated['CircuitTerminationSideEnum', strawberry.lazy('circuits.graphql.enums')]] | None
) = (
strawberry_django.filter_field()
)
termination_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = (
Expand Down Expand Up @@ -108,7 +110,7 @@ class CircuitFilter(
strawberry_django.filter_field()
)
type_id: ID | None = strawberry_django.filter_field()
status: Annotated['CircuitStatusEnum', strawberry.lazy('circuits.graphql.enums')] | None = (
status: BaseFilterLookup[Annotated['CircuitStatusEnum', strawberry.lazy('circuits.graphql.enums')]] | None = (
strawberry_django.filter_field()
)
install_date: DateFilterLookup[date] | None = strawberry_django.filter_field()
Expand Down Expand Up @@ -143,7 +145,7 @@ class CircuitGroupAssignmentFilter(
strawberry_django.filter_field()
)
group_id: ID | None = strawberry_django.filter_field()
priority: Annotated['CircuitPriorityEnum', strawberry.lazy('circuits.graphql.enums')] | None = (
priority: BaseFilterLookup[Annotated['CircuitPriorityEnum', strawberry.lazy('circuits.graphql.enums')]] | None = (
strawberry_django.filter_field()
)

Expand Down Expand Up @@ -198,7 +200,7 @@ class VirtualCircuitFilter(TenancyFilterMixin, PrimaryModelFilterMixin):
strawberry_django.filter_field()
)
type_id: ID | None = strawberry_django.filter_field()
status: Annotated['CircuitStatusEnum', strawberry.lazy('circuits.graphql.enums')] | None = (
status: BaseFilterLookup[Annotated['CircuitStatusEnum', strawberry.lazy('circuits.graphql.enums')]] | None = (
strawberry_django.filter_field()
)
group_assignments: Annotated['CircuitGroupAssignmentFilter', strawberry.lazy('circuits.graphql.filters')] | None = (
Expand All @@ -214,7 +216,11 @@ class VirtualCircuitTerminationFilter(
strawberry_django.filter_field()
)
virtual_circuit_id: ID | None = strawberry_django.filter_field()
role: Annotated['VirtualCircuitTerminationRoleEnum', strawberry.lazy('circuits.graphql.enums')] | None = (
role: (
BaseFilterLookup[
Annotated['VirtualCircuitTerminationRoleEnum', strawberry.lazy('circuits.graphql.enums')]
] | None
) = (
strawberry_django.filter_field()
)
interface: Annotated['InterfaceFilter', strawberry.lazy('dcim.graphql.filters')] | None = (
Expand Down
6 changes: 3 additions & 3 deletions netbox/core/graphql/filter_mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import strawberry
import strawberry_django
from strawberry import ID
from strawberry_django import DatetimeFilterLookup
from strawberry_django import FilterLookup, DatetimeFilterLookup

if TYPE_CHECKING:
from .filters import *
Expand All @@ -23,12 +23,12 @@ class BaseFilterMixin: ...

@dataclass
class BaseObjectTypeFilterMixin(BaseFilterMixin):
id: ID | None = strawberry.UNSET
id: FilterLookup[ID] | None = strawberry_django.filter_field()


@dataclass
class ChangeLogFilterMixin(BaseFilterMixin):
id: ID | None = strawberry.UNSET
id: FilterLookup[ID] | None = strawberry_django.filter_field()
changelog: Annotated['ObjectChangeFilter', strawberry.lazy('core.graphql.filters')] | None = (
strawberry_django.filter_field()
)
Expand Down
156 changes: 97 additions & 59 deletions netbox/dcim/graphql/filters.py

Large diffs are not rendered by default.

36 changes: 25 additions & 11 deletions netbox/extras/graphql/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import strawberry
import strawberry_django
from strawberry.scalars import ID
from strawberry_django import FilterLookup
from strawberry_django import BaseFilterLookup, FilterLookup

from core.graphql.filter_mixins import BaseObjectTypeFilterMixin, ChangeLogFilterMixin
from extras import models
Expand Down Expand Up @@ -121,7 +121,7 @@ class ConfigTemplateFilter(BaseObjectTypeFilterMixin, SyncedDataFilterMixin, Cha

@strawberry_django.filter_type(models.CustomField, lookups=True)
class CustomFieldFilter(BaseObjectTypeFilterMixin, ChangeLogFilterMixin):
type: Annotated['CustomFieldTypeEnum', strawberry.lazy('extras.graphql.enums')] | None = (
type: BaseFilterLookup[Annotated['CustomFieldTypeEnum', strawberry.lazy('extras.graphql.enums')]] | None = (
strawberry_django.filter_field()
)
object_types: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = (
Expand All @@ -139,7 +139,9 @@ class CustomFieldFilter(BaseObjectTypeFilterMixin, ChangeLogFilterMixin):
search_weight: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
strawberry_django.filter_field()
)
filter_logic: Annotated['CustomFieldFilterLogicEnum', strawberry.lazy('extras.graphql.enums')] | None = (
filter_logic: (
BaseFilterLookup[Annotated['CustomFieldFilterLogicEnum', strawberry.lazy('extras.graphql.enums')]] | None
) = (
strawberry_django.filter_field()
)
default: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
Expand All @@ -162,10 +164,14 @@ class CustomFieldFilter(BaseObjectTypeFilterMixin, ChangeLogFilterMixin):
strawberry_django.filter_field()
)
choice_set_id: ID | None = strawberry_django.filter_field()
ui_visible: Annotated['CustomFieldUIVisibleEnum', strawberry.lazy('extras.graphql.enums')] | None = (
ui_visible: (
BaseFilterLookup[Annotated['CustomFieldUIVisibleEnum', strawberry.lazy('extras.graphql.enums')]] | None
) = (
strawberry_django.filter_field()
)
ui_editable: Annotated['CustomFieldUIEditableEnum', strawberry.lazy('extras.graphql.enums')] | None = (
ui_editable: (
BaseFilterLookup[Annotated['CustomFieldUIEditableEnum', strawberry.lazy('extras.graphql.enums')]] | None
) = (
strawberry_django.filter_field()
)
is_cloneable: FilterLookup[bool] | None = strawberry_django.filter_field()
Expand All @@ -176,7 +182,9 @@ class CustomFieldFilter(BaseObjectTypeFilterMixin, ChangeLogFilterMixin):
class CustomFieldChoiceSetFilter(BaseObjectTypeFilterMixin, ChangeLogFilterMixin):
name: FilterLookup[str] | None = strawberry_django.filter_field()
description: FilterLookup[str] | None = strawberry_django.filter_field()
base_choices: Annotated['CustomFieldChoiceSetBaseEnum', strawberry.lazy('extras.graphql.enums')] | None = (
base_choices: (
BaseFilterLookup[Annotated['CustomFieldChoiceSetBaseEnum', strawberry.lazy('extras.graphql.enums')]] | None
) = (
strawberry_django.filter_field()
)
extra_choices: Annotated['StringArrayLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
Expand All @@ -195,7 +203,9 @@ class CustomLinkFilter(BaseObjectTypeFilterMixin, ChangeLogFilterMixin):
strawberry_django.filter_field()
)
group_name: FilterLookup[str] | None = strawberry_django.filter_field()
button_class: Annotated['CustomLinkButtonClassEnum', strawberry.lazy('extras.graphql.enums')] | None = (
button_class: (
BaseFilterLookup[Annotated['CustomLinkButtonClassEnum', strawberry.lazy('extras.graphql.enums')]] | None
) = (
strawberry_django.filter_field()
)
new_window: FilterLookup[bool] | None = strawberry_django.filter_field()
Expand Down Expand Up @@ -240,7 +250,7 @@ class JournalEntryFilter(BaseObjectTypeFilterMixin, CustomFieldsFilterMixin, Tag
created_by: Annotated['UserFilter', strawberry.lazy('users.graphql.filters')] | None = (
strawberry_django.filter_field()
)
kind: Annotated['JournalEntryKindEnum', strawberry.lazy('extras.graphql.enums')] | None = (
kind: BaseFilterLookup[Annotated['JournalEntryKindEnum', strawberry.lazy('extras.graphql.enums')]] | None = (
strawberry_django.filter_field()
)
comments: FilterLookup[str] | None = strawberry_django.filter_field()
Expand Down Expand Up @@ -286,7 +296,9 @@ class TableConfigFilter(BaseObjectTypeFilterMixin, ChangeLogFilterMixin):

@strawberry_django.filter_type(models.Tag, lookups=True)
class TagFilter(BaseObjectTypeFilterMixin, ChangeLogFilterMixin, TagBaseFilterMixin):
color: Annotated['ColorEnum', strawberry.lazy('netbox.graphql.enums')] | None = strawberry_django.filter_field()
color: BaseFilterLookup[Annotated['ColorEnum', strawberry.lazy('netbox.graphql.enums')]] | None = (
strawberry_django.filter_field()
)
description: FilterLookup[str] | None = strawberry_django.filter_field()


Expand All @@ -295,7 +307,9 @@ class WebhookFilter(BaseObjectTypeFilterMixin, CustomFieldsFilterMixin, TagsFilt
name: FilterLookup[str] | None = strawberry_django.filter_field()
description: FilterLookup[str] | None = strawberry_django.filter_field()
payload_url: FilterLookup[str] | None = strawberry_django.filter_field()
http_method: Annotated['WebhookHttpMethodEnum', strawberry.lazy('extras.graphql.enums')] | None = (
http_method: (
BaseFilterLookup[Annotated['WebhookHttpMethodEnum', strawberry.lazy('extras.graphql.enums')]] | None
) = (
strawberry_django.filter_field()
)
http_content_type: FilterLookup[str] | None = strawberry_django.filter_field()
Expand All @@ -320,7 +334,7 @@ class EventRuleFilter(BaseObjectTypeFilterMixin, CustomFieldsFilterMixin, TagsFi
conditions: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
strawberry_django.filter_field()
)
action_type: Annotated['EventRuleActionEnum', strawberry.lazy('extras.graphql.enums')] | None = (
action_type: BaseFilterLookup[Annotated['EventRuleActionEnum', strawberry.lazy('extras.graphql.enums')]] | None = (
strawberry_django.filter_field()
)
action_object_type: FilterLookup[str] | None = strawberry_django.filter_field()
Expand Down
20 changes: 11 additions & 9 deletions netbox/ipam/graphql/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from django.db.models import Q
from netaddr.core import AddrFormatError
from strawberry.scalars import ID
from strawberry_django import FilterLookup, DateFilterLookup
from strawberry_django import BaseFilterLookup, FilterLookup, DateFilterLookup

from core.graphql.filter_mixins import BaseObjectTypeFilterMixin, ChangeLogFilterMixin
from dcim.graphql.filter_mixins import ScopedFilterMixin
Expand Down Expand Up @@ -116,10 +116,10 @@ class FHRPGroupFilter(PrimaryModelFilterMixin):
strawberry_django.filter_field()
)
name: FilterLookup[str] | None = strawberry_django.filter_field()
protocol: Annotated['FHRPGroupProtocolEnum', strawberry.lazy('ipam.graphql.enums')] | None = (
protocol: BaseFilterLookup[Annotated['FHRPGroupProtocolEnum', strawberry.lazy('ipam.graphql.enums')]] | None = (
strawberry_django.filter_field()
)
auth_type: Annotated['FHRPGroupAuthTypeEnum', strawberry.lazy('ipam.graphql.enums')] | None = (
auth_type: BaseFilterLookup[Annotated['FHRPGroupAuthTypeEnum', strawberry.lazy('ipam.graphql.enums')]] | None = (
strawberry_django.filter_field()
)
auth_key: FilterLookup[str] | None = strawberry_django.filter_field()
Expand Down Expand Up @@ -172,10 +172,10 @@ class IPAddressFilter(ContactFilterMixin, TenancyFilterMixin, PrimaryModelFilter
address: FilterLookup[str] | None = strawberry_django.filter_field()
vrf: Annotated['VRFFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field()
vrf_id: ID | None = strawberry_django.filter_field()
status: Annotated['IPAddressStatusEnum', strawberry.lazy('ipam.graphql.enums')] | None = (
status: BaseFilterLookup[Annotated['IPAddressStatusEnum', strawberry.lazy('ipam.graphql.enums')]] | None = (
strawberry_django.filter_field()
)
role: Annotated['IPAddressRoleEnum', strawberry.lazy('ipam.graphql.enums')] | None = (
role: BaseFilterLookup[Annotated['IPAddressRoleEnum', strawberry.lazy('ipam.graphql.enums')]] | None = (
strawberry_django.filter_field()
)
assigned_object_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = (
Expand Down Expand Up @@ -227,7 +227,7 @@ class IPRangeFilter(ContactFilterMixin, TenancyFilterMixin, PrimaryModelFilterMi
)
vrf: Annotated['VRFFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field()
vrf_id: ID | None = strawberry_django.filter_field()
status: Annotated['IPRangeStatusEnum', strawberry.lazy('ipam.graphql.enums')] | None = (
status: BaseFilterLookup[Annotated['IPRangeStatusEnum', strawberry.lazy('ipam.graphql.enums')]] | None = (
strawberry_django.filter_field()
)
role: Annotated['RoleFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field()
Expand Down Expand Up @@ -279,7 +279,7 @@ class PrefixFilter(ContactFilterMixin, ScopedFilterMixin, TenancyFilterMixin, Pr
vrf_id: ID | None = strawberry_django.filter_field()
vlan: Annotated['VLANFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field()
vlan_id: ID | None = strawberry_django.filter_field()
status: Annotated['PrefixStatusEnum', strawberry.lazy('ipam.graphql.enums')] | None = (
status: BaseFilterLookup[Annotated['PrefixStatusEnum', strawberry.lazy('ipam.graphql.enums')]] | None = (
strawberry_django.filter_field()
)
role: Annotated['RoleFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field()
Expand Down Expand Up @@ -367,7 +367,9 @@ class VLANFilter(TenancyFilterMixin, PrimaryModelFilterMixin):
strawberry_django.filter_field()
)
name: FilterLookup[str] | None = strawberry_django.filter_field()
status: Annotated['VLANStatusEnum', strawberry.lazy('ipam.graphql.enums')] | None = strawberry_django.filter_field()
status: BaseFilterLookup[Annotated['VLANStatusEnum', strawberry.lazy('ipam.graphql.enums')]] | None = (
strawberry_django.filter_field()
)
role: Annotated['RoleFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field()
role_id: ID | None = strawberry_django.filter_field()
qinq_svlan: Annotated['VLANFilter', strawberry.lazy('ipam.graphql.filters')] | None = (
Expand All @@ -377,7 +379,7 @@ class VLANFilter(TenancyFilterMixin, PrimaryModelFilterMixin):
qinq_cvlans: Annotated['VLANFilter', strawberry.lazy('ipam.graphql.filters')] | None = (
strawberry_django.filter_field()
)
qinq_role: Annotated['VLANQinQRoleEnum', strawberry.lazy('ipam.graphql.enums')] | None = (
qinq_role: BaseFilterLookup[Annotated['VLANQinQRoleEnum', strawberry.lazy('ipam.graphql.enums')]] | None = (
strawberry_django.filter_field()
)
l2vpn_terminations: Annotated['L2VPNFilter', strawberry.lazy('vpn.graphql.filters')] | None = (
Expand Down
47 changes: 3 additions & 44 deletions netbox/netbox/graphql/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,9 @@
from vpn.graphql.schema import VPNQuery
from wireless.graphql.schema import WirelessQuery

__all__ = (
'Query',
'QueryV1',
'QueryV2',
'schema_v1',
'schema_v2',
)


@strawberry.type
class QueryV1(
class Query(
UsersQuery,
CircuitsQuery,
CoreQuery,
Expand All @@ -39,44 +31,11 @@ class QueryV1(
WirelessQuery,
*registry['plugins']['graphql_schemas'], # Append plugin schemas
):
"""Query class for GraphQL API v1"""
pass


@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,
schema = strawberry.Schema(
query=Query,
config=StrawberryConfig(auto_camel_case=False),
extensions=[
DjangoOptimizerExtension(prefetch_custom_queryset=True),
Expand Down
16 changes: 0 additions & 16 deletions netbox/netbox/graphql/utils.py

This file was deleted.

20 changes: 17 additions & 3 deletions netbox/netbox/tests/test_graphql.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,25 @@ def test_graphql_filter_objects(self):
self.assertEqual(len(data['data']['location_list']), 1)
self.assertIsNotNone(data['data']['location_list'][0]['site'])

# Test OR logic
# Test OR and exact logic
query = """{
location_list( filters: {
status: STATUS_PLANNED,
OR: {status: STATUS_STAGING}
status: {exact: STATUS_PLANNED},
OR: {status: {exact: STATUS_STAGING}}
}) {
id site {id}
}
}"""
response = self.client.post(url, data={'query': query}, format="json", **self.header)
self.assertHttpStatus(response, status.HTTP_200_OK)
data = json.loads(response.content)
self.assertNotIn('errors', data)
self.assertEqual(len(data['data']['location_list']), 2)

# Test in_list logic
query = """{
location_list( filters: {
status: {in_list: [STATUS_PLANNED, STATUS_STAGING]}
}) {
id site {id}
}
Expand Down
9 changes: 3 additions & 6 deletions netbox/netbox/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@

from account.views import LoginView, LogoutView
from netbox.api.views import APIRootView, StatusView
from netbox.graphql.schema import schema_v1, schema_v2
from netbox.graphql.utils import get_default_schema
from netbox.graphql.schema import 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
Expand Down Expand Up @@ -66,10 +65,8 @@
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 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'),
# GraphQL
path('graphql/', NetBoxGraphQLView.as_view(schema=schema), name='graphql'),

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