From cca556c6f95f65b7d61624c91461f09f8d485b1d Mon Sep 17 00:00:00 2001 From: Jeremy Sanders <23668453+jeremypng@users.noreply.github.com> Date: Tue, 7 Jan 2025 13:58:43 -0600 Subject: [PATCH 01/13] Closes #7598: GraphQL Filter Redesign --- netbox/circuits/graphql/enums.py | 86 ++ netbox/circuits/graphql/filter_mixins.py | 17 + netbox/circuits/graphql/filters.py | 193 +++- netbox/core/graphql/filter_lookups.py | 218 +++++ netbox/core/graphql/filter_mixins.py | 31 + netbox/core/graphql/filters.py | 83 +- netbox/core/graphql/mixins.py | 5 +- netbox/core/graphql/types.py | 8 +- netbox/dcim/graphql/enums.py | 857 ++++++++++++++++++ netbox/dcim/graphql/filter_mixins.py | 138 +++ netbox/dcim/graphql/filters.py | 800 +++++++++++++--- netbox/extras/graphql/enums.py | 190 ++++ netbox/extras/graphql/filter_mixins.py | 53 ++ netbox/extras/graphql/filters.py | 283 +++++- netbox/ipam/graphql/enums.py | 132 +++ netbox/ipam/graphql/filter_mixins.py | 22 + netbox/ipam/graphql/filters.py | 339 +++++-- netbox/netbox/graphql/enums.py | 120 +++ netbox/netbox/graphql/filter_mixins.py | 313 +++---- netbox/netbox/graphql/schema.py | 3 +- netbox/netbox/settings.py | 2 +- netbox/netbox/tests/test_graphql.py | 5 +- netbox/tenancy/graphql/filter_mixins.py | 33 + netbox/tenancy/graphql/filters.py | 150 ++- netbox/tenancy/graphql/types.py | 120 ++- netbox/users/graphql/filters.py | 46 +- netbox/virtualization/graphql/enums.py | 33 + .../virtualization/graphql/filter_mixins.py | 23 + netbox/virtualization/graphql/filters.py | 151 ++- netbox/vpn/graphql/enums.py | 161 ++++ netbox/vpn/graphql/filters.py | 180 +++- netbox/wireless/graphql/enums.py | 249 +++++ netbox/wireless/graphql/filter_mixins.py | 24 + netbox/wireless/graphql/filters.py | 67 +- 34 files changed, 4501 insertions(+), 634 deletions(-) create mode 100644 netbox/circuits/graphql/enums.py create mode 100644 netbox/circuits/graphql/filter_mixins.py create mode 100644 netbox/core/graphql/filter_lookups.py create mode 100644 netbox/core/graphql/filter_mixins.py create mode 100644 netbox/dcim/graphql/enums.py create mode 100644 netbox/dcim/graphql/filter_mixins.py create mode 100644 netbox/extras/graphql/enums.py create mode 100644 netbox/extras/graphql/filter_mixins.py create mode 100644 netbox/ipam/graphql/enums.py create mode 100644 netbox/ipam/graphql/filter_mixins.py create mode 100644 netbox/netbox/graphql/enums.py create mode 100644 netbox/tenancy/graphql/filter_mixins.py create mode 100644 netbox/virtualization/graphql/enums.py create mode 100644 netbox/virtualization/graphql/filter_mixins.py create mode 100644 netbox/vpn/graphql/enums.py create mode 100644 netbox/wireless/graphql/enums.py create mode 100644 netbox/wireless/graphql/filter_mixins.py diff --git a/netbox/circuits/graphql/enums.py b/netbox/circuits/graphql/enums.py new file mode 100644 index 00000000000..9fd3b8b0918 --- /dev/null +++ b/netbox/circuits/graphql/enums.py @@ -0,0 +1,86 @@ +from enum import Enum +import strawberry + +__all__ = [ + 'CircuitStatusEnum', + 'CircuitCommitRateEnum', + 'CircuitTerminationSideEnum', + 'CircuitTerminationPortSpeedEnum', + 'CircuitPriorityEnum', + 'VirtualCircuitTerminationRoleEnum', +] + +# +# Circuits +# + + +@strawberry.enum +class CircuitStatusEnum(Enum): + STATUS_DEPROVISIONING = 'deprovisioning' + STATUS_ACTIVE = 'active' + STATUS_PLANNED = 'planned' + STATUS_PROVISIONING = 'provisioning' + STATUS_OFFLINE = 'offline' + STATUS_DECOMMISSIONED = 'decommissioned' + + +@strawberry.enum +class CircuitCommitRateEnum(Enum): + TEN_MBPS = 10000 + HUNDRED_MBPS = 100000 + ONE_GBPS = 1000000 + TEN_GBPS = 10000000 + TWENTY_FIVE_GBPS = 25000000 + FORTY_GBPS = 40000000 + HUNDRED_GBPS = 100000000 + TWO_HUNDRED_GBPS = 200000000 + FOUR_HUNDRED_GBPS = 400000000 + T1 = 1544 + E1 = 2048 + + +# +# CircuitTerminations +# + + +@strawberry.enum +class CircuitTerminationSideEnum(Enum): + SIDE_A = 'A' + SIDE_Z = 'Z' + + +@strawberry.enum +class CircuitTerminationPortSpeedEnum(Enum): + TEN_MBPS = 10000 + HUNDRED_MBPS = 100000 + ONE_GBPS = 1000000 + TEN_GBPS = 10000000 + TWENTY_FIVE_GBPS = 25000000 + FORTY_GBPS = 40000000 + HUNDRED_GBPS = 100000000 + TWO_HUNDRED_GBPS = 200000000 + FOUR_HUNDRED_GBPS = 400000000 + T1 = 1544 + E1 = 2048 + + +@strawberry.enum +class CircuitPriorityEnum(Enum): + PRIORITY_PRIMARY = 'primary' + PRIORITY_SECONDARY = 'secondary' + PRIORITY_TERTIARY = 'tertiary' + PRIORITY_INACTIVE = 'inactive' + + +# +# Virtual circuits +# + + +@strawberry.enum +class VirtualCircuitTerminationRoleEnum(Enum): + ROLE_PEER = 'peer' + ROLE_HUB = 'hub' + ROLE_SPOKE = 'spoke' diff --git a/netbox/circuits/graphql/filter_mixins.py b/netbox/circuits/graphql/filter_mixins.py new file mode 100644 index 00000000000..0e70fbec4f4 --- /dev/null +++ b/netbox/circuits/graphql/filter_mixins.py @@ -0,0 +1,17 @@ +from dataclasses import dataclass +from typing import Annotated, TYPE_CHECKING +import strawberry +import strawberry_django +from netbox.graphql.filter_mixins import OrganizationalModelFilterMixin + +if TYPE_CHECKING: + from .filters import * + from core.graphql.filter_lookups import * + from netbox.graphql.enums import * + +__all__ = ['BaseCircuitTypeFilterMixin'] + + +@dataclass +class BaseCircuitTypeFilterMixin(OrganizationalModelFilterMixin): + color: Annotated['ColorEnum', strawberry.lazy('netbox.graphql.enums')] | None = strawberry_django.filter_field() diff --git a/netbox/circuits/graphql/filters.py b/netbox/circuits/graphql/filters.py index 7d066f42860..420367f6e62 100644 --- a/netbox/circuits/graphql/filters.py +++ b/netbox/circuits/graphql/filters.py @@ -1,7 +1,33 @@ +from datetime import date +from typing import Annotated, TYPE_CHECKING +import strawberry +from strawberry.scalars import ID import strawberry_django - -from circuits import filtersets, models -from netbox.graphql.filter_mixins import autotype_decorator, BaseFilterMixin +from strawberry_django import FilterLookup, DateFilterLookup +from extras.graphql.filter_mixins import * +from netbox.graphql.filter_mixins import * +from core.graphql.filter_mixins import * +from tenancy.graphql.filter_mixins import * +from dcim.graphql.filter_mixins import * +from .filter_mixins import * + +from circuits import models + +if TYPE_CHECKING: + from .enums import * + from netbox.graphql.enums import * + from wireless.graphql.enums import * + from core.graphql.filter_lookups import * + from core.graphql.filters import * + from extras.graphql.filters import * + from circuits.graphql.filters import * + from dcim.graphql.filters import * + from ipam.graphql.filters import * + from tenancy.graphql.filters import * + from wireless.graphql.filters import * + from users.graphql.filters import * + from virtualization.graphql.filters import * + from vpn.graphql.filters import * __all__ = ( 'CircuitFilter', @@ -19,66 +45,165 @@ @strawberry_django.filter(models.CircuitTermination, lookups=True) -@autotype_decorator(filtersets.CircuitTerminationFilterSet) -class CircuitTerminationFilter(BaseFilterMixin): - pass +class CircuitTerminationFilter( + BaseObjectTypeFilterMixin, + CustomFieldsFilterMixin, + TagsFilterMixin, + ChangeLogFilterMixin, + CabledObjectModelFilterMixin, +): + circuit: Annotated['CircuitFilter', strawberry.lazy('circuits.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + term_side: Annotated['CircuitTerminationSideEnum', strawberry.lazy('circuits.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + termination_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + termination_id: ID | None = strawberry_django.filter_field() + port_speed: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + upstream_speed: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + xconnect_id: FilterLookup[str] | None = strawberry_django.filter_field() + pp_info: FilterLookup[str] | None = strawberry_django.filter_field() + description: FilterLookup[str] | None = strawberry_django.filter_field() @strawberry_django.filter(models.Circuit, lookups=True) -@autotype_decorator(filtersets.CircuitFilterSet) -class CircuitFilter(BaseFilterMixin): - pass +class CircuitFilter(ContactFilterMixin, ImageAttachmentFilterMixin, DistanceFilterMixin, PrimaryModelFilterMixin): + cid: FilterLookup[str] | None = strawberry_django.filter_field() + provider: Annotated['ProviderFilter', strawberry.lazy('circuits.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + provider_id: ID | None = strawberry_django.filter_field() + provider_account: Annotated['ProviderAccountFilter', strawberry.lazy('circuits.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + provider_account_id: ID | None = strawberry_django.filter_field() + type: Annotated['CircuitTypeFilter', strawberry.lazy('circuits.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + type_id: ID | None = strawberry_django.filter_field() + status: Annotated['CircuitStatusEnum', strawberry.lazy('circuits.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + tenant: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + tenant_id: ID | None = strawberry_django.filter_field() + install_date: DateFilterLookup[date] | None = strawberry_django.filter_field() + termination_date: DateFilterLookup[date] | None = strawberry_django.filter_field() + commit_rate: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.CircuitType, lookups=True) -@autotype_decorator(filtersets.CircuitTypeFilterSet) -class CircuitTypeFilter(BaseFilterMixin): +class CircuitTypeFilter(BaseCircuitTypeFilterMixin): pass @strawberry_django.filter(models.CircuitGroup, lookups=True) -@autotype_decorator(filtersets.CircuitGroupFilterSet) -class CircuitGroupFilter(BaseFilterMixin): - pass +class CircuitGroupFilter(OrganizationalModelFilterMixin): + tenant: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + tenant_id: ID | None = strawberry_django.filter_field() @strawberry_django.filter(models.CircuitGroupAssignment, lookups=True) -@autotype_decorator(filtersets.CircuitGroupAssignmentFilterSet) -class CircuitGroupAssignmentFilter(BaseFilterMixin): - pass +class CircuitGroupAssignmentFilter( + BaseObjectTypeFilterMixin, CustomFieldsFilterMixin, TagsFilterMixin, ChangeLogFilterMixin +): + member_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + member_type_id: ID | None = strawberry_django.filter_field() + group: Annotated['CircuitGroupFilter', strawberry.lazy('circuits.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + group_id: ID | None = strawberry_django.filter_field() + priority: Annotated['CircuitPriorityEnum', strawberry.lazy('circuits.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.Provider, lookups=True) -@autotype_decorator(filtersets.ProviderFilterSet) -class ProviderFilter(BaseFilterMixin): - pass +class ProviderFilter(ContactFilterMixin, PrimaryModelFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() + slug: FilterLookup[str] | None = strawberry_django.filter_field() + asns: Annotated['ASNFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field() @strawberry_django.filter(models.ProviderAccount, lookups=True) -@autotype_decorator(filtersets.ProviderAccountFilterSet) -class ProviderAccountFilter(BaseFilterMixin): - pass +class ProviderAccountFilter(ContactFilterMixin, PrimaryModelFilterMixin): + provider: Annotated['ProviderFilter', strawberry.lazy('circuits.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + provider_id: ID | None = strawberry_django.filter_field() + account: FilterLookup[str] | None = strawberry_django.filter_field() + name: FilterLookup[str] | None = strawberry_django.filter_field() @strawberry_django.filter(models.ProviderNetwork, lookups=True) -@autotype_decorator(filtersets.ProviderNetworkFilterSet) -class ProviderNetworkFilter(BaseFilterMixin): - pass +class ProviderNetworkFilter(PrimaryModelFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() + provider: Annotated['ProviderFilter', strawberry.lazy('circuits.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + provider_id: ID | None = strawberry_django.filter_field() + service_id: FilterLookup[str] | None = strawberry_django.filter_field() @strawberry_django.filter(models.VirtualCircuitType, lookups=True) -@autotype_decorator(filtersets.VirtualCircuitTypeFilterSet) -class VirtualCircuitTypeFilter(BaseFilterMixin): +class VirtualCircuitTypeFilter(BaseCircuitTypeFilterMixin): pass @strawberry_django.filter(models.VirtualCircuit, lookups=True) -@autotype_decorator(filtersets.VirtualCircuitFilterSet) -class VirtualCircuitFilter(BaseFilterMixin): - pass +class VirtualCircuitFilter(PrimaryModelFilterMixin): + cid: FilterLookup[str] | None = strawberry_django.filter_field() + provider_network: Annotated['ProviderNetworkFilter', strawberry.lazy('circuits.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + provider_network_id: ID | None = strawberry_django.filter_field() + provider_account: Annotated['ProviderAccountFilter', strawberry.lazy('circuits.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + provider_account_id: ID | None = strawberry_django.filter_field() + type: Annotated['VirtualCircuitTypeFilter', strawberry.lazy('circuits.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + type_id: ID | None = strawberry_django.filter_field() + status: Annotated['CircuitStatusEnum', strawberry.lazy('circuits.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + tenant: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + tenant_id: ID | None = strawberry_django.filter_field() + group_assignments: Annotated['CircuitGroupAssignmentFilter', strawberry.lazy('circuits.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.VirtualCircuitTermination, lookups=True) -@autotype_decorator(filtersets.VirtualCircuitTerminationFilterSet) -class VirtualCircuitTerminationFilter(BaseFilterMixin): - pass +class VirtualCircuitTerminationFilter( + BaseObjectTypeFilterMixin, CustomFieldsFilterMixin, TagsFilterMixin, ChangeLogFilterMixin +): + virtual_circuit: Annotated['VirtualCircuitFilter', strawberry.lazy('circuits.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + virtual_circuit_id: ID | None = strawberry_django.filter_field() + role: Annotated['VirtualCircuitTerminationRoleEnum', strawberry.lazy('circuits.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + interface: Annotated['InterfaceFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + interface_id: ID | None = strawberry_django.filter_field() + description: FilterLookup[str] | None = strawberry_django.filter_field() diff --git a/netbox/core/graphql/filter_lookups.py b/netbox/core/graphql/filter_lookups.py new file mode 100644 index 00000000000..09f470c96cb --- /dev/null +++ b/netbox/core/graphql/filter_lookups.py @@ -0,0 +1,218 @@ +from enum import Enum +from typing import TypeVar, Tuple, Generic +from django.db.models import Q, QuerySet +from django.core.exceptions import FieldDoesNotExist +from django.db.models.fields.related import ForeignKey, ManyToManyField, ManyToManyRel, ManyToOneRel +import strawberry +from strawberry import ID +from strawberry.types import Info +import strawberry_django +from strawberry_django import ( + process_filters, + FilterLookup, + RangeLookup, + ComparisonFilterLookup, + DateFilterLookup, + DatetimeFilterLookup, + TimeFilterLookup, +) + +__all__ = [ + 'JSONFilter', + 'TreeNodeFilter', + 'IntegerLookup', + 'FloatLookup', + 'ArrayLookup', + 'IntegerArrayLookup', + 'FloatArrayLookup', + 'StringArrayLookup', +] + +T = TypeVar('T') +SKIP_MSG = 'Filter will be skipped on `null` value' + + +@strawberry.input(one_of=True, description='Lookup for JSON field. Only one of the lookup fields can be set.') +class JSONLookup: + string_lookup: FilterLookup[str] | None = strawberry_django.filter_field() + int_range_lookup: RangeLookup[int] | None = strawberry_django.filter_field() + int_comparison_lookup: ComparisonFilterLookup[int] | None = strawberry_django.filter_field() + float_range_lookup: RangeLookup[float] | None = strawberry_django.filter_field() + float_comparison_lookup: ComparisonFilterLookup[float] | None = strawberry_django.filter_field() + date_lookup: DateFilterLookup[str] | None = strawberry_django.filter_field() + datetime_lookup: DatetimeFilterLookup[str] | None = strawberry_django.filter_field() + time_lookup: TimeFilterLookup[str] | None = strawberry_django.filter_field() + boolean_lookup: FilterLookup[bool] | None = strawberry_django.filter_field() + + def get_filter(self): + for field in self.__strawberry_definition__.fields: + value = getattr(self, field.name, None) + if value is not strawberry.UNSET: + return value + return None + + +@strawberry.input(one_of=True, description='Lookup for Integer fields. Only one of the lookup fields can be set.') +class IntegerLookup: + filter_lookup: FilterLookup[int] | None = strawberry_django.filter_field() + range_lookup: RangeLookup[int] | None = strawberry_django.filter_field() + comparison_lookup: ComparisonFilterLookup[int] | None = strawberry_django.filter_field() + + def get_filter(self): + for field in self.__strawberry_definition__.fields: + value = getattr(self, field.name, None) + if value is not strawberry.UNSET: + return value + return None + + @strawberry_django.filter_field + def filter(self, info: Info, queryset: QuerySet, prefix: str = '') -> Tuple[QuerySet, Q]: + filters = self.get_filter() + + if not filters: + return queryset, Q() + + return process_filters(filters=filters, queryset=queryset, info=info, prefix=prefix) + + +@strawberry.input(one_of=True, description='Lookup for Float fields. Only one of the lookup fields can be set.') +class FloatLookup: + filter_lookup: FilterLookup[float] | None = strawberry_django.filter_field() + range_lookup: RangeLookup[float] | None = strawberry_django.filter_field() + comparison_lookup: ComparisonFilterLookup[float] | None = strawberry_django.filter_field() + + def get_filter(self): + for field in self.__strawberry_definition__.fields: + value = getattr(self, field.name, None) + if value is not strawberry.UNSET: + return value + return None + + @strawberry_django.filter_field + def filter(self, info: Info, queryset: QuerySet, prefix: str = '') -> Tuple[QuerySet, Q]: + filters = self.get_filter() + + if not filters: + return queryset, Q() + + return process_filters(filters=filters, queryset=queryset, info=info, prefix=prefix) + + +@strawberry.input +class JSONFilter: + """ + Class for JSON field lookups with paths + """ + + path: str + lookup: JSONLookup + + @strawberry_django.filter_field + def filter(self, info: Info, queryset: QuerySet, prefix: str = '') -> Tuple[QuerySet, Q]: + filters = self.lookup.get_filter() + + if not filters: + return queryset, Q() + + json_path = f'{prefix}{self.path}__' + return process_filters(filters=filters, queryset=queryset, info=info, prefix=json_path) + + +@strawberry.enum +class TreeNodeMatch(Enum): + EXACT = 'exact' # Just the node itself + DESCENDANTS = 'descendants' # Node and all descendants + SELF_AND_DESCENDANTS = 'self_and_descendants' # Node and all descendants + CHILDREN = 'children' # Just immediate children + SIBLINGS = 'siblings' # Nodes with same parent + ANCESTORS = 'ancestors' # All parent nodes + PARENT = 'parent' # Just immediate parent + + +@strawberry.input +class TreeNodeFilter: + id: ID + match_type: TreeNodeMatch + + @strawberry_django.filter_field + def filter(self, info: Info, queryset: QuerySet, prefix: str = '') -> Tuple[QuerySet, Q]: + model_field_name = prefix.removesuffix('__').removesuffix('_id') + model_field = None + try: + model_field = queryset.model._meta.get_field(model_field_name) + except FieldDoesNotExist: + try: + model_field = queryset.model._meta.get_field(f'{model_field_name}s') + except FieldDoesNotExist: + return queryset, Q(pk__in=[]) + + if hasattr(model_field, 'related_model'): + related_model = model_field.related_model + else: + return queryset, Q(pk__in=[]) + + # Generate base Q filter for the related model without prefix + q_filter = generate_tree_node_q_filter(related_model, self) + + # Handle different relationship types + if isinstance(model_field, (ManyToManyField, ManyToManyRel)): + return queryset, Q(**{f'{model_field_name}__in': related_model.objects.filter(q_filter)}) + elif isinstance(model_field, ForeignKey): + return queryset, Q(**{f'{model_field_name}__{k}': v for k, v in q_filter.children}) + elif isinstance(model_field, ManyToOneRel): + return queryset, Q(**{f'{model_field_name}__in': related_model.objects.filter(q_filter)}) + else: + return queryset, Q(**{f'{model_field_name}__{k}': v for k, v in q_filter.children}) + + +def generate_tree_node_q_filter(model_class, filter_value: TreeNodeFilter) -> Q: + """ + Generate appropriate Q filter for MPTT tree filtering based on match type + """ + try: + node = model_class.objects.get(id=filter_value.id) + except model_class.DoesNotExist: + return Q(pk__in=[]) + + if filter_value.match_type == TreeNodeMatch.EXACT: + return Q(id=filter_value.id) + elif filter_value.match_type == TreeNodeMatch.DESCENDANTS: + return Q(tree_id=node.tree_id, lft__gt=node.lft, rght__lt=node.rght) + elif filter_value.match_type == TreeNodeMatch.SELF_AND_DESCENDANTS: + return Q(tree_id=node.tree_id, lft__gte=node.lft, rght__lte=node.rght) + elif filter_value.match_type == TreeNodeMatch.CHILDREN: + return Q(tree_id=node.tree_id, level=node.level + 1, lft__gt=node.lft, rght__lt=node.rght) + elif filter_value.match_type == TreeNodeMatch.SIBLINGS: + return Q(tree_id=node.tree_id, level=node.level, parent=node.parent) & ~Q(id=node.id) + elif filter_value.match_type == TreeNodeMatch.ANCESTORS: + return Q(tree_id=node.tree_id, lft__lt=node.lft, rght__gt=node.rght) + elif filter_value.match_type == TreeNodeMatch.PARENT: + return Q(id=node.parent_id) if node.parent_id else Q(pk__in=[]) + return Q() + + +@strawberry.input(one_of=True, description='Lookup for Array fields. Only one of the lookup fields can be set.') +class ArrayLookup(Generic[T]): + """ + Class for Array field lookups + """ + + contains: list[T] | None = strawberry_django.filter_field(description='Contains the value') + contained_by: list[T] | None = strawberry_django.filter_field(description='Contained by the value') + overlap: list[T] | None = strawberry_django.filter_field(description='Overlaps with the value') + length: int | None = strawberry_django.filter_field(description='Length of the array') + + +@strawberry.input(one_of=True, description='Lookup for Array fields. Only one of the lookup fields can be set.') +class IntegerArrayLookup(ArrayLookup[int]): + pass + + +@strawberry.input(one_of=True, description='Lookup for Array fields. Only one of the lookup fields can be set.') +class FloatArrayLookup(ArrayLookup[float]): + pass + + +@strawberry.input(one_of=True, description='Lookup for Array fields. Only one of the lookup fields can be set.') +class StringArrayLookup(ArrayLookup[str]): + pass diff --git a/netbox/core/graphql/filter_mixins.py b/netbox/core/graphql/filter_mixins.py new file mode 100644 index 00000000000..1748deab50a --- /dev/null +++ b/netbox/core/graphql/filter_mixins.py @@ -0,0 +1,31 @@ +from dataclasses import dataclass +from datetime import datetime +from typing import Annotated, TYPE_CHECKING +import strawberry +from strawberry import ID +import strawberry_django +from strawberry_django import DatetimeFilterLookup + + +if TYPE_CHECKING: + from .filters import * + +__all__ = ['BaseFilterMixin', 'BaseObjectTypeFilterMixin', 'ChangeLogFilterMixin'] + + +# @strawberry.input +class BaseFilterMixin: ... + + +@dataclass +class BaseObjectTypeFilterMixin(BaseFilterMixin): + id: ID | None = strawberry.UNSET + + +@dataclass +class ChangeLogFilterMixin(BaseFilterMixin): + changelog: Annotated['ObjectChangeFilter', strawberry.lazy('core.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + created: DatetimeFilterLookup[datetime] | None = strawberry_django.filter_field() + last_updated: DatetimeFilterLookup[datetime] | None = strawberry_django.filter_field() diff --git a/netbox/core/graphql/filters.py b/netbox/core/graphql/filters.py index 82da685a590..dcbc6f4f85f 100644 --- a/netbox/core/graphql/filters.py +++ b/netbox/core/graphql/filters.py @@ -1,28 +1,93 @@ +from datetime import datetime +from typing import Annotated, TYPE_CHECKING +import strawberry +from strawberry.scalars import ID import strawberry_django +from strawberry_django import ( + DatetimeFilterLookup, + FilterLookup, +) +from django.contrib.contenttypes.models import ContentType as DjangoContentType +from core.graphql.filter_mixins import BaseFilterMixin +from core.graphql.filter_lookups import JSONFilter +from netbox.graphql.filter_mixins import ( + PrimaryModelFilterMixin, +) + +from core import models + +if TYPE_CHECKING: + from core.graphql.filter_lookups import * + from users.graphql.filters import * -from core import filtersets, models -from netbox.graphql.filter_mixins import autotype_decorator, BaseFilterMixin __all__ = ( 'DataFileFilter', 'DataSourceFilter', 'ObjectChangeFilter', + 'ContentTypeFilter', ) @strawberry_django.filter(models.DataFile, lookups=True) -@autotype_decorator(filtersets.DataFileFilterSet) class DataFileFilter(BaseFilterMixin): - pass + id: ID | None = strawberry_django.filter_field() + created: DatetimeFilterLookup[datetime] | None = strawberry_django.filter_field() + last_updated: DatetimeFilterLookup[datetime] | None = strawberry_django.filter_field() + source: Annotated['DataSourceFilter', strawberry.lazy('core.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + source_id: ID | None = strawberry_django.filter_field() + path: FilterLookup[str] | None = strawberry_django.filter_field() + size: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + hash: FilterLookup[str] | None = strawberry_django.filter_field() @strawberry_django.filter(models.DataSource, lookups=True) -@autotype_decorator(filtersets.DataSourceFilterSet) -class DataSourceFilter(BaseFilterMixin): - pass +class DataSourceFilter(PrimaryModelFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() + type: FilterLookup[str] | None = strawberry_django.filter_field() + source_url: FilterLookup[str] | None = strawberry_django.filter_field() + status: FilterLookup[str] | None = strawberry_django.filter_field() + enabled: FilterLookup[bool] | None = strawberry_django.filter_field() + ignore_rules: FilterLookup[str] | None = strawberry_django.filter_field() + parameters: Annotated['JSONFilter', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + last_synced: DatetimeFilterLookup[datetime] | None = strawberry_django.filter_field() + datafiles: Annotated['DataFileFilter', strawberry.lazy('core.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.ObjectChange, lookups=True) -@autotype_decorator(filtersets.ObjectChangeFilterSet) class ObjectChangeFilter(BaseFilterMixin): - pass + id: ID | None = strawberry_django.filter_field() + time: DatetimeFilterLookup[datetime] | None = strawberry_django.filter_field() + user: Annotated['UserFilter', strawberry.lazy('users.graphql.filters')] | None = strawberry_django.filter_field() + user_name: FilterLookup[str] | None = strawberry_django.filter_field() + request_id: FilterLookup[str] | None = strawberry_django.filter_field() + action: FilterLookup[str] | None = strawberry_django.filter_field() + changed_object_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + changed_object_type_id: ID | None = strawberry_django.filter_field() + changed_object_id: ID | None = strawberry_django.filter_field() + related_object_type_id: ID | None = strawberry_django.filter_field() + related_object_id: ID | None = strawberry_django.filter_field() + object_repr: FilterLookup[str] | None = strawberry_django.filter_field() + prechange_data: Annotated['JSONFilter', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + postchange_data: Annotated['JSONFilter', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + + +@strawberry_django.filter(DjangoContentType, lookups=True) +class ContentTypeFilter(BaseFilterMixin): + id: ID | None = strawberry_django.filter_field() + app_label: FilterLookup[str] | None = strawberry_django.filter_field() + model: FilterLookup[str] | None = strawberry_django.filter_field() diff --git a/netbox/core/graphql/mixins.py b/netbox/core/graphql/mixins.py index 5195b52a0d8..72191e6fd64 100644 --- a/netbox/core/graphql/mixins.py +++ b/netbox/core/graphql/mixins.py @@ -1,4 +1,4 @@ -from typing import Annotated, List +from typing import Annotated, List, TYPE_CHECKING import strawberry import strawberry_django @@ -6,6 +6,9 @@ from core.models import ObjectChange +if TYPE_CHECKING: + from netbox.core.graphql.types import ObjectChangeType + __all__ = ( 'ChangelogMixin', ) diff --git a/netbox/core/graphql/types.py b/netbox/core/graphql/types.py index 09385d7c124..fe28bdc4d07 100644 --- a/netbox/core/graphql/types.py +++ b/netbox/core/graphql/types.py @@ -2,7 +2,7 @@ import strawberry import strawberry_django - +from django.contrib.contenttypes.models import ContentType as DjangoContentType from core import models from netbox.graphql.types import BaseObjectType, NetBoxObjectType from .filters import * @@ -11,6 +11,7 @@ 'DataFileType', 'DataSourceType', 'ObjectChangeType', + 'ContentType', ) @@ -40,3 +41,8 @@ class DataSourceType(NetBoxObjectType): ) class ObjectChangeType(BaseObjectType): pass + + +@strawberry_django.type(DjangoContentType, fields='__all__') +class ContentType: + pass diff --git a/netbox/dcim/graphql/enums.py b/netbox/dcim/graphql/enums.py new file mode 100644 index 00000000000..2e99ab5e991 --- /dev/null +++ b/netbox/dcim/graphql/enums.py @@ -0,0 +1,857 @@ +from enum import Enum +import strawberry + + +# +# Sites +# + +@strawberry.enum +class SiteStatusEnum(Enum): + STATUS_PLANNED = 'planned' + STATUS_STAGING = 'staging' + STATUS_ACTIVE = 'active' + STATUS_DECOMMISSIONING = 'decommissioning' + STATUS_RETIRED = 'retired' + + +# +# Locations +# + +@strawberry.enum +class LocationStatusEnum(Enum): + STATUS_PLANNED = 'planned' + STATUS_STAGING = 'staging' + STATUS_ACTIVE = 'active' + STATUS_DECOMMISSIONING = 'decommissioning' + STATUS_RETIRED = 'retired' + + +# +# Racks +# + +@strawberry.enum +class RackFormFactorEnum(Enum): + TYPE_2POST = '2-post-frame' + TYPE_4POST = '4-post-frame' + TYPE_CABINET = '4-post-cabinet' + TYPE_WALLFRAME = 'wall-frame' + TYPE_WALLFRAME_VERTICAL = 'wall-frame-vertical' + TYPE_WALLCABINET = 'wall-cabinet' + TYPE_WALLCABINET_VERTICAL = 'wall-cabinet-vertical' + + +@strawberry.enum +class RackWidthEnum(Enum): + + WIDTH_10IN = 10 + WIDTH_19IN = 19 + WIDTH_21IN = 21 + WIDTH_23IN = 23 + + +@strawberry.enum +class RackStatusEnum(Enum): + STATUS_RESERVED = 'reserved' + STATUS_AVAILABLE = 'available' + STATUS_PLANNED = 'planned' + STATUS_ACTIVE = 'active' + STATUS_DEPRECATED = 'deprecated' + + +@strawberry.enum +class RackDimensionUnitEnum(Enum): + UNIT_MILLIMETER = 'mm' + UNIT_INCH = 'in' + + +@strawberry.enum +class RackElevationDetailRenderEnum(Enum): + RENDER_JSON = 'json' + RENDER_SVG = 'svg' + + +@strawberry.enum +class RackAirflowEnum(Enum): + FRONT_TO_REAR = 'front-to-rear' + REAR_TO_FRONT = 'rear-to-front' + + +# +# DeviceTypes +# + +@strawberry.enum +class SubdeviceRoleEnum(Enum): + ROLE_PARENT = 'parent' + ROLE_CHILD = 'child' + + +# +# Devices +# + +@strawberry.enum +class DeviceFaceEnum(Enum): + FACE_FRONT = 'front' + FACE_REAR = 'rear' + + +@strawberry.enum +class DeviceStatusEnum(Enum): + STATUS_OFFLINE = 'offline' + STATUS_ACTIVE = 'active' + STATUS_PLANNED = 'planned' + STATUS_STAGED = 'staged' + STATUS_FAILED = 'failed' + STATUS_INVENTORY = 'inventory' + STATUS_DECOMMISSIONING = 'decommissioning' + + +@strawberry.enum +class DeviceAirflowEnum(Enum): + + AIRFLOW_FRONT_TO_REAR = 'front-to-rear' + AIRFLOW_REAR_TO_FRONT = 'rear-to-front' + AIRFLOW_LEFT_TO_RIGHT = 'left-to-right' + AIRFLOW_RIGHT_TO_LEFT = 'right-to-left' + AIRFLOW_SIDE_TO_REAR = 'side-to-rear' + AIRFLOW_REAR_TO_SIDE = 'rear-to-side' + AIRFLOW_BOTTOM_TO_TOP = 'bottom-to-top' + AIRFLOW_TOP_TO_BOTTOM = 'top-to-bottom' + AIRFLOW_PASSIVE = 'passive' + AIRFLOW_MIXED = 'mixed' + + +# +# Modules +# + +@strawberry.enum +class ModuleStatusEnum(Enum): + key = 'Module.status' + + STATUS_OFFLINE = 'offline' + STATUS_ACTIVE = 'active' + STATUS_PLANNED = 'planned' + STATUS_STAGED = 'staged' + STATUS_FAILED = 'failed' + STATUS_DECOMMISSIONING = 'decommissioning' + + +@strawberry.enum +class ModuleAirflowEnum(Enum): + + FRONT_TO_REAR = 'front-to-rear' + REAR_TO_FRONT = 'rear-to-front' + LEFT_TO_RIGHT = 'left-to-right' + RIGHT_TO_LEFT = 'right-to-left' + SIDE_TO_REAR = 'side-to-rear' + PASSIVE = 'passive' + + +# +# ConsolePorts +# + +@strawberry.enum +class ConsolePortTypeEnum(Enum): + + TYPE_DE9 = 'de-9' + TYPE_DB25 = 'db-25' + TYPE_RJ11 = 'rj-11' + TYPE_RJ12 = 'rj-12' + TYPE_RJ45 = 'rj-45' + TYPE_MINI_DIN_8 = 'mini-din-8' + TYPE_USB_A = 'usb-a' + TYPE_USB_B = 'usb-b' + TYPE_USB_C = 'usb-c' + TYPE_USB_MINI_A = 'usb-mini-a' + TYPE_USB_MINI_B = 'usb-mini-b' + TYPE_USB_MICRO_A = 'usb-micro-a' + TYPE_USB_MICRO_B = 'usb-micro-b' + TYPE_USB_MICRO_AB = 'usb-micro-ab' + TYPE_OTHER = 'other' + + +@strawberry.enum +class ConsolePortSpeedEnum(Enum): + + SPEED_1200 = 1200 + SPEED_2400 = 2400 + SPEED_4800 = 4800 + SPEED_9600 = 9600 + SPEED_19200 = 19200 + SPEED_38400 = 38400 + SPEED_57600 = 57600 + SPEED_115200 = 115200 + + +# +# PowerPorts +# + +@strawberry.enum +class PowerPortTypeEnum(Enum): + + # IEC 60320 + TYPE_IEC_C6 = 'iec-60320-c6' + TYPE_IEC_C8 = 'iec-60320-c8' + TYPE_IEC_C14 = 'iec-60320-c14' + TYPE_IEC_C16 = 'iec-60320-c16' + TYPE_IEC_C20 = 'iec-60320-c20' + TYPE_IEC_C22 = 'iec-60320-c22' + # IEC 60309 + TYPE_IEC_PNE4H = 'iec-60309-p-n-e-4h' + TYPE_IEC_PNE6H = 'iec-60309-p-n-e-6h' + TYPE_IEC_PNE9H = 'iec-60309-p-n-e-9h' + TYPE_IEC_2PE4H = 'iec-60309-2p-e-4h' + TYPE_IEC_2PE6H = 'iec-60309-2p-e-6h' + TYPE_IEC_2PE9H = 'iec-60309-2p-e-9h' + TYPE_IEC_3PE4H = 'iec-60309-3p-e-4h' + TYPE_IEC_3PE6H = 'iec-60309-3p-e-6h' + TYPE_IEC_3PE9H = 'iec-60309-3p-e-9h' + TYPE_IEC_3PNE4H = 'iec-60309-3p-n-e-4h' + TYPE_IEC_3PNE6H = 'iec-60309-3p-n-e-6h' + TYPE_IEC_3PNE9H = 'iec-60309-3p-n-e-9h' + # IEC 60906-1 + TYPE_IEC_60906_1 = 'iec-60906-1' + TYPE_NBR_14136_10A = 'nbr-14136-10a' + TYPE_NBR_14136_20A = 'nbr-14136-20a' + # NEMA non-locking + TYPE_NEMA_115P = 'nema-1-15p' + TYPE_NEMA_515P = 'nema-5-15p' + TYPE_NEMA_520P = 'nema-5-20p' + TYPE_NEMA_530P = 'nema-5-30p' + TYPE_NEMA_550P = 'nema-5-50p' + TYPE_NEMA_615P = 'nema-6-15p' + TYPE_NEMA_620P = 'nema-6-20p' + TYPE_NEMA_630P = 'nema-6-30p' + TYPE_NEMA_650P = 'nema-6-50p' + TYPE_NEMA_1030P = 'nema-10-30p' + TYPE_NEMA_1050P = 'nema-10-50p' + TYPE_NEMA_1420P = 'nema-14-20p' + TYPE_NEMA_1430P = 'nema-14-30p' + TYPE_NEMA_1450P = 'nema-14-50p' + TYPE_NEMA_1460P = 'nema-14-60p' + TYPE_NEMA_1515P = 'nema-15-15p' + TYPE_NEMA_1520P = 'nema-15-20p' + TYPE_NEMA_1530P = 'nema-15-30p' + TYPE_NEMA_1550P = 'nema-15-50p' + TYPE_NEMA_1560P = 'nema-15-60p' + # NEMA locking + TYPE_NEMA_L115P = 'nema-l1-15p' + TYPE_NEMA_L515P = 'nema-l5-15p' + TYPE_NEMA_L520P = 'nema-l5-20p' + TYPE_NEMA_L530P = 'nema-l5-30p' + TYPE_NEMA_L550P = 'nema-l5-50p' + TYPE_NEMA_L615P = 'nema-l6-15p' + TYPE_NEMA_L620P = 'nema-l6-20p' + TYPE_NEMA_L630P = 'nema-l6-30p' + TYPE_NEMA_L650P = 'nema-l6-50p' + TYPE_NEMA_L1030P = 'nema-l10-30p' + TYPE_NEMA_L1420P = 'nema-l14-20p' + TYPE_NEMA_L1430P = 'nema-l14-30p' + TYPE_NEMA_L1450P = 'nema-l14-50p' + TYPE_NEMA_L1460P = 'nema-l14-60p' + TYPE_NEMA_L1520P = 'nema-l15-20p' + TYPE_NEMA_L1530P = 'nema-l15-30p' + TYPE_NEMA_L1550P = 'nema-l15-50p' + TYPE_NEMA_L1560P = 'nema-l15-60p' + TYPE_NEMA_L2120P = 'nema-l21-20p' + TYPE_NEMA_L2130P = 'nema-l21-30p' + TYPE_NEMA_L2220P = 'nema-l22-20p' + TYPE_NEMA_L2230P = 'nema-l22-30p' + # California style + TYPE_CS6361C = 'cs6361c' + TYPE_CS6365C = 'cs6365c' + TYPE_CS8165C = 'cs8165c' + TYPE_CS8265C = 'cs8265c' + TYPE_CS8365C = 'cs8365c' + TYPE_CS8465C = 'cs8465c' + # ITA/international + TYPE_ITA_C = 'ita-c' + TYPE_ITA_E = 'ita-e' + TYPE_ITA_F = 'ita-f' + TYPE_ITA_EF = 'ita-ef' + TYPE_ITA_G = 'ita-g' + TYPE_ITA_H = 'ita-h' + TYPE_ITA_I = 'ita-i' + TYPE_ITA_J = 'ita-j' + TYPE_ITA_K = 'ita-k' + TYPE_ITA_L = 'ita-l' + TYPE_ITA_M = 'ita-m' + TYPE_ITA_N = 'ita-n' + TYPE_ITA_O = 'ita-o' + # USB + TYPE_USB_A = 'usb-a' + TYPE_USB_B = 'usb-b' + TYPE_USB_C = 'usb-c' + TYPE_USB_MINI_A = 'usb-mini-a' + TYPE_USB_MINI_B = 'usb-mini-b' + TYPE_USB_MICRO_A = 'usb-micro-a' + TYPE_USB_MICRO_B = 'usb-micro-b' + TYPE_USB_MICRO_AB = 'usb-micro-ab' + TYPE_USB_3_B = 'usb-3-b' + TYPE_USB_3_MICROB = 'usb-3-micro-b' + # Molex + TYPE_MOLEX_MICRO_FIT_1X2 = 'molex-micro-fit-1x2' + TYPE_MOLEX_MICRO_FIT_2X2 = 'molex-micro-fit-2x2' + TYPE_MOLEX_MICRO_FIT_2X4 = 'molex-micro-fit-2x4' + # Direct current (DC) + TYPE_DC = 'dc-terminal' + # Proprietary + TYPE_SAF_D_GRID = 'saf-d-grid' + TYPE_NEUTRIK_POWERCON_20A = 'neutrik-powercon-20' + TYPE_NEUTRIK_POWERCON_32A = 'neutrik-powercon-32' + TYPE_NEUTRIK_POWERCON_TRUE1 = 'neutrik-powercon-true1' + TYPE_NEUTRIK_POWERCON_TRUE1_TOP = 'neutrik-powercon-true1-top' + TYPE_UBIQUITI_SMARTPOWER = 'ubiquiti-smartpower' + # Other + TYPE_HARDWIRED = 'hardwired' + TYPE_OTHER = 'other' + + +# +# PowerOutlets +# + +@strawberry.enum +class PowerOutletTypeEnum(Enum): + + # IEC 60320 + TYPE_IEC_C5 = 'iec-60320-c5' + TYPE_IEC_C7 = 'iec-60320-c7' + TYPE_IEC_C13 = 'iec-60320-c13' + TYPE_IEC_C15 = 'iec-60320-c15' + TYPE_IEC_C19 = 'iec-60320-c19' + TYPE_IEC_C21 = 'iec-60320-c21' + # IEC 60309 + TYPE_IEC_PNE4H = 'iec-60309-p-n-e-4h' + TYPE_IEC_PNE6H = 'iec-60309-p-n-e-6h' + TYPE_IEC_PNE9H = 'iec-60309-p-n-e-9h' + TYPE_IEC_2PE4H = 'iec-60309-2p-e-4h' + TYPE_IEC_2PE6H = 'iec-60309-2p-e-6h' + TYPE_IEC_2PE9H = 'iec-60309-2p-e-9h' + TYPE_IEC_3PE4H = 'iec-60309-3p-e-4h' + TYPE_IEC_3PE6H = 'iec-60309-3p-e-6h' + TYPE_IEC_3PE9H = 'iec-60309-3p-e-9h' + TYPE_IEC_3PNE4H = 'iec-60309-3p-n-e-4h' + TYPE_IEC_3PNE6H = 'iec-60309-3p-n-e-6h' + TYPE_IEC_3PNE9H = 'iec-60309-3p-n-e-9h' + # IEC 60906-1 + TYPE_IEC_60906_1 = 'iec-60906-1' + TYPE_NBR_14136_10A = 'nbr-14136-10a' + TYPE_NBR_14136_20A = 'nbr-14136-20a' + # NEMA non-locking + TYPE_NEMA_115R = 'nema-1-15r' + TYPE_NEMA_515R = 'nema-5-15r' + TYPE_NEMA_520R = 'nema-5-20r' + TYPE_NEMA_530R = 'nema-5-30r' + TYPE_NEMA_550R = 'nema-5-50r' + TYPE_NEMA_615R = 'nema-6-15r' + TYPE_NEMA_620R = 'nema-6-20r' + TYPE_NEMA_630R = 'nema-6-30r' + TYPE_NEMA_650R = 'nema-6-50r' + TYPE_NEMA_1030R = 'nema-10-30r' + TYPE_NEMA_1050R = 'nema-10-50r' + TYPE_NEMA_1420R = 'nema-14-20r' + TYPE_NEMA_1430R = 'nema-14-30r' + TYPE_NEMA_1450R = 'nema-14-50r' + TYPE_NEMA_1460R = 'nema-14-60r' + TYPE_NEMA_1515R = 'nema-15-15r' + TYPE_NEMA_1520R = 'nema-15-20r' + TYPE_NEMA_1530R = 'nema-15-30r' + TYPE_NEMA_1550R = 'nema-15-50r' + TYPE_NEMA_1560R = 'nema-15-60r' + # NEMA locking + TYPE_NEMA_L115R = 'nema-l1-15r' + TYPE_NEMA_L515R = 'nema-l5-15r' + TYPE_NEMA_L520R = 'nema-l5-20r' + TYPE_NEMA_L530R = 'nema-l5-30r' + TYPE_NEMA_L550R = 'nema-l5-50r' + TYPE_NEMA_L615R = 'nema-l6-15r' + TYPE_NEMA_L620R = 'nema-l6-20r' + TYPE_NEMA_L630R = 'nema-l6-30r' + TYPE_NEMA_L650R = 'nema-l6-50r' + TYPE_NEMA_L1030R = 'nema-l10-30r' + TYPE_NEMA_L1420R = 'nema-l14-20r' + TYPE_NEMA_L1430R = 'nema-l14-30r' + TYPE_NEMA_L1450R = 'nema-l14-50r' + TYPE_NEMA_L1460R = 'nema-l14-60r' + TYPE_NEMA_L1520R = 'nema-l15-20r' + TYPE_NEMA_L1530R = 'nema-l15-30r' + TYPE_NEMA_L1550R = 'nema-l15-50r' + TYPE_NEMA_L1560R = 'nema-l15-60r' + TYPE_NEMA_L2120R = 'nema-l21-20r' + TYPE_NEMA_L2130R = 'nema-l21-30r' + TYPE_NEMA_L2220R = 'nema-l22-20r' + TYPE_NEMA_L2230R = 'nema-l22-30r' + # California style + TYPE_CS6360C = 'CS6360C' + TYPE_CS6364C = 'CS6364C' + TYPE_CS8164C = 'CS8164C' + TYPE_CS8264C = 'CS8264C' + TYPE_CS8364C = 'CS8364C' + TYPE_CS8464C = 'CS8464C' + # ITA/international + TYPE_ITA_E = 'ita-e' + TYPE_ITA_F = 'ita-f' + TYPE_ITA_G = 'ita-g' + TYPE_ITA_H = 'ita-h' + TYPE_ITA_I = 'ita-i' + TYPE_ITA_J = 'ita-j' + TYPE_ITA_K = 'ita-k' + TYPE_ITA_L = 'ita-l' + TYPE_ITA_M = 'ita-m' + TYPE_ITA_N = 'ita-n' + TYPE_ITA_O = 'ita-o' + TYPE_ITA_MULTISTANDARD = 'ita-multistandard' + # USB + TYPE_USB_A = 'usb-a' + TYPE_USB_MICROB = 'usb-micro-b' + TYPE_USB_C = 'usb-c' + # Molex + TYPE_MOLEX_MICRO_FIT_1X2 = 'molex-micro-fit-1x2' + TYPE_MOLEX_MICRO_FIT_2X2 = 'molex-micro-fit-2x2' + TYPE_MOLEX_MICRO_FIT_2X4 = 'molex-micro-fit-2x4' + # Direct current (DC) + TYPE_DC = 'dc-terminal' + # Proprietary + TYPE_EATON_C39 = 'eaton-c39' + TYPE_HDOT_CX = 'hdot-cx' + TYPE_SAF_D_GRID = 'saf-d-grid' + TYPE_NEUTRIK_POWERCON_20A = 'neutrik-powercon-20a' + TYPE_NEUTRIK_POWERCON_32A = 'neutrik-powercon-32a' + TYPE_NEUTRIK_POWERCON_TRUE1 = 'neutrik-powercon-true1' + TYPE_NEUTRIK_POWERCON_TRUE1_TOP = 'neutrik-powercon-true1-top' + TYPE_UBIQUITI_SMARTPOWER = 'ubiquiti-smartpower' + # Other + TYPE_HARDWIRED = 'hardwired' + TYPE_OTHER = 'other' + + +@strawberry.enum +class PowerOutletFeedLegEnum(Enum): + + FEED_LEG_A = 'A' + FEED_LEG_B = 'B' + FEED_LEG_C = 'C' + + +# +# Interfaces +# + +@strawberry.enum +class InterfaceKindEnum(Enum): + KIND_PHYSICAL = 'physical' + KIND_VIRTUAL = 'virtual' + KIND_WIRELESS = 'wireless' + + +@strawberry.enum +class InterfaceTypeEnum(Enum): + + # Virtual + TYPE_VIRTUAL = 'virtual' + TYPE_BRIDGE = 'bridge' + TYPE_LAG = 'lag' + + # Ethernet + TYPE_100ME_FX = '100base-fx' + TYPE_100ME_LFX = '100base-lfx' + TYPE_100ME_FIXED = '100base-tx' + TYPE_100ME_T1 = '100base-t1' + TYPE_100ME_SFP = '100base-x-sfp' + TYPE_1GE_FIXED = '1000base-t' + TYPE_1GE_LX_FIXED = '1000base-lx' + TYPE_1GE_TX_FIXED = '1000base-tx' + TYPE_1GE_GBIC = '1000base-x-gbic' + TYPE_1GE_SFP = '1000base-x-sfp' + TYPE_2GE_FIXED = '2.5gbase-t' + TYPE_5GE_FIXED = '5gbase-t' + TYPE_10GE_FIXED = '10gbase-t' + TYPE_10GE_CX4 = '10gbase-cx4' + TYPE_10GE_SFP_PLUS = '10gbase-x-sfpp' + TYPE_10GE_XFP = '10gbase-x-xfp' + TYPE_10GE_XENPAK = '10gbase-x-xenpak' + TYPE_10GE_X2 = '10gbase-x-x2' + TYPE_25GE_SFP28 = '25gbase-x-sfp28' + TYPE_50GE_SFP56 = '50gbase-x-sfp56' + TYPE_40GE_QSFP_PLUS = '40gbase-x-qsfpp' + TYPE_50GE_QSFP28 = '50gbase-x-sfp28' + TYPE_100GE_CFP = '100gbase-x-cfp' + TYPE_100GE_CFP2 = '100gbase-x-cfp2' + TYPE_100GE_CFP4 = '100gbase-x-cfp4' + TYPE_100GE_CXP = '100gbase-x-cxp' + TYPE_100GE_CPAK = '100gbase-x-cpak' + TYPE_100GE_DSFP = '100gbase-x-dsfp' + TYPE_100GE_SFP_DD = '100gbase-x-sfpdd' + TYPE_100GE_QSFP28 = '100gbase-x-qsfp28' + TYPE_100GE_QSFP_DD = '100gbase-x-qsfpdd' + TYPE_200GE_CFP2 = '200gbase-x-cfp2' + TYPE_200GE_QSFP56 = '200gbase-x-qsfp56' + TYPE_200GE_QSFP_DD = '200gbase-x-qsfpdd' + TYPE_400GE_CFP2 = '400gbase-x-cfp2' + TYPE_400GE_QSFP112 = '400gbase-x-qsfp112' + TYPE_400GE_QSFP_DD = '400gbase-x-qsfpdd' + TYPE_400GE_OSFP = '400gbase-x-osfp' + TYPE_400GE_OSFP_RHS = '400gbase-x-osfp-rhs' + TYPE_400GE_CDFP = '400gbase-x-cdfp' + TYPE_400GE_CFP8 = '400gbase-x-cfp8' + TYPE_800GE_QSFP_DD = '800gbase-x-qsfpdd' + TYPE_800GE_OSFP = '800gbase-x-osfp' + + # Ethernet Backplane + TYPE_1GE_KX = '1000base-kx' + TYPE_2GE_KX = '2.5gbase-kx' + TYPE_5GE_KR = '5gbase-kr' + TYPE_10GE_KR = '10gbase-kr' + TYPE_10GE_KX4 = '10gbase-kx4' + TYPE_25GE_KR = '25gbase-kr' + TYPE_40GE_KR4 = '40gbase-kr4' + TYPE_50GE_KR = '50gbase-kr' + TYPE_100GE_KP4 = '100gbase-kp4' + TYPE_100GE_KR2 = '100gbase-kr2' + TYPE_100GE_KR4 = '100gbase-kr4' + + # Wireless + TYPE_80211A = 'ieee802.11a' + TYPE_80211G = 'ieee802.11g' + TYPE_80211N = 'ieee802.11n' + TYPE_80211AC = 'ieee802.11ac' + TYPE_80211AD = 'ieee802.11ad' + TYPE_80211AX = 'ieee802.11ax' + TYPE_80211AY = 'ieee802.11ay' + TYPE_80211BE = 'ieee802.11be' + TYPE_802151 = 'ieee802.15.1' + TYPE_802154 = 'ieee802.15.4' + TYPE_OTHER_WIRELESS = 'other-wireless' + + # Cellular + TYPE_GSM = 'gsm' + TYPE_CDMA = 'cdma' + TYPE_LTE = 'lte' + TYPE_4G = '4g' + TYPE_5G = '5g' + + # SONET + TYPE_SONET_OC3 = 'sonet-oc3' + TYPE_SONET_OC12 = 'sonet-oc12' + TYPE_SONET_OC48 = 'sonet-oc48' + TYPE_SONET_OC192 = 'sonet-oc192' + TYPE_SONET_OC768 = 'sonet-oc768' + TYPE_SONET_OC1920 = 'sonet-oc1920' + TYPE_SONET_OC3840 = 'sonet-oc3840' + + # Fibrechannel + TYPE_1GFC_SFP = '1gfc-sfp' + TYPE_2GFC_SFP = '2gfc-sfp' + TYPE_4GFC_SFP = '4gfc-sfp' + TYPE_8GFC_SFP_PLUS = '8gfc-sfpp' + TYPE_16GFC_SFP_PLUS = '16gfc-sfpp' + TYPE_32GFC_SFP28 = '32gfc-sfp28' + TYPE_32GFC_SFP_PLUS = '32gfc-sfpp' + TYPE_64GFC_QSFP_PLUS = '64gfc-qsfpp' + TYPE_64GFC_SFP_DD = '64gfc-sfpdd' + TYPE_64GFC_SFP_PLUS = '64gfc-sfpp' + TYPE_128GFC_QSFP28 = '128gfc-qsfp28' + + # InfiniBand + TYPE_INFINIBAND_SDR = 'infiniband-sdr' + TYPE_INFINIBAND_DDR = 'infiniband-ddr' + TYPE_INFINIBAND_QDR = 'infiniband-qdr' + TYPE_INFINIBAND_FDR10 = 'infiniband-fdr10' + TYPE_INFINIBAND_FDR = 'infiniband-fdr' + TYPE_INFINIBAND_EDR = 'infiniband-edr' + TYPE_INFINIBAND_HDR = 'infiniband-hdr' + TYPE_INFINIBAND_NDR = 'infiniband-ndr' + TYPE_INFINIBAND_XDR = 'infiniband-xdr' + + # Serial + TYPE_T1 = 't1' + TYPE_E1 = 'e1' + TYPE_T3 = 't3' + TYPE_E3 = 'e3' + + # ATM/DSL + TYPE_XDSL = 'xdsl' + + # Coaxial + TYPE_DOCSIS = 'docsis' + + # PON + TYPE_BPON = 'bpon' + TYPE_EPON = 'epon' + TYPE_10G_EPON = '10g-epon' + TYPE_GPON = 'gpon' + TYPE_XG_PON = 'xg-pon' + TYPE_XGS_PON = 'xgs-pon' + TYPE_NG_PON2 = 'ng-pon2' + TYPE_25G_PON = '25g-pon' + TYPE_50G_PON = '50g-pon' + + # Stacking + TYPE_STACKWISE = 'cisco-stackwise' + TYPE_STACKWISE_PLUS = 'cisco-stackwise-plus' + TYPE_FLEXSTACK = 'cisco-flexstack' + TYPE_FLEXSTACK_PLUS = 'cisco-flexstack-plus' + TYPE_STACKWISE80 = 'cisco-stackwise-80' + TYPE_STACKWISE160 = 'cisco-stackwise-160' + TYPE_STACKWISE320 = 'cisco-stackwise-320' + TYPE_STACKWISE480 = 'cisco-stackwise-480' + TYPE_STACKWISE1T = 'cisco-stackwise-1t' + TYPE_JUNIPER_VCP = 'juniper-vcp' + TYPE_SUMMITSTACK = 'extreme-summitstack' + TYPE_SUMMITSTACK128 = 'extreme-summitstack-128' + TYPE_SUMMITSTACK256 = 'extreme-summitstack-256' + TYPE_SUMMITSTACK512 = 'extreme-summitstack-512' + + # Other + TYPE_OTHER = 'other' + + +@strawberry.enum +class InterfaceSpeedEnum(Enum): + SPEED_10MBPS = 10000 + SPEED_100MBPS = 100000 + SPEED_1GBPS = 1000000 + SPEED_10GBPS = 10000000 + SPEED_25GBPS = 25000000 + SPEED_40GBPS = 40000000 + SPEED_100GBPS = 100000000 + SPEED_200GBPS = 200000000 + SPEED_400GBPS = 400000000 + + +@strawberry.enum +class InterfaceDuplexEnum(Enum): + + DUPLEX_HALF = 'half' + DUPLEX_FULL = 'full' + DUPLEX_AUTO = 'auto' + + +@strawberry.enum +class InterfaceModeEnum(Enum): + + MODE_ACCESS = 'access' + MODE_TAGGED = 'tagged' + MODE_TAGGED_ALL = 'tagged-all' + MODE_Q_IN_Q = 'q-in-q' + + +@strawberry.enum +class InterfacePoEModeEnum(Enum): + + MODE_PD = 'pd' + MODE_PSE = 'pse' + + +@strawberry.enum +class InterfacePoETypeEnum(Enum): + + TYPE_1_8023AF = 'type1-ieee802.3af' + TYPE_2_8023AT = 'type2-ieee802.3at' + TYPE_3_8023BT = 'type3-ieee802.3bt' + TYPE_4_8023BT = 'type4-ieee802.3bt' + + PASSIVE_24V_2PAIR = 'passive-24v-2pair' + PASSIVE_24V_4PAIR = 'passive-24v-4pair' + PASSIVE_48V_2PAIR = 'passive-48v-2pair' + PASSIVE_48V_4PAIR = 'passive-48v-4pair' + + +# +# FrontPorts/RearPorts +# + +@strawberry.enum +class PortTypeEnum(Enum): + + TYPE_8P8C = '8p8c' + TYPE_8P6C = '8p6c' + TYPE_8P4C = '8p4c' + TYPE_8P2C = '8p2c' + TYPE_6P6C = '6p6c' + TYPE_6P4C = '6p4c' + TYPE_6P2C = '6p2c' + TYPE_4P4C = '4p4c' + TYPE_4P2C = '4p2c' + TYPE_GG45 = 'gg45' + TYPE_TERA4P = 'tera-4p' + TYPE_TERA2P = 'tera-2p' + TYPE_TERA1P = 'tera-1p' + TYPE_110_PUNCH = '110-punch' + TYPE_BNC = 'bnc' + TYPE_F = 'f' + TYPE_N = 'n' + TYPE_MRJ21 = 'mrj21' + TYPE_ST = 'st' + TYPE_SC = 'sc' + TYPE_SC_PC = 'sc-pc' + TYPE_SC_UPC = 'sc-upc' + TYPE_SC_APC = 'sc-apc' + TYPE_FC = 'fc' + TYPE_LC = 'lc' + TYPE_LC_PC = 'lc-pc' + TYPE_LC_UPC = 'lc-upc' + TYPE_LC_APC = 'lc-apc' + TYPE_MTRJ = 'mtrj' + TYPE_MPO = 'mpo' + TYPE_LSH = 'lsh' + TYPE_LSH_PC = 'lsh-pc' + TYPE_LSH_UPC = 'lsh-upc' + TYPE_LSH_APC = 'lsh-apc' + TYPE_LX5 = 'lx5' + TYPE_LX5_PC = 'lx5-pc' + TYPE_LX5_UPC = 'lx5-upc' + TYPE_LX5_APC = 'lx5-apc' + TYPE_SPLICE = 'splice' + TYPE_CS = 'cs' + TYPE_SN = 'sn' + TYPE_SMA_905 = 'sma-905' + TYPE_SMA_906 = 'sma-906' + TYPE_URM_P2 = 'urm-p2' + TYPE_URM_P4 = 'urm-p4' + TYPE_URM_P8 = 'urm-p8' + TYPE_USB_A = 'usb-a' + TYPE_USB_B = 'usb-b' + TYPE_USB_C = 'usb-c' + TYPE_USB_MINI_A = 'usb-mini-a' + TYPE_USB_MINI_B = 'usb-mini-b' + TYPE_USB_MICRO_A = 'usb-micro-a' + TYPE_USB_MICRO_B = 'usb-micro-b' + TYPE_USB_MICRO_AB = 'usb-micro-ab' + TYPE_OTHER = 'other' + + +# +# Cables/links +# + +@strawberry.enum +class CableTypeEnum(Enum): + + TYPE_CAT3 = 'cat3' + TYPE_CAT5 = 'cat5' + TYPE_CAT5E = 'cat5e' + TYPE_CAT6 = 'cat6' + TYPE_CAT6A = 'cat6a' + TYPE_CAT7 = 'cat7' + TYPE_CAT7A = 'cat7a' + TYPE_CAT8 = 'cat8' + TYPE_DAC_ACTIVE = 'dac-active' + TYPE_DAC_PASSIVE = 'dac-passive' + TYPE_MRJ21_TRUNK = 'mrj21-trunk' + TYPE_COAXIAL = 'coaxial' + TYPE_MMF = 'mmf' + TYPE_MMF_OM1 = 'mmf-om1' + TYPE_MMF_OM2 = 'mmf-om2' + TYPE_MMF_OM3 = 'mmf-om3' + TYPE_MMF_OM4 = 'mmf-om4' + TYPE_MMF_OM5 = 'mmf-om5' + TYPE_SMF = 'smf' + TYPE_SMF_OS1 = 'smf-os1' + TYPE_SMF_OS2 = 'smf-os2' + TYPE_AOC = 'aoc' + TYPE_POWER = 'power' + TYPE_USB = 'usb' + + +@strawberry.enum +class LinkStatusEnum(Enum): + + STATUS_CONNECTED = 'connected' + STATUS_PLANNED = 'planned' + STATUS_DECOMMISSIONING = 'decommissioning' + + +@strawberry.enum +class CableLengthUnitEnum(Enum): + + # Metric + UNIT_KILOMETER = 'km' + UNIT_METER = 'm' + UNIT_CENTIMETER = 'cm' + + # Imperial + UNIT_MILE = 'mi' + UNIT_FOOT = 'ft' + UNIT_INCH = 'in' + + +# +# CableTerminations +# + +@strawberry.enum +class CableEndEnum(Enum): + SIDE_A = 'A' + SIDE_B = 'B' + + +# +# PowerFeeds +# + +@strawberry.enum +class PowerFeedStatusEnum(Enum): + key = 'PowerFeed.status' + + STATUS_OFFLINE = 'offline' + STATUS_ACTIVE = 'active' + STATUS_PLANNED = 'planned' + STATUS_FAILED = 'failed' + + +@strawberry.enum +class PowerFeedTypeEnum(Enum): + + TYPE_PRIMARY = 'primary' + TYPE_REDUNDANT = 'redundant' + + +@strawberry.enum +class PowerFeedSupplyEnum(Enum): + + SUPPLY_AC = 'ac' + SUPPLY_DC = 'dc' + + +@strawberry.enum +class PowerFeedPhaseEnum(Enum): + + PHASE_SINGLE = 'single-phase' + PHASE_3PHASE = 'three-phase' + + +# +# VDC +# +@strawberry.enum +class VirtualDeviceContextStatusEnum(Enum): + key = 'VirtualDeviceContext.status' + + STATUS_ACTIVE = 'active' + STATUS_PLANNED = 'planned' + STATUS_OFFLINE = 'offline' + + +# +# InventoryItem +# + +@strawberry.enum +class InventoryItemStatusEnum(Enum): + key = 'InventoryItem.status' + + STATUS_OFFLINE = 'offline' + STATUS_ACTIVE = 'active' + STATUS_PLANNED = 'planned' + STATUS_STAGED = 'staged' + STATUS_FAILED = 'failed' + STATUS_DECOMMISSIONING = 'decommissioning' diff --git a/netbox/dcim/graphql/filter_mixins.py b/netbox/dcim/graphql/filter_mixins.py new file mode 100644 index 00000000000..cd1586134d4 --- /dev/null +++ b/netbox/dcim/graphql/filter_mixins.py @@ -0,0 +1,138 @@ +from dataclasses import dataclass +from typing import Annotated, TYPE_CHECKING +import strawberry +from strawberry import ID +import strawberry_django +from strawberry_django import FilterLookup +from core.graphql.filter_mixins import * +from netbox.graphql.filter_mixins import * +from .enums import * + +if TYPE_CHECKING: + from .filters import * + from core.graphql.filter_lookups import * + from extras.graphql.filters import * + from ipam.graphql.filters import * + +__all__ = [ + 'ComponentModelFilterMixin', + 'ModularComponentModelFilterMixin', + 'CabledObjectModelFilterMixin', + 'ComponentTemplateFilterMixin', + 'ModularComponentTemplateFilterMixin', + 'RenderConfigFilterMixin', + 'InterfaceBaseFilterMixin', + 'RackBaseFilterMixin', +] + + +@dataclass +class ComponentModelFilterMixin(NetBoxModelFilterMixin): + device: Annotated['DeviceFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() + device_id: ID | None = strawberry_django.filter_field() + name: FilterLookup[str] | None = strawberry_django.filter_field() + lable: FilterLookup[str] | None = strawberry_django.filter_field() + description: FilterLookup[str] | None = strawberry_django.filter_field() + + +@dataclass +class ModularComponentModelFilterMixin(ComponentModelFilterMixin): + module: Annotated['ModuleFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() + module_id: ID | None = strawberry_django.filter_field() + inventory_items: Annotated['InventoryItemFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + + +@dataclass +class CabledObjectModelFilterMixin(BaseFilterMixin): + cable: Annotated['CableFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() + cable_id: ID | None = strawberry_django.filter_field() + cable_end: CableEndEnum | None = strawberry_django.filter_field() + mark_connected: FilterLookup[bool] | None = strawberry_django.filter_field() + + +@dataclass +class ComponentTemplateFilterMixin(ChangeLogFilterMixin): + device_type: Annotated['DeviceTypeFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + device_type_id: ID | None = strawberry_django.filter_field() + name: FilterLookup[str] | None = strawberry_django.filter_field() + label: FilterLookup[str] | None = strawberry_django.filter_field() + description: FilterLookup[str] | None = strawberry_django.filter_field() + + +@dataclass +class ModularComponentTemplateFilterMixin(ComponentTemplateFilterMixin): + module_type: Annotated['ModuleTypeFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + + +@dataclass +class RenderConfigFilterMixin(BaseFilterMixin): + config_template: Annotated['ConfigTemplateFilter', strawberry.lazy('extras.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + config_template_id: ID | None = strawberry_django.filter_field() + + +@dataclass +class InterfaceBaseFilterMixin(BaseFilterMixin): + enabled: FilterLookup[bool] | None = strawberry_django.filter_field() + mtu: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + mode: InterfaceModeEnum | None = strawberry_django.filter_field() + parent: Annotated['InterfaceFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + parent_id: ID | None = strawberry_django.filter_field() + bridge: Annotated['InterfaceFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + bridge_id: ID | None = strawberry_django.filter_field() + untagged_vlan: Annotated['VLANFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + tagged_vlans: Annotated['VLANFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + qinq_svlan: Annotated['VLANFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + vlan_tranlation_policy: Annotated['VLANTranslationPolicyFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + primary_mac_address: Annotated['MACAddressFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + primary_mac_address_id: ID | None = strawberry_django.filter_field() + + +@dataclass +class RackBaseFilterMixin(WeightFilterMixin, PrimaryModelFilterMixin): + width: Annotated['RackWidthEnum', strawberry.lazy('dcim.graphql.enums')] | None = strawberry_django.filter_field() + u_height: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + starting_unit: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + desc_units: FilterLookup[bool] | None = strawberry_django.filter_field() + outer_width: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + outer_depth: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + outer_unit: Annotated['RackDimensionUnitEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + mounting_depth: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + max_weight: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) diff --git a/netbox/dcim/graphql/filters.py b/netbox/dcim/graphql/filters.py index 94f2c6d38a6..b9fcedfaf7c 100644 --- a/netbox/dcim/graphql/filters.py +++ b/netbox/dcim/graphql/filters.py @@ -1,7 +1,41 @@ +from typing import Annotated, TYPE_CHECKING +import strawberry +from strawberry.scalars import ID import strawberry_django +from strawberry_django import ( + FilterLookup, +) +from extras.graphql.filter_mixins import ( + ConfigContextFilterMixin, +) +from netbox.graphql.filter_mixins import ( + PrimaryModelFilterMixin, + OrganizationalModelFilterMixin, + NestedGroupModelFilterMixin, + ImageAttachmentFilterMixin, + WeightFilterMixin, +) +from .filter_mixins import * +from dcim import models +from core.graphql.filter_mixins import * +from tenancy.graphql.filter_mixins import TenancyFilterMixin, ContactFilterMixin + +if TYPE_CHECKING: + from .enums import * + from netbox.graphql.enums import * + from wireless.graphql.enums import * + from core.graphql.filter_lookups import * + from core.graphql.filters import * + from extras.graphql.filters import * + from circuits.graphql.filters import * + from dcim.graphql.filters import * + from ipam.graphql.filters import * + from tenancy.graphql.filters import * + from wireless.graphql.filters import * + from users.graphql.filters import * + from virtualization.graphql.filters import * + from vpn.graphql.filters import * -from dcim import filtersets, models -from netbox.graphql.filter_mixins import autotype_decorator, BaseFilterMixin __all__ = ( 'CableFilter', @@ -51,258 +85,766 @@ @strawberry_django.filter(models.Cable, lookups=True) -@autotype_decorator(filtersets.CableFilterSet) -class CableFilter(BaseFilterMixin): - pass +class CableFilter(PrimaryModelFilterMixin, TenancyFilterMixin): + type: Annotated['CableTypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = strawberry_django.filter_field() + status: Annotated['LinkStatusEnum', strawberry.lazy('dcim.graphql.enums')] | None = strawberry_django.filter_field() + label: FilterLookup[str] | None = strawberry_django.filter_field() + color: Annotated['ColorEnum', strawberry.lazy('netbox.graphql.enums')] | None = strawberry_django.filter_field() + length: Annotated['FloatLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + length_unit: Annotated['CableLengthUnitEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + terminations: Annotated['CableTerminationFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.CableTermination, lookups=True) -@autotype_decorator(filtersets.CableTerminationFilterSet) -class CableTerminationFilter(BaseFilterMixin): - pass +class CableTerminationFilter(ChangeLogFilterMixin): + cable: Annotated['CableFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() + cable_id: ID | None = strawberry_django.filter_field() + cable_end: Annotated['CableEndEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + termination_type: Annotated['CableTerminationFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + termination_id: ID | None = strawberry_django.filter_field() @strawberry_django.filter(models.ConsolePort, lookups=True) -@autotype_decorator(filtersets.ConsolePortFilterSet) -class ConsolePortFilter(BaseFilterMixin): - pass +class ConsolePortFilter(ModularComponentModelFilterMixin, CabledObjectModelFilterMixin): + type: Annotated['ConsolePortTypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + speed: Annotated['ConsolePortSpeedEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.ConsolePortTemplate, lookups=True) -@autotype_decorator(filtersets.ConsolePortTemplateFilterSet) -class ConsolePortTemplateFilter(BaseFilterMixin): - pass +class ConsolePortTemplateFilter(ModularComponentTemplateFilterMixin): + type: Annotated['ConsolePortTypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.ConsoleServerPort, lookups=True) -@autotype_decorator(filtersets.ConsoleServerPortFilterSet) -class ConsoleServerPortFilter(BaseFilterMixin): - pass +class ConsoleServerPortFilter(ModularComponentModelFilterMixin, CabledObjectModelFilterMixin): + type: Annotated['ConsolePortTypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + speed: Annotated['ConsolePortSpeedEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.ConsoleServerPortTemplate, lookups=True) -@autotype_decorator(filtersets.ConsoleServerPortTemplateFilterSet) -class ConsoleServerPortTemplateFilter(BaseFilterMixin): - pass +class ConsoleServerPortTemplateFilter(ModularComponentTemplateFilterMixin): + type: Annotated['ConsolePortTypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.Device, lookups=True) -@autotype_decorator(filtersets.DeviceFilterSet) -class DeviceFilter(BaseFilterMixin): - pass +class DeviceFilter( + ContactFilterMixin, + TenancyFilterMixin, + ImageAttachmentFilterMixin, + RenderConfigFilterMixin, + ConfigContextFilterMixin, + PrimaryModelFilterMixin, +): + device_type: Annotated['DeviceTypeFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + device_type_id: ID | None = strawberry_django.filter_field() + role: Annotated['DeviceRoleFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + role_id: ID | None = strawberry_django.filter_field() + platform: Annotated['PlatformFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + name: FilterLookup[str] | None = strawberry_django.filter_field() + serial: FilterLookup[str] | None = strawberry_django.filter_field() + asset_tag: FilterLookup[str] | None = strawberry_django.filter_field() + site: Annotated['SiteFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() + site_id: ID | None = strawberry_django.filter_field() + location: Annotated['LocationFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + location_id: Annotated['TreeNodeFilter', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + rack: Annotated['RackFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() + rack_id: ID | None = strawberry_django.filter_field() + position: Annotated['FloatLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + face: Annotated['DeviceFaceEnum', strawberry.lazy('dcim.graphql.enums')] | None = strawberry_django.filter_field() + status: Annotated['DeviceStatusEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + airflow: Annotated['DeviceAirflowEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + primary_ip4: Annotated['IPAddressFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + primary_ip4_id: ID | None = strawberry_django.filter_field() + primary_ip6: Annotated['IPAddressFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + primary_ip6_id: ID | None = strawberry_django.filter_field() + oob_ip: Annotated['IPAddressFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + oob_ip_id: ID | None = strawberry_django.filter_field() + cluster: Annotated['ClusterFilter', strawberry.lazy('virtualization.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + cluster_id: ID | None = strawberry_django.filter_field() + virtual_chassis: Annotated['VirtualChassisFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + virtual_chassis_id: ID | None = strawberry_django.filter_field() + vc_position: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + vc_priority: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + latitude: Annotated['FloatLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + longitude: Annotated['FloatLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + interfaces: Annotated['InterfaceFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + consoleports: Annotated['ConsolePortFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + consoleserverports: Annotated['ConsoleServerPortFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + poweroutlets: Annotated['PowerOutletFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + powerports: Annotated['PowerPortFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + devicebays: Annotated['DeviceBayFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + frontports: Annotated['FrontPortFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + rearports: Annotated['RearPortFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + modulebays: Annotated['ModuleBayFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + modules: Annotated['ModuleFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.DeviceBay, lookups=True) -@autotype_decorator(filtersets.DeviceBayFilterSet) -class DeviceBayFilter(BaseFilterMixin): - pass +class DeviceBayFilter(ComponentModelFilterMixin): + installed_device: Annotated['DeviceFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + installed_device_id: ID | None = strawberry_django.filter_field() @strawberry_django.filter(models.DeviceBayTemplate, lookups=True) -@autotype_decorator(filtersets.DeviceBayTemplateFilterSet) -class DeviceBayTemplateFilter(BaseFilterMixin): +class DeviceBayTemplateFilter(ComponentTemplateFilterMixin): pass @strawberry_django.filter(models.InventoryItemTemplate, lookups=True) -@autotype_decorator(filtersets.InventoryItemTemplateFilterSet) -class InventoryItemTemplateFilter(BaseFilterMixin): - pass +class InventoryItemTemplateFilter(ComponentTemplateFilterMixin): + parent: Annotated['InventoryItemTemplateFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + component_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + component_id: ID | None = strawberry_django.filter_field() + role: Annotated['InventoryItemRoleFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + role_id: ID | None = strawberry_django.filter_field() + manufacturer: Annotated['ManufacturerFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + manufacturer_id: ID | None = strawberry_django.filter_field() + part_id: FilterLookup[str] | None = strawberry_django.filter_field() @strawberry_django.filter(models.DeviceRole, lookups=True) -@autotype_decorator(filtersets.DeviceRoleFilterSet) -class DeviceRoleFilter(BaseFilterMixin): - pass +class DeviceRoleFilter(OrganizationalModelFilterMixin, RenderConfigFilterMixin): + color: Annotated['ColorEnum', strawberry.lazy('netbox.graphql.enums')] | None = strawberry_django.filter_field() + vm_role: FilterLookup[bool] | None = strawberry_django.filter_field() @strawberry_django.filter(models.DeviceType, lookups=True) -@autotype_decorator(filtersets.DeviceTypeFilterSet) -class DeviceTypeFilter(BaseFilterMixin): - pass +class DeviceTypeFilter(ImageAttachmentFilterMixin, PrimaryModelFilterMixin, WeightFilterMixin): + manufacturer: Annotated['ManufacturerFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + manufacturer_id: ID | None = strawberry_django.filter_field() + model: FilterLookup[str] | None = strawberry_django.filter_field() + slug: FilterLookup[str] | None = strawberry_django.filter_field() + default_platform: Annotated['PlatformFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + default_platform_id: ID | None = strawberry_django.filter_field() + part_number: FilterLookup[str] | None = strawberry_django.filter_field() + u_height: Annotated['FloatLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + exclude_from_utilization: FilterLookup[bool] | None = strawberry_django.filter_field() + is_full_depth: FilterLookup[bool] | None = strawberry_django.filter_field() + subdevice_role: Annotated['SubdeviceRoleEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + airflow: Annotated['DeviceAirflowEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + front_image: Annotated['ImageAttachmentFilter', strawberry.lazy('extras.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + rear_image: Annotated['ImageAttachmentFilter', strawberry.lazy('extras.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.FrontPort, lookups=True) -@autotype_decorator(filtersets.FrontPortFilterSet) -class FrontPortFilter(BaseFilterMixin): - pass +class FrontPortFilter(ModularComponentModelFilterMixin, CabledObjectModelFilterMixin): + type: Annotated['PortTypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = strawberry_django.filter_field() + color: Annotated['ColorEnum', strawberry.lazy('netbox.graphql.enums')] | None = strawberry_django.filter_field() + rear_port: Annotated['RearPortFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + rear_port_id: ID | None = strawberry_django.filter_field() + rear_port_position: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.FrontPortTemplate, lookups=True) -@autotype_decorator(filtersets.FrontPortTemplateFilterSet) -class FrontPortTemplateFilter(BaseFilterMixin): - pass +class FrontPortTemplateFilter(ModularComponentTemplateFilterMixin): + type: Annotated['PortTypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = strawberry_django.filter_field() + color: Annotated['ColorEnum', strawberry.lazy('netbox.graphql.enums')] | None = strawberry_django.filter_field() + rear_port: Annotated['RearPortTemplateFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + rear_port_id: ID | None = strawberry_django.filter_field() + rear_port_position: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.MACAddress, lookups=True) -@autotype_decorator(filtersets.MACAddressFilterSet) -class MACAddressFilter(BaseFilterMixin): - pass +class MACAddressFilter(PrimaryModelFilterMixin): + mac_address: FilterLookup[str] | None = strawberry_django.filter_field() + assigned_object_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + assigned_object_id: ID | None = strawberry_django.filter_field() @strawberry_django.filter(models.Interface, lookups=True) -@autotype_decorator(filtersets.InterfaceFilterSet) -class InterfaceFilter(BaseFilterMixin): - pass +class InterfaceFilter(ModularComponentModelFilterMixin, InterfaceBaseFilterMixin, CabledObjectModelFilterMixin): + vcdcs: Annotated['VirtualDeviceContextFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + lag: Annotated['InterfaceFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() + lag_id: ID | None = strawberry_django.filter_field() + type: Annotated['InterfaceTypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + mgmt_only: FilterLookup[bool] | None = strawberry_django.filter_field() + speed: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + duplex: Annotated['InterfaceDuplexEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + wwn: FilterLookup[str] | None = strawberry_django.filter_field() + rf_role: Annotated['WirelessRoleEnum', strawberry.lazy('wireless.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + rf_channel: Annotated['WirelessChannelEnum', strawberry.lazy('wireless.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + rf_channel_frequency: Annotated['FloatLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + rf_channel_width: Annotated['FloatLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + tx_power: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + poe_mode: Annotated['InterfacePoEModeEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + poe_type: Annotated['InterfacePoETypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + wireless_link: Annotated['WirelessLinkFilter', strawberry.lazy('wireless.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + wireless_link_id: ID | None = strawberry_django.filter_field() + wireless_lans: Annotated['WirelessLANFilter', strawberry.lazy('wireless.graphql.filters')] | 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() + ip_addresses: Annotated['IPAddressFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + mac_addresses: Annotated['MACAddressFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + fhrp_group_assignments: Annotated['FHRPGroupAssignmentFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + tunnel_terminations: Annotated['TunnelTerminationFilter', strawberry.lazy('vpn.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + l2vpn_terminations: Annotated['L2VPNFilter', strawberry.lazy('vpn.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.InterfaceTemplate, lookups=True) -@autotype_decorator(filtersets.InterfaceTemplateFilterSet) -class InterfaceTemplateFilter(BaseFilterMixin): - pass +class InterfaceTemplateFilter(ModularComponentTemplateFilterMixin): + type: Annotated['InterfaceTypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + enabled: FilterLookup[bool] | None = strawberry_django.filter_field() + mgmt_only: FilterLookup[bool] | None = strawberry_django.filter_field() + bridge: Annotated['InterfaceTemplateFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + bridge_id: ID | None = strawberry_django.filter_field() + poe_mode: Annotated['InterfacePoEModeEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + poe_type: Annotated['InterfacePoETypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + rf_role: Annotated['WirelessRoleEnum', strawberry.lazy('wireless.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.InventoryItem, lookups=True) -@autotype_decorator(filtersets.InventoryItemFilterSet) -class InventoryItemFilter(BaseFilterMixin): - pass +class InventoryItemFilter(ComponentModelFilterMixin): + parent: Annotated['InventoryItemFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + parent_id: ID | None = strawberry_django.filter_field() + component_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + component_id: ID | None = strawberry_django.filter_field() + status: Annotated['InventoryItemStatusEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + role: Annotated['InventoryItemRoleFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + role_id: ID | None = strawberry_django.filter_field() + manufacturer: Annotated['ManufacturerFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + manufacturer_id: ID | None = strawberry_django.filter_field() + part_id: FilterLookup[str] | None = strawberry_django.filter_field() + serial: FilterLookup[str] | None = strawberry_django.filter_field() + asset_tag: FilterLookup[str] | None = strawberry_django.filter_field() + discovered: FilterLookup[bool] | None = strawberry_django.filter_field() @strawberry_django.filter(models.InventoryItemRole, lookups=True) -@autotype_decorator(filtersets.InventoryItemRoleFilterSet) -class InventoryItemRoleFilter(BaseFilterMixin): - pass +class InventoryItemRoleFilter(OrganizationalModelFilterMixin): + color: Annotated['ColorEnum', strawberry.lazy('netbox.graphql.enums')] | None = strawberry_django.filter_field() @strawberry_django.filter(models.Location, lookups=True) -@autotype_decorator(filtersets.LocationFilterSet) -class LocationFilter(BaseFilterMixin): - pass +class LocationFilter(ContactFilterMixin, ImageAttachmentFilterMixin, NestedGroupModelFilterMixin): + site: Annotated['SiteFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() + site_id: ID | None = strawberry_django.filter_field() + status: Annotated['LocationStatusEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + tenant: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + tenant_id: ID | None = strawberry_django.filter_field() + facility: FilterLookup[str] | None = strawberry_django.filter_field() + prefixes: Annotated['PrefixFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + vlan_groups: Annotated['VLANGroupFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.Manufacturer, lookups=True) -@autotype_decorator(filtersets.ManufacturerFilterSet) -class ManufacturerFilter(BaseFilterMixin): +class ManufacturerFilter(ContactFilterMixin, OrganizationalModelFilterMixin): pass @strawberry_django.filter(models.Module, lookups=True) -@autotype_decorator(filtersets.ModuleFilterSet) -class ModuleFilter(BaseFilterMixin): - pass +class ModuleFilter(PrimaryModelFilterMixin, ConfigContextFilterMixin): + device: Annotated['DeviceFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() + device_id: ID | None = strawberry_django.filter_field() + module_bay: Annotated['ModuleBayFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + module_bay_id: ID | None = strawberry_django.filter_field() + module_type: Annotated['ModuleTypeFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + module_type_id: ID | None = strawberry_django.filter_field() + status: Annotated['ModuleStatusEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + serial: FilterLookup[str] | None = strawberry_django.filter_field() + asset_tag: FilterLookup[str] | None = strawberry_django.filter_field() @strawberry_django.filter(models.ModuleBay, lookups=True) -@autotype_decorator(filtersets.ModuleBayFilterSet) -class ModuleBayFilter(BaseFilterMixin): - pass +class ModuleBayFilter(ModularComponentModelFilterMixin): + parent: Annotated['ModuleBayFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + parent_id: ID | None = strawberry_django.filter_field() + position: FilterLookup[str] | None = strawberry_django.filter_field() @strawberry_django.filter(models.ModuleBayTemplate, lookups=True) -@autotype_decorator(filtersets.ModuleBayTemplateFilterSet) -class ModuleBayTemplateFilter(BaseFilterMixin): - pass +class ModuleBayTemplateFilter(ModularComponentTemplateFilterMixin): + position: FilterLookup[str] | None = strawberry_django.filter_field() @strawberry_django.filter(models.ModuleType, lookups=True) -@autotype_decorator(filtersets.ModuleTypeFilterSet) -class ModuleTypeFilter(BaseFilterMixin): - pass +class ModuleTypeFilter(ImageAttachmentFilterMixin, PrimaryModelFilterMixin, WeightFilterMixin): + manufacturer: Annotated['ManufacturerFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + manufacturer_id: ID | None = strawberry_django.filter_field() + model: FilterLookup[str] | None = strawberry_django.filter_field() + part_number: FilterLookup[str] | None = strawberry_django.filter_field() + airflow: Annotated['ModuleAirflowEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.Platform, lookups=True) -@autotype_decorator(filtersets.PlatformFilterSet) -class PlatformFilter(BaseFilterMixin): - pass +class PlatformFilter(OrganizationalModelFilterMixin): + manufacturer: Annotated['ManufacturerFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + manufacturer_id: ID | None = strawberry_django.filter_field() + config_template: Annotated['ConfigTemplateFilter', strawberry.lazy('extras.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + config_template_id: ID | None = strawberry_django.filter_field() @strawberry_django.filter(models.PowerFeed, lookups=True) -@autotype_decorator(filtersets.PowerFeedFilterSet) -class PowerFeedFilter(BaseFilterMixin): - pass +class PowerFeedFilter(PrimaryModelFilterMixin, CabledObjectModelFilterMixin): + power_panel: Annotated['PowerPanelFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + power_panel_id: ID | None = strawberry_django.filter_field() + rack: Annotated['RackFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() + rack_id: ID | None = strawberry_django.filter_field() + name: FilterLookup[str] | None = strawberry_django.filter_field() + status: Annotated['PowerFeedStatusEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + type: Annotated['PowerFeedTypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + supply: Annotated['PowerFeedSupplyEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + phase: Annotated['PowerFeedPhaseEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + voltage: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + amperage: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + max_utilization: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + available_power: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + tenant: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + tenant_id: ID | None = strawberry_django.filter_field() @strawberry_django.filter(models.PowerOutlet, lookups=True) -@autotype_decorator(filtersets.PowerOutletFilterSet) -class PowerOutletFilter(BaseFilterMixin): - pass +class PowerOutletFilter(ModularComponentModelFilterMixin, CabledObjectModelFilterMixin): + type: Annotated['PowerOutletTypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + power_port: Annotated['PowerPortFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + power_port_id: ID | None = strawberry_django.filter_field() + feed_leg: Annotated['PowerOutletFeedLegEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + color: Annotated['ColorEnum', strawberry.lazy('netbox.graphql.enums')] | None = strawberry_django.filter_field() @strawberry_django.filter(models.PowerOutletTemplate, lookups=True) -@autotype_decorator(filtersets.PowerOutletTemplateFilterSet) -class PowerOutletTemplateFilter(BaseFilterMixin): - pass +class PowerOutletTemplateFilter(ModularComponentModelFilterMixin): + type: Annotated['PowerOutletTypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + power_port: Annotated['PowerPortTemplateFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + power_port_id: ID | None = strawberry_django.filter_field() + feed_leg: Annotated['PowerOutletFeedLegEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.PowerPanel, lookups=True) -@autotype_decorator(filtersets.PowerPanelFilterSet) -class PowerPanelFilter(BaseFilterMixin): - pass +class PowerPanelFilter(ContactFilterMixin, ImageAttachmentFilterMixin, PrimaryModelFilterMixin): + site: Annotated['SiteFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() + site_id: ID | None = strawberry_django.filter_field() + location: Annotated['LocationFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + location_id: Annotated['TreeNodeFilter', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + name: FilterLookup[str] | None = strawberry_django.filter_field() @strawberry_django.filter(models.PowerPort, lookups=True) -@autotype_decorator(filtersets.PowerPortFilterSet) -class PowerPortFilter(BaseFilterMixin): - pass +class PowerPortFilter(ModularComponentModelFilterMixin, CabledObjectModelFilterMixin): + type: Annotated['PowerPortTypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + maximum_draw: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + allocated_draw: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.PowerPortTemplate, lookups=True) -@autotype_decorator(filtersets.PowerPortTemplateFilterSet) -class PowerPortTemplateFilter(BaseFilterMixin): - pass +class PowerPortTemplateFilter(ModularComponentTemplateFilterMixin): + type: Annotated['PowerPortTypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + maximum_draw: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + allocated_draw: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.RackType, lookups=True) -@autotype_decorator(filtersets.RackTypeFilterSet) -class RackTypeFilter(BaseFilterMixin): - pass +class RackTypeFilter(RackBaseFilterMixin): + form_factor: Annotated['RackFormFactorEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + manufacturer: Annotated['ManufacturerFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + manufacturer_id: ID | None = strawberry_django.filter_field() + model: FilterLookup[str] | None = strawberry_django.filter_field() + slug: FilterLookup[str] | None = strawberry_django.filter_field() @strawberry_django.filter(models.Rack, lookups=True) -@autotype_decorator(filtersets.RackFilterSet) -class RackFilter(BaseFilterMixin): - pass +class RackFilter(ContactFilterMixin, ImageAttachmentFilterMixin, RackBaseFilterMixin): + form_factor: Annotated['RackFormFactorEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + rack_type: Annotated['RackTypeFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + rack_type_id: ID | None = strawberry_django.filter_field() + name: FilterLookup[str] | None = strawberry_django.filter_field() + facility_id: FilterLookup[str] | None = strawberry_django.filter_field() + site: Annotated['SiteFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() + site_id: ID | None = strawberry_django.filter_field() + location: Annotated['LocationFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + location_id: Annotated['TreeNodeFilter', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + tenant: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + tenant_id: ID | None = strawberry_django.filter_field() + status: Annotated['RackStatusEnum', strawberry.lazy('dcim.graphql.enums')] | None = strawberry_django.filter_field() + role: Annotated['RackRoleFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() + role_id: ID | None = strawberry_django.filter_field() + serial: FilterLookup[str] | None = strawberry_django.filter_field() + asset_tag: FilterLookup[str] | None = strawberry_django.filter_field() + airflow: Annotated['RackAirflowEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + vlan_groups: Annotated['VLANGroupFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.RackReservation, lookups=True) -@autotype_decorator(filtersets.RackReservationFilterSet) -class RackReservationFilter(BaseFilterMixin): - pass +class RackReservationFilter(PrimaryModelFilterMixin): + rack: Annotated['RackFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() + rack_id: ID | None = strawberry_django.filter_field() + units: Annotated['IntegerArrayLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + tenant: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + tenant_id: ID | None = strawberry_django.filter_field() + user: Annotated['UserFilter', strawberry.lazy('users.graphql.filters')] | None = strawberry_django.filter_field() + user_id: ID | None = strawberry_django.filter_field() + description: FilterLookup[str] | None = strawberry_django.filter_field() @strawberry_django.filter(models.RackRole, lookups=True) -@autotype_decorator(filtersets.RackRoleFilterSet) -class RackRoleFilter(BaseFilterMixin): - pass +class RackRoleFilter(OrganizationalModelFilterMixin): + color: Annotated['ColorEnum', strawberry.lazy('netbox.graphql.enums')] | None = strawberry_django.filter_field() @strawberry_django.filter(models.RearPort, lookups=True) -@autotype_decorator(filtersets.RearPortFilterSet) -class RearPortFilter(BaseFilterMixin): - pass +class RearPortFilter(ModularComponentModelFilterMixin, CabledObjectModelFilterMixin): + type: Annotated['PortTypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = strawberry_django.filter_field() + color: Annotated['ColorEnum', strawberry.lazy('netbox.graphql.enums')] | None = strawberry_django.filter_field() + positions: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.RearPortTemplate, lookups=True) -@autotype_decorator(filtersets.RearPortTemplateFilterSet) -class RearPortTemplateFilter(BaseFilterMixin): - pass +class RearPortTemplateFilter(ModularComponentTemplateFilterMixin): + type: Annotated['PortTypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = strawberry_django.filter_field() + color: Annotated['ColorEnum', strawberry.lazy('netbox.graphql.enums')] | None = strawberry_django.filter_field() + positions: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.Region, lookups=True) -@autotype_decorator(filtersets.RegionFilterSet) -class RegionFilter(BaseFilterMixin): - pass +class RegionFilter(ContactFilterMixin, NestedGroupModelFilterMixin): + prefixes: Annotated['PrefixFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + vlan_groups: Annotated['VLANGroupFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.Site, lookups=True) -@autotype_decorator(filtersets.SiteFilterSet) -class SiteFilter(BaseFilterMixin): - pass +class SiteFilter(ContactFilterMixin, ImageAttachmentFilterMixin, PrimaryModelFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() + slug: FilterLookup[str] | None = strawberry_django.filter_field() + status: Annotated['SiteStatusEnum', strawberry.lazy('dcim.graphql.enums')] | None = strawberry_django.filter_field() + region: Annotated['RegionFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() + region_id: Annotated['TreeNodeFilter', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + group: Annotated['SiteGroupFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + group_id: Annotated['TreeNodeFilter', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + tenant: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + tenant_id: ID | None = strawberry_django.filter_field() + facility: FilterLookup[str] | None = strawberry_django.filter_field() + asns: Annotated['ASNFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field() + time_zone: FilterLookup[str] | None = strawberry_django.filter_field() + physical_address: FilterLookup[str] | None = strawberry_django.filter_field() + shipping_address: FilterLookup[str] | None = strawberry_django.filter_field() + latitude: Annotated['FloatLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + longitude: Annotated['FloatLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + prefixes: Annotated['PrefixFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + vlan_groups: Annotated['VLANGroupFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.SiteGroup, lookups=True) -@autotype_decorator(filtersets.SiteGroupFilterSet) -class SiteGroupFilter(BaseFilterMixin): - pass +class SiteGroupFilter(ContactFilterMixin, NestedGroupModelFilterMixin): + prefixes: Annotated['PrefixFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + vlan_groups: Annotated['VLANGroupFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.VirtualChassis, lookups=True) -@autotype_decorator(filtersets.VirtualChassisFilterSet) -class VirtualChassisFilter(BaseFilterMixin): - pass +class VirtualChassisFilter(PrimaryModelFilterMixin): + master: Annotated['DeviceFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() + master_id: ID | None = strawberry_django.filter_field() + name: FilterLookup[str] | None = strawberry_django.filter_field() + domain: FilterLookup[str] | None = strawberry_django.filter_field() @strawberry_django.filter(models.VirtualDeviceContext, lookups=True) -@autotype_decorator(filtersets.VirtualDeviceContextFilterSet) -class VirtualDeviceContextFilter(BaseFilterMixin): - pass +class VirtualDeviceContextFilter(PrimaryModelFilterMixin): + device: Annotated['DeviceFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() + device_id: ID | None = strawberry_django.filter_field() + name: FilterLookup[str] | None = strawberry_django.filter_field() + status: Annotated['VirtualDeviceContextStatusEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + identifier: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + primary_ip4: Annotated['IPAddressFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + primary_ip4_id: ID | None = strawberry_django.filter_field() + primary_ip6: Annotated['IPAddressFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + primary_ip6_id: ID | None = strawberry_django.filter_field() + tenant: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + tenant_id: ID | None = strawberry_django.filter_field() + comments: FilterLookup[str] | None = strawberry_django.filter_field() diff --git a/netbox/extras/graphql/enums.py b/netbox/extras/graphql/enums.py new file mode 100644 index 00000000000..959314d9e2f --- /dev/null +++ b/netbox/extras/graphql/enums.py @@ -0,0 +1,190 @@ +from enum import Enum +import strawberry + +__all__ = [ + 'CustomFieldTypeEnum', + 'CustomFieldFilterLogicEnum', + 'CustomFieldUIVisibleEnum', + 'CustomFieldUIEditableEnum', + 'CustomFieldChoiceSetBaseEnum', + 'CustomLinkButtonClassEnum', + 'BookmarkOrderingEnum', + 'JournalEntryKindEnum', + 'LogLevelEnum', + 'DurationEnum', + 'WebhookHttpMethodEnum', + 'ChangeActionEnum', + 'DashboardWidgetColorEnum', + 'EventRuleActionEnum', +] + +# +# CustomFields +# + + +@strawberry.enum +class CustomFieldTypeEnum(Enum): + TYPE_TEXT = 'text' + TYPE_LONGTEXT = 'longtext' + TYPE_INTEGER = 'integer' + TYPE_DECIMAL = 'decimal' + TYPE_BOOLEAN = 'boolean' + TYPE_DATE = 'date' + TYPE_DATETIME = 'datetime' + TYPE_URL = 'url' + TYPE_JSON = 'json' + TYPE_SELECT = 'select' + TYPE_MULTISELECT = 'multiselect' + TYPE_OBJECT = 'object' + TYPE_MULTIOBJECT = 'multiobject' + + +@strawberry.enum +class CustomFieldFilterLogicEnum(Enum): + FILTER_DISABLED = 'disabled' + FILTER_LOOSE = 'loose' + FILTER_EXACT = 'exact' + + +@strawberry.enum +class CustomFieldUIVisibleEnum(Enum): + ALWAYS = 'always' + IF_SET = 'if-set' + HIDDEN = 'hidden' + + +@strawberry.enum +class CustomFieldUIEditableEnum(Enum): + YES = 'yes' + NO = 'no' + HIDDEN = 'hidden' + + +@strawberry.enum +class CustomFieldChoiceSetBaseEnum(Enum): + IATA = 'IATA' + ISO_3166 = 'ISO_3166' + UN_LOCODE = 'UN_LOCODE' + + +# +# CustomLinks +# + + +@strawberry.enum +class CustomLinkButtonClassEnum(Enum): + LINK = 'ghost-dark' + + +# +# Bookmarks +# + + +@strawberry.enum +class BookmarkOrderingEnum(Enum): + ORDERING_NEWEST = '-created' + ORDERING_OLDEST = 'created' + ORDERING_ALPHABETICAL_AZ = 'name' + ORDERING_ALPHABETICAL_ZA = '-name' + + +# +# Journal entries +# + + +@strawberry.enum +class JournalEntryKindEnum(Enum): + key = 'JournalEntry.kind' + + KIND_INFO = 'info' + KIND_SUCCESS = 'success' + KIND_WARNING = 'warning' + KIND_DANGER = 'danger' + + +# +# Reports and Scripts +# + + +@strawberry.enum +class LogLevelEnum(Enum): + LOG_DEBUG = 'debug' + LOG_DEFAULT = 'default' + LOG_INFO = 'info' + LOG_SUCCESS = 'success' + LOG_WARNING = 'warning' + LOG_FAILURE = 'failure' + + +@strawberry.enum +class DurationEnum(Enum): + HOURLY = 60 + TWELVE_HOURS = 720 + DAILY = 1440 + WEEKLY = 10080 + THIRTY_DAYS = 43200 + + +# +# Webhooks +# + + +@strawberry.enum +class WebhookHttpMethodEnum(Enum): + METHOD_GET = 'GET' + METHOD_POST = 'POST' + METHOD_PUT = 'PUT' + METHOD_PATCH = 'PATCH' + METHOD_DELETE = 'DELETE' + + +# +# Staging +# + + +@strawberry.enum +class ChangeActionEnum(Enum): + ACTION_CREATE = 'create' + ACTION_UPDATE = 'update' + ACTION_DELETE = 'delete' + + +# +# Dashboard widgets +# + + +@strawberry.enum +class DashboardWidgetColorEnum(Enum): + BLUE = 'blue' + INDIGO = 'indigo' + PURPLE = 'purple' + PINK = 'pink' + RED = 'red' + ORANGE = 'orange' + YELLOW = 'yellow' + GREEN = 'green' + TEAL = 'teal' + CYAN = 'cyan' + GRAY = 'gray' + BLACK = 'black' + WHITE = 'white' + + +# +# Event Rules +# + + +@strawberry.enum +class EventRuleActionEnum(Enum): + WEBHOOK = 'webhook' + SCRIPT = 'script' + NOTIFICATION = 'notification' diff --git a/netbox/extras/graphql/filter_mixins.py b/netbox/extras/graphql/filter_mixins.py new file mode 100644 index 00000000000..726ae046e74 --- /dev/null +++ b/netbox/extras/graphql/filter_mixins.py @@ -0,0 +1,53 @@ +from dataclasses import dataclass +from typing import Annotated, TYPE_CHECKING +import strawberry +import strawberry_django +from strawberry_django import FilterLookup +from core.graphql.filter_mixins import BaseFilterMixin + +if TYPE_CHECKING: + from core.graphql.filter_lookups import * + from .filters import * + +__all__ = [ + 'CustomFieldsFilterMixin', + 'JournalEntriesFilterMixin', + 'TagsFilterMixin', + 'ConfigContextFilterMixin', + 'TagBaseFilterMixin', +] + + +@dataclass +class CustomFieldsFilterMixin(BaseFilterMixin): + custom_field_data: Annotated['JSONFilter', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + + +@dataclass +class JournalEntriesFilterMixin(BaseFilterMixin): + journal_entries: Annotated['JournalEntryFilter', strawberry.lazy('extras.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + + +@dataclass +class TagsFilterMixin(BaseFilterMixin): + tags: Annotated['TagFilter', strawberry.lazy('extras.graphql.filters')] | None = strawberry_django.filter_field() + + +@dataclass +class ConfigContextFilterMixin(BaseFilterMixin): + local_config_context: Annotated['ConfigContextFilter', strawberry.lazy('extras.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + config_context: Annotated['ConfigContextFilter', strawberry.lazy('extras.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + + +@dataclass +class TagBaseFilterMixin(BaseFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() + slug: FilterLookup[str] | None = strawberry_django.filter_field() diff --git a/netbox/extras/graphql/filters.py b/netbox/extras/graphql/filters.py index ff2e6a0f177..e80487fa040 100644 --- a/netbox/extras/graphql/filters.py +++ b/netbox/extras/graphql/filters.py @@ -1,7 +1,33 @@ +from typing import Annotated, TYPE_CHECKING +import strawberry +from strawberry.scalars import ID import strawberry_django +from strawberry_django import ( + FilterLookup, +) +from netbox.graphql.filter_mixins import ( + BaseObjectTypeFilterMixin, + ChangeLogFilterMixin, + SyncedDataFilterMixin, +) +from extras import models +from extras.graphql.filter_mixins import TagBaseFilterMixin, CustomFieldsFilterMixin, TagsFilterMixin + +if TYPE_CHECKING: + from .enums import * + from netbox.graphql.enums import * + from wireless.graphql.enums import * + from core.graphql.filter_lookups import * + from extras.graphql.filters import * + from circuits.graphql.filters import * + from dcim.graphql.filters import * + from ipam.graphql.filters import * + from tenancy.graphql.filters import * + from wireless.graphql.filters import * + from users.graphql.filters import * + from virtualization.graphql.filters import * + from vpn.graphql.filters import * -from extras import filtersets, models -from netbox.graphql.filter_mixins import autotype_decorator, BaseFilterMixin __all__ = ( 'ConfigContextFilter', @@ -21,78 +47,253 @@ @strawberry_django.filter(models.ConfigContext, lookups=True) -@autotype_decorator(filtersets.ConfigContextFilterSet) -class ConfigContextFilter(BaseFilterMixin): - pass +class ConfigContextFilter(BaseObjectTypeFilterMixin, SyncedDataFilterMixin, ChangeLogFilterMixin): + name: FilterLookup[str] = strawberry_django.filter_field() + weight: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + description: FilterLookup[str] = strawberry_django.filter_field() + is_active: FilterLookup[bool] = strawberry_django.filter_field() + regions: Annotated['RegionFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + region_id: Annotated['TreeNodeFilter', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + site_groups: Annotated['SiteGroupFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + site_group_id: Annotated['TreeNodeFilter', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + sites: Annotated['SiteFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() + locations: Annotated['LocationFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + device_types: Annotated['DeviceTypeFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + roles: Annotated['DeviceRoleFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + platforms: Annotated['PlatformFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + cluster_types: Annotated['ClusterTypeFilter', strawberry.lazy('virtualization.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + cluster_groups: Annotated['ClusterGroupFilter', strawberry.lazy('virtualization.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + clusters: Annotated['ClusterFilter', strawberry.lazy('virtualization.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + tenant_groups: Annotated['TenantGroupFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + tenant_group_id: Annotated['TreeNodeFilter', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + tenants: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + tags: Annotated['TagFilter', strawberry.lazy('extras.graphql.filters')] | None = strawberry_django.filter_field() + data: Annotated['JSONFilter', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.ConfigTemplate, lookups=True) -@autotype_decorator(filtersets.ConfigTemplateFilterSet) -class ConfigTemplateFilter(BaseFilterMixin): - pass +class ConfigTemplateFilter(BaseObjectTypeFilterMixin, SyncedDataFilterMixin, ChangeLogFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() + description: FilterLookup[str] | None = strawberry_django.filter_field() + template_code: FilterLookup[str] | None = strawberry_django.filter_field() + environment_params: Annotated['JSONFilter', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.CustomField, lookups=True) -@autotype_decorator(filtersets.CustomFieldFilterSet) -class CustomFieldFilter(BaseFilterMixin): - pass +class CustomFieldFilter(BaseObjectTypeFilterMixin, ChangeLogFilterMixin): + type: Annotated['CustomFieldTypeEnum', strawberry.lazy('extras.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + related_object_type_id: ID | None = strawberry_django.filter_field() + name: FilterLookup[str] | None = strawberry_django.filter_field() + label: FilterLookup[str] | None = strawberry_django.filter_field() + group_name: FilterLookup[str] | None = strawberry_django.filter_field() + description: FilterLookup[str] | None = strawberry_django.filter_field() + required: FilterLookup[bool] | None = strawberry_django.filter_field() + unique: FilterLookup[bool] | None = strawberry_django.filter_field() + search_weight: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + filter_logic: Annotated['CustomFieldFilterLogicEnum', strawberry.lazy('extras.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + default: Annotated['JSONFilter', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + related_object_filter: Annotated['JSONFilter', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + weight: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + validation_minimum: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + validation_maximum: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + validation_regex: FilterLookup[str] | None = strawberry_django.filter_field() + choice_set: Annotated['CustomFieldChoiceSetFilter', strawberry.lazy('extras.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + choice_set_id: ID | None = strawberry_django.filter_field() + ui_visible: Annotated['CustomFieldUIVisibleEnum', strawberry.lazy('extras.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + ui_editable: Annotated['CustomFieldUIEditableEnum', strawberry.lazy('extras.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + is_cloneable: FilterLookup[bool] | None = strawberry_django.filter_field() + comments: FilterLookup[str] | None = strawberry_django.filter_field() @strawberry_django.filter(models.CustomFieldChoiceSet, lookups=True) -@autotype_decorator(filtersets.CustomFieldChoiceSetFilterSet) -class CustomFieldChoiceSetFilter(BaseFilterMixin): - pass +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 = ( + strawberry_django.filter_field() + ) + extra_choices: Annotated['StringArrayLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + order_alphabetically: FilterLookup[bool] | None = strawberry_django.filter_field() @strawberry_django.filter(models.CustomLink, lookups=True) -@autotype_decorator(filtersets.CustomLinkFilterSet) -class CustomLinkFilter(BaseFilterMixin): - pass +class CustomLinkFilter(BaseObjectTypeFilterMixin, ChangeLogFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() + enabled: FilterLookup[bool] | None = strawberry_django.filter_field() + link_text: FilterLookup[str] | None = strawberry_django.filter_field() + link_url: FilterLookup[str] | None = strawberry_django.filter_field() + weight: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + group_name: FilterLookup[str] | None = strawberry_django.filter_field() + button_class: FilterLookup[str] | None = strawberry_django.filter_field() + new_window: FilterLookup[bool] | None = strawberry_django.filter_field() @strawberry_django.filter(models.ExportTemplate, lookups=True) -@autotype_decorator(filtersets.ExportTemplateFilterSet) -class ExportTemplateFilter(BaseFilterMixin): - pass +class ExportTemplateFilter(BaseObjectTypeFilterMixin, SyncedDataFilterMixin, ChangeLogFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() + description: FilterLookup[str] | None = strawberry_django.filter_field() + template_code: FilterLookup[str] | None = strawberry_django.filter_field() + mime_type: FilterLookup[str] | None = strawberry_django.filter_field() + file_extension: FilterLookup[str] | None = strawberry_django.filter_field() + as_attachment: FilterLookup[bool] | None = strawberry_django.filter_field() @strawberry_django.filter(models.ImageAttachment, lookups=True) -@autotype_decorator(filtersets.ImageAttachmentFilterSet) -class ImageAttachmentFilter(BaseFilterMixin): - pass +class ImageAttachmentFilter(BaseObjectTypeFilterMixin, ChangeLogFilterMixin): + object_id: ID | None = strawberry_django.filter_field() + image_height: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + image_width: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + name: FilterLookup[str] | None = strawberry_django.filter_field() @strawberry_django.filter(models.JournalEntry, lookups=True) -@autotype_decorator(filtersets.JournalEntryFilterSet) -class JournalEntryFilter(BaseFilterMixin): - pass +class JournalEntryFilter(BaseObjectTypeFilterMixin, CustomFieldsFilterMixin, TagsFilterMixin, ChangeLogFilterMixin): + assigned_object_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + assigned_object_type_id: ID | None = strawberry_django.filter_field() + assigned_object_id: ID | None = strawberry_django.filter_field() + created_by: Annotated['UserFilter', strawberry.lazy('users.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + kind: Annotated['JournalEntryKindEnum', strawberry.lazy('extras.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + comments: FilterLookup[str] | None = strawberry_django.filter_field() @strawberry_django.filter(models.NotificationGroup, lookups=True) -@autotype_decorator(filtersets.NotificationGroupFilterSet) -class NotificationGroupFilter(BaseFilterMixin): - pass +class NotificationGroupFilter(BaseObjectTypeFilterMixin, ChangeLogFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() + description: FilterLookup[str] | None = strawberry_django.filter_field() + groups: Annotated['GroupFilter', strawberry.lazy('users.graphql.filters')] | None = strawberry_django.filter_field() + users: Annotated['UserFilter', strawberry.lazy('users.graphql.filters')] | None = strawberry_django.filter_field() @strawberry_django.filter(models.SavedFilter, lookups=True) -@autotype_decorator(filtersets.SavedFilterFilterSet) -class SavedFilterFilter(BaseFilterMixin): - pass +class SavedFilterFilter(BaseObjectTypeFilterMixin, ChangeLogFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() + slug: FilterLookup[str] | None = strawberry_django.filter_field() + description: FilterLookup[str] | None = strawberry_django.filter_field() + user: Annotated['UserFilter', strawberry.lazy('users.graphql.filters')] | None = strawberry_django.filter_field() + user_id: ID | None = strawberry_django.filter_field() + weight: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + enabled: FilterLookup[bool] | None = strawberry_django.filter_field() + shared: FilterLookup[bool] | None = strawberry_django.filter_field() + parameters: Annotated['JSONFilter', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.Tag, lookups=True) -@autotype_decorator(filtersets.TagFilterSet) -class TagFilter(BaseFilterMixin): - pass +class TagFilter(BaseObjectTypeFilterMixin, ChangeLogFilterMixin, TagBaseFilterMixin): + color: Annotated['ColorEnum', strawberry.lazy('netbox.graphql.enums')] | None = strawberry_django.filter_field() + description: FilterLookup[str] | None = strawberry_django.filter_field() @strawberry_django.filter(models.Webhook, lookups=True) -@autotype_decorator(filtersets.WebhookFilterSet) -class WebhookFilter(BaseFilterMixin): - pass +class WebhookFilter(BaseObjectTypeFilterMixin, CustomFieldsFilterMixin, TagsFilterMixin, ChangeLogFilterMixin): + 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 = ( + strawberry_django.filter_field() + ) + http_content_type: FilterLookup[str] | None = strawberry_django.filter_field() + additional_headers: FilterLookup[str] | None = strawberry_django.filter_field() + body_template: FilterLookup[str] | None = strawberry_django.filter_field() + secret: FilterLookup[str] | None = strawberry_django.filter_field() + ssl_verification: FilterLookup[bool] | None = strawberry_django.filter_field() + ca_file_path: FilterLookup[str] | None = strawberry_django.filter_field() + events: Annotated['EventRuleFilter', strawberry.lazy('extras.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.EventRule, lookups=True) -@autotype_decorator(filtersets.EventRuleFilterSet) -class EventRuleFilter(BaseFilterMixin): - pass +class EventRuleFilter(BaseObjectTypeFilterMixin, CustomFieldsFilterMixin, TagsFilterMixin, ChangeLogFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() + description: FilterLookup[str] | None = strawberry_django.filter_field() + event_types: Annotated['StringArrayLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + enabled: FilterLookup[bool] | None = strawberry_django.filter_field() + conditions: Annotated['JSONFilter', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + action_type: Annotated['EventRuleActionEnum', strawberry.lazy('extras.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + action_object_type: FilterLookup[str] | None = strawberry_django.filter_field() + action_object_type_id: ID | None = strawberry_django.filter_field() + action_object_id: ID | None = strawberry_django.filter_field() + action_data: Annotated['JSONFilter', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + comments: FilterLookup[str] | None = strawberry_django.filter_field() diff --git a/netbox/ipam/graphql/enums.py b/netbox/ipam/graphql/enums.py new file mode 100644 index 00000000000..3e73773cb59 --- /dev/null +++ b/netbox/ipam/graphql/enums.py @@ -0,0 +1,132 @@ +from enum import Enum +import strawberry + +__all__ = [ + 'IPAddressFamilyEnum', + 'PrefixStatusEnum', + 'IPRangeStatusEnum', + 'IPAddressStatusEnum', + 'IPAddressRoleEnum', + 'FHRPGroupProtocolEnum', + 'FHRPGroupAuthTypeEnum', + 'VLANStatusEnum', + 'VLANQinQRoleEnum', + 'ServiceProtocolEnum', +] + + +@strawberry.enum +class IPAddressFamilyEnum(Enum): + FAMILY_4 = 4 + FAMILY_6 = 6 + + +# +# Prefixes +# + + +@strawberry.enum +class PrefixStatusEnum(Enum): + key = 'Prefix.status' + + STATUS_CONTAINER = 'container' + STATUS_ACTIVE = 'active' + STATUS_RESERVED = 'reserved' + STATUS_DEPRECATED = 'deprecated' + + +# +# IP Ranges +# + + +@strawberry.enum +class IPRangeStatusEnum(Enum): + key = 'IPRange.status' + + STATUS_ACTIVE = 'active' + STATUS_RESERVED = 'reserved' + STATUS_DEPRECATED = 'deprecated' + + +# +# IP Addresses +# + + +@strawberry.enum +class IPAddressStatusEnum(Enum): + key = 'IPAddress.status' + + STATUS_ACTIVE = 'active' + STATUS_RESERVED = 'reserved' + STATUS_DEPRECATED = 'deprecated' + STATUS_DHCP = 'dhcp' + STATUS_SLAAC = 'slaac' + + +@strawberry.enum +class IPAddressRoleEnum(Enum): + ROLE_LOOPBACK = 'loopback' + ROLE_SECONDARY = 'secondary' + ROLE_ANYCAST = 'anycast' + ROLE_VIP = 'vip' + ROLE_VRRP = 'vrrp' + ROLE_HSRP = 'hsrp' + ROLE_GLBP = 'glbp' + ROLE_CARP = 'carp' + + +# +# FHRP +# + + +@strawberry.enum +class FHRPGroupProtocolEnum(Enum): + PROTOCOL_VRRP2 = 'vrrp2' + PROTOCOL_VRRP3 = 'vrrp3' + PROTOCOL_HSRP = 'hsrp' + PROTOCOL_GLBP = 'glbp' + PROTOCOL_CARP = 'carp' + PROTOCOL_CLUSTERXL = 'clusterxl' + PROTOCOL_OTHER = 'other' + + +@strawberry.enum +class FHRPGroupAuthTypeEnum(Enum): + AUTHENTICATION_PLAINTEXT = 'plaintext' + AUTHENTICATION_MD5 = 'md5' + + +# +# VLANs +# + + +@strawberry.enum +class VLANStatusEnum(Enum): + key = 'VLAN.status' + + STATUS_ACTIVE = 'active' + STATUS_RESERVED = 'reserved' + STATUS_DEPRECATED = 'deprecated' + + +@strawberry.enum +class VLANQinQRoleEnum(Enum): + ROLE_SERVICE = 'svlan' + ROLE_CUSTOMER = 'cvlan' + + +# +# Services +# + + +@strawberry.enum +class ServiceProtocolEnum(Enum): + PROTOCOL_TCP = 'tcp' + PROTOCOL_UDP = 'udp' + PROTOCOL_SCTP = 'sctp' diff --git a/netbox/ipam/graphql/filter_mixins.py b/netbox/ipam/graphql/filter_mixins.py new file mode 100644 index 00000000000..74c189344b0 --- /dev/null +++ b/netbox/ipam/graphql/filter_mixins.py @@ -0,0 +1,22 @@ +from dataclasses import dataclass +from typing import Annotated, TYPE_CHECKING +import strawberry +import strawberry_django + +from core.graphql.filter_mixins import * + +if TYPE_CHECKING: + from .enums import * + from core.graphql.filter_lookups import * + +__all__ = ['ServiceBaseFilterMixin'] + + +@dataclass +class ServiceBaseFilterMixin(BaseFilterMixin): + protocol: Annotated['ServiceProtocolEnum', strawberry.lazy('ipam.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + ports: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) diff --git a/netbox/ipam/graphql/filters.py b/netbox/ipam/graphql/filters.py index 1b0e0133b6d..40c98a99c7a 100644 --- a/netbox/ipam/graphql/filters.py +++ b/netbox/ipam/graphql/filters.py @@ -1,7 +1,37 @@ +from datetime import date +from typing import Annotated, TYPE_CHECKING +import netaddr +from netaddr.core import AddrFormatError +from django.db.models import Q +import strawberry +from strawberry.scalars import ID import strawberry_django - -from ipam import filtersets, models -from netbox.graphql.filter_mixins import autotype_decorator, BaseFilterMixin +from strawberry_django import ( + FilterLookup, + DateFilterLookup, +) +from core.graphql.filter_mixins import * +from netbox.graphql.filter_mixins import * +from tenancy.graphql.filter_mixins import * + +from ipam import models +from ipam.graphql.filter_mixins import * + +if TYPE_CHECKING: + from .enums import * + from netbox.graphql.enums import * + from wireless.graphql.enums import * + from core.graphql.filter_lookups import * + from core.graphql.filters import * + from extras.graphql.filters import * + from circuits.graphql.filters import * + from dcim.graphql.filters import * + from ipam.graphql.filters import * + from tenancy.graphql.filters import * + from wireless.graphql.filters import * + from users.graphql.filters import * + from virtualization.graphql.filters import * + from vpn.graphql.filters import * __all__ = ( 'ASNFilter', @@ -26,108 +56,303 @@ @strawberry_django.filter(models.ASN, lookups=True) -@autotype_decorator(filtersets.ASNFilterSet) -class ASNFilter(BaseFilterMixin): - pass +class ASNFilter(PrimaryModelFilterMixin): + rir: Annotated['RIRFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field() + rir_id: ID | None = strawberry_django.filter_field() + asn: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + tenant: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + tenant_id: ID | None = strawberry_django.filter_field() @strawberry_django.filter(models.ASNRange, lookups=True) -@autotype_decorator(filtersets.ASNRangeFilterSet) -class ASNRangeFilter(BaseFilterMixin): - pass +class ASNRangeFilter(OrganizationalModelFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() + slug: FilterLookup[str] | None = strawberry_django.filter_field() + rir: Annotated['RIRFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field() + rir_id: ID | None = strawberry_django.filter_field() + start: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + end: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + tenant: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + tenant_id: ID | None = strawberry_django.filter_field() @strawberry_django.filter(models.Aggregate, lookups=True) -@autotype_decorator(filtersets.AggregateFilterSet) -class AggregateFilter(BaseFilterMixin): - pass +class AggregateFilter(ContactFilterMixin, PrimaryModelFilterMixin): + prefix: Annotated['PrefixFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field() + prefix_id: ID | None = strawberry_django.filter_field() + rir: Annotated['RIRFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field() + rir_id: ID | None = strawberry_django.filter_field() + tenant: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + tenant_id: ID | None = strawberry_django.filter_field() + date_added: DateFilterLookup[date] | None = strawberry_django.filter_field() @strawberry_django.filter(models.FHRPGroup, lookups=True) -@autotype_decorator(filtersets.FHRPGroupFilterSet) -class FHRPGroupFilter(BaseFilterMixin): - pass +class FHRPGroupFilter(PrimaryModelFilterMixin): + group_id: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + name: FilterLookup[str] | None = strawberry_django.filter_field() + protocol: Annotated['FHRPGroupProtocolEnum', strawberry.lazy('ipam.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + auth_type: Annotated['FHRPGroupAuthTypeEnum', strawberry.lazy('ipam.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + auth_key: FilterLookup[str] | None = strawberry_django.filter_field() + ip_addresses: Annotated['IPAddressFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.FHRPGroupAssignment, lookups=True) -@autotype_decorator(filtersets.FHRPGroupAssignmentFilterSet) -class FHRPGroupAssignmentFilter(BaseFilterMixin): - pass +class FHRPGroupAssignmentFilter(BaseObjectTypeFilterMixin, ChangeLogFilterMixin): + inteface_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + interface_id: FilterLookup[str] | None = strawberry_django.filter_field() + group: Annotated['FHRPGroupFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + group_id: ID | None = strawberry_django.filter_field() + priority: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.IPAddress, lookups=True) -@autotype_decorator(filtersets.IPAddressFilterSet) -class IPAddressFilter(BaseFilterMixin): - pass +class IPAddressFilter(ContactFilterMixin, PrimaryModelFilterMixin): + 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() + tenant: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + tenant_id: ID | None = strawberry_django.filter_field() + status: Annotated['IPAddressStatusEnum', strawberry.lazy('ipam.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + role: Annotated['IPAddressRoleEnum', strawberry.lazy('ipam.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + assigned_object_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + assigned_object_id: ID | None = strawberry_django.filter_field() + nat_inside: Annotated['IPAddressFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + nat_inside_id: ID | None = strawberry_django.filter_field() + nat_outside: Annotated['IPAddressFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + nat_outside_id: ID | None = strawberry_django.filter_field() + dns_name: FilterLookup[str] | None = strawberry_django.filter_field() + + @strawberry_django.filter_field() + def parent(self, value: list[str], prefix) -> Q: + if not value: + return Q() + q = Q() + for subnet in value: + try: + query = str(netaddr.IPNetwork(subnet.strip()).cidr) + q |= Q(address__net_host_contained=query) + except (AddrFormatError, ValueError): + return Q() + return q @strawberry_django.filter(models.IPRange, lookups=True) -@autotype_decorator(filtersets.IPRangeFilterSet) -class IPRangeFilter(BaseFilterMixin): - pass +class IPRangeFilter(ContactFilterMixin, PrimaryModelFilterMixin): + start_address: FilterLookup[str] | None = strawberry_django.filter_field() + end_address: FilterLookup[str] | None = strawberry_django.filter_field() + size: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | 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() + tenant: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + tenant_id: ID | None = strawberry_django.filter_field() + status: Annotated['IPRangeStatusEnum', strawberry.lazy('ipam.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + role: Annotated['IPAddressRoleEnum', strawberry.lazy('ipam.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + mark_utilized: FilterLookup[bool] | None = strawberry_django.filter_field() + + @strawberry_django.filter_field() + def parent(self, value: list[str], prefix) -> Q: + if not value: + return Q() + q = Q() + for subnet in value: + try: + query = str(netaddr.IPNetwork(subnet.strip()).cidr) + q |= Q(start_address__net_host_contained=query, end_address__net_host_contained=query) + except (AddrFormatError, ValueError): + return Q() + return q @strawberry_django.filter(models.Prefix, lookups=True) -@autotype_decorator(filtersets.PrefixFilterSet) -class PrefixFilter(BaseFilterMixin): - pass +class PrefixFilter(ContactFilterMixin, PrimaryModelFilterMixin): + prefix: 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() + tenant: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + tenant_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 = ( + 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() + is_pool: FilterLookup[bool] | None = strawberry_django.filter_field() + mark_utilized: FilterLookup[bool] | None = strawberry_django.filter_field() + scope_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + scope_id: ID | None = strawberry_django.filter_field() @strawberry_django.filter(models.RIR, lookups=True) -@autotype_decorator(filtersets.RIRFilterSet) -class RIRFilter(BaseFilterMixin): - pass +class RIRFilter(OrganizationalModelFilterMixin): + is_private: FilterLookup[bool] | None = strawberry_django.filter_field() @strawberry_django.filter(models.Role, lookups=True) -@autotype_decorator(filtersets.RoleFilterSet) -class RoleFilter(BaseFilterMixin): - pass +class RoleFilter(OrganizationalModelFilterMixin): + weight: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.RouteTarget, lookups=True) -@autotype_decorator(filtersets.RouteTargetFilterSet) -class RouteTargetFilter(BaseFilterMixin): - pass +class RouteTargetFilter(PrimaryModelFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() + tenant: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + tenant_id: ID | None = strawberry_django.filter_field() @strawberry_django.filter(models.Service, lookups=True) -@autotype_decorator(filtersets.ServiceFilterSet) -class ServiceFilter(BaseFilterMixin): - pass +class ServiceFilter(ContactFilterMixin, ServiceBaseFilterMixin, PrimaryModelFilterMixin): + device: Annotated['DeviceFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() + device_id: ID | None = strawberry_django.filter_field() + virtual_machine: Annotated['VirtualMachineFilter', strawberry.lazy('virtualization.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + virtual_machine_id: ID | None = strawberry_django.filter_field() + name: FilterLookup[str] | None = strawberry_django.filter_field() + ipaddresses: Annotated['IPAddressFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.ServiceTemplate, lookups=True) -@autotype_decorator(filtersets.ServiceTemplateFilterSet) -class ServiceTemplateFilter(BaseFilterMixin): - pass +class ServiceTemplateFilter(ServiceBaseFilterMixin, PrimaryModelFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() @strawberry_django.filter(models.VLAN, lookups=True) -@autotype_decorator(filtersets.VLANFilterSet) -class VLANFilter(BaseFilterMixin): - pass +class VLANFilter(PrimaryModelFilterMixin): + site: Annotated['SiteFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() + site_id: ID | None = strawberry_django.filter_field() + group: Annotated['VLANGroupFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + group_id: ID | None = strawberry_django.filter_field() + vid: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + name: FilterLookup[str] | None = strawberry_django.filter_field() + tenant: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + tenant_id: ID | None = strawberry_django.filter_field() + status: 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 = ( + strawberry_django.filter_field() + ) + qinq_svlan_id: ID | None = strawberry_django.filter_field() + qinq_cvlan: Annotated['VLANFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + qinq_cvlan_id: ID | None = strawberry_django.filter_field() + qinq_role: Annotated['VLANQinQRoleEnum', strawberry.lazy('ipam.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + l2vpn_terminations: Annotated['L2VPNFilter', strawberry.lazy('vpn.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.VLANGroup, lookups=True) -@autotype_decorator(filtersets.VLANGroupFilterSet) -class VLANGroupFilter(BaseFilterMixin): - pass +class VLANGroupFilter(OrganizationalModelFilterMixin): + scope_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + scope_type_id: ID | None = strawberry_django.filter_field() + scope_id: ID | None = strawberry_django.filter_field() + vid_ranges: Annotated['IntegerArrayLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.VLANTranslationPolicy, lookups=True) -@autotype_decorator(filtersets.VLANTranslationPolicyFilterSet) -class VLANTranslationPolicyFilter(BaseFilterMixin): - pass +class VLANTranslationPolicyFilter(PrimaryModelFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() @strawberry_django.filter(models.VLANTranslationRule, lookups=True) -@autotype_decorator(filtersets.VLANTranslationRuleFilterSet) -class VLANTranslationRuleFilter(BaseFilterMixin): - pass +class VLANTranslationRuleFilter(NetBoxModelFilterMixin): + policy: Annotated['VLANTranslationPolicyFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + policy_id: ID | None = strawberry_django.filter_field() + description: FilterLookup[str] | None = strawberry_django.filter_field() + local_vid: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + remote_vid: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.VRF, lookups=True) -@autotype_decorator(filtersets.VRFFilterSet) -class VRFFilter(BaseFilterMixin): - pass +class VRFFilter(PrimaryModelFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() + rd: FilterLookup[str] | None = strawberry_django.filter_field() + tenant: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + tenant_id: ID | None = strawberry_django.filter_field() + enforce_unique: FilterLookup[bool] | None = strawberry_django.filter_field() + import_targets: Annotated['RouteTargetFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + export_targets: Annotated['RouteTargetFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) diff --git a/netbox/netbox/graphql/enums.py b/netbox/netbox/graphql/enums.py new file mode 100644 index 00000000000..d3d7a66905a --- /dev/null +++ b/netbox/netbox/graphql/enums.py @@ -0,0 +1,120 @@ +from enum import Enum +import strawberry + +__all__ = [ + 'ColorEnum', + 'ButtonColorEnum', + 'ImportMethodEnumEnum', + 'ImportFormatEnum', + 'DistanceUnitEnum', + 'WeightUnitEnum', +] + +# +# Generic color choices +# + + +@strawberry.enum +class ColorEnum(Enum): + COLOR_DARK_RED = 'aa1409' + COLOR_RED = 'f44336' + COLOR_PINK = 'e91e63' + COLOR_ROSE = 'ffe4e1' + COLOR_FUCHSIA = 'ff66ff' + COLOR_PURPLE = '9c27b0' + COLOR_DARK_PURPLE = '673ab7' + COLOR_INDIGO = '3f51b5' + COLOR_BLUE = '2196f3' + COLOR_LIGHT_BLUE = '03a9f4' + COLOR_CYAN = '00bcd4' + COLOR_TEAL = '009688' + COLOR_AQUA = '00ffff' + COLOR_DARK_GREEN = '2f6a31' + COLOR_GREEN = '4caf50' + COLOR_LIGHT_GREEN = '8bc34a' + COLOR_LIME = 'cddc39' + COLOR_YELLOW = 'ffeb3b' + COLOR_AMBER = 'ffc107' + COLOR_ORANGE = 'ff9800' + COLOR_DARK_ORANGE = 'ff5722' + COLOR_BROWN = '795548' + COLOR_LIGHT_GREY = 'c0c0c0' + COLOR_GREY = '9e9e9e' + COLOR_DARK_GREY = '607d8b' + COLOR_BLACK = '111111' + COLOR_WHITE = 'ffffff' + + +# +# Button color choices +# + + +@strawberry.enum +class ButtonColorEnum(Enum): + DEFAULT = 'default' + BLUE = 'blue' + INDIGO = 'indigo' + PURPLE = 'purple' + PINK = 'pink' + RED = 'red' + ORANGE = 'orange' + YELLOW = 'yellow' + GREEN = 'green' + TEAL = 'teal' + CYAN = 'cyan' + GRAY = 'gray' + GREY = 'gray' # Backward compatability for <3.2 + BLACK = 'black' + WHITE = 'white' + + +# +# Import +# + + +@strawberry.enum +class ImportMethodEnumEnum(Enum): + DIRECT = 'direct' + UPLOAD = 'upload' + DATA_FILE = 'datafile' + + +@strawberry.enum +class ImportFormatEnum(Enum): + AUTO = 'auto' + CSV = 'csv' + JSON = 'json' + YAML = 'yaml' + + +# @strawberry.enum +# class CSVDelimiterEnum(Enum): +# AUTO = 'auto' +# COMMA = CSV_DELIMITERS['comma'] +# SEMICOLON = CSV_DELIMITERS['semicolon'] +# TAB = CSV_DELIMITERS['tab'] + + +@strawberry.enum +class DistanceUnitEnum(Enum): + # Metric + UNIT_KILOMETER = 'km' + UNIT_METER = 'm' + + # Imperial + UNIT_MILE = 'mi' + UNIT_FOOT = 'ft' + + +@strawberry.enum +class WeightUnitEnum(Enum): + # Metric + UNIT_KILOGRAM = 'kg' + UNIT_GRAM = 'g' + + # Imperial + UNIT_POUND = 'lb' + UNIT_OUNCE = 'oz' diff --git a/netbox/netbox/graphql/filter_mixins.py b/netbox/netbox/graphql/filter_mixins.py index 2044a1ddeeb..078bf81edc3 100644 --- a/netbox/netbox/graphql/filter_mixins.py +++ b/netbox/netbox/graphql/filter_mixins.py @@ -1,209 +1,114 @@ -from functools import partialmethod -from typing import List +from dataclasses import dataclass +from typing import TypeVar, TYPE_CHECKING, Annotated -import django_filters + +from datetime import datetime import strawberry +from strawberry import ID import strawberry_django -from django.core.exceptions import FieldDoesNotExist -from strawberry import auto +from strawberry_django import FilterLookup, DatetimeFilterLookup -from ipam.fields import ASNField -from netbox.graphql.scalars import BigInt -from utilities.fields import ColorField, CounterCacheField +from extras.models import * from utilities.filters import * - - -def map_strawberry_type(field): - should_create_function = False - attr_type = None - - # NetBox Filter types - put base classes after derived classes - if isinstance(field, ContentTypeFilter): - should_create_function = True - attr_type = str | None - elif isinstance(field, MultiValueArrayFilter): - pass - elif isinstance(field, MultiValueCharFilter): - # Note: Need to use the legacy FilterLookup from filters, not from - # strawberry_django.FilterLookup as we currently have USE_DEPRECATED_FILTERS - attr_type = strawberry_django.filters.FilterLookup[str] | None - elif isinstance(field, MultiValueDateFilter): - attr_type = auto - elif isinstance(field, MultiValueDateTimeFilter): - attr_type = auto - elif isinstance(field, MultiValueDecimalFilter): - pass - elif isinstance(field, MultiValueMACAddressFilter): - should_create_function = True - attr_type = List[str] | None - elif isinstance(field, MultiValueNumberFilter): - should_create_function = True - attr_type = List[str] | None - elif isinstance(field, MultiValueTimeFilter): - pass - elif isinstance(field, MultiValueWWNFilter): - should_create_function = True - attr_type = List[str] | None - elif isinstance(field, NullableCharFieldFilter): - pass - elif isinstance(field, NumericArrayFilter): - should_create_function = True - attr_type = int | None - elif isinstance(field, TreeNodeMultipleChoiceFilter): - should_create_function = True - attr_type = List[str] | None - - # From django_filters - ordering of these matters as base classes must - # come after derived classes so the base class doesn't get matched first - # a pass for the check (no attr_type) means we don't currently handle - # or use that type - elif issubclass(type(field), django_filters.OrderingFilter): - pass - elif issubclass(type(field), django_filters.BaseRangeFilter): - pass - elif issubclass(type(field), django_filters.BaseInFilter): - pass - elif issubclass(type(field), django_filters.LookupChoiceFilter): - pass - elif issubclass(type(field), django_filters.AllValuesMultipleFilter): - pass - elif issubclass(type(field), django_filters.AllValuesFilter): - pass - elif issubclass(type(field), django_filters.TimeRangeFilter): - pass - elif issubclass(type(field), django_filters.IsoDateTimeFromToRangeFilter): - should_create_function = True - attr_type = str | None - elif issubclass(type(field), django_filters.DateTimeFromToRangeFilter): - should_create_function = True - attr_type = str | None - elif issubclass(type(field), django_filters.DateFromToRangeFilter): - should_create_function = True - attr_type = str | None - elif issubclass(type(field), django_filters.DateRangeFilter): - should_create_function = True - attr_type = str | None - elif issubclass(type(field), django_filters.RangeFilter): - pass - elif issubclass(type(field), django_filters.NumericRangeFilter): - pass - elif issubclass(type(field), django_filters.NumberFilter): - should_create_function = True - attr_type = int | None - elif issubclass(type(field), django_filters.ModelMultipleChoiceFilter): - should_create_function = True - attr_type = List[str] | None - elif issubclass(type(field), django_filters.ModelChoiceFilter): - should_create_function = True - attr_type = str | None - elif issubclass(type(field), django_filters.DurationFilter): - pass - elif issubclass(type(field), django_filters.IsoDateTimeFilter): - pass - elif issubclass(type(field), django_filters.DateTimeFilter): - attr_type = auto - elif issubclass(type(field), django_filters.TimeFilter): - attr_type = auto - elif issubclass(type(field), django_filters.DateFilter): - attr_type = auto - elif issubclass(type(field), django_filters.TypedMultipleChoiceFilter): - pass - elif issubclass(type(field), django_filters.MultipleChoiceFilter): - attr_type = str | None - elif issubclass(type(field), django_filters.TypedChoiceFilter): - pass - elif issubclass(type(field), django_filters.ChoiceFilter): - pass - elif issubclass(type(field), django_filters.BooleanFilter): - should_create_function = True - attr_type = bool | None - elif issubclass(type(field), django_filters.UUIDFilter): - should_create_function = True - attr_type = str | None - elif issubclass(type(field), django_filters.CharFilter): - # looks like only used by 'q' - should_create_function = True - attr_type = str | None - - return should_create_function, attr_type - - -def autotype_decorator(filterset): - """ - Decorator used to auto creates a dataclass used by Strawberry based on a filterset. - Must go after the Strawberry decorator as follows: - - @strawberry_django.filter(models.Example, lookups=True) - @autotype_decorator(filtersets.ExampleFilterSet) - class ExampleFilter(BaseFilterMixin): - pass - - The Filter itself must be derived from BaseFilterMixin. For items listed in meta.fields - of the filterset, usually just a type specifier is generated, so for - `fields = [created, ]` the dataclass would be: - - class ExampleFilter(BaseFilterMixin): - created: auto - - For other filter fields a function needs to be created for Strawberry with the - naming convention `filter_{fieldname}` which is auto detected and called by - Strawberry, this function uses the filterset to handle the query. - """ - def create_attribute_and_function(cls, fieldname, attr_type, should_create_function): - if fieldname not in cls.__annotations__ and attr_type: - cls.__annotations__[fieldname] = attr_type - - filter_name = f"filter_{fieldname}" - if should_create_function and not hasattr(cls, filter_name): - filter_by_filterset = getattr(cls, 'filter_by_filterset') - setattr(cls, filter_name, partialmethod(filter_by_filterset, key=fieldname)) - - def wrapper(cls): - cls.filterset = filterset - fields = filterset.get_fields() - model = filterset._meta.model - for fieldname in fields.keys(): - should_create_function = False - attr_type = auto - if fieldname not in cls.__annotations__: - try: - field = model._meta.get_field(fieldname) - except FieldDoesNotExist: - continue - - if isinstance(field, CounterCacheField): - should_create_function = True - attr_type = BigInt | None - elif isinstance(field, ASNField): - should_create_function = True - attr_type = List[str] | None - elif isinstance(field, ColorField): - should_create_function = True - attr_type = List[str] | None - - create_attribute_and_function(cls, fieldname, attr_type, should_create_function) - - declared_filters = filterset.declared_filters - for fieldname, field in declared_filters.items(): - - should_create_function, attr_type = map_strawberry_type(field) - if attr_type is None: - raise NotImplementedError(f"GraphQL Filter field unknown: {fieldname}: {field}") - - create_attribute_and_function(cls, fieldname, attr_type, should_create_function) - - return cls - - return wrapper - - -@strawberry.input -class BaseFilterMixin: - - def filter_by_filterset(self, queryset, key): - filterset = self.filterset(data={key: getattr(self, key)}, queryset=queryset) - if not filterset.is_valid(): - # We could raise validation error but strawberry logs it all to the - # console i.e. raise ValidationError(f"{k}: {v[0]}") - return filterset.qs.none() - return filterset.qs +from core.graphql.filter_lookups import * +from core.graphql.filter_mixins import * +from extras.graphql.filter_mixins import * + +__all__ = [ + 'NetBoxModelFilterMixin', + 'OrganizationalModelFilterMixin', + 'PrimaryModelFilterMixin', + # 'autotype_decorator', + 'NestedGroupModelFilterMixin', + 'ImageAttachmentFilterMixin', + 'WeightFilterMixin', + 'SyncedDataFilterMixin', + 'DistanceFilterMixin', +] + +T = TypeVar('T') + + +if TYPE_CHECKING: + from .enums import * + from core.graphql.filters import * + from tenancy.graphql.filters import * + from extras.graphql.filters import * + + +class NetBoxModelFilterMixin( + ChangeLogFilterMixin, + CustomFieldsFilterMixin, + JournalEntriesFilterMixin, + TagsFilterMixin, + BaseObjectTypeFilterMixin, +): + pass + + +@dataclass +class NestedGroupModelFilterMixin(NetBoxModelFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() + slug: FilterLookup[str] | None = strawberry_django.filter_field() + description: FilterLookup[str] | None = strawberry_django.filter_field() + lft: IntegerLookup | None = strawberry_django.filter_field() + rght: IntegerLookup | None = strawberry_django.filter_field() + tree_id: IntegerLookup | None = strawberry_django.filter_field() + level: IntegerLookup | None = strawberry_django.filter_field() + parent_id: ID | None = strawberry_django.filter_field() + + +@dataclass +class OrganizationalModelFilterMixin( + ChangeLogFilterMixin, + CustomFieldsFilterMixin, + TagsFilterMixin, + BaseObjectTypeFilterMixin, +): + name: FilterLookup[str] | None = strawberry_django.filter_field() + slug: FilterLookup[str] | None = strawberry_django.filter_field() + description: FilterLookup[str] | None = strawberry_django.filter_field() + + +@dataclass +class PrimaryModelFilterMixin(NetBoxModelFilterMixin): + description: FilterLookup[str] | None = strawberry_django.filter_field() + comments: FilterLookup[str] | None = strawberry_django.filter_field() + + +@dataclass +class ImageAttachmentFilterMixin(BaseFilterMixin): + images: Annotated['ImageAttachmentFilter', strawberry.lazy('extras.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + + +@dataclass +class WeightFilterMixin(BaseFilterMixin): + weight: FilterLookup[float] | None = strawberry_django.filter_field() + weight_unit: Annotated['WeightUnitEnum', strawberry.lazy('netbox.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + + +@dataclass +class SyncedDataFilterMixin(BaseFilterMixin): + data_source: Annotated['DataSourceFilter', strawberry.lazy('core.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + data_source_id: FilterLookup[int] | None = strawberry_django.filter_field() + data_file: Annotated['DataFileFilter', strawberry.lazy('core.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + data_file_id: FilterLookup[int] | None = strawberry_django.filter_field() + data_path: FilterLookup[str] | None = strawberry_django.filter_field() + auto_sync_enabled: FilterLookup[bool] | None = strawberry_django.filter_field() + data_synced: DatetimeFilterLookup[datetime] | None = strawberry_django.filter_field() + + +@dataclass +class DistanceFilterMixin(BaseFilterMixin): + distance: FilterLookup[float] | None = strawberry_django.filter_field() + distance_unit: Annotated['DistanceUnitEnum', strawberry.lazy('netbox.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) diff --git a/netbox/netbox/graphql/schema.py b/netbox/netbox/graphql/schema.py index a7609c9d206..625f7cd1b29 100644 --- a/netbox/netbox/graphql/schema.py +++ b/netbox/netbox/graphql/schema.py @@ -38,7 +38,8 @@ class Query( query=Query, config=StrawberryConfig(auto_camel_case=False), extensions=[ - DjangoOptimizerExtension(prefetch_custom_queryset=True), + # DjangoOptimizerExtension(prefetch_custom_queryset=True), + DjangoOptimizerExtension(prefetch_custom_queryset=True, enable_prefetch_related_optimization=False), MaxAliasesLimiter(max_alias_count=settings.GRAPHQL_MAX_ALIASES), ] ) diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index a17bb77305e..ddc7234ed2a 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -776,7 +776,7 @@ def _setting(name, default=None): STRAWBERRY_DJANGO = { "DEFAULT_PK_FIELD_NAME": "id", "TYPE_DESCRIPTION_FROM_MODEL_DOCSTRING": True, - "USE_DEPRECATED_FILTERS": True, + "USE_DEPRECATED_FILTERS": False, } # diff --git a/netbox/netbox/tests/test_graphql.py b/netbox/netbox/tests/test_graphql.py index b04d42d2447..60a8ec5ae25 100644 --- a/netbox/netbox/tests/test_graphql.py +++ b/netbox/netbox/tests/test_graphql.py @@ -99,12 +99,13 @@ def test_graphql_filter_objects(self): # Test OR logic query = """{ location_list( filters: { - status: \"""" + LocationStatusChoices.STATUS_PLANNED + """\", - OR: {status: \"""" + LocationStatusChoices.STATUS_STAGING + """\"} + status: STATUS_PLANNED, + OR: {status: STATUS_STAGING} }) { id site {id} } }""" + print(query) response = self.client.post(url, data={'query': query}, format="json", **self.header) self.assertHttpStatus(response, status.HTTP_200_OK) data = json.loads(response.content) diff --git a/netbox/tenancy/graphql/filter_mixins.py b/netbox/tenancy/graphql/filter_mixins.py new file mode 100644 index 00000000000..dffd5d85bcf --- /dev/null +++ b/netbox/tenancy/graphql/filter_mixins.py @@ -0,0 +1,33 @@ +from dataclasses import dataclass +from typing import Annotated, TYPE_CHECKING +import strawberry +from strawberry import ID +import strawberry_django +from core.graphql.filter_mixins import BaseFilterMixin + +if TYPE_CHECKING: + from .filters import * + from core.graphql.filter_lookups import * + +__all__ = ['TenancyFilterMixin', 'ContactFilterMixin'] + + +@dataclass +class ContactFilterMixin(BaseFilterMixin): + contacts: Annotated['ContactFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + + +@dataclass +class TenancyFilterMixin(BaseFilterMixin): + tenant: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + tenant_id: ID | None = strawberry_django.filter_field() + group: Annotated['TenantGroupFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + group_id: Annotated['TreeNodeFilter', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) diff --git a/netbox/tenancy/graphql/filters.py b/netbox/tenancy/graphql/filters.py index e82b1cd07c8..97074226b3e 100644 --- a/netbox/tenancy/graphql/filters.py +++ b/netbox/tenancy/graphql/filters.py @@ -1,7 +1,28 @@ +from typing import Annotated, TYPE_CHECKING +import strawberry +from strawberry.scalars import ID import strawberry_django +from strawberry_django import ( + FilterLookup, +) +from netbox.graphql.filter_mixins import ( + PrimaryModelFilterMixin, + OrganizationalModelFilterMixin, + NestedGroupModelFilterMixin, +) +from extras.graphql.filter_mixins import CustomFieldsFilterMixin, TagsFilterMixin +from core.graphql.filter_mixins import ChangeLogFilterMixin +from tenancy import models +from .filter_mixins import ContactFilterMixin -from netbox.graphql.filter_mixins import autotype_decorator, BaseFilterMixin -from tenancy import filtersets, models +if TYPE_CHECKING: + from core.graphql.filter_lookups import TreeNodeFilter + from circuits.graphql.filters import * + from dcim.graphql.filters import * + from ipam.graphql.filters import * + from wireless.graphql.filters import * + from virtualization.graphql.filters import * + from vpn.graphql.filters import * __all__ = ( 'TenantFilter', @@ -14,36 +35,127 @@ @strawberry_django.filter(models.Tenant, lookups=True) -@autotype_decorator(filtersets.TenantFilterSet) -class TenantFilter(BaseFilterMixin): - pass +class TenantFilter(PrimaryModelFilterMixin, ContactFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() + slug: FilterLookup[str] | None = strawberry_django.filter_field() + group: Annotated['TenantGroupFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + group_id: Annotated['TreeNodeFilter', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + asns: Annotated['ASNFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field() + circuits: Annotated['CircuitFilter', strawberry.lazy('circuits.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + sites: Annotated['SiteFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() + vlans: Annotated['VLANFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field() + wireless_lans: Annotated['WirelessLANFilter', strawberry.lazy('wireless.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + route_targets: Annotated['RouteTargetFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + locations: Annotated['LocationFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + ip_ranges: Annotated['IPRangeFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + rackreservations: Annotated['RackReservationFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + racks: Annotated['RackFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() + vdcs: Annotated['VirtualDeviceContextFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + prefixes: Annotated['PrefixFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + cables: Annotated['CableFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() + virtual_machines: Annotated['VirtualMachineFilter', strawberry.lazy('virtualization.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + vrfs: Annotated['VRFFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field() + asn_ranges: Annotated['ASNRangeFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + wireless_links: Annotated['WirelessLinkFilter', strawberry.lazy('wireless.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + aggregates: Annotated['AggregateFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + power_feeds: Annotated['PowerFeedFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + devices: Annotated['DeviceFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + tunnels: Annotated['TunnelFilter', strawberry.lazy('vpn.graphql.filters')] | None = strawberry_django.filter_field() + ip_addresses: Annotated['IPAddressFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + clusters: Annotated['ClusterFilter', strawberry.lazy('virtualization.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + l2vpns: Annotated['L2VPNFilter', strawberry.lazy('vpn.graphql.filters')] | None = strawberry_django.filter_field() @strawberry_django.filter(models.TenantGroup, lookups=True) -@autotype_decorator(filtersets.TenantGroupFilterSet) -class TenantGroupFilter(BaseFilterMixin): - pass +class TenantGroupFilter(OrganizationalModelFilterMixin): + parent: Annotated['TenantGroupFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + parent_id: ID | None = strawberry.UNSET + tenants: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + children: Annotated['TenantGroupFilter', strawberry.lazy('tenancy.graphql.filters'), True] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.Contact, lookups=True) -@autotype_decorator(filtersets.ContactFilterSet) -class ContactFilter(BaseFilterMixin): - pass +class ContactFilter(PrimaryModelFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() + title: FilterLookup[str] | None = strawberry_django.filter_field() + phone: FilterLookup[str] | None = strawberry_django.filter_field() + email: FilterLookup[str] | None = strawberry_django.filter_field() + address: FilterLookup[str] | None = strawberry_django.filter_field() + link: FilterLookup[str] | None = strawberry_django.filter_field() + group: Annotated['ContactGroupFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + group_id: Annotated['TreeNodeFilter', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + assignments: Annotated['ContactAssignmentFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.ContactRole, lookups=True) -@autotype_decorator(filtersets.ContactRoleFilterSet) -class ContactRoleFilter(BaseFilterMixin): +class ContactRoleFilter(OrganizationalModelFilterMixin): pass @strawberry_django.filter(models.ContactGroup, lookups=True) -@autotype_decorator(filtersets.ContactGroupFilterSet) -class ContactGroupFilter(BaseFilterMixin): - pass +class ContactGroupFilter(NestedGroupModelFilterMixin): + parent: Annotated['ContactGroupFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.ContactAssignment, lookups=True) -@autotype_decorator(filtersets.ContactAssignmentFilterSet) -class ContactAssignmentFilter(BaseFilterMixin): - pass +class ContactAssignmentFilter(CustomFieldsFilterMixin, TagsFilterMixin, ChangeLogFilterMixin): + object_id: ID | None = strawberry_django.filter_field() + contact: Annotated['ContactFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + contact_id: ID | None = strawberry_django.filter_field() + role: Annotated['ContactRoleFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + role_id: ID | None = strawberry_django.filter_field() + priority: FilterLookup[str] | None = strawberry_django.filter_field() diff --git a/netbox/tenancy/graphql/types.py b/netbox/tenancy/graphql/types.py index 7baa136b37e..9d537a05c85 100644 --- a/netbox/tenancy/graphql/types.py +++ b/netbox/tenancy/graphql/types.py @@ -1,4 +1,4 @@ -from typing import Annotated, List +from typing import Annotated, List, TYPE_CHECKING import strawberry import strawberry_django @@ -9,6 +9,14 @@ from .mixins import ContactAssignmentsMixin from .filters import * +if TYPE_CHECKING: + from circuits.graphql.types import * + from dcim.graphql.types import * + from ipam.graphql.types import * + from wireless.graphql.types import * + from virtualization.graphql.types import * + from vpn.graphql.types import * + __all__ = ( 'ContactAssignmentType', 'ContactGroupType', @@ -23,92 +31,70 @@ # Tenants # -@strawberry_django.type( - models.Tenant, - fields='__all__', - filters=TenantFilter -) + +@strawberry_django.type(models.Tenant, fields='__all__', filters=TenantFilter) class TenantType(NetBoxObjectType): - group: Annotated["TenantGroupType", strawberry.lazy('tenancy.graphql.types')] | None - - asns: List[Annotated["ASNType", strawberry.lazy('ipam.graphql.types')]] - circuits: List[Annotated["CircuitType", strawberry.lazy('circuits.graphql.types')]] - sites: List[Annotated["SiteType", strawberry.lazy('dcim.graphql.types')]] - vlans: List[Annotated["VLANType", strawberry.lazy('ipam.graphql.types')]] - wireless_lans: List[Annotated["WirelessLANType", strawberry.lazy('wireless.graphql.types')]] - route_targets: List[Annotated["RouteTargetType", strawberry.lazy('ipam.graphql.types')]] - locations: List[Annotated["LocationType", strawberry.lazy('dcim.graphql.types')]] - ip_ranges: List[Annotated["IPRangeType", strawberry.lazy('ipam.graphql.types')]] - rackreservations: List[Annotated["RackReservationType", strawberry.lazy('dcim.graphql.types')]] - racks: List[Annotated["RackType", strawberry.lazy('dcim.graphql.types')]] - vdcs: List[Annotated["VirtualDeviceContextType", strawberry.lazy('dcim.graphql.types')]] - prefixes: List[Annotated["PrefixType", strawberry.lazy('ipam.graphql.types')]] - cables: List[Annotated["CableType", strawberry.lazy('dcim.graphql.types')]] - virtual_machines: List[Annotated["VirtualMachineType", strawberry.lazy('virtualization.graphql.types')]] - vrfs: List[Annotated["VRFType", strawberry.lazy('ipam.graphql.types')]] - asn_ranges: List[Annotated["ASNRangeType", strawberry.lazy('ipam.graphql.types')]] - wireless_links: List[Annotated["WirelessLinkType", strawberry.lazy('wireless.graphql.types')]] - aggregates: List[Annotated["AggregateType", strawberry.lazy('ipam.graphql.types')]] - power_feeds: List[Annotated["PowerFeedType", strawberry.lazy('dcim.graphql.types')]] - devices: List[Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')]] - tunnels: List[Annotated["TunnelType", strawberry.lazy('vpn.graphql.types')]] - ip_addresses: List[Annotated["IPAddressType", strawberry.lazy('ipam.graphql.types')]] - clusters: List[Annotated["ClusterType", strawberry.lazy('virtualization.graphql.types')]] - l2vpns: List[Annotated["L2VPNType", strawberry.lazy('vpn.graphql.types')]] - - -@strawberry_django.type( - models.TenantGroup, - fields='__all__', - filters=TenantGroupFilter -) + group: Annotated['TenantGroupType', strawberry.lazy('tenancy.graphql.types')] + contacts: List[Annotated['ContactType', strawberry.lazy('tenancy.graphql.types')]] + asns: List[Annotated['ASNType', strawberry.lazy('ipam.graphql.types')]] + circuits: List[Annotated['CircuitType', strawberry.lazy('circuits.graphql.types')]] + sites: List[Annotated['SiteType', strawberry.lazy('dcim.graphql.types')]] + vlans: List[Annotated['VLANType', strawberry.lazy('ipam.graphql.types')]] + wireless_lans: List[Annotated['WirelessLANType', strawberry.lazy('wireless.graphql.types')]] + route_targets: List[Annotated['RouteTargetType', strawberry.lazy('ipam.graphql.types')]] + locations: List[Annotated['LocationType', strawberry.lazy('dcim.graphql.types')]] + ip_ranges: List[Annotated['IPRangeType', strawberry.lazy('ipam.graphql.types')]] + rackreservations: List[Annotated['RackReservationType', strawberry.lazy('dcim.graphql.types')]] + racks: List[Annotated['RackType', strawberry.lazy('dcim.graphql.types')]] + vdcs: List[Annotated['VirtualDeviceContextType', strawberry.lazy('dcim.graphql.types')]] + prefixes: List[Annotated['PrefixType', strawberry.lazy('ipam.graphql.types')]] + cables: List[Annotated['CableType', strawberry.lazy('dcim.graphql.types')]] + virtual_machines: List[Annotated['VirtualMachineType', strawberry.lazy('virtualization.graphql.types')]] + vrfs: List[Annotated['VRFType', strawberry.lazy('ipam.graphql.types')]] + asn_ranges: List[Annotated['ASNRangeType', strawberry.lazy('ipam.graphql.types')]] + wireless_links: List[Annotated['WirelessLinkType', strawberry.lazy('wireless.graphql.types')]] + aggregates: List[Annotated['AggregateType', strawberry.lazy('ipam.graphql.types')]] + power_feeds: List[Annotated['PowerFeedType', strawberry.lazy('dcim.graphql.types')]] + devices: List[Annotated['DeviceType', strawberry.lazy('dcim.graphql.types')]] + tunnels: List[Annotated['TunnelType', strawberry.lazy('vpn.graphql.types')]] + ip_addresses: List[Annotated['IPAddressType', strawberry.lazy('ipam.graphql.types')]] + clusters: List[Annotated['ClusterType', strawberry.lazy('virtualization.graphql.types')]] + l2vpns: List[Annotated['L2VPNType', strawberry.lazy('vpn.graphql.types')]] + + +@strawberry_django.type(models.TenantGroup, fields='__all__', filters=TenantGroupFilter) class TenantGroupType(OrganizationalObjectType): - parent: Annotated["TenantGroupType", strawberry.lazy('tenancy.graphql.types')] | None + parent: Annotated['TenantGroupType', strawberry.lazy('tenancy.graphql.types')] | None tenants: List[TenantType] - children: List[Annotated["TenantGroupType", strawberry.lazy('tenancy.graphql.types')]] + children: List[Annotated['TenantGroupType', strawberry.lazy('tenancy.graphql.types')]] # # Contacts # -@strawberry_django.type( - models.Contact, - fields='__all__', - filters=ContactFilter -) + +@strawberry_django.type(models.Contact, fields='__all__', filters=ContactFilter) class ContactType(ContactAssignmentsMixin, NetBoxObjectType): - group: Annotated["ContactGroupType", strawberry.lazy('tenancy.graphql.types')] | None + group: Annotated['ContactGroupType', strawberry.lazy('tenancy.graphql.types')] | None -@strawberry_django.type( - models.ContactRole, - fields='__all__', - filters=ContactRoleFilter -) +@strawberry_django.type(models.ContactRole, fields='__all__', filters=ContactRoleFilter) class ContactRoleType(ContactAssignmentsMixin, OrganizationalObjectType): pass -@strawberry_django.type( - models.ContactGroup, - fields='__all__', - filters=ContactGroupFilter -) +@strawberry_django.type(models.ContactGroup, fields='__all__', filters=ContactGroupFilter) class ContactGroupType(OrganizationalObjectType): - parent: Annotated["ContactGroupType", strawberry.lazy('tenancy.graphql.types')] | None + parent: Annotated['ContactGroupType', strawberry.lazy('tenancy.graphql.types')] | None contacts: List[ContactType] - children: List[Annotated["ContactGroupType", strawberry.lazy('tenancy.graphql.types')]] + children: List[Annotated['ContactGroupType', strawberry.lazy('tenancy.graphql.types')]] -@strawberry_django.type( - models.ContactAssignment, - fields='__all__', - filters=ContactAssignmentFilter -) +@strawberry_django.type(models.ContactAssignment, fields='__all__', filters=ContactAssignmentFilter) class ContactAssignmentType(CustomFieldsMixin, TagsMixin, BaseObjectType): - object_type: Annotated["ContentTypeType", strawberry.lazy('netbox.graphql.types')] | None - contact: Annotated["ContactType", strawberry.lazy('tenancy.graphql.types')] | None - role: Annotated["ContactRoleType", strawberry.lazy('tenancy.graphql.types')] | None + object_type: Annotated['ContentTypeType', strawberry.lazy('netbox.graphql.types')] | None + contact: Annotated['ContactType', strawberry.lazy('tenancy.graphql.types')] | None + role: Annotated['ContactRoleType', strawberry.lazy('tenancy.graphql.types')] | None diff --git a/netbox/users/graphql/filters.py b/netbox/users/graphql/filters.py index d30781b1c00..e9d6e51f199 100644 --- a/netbox/users/graphql/filters.py +++ b/netbox/users/graphql/filters.py @@ -1,7 +1,31 @@ +from datetime import datetime +from typing import Annotated, TYPE_CHECKING +import strawberry import strawberry_django +from strawberry_django import ( + FilterLookup, + DatetimeFilterLookup, +) +from core.graphql.filter_mixins import * +from netbox.graphql.filter_mixins import * +from tenancy.graphql.filter_mixins import * + +from users import models -from netbox.graphql.filter_mixins import autotype_decorator, BaseFilterMixin -from users import filtersets, models +if TYPE_CHECKING: + from .enums import * + from netbox.graphql.enums import * + from wireless.graphql.enums import * + from core.graphql.filter_lookups import * + from extras.graphql.filters import * + from circuits.graphql.filters import * + from dcim.graphql.filters import * + from ipam.graphql.filters import * + from tenancy.graphql.filters import * + from wireless.graphql.filters import * + from users.graphql.filters import * + from virtualization.graphql.filters import * + from vpn.graphql.filters import * __all__ = ( 'GroupFilter', @@ -10,12 +34,18 @@ @strawberry_django.filter(models.Group, lookups=True) -@autotype_decorator(filtersets.GroupFilterSet) -class GroupFilter(BaseFilterMixin): - pass +class GroupFilter(BaseObjectTypeFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() + description: FilterLookup[str] | None = strawberry_django.filter_field() @strawberry_django.filter(models.User, lookups=True) -@autotype_decorator(filtersets.UserFilterSet) -class UserFilter(BaseFilterMixin): - pass +class UserFilter(BaseObjectTypeFilterMixin): + username: FilterLookup[str] | None = strawberry_django.filter_field() + first_name: FilterLookup[str] | None = strawberry_django.filter_field() + last_name: FilterLookup[str] | None = strawberry_django.filter_field() + email: FilterLookup[str] | None = strawberry_django.filter_field() + is_staff: FilterLookup[bool] | None = strawberry_django.filter_field() + is_active: FilterLookup[bool] | None = strawberry_django.filter_field() + date_joined: DatetimeFilterLookup[datetime] | None = strawberry_django.filter_field() + groups: Annotated['GroupFilter', strawberry.lazy('users.graphql.filters')] | None = strawberry_django.filter_field() diff --git a/netbox/virtualization/graphql/enums.py b/netbox/virtualization/graphql/enums.py new file mode 100644 index 00000000000..081669f1f21 --- /dev/null +++ b/netbox/virtualization/graphql/enums.py @@ -0,0 +1,33 @@ +from enum import Enum +import strawberry + +__all__ = ['ClusterStatusEnum', 'VirtualMachineStatusEnum'] + + +# +# Clusters +# + +@strawberry.enum +class ClusterStatusEnum(Enum): + + STATUS_PLANNED = 'planned' + STATUS_STAGING = 'staging' + STATUS_ACTIVE = 'active' + STATUS_DECOMMISSIONING = 'decommissioning' + STATUS_OFFLINE = 'offline' + + +# +# VirtualMachines +# + +@strawberry.enum +class VirtualMachineStatusEnum(Enum): + + STATUS_OFFLINE = 'offline' + STATUS_ACTIVE = 'active' + STATUS_PLANNED = 'planned' + STATUS_STAGED = 'staged' + STATUS_FAILED = 'failed' + STATUS_DECOMMISSIONING = 'decommissioning' diff --git a/netbox/virtualization/graphql/filter_mixins.py b/netbox/virtualization/graphql/filter_mixins.py new file mode 100644 index 00000000000..4a7386dbee5 --- /dev/null +++ b/netbox/virtualization/graphql/filter_mixins.py @@ -0,0 +1,23 @@ +from dataclasses import dataclass +from typing import Annotated, TYPE_CHECKING +import strawberry +from strawberry import ID +import strawberry_django +from strawberry_django import FilterLookup + +from netbox.graphql.filter_mixins import NetBoxModelFilterMixin + +if TYPE_CHECKING: + from .filters import * + +__all__ = ['VMComponentFilterMixin'] + + +@dataclass +class VMComponentFilterMixin(NetBoxModelFilterMixin): + virtual_manchine: Annotated['VirtualMachineFilter', strawberry.lazy('virtualization.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + virtual_machine_id: ID | None = strawberry_django.filter_field() + name: FilterLookup[str] | None = strawberry_django.filter_field() + description: FilterLookup[str] | None = strawberry_django.filter_field() diff --git a/netbox/virtualization/graphql/filters.py b/netbox/virtualization/graphql/filters.py index 610275d376d..b3910719da6 100644 --- a/netbox/virtualization/graphql/filters.py +++ b/netbox/virtualization/graphql/filters.py @@ -1,7 +1,33 @@ +from typing import Annotated, TYPE_CHECKING +import strawberry +from strawberry.scalars import ID import strawberry_django +from strawberry_django import ( + FilterLookup, +) +from core.graphql.filter_mixins import * +from netbox.graphql.filter_mixins import * +from tenancy.graphql.filter_mixins import * +from dcim.graphql.filter_mixins import * +from extras.graphql.filter_mixins import * +from virtualization.graphql.filter_mixins import * + +from virtualization import models -from netbox.graphql.filter_mixins import autotype_decorator, BaseFilterMixin -from virtualization import filtersets, models +if TYPE_CHECKING: + from .enums import * + from netbox.graphql.enums import * + from wireless.graphql.enums import * + from core.graphql.filter_lookups import * + from extras.graphql.filters import * + from circuits.graphql.filters import * + from dcim.graphql.filters import * + from ipam.graphql.filters import * + from tenancy.graphql.filters import * + from wireless.graphql.filters import * + from users.graphql.filters import * + from virtualization.graphql.filters import * + from vpn.graphql.filters import * __all__ = ( 'ClusterFilter', @@ -14,36 +40,123 @@ @strawberry_django.filter(models.Cluster, lookups=True) -@autotype_decorator(filtersets.ClusterFilterSet) -class ClusterFilter(BaseFilterMixin): - pass +class ClusterFilter(ContactFilterMixin, PrimaryModelFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() + type: Annotated['ClusterTypeFilter', strawberry.lazy('virtualization.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + type_id: ID | None = strawberry_django.filter_field() + group: Annotated['ClusterGroupFilter', strawberry.lazy('virtualization.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + group_id: ID | None = strawberry_django.filter_field() + status: Annotated['ClusterStatusEnum', strawberry.lazy('virtualization.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + tenant: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + tenant_id: ID | None = strawberry_django.filter_field() + vlan_groups: Annotated['VLANGroupFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.ClusterGroup, lookups=True) -@autotype_decorator(filtersets.ClusterGroupFilterSet) -class ClusterGroupFilter(BaseFilterMixin): - pass +class ClusterGroupFilter(ContactFilterMixin, OrganizationalModelFilterMixin): + vlan_groups: Annotated['VLANGroupFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.ClusterType, lookups=True) -@autotype_decorator(filtersets.ClusterTypeFilterSet) -class ClusterTypeFilter(BaseFilterMixin): +class ClusterTypeFilter(OrganizationalModelFilterMixin): pass @strawberry_django.filter(models.VirtualMachine, lookups=True) -@autotype_decorator(filtersets.VirtualMachineFilterSet) -class VirtualMachineFilter(BaseFilterMixin): - pass +class VirtualMachineFilter( + ContactFilterMixin, + ImageAttachmentFilterMixin, + RenderConfigFilterMixin, + ConfigContextFilterMixin, + PrimaryModelFilterMixin, +): + site: Annotated['SiteFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() + site_id: ID | None = strawberry_django.filter_field() + cluster: Annotated['ClusterFilter', strawberry.lazy('virtualization.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + cluster_id: ID | None = strawberry_django.filter_field() + device: Annotated['DeviceFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() + device_id: ID | None = strawberry_django.filter_field() + tenant: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + tenant_id: ID | None = strawberry_django.filter_field() + platform: Annotated['PlatformFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + platform_id: ID | None = strawberry_django.filter_field() + status: Annotated['VirtualMachineStatusEnum', strawberry.lazy('virtualization.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + role: Annotated['DeviceRoleFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + role_id: ID | None = strawberry_django.filter_field() + primary_ip4: Annotated['IPAddressFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + primary_ip4_id: ID | None = strawberry_django.filter_field() + primary_ip6: Annotated['IPAddressFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + primary_ip6_id: ID | None = strawberry_django.filter_field() + vcpus: Annotated['FloatLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + memory: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + disk: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + serial: FilterLookup[str] | None = strawberry_django.filter_field() + interfaces: Annotated['VMInterfaceFilter', strawberry.lazy('virtualization.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + services: Annotated['ServiceFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + virtual_disks: Annotated['VirtualDiskFilter', strawberry.lazy('virtualization.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.VMInterface, lookups=True) -@autotype_decorator(filtersets.VMInterfaceFilterSet) -class VMInterfaceFilter(BaseFilterMixin): - pass +class VMInterfaceFilter(VMComponentFilterMixin, InterfaceBaseFilterMixin): + ip_addresses: Annotated['IPAddressFilter', strawberry.lazy('ipam.graphql.filters')] | 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() + fhrp_group_assignments: Annotated['FHRPGroupAssignmentFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + tunnel_terminations: Annotated['TunnelTerminationFilter', strawberry.lazy('vpn.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + l2vpn_terminations: Annotated['L2VPNFilter', strawberry.lazy('vpn.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + mac_addresses: Annotated['MACAddressFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.VirtualDisk, lookups=True) -@autotype_decorator(filtersets.VirtualDiskFilterSet) -class VirtualDiskFilter(BaseFilterMixin): - pass +class VirtualDiskFilter(VMComponentFilterMixin): + size: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) diff --git a/netbox/vpn/graphql/enums.py b/netbox/vpn/graphql/enums.py new file mode 100644 index 00000000000..7b66818adfd --- /dev/null +++ b/netbox/vpn/graphql/enums.py @@ -0,0 +1,161 @@ +from enum import Enum +import strawberry + +__all__ = [ + 'TunnelStatusEnum', + 'TunnelEncapsulationEnum', + 'TunnelTerminationTypeEnum', + 'TunnelTerminationRoleEnum', + 'IKEVersionEnum', + 'IKEModeEnum', + 'AuthenticationMethodEnum', + 'IPSecModeEnum', + 'EncryptionAlgorithmEnum', + 'AuthenticationAlgorithmEnum', + 'DHGroupEnum', + 'L2VPNTypeEnum', +] + +# +# Tunnels +# + + +@strawberry.enum +class TunnelStatusEnum(Enum): + key = 'Tunnel.status' + + STATUS_PLANNED = 'planned' + STATUS_ACTIVE = 'active' + STATUS_DISABLED = 'disabled' + + +@strawberry.enum +class TunnelEncapsulationEnum(Enum): + ENCAP_GRE = 'gre' + ENCAP_IPSEC_TRANSPORT = 'ipsec-transport' + ENCAP_IPSEC_TUNNEL = 'ipsec-tunnel' + ENCAP_IP_IP = 'ip-ip' + ENCAP_L2TP = 'l2tp' + ENCAP_OPENVPN = 'openvpn' + ENCAP_PPTP = 'pptp' + ENCAP_WIREGUARD = 'wireguard' + + +@strawberry.enum +class TunnelTerminationTypeEnum(Enum): + # For TunnelCreateForm + TYPE_DEVICE = 'dcim.device' + TYPE_VIRTUALMACHINE = 'virtualization.virtualmachine' + + +@strawberry.enum +class TunnelTerminationRoleEnum(Enum): + ROLE_PEER = 'peer' + ROLE_HUB = 'hub' + ROLE_SPOKE = 'spoke' + + +# +# Crypto +# + + +@strawberry.enum +class IKEVersionEnum(Enum): + VERSION_1 = 1 + VERSION_2 = 2 + + +@strawberry.enum +class IKEModeEnum(Enum): + AGGRESSIVE = 'aggressive' + MAIN = 'main' + + +@strawberry.enum +class AuthenticationMethodEnum(Enum): + PRESHARED_KEYS = 'preshared-keys' + CERTIFICATES = 'certificates' + RSA_SIGNATURES = 'rsa-signatures' + DSA_SIGNATURES = 'dsa-signatures' + + +@strawberry.enum +class IPSecModeEnum(Enum): + ESP = 'esp' + AH = 'ah' + + +@strawberry.enum +class EncryptionAlgorithmEnum(Enum): + ENCRYPTION_AES128_CBC = 'aes-128-cbc' + ENCRYPTION_AES128_GCM = 'aes-128-gcm' + ENCRYPTION_AES192_CBC = 'aes-192-cbc' + ENCRYPTION_AES192_GCM = 'aes-192-gcm' + ENCRYPTION_AES256_CBC = 'aes-256-cbc' + ENCRYPTION_AES256_GCM = 'aes-256-gcm' + ENCRYPTION_3DES = '3des-cbc' + ENCRYPTION_DES = 'des-cbc' + + +@strawberry.enum +class AuthenticationAlgorithmEnum(Enum): + AUTH_HMAC_SHA1 = 'hmac-sha1' + AUTH_HMAC_SHA256 = 'hmac-sha256' + AUTH_HMAC_SHA384 = 'hmac-sha384' + AUTH_HMAC_SHA512 = 'hmac-sha512' + AUTH_HMAC_MD5 = 'hmac-md5' + + +@strawberry.enum +class DHGroupEnum(Enum): + # https://www.iana.org/assignments/ikev2-parameters/ikev2-parameters.xhtml#ikev2-parameters-8 + GROUP_1 = 1 # 768-bit MODP + GROUP_2 = 2 # 1024-but MODP + # Groups 3-4 reserved + GROUP_5 = 5 # 1536-bit MODP + # Groups 6-13 unassigned + GROUP_14 = 14 # 2048-bit MODP + GROUP_15 = 15 # 3072-bit MODP + GROUP_16 = 16 # 4096-bit MODP + GROUP_17 = 17 # 6144-bit MODP + GROUP_18 = 18 # 8192-bit MODP + GROUP_19 = 19 # 256-bit random ECP + GROUP_20 = 20 # 384-bit random ECP + GROUP_21 = 21 # 521-bit random ECP (521 is not a typo) + GROUP_22 = 22 # 1024-bit MODP w/160-bit prime + GROUP_23 = 23 # 2048-bit MODP w/224-bit prime + GROUP_24 = 24 # 2048-bit MODP w/256-bit prime + GROUP_25 = 25 # 192-bit ECP + GROUP_26 = 26 # 224-bit ECP + GROUP_27 = 27 # brainpoolP224r1 + GROUP_28 = 28 # brainpoolP256r1 + GROUP_29 = 29 # brainpoolP384r1 + GROUP_30 = 30 # brainpoolP512r1 + GROUP_31 = 31 # Curve25519 + GROUP_32 = 32 # Curve448 + GROUP_33 = 33 # GOST3410_2012_256 + GROUP_34 = 34 # GOST3410_2012_512 + + +# +# L2VPN +# + + +@strawberry.enum +class L2VPNTypeEnum(Enum): + TYPE_VPLS = 'vpls' + TYPE_VPWS = 'vpws' + TYPE_EPL = 'epl' + TYPE_EVPL = 'evpl' + TYPE_EPLAN = 'ep-lan' + TYPE_EVPLAN = 'evp-lan' + TYPE_EPTREE = 'ep-tree' + TYPE_EVPTREE = 'evp-tree' + TYPE_VXLAN = 'vxlan' + TYPE_VXLAN_EVPN = 'vxlan-evpn' + TYPE_MPLS_EVPN = 'mpls-evpn' + TYPE_PBB_EVPN = 'pbb-evpn' + TYPE_EVPN_VPWS = 'evpn-vpws' diff --git a/netbox/vpn/graphql/filters.py b/netbox/vpn/graphql/filters.py index 34594458b9f..f910ce18ab1 100644 --- a/netbox/vpn/graphql/filters.py +++ b/netbox/vpn/graphql/filters.py @@ -1,7 +1,34 @@ +from typing import Annotated, TYPE_CHECKING +import strawberry +from strawberry.scalars import ID import strawberry_django +from strawberry_django import ( + FilterLookup, +) +from extras.graphql.filter_mixins import * +from netbox.graphql.filter_mixins import * +from core.graphql.filter_mixins import * +from tenancy.graphql.filter_mixins import * +# from .filter_mixins import * + +from vpn import models + +if TYPE_CHECKING: + from .enums import * + from netbox.graphql.enums import * + from wireless.graphql.enums import * + from core.graphql.filter_lookups import * + from core.graphql.filters import * + from extras.graphql.filters import * + from circuits.graphql.filters import * + from dcim.graphql.filters import * + from ipam.graphql.filters import * + from tenancy.graphql.filters import * + from wireless.graphql.filters import * + from users.graphql.filters import * + from virtualization.graphql.filters import * + from vpn.graphql.filters import * -from netbox.graphql.filter_mixins import autotype_decorator, BaseFilterMixin -from vpn import filtersets, models __all__ = ( 'TunnelGroupFilter', @@ -18,60 +45,151 @@ @strawberry_django.filter(models.TunnelGroup, lookups=True) -@autotype_decorator(filtersets.TunnelGroupFilterSet) -class TunnelGroupFilter(BaseFilterMixin): +class TunnelGroupFilter(OrganizationalModelFilterMixin): pass @strawberry_django.filter(models.TunnelTermination, lookups=True) -@autotype_decorator(filtersets.TunnelTerminationFilterSet) -class TunnelTerminationFilter(BaseFilterMixin): - pass +class TunnelTerminationFilter( + BaseObjectTypeFilterMixin, CustomFieldsFilterMixin, TagsFilterMixin, ChangeLogFilterMixin +): + tunnel: Annotated['TunnelFilter', strawberry.lazy('vpn.graphql.filters')] | None = strawberry_django.filter_field() + tunnel_id: ID | None = strawberry_django.filter_field() + role: Annotated['TunnelTerminationRoleEnum', strawberry.lazy('vpn.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + termination_type: Annotated['TunnelTerminationTypeEnum', strawberry.lazy('vpn.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + termination_type_id: ID | None = strawberry_django.filter_field() + termination_id: ID | None = strawberry_django.filter_field() + outside_ip: Annotated['IPAddressFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + outside_ip_id: ID | None = strawberry_django.filter_field() @strawberry_django.filter(models.Tunnel, lookups=True) -@autotype_decorator(filtersets.TunnelFilterSet) -class TunnelFilter(BaseFilterMixin): - pass +class TunnelFilter(PrimaryModelFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() + status: Annotated['TunnelStatusEnum', strawberry.lazy('vpn.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + group: Annotated['TunnelGroupFilter', strawberry.lazy('vpn.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + group_id: ID | None = strawberry_django.filter_field() + encapsulation: Annotated['TunnelEncapsulationEnum', strawberry.lazy('vpn.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + ipsec_profile: Annotated['IPSecProfileFilter', strawberry.lazy('vpn.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + tenant: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + tenant_id: ID | None = strawberry_django.filter_field() + tunnel_id: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.IKEProposal, lookups=True) -@autotype_decorator(filtersets.IKEProposalFilterSet) -class IKEProposalFilter(BaseFilterMixin): - pass +class IKEProposalFilter(PrimaryModelFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() + authentication_method: Annotated['AuthenticationMethodEnum', strawberry.lazy('vpn.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + encryption_algorithm: Annotated['EncryptionAlgorithmEnum', strawberry.lazy('vpn.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + authentication_algorithm: Annotated['AuthenticationAlgorithmEnum', strawberry.lazy('vpn.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + group: Annotated['DHGroupEnum', strawberry.lazy('vpn.graphql.enums')] | None = strawberry_django.filter_field() + sa_lifetime: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.IKEPolicy, lookups=True) -@autotype_decorator(filtersets.IKEPolicyFilterSet) -class IKEPolicyFilter(BaseFilterMixin): - pass +class IKEPolicyFilter(PrimaryModelFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() + version: Annotated['IKEVersionEnum', strawberry.lazy('vpn.graphql.enums')] | None = strawberry_django.filter_field() + mode: Annotated['IKEModeEnum', strawberry.lazy('vpn.graphql.enums')] | None = strawberry_django.filter_field() + proposals: Annotated['IKEProposalFilter', strawberry.lazy('vpn.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + preshared_key: FilterLookup[str] | None = strawberry_django.filter_field() @strawberry_django.filter(models.IPSecProposal, lookups=True) -@autotype_decorator(filtersets.IPSecProposalFilterSet) -class IPSecProposalFilter(BaseFilterMixin): - pass +class IPSecProposalFilter(PrimaryModelFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() + encryption_algorithm: Annotated['EncryptionAlgorithmEnum', strawberry.lazy('vpn.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + authentication_algorithm: Annotated['AuthenticationAlgorithmEnum', strawberry.lazy('vpn.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + sa_lifetime_seconds: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + sa_lifetime_data: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) @strawberry_django.filter(models.IPSecPolicy, lookups=True) -@autotype_decorator(filtersets.IPSecPolicyFilterSet) -class IPSecPolicyFilter(BaseFilterMixin): - pass +class IPSecPolicyFilter(PrimaryModelFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() + proposals: Annotated['IPSecProposalFilter', strawberry.lazy('vpn.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + pfs_group: Annotated['DHGroupEnum', strawberry.lazy('vpn.graphql.enums')] | None = strawberry_django.filter_field() @strawberry_django.filter(models.IPSecProfile, lookups=True) -@autotype_decorator(filtersets.IPSecProfileFilterSet) -class IPSecProfileFilter(BaseFilterMixin): - pass +class IPSecProfileFilter(PrimaryModelFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() + mode: Annotated['IPSecModeEnum', strawberry.lazy('vpn.graphql.enums')] | None = strawberry_django.filter_field() + ike_policy: Annotated['IKEPolicyFilter', strawberry.lazy('vpn.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + ike_policy_id: ID | None = strawberry_django.filter_field() + ipsec_policy: Annotated['IPSecPolicyFilter', strawberry.lazy('vpn.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + ipsec_policy_id: ID | None = strawberry_django.filter_field() @strawberry_django.filter(models.L2VPN, lookups=True) -@autotype_decorator(filtersets.L2VPNFilterSet) -class L2VPNFilter(BaseFilterMixin): - pass +class L2VPNFilter(ContactFilterMixin, PrimaryModelFilterMixin): + name: FilterLookup[str] | None = strawberry_django.filter_field() + slug: FilterLookup[str] | None = strawberry_django.filter_field() + type: Annotated['L2VPNTypeEnum', strawberry.lazy('vpn.graphql.enums')] | None = strawberry_django.filter_field() + identifier: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) + import_targets: Annotated['RouteTargetFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + export_targets: Annotated['RouteTargetFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + tenant: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + tenant_id: ID | None = strawberry_django.filter_field() @strawberry_django.filter(models.L2VPNTermination, lookups=True) -@autotype_decorator(filtersets.L2VPNTerminationFilterSet) -class L2VPNTerminationFilter(BaseFilterMixin): - pass +class L2VPNTerminationFilter(NetBoxModelFilterMixin): + l2vpn: Annotated['L2VPNFilter', strawberry.lazy('vpn.graphql.filters')] | None = strawberry_django.filter_field() + l2vpn_id: ID | None = strawberry_django.filter_field() + assigned_object_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + assigned_object_id: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + strawberry_django.filter_field() + ) diff --git a/netbox/wireless/graphql/enums.py b/netbox/wireless/graphql/enums.py new file mode 100644 index 00000000000..1526b2ff3b9 --- /dev/null +++ b/netbox/wireless/graphql/enums.py @@ -0,0 +1,249 @@ +from enum import Enum +import strawberry + +__all__ = [ + 'WirelessRoleEnum', + 'WirelessLANStatusEnum', + 'WirelessChannelEnum', + 'WirelessAuthTypeEnum', + 'WirelessAuthCipherEnum', +] + + +@strawberry.enum +class WirelessRoleEnum(Enum): + ROLE_AP = 'ap' + ROLE_STATION = 'station' + + +@strawberry.enum +class WirelessLANStatusEnum(Enum): + key = 'WirelessLAN.status' + + STATUS_ACTIVE = 'active' + STATUS_RESERVED = 'reserved' + STATUS_DISABLED = 'disabled' + STATUS_DEPRECATED = 'deprecated' + + +@strawberry.enum +class WirelessChannelEnum(Enum): + # 2.4 GHz + CHANNEL_24G_1 = '2.4g-1-2412-22' + CHANNEL_24G_2 = '2.4g-2-2417-22' + CHANNEL_24G_3 = '2.4g-3-2422-22' + CHANNEL_24G_4 = '2.4g-4-2427-22' + CHANNEL_24G_5 = '2.4g-5-2432-22' + CHANNEL_24G_6 = '2.4g-6-2437-22' + CHANNEL_24G_7 = '2.4g-7-2442-22' + CHANNEL_24G_8 = '2.4g-8-2447-22' + CHANNEL_24G_9 = '2.4g-9-2452-22' + CHANNEL_24G_10 = '2.4g-10-2457-22' + CHANNEL_24G_11 = '2.4g-11-2462-22' + CHANNEL_24G_12 = '2.4g-12-2467-22' + CHANNEL_24G_13 = '2.4g-13-2472-22' + + # 5 GHz + CHANNEL_5G_32 = '5g-32-5160-20' + CHANNEL_5G_34 = '5g-34-5170-40' + CHANNEL_5G_36 = '5g-36-5180-20' + CHANNEL_5G_38 = '5g-38-5190-40' + CHANNEL_5G_40 = '5g-40-5200-20' + CHANNEL_5G_42 = '5g-42-5210-80' + CHANNEL_5G_44 = '5g-44-5220-20' + CHANNEL_5G_46 = '5g-46-5230-40' + CHANNEL_5G_48 = '5g-48-5240-20' + CHANNEL_5G_50 = '5g-50-5250-160' + CHANNEL_5G_52 = '5g-52-5260-20' + CHANNEL_5G_54 = '5g-54-5270-40' + CHANNEL_5G_56 = '5g-56-5280-20' + CHANNEL_5G_58 = '5g-58-5290-80' + CHANNEL_5G_60 = '5g-60-5300-20' + CHANNEL_5G_62 = '5g-62-5310-40' + CHANNEL_5G_64 = '5g-64-5320-20' + CHANNEL_5G_100 = '5g-100-5500-20' + CHANNEL_5G_102 = '5g-102-5510-40' + CHANNEL_5G_104 = '5g-104-5520-20' + CHANNEL_5G_106 = '5g-106-5530-80' + CHANNEL_5G_108 = '5g-108-5540-20' + CHANNEL_5G_110 = '5g-110-5550-40' + CHANNEL_5G_112 = '5g-112-5560-20' + CHANNEL_5G_114 = '5g-114-5570-160' + CHANNEL_5G_116 = '5g-116-5580-20' + CHANNEL_5G_118 = '5g-118-5590-40' + CHANNEL_5G_120 = '5g-120-5600-20' + CHANNEL_5G_122 = '5g-122-5610-80' + CHANNEL_5G_124 = '5g-124-5620-20' + CHANNEL_5G_126 = '5g-126-5630-40' + CHANNEL_5G_128 = '5g-128-5640-20' + CHANNEL_5G_132 = '5g-132-5660-20' + CHANNEL_5G_134 = '5g-134-5670-40' + CHANNEL_5G_136 = '5g-136-5680-20' + CHANNEL_5G_138 = '5g-138-5690-80' + CHANNEL_5G_140 = '5g-140-5700-20' + CHANNEL_5G_142 = '5g-142-5710-40' + CHANNEL_5G_144 = '5g-144-5720-20' + CHANNEL_5G_149 = '5g-149-5745-20' + CHANNEL_5G_151 = '5g-151-5755-40' + CHANNEL_5G_153 = '5g-153-5765-20' + CHANNEL_5G_155 = '5g-155-5775-80' + CHANNEL_5G_157 = '5g-157-5785-20' + CHANNEL_5G_159 = '5g-159-5795-40' + CHANNEL_5G_161 = '5g-161-5805-20' + CHANNEL_5G_163 = '5g-163-5815-160' + CHANNEL_5G_165 = '5g-165-5825-20' + CHANNEL_5G_167 = '5g-167-5835-40' + CHANNEL_5G_169 = '5g-169-5845-20' + CHANNEL_5G_171 = '5g-171-5855-80' + CHANNEL_5G_173 = '5g-173-5865-20' + CHANNEL_5G_175 = '5g-175-5875-40' + CHANNEL_5G_177 = '5g-177-5885-20' + + # 6 GHz + CHANNEL_6G_1 = '6g-1-5955-20' + CHANNEL_6G_3 = '6g-3-5965-40' + CHANNEL_6G_5 = '6g-5-5975-20' + CHANNEL_6G_7 = '6g-7-5985-80' + CHANNEL_6G_9 = '6g-9-5995-20' + CHANNEL_6G_11 = '6g-11-6005-40' + CHANNEL_6G_13 = '6g-13-6015-20' + CHANNEL_6G_15 = '6g-15-6025-160' + CHANNEL_6G_17 = '6g-17-6035-20' + CHANNEL_6G_19 = '6g-19-6045-40' + CHANNEL_6G_21 = '6g-21-6055-20' + CHANNEL_6G_23 = '6g-23-6065-80' + CHANNEL_6G_25 = '6g-25-6075-20' + CHANNEL_6G_27 = '6g-27-6085-40' + CHANNEL_6G_29 = '6g-29-6095-20' + CHANNEL_6G_31 = '6g-31-6105-320' + CHANNEL_6G_33 = '6g-33-6115-20' + CHANNEL_6G_35 = '6g-35-6125-40' + CHANNEL_6G_37 = '6g-37-6135-20' + CHANNEL_6G_39 = '6g-39-6145-80' + CHANNEL_6G_41 = '6g-41-6155-20' + CHANNEL_6G_43 = '6g-43-6165-40' + CHANNEL_6G_45 = '6g-45-6175-20' + CHANNEL_6G_47 = '6g-47-6185-160' + CHANNEL_6G_49 = '6g-49-6195-20' + CHANNEL_6G_51 = '6g-51-6205-40' + CHANNEL_6G_53 = '6g-53-6215-20' + CHANNEL_6G_55 = '6g-55-6225-80' + CHANNEL_6G_57 = '6g-57-6235-20' + CHANNEL_6G_59 = '6g-59-6245-40' + CHANNEL_6G_61 = '6g-61-6255-20' + CHANNEL_6G_65 = '6g-65-6275-20' + CHANNEL_6G_67 = '6g-67-6285-40' + CHANNEL_6G_69 = '6g-69-6295-20' + CHANNEL_6G_71 = '6g-71-6305-80' + CHANNEL_6G_73 = '6g-73-6315-20' + CHANNEL_6G_75 = '6g-75-6325-40' + CHANNEL_6G_77 = '6g-77-6335-20' + CHANNEL_6G_79 = '6g-79-6345-160' + CHANNEL_6G_81 = '6g-81-6355-20' + CHANNEL_6G_83 = '6g-83-6365-40' + CHANNEL_6G_85 = '6g-85-6375-20' + CHANNEL_6G_87 = '6g-87-6385-80' + CHANNEL_6G_89 = '6g-89-6395-20' + CHANNEL_6G_91 = '6g-91-6405-40' + CHANNEL_6G_93 = '6g-93-6415-20' + CHANNEL_6G_95 = '6g-95-6425-320' + CHANNEL_6G_97 = '6g-97-6435-20' + CHANNEL_6G_99 = '6g-99-6445-40' + CHANNEL_6G_101 = '6g-101-6455-20' + CHANNEL_6G_103 = '6g-103-6465-80' + CHANNEL_6G_105 = '6g-105-6475-20' + CHANNEL_6G_107 = '6g-107-6485-40' + CHANNEL_6G_109 = '6g-109-6495-20' + CHANNEL_6G_111 = '6g-111-6505-160' + CHANNEL_6G_113 = '6g-113-6515-20' + CHANNEL_6G_115 = '6g-115-6525-40' + CHANNEL_6G_117 = '6g-117-6535-20' + CHANNEL_6G_119 = '6g-119-6545-80' + CHANNEL_6G_121 = '6g-121-6555-20' + CHANNEL_6G_123 = '6g-123-6565-40' + CHANNEL_6G_125 = '6g-125-6575-20' + CHANNEL_6G_129 = '6g-129-6595-20' + CHANNEL_6G_131 = '6g-131-6605-40' + CHANNEL_6G_133 = '6g-133-6615-20' + CHANNEL_6G_135 = '6g-135-6625-80' + CHANNEL_6G_137 = '6g-137-6635-20' + CHANNEL_6G_139 = '6g-139-6645-40' + CHANNEL_6G_141 = '6g-141-6655-20' + CHANNEL_6G_143 = '6g-143-6665-160' + CHANNEL_6G_145 = '6g-145-6675-20' + CHANNEL_6G_147 = '6g-147-6685-40' + CHANNEL_6G_149 = '6g-149-6695-20' + CHANNEL_6G_151 = '6g-151-6705-80' + CHANNEL_6G_153 = '6g-153-6715-20' + CHANNEL_6G_155 = '6g-155-6725-40' + CHANNEL_6G_157 = '6g-157-6735-20' + CHANNEL_6G_159 = '6g-159-6745-320' + CHANNEL_6G_161 = '6g-161-6755-20' + CHANNEL_6G_163 = '6g-163-6765-40' + CHANNEL_6G_165 = '6g-165-6775-20' + CHANNEL_6G_167 = '6g-167-6785-80' + CHANNEL_6G_169 = '6g-169-6795-20' + CHANNEL_6G_171 = '6g-171-6805-40' + CHANNEL_6G_173 = '6g-173-6815-20' + CHANNEL_6G_175 = '6g-175-6825-160' + CHANNEL_6G_177 = '6g-177-6835-20' + CHANNEL_6G_179 = '6g-179-6845-40' + CHANNEL_6G_181 = '6g-181-6855-20' + CHANNEL_6G_183 = '6g-183-6865-80' + CHANNEL_6G_185 = '6g-185-6875-20' + CHANNEL_6G_187 = '6g-187-6885-40' + CHANNEL_6G_189 = '6g-189-6895-20' + CHANNEL_6G_193 = '6g-193-6915-20' + CHANNEL_6G_195 = '6g-195-6925-40' + CHANNEL_6G_197 = '6g-197-6935-20' + CHANNEL_6G_199 = '6g-199-6945-80' + CHANNEL_6G_201 = '6g-201-6955-20' + CHANNEL_6G_203 = '6g-203-6965-40' + CHANNEL_6G_205 = '6g-205-6975-20' + CHANNEL_6G_207 = '6g-207-6985-160' + CHANNEL_6G_209 = '6g-209-6995-20' + CHANNEL_6G_211 = '6g-211-7005-40' + CHANNEL_6G_213 = '6g-213-7015-20' + CHANNEL_6G_215 = '6g-215-7025-80' + CHANNEL_6G_217 = '6g-217-7035-20' + CHANNEL_6G_219 = '6g-219-7045-40' + CHANNEL_6G_221 = '6g-221-7055-20' + CHANNEL_6G_225 = '6g-225-7075-20' + CHANNEL_6G_227 = '6g-227-7085-40' + CHANNEL_6G_229 = '6g-229-7095-20' + CHANNEL_6G_233 = '6g-233-7115-20' + + # 60 GHz + CHANNEL_60G_1 = '60g-1-58320-2160' + CHANNEL_60G_2 = '60g-2-60480-2160' + CHANNEL_60G_3 = '60g-3-62640-2160' + CHANNEL_60G_4 = '60g-4-64800-2160' + CHANNEL_60G_5 = '60g-5-66960-2160' + CHANNEL_60G_6 = '60g-6-69120-2160' + CHANNEL_60G_9 = '60g-9-59400-4320' + CHANNEL_60G_10 = '60g-10-61560-4320' + CHANNEL_60G_11 = '60g-11-63720-4320' + CHANNEL_60G_12 = '60g-12-65880-4320' + CHANNEL_60G_13 = '60g-13-68040-4320' + CHANNEL_60G_17 = '60g-17-60480-6480' + CHANNEL_60G_18 = '60g-18-62640-6480' + CHANNEL_60G_19 = '60g-19-64800-6480' + CHANNEL_60G_20 = '60g-20-66960-6480' + CHANNEL_60G_25 = '60g-25-61560-6480' + CHANNEL_60G_26 = '60g-26-63720-6480' + CHANNEL_60G_27 = '60g-27-65880-6480' + + +@strawberry.enum +class WirelessAuthTypeEnum(Enum): + TYPE_OPEN = 'open' + TYPE_WEP = 'wep' + TYPE_WPA_PERSONAL = 'wpa-personal' + TYPE_WPA_ENTERPRISE = 'wpa-enterprise' + + +@strawberry.enum +class WirelessAuthCipherEnum(Enum): + CIPHER_AUTO = 'auto' + CIPHER_TKIP = 'tkip' + CIPHER_AES = 'aes' diff --git a/netbox/wireless/graphql/filter_mixins.py b/netbox/wireless/graphql/filter_mixins.py new file mode 100644 index 00000000000..2028bba4ed8 --- /dev/null +++ b/netbox/wireless/graphql/filter_mixins.py @@ -0,0 +1,24 @@ +from dataclasses import dataclass +from typing import Annotated, TYPE_CHECKING +import strawberry +import strawberry_django +from strawberry_django import FilterLookup + +from core.graphql.filter_mixins import BaseFilterMixin + +if TYPE_CHECKING: + from .filters import * + from .enums import * + +__all__ = ['WirelessAuthenticationBaseFilterMixin'] + + +@dataclass +class WirelessAuthenticationBaseFilterMixin(BaseFilterMixin): + auth_type: Annotated['WirelessAuthTypeEnum', strawberry.lazy('wireless.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + auth_cipher: Annotated['WirelessAuthCipherEnum', strawberry.lazy('wireless.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + auth_psk: FilterLookup[str] | None = strawberry_django.filter_field() diff --git a/netbox/wireless/graphql/filters.py b/netbox/wireless/graphql/filters.py index 47d04bedcbc..890991a4dfc 100644 --- a/netbox/wireless/graphql/filters.py +++ b/netbox/wireless/graphql/filters.py @@ -1,7 +1,32 @@ +from typing import Annotated, TYPE_CHECKING +import strawberry +from strawberry.scalars import ID import strawberry_django +from strawberry_django import ( + FilterLookup, +) +from extras.graphql.filter_mixins import * +from netbox.graphql.filter_mixins import * +from core.graphql.filter_mixins import * +from tenancy.graphql.filter_mixins import * +from .filter_mixins import * + +from wireless import models -from netbox.graphql.filter_mixins import autotype_decorator, BaseFilterMixin -from wireless import filtersets, models +if TYPE_CHECKING: + from .enums import * + from netbox.graphql.enums import * + from wireless.graphql.enums import * + from core.graphql.filter_lookups import * + from extras.graphql.filters import * + from circuits.graphql.filters import * + from dcim.graphql.filters import * + from ipam.graphql.filters import * + from tenancy.graphql.filters import * + from wireless.graphql.filters import * + from users.graphql.filters import * + from virtualization.graphql.filters import * + from vpn.graphql.filters import * __all__ = ( 'WirelessLANGroupFilter', @@ -11,18 +36,40 @@ @strawberry_django.filter(models.WirelessLANGroup, lookups=True) -@autotype_decorator(filtersets.WirelessLANGroupFilterSet) -class WirelessLANGroupFilter(BaseFilterMixin): +class WirelessLANGroupFilter(NestedGroupModelFilterMixin): pass @strawberry_django.filter(models.WirelessLAN, lookups=True) -@autotype_decorator(filtersets.WirelessLANFilterSet) -class WirelessLANFilter(BaseFilterMixin): - pass +class WirelessLANFilter(WirelessAuthenticationBaseFilterMixin, PrimaryModelFilterMixin): + ssid: FilterLookup[str] | None = strawberry_django.filter_field() + group: Annotated['WirelessLANGroupFilter', strawberry.lazy('wireless.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + group_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() + tenant: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + tenant_id: ID | None = strawberry_django.filter_field() @strawberry_django.filter(models.WirelessLink, lookups=True) -@autotype_decorator(filtersets.WirelessLinkFilterSet) -class WirelessLinkFilter(BaseFilterMixin): - pass +class WirelessLinkFilter(WirelessAuthenticationBaseFilterMixin, DistanceFilterMixin, PrimaryModelFilterMixin): + interface_a: Annotated['InterfaceFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + interface_a_id: ID | None = strawberry_django.filter_field() + interface_b: Annotated['InterfaceFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + interface_b_id: ID | None = strawberry_django.filter_field() + ssid: FilterLookup[str] | None = strawberry_django.filter_field() + status: Annotated['WirelessLANStatusEnum', strawberry.lazy('wireless.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) + tenant: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + tenant_id: ID | None = strawberry_django.filter_field() From ddbda68595a3ccbca68d8d0a48f038a170686fc5 Mon Sep 17 00:00:00 2001 From: Jeremy Sanders <23668453+jeremypng@users.noreply.github.com> Date: Tue, 11 Feb 2025 17:59:31 -0600 Subject: [PATCH 02/13] Update netbox/netbox/tests/test_graphql.py Co-authored-by: Jeremy Stretch --- netbox/netbox/tests/test_graphql.py | 1 - 1 file changed, 1 deletion(-) diff --git a/netbox/netbox/tests/test_graphql.py b/netbox/netbox/tests/test_graphql.py index 60a8ec5ae25..ca231526fa7 100644 --- a/netbox/netbox/tests/test_graphql.py +++ b/netbox/netbox/tests/test_graphql.py @@ -105,7 +105,6 @@ def test_graphql_filter_objects(self): id site {id} } }""" - print(query) response = self.client.post(url, data={'query': query}, format="json", **self.header) self.assertHttpStatus(response, status.HTTP_200_OK) data = json.loads(response.content) From 2482c18d27ea4349224024b1b07594c278b7935b Mon Sep 17 00:00:00 2001 From: Jeremy Sanders <23668453+jeremypng@users.noreply.github.com> Date: Wed, 12 Feb 2025 00:24:02 +0000 Subject: [PATCH 03/13] cleanup enums, enable strawberry-django optimizer, extra line --- netbox/dcim/graphql/enums.py | 9 --------- netbox/extras/graphql/enums.py | 2 -- netbox/ipam/graphql/enums.py | 9 --------- netbox/netbox/graphql/schema.py | 3 +-- netbox/netbox/settings.py | 1 - netbox/vpn/graphql/enums.py | 2 -- netbox/wireless/graphql/enums.py | 2 -- 7 files changed, 1 insertion(+), 27 deletions(-) diff --git a/netbox/dcim/graphql/enums.py b/netbox/dcim/graphql/enums.py index 2e99ab5e991..0b3441d5293 100644 --- a/netbox/dcim/graphql/enums.py +++ b/netbox/dcim/graphql/enums.py @@ -131,8 +131,6 @@ class DeviceAirflowEnum(Enum): @strawberry.enum class ModuleStatusEnum(Enum): - key = 'Module.status' - STATUS_OFFLINE = 'offline' STATUS_ACTIVE = 'active' STATUS_PLANNED = 'planned' @@ -800,8 +798,6 @@ class CableEndEnum(Enum): @strawberry.enum class PowerFeedStatusEnum(Enum): - key = 'PowerFeed.status' - STATUS_OFFLINE = 'offline' STATUS_ACTIVE = 'active' STATUS_PLANNED = 'planned' @@ -810,7 +806,6 @@ class PowerFeedStatusEnum(Enum): @strawberry.enum class PowerFeedTypeEnum(Enum): - TYPE_PRIMARY = 'primary' TYPE_REDUNDANT = 'redundant' @@ -834,8 +829,6 @@ class PowerFeedPhaseEnum(Enum): # @strawberry.enum class VirtualDeviceContextStatusEnum(Enum): - key = 'VirtualDeviceContext.status' - STATUS_ACTIVE = 'active' STATUS_PLANNED = 'planned' STATUS_OFFLINE = 'offline' @@ -847,8 +840,6 @@ class VirtualDeviceContextStatusEnum(Enum): @strawberry.enum class InventoryItemStatusEnum(Enum): - key = 'InventoryItem.status' - STATUS_OFFLINE = 'offline' STATUS_ACTIVE = 'active' STATUS_PLANNED = 'planned' diff --git a/netbox/extras/graphql/enums.py b/netbox/extras/graphql/enums.py index 959314d9e2f..1e44e91913e 100644 --- a/netbox/extras/graphql/enums.py +++ b/netbox/extras/graphql/enums.py @@ -98,8 +98,6 @@ class BookmarkOrderingEnum(Enum): @strawberry.enum class JournalEntryKindEnum(Enum): - key = 'JournalEntry.kind' - KIND_INFO = 'info' KIND_SUCCESS = 'success' KIND_WARNING = 'warning' diff --git a/netbox/ipam/graphql/enums.py b/netbox/ipam/graphql/enums.py index 3e73773cb59..b8125488072 100644 --- a/netbox/ipam/graphql/enums.py +++ b/netbox/ipam/graphql/enums.py @@ -28,8 +28,6 @@ class IPAddressFamilyEnum(Enum): @strawberry.enum class PrefixStatusEnum(Enum): - key = 'Prefix.status' - STATUS_CONTAINER = 'container' STATUS_ACTIVE = 'active' STATUS_RESERVED = 'reserved' @@ -43,8 +41,6 @@ class PrefixStatusEnum(Enum): @strawberry.enum class IPRangeStatusEnum(Enum): - key = 'IPRange.status' - STATUS_ACTIVE = 'active' STATUS_RESERVED = 'reserved' STATUS_DEPRECATED = 'deprecated' @@ -57,8 +53,6 @@ class IPRangeStatusEnum(Enum): @strawberry.enum class IPAddressStatusEnum(Enum): - key = 'IPAddress.status' - STATUS_ACTIVE = 'active' STATUS_RESERVED = 'reserved' STATUS_DEPRECATED = 'deprecated' @@ -107,8 +101,6 @@ class FHRPGroupAuthTypeEnum(Enum): @strawberry.enum class VLANStatusEnum(Enum): - key = 'VLAN.status' - STATUS_ACTIVE = 'active' STATUS_RESERVED = 'reserved' STATUS_DEPRECATED = 'deprecated' @@ -119,7 +111,6 @@ class VLANQinQRoleEnum(Enum): ROLE_SERVICE = 'svlan' ROLE_CUSTOMER = 'cvlan' - # # Services # diff --git a/netbox/netbox/graphql/schema.py b/netbox/netbox/graphql/schema.py index 625f7cd1b29..a7609c9d206 100644 --- a/netbox/netbox/graphql/schema.py +++ b/netbox/netbox/graphql/schema.py @@ -38,8 +38,7 @@ class Query( query=Query, config=StrawberryConfig(auto_camel_case=False), extensions=[ - # DjangoOptimizerExtension(prefetch_custom_queryset=True), - DjangoOptimizerExtension(prefetch_custom_queryset=True, enable_prefetch_related_optimization=False), + DjangoOptimizerExtension(prefetch_custom_queryset=True), MaxAliasesLimiter(max_alias_count=settings.GRAPHQL_MAX_ALIASES), ] ) diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index ddc7234ed2a..235ae113b4d 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -776,7 +776,6 @@ def _setting(name, default=None): STRAWBERRY_DJANGO = { "DEFAULT_PK_FIELD_NAME": "id", "TYPE_DESCRIPTION_FROM_MODEL_DOCSTRING": True, - "USE_DEPRECATED_FILTERS": False, } # diff --git a/netbox/vpn/graphql/enums.py b/netbox/vpn/graphql/enums.py index 7b66818adfd..9dc6bdac46b 100644 --- a/netbox/vpn/graphql/enums.py +++ b/netbox/vpn/graphql/enums.py @@ -23,8 +23,6 @@ @strawberry.enum class TunnelStatusEnum(Enum): - key = 'Tunnel.status' - STATUS_PLANNED = 'planned' STATUS_ACTIVE = 'active' STATUS_DISABLED = 'disabled' diff --git a/netbox/wireless/graphql/enums.py b/netbox/wireless/graphql/enums.py index 1526b2ff3b9..da8368cde3e 100644 --- a/netbox/wireless/graphql/enums.py +++ b/netbox/wireless/graphql/enums.py @@ -18,8 +18,6 @@ class WirelessRoleEnum(Enum): @strawberry.enum class WirelessLANStatusEnum(Enum): - key = 'WirelessLAN.status' - STATUS_ACTIVE = 'active' STATUS_RESERVED = 'reserved' STATUS_DISABLED = 'disabled' From 02942faa267784b459c684f1aeb90aebf99f3076 Mon Sep 17 00:00:00 2001 From: Jeremy Sanders <23668453+jeremypng@users.noreply.github.com> Date: Wed, 12 Feb 2025 00:25:06 +0000 Subject: [PATCH 04/13] removing commented enum --- netbox/netbox/graphql/enums.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/netbox/netbox/graphql/enums.py b/netbox/netbox/graphql/enums.py index d3d7a66905a..d92f9df1487 100644 --- a/netbox/netbox/graphql/enums.py +++ b/netbox/netbox/graphql/enums.py @@ -90,14 +90,6 @@ class ImportFormatEnum(Enum): YAML = 'yaml' -# @strawberry.enum -# class CSVDelimiterEnum(Enum): -# AUTO = 'auto' -# COMMA = CSV_DELIMITERS['comma'] -# SEMICOLON = CSV_DELIMITERS['semicolon'] -# TAB = CSV_DELIMITERS['tab'] - - @strawberry.enum class DistanceUnitEnum(Enum): # Metric From f1d7ad82dc9165c8c4e2b1944123ea411a8671a1 Mon Sep 17 00:00:00 2001 From: Jeremy Sanders <23668453+jeremypng@users.noreply.github.com> Date: Wed, 12 Feb 2025 00:30:26 +0000 Subject: [PATCH 05/13] moving filter_lookups.py to netbox/graphql --- netbox/circuits/graphql/filter_mixins.py | 2 +- netbox/circuits/graphql/filters.py | 8 +-- netbox/core/graphql/filters.py | 12 ++-- netbox/dcim/graphql/filter_mixins.py | 16 ++--- netbox/dcim/graphql/filters.py | 66 +++++++++---------- netbox/extras/graphql/filter_mixins.py | 4 +- netbox/extras/graphql/filters.py | 44 ++++++------- netbox/ipam/graphql/filter_mixins.py | 4 +- netbox/ipam/graphql/filters.py | 24 +++---- .../graphql/filter_lookups.py | 0 netbox/netbox/graphql/filter_mixins.py | 2 +- netbox/tenancy/graphql/filter_mixins.py | 4 +- netbox/tenancy/graphql/filters.py | 6 +- netbox/users/graphql/filters.py | 2 +- netbox/virtualization/graphql/filters.py | 10 +-- netbox/vpn/graphql/filters.py | 14 ++-- netbox/wireless/graphql/filters.py | 2 +- 17 files changed, 110 insertions(+), 110 deletions(-) rename netbox/{core => netbox}/graphql/filter_lookups.py (100%) diff --git a/netbox/circuits/graphql/filter_mixins.py b/netbox/circuits/graphql/filter_mixins.py index 0e70fbec4f4..f3a0d1f4a3b 100644 --- a/netbox/circuits/graphql/filter_mixins.py +++ b/netbox/circuits/graphql/filter_mixins.py @@ -6,7 +6,7 @@ if TYPE_CHECKING: from .filters import * - from core.graphql.filter_lookups import * + from netbox.graphql.filter_lookups import * from netbox.graphql.enums import * __all__ = ['BaseCircuitTypeFilterMixin'] diff --git a/netbox/circuits/graphql/filters.py b/netbox/circuits/graphql/filters.py index 420367f6e62..8e94ec2c2eb 100644 --- a/netbox/circuits/graphql/filters.py +++ b/netbox/circuits/graphql/filters.py @@ -17,7 +17,7 @@ from .enums import * from netbox.graphql.enums import * from wireless.graphql.enums import * - from core.graphql.filter_lookups import * + from netbox.graphql.filter_lookups import * from core.graphql.filters import * from extras.graphql.filters import * from circuits.graphql.filters import * @@ -62,10 +62,10 @@ class CircuitTerminationFilter( strawberry_django.filter_field() ) termination_id: ID | None = strawberry_django.filter_field() - port_speed: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + port_speed: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) - upstream_speed: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + upstream_speed: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) xconnect_id: FilterLookup[str] | None = strawberry_django.filter_field() @@ -97,7 +97,7 @@ class CircuitFilter(ContactFilterMixin, ImageAttachmentFilterMixin, DistanceFilt tenant_id: ID | None = strawberry_django.filter_field() install_date: DateFilterLookup[date] | None = strawberry_django.filter_field() termination_date: DateFilterLookup[date] | None = strawberry_django.filter_field() - commit_rate: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + commit_rate: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) diff --git a/netbox/core/graphql/filters.py b/netbox/core/graphql/filters.py index dcbc6f4f85f..e1feac715b2 100644 --- a/netbox/core/graphql/filters.py +++ b/netbox/core/graphql/filters.py @@ -9,7 +9,7 @@ ) from django.contrib.contenttypes.models import ContentType as DjangoContentType from core.graphql.filter_mixins import BaseFilterMixin -from core.graphql.filter_lookups import JSONFilter +from netbox.graphql.filter_lookups import JSONFilter from netbox.graphql.filter_mixins import ( PrimaryModelFilterMixin, ) @@ -17,7 +17,7 @@ from core import models if TYPE_CHECKING: - from core.graphql.filter_lookups import * + from netbox.graphql.filter_lookups import * from users.graphql.filters import * @@ -39,7 +39,7 @@ class DataFileFilter(BaseFilterMixin): ) source_id: ID | None = strawberry_django.filter_field() path: FilterLookup[str] | None = strawberry_django.filter_field() - size: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + size: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) hash: FilterLookup[str] | None = strawberry_django.filter_field() @@ -53,7 +53,7 @@ class DataSourceFilter(PrimaryModelFilterMixin): status: FilterLookup[str] | None = strawberry_django.filter_field() enabled: FilterLookup[bool] | None = strawberry_django.filter_field() ignore_rules: FilterLookup[str] | None = strawberry_django.filter_field() - parameters: Annotated['JSONFilter', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + parameters: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) last_synced: DatetimeFilterLookup[datetime] | None = strawberry_django.filter_field() @@ -78,10 +78,10 @@ class ObjectChangeFilter(BaseFilterMixin): related_object_type_id: ID | None = strawberry_django.filter_field() related_object_id: ID | None = strawberry_django.filter_field() object_repr: FilterLookup[str] | None = strawberry_django.filter_field() - prechange_data: Annotated['JSONFilter', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + prechange_data: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) - postchange_data: Annotated['JSONFilter', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + postchange_data: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) diff --git a/netbox/dcim/graphql/filter_mixins.py b/netbox/dcim/graphql/filter_mixins.py index cd1586134d4..dbe0fba00dd 100644 --- a/netbox/dcim/graphql/filter_mixins.py +++ b/netbox/dcim/graphql/filter_mixins.py @@ -10,7 +10,7 @@ if TYPE_CHECKING: from .filters import * - from core.graphql.filter_lookups import * + from netbox.graphql.filter_lookups import * from extras.graphql.filters import * from ipam.graphql.filters import * @@ -81,7 +81,7 @@ class RenderConfigFilterMixin(BaseFilterMixin): @dataclass class InterfaceBaseFilterMixin(BaseFilterMixin): enabled: FilterLookup[bool] | None = strawberry_django.filter_field() - mtu: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + mtu: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) mode: InterfaceModeEnum | None = strawberry_django.filter_field() @@ -114,25 +114,25 @@ class InterfaceBaseFilterMixin(BaseFilterMixin): @dataclass class RackBaseFilterMixin(WeightFilterMixin, PrimaryModelFilterMixin): width: Annotated['RackWidthEnum', strawberry.lazy('dcim.graphql.enums')] | None = strawberry_django.filter_field() - u_height: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + u_height: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) - starting_unit: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + starting_unit: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) desc_units: FilterLookup[bool] | None = strawberry_django.filter_field() - outer_width: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + outer_width: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) - outer_depth: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + outer_depth: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) outer_unit: Annotated['RackDimensionUnitEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( strawberry_django.filter_field() ) - mounting_depth: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + mounting_depth: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) - max_weight: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + max_weight: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) diff --git a/netbox/dcim/graphql/filters.py b/netbox/dcim/graphql/filters.py index b9fcedfaf7c..fff31210670 100644 --- a/netbox/dcim/graphql/filters.py +++ b/netbox/dcim/graphql/filters.py @@ -24,7 +24,7 @@ from .enums import * from netbox.graphql.enums import * from wireless.graphql.enums import * - from core.graphql.filter_lookups import * + from netbox.graphql.filter_lookups import * from core.graphql.filters import * from extras.graphql.filters import * from circuits.graphql.filters import * @@ -90,7 +90,7 @@ class CableFilter(PrimaryModelFilterMixin, TenancyFilterMixin): status: Annotated['LinkStatusEnum', strawberry.lazy('dcim.graphql.enums')] | None = strawberry_django.filter_field() label: FilterLookup[str] | None = strawberry_django.filter_field() color: Annotated['ColorEnum', strawberry.lazy('netbox.graphql.enums')] | None = strawberry_django.filter_field() - length: Annotated['FloatLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + length: Annotated['FloatLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) length_unit: Annotated['CableLengthUnitEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( @@ -176,12 +176,12 @@ class DeviceFilter( location: Annotated['LocationFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( strawberry_django.filter_field() ) - location_id: Annotated['TreeNodeFilter', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + location_id: Annotated['TreeNodeFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) rack: Annotated['RackFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() rack_id: ID | None = strawberry_django.filter_field() - position: Annotated['FloatLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + position: Annotated['FloatLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) face: Annotated['DeviceFaceEnum', strawberry.lazy('dcim.graphql.enums')] | None = strawberry_django.filter_field() @@ -211,16 +211,16 @@ class DeviceFilter( strawberry_django.filter_field() ) virtual_chassis_id: ID | None = strawberry_django.filter_field() - vc_position: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + vc_position: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) - vc_priority: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + vc_priority: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) - latitude: Annotated['FloatLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + latitude: Annotated['FloatLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) - longitude: Annotated['FloatLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + longitude: Annotated['FloatLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) interfaces: Annotated['InterfaceFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( @@ -307,7 +307,7 @@ class DeviceTypeFilter(ImageAttachmentFilterMixin, PrimaryModelFilterMixin, Weig ) default_platform_id: ID | None = strawberry_django.filter_field() part_number: FilterLookup[str] | None = strawberry_django.filter_field() - u_height: Annotated['FloatLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + u_height: Annotated['FloatLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) exclude_from_utilization: FilterLookup[bool] | None = strawberry_django.filter_field() @@ -334,7 +334,7 @@ class FrontPortFilter(ModularComponentModelFilterMixin, CabledObjectModelFilterM strawberry_django.filter_field() ) rear_port_id: ID | None = strawberry_django.filter_field() - rear_port_position: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + rear_port_position: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) @@ -347,7 +347,7 @@ class FrontPortTemplateFilter(ModularComponentTemplateFilterMixin): strawberry_django.filter_field() ) rear_port_id: ID | None = strawberry_django.filter_field() - rear_port_position: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + rear_port_position: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) @@ -372,7 +372,7 @@ class InterfaceFilter(ModularComponentModelFilterMixin, InterfaceBaseFilterMixin strawberry_django.filter_field() ) mgmt_only: FilterLookup[bool] | None = strawberry_django.filter_field() - speed: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + speed: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) duplex: Annotated['InterfaceDuplexEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( @@ -385,13 +385,13 @@ class InterfaceFilter(ModularComponentModelFilterMixin, InterfaceBaseFilterMixin rf_channel: Annotated['WirelessChannelEnum', strawberry.lazy('wireless.graphql.enums')] | None = ( strawberry_django.filter_field() ) - rf_channel_frequency: Annotated['FloatLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + rf_channel_frequency: Annotated['FloatLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) - rf_channel_width: Annotated['FloatLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + rf_channel_width: Annotated['FloatLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) - tx_power: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + tx_power: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) poe_mode: Annotated['InterfacePoEModeEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( @@ -584,16 +584,16 @@ class PowerFeedFilter(PrimaryModelFilterMixin, CabledObjectModelFilterMixin): phase: Annotated['PowerFeedPhaseEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( strawberry_django.filter_field() ) - voltage: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + voltage: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) - amperage: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + amperage: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) - max_utilization: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + max_utilization: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) - available_power: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + available_power: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) tenant: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( @@ -638,7 +638,7 @@ class PowerPanelFilter(ContactFilterMixin, ImageAttachmentFilterMixin, PrimaryMo location: Annotated['LocationFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( strawberry_django.filter_field() ) - location_id: Annotated['TreeNodeFilter', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + location_id: Annotated['TreeNodeFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) name: FilterLookup[str] | None = strawberry_django.filter_field() @@ -649,10 +649,10 @@ class PowerPortFilter(ModularComponentModelFilterMixin, CabledObjectModelFilterM type: Annotated['PowerPortTypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( strawberry_django.filter_field() ) - maximum_draw: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + maximum_draw: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) - allocated_draw: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + allocated_draw: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) @@ -662,10 +662,10 @@ class PowerPortTemplateFilter(ModularComponentTemplateFilterMixin): type: Annotated['PowerPortTypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( strawberry_django.filter_field() ) - maximum_draw: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + maximum_draw: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) - allocated_draw: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + allocated_draw: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) @@ -699,7 +699,7 @@ class RackFilter(ContactFilterMixin, ImageAttachmentFilterMixin, RackBaseFilterM location: Annotated['LocationFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( strawberry_django.filter_field() ) - location_id: Annotated['TreeNodeFilter', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + location_id: Annotated['TreeNodeFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) tenant: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( @@ -723,7 +723,7 @@ class RackFilter(ContactFilterMixin, ImageAttachmentFilterMixin, RackBaseFilterM class RackReservationFilter(PrimaryModelFilterMixin): rack: Annotated['RackFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() rack_id: ID | None = strawberry_django.filter_field() - units: Annotated['IntegerArrayLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + units: Annotated['IntegerArrayLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) tenant: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( @@ -744,7 +744,7 @@ class RackRoleFilter(OrganizationalModelFilterMixin): class RearPortFilter(ModularComponentModelFilterMixin, CabledObjectModelFilterMixin): type: Annotated['PortTypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = strawberry_django.filter_field() color: Annotated['ColorEnum', strawberry.lazy('netbox.graphql.enums')] | None = strawberry_django.filter_field() - positions: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + positions: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) @@ -753,7 +753,7 @@ class RearPortFilter(ModularComponentModelFilterMixin, CabledObjectModelFilterMi class RearPortTemplateFilter(ModularComponentTemplateFilterMixin): type: Annotated['PortTypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = strawberry_django.filter_field() color: Annotated['ColorEnum', strawberry.lazy('netbox.graphql.enums')] | None = strawberry_django.filter_field() - positions: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + positions: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) @@ -774,13 +774,13 @@ class SiteFilter(ContactFilterMixin, ImageAttachmentFilterMixin, PrimaryModelFil slug: FilterLookup[str] | None = strawberry_django.filter_field() status: Annotated['SiteStatusEnum', strawberry.lazy('dcim.graphql.enums')] | None = strawberry_django.filter_field() region: Annotated['RegionFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() - region_id: Annotated['TreeNodeFilter', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + region_id: Annotated['TreeNodeFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) group: Annotated['SiteGroupFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( strawberry_django.filter_field() ) - group_id: Annotated['TreeNodeFilter', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + group_id: Annotated['TreeNodeFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) tenant: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( @@ -792,10 +792,10 @@ class SiteFilter(ContactFilterMixin, ImageAttachmentFilterMixin, PrimaryModelFil time_zone: FilterLookup[str] | None = strawberry_django.filter_field() physical_address: FilterLookup[str] | None = strawberry_django.filter_field() shipping_address: FilterLookup[str] | None = strawberry_django.filter_field() - latitude: Annotated['FloatLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + latitude: Annotated['FloatLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) - longitude: Annotated['FloatLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + longitude: Annotated['FloatLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) prefixes: Annotated['PrefixFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( @@ -832,7 +832,7 @@ class VirtualDeviceContextFilter(PrimaryModelFilterMixin): status: Annotated['VirtualDeviceContextStatusEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( strawberry_django.filter_field() ) - identifier: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + identifier: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) primary_ip4: Annotated['IPAddressFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( diff --git a/netbox/extras/graphql/filter_mixins.py b/netbox/extras/graphql/filter_mixins.py index 726ae046e74..b0e8d3c5393 100644 --- a/netbox/extras/graphql/filter_mixins.py +++ b/netbox/extras/graphql/filter_mixins.py @@ -6,7 +6,7 @@ from core.graphql.filter_mixins import BaseFilterMixin if TYPE_CHECKING: - from core.graphql.filter_lookups import * + from netbox.graphql.filter_lookups import * from .filters import * __all__ = [ @@ -20,7 +20,7 @@ @dataclass class CustomFieldsFilterMixin(BaseFilterMixin): - custom_field_data: Annotated['JSONFilter', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + custom_field_data: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) diff --git a/netbox/extras/graphql/filters.py b/netbox/extras/graphql/filters.py index e80487fa040..7fc390b54c3 100644 --- a/netbox/extras/graphql/filters.py +++ b/netbox/extras/graphql/filters.py @@ -17,7 +17,7 @@ from .enums import * from netbox.graphql.enums import * from wireless.graphql.enums import * - from core.graphql.filter_lookups import * + from netbox.graphql.filter_lookups import * from extras.graphql.filters import * from circuits.graphql.filters import * from dcim.graphql.filters import * @@ -49,7 +49,7 @@ @strawberry_django.filter(models.ConfigContext, lookups=True) class ConfigContextFilter(BaseObjectTypeFilterMixin, SyncedDataFilterMixin, ChangeLogFilterMixin): name: FilterLookup[str] = strawberry_django.filter_field() - weight: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + weight: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) description: FilterLookup[str] = strawberry_django.filter_field() @@ -57,13 +57,13 @@ class ConfigContextFilter(BaseObjectTypeFilterMixin, SyncedDataFilterMixin, Chan regions: Annotated['RegionFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( strawberry_django.filter_field() ) - region_id: Annotated['TreeNodeFilter', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + region_id: Annotated['TreeNodeFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) site_groups: Annotated['SiteGroupFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( strawberry_django.filter_field() ) - site_group_id: Annotated['TreeNodeFilter', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + site_group_id: Annotated['TreeNodeFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) sites: Annotated['SiteFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() @@ -91,14 +91,14 @@ class ConfigContextFilter(BaseObjectTypeFilterMixin, SyncedDataFilterMixin, Chan tenant_groups: Annotated['TenantGroupFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( strawberry_django.filter_field() ) - tenant_group_id: Annotated['TreeNodeFilter', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + tenant_group_id: Annotated['TreeNodeFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) tenants: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( strawberry_django.filter_field() ) tags: Annotated['TagFilter', strawberry.lazy('extras.graphql.filters')] | None = strawberry_django.filter_field() - data: Annotated['JSONFilter', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + data: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) @@ -108,7 +108,7 @@ class ConfigTemplateFilter(BaseObjectTypeFilterMixin, SyncedDataFilterMixin, Cha name: FilterLookup[str] | None = strawberry_django.filter_field() description: FilterLookup[str] | None = strawberry_django.filter_field() template_code: FilterLookup[str] | None = strawberry_django.filter_field() - environment_params: Annotated['JSONFilter', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + environment_params: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) @@ -125,25 +125,25 @@ class CustomFieldFilter(BaseObjectTypeFilterMixin, ChangeLogFilterMixin): description: FilterLookup[str] | None = strawberry_django.filter_field() required: FilterLookup[bool] | None = strawberry_django.filter_field() unique: FilterLookup[bool] | None = strawberry_django.filter_field() - search_weight: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + 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 = ( strawberry_django.filter_field() ) - default: Annotated['JSONFilter', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + default: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) - related_object_filter: Annotated['JSONFilter', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + related_object_filter: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) - weight: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + weight: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) - validation_minimum: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + validation_minimum: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) - validation_maximum: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + validation_maximum: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) validation_regex: FilterLookup[str] | None = strawberry_django.filter_field() @@ -168,7 +168,7 @@ class CustomFieldChoiceSetFilter(BaseObjectTypeFilterMixin, ChangeLogFilterMixin base_choices: Annotated['CustomFieldChoiceSetBaseEnum', strawberry.lazy('extras.graphql.enums')] | None = ( strawberry_django.filter_field() ) - extra_choices: Annotated['StringArrayLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + extra_choices: Annotated['StringArrayLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) order_alphabetically: FilterLookup[bool] | None = strawberry_django.filter_field() @@ -180,7 +180,7 @@ class CustomLinkFilter(BaseObjectTypeFilterMixin, ChangeLogFilterMixin): enabled: FilterLookup[bool] | None = strawberry_django.filter_field() link_text: FilterLookup[str] | None = strawberry_django.filter_field() link_url: FilterLookup[str] | None = strawberry_django.filter_field() - weight: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + weight: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) group_name: FilterLookup[str] | None = strawberry_django.filter_field() @@ -201,10 +201,10 @@ class ExportTemplateFilter(BaseObjectTypeFilterMixin, SyncedDataFilterMixin, Cha @strawberry_django.filter(models.ImageAttachment, lookups=True) class ImageAttachmentFilter(BaseObjectTypeFilterMixin, ChangeLogFilterMixin): object_id: ID | None = strawberry_django.filter_field() - image_height: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + image_height: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) - image_width: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + image_width: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) name: FilterLookup[str] | None = strawberry_django.filter_field() @@ -241,12 +241,12 @@ class SavedFilterFilter(BaseObjectTypeFilterMixin, ChangeLogFilterMixin): description: FilterLookup[str] | None = strawberry_django.filter_field() user: Annotated['UserFilter', strawberry.lazy('users.graphql.filters')] | None = strawberry_django.filter_field() user_id: ID | None = strawberry_django.filter_field() - weight: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + weight: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) enabled: FilterLookup[bool] | None = strawberry_django.filter_field() shared: FilterLookup[bool] | None = strawberry_django.filter_field() - parameters: Annotated['JSONFilter', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + parameters: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) @@ -280,11 +280,11 @@ class WebhookFilter(BaseObjectTypeFilterMixin, CustomFieldsFilterMixin, TagsFilt class EventRuleFilter(BaseObjectTypeFilterMixin, CustomFieldsFilterMixin, TagsFilterMixin, ChangeLogFilterMixin): name: FilterLookup[str] | None = strawberry_django.filter_field() description: FilterLookup[str] | None = strawberry_django.filter_field() - event_types: Annotated['StringArrayLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + event_types: Annotated['StringArrayLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) enabled: FilterLookup[bool] | None = strawberry_django.filter_field() - conditions: Annotated['JSONFilter', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + conditions: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) action_type: Annotated['EventRuleActionEnum', strawberry.lazy('extras.graphql.enums')] | None = ( @@ -293,7 +293,7 @@ class EventRuleFilter(BaseObjectTypeFilterMixin, CustomFieldsFilterMixin, TagsFi action_object_type: FilterLookup[str] | None = strawberry_django.filter_field() action_object_type_id: ID | None = strawberry_django.filter_field() action_object_id: ID | None = strawberry_django.filter_field() - action_data: Annotated['JSONFilter', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + action_data: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) comments: FilterLookup[str] | None = strawberry_django.filter_field() diff --git a/netbox/ipam/graphql/filter_mixins.py b/netbox/ipam/graphql/filter_mixins.py index 74c189344b0..187a0a270a5 100644 --- a/netbox/ipam/graphql/filter_mixins.py +++ b/netbox/ipam/graphql/filter_mixins.py @@ -7,7 +7,7 @@ if TYPE_CHECKING: from .enums import * - from core.graphql.filter_lookups import * + from netbox.graphql.filter_lookups import * __all__ = ['ServiceBaseFilterMixin'] @@ -17,6 +17,6 @@ class ServiceBaseFilterMixin(BaseFilterMixin): protocol: Annotated['ServiceProtocolEnum', strawberry.lazy('ipam.graphql.enums')] | None = ( strawberry_django.filter_field() ) - ports: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + ports: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) diff --git a/netbox/ipam/graphql/filters.py b/netbox/ipam/graphql/filters.py index 40c98a99c7a..a66d40e31eb 100644 --- a/netbox/ipam/graphql/filters.py +++ b/netbox/ipam/graphql/filters.py @@ -21,7 +21,7 @@ from .enums import * from netbox.graphql.enums import * from wireless.graphql.enums import * - from core.graphql.filter_lookups import * + from netbox.graphql.filter_lookups import * from core.graphql.filters import * from extras.graphql.filters import * from circuits.graphql.filters import * @@ -59,7 +59,7 @@ class ASNFilter(PrimaryModelFilterMixin): rir: Annotated['RIRFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field() rir_id: ID | None = strawberry_django.filter_field() - asn: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + asn: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) tenant: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( @@ -74,10 +74,10 @@ class ASNRangeFilter(OrganizationalModelFilterMixin): slug: FilterLookup[str] | None = strawberry_django.filter_field() rir: Annotated['RIRFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field() rir_id: ID | None = strawberry_django.filter_field() - start: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + start: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) - end: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + end: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) tenant: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( @@ -101,7 +101,7 @@ class AggregateFilter(ContactFilterMixin, PrimaryModelFilterMixin): @strawberry_django.filter(models.FHRPGroup, lookups=True) class FHRPGroupFilter(PrimaryModelFilterMixin): - group_id: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + group_id: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) name: FilterLookup[str] | None = strawberry_django.filter_field() @@ -127,7 +127,7 @@ class FHRPGroupAssignmentFilter(BaseObjectTypeFilterMixin, ChangeLogFilterMixin) strawberry_django.filter_field() ) group_id: ID | None = strawberry_django.filter_field() - priority: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + priority: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) @@ -179,7 +179,7 @@ def parent(self, value: list[str], prefix) -> Q: class IPRangeFilter(ContactFilterMixin, PrimaryModelFilterMixin): start_address: FilterLookup[str] | None = strawberry_django.filter_field() end_address: FilterLookup[str] | None = strawberry_django.filter_field() - size: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + size: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) vrf: Annotated['VRFFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field() @@ -241,7 +241,7 @@ class RIRFilter(OrganizationalModelFilterMixin): @strawberry_django.filter(models.Role, lookups=True) class RoleFilter(OrganizationalModelFilterMixin): - weight: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + weight: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) @@ -282,7 +282,7 @@ class VLANFilter(PrimaryModelFilterMixin): strawberry_django.filter_field() ) group_id: ID | None = strawberry_django.filter_field() - vid: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + vid: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) name: FilterLookup[str] | None = strawberry_django.filter_field() @@ -316,7 +316,7 @@ class VLANGroupFilter(OrganizationalModelFilterMixin): ) scope_type_id: ID | None = strawberry_django.filter_field() scope_id: ID | None = strawberry_django.filter_field() - vid_ranges: Annotated['IntegerArrayLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + vid_ranges: Annotated['IntegerArrayLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) @@ -333,10 +333,10 @@ class VLANTranslationRuleFilter(NetBoxModelFilterMixin): ) policy_id: ID | None = strawberry_django.filter_field() description: FilterLookup[str] | None = strawberry_django.filter_field() - local_vid: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + local_vid: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) - remote_vid: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + remote_vid: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) diff --git a/netbox/core/graphql/filter_lookups.py b/netbox/netbox/graphql/filter_lookups.py similarity index 100% rename from netbox/core/graphql/filter_lookups.py rename to netbox/netbox/graphql/filter_lookups.py diff --git a/netbox/netbox/graphql/filter_mixins.py b/netbox/netbox/graphql/filter_mixins.py index 078bf81edc3..09bd71bfc3b 100644 --- a/netbox/netbox/graphql/filter_mixins.py +++ b/netbox/netbox/graphql/filter_mixins.py @@ -10,7 +10,7 @@ from extras.models import * from utilities.filters import * -from core.graphql.filter_lookups import * +from netbox.graphql.filter_lookups import * from core.graphql.filter_mixins import * from extras.graphql.filter_mixins import * diff --git a/netbox/tenancy/graphql/filter_mixins.py b/netbox/tenancy/graphql/filter_mixins.py index dffd5d85bcf..e1604fa873f 100644 --- a/netbox/tenancy/graphql/filter_mixins.py +++ b/netbox/tenancy/graphql/filter_mixins.py @@ -7,7 +7,7 @@ if TYPE_CHECKING: from .filters import * - from core.graphql.filter_lookups import * + from netbox.graphql.filter_lookups import * __all__ = ['TenancyFilterMixin', 'ContactFilterMixin'] @@ -28,6 +28,6 @@ class TenancyFilterMixin(BaseFilterMixin): group: Annotated['TenantGroupFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( strawberry_django.filter_field() ) - group_id: Annotated['TreeNodeFilter', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + group_id: Annotated['TreeNodeFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) diff --git a/netbox/tenancy/graphql/filters.py b/netbox/tenancy/graphql/filters.py index 97074226b3e..06af3f8b9d1 100644 --- a/netbox/tenancy/graphql/filters.py +++ b/netbox/tenancy/graphql/filters.py @@ -16,7 +16,7 @@ from .filter_mixins import ContactFilterMixin if TYPE_CHECKING: - from core.graphql.filter_lookups import TreeNodeFilter + from netbox.graphql.filter_lookups import TreeNodeFilter from circuits.graphql.filters import * from dcim.graphql.filters import * from ipam.graphql.filters import * @@ -41,7 +41,7 @@ class TenantFilter(PrimaryModelFilterMixin, ContactFilterMixin): group: Annotated['TenantGroupFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( strawberry_django.filter_field() ) - group_id: Annotated['TreeNodeFilter', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + group_id: Annotated['TreeNodeFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) asns: Annotated['ASNFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field() @@ -127,7 +127,7 @@ class ContactFilter(PrimaryModelFilterMixin): group: Annotated['ContactGroupFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( strawberry_django.filter_field() ) - group_id: Annotated['TreeNodeFilter', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + group_id: Annotated['TreeNodeFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) assignments: Annotated['ContactAssignmentFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( diff --git a/netbox/users/graphql/filters.py b/netbox/users/graphql/filters.py index e9d6e51f199..1027c7c3948 100644 --- a/netbox/users/graphql/filters.py +++ b/netbox/users/graphql/filters.py @@ -16,7 +16,7 @@ from .enums import * from netbox.graphql.enums import * from wireless.graphql.enums import * - from core.graphql.filter_lookups import * + from netbox.graphql.filter_lookups import * from extras.graphql.filters import * from circuits.graphql.filters import * from dcim.graphql.filters import * diff --git a/netbox/virtualization/graphql/filters.py b/netbox/virtualization/graphql/filters.py index b3910719da6..d2d34d54e3e 100644 --- a/netbox/virtualization/graphql/filters.py +++ b/netbox/virtualization/graphql/filters.py @@ -18,7 +18,7 @@ from .enums import * from netbox.graphql.enums import * from wireless.graphql.enums import * - from core.graphql.filter_lookups import * + from netbox.graphql.filter_lookups import * from extras.graphql.filters import * from circuits.graphql.filters import * from dcim.graphql.filters import * @@ -113,13 +113,13 @@ class VirtualMachineFilter( strawberry_django.filter_field() ) primary_ip6_id: ID | None = strawberry_django.filter_field() - vcpus: Annotated['FloatLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + vcpus: Annotated['FloatLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) - memory: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + memory: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) - disk: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + disk: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) serial: FilterLookup[str] | None = strawberry_django.filter_field() @@ -157,6 +157,6 @@ class VMInterfaceFilter(VMComponentFilterMixin, InterfaceBaseFilterMixin): @strawberry_django.filter(models.VirtualDisk, lookups=True) class VirtualDiskFilter(VMComponentFilterMixin): - size: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + size: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) diff --git a/netbox/vpn/graphql/filters.py b/netbox/vpn/graphql/filters.py index f910ce18ab1..e69455ae59f 100644 --- a/netbox/vpn/graphql/filters.py +++ b/netbox/vpn/graphql/filters.py @@ -17,7 +17,7 @@ from .enums import * from netbox.graphql.enums import * from wireless.graphql.enums import * - from core.graphql.filter_lookups import * + from netbox.graphql.filter_lookups import * from core.graphql.filters import * from extras.graphql.filters import * from circuits.graphql.filters import * @@ -89,7 +89,7 @@ class TunnelFilter(PrimaryModelFilterMixin): strawberry_django.filter_field() ) tenant_id: ID | None = strawberry_django.filter_field() - tunnel_id: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + tunnel_id: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) @@ -107,7 +107,7 @@ class IKEProposalFilter(PrimaryModelFilterMixin): strawberry_django.filter_field() ) group: Annotated['DHGroupEnum', strawberry.lazy('vpn.graphql.enums')] | None = strawberry_django.filter_field() - sa_lifetime: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + sa_lifetime: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) @@ -132,10 +132,10 @@ class IPSecProposalFilter(PrimaryModelFilterMixin): authentication_algorithm: Annotated['AuthenticationAlgorithmEnum', strawberry.lazy('vpn.graphql.enums')] | None = ( strawberry_django.filter_field() ) - sa_lifetime_seconds: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + sa_lifetime_seconds: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) - sa_lifetime_data: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + sa_lifetime_data: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) @@ -168,7 +168,7 @@ class L2VPNFilter(ContactFilterMixin, PrimaryModelFilterMixin): name: FilterLookup[str] | None = strawberry_django.filter_field() slug: FilterLookup[str] | None = strawberry_django.filter_field() type: Annotated['L2VPNTypeEnum', strawberry.lazy('vpn.graphql.enums')] | None = strawberry_django.filter_field() - identifier: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + identifier: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) import_targets: Annotated['RouteTargetFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( @@ -190,6 +190,6 @@ class L2VPNTerminationFilter(NetBoxModelFilterMixin): assigned_object_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = ( strawberry_django.filter_field() ) - assigned_object_id: Annotated['IntegerLookup', strawberry.lazy('core.graphql.filter_lookups')] | None = ( + assigned_object_id: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) diff --git a/netbox/wireless/graphql/filters.py b/netbox/wireless/graphql/filters.py index 890991a4dfc..5fe233a6a1c 100644 --- a/netbox/wireless/graphql/filters.py +++ b/netbox/wireless/graphql/filters.py @@ -17,7 +17,7 @@ from .enums import * from netbox.graphql.enums import * from wireless.graphql.enums import * - from core.graphql.filter_lookups import * + from netbox.graphql.filter_lookups import * from extras.graphql.filters import * from circuits.graphql.filters import * from dcim.graphql.filters import * From 36dc8cd0d91736b9c3da19c37a138b7216d08ef7 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 19 Feb 2025 11:19:23 -0500 Subject: [PATCH 06/13] Generate Strawberry filter enums from ChoiceSets (#18676) * Add as_num() method to ChoiceSet * Update circuits enums * Update DCIM enums * Update extras enums * Update IPAM enums * Add tenancy enums * Update virtualization enums * Update VPN enums * Update wireless enums * Update netbox enums * Permit TenantType.group to be null --- netbox/circuits/graphql/enums.py | 86 +-- netbox/dcim/graphql/enums.py | 921 ++----------------------- netbox/extras/graphql/enums.py | 200 +----- netbox/extras/graphql/filters.py | 4 +- netbox/ipam/graphql/enums.py | 138 +--- netbox/netbox/graphql/enums.py | 113 +-- netbox/tenancy/graphql/enums.py | 9 + netbox/tenancy/graphql/filters.py | 5 +- netbox/tenancy/graphql/types.py | 2 +- netbox/utilities/choices.py | 19 + netbox/virtualization/graphql/enums.py | 36 +- netbox/vpn/graphql/enums.py | 180 +---- netbox/wireless/graphql/enums.py | 256 +------ 13 files changed, 214 insertions(+), 1755 deletions(-) create mode 100644 netbox/tenancy/graphql/enums.py diff --git a/netbox/circuits/graphql/enums.py b/netbox/circuits/graphql/enums.py index 9fd3b8b0918..609e4435b95 100644 --- a/netbox/circuits/graphql/enums.py +++ b/netbox/circuits/graphql/enums.py @@ -1,86 +1,20 @@ -from enum import Enum import strawberry -__all__ = [ +from circuits.choices import * + +__all__ = ( 'CircuitStatusEnum', 'CircuitCommitRateEnum', 'CircuitTerminationSideEnum', 'CircuitTerminationPortSpeedEnum', 'CircuitPriorityEnum', 'VirtualCircuitTerminationRoleEnum', -] - -# -# Circuits -# - - -@strawberry.enum -class CircuitStatusEnum(Enum): - STATUS_DEPROVISIONING = 'deprovisioning' - STATUS_ACTIVE = 'active' - STATUS_PLANNED = 'planned' - STATUS_PROVISIONING = 'provisioning' - STATUS_OFFLINE = 'offline' - STATUS_DECOMMISSIONED = 'decommissioned' - - -@strawberry.enum -class CircuitCommitRateEnum(Enum): - TEN_MBPS = 10000 - HUNDRED_MBPS = 100000 - ONE_GBPS = 1000000 - TEN_GBPS = 10000000 - TWENTY_FIVE_GBPS = 25000000 - FORTY_GBPS = 40000000 - HUNDRED_GBPS = 100000000 - TWO_HUNDRED_GBPS = 200000000 - FOUR_HUNDRED_GBPS = 400000000 - T1 = 1544 - E1 = 2048 - - -# -# CircuitTerminations -# - - -@strawberry.enum -class CircuitTerminationSideEnum(Enum): - SIDE_A = 'A' - SIDE_Z = 'Z' - - -@strawberry.enum -class CircuitTerminationPortSpeedEnum(Enum): - TEN_MBPS = 10000 - HUNDRED_MBPS = 100000 - ONE_GBPS = 1000000 - TEN_GBPS = 10000000 - TWENTY_FIVE_GBPS = 25000000 - FORTY_GBPS = 40000000 - HUNDRED_GBPS = 100000000 - TWO_HUNDRED_GBPS = 200000000 - FOUR_HUNDRED_GBPS = 400000000 - T1 = 1544 - E1 = 2048 - - -@strawberry.enum -class CircuitPriorityEnum(Enum): - PRIORITY_PRIMARY = 'primary' - PRIORITY_SECONDARY = 'secondary' - PRIORITY_TERTIARY = 'tertiary' - PRIORITY_INACTIVE = 'inactive' - - -# -# Virtual circuits -# +) -@strawberry.enum -class VirtualCircuitTerminationRoleEnum(Enum): - ROLE_PEER = 'peer' - ROLE_HUB = 'hub' - ROLE_SPOKE = 'spoke' +CircuitCommitRateEnum = strawberry.enum(CircuitCommitRateChoices.as_enum()) +CircuitPriorityEnum = strawberry.enum(CircuitPriorityChoices.as_enum()) +CircuitStatusEnum = strawberry.enum(CircuitStatusChoices.as_enum()) +CircuitTerminationSideEnum = strawberry.enum(CircuitTerminationSideChoices.as_enum()) +CircuitTerminationPortSpeedEnum = strawberry.enum(CircuitTerminationPortSpeedChoices.as_enum()) +VirtualCircuitTerminationRoleEnum = strawberry.enum(VirtualCircuitTerminationRoleChoices.as_enum()) diff --git a/netbox/dcim/graphql/enums.py b/netbox/dcim/graphql/enums.py index 0b3441d5293..a60c0df14ac 100644 --- a/netbox/dcim/graphql/enums.py +++ b/netbox/dcim/graphql/enums.py @@ -1,848 +1,77 @@ -from enum import Enum import strawberry - -# -# Sites -# - -@strawberry.enum -class SiteStatusEnum(Enum): - STATUS_PLANNED = 'planned' - STATUS_STAGING = 'staging' - STATUS_ACTIVE = 'active' - STATUS_DECOMMISSIONING = 'decommissioning' - STATUS_RETIRED = 'retired' - - -# -# Locations -# - -@strawberry.enum -class LocationStatusEnum(Enum): - STATUS_PLANNED = 'planned' - STATUS_STAGING = 'staging' - STATUS_ACTIVE = 'active' - STATUS_DECOMMISSIONING = 'decommissioning' - STATUS_RETIRED = 'retired' - - -# -# Racks -# - -@strawberry.enum -class RackFormFactorEnum(Enum): - TYPE_2POST = '2-post-frame' - TYPE_4POST = '4-post-frame' - TYPE_CABINET = '4-post-cabinet' - TYPE_WALLFRAME = 'wall-frame' - TYPE_WALLFRAME_VERTICAL = 'wall-frame-vertical' - TYPE_WALLCABINET = 'wall-cabinet' - TYPE_WALLCABINET_VERTICAL = 'wall-cabinet-vertical' - - -@strawberry.enum -class RackWidthEnum(Enum): - - WIDTH_10IN = 10 - WIDTH_19IN = 19 - WIDTH_21IN = 21 - WIDTH_23IN = 23 - - -@strawberry.enum -class RackStatusEnum(Enum): - STATUS_RESERVED = 'reserved' - STATUS_AVAILABLE = 'available' - STATUS_PLANNED = 'planned' - STATUS_ACTIVE = 'active' - STATUS_DEPRECATED = 'deprecated' - - -@strawberry.enum -class RackDimensionUnitEnum(Enum): - UNIT_MILLIMETER = 'mm' - UNIT_INCH = 'in' - - -@strawberry.enum -class RackElevationDetailRenderEnum(Enum): - RENDER_JSON = 'json' - RENDER_SVG = 'svg' - - -@strawberry.enum -class RackAirflowEnum(Enum): - FRONT_TO_REAR = 'front-to-rear' - REAR_TO_FRONT = 'rear-to-front' - - -# -# DeviceTypes -# - -@strawberry.enum -class SubdeviceRoleEnum(Enum): - ROLE_PARENT = 'parent' - ROLE_CHILD = 'child' - - -# -# Devices -# - -@strawberry.enum -class DeviceFaceEnum(Enum): - FACE_FRONT = 'front' - FACE_REAR = 'rear' - - -@strawberry.enum -class DeviceStatusEnum(Enum): - STATUS_OFFLINE = 'offline' - STATUS_ACTIVE = 'active' - STATUS_PLANNED = 'planned' - STATUS_STAGED = 'staged' - STATUS_FAILED = 'failed' - STATUS_INVENTORY = 'inventory' - STATUS_DECOMMISSIONING = 'decommissioning' - - -@strawberry.enum -class DeviceAirflowEnum(Enum): - - AIRFLOW_FRONT_TO_REAR = 'front-to-rear' - AIRFLOW_REAR_TO_FRONT = 'rear-to-front' - AIRFLOW_LEFT_TO_RIGHT = 'left-to-right' - AIRFLOW_RIGHT_TO_LEFT = 'right-to-left' - AIRFLOW_SIDE_TO_REAR = 'side-to-rear' - AIRFLOW_REAR_TO_SIDE = 'rear-to-side' - AIRFLOW_BOTTOM_TO_TOP = 'bottom-to-top' - AIRFLOW_TOP_TO_BOTTOM = 'top-to-bottom' - AIRFLOW_PASSIVE = 'passive' - AIRFLOW_MIXED = 'mixed' - - -# -# Modules -# - -@strawberry.enum -class ModuleStatusEnum(Enum): - STATUS_OFFLINE = 'offline' - STATUS_ACTIVE = 'active' - STATUS_PLANNED = 'planned' - STATUS_STAGED = 'staged' - STATUS_FAILED = 'failed' - STATUS_DECOMMISSIONING = 'decommissioning' - - -@strawberry.enum -class ModuleAirflowEnum(Enum): - - FRONT_TO_REAR = 'front-to-rear' - REAR_TO_FRONT = 'rear-to-front' - LEFT_TO_RIGHT = 'left-to-right' - RIGHT_TO_LEFT = 'right-to-left' - SIDE_TO_REAR = 'side-to-rear' - PASSIVE = 'passive' - - -# -# ConsolePorts -# - -@strawberry.enum -class ConsolePortTypeEnum(Enum): - - TYPE_DE9 = 'de-9' - TYPE_DB25 = 'db-25' - TYPE_RJ11 = 'rj-11' - TYPE_RJ12 = 'rj-12' - TYPE_RJ45 = 'rj-45' - TYPE_MINI_DIN_8 = 'mini-din-8' - TYPE_USB_A = 'usb-a' - TYPE_USB_B = 'usb-b' - TYPE_USB_C = 'usb-c' - TYPE_USB_MINI_A = 'usb-mini-a' - TYPE_USB_MINI_B = 'usb-mini-b' - TYPE_USB_MICRO_A = 'usb-micro-a' - TYPE_USB_MICRO_B = 'usb-micro-b' - TYPE_USB_MICRO_AB = 'usb-micro-ab' - TYPE_OTHER = 'other' - - -@strawberry.enum -class ConsolePortSpeedEnum(Enum): - - SPEED_1200 = 1200 - SPEED_2400 = 2400 - SPEED_4800 = 4800 - SPEED_9600 = 9600 - SPEED_19200 = 19200 - SPEED_38400 = 38400 - SPEED_57600 = 57600 - SPEED_115200 = 115200 - - -# -# PowerPorts -# - -@strawberry.enum -class PowerPortTypeEnum(Enum): - - # IEC 60320 - TYPE_IEC_C6 = 'iec-60320-c6' - TYPE_IEC_C8 = 'iec-60320-c8' - TYPE_IEC_C14 = 'iec-60320-c14' - TYPE_IEC_C16 = 'iec-60320-c16' - TYPE_IEC_C20 = 'iec-60320-c20' - TYPE_IEC_C22 = 'iec-60320-c22' - # IEC 60309 - TYPE_IEC_PNE4H = 'iec-60309-p-n-e-4h' - TYPE_IEC_PNE6H = 'iec-60309-p-n-e-6h' - TYPE_IEC_PNE9H = 'iec-60309-p-n-e-9h' - TYPE_IEC_2PE4H = 'iec-60309-2p-e-4h' - TYPE_IEC_2PE6H = 'iec-60309-2p-e-6h' - TYPE_IEC_2PE9H = 'iec-60309-2p-e-9h' - TYPE_IEC_3PE4H = 'iec-60309-3p-e-4h' - TYPE_IEC_3PE6H = 'iec-60309-3p-e-6h' - TYPE_IEC_3PE9H = 'iec-60309-3p-e-9h' - TYPE_IEC_3PNE4H = 'iec-60309-3p-n-e-4h' - TYPE_IEC_3PNE6H = 'iec-60309-3p-n-e-6h' - TYPE_IEC_3PNE9H = 'iec-60309-3p-n-e-9h' - # IEC 60906-1 - TYPE_IEC_60906_1 = 'iec-60906-1' - TYPE_NBR_14136_10A = 'nbr-14136-10a' - TYPE_NBR_14136_20A = 'nbr-14136-20a' - # NEMA non-locking - TYPE_NEMA_115P = 'nema-1-15p' - TYPE_NEMA_515P = 'nema-5-15p' - TYPE_NEMA_520P = 'nema-5-20p' - TYPE_NEMA_530P = 'nema-5-30p' - TYPE_NEMA_550P = 'nema-5-50p' - TYPE_NEMA_615P = 'nema-6-15p' - TYPE_NEMA_620P = 'nema-6-20p' - TYPE_NEMA_630P = 'nema-6-30p' - TYPE_NEMA_650P = 'nema-6-50p' - TYPE_NEMA_1030P = 'nema-10-30p' - TYPE_NEMA_1050P = 'nema-10-50p' - TYPE_NEMA_1420P = 'nema-14-20p' - TYPE_NEMA_1430P = 'nema-14-30p' - TYPE_NEMA_1450P = 'nema-14-50p' - TYPE_NEMA_1460P = 'nema-14-60p' - TYPE_NEMA_1515P = 'nema-15-15p' - TYPE_NEMA_1520P = 'nema-15-20p' - TYPE_NEMA_1530P = 'nema-15-30p' - TYPE_NEMA_1550P = 'nema-15-50p' - TYPE_NEMA_1560P = 'nema-15-60p' - # NEMA locking - TYPE_NEMA_L115P = 'nema-l1-15p' - TYPE_NEMA_L515P = 'nema-l5-15p' - TYPE_NEMA_L520P = 'nema-l5-20p' - TYPE_NEMA_L530P = 'nema-l5-30p' - TYPE_NEMA_L550P = 'nema-l5-50p' - TYPE_NEMA_L615P = 'nema-l6-15p' - TYPE_NEMA_L620P = 'nema-l6-20p' - TYPE_NEMA_L630P = 'nema-l6-30p' - TYPE_NEMA_L650P = 'nema-l6-50p' - TYPE_NEMA_L1030P = 'nema-l10-30p' - TYPE_NEMA_L1420P = 'nema-l14-20p' - TYPE_NEMA_L1430P = 'nema-l14-30p' - TYPE_NEMA_L1450P = 'nema-l14-50p' - TYPE_NEMA_L1460P = 'nema-l14-60p' - TYPE_NEMA_L1520P = 'nema-l15-20p' - TYPE_NEMA_L1530P = 'nema-l15-30p' - TYPE_NEMA_L1550P = 'nema-l15-50p' - TYPE_NEMA_L1560P = 'nema-l15-60p' - TYPE_NEMA_L2120P = 'nema-l21-20p' - TYPE_NEMA_L2130P = 'nema-l21-30p' - TYPE_NEMA_L2220P = 'nema-l22-20p' - TYPE_NEMA_L2230P = 'nema-l22-30p' - # California style - TYPE_CS6361C = 'cs6361c' - TYPE_CS6365C = 'cs6365c' - TYPE_CS8165C = 'cs8165c' - TYPE_CS8265C = 'cs8265c' - TYPE_CS8365C = 'cs8365c' - TYPE_CS8465C = 'cs8465c' - # ITA/international - TYPE_ITA_C = 'ita-c' - TYPE_ITA_E = 'ita-e' - TYPE_ITA_F = 'ita-f' - TYPE_ITA_EF = 'ita-ef' - TYPE_ITA_G = 'ita-g' - TYPE_ITA_H = 'ita-h' - TYPE_ITA_I = 'ita-i' - TYPE_ITA_J = 'ita-j' - TYPE_ITA_K = 'ita-k' - TYPE_ITA_L = 'ita-l' - TYPE_ITA_M = 'ita-m' - TYPE_ITA_N = 'ita-n' - TYPE_ITA_O = 'ita-o' - # USB - TYPE_USB_A = 'usb-a' - TYPE_USB_B = 'usb-b' - TYPE_USB_C = 'usb-c' - TYPE_USB_MINI_A = 'usb-mini-a' - TYPE_USB_MINI_B = 'usb-mini-b' - TYPE_USB_MICRO_A = 'usb-micro-a' - TYPE_USB_MICRO_B = 'usb-micro-b' - TYPE_USB_MICRO_AB = 'usb-micro-ab' - TYPE_USB_3_B = 'usb-3-b' - TYPE_USB_3_MICROB = 'usb-3-micro-b' - # Molex - TYPE_MOLEX_MICRO_FIT_1X2 = 'molex-micro-fit-1x2' - TYPE_MOLEX_MICRO_FIT_2X2 = 'molex-micro-fit-2x2' - TYPE_MOLEX_MICRO_FIT_2X4 = 'molex-micro-fit-2x4' - # Direct current (DC) - TYPE_DC = 'dc-terminal' - # Proprietary - TYPE_SAF_D_GRID = 'saf-d-grid' - TYPE_NEUTRIK_POWERCON_20A = 'neutrik-powercon-20' - TYPE_NEUTRIK_POWERCON_32A = 'neutrik-powercon-32' - TYPE_NEUTRIK_POWERCON_TRUE1 = 'neutrik-powercon-true1' - TYPE_NEUTRIK_POWERCON_TRUE1_TOP = 'neutrik-powercon-true1-top' - TYPE_UBIQUITI_SMARTPOWER = 'ubiquiti-smartpower' - # Other - TYPE_HARDWIRED = 'hardwired' - TYPE_OTHER = 'other' - - -# -# PowerOutlets -# - -@strawberry.enum -class PowerOutletTypeEnum(Enum): - - # IEC 60320 - TYPE_IEC_C5 = 'iec-60320-c5' - TYPE_IEC_C7 = 'iec-60320-c7' - TYPE_IEC_C13 = 'iec-60320-c13' - TYPE_IEC_C15 = 'iec-60320-c15' - TYPE_IEC_C19 = 'iec-60320-c19' - TYPE_IEC_C21 = 'iec-60320-c21' - # IEC 60309 - TYPE_IEC_PNE4H = 'iec-60309-p-n-e-4h' - TYPE_IEC_PNE6H = 'iec-60309-p-n-e-6h' - TYPE_IEC_PNE9H = 'iec-60309-p-n-e-9h' - TYPE_IEC_2PE4H = 'iec-60309-2p-e-4h' - TYPE_IEC_2PE6H = 'iec-60309-2p-e-6h' - TYPE_IEC_2PE9H = 'iec-60309-2p-e-9h' - TYPE_IEC_3PE4H = 'iec-60309-3p-e-4h' - TYPE_IEC_3PE6H = 'iec-60309-3p-e-6h' - TYPE_IEC_3PE9H = 'iec-60309-3p-e-9h' - TYPE_IEC_3PNE4H = 'iec-60309-3p-n-e-4h' - TYPE_IEC_3PNE6H = 'iec-60309-3p-n-e-6h' - TYPE_IEC_3PNE9H = 'iec-60309-3p-n-e-9h' - # IEC 60906-1 - TYPE_IEC_60906_1 = 'iec-60906-1' - TYPE_NBR_14136_10A = 'nbr-14136-10a' - TYPE_NBR_14136_20A = 'nbr-14136-20a' - # NEMA non-locking - TYPE_NEMA_115R = 'nema-1-15r' - TYPE_NEMA_515R = 'nema-5-15r' - TYPE_NEMA_520R = 'nema-5-20r' - TYPE_NEMA_530R = 'nema-5-30r' - TYPE_NEMA_550R = 'nema-5-50r' - TYPE_NEMA_615R = 'nema-6-15r' - TYPE_NEMA_620R = 'nema-6-20r' - TYPE_NEMA_630R = 'nema-6-30r' - TYPE_NEMA_650R = 'nema-6-50r' - TYPE_NEMA_1030R = 'nema-10-30r' - TYPE_NEMA_1050R = 'nema-10-50r' - TYPE_NEMA_1420R = 'nema-14-20r' - TYPE_NEMA_1430R = 'nema-14-30r' - TYPE_NEMA_1450R = 'nema-14-50r' - TYPE_NEMA_1460R = 'nema-14-60r' - TYPE_NEMA_1515R = 'nema-15-15r' - TYPE_NEMA_1520R = 'nema-15-20r' - TYPE_NEMA_1530R = 'nema-15-30r' - TYPE_NEMA_1550R = 'nema-15-50r' - TYPE_NEMA_1560R = 'nema-15-60r' - # NEMA locking - TYPE_NEMA_L115R = 'nema-l1-15r' - TYPE_NEMA_L515R = 'nema-l5-15r' - TYPE_NEMA_L520R = 'nema-l5-20r' - TYPE_NEMA_L530R = 'nema-l5-30r' - TYPE_NEMA_L550R = 'nema-l5-50r' - TYPE_NEMA_L615R = 'nema-l6-15r' - TYPE_NEMA_L620R = 'nema-l6-20r' - TYPE_NEMA_L630R = 'nema-l6-30r' - TYPE_NEMA_L650R = 'nema-l6-50r' - TYPE_NEMA_L1030R = 'nema-l10-30r' - TYPE_NEMA_L1420R = 'nema-l14-20r' - TYPE_NEMA_L1430R = 'nema-l14-30r' - TYPE_NEMA_L1450R = 'nema-l14-50r' - TYPE_NEMA_L1460R = 'nema-l14-60r' - TYPE_NEMA_L1520R = 'nema-l15-20r' - TYPE_NEMA_L1530R = 'nema-l15-30r' - TYPE_NEMA_L1550R = 'nema-l15-50r' - TYPE_NEMA_L1560R = 'nema-l15-60r' - TYPE_NEMA_L2120R = 'nema-l21-20r' - TYPE_NEMA_L2130R = 'nema-l21-30r' - TYPE_NEMA_L2220R = 'nema-l22-20r' - TYPE_NEMA_L2230R = 'nema-l22-30r' - # California style - TYPE_CS6360C = 'CS6360C' - TYPE_CS6364C = 'CS6364C' - TYPE_CS8164C = 'CS8164C' - TYPE_CS8264C = 'CS8264C' - TYPE_CS8364C = 'CS8364C' - TYPE_CS8464C = 'CS8464C' - # ITA/international - TYPE_ITA_E = 'ita-e' - TYPE_ITA_F = 'ita-f' - TYPE_ITA_G = 'ita-g' - TYPE_ITA_H = 'ita-h' - TYPE_ITA_I = 'ita-i' - TYPE_ITA_J = 'ita-j' - TYPE_ITA_K = 'ita-k' - TYPE_ITA_L = 'ita-l' - TYPE_ITA_M = 'ita-m' - TYPE_ITA_N = 'ita-n' - TYPE_ITA_O = 'ita-o' - TYPE_ITA_MULTISTANDARD = 'ita-multistandard' - # USB - TYPE_USB_A = 'usb-a' - TYPE_USB_MICROB = 'usb-micro-b' - TYPE_USB_C = 'usb-c' - # Molex - TYPE_MOLEX_MICRO_FIT_1X2 = 'molex-micro-fit-1x2' - TYPE_MOLEX_MICRO_FIT_2X2 = 'molex-micro-fit-2x2' - TYPE_MOLEX_MICRO_FIT_2X4 = 'molex-micro-fit-2x4' - # Direct current (DC) - TYPE_DC = 'dc-terminal' - # Proprietary - TYPE_EATON_C39 = 'eaton-c39' - TYPE_HDOT_CX = 'hdot-cx' - TYPE_SAF_D_GRID = 'saf-d-grid' - TYPE_NEUTRIK_POWERCON_20A = 'neutrik-powercon-20a' - TYPE_NEUTRIK_POWERCON_32A = 'neutrik-powercon-32a' - TYPE_NEUTRIK_POWERCON_TRUE1 = 'neutrik-powercon-true1' - TYPE_NEUTRIK_POWERCON_TRUE1_TOP = 'neutrik-powercon-true1-top' - TYPE_UBIQUITI_SMARTPOWER = 'ubiquiti-smartpower' - # Other - TYPE_HARDWIRED = 'hardwired' - TYPE_OTHER = 'other' - - -@strawberry.enum -class PowerOutletFeedLegEnum(Enum): - - FEED_LEG_A = 'A' - FEED_LEG_B = 'B' - FEED_LEG_C = 'C' - - -# -# Interfaces -# - -@strawberry.enum -class InterfaceKindEnum(Enum): - KIND_PHYSICAL = 'physical' - KIND_VIRTUAL = 'virtual' - KIND_WIRELESS = 'wireless' - - -@strawberry.enum -class InterfaceTypeEnum(Enum): - - # Virtual - TYPE_VIRTUAL = 'virtual' - TYPE_BRIDGE = 'bridge' - TYPE_LAG = 'lag' - - # Ethernet - TYPE_100ME_FX = '100base-fx' - TYPE_100ME_LFX = '100base-lfx' - TYPE_100ME_FIXED = '100base-tx' - TYPE_100ME_T1 = '100base-t1' - TYPE_100ME_SFP = '100base-x-sfp' - TYPE_1GE_FIXED = '1000base-t' - TYPE_1GE_LX_FIXED = '1000base-lx' - TYPE_1GE_TX_FIXED = '1000base-tx' - TYPE_1GE_GBIC = '1000base-x-gbic' - TYPE_1GE_SFP = '1000base-x-sfp' - TYPE_2GE_FIXED = '2.5gbase-t' - TYPE_5GE_FIXED = '5gbase-t' - TYPE_10GE_FIXED = '10gbase-t' - TYPE_10GE_CX4 = '10gbase-cx4' - TYPE_10GE_SFP_PLUS = '10gbase-x-sfpp' - TYPE_10GE_XFP = '10gbase-x-xfp' - TYPE_10GE_XENPAK = '10gbase-x-xenpak' - TYPE_10GE_X2 = '10gbase-x-x2' - TYPE_25GE_SFP28 = '25gbase-x-sfp28' - TYPE_50GE_SFP56 = '50gbase-x-sfp56' - TYPE_40GE_QSFP_PLUS = '40gbase-x-qsfpp' - TYPE_50GE_QSFP28 = '50gbase-x-sfp28' - TYPE_100GE_CFP = '100gbase-x-cfp' - TYPE_100GE_CFP2 = '100gbase-x-cfp2' - TYPE_100GE_CFP4 = '100gbase-x-cfp4' - TYPE_100GE_CXP = '100gbase-x-cxp' - TYPE_100GE_CPAK = '100gbase-x-cpak' - TYPE_100GE_DSFP = '100gbase-x-dsfp' - TYPE_100GE_SFP_DD = '100gbase-x-sfpdd' - TYPE_100GE_QSFP28 = '100gbase-x-qsfp28' - TYPE_100GE_QSFP_DD = '100gbase-x-qsfpdd' - TYPE_200GE_CFP2 = '200gbase-x-cfp2' - TYPE_200GE_QSFP56 = '200gbase-x-qsfp56' - TYPE_200GE_QSFP_DD = '200gbase-x-qsfpdd' - TYPE_400GE_CFP2 = '400gbase-x-cfp2' - TYPE_400GE_QSFP112 = '400gbase-x-qsfp112' - TYPE_400GE_QSFP_DD = '400gbase-x-qsfpdd' - TYPE_400GE_OSFP = '400gbase-x-osfp' - TYPE_400GE_OSFP_RHS = '400gbase-x-osfp-rhs' - TYPE_400GE_CDFP = '400gbase-x-cdfp' - TYPE_400GE_CFP8 = '400gbase-x-cfp8' - TYPE_800GE_QSFP_DD = '800gbase-x-qsfpdd' - TYPE_800GE_OSFP = '800gbase-x-osfp' - - # Ethernet Backplane - TYPE_1GE_KX = '1000base-kx' - TYPE_2GE_KX = '2.5gbase-kx' - TYPE_5GE_KR = '5gbase-kr' - TYPE_10GE_KR = '10gbase-kr' - TYPE_10GE_KX4 = '10gbase-kx4' - TYPE_25GE_KR = '25gbase-kr' - TYPE_40GE_KR4 = '40gbase-kr4' - TYPE_50GE_KR = '50gbase-kr' - TYPE_100GE_KP4 = '100gbase-kp4' - TYPE_100GE_KR2 = '100gbase-kr2' - TYPE_100GE_KR4 = '100gbase-kr4' - - # Wireless - TYPE_80211A = 'ieee802.11a' - TYPE_80211G = 'ieee802.11g' - TYPE_80211N = 'ieee802.11n' - TYPE_80211AC = 'ieee802.11ac' - TYPE_80211AD = 'ieee802.11ad' - TYPE_80211AX = 'ieee802.11ax' - TYPE_80211AY = 'ieee802.11ay' - TYPE_80211BE = 'ieee802.11be' - TYPE_802151 = 'ieee802.15.1' - TYPE_802154 = 'ieee802.15.4' - TYPE_OTHER_WIRELESS = 'other-wireless' - - # Cellular - TYPE_GSM = 'gsm' - TYPE_CDMA = 'cdma' - TYPE_LTE = 'lte' - TYPE_4G = '4g' - TYPE_5G = '5g' - - # SONET - TYPE_SONET_OC3 = 'sonet-oc3' - TYPE_SONET_OC12 = 'sonet-oc12' - TYPE_SONET_OC48 = 'sonet-oc48' - TYPE_SONET_OC192 = 'sonet-oc192' - TYPE_SONET_OC768 = 'sonet-oc768' - TYPE_SONET_OC1920 = 'sonet-oc1920' - TYPE_SONET_OC3840 = 'sonet-oc3840' - - # Fibrechannel - TYPE_1GFC_SFP = '1gfc-sfp' - TYPE_2GFC_SFP = '2gfc-sfp' - TYPE_4GFC_SFP = '4gfc-sfp' - TYPE_8GFC_SFP_PLUS = '8gfc-sfpp' - TYPE_16GFC_SFP_PLUS = '16gfc-sfpp' - TYPE_32GFC_SFP28 = '32gfc-sfp28' - TYPE_32GFC_SFP_PLUS = '32gfc-sfpp' - TYPE_64GFC_QSFP_PLUS = '64gfc-qsfpp' - TYPE_64GFC_SFP_DD = '64gfc-sfpdd' - TYPE_64GFC_SFP_PLUS = '64gfc-sfpp' - TYPE_128GFC_QSFP28 = '128gfc-qsfp28' - - # InfiniBand - TYPE_INFINIBAND_SDR = 'infiniband-sdr' - TYPE_INFINIBAND_DDR = 'infiniband-ddr' - TYPE_INFINIBAND_QDR = 'infiniband-qdr' - TYPE_INFINIBAND_FDR10 = 'infiniband-fdr10' - TYPE_INFINIBAND_FDR = 'infiniband-fdr' - TYPE_INFINIBAND_EDR = 'infiniband-edr' - TYPE_INFINIBAND_HDR = 'infiniband-hdr' - TYPE_INFINIBAND_NDR = 'infiniband-ndr' - TYPE_INFINIBAND_XDR = 'infiniband-xdr' - - # Serial - TYPE_T1 = 't1' - TYPE_E1 = 'e1' - TYPE_T3 = 't3' - TYPE_E3 = 'e3' - - # ATM/DSL - TYPE_XDSL = 'xdsl' - - # Coaxial - TYPE_DOCSIS = 'docsis' - - # PON - TYPE_BPON = 'bpon' - TYPE_EPON = 'epon' - TYPE_10G_EPON = '10g-epon' - TYPE_GPON = 'gpon' - TYPE_XG_PON = 'xg-pon' - TYPE_XGS_PON = 'xgs-pon' - TYPE_NG_PON2 = 'ng-pon2' - TYPE_25G_PON = '25g-pon' - TYPE_50G_PON = '50g-pon' - - # Stacking - TYPE_STACKWISE = 'cisco-stackwise' - TYPE_STACKWISE_PLUS = 'cisco-stackwise-plus' - TYPE_FLEXSTACK = 'cisco-flexstack' - TYPE_FLEXSTACK_PLUS = 'cisco-flexstack-plus' - TYPE_STACKWISE80 = 'cisco-stackwise-80' - TYPE_STACKWISE160 = 'cisco-stackwise-160' - TYPE_STACKWISE320 = 'cisco-stackwise-320' - TYPE_STACKWISE480 = 'cisco-stackwise-480' - TYPE_STACKWISE1T = 'cisco-stackwise-1t' - TYPE_JUNIPER_VCP = 'juniper-vcp' - TYPE_SUMMITSTACK = 'extreme-summitstack' - TYPE_SUMMITSTACK128 = 'extreme-summitstack-128' - TYPE_SUMMITSTACK256 = 'extreme-summitstack-256' - TYPE_SUMMITSTACK512 = 'extreme-summitstack-512' - - # Other - TYPE_OTHER = 'other' - - -@strawberry.enum -class InterfaceSpeedEnum(Enum): - SPEED_10MBPS = 10000 - SPEED_100MBPS = 100000 - SPEED_1GBPS = 1000000 - SPEED_10GBPS = 10000000 - SPEED_25GBPS = 25000000 - SPEED_40GBPS = 40000000 - SPEED_100GBPS = 100000000 - SPEED_200GBPS = 200000000 - SPEED_400GBPS = 400000000 - - -@strawberry.enum -class InterfaceDuplexEnum(Enum): - - DUPLEX_HALF = 'half' - DUPLEX_FULL = 'full' - DUPLEX_AUTO = 'auto' - - -@strawberry.enum -class InterfaceModeEnum(Enum): - - MODE_ACCESS = 'access' - MODE_TAGGED = 'tagged' - MODE_TAGGED_ALL = 'tagged-all' - MODE_Q_IN_Q = 'q-in-q' - - -@strawberry.enum -class InterfacePoEModeEnum(Enum): - - MODE_PD = 'pd' - MODE_PSE = 'pse' - - -@strawberry.enum -class InterfacePoETypeEnum(Enum): - - TYPE_1_8023AF = 'type1-ieee802.3af' - TYPE_2_8023AT = 'type2-ieee802.3at' - TYPE_3_8023BT = 'type3-ieee802.3bt' - TYPE_4_8023BT = 'type4-ieee802.3bt' - - PASSIVE_24V_2PAIR = 'passive-24v-2pair' - PASSIVE_24V_4PAIR = 'passive-24v-4pair' - PASSIVE_48V_2PAIR = 'passive-48v-2pair' - PASSIVE_48V_4PAIR = 'passive-48v-4pair' - - -# -# FrontPorts/RearPorts -# - -@strawberry.enum -class PortTypeEnum(Enum): - - TYPE_8P8C = '8p8c' - TYPE_8P6C = '8p6c' - TYPE_8P4C = '8p4c' - TYPE_8P2C = '8p2c' - TYPE_6P6C = '6p6c' - TYPE_6P4C = '6p4c' - TYPE_6P2C = '6p2c' - TYPE_4P4C = '4p4c' - TYPE_4P2C = '4p2c' - TYPE_GG45 = 'gg45' - TYPE_TERA4P = 'tera-4p' - TYPE_TERA2P = 'tera-2p' - TYPE_TERA1P = 'tera-1p' - TYPE_110_PUNCH = '110-punch' - TYPE_BNC = 'bnc' - TYPE_F = 'f' - TYPE_N = 'n' - TYPE_MRJ21 = 'mrj21' - TYPE_ST = 'st' - TYPE_SC = 'sc' - TYPE_SC_PC = 'sc-pc' - TYPE_SC_UPC = 'sc-upc' - TYPE_SC_APC = 'sc-apc' - TYPE_FC = 'fc' - TYPE_LC = 'lc' - TYPE_LC_PC = 'lc-pc' - TYPE_LC_UPC = 'lc-upc' - TYPE_LC_APC = 'lc-apc' - TYPE_MTRJ = 'mtrj' - TYPE_MPO = 'mpo' - TYPE_LSH = 'lsh' - TYPE_LSH_PC = 'lsh-pc' - TYPE_LSH_UPC = 'lsh-upc' - TYPE_LSH_APC = 'lsh-apc' - TYPE_LX5 = 'lx5' - TYPE_LX5_PC = 'lx5-pc' - TYPE_LX5_UPC = 'lx5-upc' - TYPE_LX5_APC = 'lx5-apc' - TYPE_SPLICE = 'splice' - TYPE_CS = 'cs' - TYPE_SN = 'sn' - TYPE_SMA_905 = 'sma-905' - TYPE_SMA_906 = 'sma-906' - TYPE_URM_P2 = 'urm-p2' - TYPE_URM_P4 = 'urm-p4' - TYPE_URM_P8 = 'urm-p8' - TYPE_USB_A = 'usb-a' - TYPE_USB_B = 'usb-b' - TYPE_USB_C = 'usb-c' - TYPE_USB_MINI_A = 'usb-mini-a' - TYPE_USB_MINI_B = 'usb-mini-b' - TYPE_USB_MICRO_A = 'usb-micro-a' - TYPE_USB_MICRO_B = 'usb-micro-b' - TYPE_USB_MICRO_AB = 'usb-micro-ab' - TYPE_OTHER = 'other' - - -# -# Cables/links -# - -@strawberry.enum -class CableTypeEnum(Enum): - - TYPE_CAT3 = 'cat3' - TYPE_CAT5 = 'cat5' - TYPE_CAT5E = 'cat5e' - TYPE_CAT6 = 'cat6' - TYPE_CAT6A = 'cat6a' - TYPE_CAT7 = 'cat7' - TYPE_CAT7A = 'cat7a' - TYPE_CAT8 = 'cat8' - TYPE_DAC_ACTIVE = 'dac-active' - TYPE_DAC_PASSIVE = 'dac-passive' - TYPE_MRJ21_TRUNK = 'mrj21-trunk' - TYPE_COAXIAL = 'coaxial' - TYPE_MMF = 'mmf' - TYPE_MMF_OM1 = 'mmf-om1' - TYPE_MMF_OM2 = 'mmf-om2' - TYPE_MMF_OM3 = 'mmf-om3' - TYPE_MMF_OM4 = 'mmf-om4' - TYPE_MMF_OM5 = 'mmf-om5' - TYPE_SMF = 'smf' - TYPE_SMF_OS1 = 'smf-os1' - TYPE_SMF_OS2 = 'smf-os2' - TYPE_AOC = 'aoc' - TYPE_POWER = 'power' - TYPE_USB = 'usb' - - -@strawberry.enum -class LinkStatusEnum(Enum): - - STATUS_CONNECTED = 'connected' - STATUS_PLANNED = 'planned' - STATUS_DECOMMISSIONING = 'decommissioning' - - -@strawberry.enum -class CableLengthUnitEnum(Enum): - - # Metric - UNIT_KILOMETER = 'km' - UNIT_METER = 'm' - UNIT_CENTIMETER = 'cm' - - # Imperial - UNIT_MILE = 'mi' - UNIT_FOOT = 'ft' - UNIT_INCH = 'in' - - -# -# CableTerminations -# - -@strawberry.enum -class CableEndEnum(Enum): - SIDE_A = 'A' - SIDE_B = 'B' - - -# -# PowerFeeds -# - -@strawberry.enum -class PowerFeedStatusEnum(Enum): - STATUS_OFFLINE = 'offline' - STATUS_ACTIVE = 'active' - STATUS_PLANNED = 'planned' - STATUS_FAILED = 'failed' - - -@strawberry.enum -class PowerFeedTypeEnum(Enum): - TYPE_PRIMARY = 'primary' - TYPE_REDUNDANT = 'redundant' - - -@strawberry.enum -class PowerFeedSupplyEnum(Enum): - - SUPPLY_AC = 'ac' - SUPPLY_DC = 'dc' - - -@strawberry.enum -class PowerFeedPhaseEnum(Enum): - - PHASE_SINGLE = 'single-phase' - PHASE_3PHASE = 'three-phase' - - -# -# VDC -# -@strawberry.enum -class VirtualDeviceContextStatusEnum(Enum): - STATUS_ACTIVE = 'active' - STATUS_PLANNED = 'planned' - STATUS_OFFLINE = 'offline' - - -# -# InventoryItem -# - -@strawberry.enum -class InventoryItemStatusEnum(Enum): - STATUS_OFFLINE = 'offline' - STATUS_ACTIVE = 'active' - STATUS_PLANNED = 'planned' - STATUS_STAGED = 'staged' - STATUS_FAILED = 'failed' - STATUS_DECOMMISSIONING = 'decommissioning' +from dcim.choices import * + +__all__ = ( + 'CableEndEnum', + 'CableLengthUnitEnum', + 'CableTypeEnum', + 'ConsolePortSpeedEnum', + 'ConsolePortTypeEnum', + 'DeviceAirflowEnum', + 'DeviceFaceEnum', + 'DeviceStatusEnum', + 'InterfaceDuplexEnum', + 'InterfaceModeEnum', + 'InterfacePoEModeEnum', + 'InterfacePoETypeEnum', + 'InterfaceSpeedEnum', + 'InterfaceTypeEnum', + 'InventoryItemStatusEnum', + 'LinkStatusEnum', + 'LocationStatusEnum', + 'ModuleAirflowEnum', + 'ModuleStatusEnum', + 'PortTypeEnum', + 'PowerFeedPhaseEnum', + 'PowerFeedStatusEnum', + 'PowerFeedSupplyEnum', + 'PowerFeedTypeEnum', + 'PowerOutletFeedLegEnum', + 'PowerOutletTypeEnum', + 'PowerPortTypeEnum', + 'RackAirflowEnum', + 'RackDimensionUnitEnum', + 'RackFormFactorEnum', + 'RackStatusEnum', + 'RackWidthEnum', + 'SiteStatusEnum', + 'SubdeviceRoleEnum', + 'VirtualDeviceContextStatusEnum', +) + +CableEndEnum = strawberry.enum(CableEndChoices.as_enum()) +CableLengthUnitEnum = strawberry.enum(CableLengthUnitChoices.as_enum()) +CableTypeEnum = strawberry.enum(CableTypeChoices.as_enum()) +ConsolePortSpeedEnum = strawberry.enum(ConsolePortSpeedChoices.as_enum()) +ConsolePortTypeEnum = strawberry.enum(ConsolePortTypeChoices.as_enum()) +DeviceAirflowEnum = strawberry.enum(DeviceAirflowChoices.as_enum()) +DeviceFaceEnum = strawberry.enum(DeviceFaceChoices.as_enum()) +DeviceStatusEnum = strawberry.enum(DeviceStatusChoices.as_enum()) +InterfaceDuplexEnum = strawberry.enum(InterfaceDuplexChoices.as_enum()) +InterfaceModeEnum = strawberry.enum(InterfaceModeChoices.as_enum()) +InterfacePoEModeEnum = strawberry.enum(InterfacePoEModeChoices.as_enum()) +InterfacePoETypeEnum = strawberry.enum(InterfacePoETypeChoices.as_enum()) +InterfaceSpeedEnum = strawberry.enum(InterfaceSpeedChoices.as_enum()) +InterfaceTypeEnum = strawberry.enum(InterfaceTypeChoices.as_enum()) +InventoryItemStatusEnum = strawberry.enum(InventoryItemStatusChoices.as_enum()) +LinkStatusEnum = strawberry.enum(LinkStatusChoices.as_enum()) +LocationStatusEnum = strawberry.enum(LocationStatusChoices.as_enum()) +ModuleAirflowEnum = strawberry.enum(ModuleAirflowChoices.as_enum()) +ModuleStatusEnum = strawberry.enum(ModuleStatusChoices.as_enum()) +PortTypeEnum = strawberry.enum(PortTypeChoices.as_enum()) +PowerFeedPhaseEnum = strawberry.enum(PowerFeedPhaseChoices.as_enum()) +PowerFeedStatusEnum = strawberry.enum(PowerFeedStatusChoices.as_enum()) +PowerFeedSupplyEnum = strawberry.enum(PowerFeedSupplyChoices.as_enum()) +PowerFeedTypeEnum = strawberry.enum(PowerFeedTypeChoices.as_enum()) +PowerOutletFeedLegEnum = strawberry.enum(PowerOutletFeedLegChoices.as_enum()) +PowerOutletTypeEnum = strawberry.enum(PowerOutletTypeChoices.as_enum()) +PowerPortTypeEnum = strawberry.enum(PowerPortTypeChoices.as_enum()) +RackAirflowEnum = strawberry.enum(RackAirflowChoices.as_enum()) +RackDimensionUnitEnum = strawberry.enum(RackDimensionUnitChoices.as_enum()) +RackFormFactorEnum = strawberry.enum(RackFormFactorChoices.as_enum()) +RackStatusEnum = strawberry.enum(RackStatusChoices.as_enum()) +RackWidthEnum = strawberry.enum(RackWidthChoices.as_enum()) +SiteStatusEnum = strawberry.enum(SiteStatusChoices.as_enum()) +SubdeviceRoleEnum = strawberry.enum(SubdeviceRoleChoices.as_enum()) +VirtualDeviceContextStatusEnum = strawberry.enum(VirtualDeviceContextStatusChoices.as_enum()) diff --git a/netbox/extras/graphql/enums.py b/netbox/extras/graphql/enums.py index 1e44e91913e..0d352b835f6 100644 --- a/netbox/extras/graphql/enums.py +++ b/netbox/extras/graphql/enums.py @@ -1,188 +1,26 @@ -from enum import Enum import strawberry -__all__ = [ - 'CustomFieldTypeEnum', +from extras.choices import * + +__all__ = ( + 'CustomFieldChoiceSetBaseEnum', 'CustomFieldFilterLogicEnum', - 'CustomFieldUIVisibleEnum', + 'CustomFieldTypeEnum', 'CustomFieldUIEditableEnum', - 'CustomFieldChoiceSetBaseEnum', + 'CustomFieldUIVisibleEnum', 'CustomLinkButtonClassEnum', - 'BookmarkOrderingEnum', + 'EventRuleActionEnum', 'JournalEntryKindEnum', - 'LogLevelEnum', - 'DurationEnum', 'WebhookHttpMethodEnum', - 'ChangeActionEnum', - 'DashboardWidgetColorEnum', - 'EventRuleActionEnum', -] - -# -# CustomFields -# - - -@strawberry.enum -class CustomFieldTypeEnum(Enum): - TYPE_TEXT = 'text' - TYPE_LONGTEXT = 'longtext' - TYPE_INTEGER = 'integer' - TYPE_DECIMAL = 'decimal' - TYPE_BOOLEAN = 'boolean' - TYPE_DATE = 'date' - TYPE_DATETIME = 'datetime' - TYPE_URL = 'url' - TYPE_JSON = 'json' - TYPE_SELECT = 'select' - TYPE_MULTISELECT = 'multiselect' - TYPE_OBJECT = 'object' - TYPE_MULTIOBJECT = 'multiobject' - - -@strawberry.enum -class CustomFieldFilterLogicEnum(Enum): - FILTER_DISABLED = 'disabled' - FILTER_LOOSE = 'loose' - FILTER_EXACT = 'exact' - - -@strawberry.enum -class CustomFieldUIVisibleEnum(Enum): - ALWAYS = 'always' - IF_SET = 'if-set' - HIDDEN = 'hidden' - - -@strawberry.enum -class CustomFieldUIEditableEnum(Enum): - YES = 'yes' - NO = 'no' - HIDDEN = 'hidden' - - -@strawberry.enum -class CustomFieldChoiceSetBaseEnum(Enum): - IATA = 'IATA' - ISO_3166 = 'ISO_3166' - UN_LOCODE = 'UN_LOCODE' - - -# -# CustomLinks -# - - -@strawberry.enum -class CustomLinkButtonClassEnum(Enum): - LINK = 'ghost-dark' - - -# -# Bookmarks -# - - -@strawberry.enum -class BookmarkOrderingEnum(Enum): - ORDERING_NEWEST = '-created' - ORDERING_OLDEST = 'created' - ORDERING_ALPHABETICAL_AZ = 'name' - ORDERING_ALPHABETICAL_ZA = '-name' - - -# -# Journal entries -# - - -@strawberry.enum -class JournalEntryKindEnum(Enum): - KIND_INFO = 'info' - KIND_SUCCESS = 'success' - KIND_WARNING = 'warning' - KIND_DANGER = 'danger' - - -# -# Reports and Scripts -# - - -@strawberry.enum -class LogLevelEnum(Enum): - LOG_DEBUG = 'debug' - LOG_DEFAULT = 'default' - LOG_INFO = 'info' - LOG_SUCCESS = 'success' - LOG_WARNING = 'warning' - LOG_FAILURE = 'failure' - - -@strawberry.enum -class DurationEnum(Enum): - HOURLY = 60 - TWELVE_HOURS = 720 - DAILY = 1440 - WEEKLY = 10080 - THIRTY_DAYS = 43200 - - -# -# Webhooks -# - - -@strawberry.enum -class WebhookHttpMethodEnum(Enum): - METHOD_GET = 'GET' - METHOD_POST = 'POST' - METHOD_PUT = 'PUT' - METHOD_PATCH = 'PATCH' - METHOD_DELETE = 'DELETE' - - -# -# Staging -# - - -@strawberry.enum -class ChangeActionEnum(Enum): - ACTION_CREATE = 'create' - ACTION_UPDATE = 'update' - ACTION_DELETE = 'delete' - - -# -# Dashboard widgets -# - - -@strawberry.enum -class DashboardWidgetColorEnum(Enum): - BLUE = 'blue' - INDIGO = 'indigo' - PURPLE = 'purple' - PINK = 'pink' - RED = 'red' - ORANGE = 'orange' - YELLOW = 'yellow' - GREEN = 'green' - TEAL = 'teal' - CYAN = 'cyan' - GRAY = 'gray' - BLACK = 'black' - WHITE = 'white' - - -# -# Event Rules -# - - -@strawberry.enum -class EventRuleActionEnum(Enum): - WEBHOOK = 'webhook' - SCRIPT = 'script' - NOTIFICATION = 'notification' +) + + +CustomFieldChoiceSetBaseEnum = strawberry.enum(CustomFieldChoiceSetBaseChoices.as_enum()) +CustomFieldFilterLogicEnum = strawberry.enum(CustomFieldFilterLogicChoices.as_enum()) +CustomFieldTypeEnum = strawberry.enum(CustomFieldTypeChoices.as_enum()) +CustomFieldUIEditableEnum = strawberry.enum(CustomFieldUIEditableChoices.as_enum()) +CustomFieldUIVisibleEnum = strawberry.enum(CustomFieldUIVisibleChoices.as_enum()) +CustomLinkButtonClassEnum = strawberry.enum(CustomLinkButtonClassChoices.as_enum()) +EventRuleActionEnum = strawberry.enum(EventRuleActionChoices.as_enum()) +JournalEntryKindEnum = strawberry.enum(JournalEntryKindChoices.as_enum()) +WebhookHttpMethodEnum = strawberry.enum(WebhookHttpMethodChoices.as_enum()) diff --git a/netbox/extras/graphql/filters.py b/netbox/extras/graphql/filters.py index 7fc390b54c3..733fb6423af 100644 --- a/netbox/extras/graphql/filters.py +++ b/netbox/extras/graphql/filters.py @@ -184,7 +184,9 @@ class CustomLinkFilter(BaseObjectTypeFilterMixin, ChangeLogFilterMixin): strawberry_django.filter_field() ) group_name: FilterLookup[str] | None = strawberry_django.filter_field() - button_class: FilterLookup[str] | None = strawberry_django.filter_field() + button_class: Annotated['CustomLinkButtonClassEnum', strawberry.lazy('extras.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) new_window: FilterLookup[bool] | None = strawberry_django.filter_field() diff --git a/netbox/ipam/graphql/enums.py b/netbox/ipam/graphql/enums.py index b8125488072..34fb1a6fdbb 100644 --- a/netbox/ipam/graphql/enums.py +++ b/netbox/ipam/graphql/enums.py @@ -1,123 +1,27 @@ -from enum import Enum import strawberry -__all__ = [ +from ipam.choices import * + +__all__ = ( + 'FHRPGroupAuthTypeEnum', + 'FHRPGroupProtocolEnum', 'IPAddressFamilyEnum', - 'PrefixStatusEnum', - 'IPRangeStatusEnum', - 'IPAddressStatusEnum', 'IPAddressRoleEnum', - 'FHRPGroupProtocolEnum', - 'FHRPGroupAuthTypeEnum', + 'IPAddressStatusEnum', + 'IPRangeStatusEnum', + 'PrefixStatusEnum', + 'ServiceProtocolEnum', 'VLANStatusEnum', 'VLANQinQRoleEnum', - 'ServiceProtocolEnum', -] - - -@strawberry.enum -class IPAddressFamilyEnum(Enum): - FAMILY_4 = 4 - FAMILY_6 = 6 - - -# -# Prefixes -# - - -@strawberry.enum -class PrefixStatusEnum(Enum): - STATUS_CONTAINER = 'container' - STATUS_ACTIVE = 'active' - STATUS_RESERVED = 'reserved' - STATUS_DEPRECATED = 'deprecated' - - -# -# IP Ranges -# - - -@strawberry.enum -class IPRangeStatusEnum(Enum): - STATUS_ACTIVE = 'active' - STATUS_RESERVED = 'reserved' - STATUS_DEPRECATED = 'deprecated' - - -# -# IP Addresses -# - - -@strawberry.enum -class IPAddressStatusEnum(Enum): - STATUS_ACTIVE = 'active' - STATUS_RESERVED = 'reserved' - STATUS_DEPRECATED = 'deprecated' - STATUS_DHCP = 'dhcp' - STATUS_SLAAC = 'slaac' - - -@strawberry.enum -class IPAddressRoleEnum(Enum): - ROLE_LOOPBACK = 'loopback' - ROLE_SECONDARY = 'secondary' - ROLE_ANYCAST = 'anycast' - ROLE_VIP = 'vip' - ROLE_VRRP = 'vrrp' - ROLE_HSRP = 'hsrp' - ROLE_GLBP = 'glbp' - ROLE_CARP = 'carp' - - -# -# FHRP -# - - -@strawberry.enum -class FHRPGroupProtocolEnum(Enum): - PROTOCOL_VRRP2 = 'vrrp2' - PROTOCOL_VRRP3 = 'vrrp3' - PROTOCOL_HSRP = 'hsrp' - PROTOCOL_GLBP = 'glbp' - PROTOCOL_CARP = 'carp' - PROTOCOL_CLUSTERXL = 'clusterxl' - PROTOCOL_OTHER = 'other' - - -@strawberry.enum -class FHRPGroupAuthTypeEnum(Enum): - AUTHENTICATION_PLAINTEXT = 'plaintext' - AUTHENTICATION_MD5 = 'md5' - - -# -# VLANs -# - - -@strawberry.enum -class VLANStatusEnum(Enum): - STATUS_ACTIVE = 'active' - STATUS_RESERVED = 'reserved' - STATUS_DEPRECATED = 'deprecated' - - -@strawberry.enum -class VLANQinQRoleEnum(Enum): - ROLE_SERVICE = 'svlan' - ROLE_CUSTOMER = 'cvlan' - -# -# Services -# - - -@strawberry.enum -class ServiceProtocolEnum(Enum): - PROTOCOL_TCP = 'tcp' - PROTOCOL_UDP = 'udp' - PROTOCOL_SCTP = 'sctp' +) + +FHRPGroupAuthTypeEnum = strawberry.enum(FHRPGroupAuthTypeChoices.as_enum()) +FHRPGroupProtocolEnum = strawberry.enum(FHRPGroupProtocolChoices.as_enum()) +IPAddressFamilyEnum = strawberry.enum(IPAddressFamilyChoices.as_enum()) +IPAddressRoleEnum = strawberry.enum(IPAddressRoleChoices.as_enum()) +IPAddressStatusEnum = strawberry.enum(IPAddressStatusChoices.as_enum()) +IPRangeStatusEnum = strawberry.enum(IPRangeStatusChoices.as_enum()) +PrefixStatusEnum = strawberry.enum(PrefixStatusChoices.as_enum()) +ServiceProtocolEnum = strawberry.enum(ServiceProtocolChoices.as_enum()) +VLANStatusEnum = strawberry.enum(VLANStatusChoices.as_enum()) +VLANQinQRoleEnum = strawberry.enum(VLANQinQRoleChoices.as_enum()) diff --git a/netbox/netbox/graphql/enums.py b/netbox/netbox/graphql/enums.py index d92f9df1487..df62f8b3d86 100644 --- a/netbox/netbox/graphql/enums.py +++ b/netbox/netbox/graphql/enums.py @@ -1,112 +1,13 @@ -from enum import Enum import strawberry -__all__ = [ +from netbox.choices import * + +__all__ = ( 'ColorEnum', - 'ButtonColorEnum', - 'ImportMethodEnumEnum', - 'ImportFormatEnum', 'DistanceUnitEnum', 'WeightUnitEnum', -] - -# -# Generic color choices -# - - -@strawberry.enum -class ColorEnum(Enum): - COLOR_DARK_RED = 'aa1409' - COLOR_RED = 'f44336' - COLOR_PINK = 'e91e63' - COLOR_ROSE = 'ffe4e1' - COLOR_FUCHSIA = 'ff66ff' - COLOR_PURPLE = '9c27b0' - COLOR_DARK_PURPLE = '673ab7' - COLOR_INDIGO = '3f51b5' - COLOR_BLUE = '2196f3' - COLOR_LIGHT_BLUE = '03a9f4' - COLOR_CYAN = '00bcd4' - COLOR_TEAL = '009688' - COLOR_AQUA = '00ffff' - COLOR_DARK_GREEN = '2f6a31' - COLOR_GREEN = '4caf50' - COLOR_LIGHT_GREEN = '8bc34a' - COLOR_LIME = 'cddc39' - COLOR_YELLOW = 'ffeb3b' - COLOR_AMBER = 'ffc107' - COLOR_ORANGE = 'ff9800' - COLOR_DARK_ORANGE = 'ff5722' - COLOR_BROWN = '795548' - COLOR_LIGHT_GREY = 'c0c0c0' - COLOR_GREY = '9e9e9e' - COLOR_DARK_GREY = '607d8b' - COLOR_BLACK = '111111' - COLOR_WHITE = 'ffffff' - - -# -# Button color choices -# - - -@strawberry.enum -class ButtonColorEnum(Enum): - DEFAULT = 'default' - BLUE = 'blue' - INDIGO = 'indigo' - PURPLE = 'purple' - PINK = 'pink' - RED = 'red' - ORANGE = 'orange' - YELLOW = 'yellow' - GREEN = 'green' - TEAL = 'teal' - CYAN = 'cyan' - GRAY = 'gray' - GREY = 'gray' # Backward compatability for <3.2 - BLACK = 'black' - WHITE = 'white' - - -# -# Import -# - - -@strawberry.enum -class ImportMethodEnumEnum(Enum): - DIRECT = 'direct' - UPLOAD = 'upload' - DATA_FILE = 'datafile' - - -@strawberry.enum -class ImportFormatEnum(Enum): - AUTO = 'auto' - CSV = 'csv' - JSON = 'json' - YAML = 'yaml' - - -@strawberry.enum -class DistanceUnitEnum(Enum): - # Metric - UNIT_KILOMETER = 'km' - UNIT_METER = 'm' - - # Imperial - UNIT_MILE = 'mi' - UNIT_FOOT = 'ft' - - -@strawberry.enum -class WeightUnitEnum(Enum): - # Metric - UNIT_KILOGRAM = 'kg' - UNIT_GRAM = 'g' +) - # Imperial - UNIT_POUND = 'lb' - UNIT_OUNCE = 'oz' +ColorEnum = strawberry.enum(ColorChoices.as_enum()) +DistanceUnitEnum = strawberry.enum(DistanceUnitChoices.as_enum()) +WeightUnitEnum = strawberry.enum(WeightUnitChoices.as_enum()) diff --git a/netbox/tenancy/graphql/enums.py b/netbox/tenancy/graphql/enums.py new file mode 100644 index 00000000000..90fc30483f4 --- /dev/null +++ b/netbox/tenancy/graphql/enums.py @@ -0,0 +1,9 @@ +import strawberry + +from tenancy.choices import * + +__all__ = ( + 'ContactPriorityEnum', +) + +ContactPriorityEnum = strawberry.enum(ContactPriorityChoices.as_enum()) diff --git a/netbox/tenancy/graphql/filters.py b/netbox/tenancy/graphql/filters.py index 06af3f8b9d1..a63f8da6059 100644 --- a/netbox/tenancy/graphql/filters.py +++ b/netbox/tenancy/graphql/filters.py @@ -16,6 +16,7 @@ from .filter_mixins import ContactFilterMixin if TYPE_CHECKING: + from .enums import * from netbox.graphql.filter_lookups import TreeNodeFilter from circuits.graphql.filters import * from dcim.graphql.filters import * @@ -158,4 +159,6 @@ class ContactAssignmentFilter(CustomFieldsFilterMixin, TagsFilterMixin, ChangeLo strawberry_django.filter_field() ) role_id: ID | None = strawberry_django.filter_field() - priority: FilterLookup[str] | None = strawberry_django.filter_field() + priority: Annotated['ContactPriorityEnum', strawberry.lazy('tenancy.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) diff --git a/netbox/tenancy/graphql/types.py b/netbox/tenancy/graphql/types.py index 9d537a05c85..a8b338578b9 100644 --- a/netbox/tenancy/graphql/types.py +++ b/netbox/tenancy/graphql/types.py @@ -34,7 +34,7 @@ @strawberry_django.type(models.Tenant, fields='__all__', filters=TenantFilter) class TenantType(NetBoxObjectType): - group: Annotated['TenantGroupType', strawberry.lazy('tenancy.graphql.types')] + group: Annotated['TenantGroupType', strawberry.lazy('tenancy.graphql.types')] | None contacts: List[Annotated['ContactType', strawberry.lazy('tenancy.graphql.types')]] asns: List[Annotated['ASNType', strawberry.lazy('ipam.graphql.types')]] circuits: List[Annotated['CircuitType', strawberry.lazy('circuits.graphql.types')]] diff --git a/netbox/utilities/choices.py b/netbox/utilities/choices.py index 25d05594294..7b3648afa37 100644 --- a/netbox/utilities/choices.py +++ b/netbox/utilities/choices.py @@ -1,3 +1,5 @@ +import enum + from django.conf import settings from django.utils.translation import gettext_lazy as _ @@ -65,6 +67,23 @@ class ChoiceSet(metaclass=ChoiceSetMeta): def values(cls): return [c[0] for c in unpack_grouped_choices(cls._choices)] + @classmethod + def as_enum(cls, name=None): + """ + Return the ChoiceSet as an Enum. If no name is provided, "Choices" will be stripped from the class name (if + present) and "Enum" will be appended. For example, "CircuitStatusChoices" will become "CircuitStatusEnum". + """ + name = name or f"{cls.__name__.split('Choices')[0]}Enum" + data = {} + choices = cls.values() + + for attr in dir(cls): + value = getattr(cls, attr) + if attr.isupper() and value in choices: + data[attr] = value + + return enum.Enum(name, data) + def unpack_grouped_choices(choices): """ diff --git a/netbox/virtualization/graphql/enums.py b/netbox/virtualization/graphql/enums.py index 081669f1f21..5b1c54e0cd1 100644 --- a/netbox/virtualization/graphql/enums.py +++ b/netbox/virtualization/graphql/enums.py @@ -1,33 +1,11 @@ -from enum import Enum import strawberry -__all__ = ['ClusterStatusEnum', 'VirtualMachineStatusEnum'] +from virtualization.choices import * +__all__ = ( + 'ClusterStatusEnum', + 'VirtualMachineStatusEnum', +) -# -# Clusters -# - -@strawberry.enum -class ClusterStatusEnum(Enum): - - STATUS_PLANNED = 'planned' - STATUS_STAGING = 'staging' - STATUS_ACTIVE = 'active' - STATUS_DECOMMISSIONING = 'decommissioning' - STATUS_OFFLINE = 'offline' - - -# -# VirtualMachines -# - -@strawberry.enum -class VirtualMachineStatusEnum(Enum): - - STATUS_OFFLINE = 'offline' - STATUS_ACTIVE = 'active' - STATUS_PLANNED = 'planned' - STATUS_STAGED = 'staged' - STATUS_FAILED = 'failed' - STATUS_DECOMMISSIONING = 'decommissioning' +ClusterStatusEnum = strawberry.enum(ClusterStatusChoices.as_enum()) +VirtualMachineStatusEnum = strawberry.enum(VirtualMachineStatusChoices.as_enum()) diff --git a/netbox/vpn/graphql/enums.py b/netbox/vpn/graphql/enums.py index 9dc6bdac46b..053932ade2f 100644 --- a/netbox/vpn/graphql/enums.py +++ b/netbox/vpn/graphql/enums.py @@ -1,159 +1,31 @@ -from enum import Enum import strawberry -__all__ = [ - 'TunnelStatusEnum', - 'TunnelEncapsulationEnum', - 'TunnelTerminationTypeEnum', - 'TunnelTerminationRoleEnum', - 'IKEVersionEnum', - 'IKEModeEnum', - 'AuthenticationMethodEnum', - 'IPSecModeEnum', - 'EncryptionAlgorithmEnum', +from vpn.choices import * + +__all__ = ( 'AuthenticationAlgorithmEnum', + 'AuthenticationMethodEnum', 'DHGroupEnum', + 'EncryptionAlgorithmEnum', + 'IKEModeEnum', + 'IKEVersionEnum', + 'IPSecModeEnum', 'L2VPNTypeEnum', -] - -# -# Tunnels -# - - -@strawberry.enum -class TunnelStatusEnum(Enum): - STATUS_PLANNED = 'planned' - STATUS_ACTIVE = 'active' - STATUS_DISABLED = 'disabled' - - -@strawberry.enum -class TunnelEncapsulationEnum(Enum): - ENCAP_GRE = 'gre' - ENCAP_IPSEC_TRANSPORT = 'ipsec-transport' - ENCAP_IPSEC_TUNNEL = 'ipsec-tunnel' - ENCAP_IP_IP = 'ip-ip' - ENCAP_L2TP = 'l2tp' - ENCAP_OPENVPN = 'openvpn' - ENCAP_PPTP = 'pptp' - ENCAP_WIREGUARD = 'wireguard' - - -@strawberry.enum -class TunnelTerminationTypeEnum(Enum): - # For TunnelCreateForm - TYPE_DEVICE = 'dcim.device' - TYPE_VIRTUALMACHINE = 'virtualization.virtualmachine' - - -@strawberry.enum -class TunnelTerminationRoleEnum(Enum): - ROLE_PEER = 'peer' - ROLE_HUB = 'hub' - ROLE_SPOKE = 'spoke' - - -# -# Crypto -# - - -@strawberry.enum -class IKEVersionEnum(Enum): - VERSION_1 = 1 - VERSION_2 = 2 - - -@strawberry.enum -class IKEModeEnum(Enum): - AGGRESSIVE = 'aggressive' - MAIN = 'main' - - -@strawberry.enum -class AuthenticationMethodEnum(Enum): - PRESHARED_KEYS = 'preshared-keys' - CERTIFICATES = 'certificates' - RSA_SIGNATURES = 'rsa-signatures' - DSA_SIGNATURES = 'dsa-signatures' - - -@strawberry.enum -class IPSecModeEnum(Enum): - ESP = 'esp' - AH = 'ah' - - -@strawberry.enum -class EncryptionAlgorithmEnum(Enum): - ENCRYPTION_AES128_CBC = 'aes-128-cbc' - ENCRYPTION_AES128_GCM = 'aes-128-gcm' - ENCRYPTION_AES192_CBC = 'aes-192-cbc' - ENCRYPTION_AES192_GCM = 'aes-192-gcm' - ENCRYPTION_AES256_CBC = 'aes-256-cbc' - ENCRYPTION_AES256_GCM = 'aes-256-gcm' - ENCRYPTION_3DES = '3des-cbc' - ENCRYPTION_DES = 'des-cbc' - - -@strawberry.enum -class AuthenticationAlgorithmEnum(Enum): - AUTH_HMAC_SHA1 = 'hmac-sha1' - AUTH_HMAC_SHA256 = 'hmac-sha256' - AUTH_HMAC_SHA384 = 'hmac-sha384' - AUTH_HMAC_SHA512 = 'hmac-sha512' - AUTH_HMAC_MD5 = 'hmac-md5' - - -@strawberry.enum -class DHGroupEnum(Enum): - # https://www.iana.org/assignments/ikev2-parameters/ikev2-parameters.xhtml#ikev2-parameters-8 - GROUP_1 = 1 # 768-bit MODP - GROUP_2 = 2 # 1024-but MODP - # Groups 3-4 reserved - GROUP_5 = 5 # 1536-bit MODP - # Groups 6-13 unassigned - GROUP_14 = 14 # 2048-bit MODP - GROUP_15 = 15 # 3072-bit MODP - GROUP_16 = 16 # 4096-bit MODP - GROUP_17 = 17 # 6144-bit MODP - GROUP_18 = 18 # 8192-bit MODP - GROUP_19 = 19 # 256-bit random ECP - GROUP_20 = 20 # 384-bit random ECP - GROUP_21 = 21 # 521-bit random ECP (521 is not a typo) - GROUP_22 = 22 # 1024-bit MODP w/160-bit prime - GROUP_23 = 23 # 2048-bit MODP w/224-bit prime - GROUP_24 = 24 # 2048-bit MODP w/256-bit prime - GROUP_25 = 25 # 192-bit ECP - GROUP_26 = 26 # 224-bit ECP - GROUP_27 = 27 # brainpoolP224r1 - GROUP_28 = 28 # brainpoolP256r1 - GROUP_29 = 29 # brainpoolP384r1 - GROUP_30 = 30 # brainpoolP512r1 - GROUP_31 = 31 # Curve25519 - GROUP_32 = 32 # Curve448 - GROUP_33 = 33 # GOST3410_2012_256 - GROUP_34 = 34 # GOST3410_2012_512 - - -# -# L2VPN -# - - -@strawberry.enum -class L2VPNTypeEnum(Enum): - TYPE_VPLS = 'vpls' - TYPE_VPWS = 'vpws' - TYPE_EPL = 'epl' - TYPE_EVPL = 'evpl' - TYPE_EPLAN = 'ep-lan' - TYPE_EVPLAN = 'evp-lan' - TYPE_EPTREE = 'ep-tree' - TYPE_EVPTREE = 'evp-tree' - TYPE_VXLAN = 'vxlan' - TYPE_VXLAN_EVPN = 'vxlan-evpn' - TYPE_MPLS_EVPN = 'mpls-evpn' - TYPE_PBB_EVPN = 'pbb-evpn' - TYPE_EVPN_VPWS = 'evpn-vpws' + 'TunnelEncapsulationEnum', + 'TunnelStatusEnum', + 'TunnelTerminationRoleEnum', + 'TunnelTerminationTypeEnum', +) + +AuthenticationAlgorithmEnum = strawberry.enum(AuthenticationAlgorithmChoices.as_enum()) +AuthenticationMethodEnum = strawberry.enum(AuthenticationMethodChoices.as_enum()) +DHGroupEnum = strawberry.enum(DHGroupChoices.as_enum()) +EncryptionAlgorithmEnum = strawberry.enum(EncryptionAlgorithmChoices.as_enum()) +IKEModeEnum = strawberry.enum(IKEModeChoices.as_enum()) +IKEVersionEnum = strawberry.enum(IKEVersionChoices.as_enum()) +IPSecModeEnum = strawberry.enum(IPSecModeChoices.as_enum()) +L2VPNTypeEnum = strawberry.enum(L2VPNTypeChoices.as_enum()) +TunnelEncapsulationEnum = strawberry.enum(TunnelEncapsulationChoices.as_enum()) +TunnelStatusEnum = strawberry.enum(TunnelStatusChoices.as_enum()) +TunnelTerminationRoleEnum = strawberry.enum(TunnelTerminationRoleChoices.as_enum()) +TunnelTerminationTypeEnum = strawberry.enum(TunnelTerminationTypeChoices.as_enum()) diff --git a/netbox/wireless/graphql/enums.py b/netbox/wireless/graphql/enums.py index da8368cde3e..d3c6ad21abf 100644 --- a/netbox/wireless/graphql/enums.py +++ b/netbox/wireless/graphql/enums.py @@ -1,247 +1,17 @@ -from enum import Enum import strawberry -__all__ = [ - 'WirelessRoleEnum', - 'WirelessLANStatusEnum', - 'WirelessChannelEnum', - 'WirelessAuthTypeEnum', - 'WirelessAuthCipherEnum', -] - - -@strawberry.enum -class WirelessRoleEnum(Enum): - ROLE_AP = 'ap' - ROLE_STATION = 'station' - - -@strawberry.enum -class WirelessLANStatusEnum(Enum): - STATUS_ACTIVE = 'active' - STATUS_RESERVED = 'reserved' - STATUS_DISABLED = 'disabled' - STATUS_DEPRECATED = 'deprecated' - - -@strawberry.enum -class WirelessChannelEnum(Enum): - # 2.4 GHz - CHANNEL_24G_1 = '2.4g-1-2412-22' - CHANNEL_24G_2 = '2.4g-2-2417-22' - CHANNEL_24G_3 = '2.4g-3-2422-22' - CHANNEL_24G_4 = '2.4g-4-2427-22' - CHANNEL_24G_5 = '2.4g-5-2432-22' - CHANNEL_24G_6 = '2.4g-6-2437-22' - CHANNEL_24G_7 = '2.4g-7-2442-22' - CHANNEL_24G_8 = '2.4g-8-2447-22' - CHANNEL_24G_9 = '2.4g-9-2452-22' - CHANNEL_24G_10 = '2.4g-10-2457-22' - CHANNEL_24G_11 = '2.4g-11-2462-22' - CHANNEL_24G_12 = '2.4g-12-2467-22' - CHANNEL_24G_13 = '2.4g-13-2472-22' - - # 5 GHz - CHANNEL_5G_32 = '5g-32-5160-20' - CHANNEL_5G_34 = '5g-34-5170-40' - CHANNEL_5G_36 = '5g-36-5180-20' - CHANNEL_5G_38 = '5g-38-5190-40' - CHANNEL_5G_40 = '5g-40-5200-20' - CHANNEL_5G_42 = '5g-42-5210-80' - CHANNEL_5G_44 = '5g-44-5220-20' - CHANNEL_5G_46 = '5g-46-5230-40' - CHANNEL_5G_48 = '5g-48-5240-20' - CHANNEL_5G_50 = '5g-50-5250-160' - CHANNEL_5G_52 = '5g-52-5260-20' - CHANNEL_5G_54 = '5g-54-5270-40' - CHANNEL_5G_56 = '5g-56-5280-20' - CHANNEL_5G_58 = '5g-58-5290-80' - CHANNEL_5G_60 = '5g-60-5300-20' - CHANNEL_5G_62 = '5g-62-5310-40' - CHANNEL_5G_64 = '5g-64-5320-20' - CHANNEL_5G_100 = '5g-100-5500-20' - CHANNEL_5G_102 = '5g-102-5510-40' - CHANNEL_5G_104 = '5g-104-5520-20' - CHANNEL_5G_106 = '5g-106-5530-80' - CHANNEL_5G_108 = '5g-108-5540-20' - CHANNEL_5G_110 = '5g-110-5550-40' - CHANNEL_5G_112 = '5g-112-5560-20' - CHANNEL_5G_114 = '5g-114-5570-160' - CHANNEL_5G_116 = '5g-116-5580-20' - CHANNEL_5G_118 = '5g-118-5590-40' - CHANNEL_5G_120 = '5g-120-5600-20' - CHANNEL_5G_122 = '5g-122-5610-80' - CHANNEL_5G_124 = '5g-124-5620-20' - CHANNEL_5G_126 = '5g-126-5630-40' - CHANNEL_5G_128 = '5g-128-5640-20' - CHANNEL_5G_132 = '5g-132-5660-20' - CHANNEL_5G_134 = '5g-134-5670-40' - CHANNEL_5G_136 = '5g-136-5680-20' - CHANNEL_5G_138 = '5g-138-5690-80' - CHANNEL_5G_140 = '5g-140-5700-20' - CHANNEL_5G_142 = '5g-142-5710-40' - CHANNEL_5G_144 = '5g-144-5720-20' - CHANNEL_5G_149 = '5g-149-5745-20' - CHANNEL_5G_151 = '5g-151-5755-40' - CHANNEL_5G_153 = '5g-153-5765-20' - CHANNEL_5G_155 = '5g-155-5775-80' - CHANNEL_5G_157 = '5g-157-5785-20' - CHANNEL_5G_159 = '5g-159-5795-40' - CHANNEL_5G_161 = '5g-161-5805-20' - CHANNEL_5G_163 = '5g-163-5815-160' - CHANNEL_5G_165 = '5g-165-5825-20' - CHANNEL_5G_167 = '5g-167-5835-40' - CHANNEL_5G_169 = '5g-169-5845-20' - CHANNEL_5G_171 = '5g-171-5855-80' - CHANNEL_5G_173 = '5g-173-5865-20' - CHANNEL_5G_175 = '5g-175-5875-40' - CHANNEL_5G_177 = '5g-177-5885-20' - - # 6 GHz - CHANNEL_6G_1 = '6g-1-5955-20' - CHANNEL_6G_3 = '6g-3-5965-40' - CHANNEL_6G_5 = '6g-5-5975-20' - CHANNEL_6G_7 = '6g-7-5985-80' - CHANNEL_6G_9 = '6g-9-5995-20' - CHANNEL_6G_11 = '6g-11-6005-40' - CHANNEL_6G_13 = '6g-13-6015-20' - CHANNEL_6G_15 = '6g-15-6025-160' - CHANNEL_6G_17 = '6g-17-6035-20' - CHANNEL_6G_19 = '6g-19-6045-40' - CHANNEL_6G_21 = '6g-21-6055-20' - CHANNEL_6G_23 = '6g-23-6065-80' - CHANNEL_6G_25 = '6g-25-6075-20' - CHANNEL_6G_27 = '6g-27-6085-40' - CHANNEL_6G_29 = '6g-29-6095-20' - CHANNEL_6G_31 = '6g-31-6105-320' - CHANNEL_6G_33 = '6g-33-6115-20' - CHANNEL_6G_35 = '6g-35-6125-40' - CHANNEL_6G_37 = '6g-37-6135-20' - CHANNEL_6G_39 = '6g-39-6145-80' - CHANNEL_6G_41 = '6g-41-6155-20' - CHANNEL_6G_43 = '6g-43-6165-40' - CHANNEL_6G_45 = '6g-45-6175-20' - CHANNEL_6G_47 = '6g-47-6185-160' - CHANNEL_6G_49 = '6g-49-6195-20' - CHANNEL_6G_51 = '6g-51-6205-40' - CHANNEL_6G_53 = '6g-53-6215-20' - CHANNEL_6G_55 = '6g-55-6225-80' - CHANNEL_6G_57 = '6g-57-6235-20' - CHANNEL_6G_59 = '6g-59-6245-40' - CHANNEL_6G_61 = '6g-61-6255-20' - CHANNEL_6G_65 = '6g-65-6275-20' - CHANNEL_6G_67 = '6g-67-6285-40' - CHANNEL_6G_69 = '6g-69-6295-20' - CHANNEL_6G_71 = '6g-71-6305-80' - CHANNEL_6G_73 = '6g-73-6315-20' - CHANNEL_6G_75 = '6g-75-6325-40' - CHANNEL_6G_77 = '6g-77-6335-20' - CHANNEL_6G_79 = '6g-79-6345-160' - CHANNEL_6G_81 = '6g-81-6355-20' - CHANNEL_6G_83 = '6g-83-6365-40' - CHANNEL_6G_85 = '6g-85-6375-20' - CHANNEL_6G_87 = '6g-87-6385-80' - CHANNEL_6G_89 = '6g-89-6395-20' - CHANNEL_6G_91 = '6g-91-6405-40' - CHANNEL_6G_93 = '6g-93-6415-20' - CHANNEL_6G_95 = '6g-95-6425-320' - CHANNEL_6G_97 = '6g-97-6435-20' - CHANNEL_6G_99 = '6g-99-6445-40' - CHANNEL_6G_101 = '6g-101-6455-20' - CHANNEL_6G_103 = '6g-103-6465-80' - CHANNEL_6G_105 = '6g-105-6475-20' - CHANNEL_6G_107 = '6g-107-6485-40' - CHANNEL_6G_109 = '6g-109-6495-20' - CHANNEL_6G_111 = '6g-111-6505-160' - CHANNEL_6G_113 = '6g-113-6515-20' - CHANNEL_6G_115 = '6g-115-6525-40' - CHANNEL_6G_117 = '6g-117-6535-20' - CHANNEL_6G_119 = '6g-119-6545-80' - CHANNEL_6G_121 = '6g-121-6555-20' - CHANNEL_6G_123 = '6g-123-6565-40' - CHANNEL_6G_125 = '6g-125-6575-20' - CHANNEL_6G_129 = '6g-129-6595-20' - CHANNEL_6G_131 = '6g-131-6605-40' - CHANNEL_6G_133 = '6g-133-6615-20' - CHANNEL_6G_135 = '6g-135-6625-80' - CHANNEL_6G_137 = '6g-137-6635-20' - CHANNEL_6G_139 = '6g-139-6645-40' - CHANNEL_6G_141 = '6g-141-6655-20' - CHANNEL_6G_143 = '6g-143-6665-160' - CHANNEL_6G_145 = '6g-145-6675-20' - CHANNEL_6G_147 = '6g-147-6685-40' - CHANNEL_6G_149 = '6g-149-6695-20' - CHANNEL_6G_151 = '6g-151-6705-80' - CHANNEL_6G_153 = '6g-153-6715-20' - CHANNEL_6G_155 = '6g-155-6725-40' - CHANNEL_6G_157 = '6g-157-6735-20' - CHANNEL_6G_159 = '6g-159-6745-320' - CHANNEL_6G_161 = '6g-161-6755-20' - CHANNEL_6G_163 = '6g-163-6765-40' - CHANNEL_6G_165 = '6g-165-6775-20' - CHANNEL_6G_167 = '6g-167-6785-80' - CHANNEL_6G_169 = '6g-169-6795-20' - CHANNEL_6G_171 = '6g-171-6805-40' - CHANNEL_6G_173 = '6g-173-6815-20' - CHANNEL_6G_175 = '6g-175-6825-160' - CHANNEL_6G_177 = '6g-177-6835-20' - CHANNEL_6G_179 = '6g-179-6845-40' - CHANNEL_6G_181 = '6g-181-6855-20' - CHANNEL_6G_183 = '6g-183-6865-80' - CHANNEL_6G_185 = '6g-185-6875-20' - CHANNEL_6G_187 = '6g-187-6885-40' - CHANNEL_6G_189 = '6g-189-6895-20' - CHANNEL_6G_193 = '6g-193-6915-20' - CHANNEL_6G_195 = '6g-195-6925-40' - CHANNEL_6G_197 = '6g-197-6935-20' - CHANNEL_6G_199 = '6g-199-6945-80' - CHANNEL_6G_201 = '6g-201-6955-20' - CHANNEL_6G_203 = '6g-203-6965-40' - CHANNEL_6G_205 = '6g-205-6975-20' - CHANNEL_6G_207 = '6g-207-6985-160' - CHANNEL_6G_209 = '6g-209-6995-20' - CHANNEL_6G_211 = '6g-211-7005-40' - CHANNEL_6G_213 = '6g-213-7015-20' - CHANNEL_6G_215 = '6g-215-7025-80' - CHANNEL_6G_217 = '6g-217-7035-20' - CHANNEL_6G_219 = '6g-219-7045-40' - CHANNEL_6G_221 = '6g-221-7055-20' - CHANNEL_6G_225 = '6g-225-7075-20' - CHANNEL_6G_227 = '6g-227-7085-40' - CHANNEL_6G_229 = '6g-229-7095-20' - CHANNEL_6G_233 = '6g-233-7115-20' - - # 60 GHz - CHANNEL_60G_1 = '60g-1-58320-2160' - CHANNEL_60G_2 = '60g-2-60480-2160' - CHANNEL_60G_3 = '60g-3-62640-2160' - CHANNEL_60G_4 = '60g-4-64800-2160' - CHANNEL_60G_5 = '60g-5-66960-2160' - CHANNEL_60G_6 = '60g-6-69120-2160' - CHANNEL_60G_9 = '60g-9-59400-4320' - CHANNEL_60G_10 = '60g-10-61560-4320' - CHANNEL_60G_11 = '60g-11-63720-4320' - CHANNEL_60G_12 = '60g-12-65880-4320' - CHANNEL_60G_13 = '60g-13-68040-4320' - CHANNEL_60G_17 = '60g-17-60480-6480' - CHANNEL_60G_18 = '60g-18-62640-6480' - CHANNEL_60G_19 = '60g-19-64800-6480' - CHANNEL_60G_20 = '60g-20-66960-6480' - CHANNEL_60G_25 = '60g-25-61560-6480' - CHANNEL_60G_26 = '60g-26-63720-6480' - CHANNEL_60G_27 = '60g-27-65880-6480' - - -@strawberry.enum -class WirelessAuthTypeEnum(Enum): - TYPE_OPEN = 'open' - TYPE_WEP = 'wep' - TYPE_WPA_PERSONAL = 'wpa-personal' - TYPE_WPA_ENTERPRISE = 'wpa-enterprise' +from wireless.choices import * +__all__ = ( + 'WirelessAuthCipherEnum', + 'WirelessAuthTypeEnum', + 'WirelessChannelEnum', + 'WirelessLANStatusEnum', + 'WirelessRoleEnum', +) -@strawberry.enum -class WirelessAuthCipherEnum(Enum): - CIPHER_AUTO = 'auto' - CIPHER_TKIP = 'tkip' - CIPHER_AES = 'aes' +WirelessAuthCipherEnum = strawberry.enum(WirelessAuthCipherChoices.as_enum()) +WirelessAuthTypeEnum = strawberry.enum(WirelessAuthTypeChoices.as_enum()) +WirelessChannelEnum = strawberry.enum(WirelessChannelChoices.as_enum()) +WirelessLANStatusEnum = strawberry.enum(WirelessLANStatusChoices.as_enum()) +WirelessRoleEnum = strawberry.enum(WirelessRoleChoices.as_enum()) From 186be5f5d67d6bce0f669fcce6ec4d4e209bf272 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 25 Feb 2025 15:03:36 -0500 Subject: [PATCH 07/13] Update GraphQL filter documentation --- docs/development/adding-models.md | 8 ++++-- docs/integrations/graphql-api.md | 39 ++++++++++++++++++++++---- docs/plugins/development/filtersets.md | 2 +- 3 files changed, 40 insertions(+), 9 deletions(-) diff --git a/docs/development/adding-models.md b/docs/development/adding-models.md index 0bf02066268..da0e49511a3 100644 --- a/docs/development/adding-models.md +++ b/docs/development/adding-models.md @@ -76,11 +76,13 @@ Create the following for each model: ## 13. GraphQL API components -Create a GraphQL object type for the model in `graphql/types.py` by subclassing the appropriate class from `netbox.graphql.types`. +Create the following for each model: -**Note:** GraphQL unit tests may fail citing null values on a non-nullable field if related objects are prefetched. You may need to fix this by setting the type annotation to be `= strawberry_django.field(select_related=["policy"])` or similar. +* GraphQL object type for the model in `graphql/types.py` (subclass the appropriate class from `netbox.graphql.types`) +* Add a GraphQL filter for the model in `graphql/filters.py` +* Extend the query class for the app in `graphql/schema.py` with the individual object and object list fields -Also extend the schema class defined in `graphql/schema.py` with the individual object and object list fields per the established convention. +**Note:** GraphQL unit tests may fail citing null values on a non-nullable field if related objects are prefetched. You may need to fix this by setting the type annotation to be `= strawberry_django.field(select_related=["foo"])` or similar. ## 14. Add tests diff --git a/docs/integrations/graphql-api.md b/docs/integrations/graphql-api.md index c02045f347b..23824ad2b46 100644 --- a/docs/integrations/graphql-api.md +++ b/docs/integrations/graphql-api.md @@ -11,7 +11,7 @@ curl -H "Authorization: Token $TOKEN" \ -H "Content-Type: application/json" \ -H "Accept: application/json" \ http://netbox/graphql/ \ ---data '{"query": "query {circuit_list(status:\"active\") {cid provider {name}}}"}' +--data '{"query": "query {circuit_list(filters:{status: STATUS_ACTIVE}) {cid provider {name}}}"}' ``` The response will include the requested data formatted as JSON: @@ -51,19 +51,48 @@ For more detail on constructing GraphQL queries, see the [GraphQL queries docume ## Filtering -The GraphQL API employs the same filtering logic as the UI and REST API. Filters can be specified as key-value pairs within parentheses immediately following the query name. For example, the following will return only sites within the North Carolina region with a status of active: +!!! note "Changed in NetBox v4.3" + The filtering syntax fo the GraphQL API has changed substantially in NetBox v4.3. + +Filters can be specified as key-value pairs within parentheses immediately following the query name. For example, the following will return only active sites: ``` query { - site_list(filters: {region: "us-nc", status: "active"}) { + site_list( + filters: { + status: STATUS_ACTIVE + } + ) { name } } ``` -In addition, filtering can be done on list of related objects as shown in the following query: + +Filters can be combined with logical operators, such as `OR` and `NOT`. For example, the following will return every site that is planned _or_ assigned to a tenant named Foo: ``` -{ +query { + site_list( + filters: { + status: STATUS_PLANNED, + OR: { + tenant: { + name: { + exact: "Foo" + } + } + } + } + ) { + name + } +} +``` + +Filtering can also be applied to related objects. For example, the following query will return only enabled interfaces for each device: + +``` +query { device_list { id name diff --git a/docs/plugins/development/filtersets.md b/docs/plugins/development/filtersets.md index d803ce2f4b5..22480239733 100644 --- a/docs/plugins/development/filtersets.md +++ b/docs/plugins/development/filtersets.md @@ -1,6 +1,6 @@ # Filters & Filter Sets -Filter sets define the mechanisms available for filtering or searching through a set of objects in NetBox. For instance, sites can be filtered by their parent region or group, status, facility ID, and so on. The same filter set is used consistently for a model whether the request is made via the UI, REST API, or GraphQL API. NetBox employs the [django-filters2](https://django-tables2.readthedocs.io/en/latest/) library to define filter sets. +Filter sets define the mechanisms available for filtering or searching through a set of objects in NetBox. For instance, sites can be filtered by their parent region or group, status, facility ID, and so on. The same filter set is used consistently for a model whether the request is made via the UI or REST API. (Note that the GraphQL API uses a separate filter class.) NetBox employs the [django-filters2](https://django-tables2.readthedocs.io/en/latest/) library to define filter sets. ## FilterSet Classes From 5fcf32cc9e8208f384185c6ac1df59788cffdcbc Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 4 Mar 2025 15:14:21 -0500 Subject: [PATCH 08/13] Apply tenant filters consistently --- netbox/circuits/graphql/filters.py | 25 +++++------ netbox/dcim/graphql/filters.py | 36 +++------------- netbox/ipam/graphql/filters.py | 54 ++++-------------------- netbox/tenancy/graphql/filter_mixins.py | 4 +- netbox/virtualization/graphql/filters.py | 11 +---- netbox/vpn/graphql/filters.py | 12 +----- netbox/wireless/graphql/filters.py | 17 +++----- 7 files changed, 38 insertions(+), 121 deletions(-) diff --git a/netbox/circuits/graphql/filters.py b/netbox/circuits/graphql/filters.py index 8e94ec2c2eb..a3f2f2e8565 100644 --- a/netbox/circuits/graphql/filters.py +++ b/netbox/circuits/graphql/filters.py @@ -74,7 +74,13 @@ class CircuitTerminationFilter( @strawberry_django.filter(models.Circuit, lookups=True) -class CircuitFilter(ContactFilterMixin, ImageAttachmentFilterMixin, DistanceFilterMixin, PrimaryModelFilterMixin): +class CircuitFilter( + ContactFilterMixin, + ImageAttachmentFilterMixin, + DistanceFilterMixin, + TenancyFilterMixin, + PrimaryModelFilterMixin +): cid: FilterLookup[str] | None = strawberry_django.filter_field() provider: Annotated['ProviderFilter', strawberry.lazy('circuits.graphql.filters')] | None = ( strawberry_django.filter_field() @@ -91,10 +97,6 @@ class CircuitFilter(ContactFilterMixin, ImageAttachmentFilterMixin, DistanceFilt status: Annotated['CircuitStatusEnum', strawberry.lazy('circuits.graphql.enums')] | None = ( strawberry_django.filter_field() ) - tenant: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( - strawberry_django.filter_field() - ) - tenant_id: ID | None = strawberry_django.filter_field() install_date: DateFilterLookup[date] | None = strawberry_django.filter_field() termination_date: DateFilterLookup[date] | None = strawberry_django.filter_field() commit_rate: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( @@ -108,11 +110,8 @@ class CircuitTypeFilter(BaseCircuitTypeFilterMixin): @strawberry_django.filter(models.CircuitGroup, lookups=True) -class CircuitGroupFilter(OrganizationalModelFilterMixin): - tenant: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( - strawberry_django.filter_field() - ) - tenant_id: ID | None = strawberry_django.filter_field() +class CircuitGroupFilter(TenancyFilterMixin, OrganizationalModelFilterMixin): + pass @strawberry_django.filter(models.CircuitGroupAssignment, lookups=True) @@ -165,7 +164,7 @@ class VirtualCircuitTypeFilter(BaseCircuitTypeFilterMixin): @strawberry_django.filter(models.VirtualCircuit, lookups=True) -class VirtualCircuitFilter(PrimaryModelFilterMixin): +class VirtualCircuitFilter(TenancyFilterMixin, PrimaryModelFilterMixin): cid: FilterLookup[str] | None = strawberry_django.filter_field() provider_network: Annotated['ProviderNetworkFilter', strawberry.lazy('circuits.graphql.filters')] | None = ( strawberry_django.filter_field() @@ -182,10 +181,6 @@ class VirtualCircuitFilter(PrimaryModelFilterMixin): status: Annotated['CircuitStatusEnum', strawberry.lazy('circuits.graphql.enums')] | None = ( strawberry_django.filter_field() ) - tenant: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( - strawberry_django.filter_field() - ) - tenant_id: ID | None = strawberry_django.filter_field() group_assignments: Annotated['CircuitGroupAssignmentFilter', strawberry.lazy('circuits.graphql.filters')] | None = ( strawberry_django.filter_field() ) diff --git a/netbox/dcim/graphql/filters.py b/netbox/dcim/graphql/filters.py index fff31210670..1a146f82de1 100644 --- a/netbox/dcim/graphql/filters.py +++ b/netbox/dcim/graphql/filters.py @@ -481,16 +481,12 @@ class InventoryItemRoleFilter(OrganizationalModelFilterMixin): @strawberry_django.filter(models.Location, lookups=True) -class LocationFilter(ContactFilterMixin, ImageAttachmentFilterMixin, NestedGroupModelFilterMixin): +class LocationFilter(ContactFilterMixin, ImageAttachmentFilterMixin, TenancyFilterMixin, NestedGroupModelFilterMixin): site: Annotated['SiteFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() site_id: ID | None = strawberry_django.filter_field() status: Annotated['LocationStatusEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( strawberry_django.filter_field() ) - tenant: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( - strawberry_django.filter_field() - ) - tenant_id: ID | None = strawberry_django.filter_field() facility: FilterLookup[str] | None = strawberry_django.filter_field() prefixes: Annotated['PrefixFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( strawberry_django.filter_field() @@ -564,7 +560,7 @@ class PlatformFilter(OrganizationalModelFilterMixin): @strawberry_django.filter(models.PowerFeed, lookups=True) -class PowerFeedFilter(PrimaryModelFilterMixin, CabledObjectModelFilterMixin): +class PowerFeedFilter(CabledObjectModelFilterMixin, TenancyFilterMixin, PrimaryModelFilterMixin): power_panel: Annotated['PowerPanelFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( strawberry_django.filter_field() ) @@ -596,10 +592,6 @@ class PowerFeedFilter(PrimaryModelFilterMixin, CabledObjectModelFilterMixin): available_power: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) - tenant: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( - strawberry_django.filter_field() - ) - tenant_id: ID | None = strawberry_django.filter_field() @strawberry_django.filter(models.PowerOutlet, lookups=True) @@ -684,7 +676,7 @@ class RackTypeFilter(RackBaseFilterMixin): @strawberry_django.filter(models.Rack, lookups=True) -class RackFilter(ContactFilterMixin, ImageAttachmentFilterMixin, RackBaseFilterMixin): +class RackFilter(ContactFilterMixin, ImageAttachmentFilterMixin, TenancyFilterMixin, RackBaseFilterMixin): form_factor: Annotated['RackFormFactorEnum', strawberry.lazy('dcim.graphql.enums')] | None = ( strawberry_django.filter_field() ) @@ -702,10 +694,6 @@ class RackFilter(ContactFilterMixin, ImageAttachmentFilterMixin, RackBaseFilterM location_id: Annotated['TreeNodeFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) - tenant: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( - strawberry_django.filter_field() - ) - tenant_id: ID | None = strawberry_django.filter_field() status: Annotated['RackStatusEnum', strawberry.lazy('dcim.graphql.enums')] | None = strawberry_django.filter_field() role: Annotated['RackRoleFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() role_id: ID | None = strawberry_django.filter_field() @@ -720,16 +708,12 @@ class RackFilter(ContactFilterMixin, ImageAttachmentFilterMixin, RackBaseFilterM @strawberry_django.filter(models.RackReservation, lookups=True) -class RackReservationFilter(PrimaryModelFilterMixin): +class RackReservationFilter(TenancyFilterMixin, PrimaryModelFilterMixin): rack: Annotated['RackFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() rack_id: ID | None = strawberry_django.filter_field() units: Annotated['IntegerArrayLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) - tenant: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( - strawberry_django.filter_field() - ) - tenant_id: ID | None = strawberry_django.filter_field() user: Annotated['UserFilter', strawberry.lazy('users.graphql.filters')] | None = strawberry_django.filter_field() user_id: ID | None = strawberry_django.filter_field() description: FilterLookup[str] | None = strawberry_django.filter_field() @@ -769,7 +753,7 @@ class RegionFilter(ContactFilterMixin, NestedGroupModelFilterMixin): @strawberry_django.filter(models.Site, lookups=True) -class SiteFilter(ContactFilterMixin, ImageAttachmentFilterMixin, PrimaryModelFilterMixin): +class SiteFilter(ContactFilterMixin, ImageAttachmentFilterMixin, TenancyFilterMixin, PrimaryModelFilterMixin): name: FilterLookup[str] | None = strawberry_django.filter_field() slug: FilterLookup[str] | None = strawberry_django.filter_field() status: Annotated['SiteStatusEnum', strawberry.lazy('dcim.graphql.enums')] | None = strawberry_django.filter_field() @@ -783,10 +767,6 @@ class SiteFilter(ContactFilterMixin, ImageAttachmentFilterMixin, PrimaryModelFil group_id: Annotated['TreeNodeFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) - tenant: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( - strawberry_django.filter_field() - ) - tenant_id: ID | None = strawberry_django.filter_field() facility: FilterLookup[str] | None = strawberry_django.filter_field() asns: Annotated['ASNFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field() time_zone: FilterLookup[str] | None = strawberry_django.filter_field() @@ -825,7 +805,7 @@ class VirtualChassisFilter(PrimaryModelFilterMixin): @strawberry_django.filter(models.VirtualDeviceContext, lookups=True) -class VirtualDeviceContextFilter(PrimaryModelFilterMixin): +class VirtualDeviceContextFilter(TenancyFilterMixin, PrimaryModelFilterMixin): device: Annotated['DeviceFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() device_id: ID | None = strawberry_django.filter_field() name: FilterLookup[str] | None = strawberry_django.filter_field() @@ -843,8 +823,4 @@ class VirtualDeviceContextFilter(PrimaryModelFilterMixin): strawberry_django.filter_field() ) primary_ip6_id: ID | None = strawberry_django.filter_field() - tenant: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( - strawberry_django.filter_field() - ) - tenant_id: ID | None = strawberry_django.filter_field() comments: FilterLookup[str] | None = strawberry_django.filter_field() diff --git a/netbox/ipam/graphql/filters.py b/netbox/ipam/graphql/filters.py index a66d40e31eb..a257e37d83c 100644 --- a/netbox/ipam/graphql/filters.py +++ b/netbox/ipam/graphql/filters.py @@ -56,20 +56,16 @@ @strawberry_django.filter(models.ASN, lookups=True) -class ASNFilter(PrimaryModelFilterMixin): +class ASNFilter(TenancyFilterMixin, PrimaryModelFilterMixin): rir: Annotated['RIRFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field() rir_id: ID | None = strawberry_django.filter_field() asn: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) - tenant: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( - strawberry_django.filter_field() - ) - tenant_id: ID | None = strawberry_django.filter_field() @strawberry_django.filter(models.ASNRange, lookups=True) -class ASNRangeFilter(OrganizationalModelFilterMixin): +class ASNRangeFilter(TenancyFilterMixin, OrganizationalModelFilterMixin): name: FilterLookup[str] | None = strawberry_django.filter_field() slug: FilterLookup[str] | None = strawberry_django.filter_field() rir: Annotated['RIRFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field() @@ -80,22 +76,14 @@ class ASNRangeFilter(OrganizationalModelFilterMixin): end: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) - tenant: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( - strawberry_django.filter_field() - ) - tenant_id: ID | None = strawberry_django.filter_field() @strawberry_django.filter(models.Aggregate, lookups=True) -class AggregateFilter(ContactFilterMixin, PrimaryModelFilterMixin): +class AggregateFilter(ContactFilterMixin, TenancyFilterMixin, PrimaryModelFilterMixin): prefix: Annotated['PrefixFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field() prefix_id: ID | None = strawberry_django.filter_field() rir: Annotated['RIRFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field() rir_id: ID | None = strawberry_django.filter_field() - tenant: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( - strawberry_django.filter_field() - ) - tenant_id: ID | None = strawberry_django.filter_field() date_added: DateFilterLookup[date] | None = strawberry_django.filter_field() @@ -133,14 +121,10 @@ class FHRPGroupAssignmentFilter(BaseObjectTypeFilterMixin, ChangeLogFilterMixin) @strawberry_django.filter(models.IPAddress, lookups=True) -class IPAddressFilter(ContactFilterMixin, PrimaryModelFilterMixin): +class IPAddressFilter(ContactFilterMixin, TenancyFilterMixin, PrimaryModelFilterMixin): 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() - tenant: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( - strawberry_django.filter_field() - ) - tenant_id: ID | None = strawberry_django.filter_field() status: Annotated['IPAddressStatusEnum', strawberry.lazy('ipam.graphql.enums')] | None = ( strawberry_django.filter_field() ) @@ -176,7 +160,7 @@ def parent(self, value: list[str], prefix) -> Q: @strawberry_django.filter(models.IPRange, lookups=True) -class IPRangeFilter(ContactFilterMixin, PrimaryModelFilterMixin): +class IPRangeFilter(ContactFilterMixin, TenancyFilterMixin, PrimaryModelFilterMixin): start_address: FilterLookup[str] | None = strawberry_django.filter_field() end_address: FilterLookup[str] | None = strawberry_django.filter_field() size: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( @@ -184,10 +168,6 @@ class IPRangeFilter(ContactFilterMixin, PrimaryModelFilterMixin): ) vrf: Annotated['VRFFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field() vrf_id: ID | None = strawberry_django.filter_field() - tenant: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( - strawberry_django.filter_field() - ) - tenant_id: ID | None = strawberry_django.filter_field() status: Annotated['IPRangeStatusEnum', strawberry.lazy('ipam.graphql.enums')] | None = ( strawberry_django.filter_field() ) @@ -211,14 +191,10 @@ def parent(self, value: list[str], prefix) -> Q: @strawberry_django.filter(models.Prefix, lookups=True) -class PrefixFilter(ContactFilterMixin, PrimaryModelFilterMixin): +class PrefixFilter(ContactFilterMixin, TenancyFilterMixin, PrimaryModelFilterMixin): prefix: 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() - tenant: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( - strawberry_django.filter_field() - ) - tenant_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 = ( @@ -247,12 +223,8 @@ class RoleFilter(OrganizationalModelFilterMixin): @strawberry_django.filter(models.RouteTarget, lookups=True) -class RouteTargetFilter(PrimaryModelFilterMixin): +class RouteTargetFilter(TenancyFilterMixin, PrimaryModelFilterMixin): name: FilterLookup[str] | None = strawberry_django.filter_field() - tenant: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( - strawberry_django.filter_field() - ) - tenant_id: ID | None = strawberry_django.filter_field() @strawberry_django.filter(models.Service, lookups=True) @@ -275,7 +247,7 @@ class ServiceTemplateFilter(ServiceBaseFilterMixin, PrimaryModelFilterMixin): @strawberry_django.filter(models.VLAN, lookups=True) -class VLANFilter(PrimaryModelFilterMixin): +class VLANFilter(TenancyFilterMixin, PrimaryModelFilterMixin): site: Annotated['SiteFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() site_id: ID | None = strawberry_django.filter_field() group: Annotated['VLANGroupFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( @@ -286,10 +258,6 @@ class VLANFilter(PrimaryModelFilterMixin): strawberry_django.filter_field() ) name: FilterLookup[str] | None = strawberry_django.filter_field() - tenant: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( - strawberry_django.filter_field() - ) - tenant_id: ID | None = strawberry_django.filter_field() status: 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() @@ -342,13 +310,9 @@ class VLANTranslationRuleFilter(NetBoxModelFilterMixin): @strawberry_django.filter(models.VRF, lookups=True) -class VRFFilter(PrimaryModelFilterMixin): +class VRFFilter(TenancyFilterMixin, PrimaryModelFilterMixin): name: FilterLookup[str] | None = strawberry_django.filter_field() rd: FilterLookup[str] | None = strawberry_django.filter_field() - tenant: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( - strawberry_django.filter_field() - ) - tenant_id: ID | None = strawberry_django.filter_field() enforce_unique: FilterLookup[bool] | None = strawberry_django.filter_field() import_targets: Annotated['RouteTargetFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( strawberry_django.filter_field() diff --git a/netbox/tenancy/graphql/filter_mixins.py b/netbox/tenancy/graphql/filter_mixins.py index e1604fa873f..1bf690013cd 100644 --- a/netbox/tenancy/graphql/filter_mixins.py +++ b/netbox/tenancy/graphql/filter_mixins.py @@ -25,9 +25,9 @@ class TenancyFilterMixin(BaseFilterMixin): strawberry_django.filter_field() ) tenant_id: ID | None = strawberry_django.filter_field() - group: Annotated['TenantGroupFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( + tenant_group: Annotated['TenantGroupFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( strawberry_django.filter_field() ) - group_id: Annotated['TreeNodeFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( + tenant_group_id: Annotated['TreeNodeFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) diff --git a/netbox/virtualization/graphql/filters.py b/netbox/virtualization/graphql/filters.py index d2d34d54e3e..282cbb19617 100644 --- a/netbox/virtualization/graphql/filters.py +++ b/netbox/virtualization/graphql/filters.py @@ -40,7 +40,7 @@ @strawberry_django.filter(models.Cluster, lookups=True) -class ClusterFilter(ContactFilterMixin, PrimaryModelFilterMixin): +class ClusterFilter(ContactFilterMixin, TenancyFilterMixin, PrimaryModelFilterMixin): name: FilterLookup[str] | None = strawberry_django.filter_field() type: Annotated['ClusterTypeFilter', strawberry.lazy('virtualization.graphql.filters')] | None = ( strawberry_django.filter_field() @@ -53,10 +53,6 @@ class ClusterFilter(ContactFilterMixin, PrimaryModelFilterMixin): status: Annotated['ClusterStatusEnum', strawberry.lazy('virtualization.graphql.enums')] | None = ( strawberry_django.filter_field() ) - tenant: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( - strawberry_django.filter_field() - ) - tenant_id: ID | None = strawberry_django.filter_field() vlan_groups: Annotated['VLANGroupFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( strawberry_django.filter_field() ) @@ -80,6 +76,7 @@ class VirtualMachineFilter( ImageAttachmentFilterMixin, RenderConfigFilterMixin, ConfigContextFilterMixin, + TenancyFilterMixin, PrimaryModelFilterMixin, ): site: Annotated['SiteFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() @@ -90,10 +87,6 @@ class VirtualMachineFilter( cluster_id: ID | None = strawberry_django.filter_field() device: Annotated['DeviceFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() device_id: ID | None = strawberry_django.filter_field() - tenant: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( - strawberry_django.filter_field() - ) - tenant_id: ID | None = strawberry_django.filter_field() platform: Annotated['PlatformFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( strawberry_django.filter_field() ) diff --git a/netbox/vpn/graphql/filters.py b/netbox/vpn/graphql/filters.py index e69455ae59f..ca038ce3d69 100644 --- a/netbox/vpn/graphql/filters.py +++ b/netbox/vpn/graphql/filters.py @@ -70,7 +70,7 @@ class TunnelTerminationFilter( @strawberry_django.filter(models.Tunnel, lookups=True) -class TunnelFilter(PrimaryModelFilterMixin): +class TunnelFilter(TenancyFilterMixin, PrimaryModelFilterMixin): name: FilterLookup[str] | None = strawberry_django.filter_field() status: Annotated['TunnelStatusEnum', strawberry.lazy('vpn.graphql.enums')] | None = ( strawberry_django.filter_field() @@ -85,10 +85,6 @@ class TunnelFilter(PrimaryModelFilterMixin): ipsec_profile: Annotated['IPSecProfileFilter', strawberry.lazy('vpn.graphql.filters')] | None = ( strawberry_django.filter_field() ) - tenant: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( - strawberry_django.filter_field() - ) - tenant_id: ID | None = strawberry_django.filter_field() tunnel_id: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) @@ -164,7 +160,7 @@ class IPSecProfileFilter(PrimaryModelFilterMixin): @strawberry_django.filter(models.L2VPN, lookups=True) -class L2VPNFilter(ContactFilterMixin, PrimaryModelFilterMixin): +class L2VPNFilter(ContactFilterMixin, TenancyFilterMixin, PrimaryModelFilterMixin): name: FilterLookup[str] | None = strawberry_django.filter_field() slug: FilterLookup[str] | None = strawberry_django.filter_field() type: Annotated['L2VPNTypeEnum', strawberry.lazy('vpn.graphql.enums')] | None = strawberry_django.filter_field() @@ -177,10 +173,6 @@ class L2VPNFilter(ContactFilterMixin, PrimaryModelFilterMixin): export_targets: Annotated['RouteTargetFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( strawberry_django.filter_field() ) - tenant: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( - strawberry_django.filter_field() - ) - tenant_id: ID | None = strawberry_django.filter_field() @strawberry_django.filter(models.L2VPNTermination, lookups=True) diff --git a/netbox/wireless/graphql/filters.py b/netbox/wireless/graphql/filters.py index 5fe233a6a1c..73e1f729aeb 100644 --- a/netbox/wireless/graphql/filters.py +++ b/netbox/wireless/graphql/filters.py @@ -41,7 +41,7 @@ class WirelessLANGroupFilter(NestedGroupModelFilterMixin): @strawberry_django.filter(models.WirelessLAN, lookups=True) -class WirelessLANFilter(WirelessAuthenticationBaseFilterMixin, PrimaryModelFilterMixin): +class WirelessLANFilter(WirelessAuthenticationBaseFilterMixin, TenancyFilterMixin, PrimaryModelFilterMixin): ssid: FilterLookup[str] | None = strawberry_django.filter_field() group: Annotated['WirelessLANGroupFilter', strawberry.lazy('wireless.graphql.filters')] | None = ( strawberry_django.filter_field() @@ -49,14 +49,15 @@ class WirelessLANFilter(WirelessAuthenticationBaseFilterMixin, PrimaryModelFilte group_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() - tenant: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( - strawberry_django.filter_field() - ) - tenant_id: ID | None = strawberry_django.filter_field() @strawberry_django.filter(models.WirelessLink, lookups=True) -class WirelessLinkFilter(WirelessAuthenticationBaseFilterMixin, DistanceFilterMixin, PrimaryModelFilterMixin): +class WirelessLinkFilter( + WirelessAuthenticationBaseFilterMixin, + DistanceFilterMixin, + TenancyFilterMixin, + PrimaryModelFilterMixin +): interface_a: Annotated['InterfaceFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( strawberry_django.filter_field() ) @@ -69,7 +70,3 @@ class WirelessLinkFilter(WirelessAuthenticationBaseFilterMixin, DistanceFilterMi status: Annotated['WirelessLANStatusEnum', strawberry.lazy('wireless.graphql.enums')] | None = ( strawberry_django.filter_field() ) - tenant: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( - strawberry_django.filter_field() - ) - tenant_id: ID | None = strawberry_django.filter_field() From 380c9c10417821e0b92abf5cf1845e8f7ae72c0b Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 4 Mar 2025 15:26:28 -0500 Subject: [PATCH 09/13] Introduce ScopedFilterMixin --- netbox/dcim/graphql/filter_mixins.py | 21 +++++++++++++++------ netbox/ipam/graphql/filters.py | 15 +++------------ netbox/virtualization/graphql/filters.py | 2 +- netbox/wireless/graphql/filters.py | 8 +++++++- 4 files changed, 26 insertions(+), 20 deletions(-) diff --git a/netbox/dcim/graphql/filter_mixins.py b/netbox/dcim/graphql/filter_mixins.py index dbe0fba00dd..41e3e5d44bc 100644 --- a/netbox/dcim/graphql/filter_mixins.py +++ b/netbox/dcim/graphql/filter_mixins.py @@ -14,16 +14,25 @@ from extras.graphql.filters import * from ipam.graphql.filters import * -__all__ = [ - 'ComponentModelFilterMixin', - 'ModularComponentModelFilterMixin', +__all__ = ( 'CabledObjectModelFilterMixin', + 'ComponentModelFilterMixin', 'ComponentTemplateFilterMixin', - 'ModularComponentTemplateFilterMixin', - 'RenderConfigFilterMixin', 'InterfaceBaseFilterMixin', + 'ModularComponentModelFilterMixin', + 'ModularComponentTemplateFilterMixin', 'RackBaseFilterMixin', -] + 'RenderConfigFilterMixin', + 'ScopedFilterMixin', +) + + +@dataclass +class ScopedFilterMixin(BaseFilterMixin): + scope_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + scope_id: ID | None = strawberry_django.filter_field() @dataclass diff --git a/netbox/ipam/graphql/filters.py b/netbox/ipam/graphql/filters.py index a257e37d83c..385f48cce34 100644 --- a/netbox/ipam/graphql/filters.py +++ b/netbox/ipam/graphql/filters.py @@ -11,9 +11,9 @@ DateFilterLookup, ) from core.graphql.filter_mixins import * +from dcim.graphql.filter_mixins import ScopedFilterMixin from netbox.graphql.filter_mixins import * from tenancy.graphql.filter_mixins import * - from ipam import models from ipam.graphql.filter_mixins import * @@ -191,7 +191,7 @@ def parent(self, value: list[str], prefix) -> Q: @strawberry_django.filter(models.Prefix, lookups=True) -class PrefixFilter(ContactFilterMixin, TenancyFilterMixin, PrimaryModelFilterMixin): +class PrefixFilter(ContactFilterMixin, ScopedFilterMixin, TenancyFilterMixin, PrimaryModelFilterMixin): prefix: 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() @@ -204,10 +204,6 @@ class PrefixFilter(ContactFilterMixin, TenancyFilterMixin, PrimaryModelFilterMix role_id: ID | None = strawberry_django.filter_field() is_pool: FilterLookup[bool] | None = strawberry_django.filter_field() mark_utilized: FilterLookup[bool] | None = strawberry_django.filter_field() - scope_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = ( - strawberry_django.filter_field() - ) - scope_id: ID | None = strawberry_django.filter_field() @strawberry_django.filter(models.RIR, lookups=True) @@ -278,12 +274,7 @@ class VLANFilter(TenancyFilterMixin, PrimaryModelFilterMixin): @strawberry_django.filter(models.VLANGroup, lookups=True) -class VLANGroupFilter(OrganizationalModelFilterMixin): - scope_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = ( - strawberry_django.filter_field() - ) - scope_type_id: ID | None = strawberry_django.filter_field() - scope_id: ID | None = strawberry_django.filter_field() +class VLANGroupFilter(ScopedFilterMixin, OrganizationalModelFilterMixin): vid_ranges: Annotated['IntegerArrayLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) diff --git a/netbox/virtualization/graphql/filters.py b/netbox/virtualization/graphql/filters.py index 282cbb19617..c9bd964180a 100644 --- a/netbox/virtualization/graphql/filters.py +++ b/netbox/virtualization/graphql/filters.py @@ -40,7 +40,7 @@ @strawberry_django.filter(models.Cluster, lookups=True) -class ClusterFilter(ContactFilterMixin, TenancyFilterMixin, PrimaryModelFilterMixin): +class ClusterFilter(ContactFilterMixin, ScopedFilterMixin, TenancyFilterMixin, PrimaryModelFilterMixin): name: FilterLookup[str] | None = strawberry_django.filter_field() type: Annotated['ClusterTypeFilter', strawberry.lazy('virtualization.graphql.filters')] | None = ( strawberry_django.filter_field() diff --git a/netbox/wireless/graphql/filters.py b/netbox/wireless/graphql/filters.py index 73e1f729aeb..198f0862ac9 100644 --- a/netbox/wireless/graphql/filters.py +++ b/netbox/wireless/graphql/filters.py @@ -5,6 +5,7 @@ from strawberry_django import ( FilterLookup, ) +from dcim.graphql.filter_mixins import ScopedFilterMixin from extras.graphql.filter_mixins import * from netbox.graphql.filter_mixins import * from core.graphql.filter_mixins import * @@ -41,7 +42,12 @@ class WirelessLANGroupFilter(NestedGroupModelFilterMixin): @strawberry_django.filter(models.WirelessLAN, lookups=True) -class WirelessLANFilter(WirelessAuthenticationBaseFilterMixin, TenancyFilterMixin, PrimaryModelFilterMixin): +class WirelessLANFilter( + WirelessAuthenticationBaseFilterMixin, + ScopedFilterMixin, + TenancyFilterMixin, + PrimaryModelFilterMixin +): ssid: FilterLookup[str] | None = strawberry_django.filter_field() group: Annotated['WirelessLANGroupFilter', strawberry.lazy('wireless.graphql.filters')] | None = ( strawberry_django.filter_field() From c369ac929d15d787790e07120335d7df9cc840e1 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 5 Mar 2025 09:23:12 -0500 Subject: [PATCH 10/13] Misc cleanup --- netbox/core/graphql/filter_mixins.py | 1 + netbox/dcim/graphql/filter_mixins.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/netbox/core/graphql/filter_mixins.py b/netbox/core/graphql/filter_mixins.py index 1748deab50a..463350ab917 100644 --- a/netbox/core/graphql/filter_mixins.py +++ b/netbox/core/graphql/filter_mixins.py @@ -24,6 +24,7 @@ class BaseObjectTypeFilterMixin(BaseFilterMixin): @dataclass class ChangeLogFilterMixin(BaseFilterMixin): + id: ID | None = strawberry.UNSET changelog: Annotated['ObjectChangeFilter', strawberry.lazy('core.graphql.filters')] | None = ( strawberry_django.filter_field() ) diff --git a/netbox/dcim/graphql/filter_mixins.py b/netbox/dcim/graphql/filter_mixins.py index 41e3e5d44bc..23ab29813b1 100644 --- a/netbox/dcim/graphql/filter_mixins.py +++ b/netbox/dcim/graphql/filter_mixins.py @@ -40,7 +40,7 @@ class ComponentModelFilterMixin(NetBoxModelFilterMixin): device: Annotated['DeviceFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() device_id: ID | None = strawberry_django.filter_field() name: FilterLookup[str] | None = strawberry_django.filter_field() - lable: FilterLookup[str] | None = strawberry_django.filter_field() + label: FilterLookup[str] | None = strawberry_django.filter_field() description: FilterLookup[str] | None = strawberry_django.filter_field() From 8894c35e8499f2e83d7266ae25cfeb9e10734da6 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Thu, 6 Mar 2025 14:52:33 -0500 Subject: [PATCH 11/13] Add missing GraphQL filters --- netbox/circuits/graphql/filters.py | 2 +- netbox/core/graphql/filters.py | 4 +++- netbox/dcim/graphql/filters.py | 23 ++++++++++++++++++++++- netbox/extras/graphql/filter_mixins.py | 5 +---- netbox/extras/graphql/filters.py | 10 +++++++++- netbox/ipam/graphql/filters.py | 2 +- netbox/tenancy/graphql/filters.py | 3 +++ netbox/users/graphql/filters.py | 2 ++ netbox/virtualization/graphql/filters.py | 3 +++ netbox/wireless/graphql/filters.py | 3 +++ 10 files changed, 48 insertions(+), 9 deletions(-) diff --git a/netbox/circuits/graphql/filters.py b/netbox/circuits/graphql/filters.py index a3f2f2e8565..d8154368b68 100644 --- a/netbox/circuits/graphql/filters.py +++ b/netbox/circuits/graphql/filters.py @@ -121,7 +121,7 @@ class CircuitGroupAssignmentFilter( member_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = ( strawberry_django.filter_field() ) - member_type_id: ID | None = strawberry_django.filter_field() + member_id: ID | None = strawberry_django.filter_field() group: Annotated['CircuitGroupFilter', strawberry.lazy('circuits.graphql.filters')] | None = ( strawberry_django.filter_field() ) diff --git a/netbox/core/graphql/filters.py b/netbox/core/graphql/filters.py index e1feac715b2..476e69533fc 100644 --- a/netbox/core/graphql/filters.py +++ b/netbox/core/graphql/filters.py @@ -75,7 +75,9 @@ class ObjectChangeFilter(BaseFilterMixin): ) changed_object_type_id: ID | None = strawberry_django.filter_field() changed_object_id: ID | None = strawberry_django.filter_field() - related_object_type_id: ID | None = strawberry_django.filter_field() + related_object_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) related_object_id: ID | None = strawberry_django.filter_field() object_repr: FilterLookup[str] | None = strawberry_django.filter_field() prechange_data: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( diff --git a/netbox/dcim/graphql/filters.py b/netbox/dcim/graphql/filters.py index 1a146f82de1..5d9e84aff7b 100644 --- a/netbox/dcim/graphql/filters.py +++ b/netbox/dcim/graphql/filters.py @@ -47,7 +47,6 @@ 'DeviceFilter', 'DeviceBayFilter', 'DeviceBayTemplateFilter', - 'InventoryItemTemplateFilter', 'DeviceRoleFilter', 'DeviceTypeFilter', 'FrontPortFilter', @@ -56,6 +55,7 @@ 'InterfaceTemplateFilter', 'InventoryItemFilter', 'InventoryItemRoleFilter', + 'InventoryItemTemplateFilter', 'LocationFilter', 'MACAddressFilter', 'ManufacturerFilter', @@ -253,6 +253,16 @@ class DeviceFilter( modules: Annotated['ModuleFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( strawberry_django.filter_field() ) + console_port_count: FilterLookup[int] | None = strawberry_django.filter_field() + console_server_port_count: FilterLookup[int] | None = strawberry_django.filter_field() + power_port_count: FilterLookup[int] | None = strawberry_django.filter_field() + power_outlet_count: FilterLookup[int] | None = strawberry_django.filter_field() + interface_count: FilterLookup[int] | None = strawberry_django.filter_field() + front_port_count: FilterLookup[int] | None = strawberry_django.filter_field() + rear_port_count: FilterLookup[int] | None = strawberry_django.filter_field() + device_bay_count: FilterLookup[int] | None = strawberry_django.filter_field() + module_bay_count: FilterLookup[int] | None = strawberry_django.filter_field() + inventory_item_count: FilterLookup[int] | None = strawberry_django.filter_field() @strawberry_django.filter(models.DeviceBay, lookups=True) @@ -324,6 +334,16 @@ class DeviceTypeFilter(ImageAttachmentFilterMixin, PrimaryModelFilterMixin, Weig rear_image: Annotated['ImageAttachmentFilter', strawberry.lazy('extras.graphql.filters')] | None = ( strawberry_django.filter_field() ) + console_port_template_count: FilterLookup[int] | None = strawberry_django.filter_field() + console_server_port_template_count: FilterLookup[int] | None = strawberry_django.filter_field() + power_port_template_count: FilterLookup[int] | None = strawberry_django.filter_field() + power_outlet_template_count: FilterLookup[int] | None = strawberry_django.filter_field() + interface_template_count: FilterLookup[int] | None = strawberry_django.filter_field() + front_port_template_count: FilterLookup[int] | None = strawberry_django.filter_field() + rear_port_template_count: FilterLookup[int] | None = strawberry_django.filter_field() + device_bay_template_count: FilterLookup[int] | None = strawberry_django.filter_field() + module_bay_template_count: FilterLookup[int] | None = strawberry_django.filter_field() + inventory_item_template_count: FilterLookup[int] | None = strawberry_django.filter_field() @strawberry_django.filter(models.FrontPort, lookups=True) @@ -802,6 +822,7 @@ class VirtualChassisFilter(PrimaryModelFilterMixin): master_id: ID | None = strawberry_django.filter_field() name: FilterLookup[str] | None = strawberry_django.filter_field() domain: FilterLookup[str] | None = strawberry_django.filter_field() + member_count: FilterLookup[int] | None = strawberry_django.filter_field() @strawberry_django.filter(models.VirtualDeviceContext, lookups=True) diff --git a/netbox/extras/graphql/filter_mixins.py b/netbox/extras/graphql/filter_mixins.py index b0e8d3c5393..228f78b3c2f 100644 --- a/netbox/extras/graphql/filter_mixins.py +++ b/netbox/extras/graphql/filter_mixins.py @@ -39,10 +39,7 @@ class TagsFilterMixin(BaseFilterMixin): @dataclass class ConfigContextFilterMixin(BaseFilterMixin): - local_config_context: Annotated['ConfigContextFilter', strawberry.lazy('extras.graphql.filters')] | None = ( - strawberry_django.filter_field() - ) - config_context: Annotated['ConfigContextFilter', strawberry.lazy('extras.graphql.filters')] | None = ( + local_context_data: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() ) diff --git a/netbox/extras/graphql/filters.py b/netbox/extras/graphql/filters.py index 733fb6423af..f7fb8ec9613 100644 --- a/netbox/extras/graphql/filters.py +++ b/netbox/extras/graphql/filters.py @@ -118,7 +118,12 @@ class CustomFieldFilter(BaseObjectTypeFilterMixin, ChangeLogFilterMixin): type: Annotated['CustomFieldTypeEnum', strawberry.lazy('extras.graphql.enums')] | None = ( strawberry_django.filter_field() ) - related_object_type_id: ID | None = strawberry_django.filter_field() + object_types: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) + related_object_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) name: FilterLookup[str] | None = strawberry_django.filter_field() label: FilterLookup[str] | None = strawberry_django.filter_field() group_name: FilterLookup[str] | None = strawberry_django.filter_field() @@ -202,6 +207,9 @@ class ExportTemplateFilter(BaseObjectTypeFilterMixin, SyncedDataFilterMixin, Cha @strawberry_django.filter(models.ImageAttachment, lookups=True) class ImageAttachmentFilter(BaseObjectTypeFilterMixin, ChangeLogFilterMixin): + object_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) object_id: ID | None = strawberry_django.filter_field() image_height: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = ( strawberry_django.filter_field() diff --git a/netbox/ipam/graphql/filters.py b/netbox/ipam/graphql/filters.py index 385f48cce34..e45e406eedd 100644 --- a/netbox/ipam/graphql/filters.py +++ b/netbox/ipam/graphql/filters.py @@ -107,7 +107,7 @@ class FHRPGroupFilter(PrimaryModelFilterMixin): @strawberry_django.filter(models.FHRPGroupAssignment, lookups=True) class FHRPGroupAssignmentFilter(BaseObjectTypeFilterMixin, ChangeLogFilterMixin): - inteface_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = ( + interface_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = ( strawberry_django.filter_field() ) interface_id: FilterLookup[str] | None = strawberry_django.filter_field() diff --git a/netbox/tenancy/graphql/filters.py b/netbox/tenancy/graphql/filters.py index a63f8da6059..59778445756 100644 --- a/netbox/tenancy/graphql/filters.py +++ b/netbox/tenancy/graphql/filters.py @@ -150,6 +150,9 @@ class ContactGroupFilter(NestedGroupModelFilterMixin): @strawberry_django.filter(models.ContactAssignment, lookups=True) class ContactAssignmentFilter(CustomFieldsFilterMixin, TagsFilterMixin, ChangeLogFilterMixin): + object_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = ( + strawberry_django.filter_field() + ) object_id: ID | None = strawberry_django.filter_field() contact: Annotated['ContactFilter', strawberry.lazy('tenancy.graphql.filters')] | None = ( strawberry_django.filter_field() diff --git a/netbox/users/graphql/filters.py b/netbox/users/graphql/filters.py index 1027c7c3948..8ead122130d 100644 --- a/netbox/users/graphql/filters.py +++ b/netbox/users/graphql/filters.py @@ -45,7 +45,9 @@ class UserFilter(BaseObjectTypeFilterMixin): first_name: FilterLookup[str] | None = strawberry_django.filter_field() last_name: FilterLookup[str] | None = strawberry_django.filter_field() email: FilterLookup[str] | None = strawberry_django.filter_field() + is_superuser: FilterLookup[bool] | None = strawberry_django.filter_field() is_staff: FilterLookup[bool] | None = strawberry_django.filter_field() is_active: FilterLookup[bool] | None = strawberry_django.filter_field() date_joined: DatetimeFilterLookup[datetime] | None = strawberry_django.filter_field() + last_login: DatetimeFilterLookup[datetime] | None = strawberry_django.filter_field() groups: Annotated['GroupFilter', strawberry.lazy('users.graphql.filters')] | None = strawberry_django.filter_field() diff --git a/netbox/virtualization/graphql/filters.py b/netbox/virtualization/graphql/filters.py index c9bd964180a..66c0e71392e 100644 --- a/netbox/virtualization/graphql/filters.py +++ b/netbox/virtualization/graphql/filters.py @@ -79,6 +79,7 @@ class VirtualMachineFilter( TenancyFilterMixin, PrimaryModelFilterMixin, ): + name: FilterLookup[str] | None = strawberry_django.filter_field() site: Annotated['SiteFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field() site_id: ID | None = strawberry_django.filter_field() cluster: Annotated['ClusterFilter', strawberry.lazy('virtualization.graphql.filters')] | None = ( @@ -116,6 +117,8 @@ class VirtualMachineFilter( strawberry_django.filter_field() ) serial: FilterLookup[str] | None = strawberry_django.filter_field() + interface_count: FilterLookup[int] | None = strawberry_django.filter_field() + virtual_disk_count: FilterLookup[int] | None = strawberry_django.filter_field() interfaces: Annotated['VMInterfaceFilter', strawberry.lazy('virtualization.graphql.filters')] | None = ( strawberry_django.filter_field() ) diff --git a/netbox/wireless/graphql/filters.py b/netbox/wireless/graphql/filters.py index 198f0862ac9..cdbf715a296 100644 --- a/netbox/wireless/graphql/filters.py +++ b/netbox/wireless/graphql/filters.py @@ -49,6 +49,9 @@ class WirelessLANFilter( PrimaryModelFilterMixin ): ssid: FilterLookup[str] | None = strawberry_django.filter_field() + status: Annotated['WirelessLANStatusEnum', strawberry.lazy('wireless.graphql.enums')] | None = ( + strawberry_django.filter_field() + ) group: Annotated['WirelessLANGroupFilter', strawberry.lazy('wireless.graphql.filters')] | None = ( strawberry_django.filter_field() ) From 3a7edf3c0a11e1c8a8df38cc22e295d34e5ef14f Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 7 Mar 2025 10:21:47 -0500 Subject: [PATCH 12/13] Clean up imports --- netbox/circuits/graphql/filter_mixins.py | 10 ++-- netbox/circuits/graphql/filters.py | 37 ++++++------- netbox/core/graphql/filter_mixins.py | 10 ++-- netbox/core/graphql/filters.py | 20 +++---- netbox/dcim/graphql/filter_mixins.py | 20 +++---- netbox/dcim/graphql/filters.py | 53 ++++++++++--------- netbox/extras/graphql/filter_mixins.py | 8 +-- netbox/extras/graphql/filters.py | 37 ++++++------- netbox/ipam/graphql/filter_mixins.py | 9 ++-- netbox/ipam/graphql/filters.py | 39 ++++++-------- netbox/netbox/graphql/filter_lookups.py | 27 +++++----- netbox/netbox/graphql/filter_mixins.py | 27 ++++------ netbox/tenancy/graphql/filter_mixins.py | 13 +++-- netbox/tenancy/graphql/filters.py | 50 +++++++++++------ netbox/tenancy/graphql/mixins.py | 1 - netbox/users/graphql/filters.py | 27 ++-------- .../virtualization/graphql/filter_mixins.py | 9 ++-- netbox/virtualization/graphql/filters.py | 44 +++++++-------- netbox/vpn/graphql/filters.py | 33 ++++-------- netbox/wireless/graphql/filter_mixins.py | 6 ++- netbox/wireless/graphql/filters.py | 31 ++++------- 21 files changed, 242 insertions(+), 269 deletions(-) diff --git a/netbox/circuits/graphql/filter_mixins.py b/netbox/circuits/graphql/filter_mixins.py index f3a0d1f4a3b..3ae6fa82ea7 100644 --- a/netbox/circuits/graphql/filter_mixins.py +++ b/netbox/circuits/graphql/filter_mixins.py @@ -1,15 +1,17 @@ from dataclasses import dataclass from typing import Annotated, TYPE_CHECKING + import strawberry import strawberry_django + from netbox.graphql.filter_mixins import OrganizationalModelFilterMixin if TYPE_CHECKING: - from .filters import * - from netbox.graphql.filter_lookups import * - from netbox.graphql.enums import * + from netbox.graphql.enums import ColorEnum -__all__ = ['BaseCircuitTypeFilterMixin'] +__all__ = ( + 'BaseCircuitTypeFilterMixin', +) @dataclass diff --git a/netbox/circuits/graphql/filters.py b/netbox/circuits/graphql/filters.py index d8154368b68..48b4252ac42 100644 --- a/netbox/circuits/graphql/filters.py +++ b/netbox/circuits/graphql/filters.py @@ -1,33 +1,30 @@ from datetime import date from typing import Annotated, TYPE_CHECKING + import strawberry -from strawberry.scalars import ID import strawberry_django +from strawberry.scalars import ID from strawberry_django import FilterLookup, DateFilterLookup -from extras.graphql.filter_mixins import * -from netbox.graphql.filter_mixins import * -from core.graphql.filter_mixins import * -from tenancy.graphql.filter_mixins import * -from dcim.graphql.filter_mixins import * -from .filter_mixins import * from circuits import models +from core.graphql.filter_mixins import BaseObjectTypeFilterMixin, ChangeLogFilterMixin +from dcim.graphql.filter_mixins import CabledObjectModelFilterMixin +from extras.graphql.filter_mixins import CustomFieldsFilterMixin, TagsFilterMixin +from netbox.graphql.filter_mixins import ( + DistanceFilterMixin, + ImageAttachmentFilterMixin, + OrganizationalModelFilterMixin, + PrimaryModelFilterMixin, +) +from tenancy.graphql.filter_mixins import ContactFilterMixin, TenancyFilterMixin +from .filter_mixins import BaseCircuitTypeFilterMixin if TYPE_CHECKING: + from core.graphql.filters import ContentTypeFilter + from dcim.graphql.filters import InterfaceFilter + from ipam.graphql.filters import ASNFilter + from netbox.graphql.filter_lookups import IntegerLookup from .enums import * - from netbox.graphql.enums import * - from wireless.graphql.enums import * - from netbox.graphql.filter_lookups import * - from core.graphql.filters import * - from extras.graphql.filters import * - from circuits.graphql.filters import * - from dcim.graphql.filters import * - from ipam.graphql.filters import * - from tenancy.graphql.filters import * - from wireless.graphql.filters import * - from users.graphql.filters import * - from virtualization.graphql.filters import * - from vpn.graphql.filters import * __all__ = ( 'CircuitFilter', diff --git a/netbox/core/graphql/filter_mixins.py b/netbox/core/graphql/filter_mixins.py index 463350ab917..670ec2ebb3c 100644 --- a/netbox/core/graphql/filter_mixins.py +++ b/netbox/core/graphql/filter_mixins.py @@ -1,16 +1,20 @@ from dataclasses import dataclass from datetime import datetime from typing import Annotated, TYPE_CHECKING + import strawberry -from strawberry import ID import strawberry_django +from strawberry import ID from strawberry_django import DatetimeFilterLookup - if TYPE_CHECKING: from .filters import * -__all__ = ['BaseFilterMixin', 'BaseObjectTypeFilterMixin', 'ChangeLogFilterMixin'] +__all__ = ( + 'BaseFilterMixin', + 'BaseObjectTypeFilterMixin', + 'ChangeLogFilterMixin', +) # @strawberry.input diff --git a/netbox/core/graphql/filters.py b/netbox/core/graphql/filters.py index 476e69533fc..e5d44674aad 100644 --- a/netbox/core/graphql/filters.py +++ b/netbox/core/graphql/filters.py @@ -1,25 +1,19 @@ from datetime import datetime from typing import Annotated, TYPE_CHECKING + import strawberry -from strawberry.scalars import ID import strawberry_django -from strawberry_django import ( - DatetimeFilterLookup, - FilterLookup, -) from django.contrib.contenttypes.models import ContentType as DjangoContentType -from core.graphql.filter_mixins import BaseFilterMixin -from netbox.graphql.filter_lookups import JSONFilter -from netbox.graphql.filter_mixins import ( - PrimaryModelFilterMixin, -) +from strawberry.scalars import ID +from strawberry_django import DatetimeFilterLookup, FilterLookup from core import models +from core.graphql.filter_mixins import BaseFilterMixin +from netbox.graphql.filter_mixins import PrimaryModelFilterMixin if TYPE_CHECKING: - from netbox.graphql.filter_lookups import * - from users.graphql.filters import * - + from netbox.graphql.filter_lookups import IntegerLookup, JSONFilter + from users.graphql.filters import UserFilter __all__ = ( 'DataFileFilter', diff --git a/netbox/dcim/graphql/filter_mixins.py b/netbox/dcim/graphql/filter_mixins.py index 23ab29813b1..47a75d08e11 100644 --- a/netbox/dcim/graphql/filter_mixins.py +++ b/netbox/dcim/graphql/filter_mixins.py @@ -1,18 +1,21 @@ from dataclasses import dataclass from typing import Annotated, TYPE_CHECKING + import strawberry -from strawberry import ID import strawberry_django +from strawberry import ID from strawberry_django import FilterLookup -from core.graphql.filter_mixins import * -from netbox.graphql.filter_mixins import * + +from core.graphql.filter_mixins import BaseFilterMixin, ChangeLogFilterMixin +from core.graphql.filters import ContentTypeFilter +from netbox.graphql.filter_mixins import NetBoxModelFilterMixin, PrimaryModelFilterMixin, WeightFilterMixin from .enums import * if TYPE_CHECKING: + from netbox.graphql.filter_lookups import IntegerLookup + from extras.graphql.filters import ConfigTemplateFilter + from ipam.graphql.filters import VLANFilter, VLANTranslationPolicyFilter from .filters import * - from netbox.graphql.filter_lookups import * - from extras.graphql.filters import * - from ipam.graphql.filters import * __all__ = ( 'CabledObjectModelFilterMixin', @@ -111,9 +114,8 @@ class InterfaceBaseFilterMixin(BaseFilterMixin): qinq_svlan: Annotated['VLANFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( strawberry_django.filter_field() ) - vlan_tranlation_policy: Annotated['VLANTranslationPolicyFilter', strawberry.lazy('ipam.graphql.filters')] | None = ( - strawberry_django.filter_field() - ) + vlan_translation_policy: Annotated['VLANTranslationPolicyFilter', strawberry.lazy('ipam.graphql.filters')] | None \ + = strawberry_django.filter_field() primary_mac_address: Annotated['MACAddressFilter', strawberry.lazy('dcim.graphql.filters')] | None = ( strawberry_django.filter_field() ) diff --git a/netbox/dcim/graphql/filters.py b/netbox/dcim/graphql/filters.py index 5d9e84aff7b..4203517cc7a 100644 --- a/netbox/dcim/graphql/filters.py +++ b/netbox/dcim/graphql/filters.py @@ -1,13 +1,13 @@ from typing import Annotated, TYPE_CHECKING + import strawberry -from strawberry.scalars import ID import strawberry_django -from strawberry_django import ( - FilterLookup, -) -from extras.graphql.filter_mixins import ( - ConfigContextFilterMixin, -) +from strawberry.scalars import ID +from strawberry_django import FilterLookup + +from core.graphql.filter_mixins import ChangeLogFilterMixin +from dcim import models +from extras.graphql.filter_mixins import ConfigContextFilterMixin from netbox.graphql.filter_mixins import ( PrimaryModelFilterMixin, OrganizationalModelFilterMixin, @@ -15,27 +15,32 @@ ImageAttachmentFilterMixin, WeightFilterMixin, ) -from .filter_mixins import * -from dcim import models -from core.graphql.filter_mixins import * from tenancy.graphql.filter_mixins import TenancyFilterMixin, ContactFilterMixin +from .filter_mixins import ( + CabledObjectModelFilterMixin, + ComponentModelFilterMixin, + ComponentTemplateFilterMixin, + InterfaceBaseFilterMixin, + ModularComponentModelFilterMixin, + ModularComponentTemplateFilterMixin, + RackBaseFilterMixin, + RenderConfigFilterMixin, +) if TYPE_CHECKING: + from core.graphql.filters import ContentTypeFilter + from extras.graphql.filters import ConfigTemplateFilter, ImageAttachmentFilter + from ipam.graphql.filters import ( + ASNFilter, FHRPGroupAssignmentFilter, IPAddressFilter, PrefixFilter, VLANGroupFilter, VRFFilter, + ) + from netbox.graphql.enums import ColorEnum + from netbox.graphql.filter_lookups import FloatLookup, IntegerArrayLookup, IntegerLookup, TreeNodeFilter + from users.graphql.filters import UserFilter + from virtualization.graphql.filters import ClusterFilter + from vpn.graphql.filters import L2VPNFilter, TunnelTerminationFilter + from wireless.graphql.enums import WirelessChannelEnum, WirelessRoleEnum + from wireless.graphql.filters import WirelessLANFilter, WirelessLinkFilter from .enums import * - from netbox.graphql.enums import * - from wireless.graphql.enums import * - from netbox.graphql.filter_lookups import * - from core.graphql.filters import * - from extras.graphql.filters import * - from circuits.graphql.filters import * - from dcim.graphql.filters import * - from ipam.graphql.filters import * - from tenancy.graphql.filters import * - from wireless.graphql.filters import * - from users.graphql.filters import * - from virtualization.graphql.filters import * - from vpn.graphql.filters import * - __all__ = ( 'CableFilter', diff --git a/netbox/extras/graphql/filter_mixins.py b/netbox/extras/graphql/filter_mixins.py index 228f78b3c2f..7e9a970f26e 100644 --- a/netbox/extras/graphql/filter_mixins.py +++ b/netbox/extras/graphql/filter_mixins.py @@ -1,21 +1,23 @@ from dataclasses import dataclass from typing import Annotated, TYPE_CHECKING + import strawberry import strawberry_django from strawberry_django import FilterLookup + from core.graphql.filter_mixins import BaseFilterMixin if TYPE_CHECKING: - from netbox.graphql.filter_lookups import * + from netbox.graphql.filter_lookups import JSONFilter from .filters import * -__all__ = [ +__all__ = ( 'CustomFieldsFilterMixin', 'JournalEntriesFilterMixin', 'TagsFilterMixin', 'ConfigContextFilterMixin', 'TagBaseFilterMixin', -] +) @dataclass diff --git a/netbox/extras/graphql/filters.py b/netbox/extras/graphql/filters.py index f7fb8ec9613..e22bda0ac8a 100644 --- a/netbox/extras/graphql/filters.py +++ b/netbox/extras/graphql/filters.py @@ -1,33 +1,26 @@ from typing import Annotated, TYPE_CHECKING + import strawberry -from strawberry.scalars import ID import strawberry_django -from strawberry_django import ( - FilterLookup, -) -from netbox.graphql.filter_mixins import ( - BaseObjectTypeFilterMixin, - ChangeLogFilterMixin, - SyncedDataFilterMixin, -) +from strawberry.scalars import ID +from strawberry_django import FilterLookup + +from core.graphql.filter_mixins import BaseObjectTypeFilterMixin, ChangeLogFilterMixin from extras import models from extras.graphql.filter_mixins import TagBaseFilterMixin, CustomFieldsFilterMixin, TagsFilterMixin +from netbox.graphql.filter_mixins import SyncedDataFilterMixin if TYPE_CHECKING: + from core.graphql.filters import ContentTypeFilter + from dcim.graphql.filters import ( + DeviceRoleFilter, DeviceTypeFilter, LocationFilter, PlatformFilter, RegionFilter, SiteFilter, SiteGroupFilter, + ) + from tenancy.graphql.filters import TenantFilter, TenantGroupFilter + from netbox.graphql.enums import ColorEnum + from netbox.graphql.filter_lookups import IntegerLookup, JSONFilter, StringArrayLookup, TreeNodeFilter + from users.graphql.filters import GroupFilter, UserFilter + from virtualization.graphql.filters import ClusterFilter, ClusterGroupFilter, ClusterTypeFilter from .enums import * - from netbox.graphql.enums import * - from wireless.graphql.enums import * - from netbox.graphql.filter_lookups import * - from extras.graphql.filters import * - from circuits.graphql.filters import * - from dcim.graphql.filters import * - from ipam.graphql.filters import * - from tenancy.graphql.filters import * - from wireless.graphql.filters import * - from users.graphql.filters import * - from virtualization.graphql.filters import * - from vpn.graphql.filters import * - __all__ = ( 'ConfigContextFilter', diff --git a/netbox/ipam/graphql/filter_mixins.py b/netbox/ipam/graphql/filter_mixins.py index 187a0a270a5..511850285a2 100644 --- a/netbox/ipam/graphql/filter_mixins.py +++ b/netbox/ipam/graphql/filter_mixins.py @@ -1,15 +1,18 @@ from dataclasses import dataclass from typing import Annotated, TYPE_CHECKING + import strawberry import strawberry_django -from core.graphql.filter_mixins import * +from core.graphql.filter_mixins import BaseFilterMixin if TYPE_CHECKING: + from netbox.graphql.filter_lookups import IntegerLookup from .enums import * - from netbox.graphql.filter_lookups import * -__all__ = ['ServiceBaseFilterMixin'] +__all__ = ( + 'ServiceBaseFilterMixin', +) @dataclass diff --git a/netbox/ipam/graphql/filters.py b/netbox/ipam/graphql/filters.py index e45e406eedd..2f4e185f15e 100644 --- a/netbox/ipam/graphql/filters.py +++ b/netbox/ipam/graphql/filters.py @@ -1,37 +1,28 @@ from datetime import date from typing import Annotated, TYPE_CHECKING + import netaddr -from netaddr.core import AddrFormatError -from django.db.models import Q import strawberry -from strawberry.scalars import ID import strawberry_django -from strawberry_django import ( - FilterLookup, - DateFilterLookup, -) -from core.graphql.filter_mixins import * +from django.db.models import Q +from netaddr.core import AddrFormatError +from strawberry.scalars import ID +from strawberry_django import FilterLookup, DateFilterLookup + +from core.graphql.filter_mixins import BaseObjectTypeFilterMixin, ChangeLogFilterMixin from dcim.graphql.filter_mixins import ScopedFilterMixin -from netbox.graphql.filter_mixins import * -from tenancy.graphql.filter_mixins import * from ipam import models -from ipam.graphql.filter_mixins import * +from ipam.graphql.filter_mixins import ServiceBaseFilterMixin +from netbox.graphql.filter_mixins import NetBoxModelFilterMixin, OrganizationalModelFilterMixin, PrimaryModelFilterMixin +from tenancy.graphql.filter_mixins import ContactFilterMixin, TenancyFilterMixin if TYPE_CHECKING: + from netbox.graphql.filter_lookups import IntegerArrayLookup, IntegerLookup + from core.graphql.filters import ContentTypeFilter + from dcim.graphql.filters import DeviceFilter, SiteFilter + from virtualization.graphql.filters import VirtualMachineFilter + from vpn.graphql.filters import L2VPNFilter from .enums import * - from netbox.graphql.enums import * - from wireless.graphql.enums import * - from netbox.graphql.filter_lookups import * - from core.graphql.filters import * - from extras.graphql.filters import * - from circuits.graphql.filters import * - from dcim.graphql.filters import * - from ipam.graphql.filters import * - from tenancy.graphql.filters import * - from wireless.graphql.filters import * - from users.graphql.filters import * - from virtualization.graphql.filters import * - from vpn.graphql.filters import * __all__ = ( 'ASNFilter', diff --git a/netbox/netbox/graphql/filter_lookups.py b/netbox/netbox/graphql/filter_lookups.py index 09f470c96cb..859236e4d25 100644 --- a/netbox/netbox/graphql/filter_lookups.py +++ b/netbox/netbox/graphql/filter_lookups.py @@ -1,32 +1,33 @@ from enum import Enum from typing import TypeVar, Tuple, Generic -from django.db.models import Q, QuerySet + +import strawberry +import strawberry_django from django.core.exceptions import FieldDoesNotExist +from django.db.models import Q, QuerySet from django.db.models.fields.related import ForeignKey, ManyToManyField, ManyToManyRel, ManyToOneRel -import strawberry from strawberry import ID from strawberry.types import Info -import strawberry_django from strawberry_django import ( - process_filters, - FilterLookup, - RangeLookup, ComparisonFilterLookup, DateFilterLookup, DatetimeFilterLookup, + FilterLookup, + RangeLookup, TimeFilterLookup, + process_filters, ) -__all__ = [ - 'JSONFilter', - 'TreeNodeFilter', - 'IntegerLookup', - 'FloatLookup', +__all__ = ( 'ArrayLookup', - 'IntegerArrayLookup', 'FloatArrayLookup', + 'FloatLookup', + 'IntegerArrayLookup', + 'IntegerLookup', + 'JSONFilter', 'StringArrayLookup', -] + 'TreeNodeFilter', +) T = TypeVar('T') SKIP_MSG = 'Filter will be skipped on `null` value' diff --git a/netbox/netbox/graphql/filter_mixins.py b/netbox/netbox/graphql/filter_mixins.py index 09bd71bfc3b..ce6d81a8690 100644 --- a/netbox/netbox/graphql/filter_mixins.py +++ b/netbox/netbox/graphql/filter_mixins.py @@ -1,30 +1,26 @@ from dataclasses import dataclass +from datetime import datetime from typing import TypeVar, TYPE_CHECKING, Annotated - -from datetime import datetime import strawberry -from strawberry import ID import strawberry_django +from strawberry import ID from strawberry_django import FilterLookup, DatetimeFilterLookup -from extras.models import * -from utilities.filters import * -from netbox.graphql.filter_lookups import * -from core.graphql.filter_mixins import * -from extras.graphql.filter_mixins import * +from core.graphql.filter_mixins import BaseFilterMixin, BaseObjectTypeFilterMixin, ChangeLogFilterMixin +from extras.graphql.filter_mixins import CustomFieldsFilterMixin, JournalEntriesFilterMixin, TagsFilterMixin +from netbox.graphql.filter_lookups import IntegerLookup -__all__ = [ +__all__ = ( + 'DistanceFilterMixin', + 'ImageAttachmentFilterMixin', + 'NestedGroupModelFilterMixin', 'NetBoxModelFilterMixin', 'OrganizationalModelFilterMixin', 'PrimaryModelFilterMixin', - # 'autotype_decorator', - 'NestedGroupModelFilterMixin', - 'ImageAttachmentFilterMixin', - 'WeightFilterMixin', 'SyncedDataFilterMixin', - 'DistanceFilterMixin', -] + 'WeightFilterMixin', +) T = TypeVar('T') @@ -32,7 +28,6 @@ if TYPE_CHECKING: from .enums import * from core.graphql.filters import * - from tenancy.graphql.filters import * from extras.graphql.filters import * diff --git a/netbox/tenancy/graphql/filter_mixins.py b/netbox/tenancy/graphql/filter_mixins.py index 1bf690013cd..cc4a4297cf7 100644 --- a/netbox/tenancy/graphql/filter_mixins.py +++ b/netbox/tenancy/graphql/filter_mixins.py @@ -1,15 +1,20 @@ from dataclasses import dataclass from typing import Annotated, TYPE_CHECKING + import strawberry -from strawberry import ID import strawberry_django +from strawberry import ID + from core.graphql.filter_mixins import BaseFilterMixin if TYPE_CHECKING: - from .filters import * - from netbox.graphql.filter_lookups import * + from netbox.graphql.filter_lookups import TreeNodeFilter + from .filters import ContactFilter, TenantFilter, TenantGroupFilter -__all__ = ['TenancyFilterMixin', 'ContactFilterMixin'] +__all__ = ( + 'ContactFilterMixin', + 'TenancyFilterMixin', +) @dataclass diff --git a/netbox/tenancy/graphql/filters.py b/netbox/tenancy/graphql/filters.py index 59778445756..5abfa0a6ccf 100644 --- a/netbox/tenancy/graphql/filters.py +++ b/netbox/tenancy/graphql/filters.py @@ -1,29 +1,49 @@ from typing import Annotated, TYPE_CHECKING + import strawberry -from strawberry.scalars import ID import strawberry_django -from strawberry_django import ( - FilterLookup, -) +from strawberry.scalars import ID +from strawberry_django import FilterLookup + +from core.graphql.filter_mixins import ChangeLogFilterMixin +from extras.graphql.filter_mixins import CustomFieldsFilterMixin, TagsFilterMixin from netbox.graphql.filter_mixins import ( - PrimaryModelFilterMixin, - OrganizationalModelFilterMixin, NestedGroupModelFilterMixin, + OrganizationalModelFilterMixin, + PrimaryModelFilterMixin, ) -from extras.graphql.filter_mixins import CustomFieldsFilterMixin, TagsFilterMixin -from core.graphql.filter_mixins import ChangeLogFilterMixin from tenancy import models from .filter_mixins import ContactFilterMixin if TYPE_CHECKING: - from .enums import * + from core.graphql.filters import ContentTypeFilter + from circuits.graphql.filters import CircuitFilter + from dcim.graphql.filters import ( + CableFilter, + DeviceFilter, + LocationFilter, + PowerFeedFilter, + RackFilter, + RackReservationFilter, + SiteFilter, + VirtualDeviceContextFilter, + ) + from ipam.graphql.filters import ( + AggregateFilter, + ASNFilter, + ASNRangeFilter, + IPAddressFilter, + IPRangeFilter, + PrefixFilter, + RouteTargetFilter, + VLANFilter, + VRFFilter, + ) from netbox.graphql.filter_lookups import TreeNodeFilter - from circuits.graphql.filters import * - from dcim.graphql.filters import * - from ipam.graphql.filters import * - from wireless.graphql.filters import * - from virtualization.graphql.filters import * - from vpn.graphql.filters import * + from wireless.graphql.filters import WirelessLANFilter, WirelessLinkFilter + from virtualization.graphql.filters import ClusterFilter, VirtualMachineFilter + from vpn.graphql.filters import L2VPNFilter, TunnelFilter + from .enums import * __all__ = ( 'TenantFilter', diff --git a/netbox/tenancy/graphql/mixins.py b/netbox/tenancy/graphql/mixins.py index 9cdba100e04..9437a06f231 100644 --- a/netbox/tenancy/graphql/mixins.py +++ b/netbox/tenancy/graphql/mixins.py @@ -9,5 +9,4 @@ @strawberry.type class ContactAssignmentsMixin: - assignments: List[Annotated["ContactAssignmentType", strawberry.lazy('tenancy.graphql.types')]] # noqa: F821 diff --git a/netbox/users/graphql/filters.py b/netbox/users/graphql/filters.py index 8ead122130d..8f8a8f9466a 100644 --- a/netbox/users/graphql/filters.py +++ b/netbox/users/graphql/filters.py @@ -1,32 +1,13 @@ from datetime import datetime -from typing import Annotated, TYPE_CHECKING +from typing import Annotated + import strawberry import strawberry_django -from strawberry_django import ( - FilterLookup, - DatetimeFilterLookup, -) -from core.graphql.filter_mixins import * -from netbox.graphql.filter_mixins import * -from tenancy.graphql.filter_mixins import * +from strawberry_django import DatetimeFilterLookup, FilterLookup +from core.graphql.filter_mixins import BaseObjectTypeFilterMixin from users import models -if TYPE_CHECKING: - from .enums import * - from netbox.graphql.enums import * - from wireless.graphql.enums import * - from netbox.graphql.filter_lookups import * - from extras.graphql.filters import * - from circuits.graphql.filters import * - from dcim.graphql.filters import * - from ipam.graphql.filters import * - from tenancy.graphql.filters import * - from wireless.graphql.filters import * - from users.graphql.filters import * - from virtualization.graphql.filters import * - from vpn.graphql.filters import * - __all__ = ( 'GroupFilter', 'UserFilter', diff --git a/netbox/virtualization/graphql/filter_mixins.py b/netbox/virtualization/graphql/filter_mixins.py index 4a7386dbee5..e4c3344251d 100644 --- a/netbox/virtualization/graphql/filter_mixins.py +++ b/netbox/virtualization/graphql/filter_mixins.py @@ -1,16 +1,19 @@ from dataclasses import dataclass from typing import Annotated, TYPE_CHECKING + import strawberry -from strawberry import ID import strawberry_django +from strawberry import ID from strawberry_django import FilterLookup from netbox.graphql.filter_mixins import NetBoxModelFilterMixin if TYPE_CHECKING: - from .filters import * + from .filters import VirtualMachineFilter -__all__ = ['VMComponentFilterMixin'] +__all__ = ( + 'VMComponentFilterMixin', +) @dataclass diff --git a/netbox/virtualization/graphql/filters.py b/netbox/virtualization/graphql/filters.py index 66c0e71392e..ab4753616ef 100644 --- a/netbox/virtualization/graphql/filters.py +++ b/netbox/virtualization/graphql/filters.py @@ -1,33 +1,33 @@ from typing import Annotated, TYPE_CHECKING + import strawberry -from strawberry.scalars import ID import strawberry_django -from strawberry_django import ( - FilterLookup, -) -from core.graphql.filter_mixins import * -from netbox.graphql.filter_mixins import * -from tenancy.graphql.filter_mixins import * -from dcim.graphql.filter_mixins import * -from extras.graphql.filter_mixins import * -from virtualization.graphql.filter_mixins import * +from strawberry.scalars import ID +from strawberry_django import FilterLookup +from dcim.graphql.filter_mixins import InterfaceBaseFilterMixin, RenderConfigFilterMixin, ScopedFilterMixin +from extras.graphql.filter_mixins import ConfigContextFilterMixin +from netbox.graphql.filter_mixins import ( + ImageAttachmentFilterMixin, + OrganizationalModelFilterMixin, + PrimaryModelFilterMixin, +) +from tenancy.graphql.filter_mixins import ContactFilterMixin, TenancyFilterMixin from virtualization import models +from virtualization.graphql.filter_mixins import VMComponentFilterMixin if TYPE_CHECKING: from .enums import * - from netbox.graphql.enums import * - from wireless.graphql.enums import * - from netbox.graphql.filter_lookups import * - from extras.graphql.filters import * - from circuits.graphql.filters import * - from dcim.graphql.filters import * - from ipam.graphql.filters import * - from tenancy.graphql.filters import * - from wireless.graphql.filters import * - from users.graphql.filters import * - from virtualization.graphql.filters import * - from vpn.graphql.filters import * + from netbox.graphql.filter_lookups import FloatLookup, IntegerLookup + from dcim.graphql.filters import DeviceFilter, DeviceRoleFilter, MACAddressFilter, PlatformFilter, SiteFilter + from ipam.graphql.filters import ( + FHRPGroupAssignmentFilter, + IPAddressFilter, + ServiceFilter, + VLANGroupFilter, + VRFFilter, + ) + from vpn.graphql.filters import L2VPNFilter, TunnelTerminationFilter __all__ = ( 'ClusterFilter', diff --git a/netbox/vpn/graphql/filters.py b/netbox/vpn/graphql/filters.py index ca038ce3d69..4e12012dd81 100644 --- a/netbox/vpn/graphql/filters.py +++ b/netbox/vpn/graphql/filters.py @@ -1,34 +1,21 @@ from typing import Annotated, TYPE_CHECKING + import strawberry -from strawberry.scalars import ID import strawberry_django -from strawberry_django import ( - FilterLookup, -) -from extras.graphql.filter_mixins import * -from netbox.graphql.filter_mixins import * -from core.graphql.filter_mixins import * -from tenancy.graphql.filter_mixins import * -# from .filter_mixins import * +from strawberry.scalars import ID +from strawberry_django import FilterLookup +from core.graphql.filter_mixins import BaseObjectTypeFilterMixin, ChangeLogFilterMixin +from extras.graphql.filter_mixins import CustomFieldsFilterMixin, TagsFilterMixin +from netbox.graphql.filter_mixins import NetBoxModelFilterMixin, OrganizationalModelFilterMixin, PrimaryModelFilterMixin +from tenancy.graphql.filter_mixins import ContactFilterMixin, TenancyFilterMixin from vpn import models if TYPE_CHECKING: + from core.graphql.filters import ContentTypeFilter + from ipam.graphql.filters import IPAddressFilter, RouteTargetFilter + from netbox.graphql.filter_lookups import IntegerLookup from .enums import * - from netbox.graphql.enums import * - from wireless.graphql.enums import * - from netbox.graphql.filter_lookups import * - from core.graphql.filters import * - from extras.graphql.filters import * - from circuits.graphql.filters import * - from dcim.graphql.filters import * - from ipam.graphql.filters import * - from tenancy.graphql.filters import * - from wireless.graphql.filters import * - from users.graphql.filters import * - from virtualization.graphql.filters import * - from vpn.graphql.filters import * - __all__ = ( 'TunnelGroupFilter', diff --git a/netbox/wireless/graphql/filter_mixins.py b/netbox/wireless/graphql/filter_mixins.py index 2028bba4ed8..636bc8a52ce 100644 --- a/netbox/wireless/graphql/filter_mixins.py +++ b/netbox/wireless/graphql/filter_mixins.py @@ -1,5 +1,6 @@ from dataclasses import dataclass from typing import Annotated, TYPE_CHECKING + import strawberry import strawberry_django from strawberry_django import FilterLookup @@ -7,10 +8,11 @@ from core.graphql.filter_mixins import BaseFilterMixin if TYPE_CHECKING: - from .filters import * from .enums import * -__all__ = ['WirelessAuthenticationBaseFilterMixin'] +__all__ = ( + 'WirelessAuthenticationBaseFilterMixin', +) @dataclass diff --git a/netbox/wireless/graphql/filters.py b/netbox/wireless/graphql/filters.py index cdbf715a296..d71af7ae2e4 100644 --- a/netbox/wireless/graphql/filters.py +++ b/netbox/wireless/graphql/filters.py @@ -1,33 +1,20 @@ from typing import Annotated, TYPE_CHECKING + import strawberry -from strawberry.scalars import ID import strawberry_django -from strawberry_django import ( - FilterLookup, -) -from dcim.graphql.filter_mixins import ScopedFilterMixin -from extras.graphql.filter_mixins import * -from netbox.graphql.filter_mixins import * -from core.graphql.filter_mixins import * -from tenancy.graphql.filter_mixins import * -from .filter_mixins import * +from strawberry.scalars import ID +from strawberry_django import FilterLookup +from dcim.graphql.filter_mixins import ScopedFilterMixin +from netbox.graphql.filter_mixins import DistanceFilterMixin, PrimaryModelFilterMixin, NestedGroupModelFilterMixin +from tenancy.graphql.filter_mixins import TenancyFilterMixin from wireless import models +from .filter_mixins import WirelessAuthenticationBaseFilterMixin if TYPE_CHECKING: + from dcim.graphql.filters import InterfaceFilter + from ipam.graphql.filters import VLANFilter from .enums import * - from netbox.graphql.enums import * - from wireless.graphql.enums import * - from netbox.graphql.filter_lookups import * - from extras.graphql.filters import * - from circuits.graphql.filters import * - from dcim.graphql.filters import * - from ipam.graphql.filters import * - from tenancy.graphql.filters import * - from wireless.graphql.filters import * - from users.graphql.filters import * - from virtualization.graphql.filters import * - from vpn.graphql.filters import * __all__ = ( 'WirelessLANGroupFilter', From 52b0555940b577cd6063bfe415b1ee3eda132675 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Fri, 7 Mar 2025 11:24:11 -0500 Subject: [PATCH 13/13] Clean up typing for GraphQL types --- netbox/circuits/graphql/types.py | 14 ++++--- netbox/core/graphql/types.py | 3 +- netbox/dcim/graphql/types.py | 55 +++++++++++++++++--------- netbox/extras/graphql/types.py | 21 ++++++++-- netbox/ipam/graphql/types.py | 27 ++++++++++--- netbox/netbox/graphql/types.py | 1 + netbox/tenancy/graphql/types.py | 34 ++++++++++++---- netbox/virtualization/graphql/types.py | 19 ++++++++- netbox/vpn/graphql/types.py | 11 +++++- netbox/wireless/graphql/types.py | 9 ++++- 10 files changed, 148 insertions(+), 46 deletions(-) diff --git a/netbox/circuits/graphql/types.py b/netbox/circuits/graphql/types.py index 564b5ed6f93..860c19852b6 100644 --- a/netbox/circuits/graphql/types.py +++ b/netbox/circuits/graphql/types.py @@ -1,4 +1,4 @@ -from typing import Annotated, List, Union +from typing import Annotated, List, TYPE_CHECKING, Union import strawberry import strawberry_django @@ -10,11 +10,15 @@ from tenancy.graphql.types import TenantType from .filters import * +if TYPE_CHECKING: + from dcim.graphql.types import InterfaceType, LocationType, RegionType, SiteGroupType, SiteType + from ipam.graphql.types import ASNType + __all__ = ( - 'CircuitTerminationType', - 'CircuitType', 'CircuitGroupAssignmentType', 'CircuitGroupType', + 'CircuitTerminationType', + 'CircuitType', 'CircuitTypeType', 'ProviderType', 'ProviderAccountType', @@ -62,7 +66,7 @@ class ProviderNetworkType(NetBoxObjectType): @strawberry_django.type( models.CircuitTermination, - exclude=('termination_type', 'termination_id', '_location', '_region', '_site', '_site_group', '_provider_network'), + exclude=['termination_type', 'termination_id', '_location', '_region', '_site', '_site_group', '_provider_network'], filters=CircuitTerminationFilter ) class CircuitTerminationType(CustomFieldsMixin, TagsMixin, CabledObjectMixin, ObjectType): @@ -117,7 +121,7 @@ class CircuitGroupType(OrganizationalObjectType): @strawberry_django.type( models.CircuitGroupAssignment, - exclude=('member_type', 'member_id'), + exclude=['member_type', 'member_id'], filters=CircuitGroupAssignmentFilter ) class CircuitGroupAssignmentType(TagsMixin, BaseObjectType): diff --git a/netbox/core/graphql/types.py b/netbox/core/graphql/types.py index fe28bdc4d07..1c57a2dbc87 100644 --- a/netbox/core/graphql/types.py +++ b/netbox/core/graphql/types.py @@ -3,15 +3,16 @@ import strawberry import strawberry_django from django.contrib.contenttypes.models import ContentType as DjangoContentType + from core import models from netbox.graphql.types import BaseObjectType, NetBoxObjectType from .filters import * __all__ = ( + 'ContentType', 'DataFileType', 'DataSourceType', 'ObjectChangeType', - 'ContentType', ) diff --git a/netbox/dcim/graphql/types.py b/netbox/dcim/graphql/types.py index 8d992176a22..6cd0724791b 100644 --- a/netbox/dcim/graphql/types.py +++ b/netbox/dcim/graphql/types.py @@ -1,4 +1,4 @@ -from typing import Annotated, List, Union +from typing import Annotated, List, TYPE_CHECKING, Union import strawberry import strawberry_django @@ -6,7 +6,11 @@ from core.graphql.mixins import ChangelogMixin from dcim import models from extras.graphql.mixins import ( - ConfigContextMixin, ContactsMixin, CustomFieldsMixin, ImageAttachmentsMixin, TagsMixin, + ConfigContextMixin, + ContactsMixin, + CustomFieldsMixin, + ImageAttachmentsMixin, + TagsMixin, ) from ipam.graphql.mixins import IPAddressesMixin, VLANGroupsMixin from netbox.graphql.scalars import BigInt @@ -14,6 +18,23 @@ from .filters import * from .mixins import CabledObjectMixin, PathEndpointMixin +if TYPE_CHECKING: + from circuits.graphql.types import CircuitTerminationType + from extras.graphql.types import ConfigTemplateType + from ipam.graphql.types import ( + ASNType, + IPAddressType, + PrefixType, + ServiceType, + VLANTranslationPolicyType, + VLANType, + VRFType, + ) + from tenancy.graphql.types import TenantType + from users.graphql.types import UserType + from virtualization.graphql.types import ClusterType, VMInterfaceType, VirtualMachineType + from wireless.graphql.types import WirelessLANType, WirelessLinkType + __all__ = ( 'CableType', 'ComponentType', @@ -111,7 +132,7 @@ class ModularComponentTemplateType(ComponentTemplateType): @strawberry_django.type( models.CableTermination, - exclude=('termination_type', 'termination_id', '_device', '_rack', '_location', '_site'), + exclude=['termination_type', 'termination_id', '_device', '_rack', '_location', '_site'], filters=CableTerminationFilter ) class CableTerminationType(NetBoxObjectType): @@ -167,7 +188,7 @@ class CableType(NetBoxObjectType): @strawberry_django.type( models.ConsolePort, - exclude=('_path',), + exclude=['_path'], filters=ConsolePortFilter ) class ConsolePortType(ModularComponentType, CabledObjectMixin, PathEndpointMixin): @@ -185,7 +206,7 @@ class ConsolePortTemplateType(ModularComponentTemplateType): @strawberry_django.type( models.ConsoleServerPort, - exclude=('_path',), + exclude=['_path'], filters=ConsoleServerPortFilter ) class ConsoleServerPortType(ModularComponentType, CabledObjectMixin, PathEndpointMixin): @@ -276,7 +297,7 @@ class DeviceBayTemplateType(ComponentTemplateType): @strawberry_django.type( models.InventoryItemTemplate, - exclude=('component_type', 'component_id', 'parent'), + exclude=['component_type', 'component_id', 'parent'], filters=InventoryItemTemplateFilter ) class InventoryItemTemplateType(ComponentTemplateType): @@ -369,7 +390,7 @@ class FrontPortTemplateType(ModularComponentTemplateType): @strawberry_django.type( models.MACAddress, - exclude=('assigned_object_type', 'assigned_object_id'), + exclude=['assigned_object_type', 'assigned_object_id'], filters=MACAddressFilter ) class MACAddressType(NetBoxObjectType): @@ -385,7 +406,7 @@ def assigned_object(self) -> Annotated[Union[ @strawberry_django.type( models.Interface, - exclude=('_path',), + exclude=['_path'], filters=InterfaceFilter ) class InterfaceType(IPAddressesMixin, ModularComponentType, CabledObjectMixin, PathEndpointMixin): @@ -424,7 +445,7 @@ class InterfaceTemplateType(ModularComponentTemplateType): @strawberry_django.type( models.InventoryItem, - exclude=('component_type', 'component_id', 'parent'), + exclude=['component_type', 'component_id', 'parent'], filters=InventoryItemFilter ) class InventoryItemType(ComponentType): @@ -463,7 +484,7 @@ class InventoryItemRoleType(OrganizationalObjectType): @strawberry_django.type( models.Location, # fields='__all__', - exclude=('parent',), # bug - temp + exclude=['parent'], # bug - temp filters=LocationFilter ) class LocationType(VLANGroupsMixin, ImageAttachmentsMixin, ContactsMixin, OrganizationalObjectType): @@ -524,7 +545,7 @@ class ModuleType(NetBoxObjectType): @strawberry_django.type( models.ModuleBay, # fields='__all__', - exclude=('parent',), + exclude=['parent'], filters=ModuleBayFilter ) class ModuleBayType(ModularComponentType): @@ -579,7 +600,7 @@ class PlatformType(OrganizationalObjectType): @strawberry_django.type( models.PowerFeed, - exclude=('_path',), + exclude=['_path'], filters=PowerFeedFilter ) class PowerFeedType(NetBoxObjectType, CabledObjectMixin, PathEndpointMixin): @@ -590,7 +611,7 @@ class PowerFeedType(NetBoxObjectType, CabledObjectMixin, PathEndpointMixin): @strawberry_django.type( models.PowerOutlet, - exclude=('_path',), + exclude=['_path'], filters=PowerOutletFilter ) class PowerOutletType(ModularComponentType, CabledObjectMixin, PathEndpointMixin): @@ -621,7 +642,7 @@ class PowerPanelType(NetBoxObjectType, ContactsMixin): @strawberry_django.type( models.PowerPort, - exclude=('_path',), + exclude=['_path'], filters=PowerPortFilter ) class PowerPortType(ModularComponentType, CabledObjectMixin, PathEndpointMixin): @@ -712,8 +733,7 @@ class RearPortTemplateType(ModularComponentTemplateType): @strawberry_django.type( models.Region, - exclude=('parent',), - # fields='__all__', + exclude=['parent'], filters=RegionFilter ) class RegionType(VLANGroupsMixin, ContactsMixin, OrganizationalObjectType): @@ -772,8 +792,7 @@ def circuit_terminations(self) -> List[ @strawberry_django.type( models.SiteGroup, - # fields='__all__', - exclude=('parent',), # bug - temp + exclude=['parent'], # bug - temp filters=SiteGroupFilter ) class SiteGroupType(VLANGroupsMixin, ContactsMixin, OrganizationalObjectType): diff --git a/netbox/extras/graphql/types.py b/netbox/extras/graphql/types.py index a53c7bed3e7..1ceb2682c21 100644 --- a/netbox/extras/graphql/types.py +++ b/netbox/extras/graphql/types.py @@ -1,4 +1,4 @@ -from typing import Annotated, List +from typing import Annotated, List, TYPE_CHECKING import strawberry import strawberry_django @@ -8,6 +8,22 @@ from netbox.graphql.types import BaseObjectType, ContentTypeType, ObjectType, OrganizationalObjectType from .filters import * +if TYPE_CHECKING: + from core.graphql.types import DataFileType, DataSourceType + from dcim.graphql.types import ( + DeviceRoleType, + DeviceType, + DeviceTypeType, + LocationType, + PlatformType, + RegionType, + SiteGroupType, + SiteType, + ) + from tenancy.graphql.types import TenantGroupType, TenantType + from users.graphql.types import GroupType, UserType + from virtualization.graphql.types import ClusterGroupType, ClusterType, ClusterTypeType, VirtualMachineType + __all__ = ( 'ConfigContextType', 'ConfigTemplateType', @@ -35,7 +51,6 @@ class ConfigContextType(ObjectType): data_source: Annotated["DataSourceType", strawberry.lazy('core.graphql.types')] | None data_file: Annotated["DataFileType", strawberry.lazy('core.graphql.types')] | None - roles: List[Annotated["DeviceRoleType", strawberry.lazy('dcim.graphql.types')]] device_types: List[Annotated["DeviceTypeType", strawberry.lazy('dcim.graphql.types')]] tags: List[Annotated["TagType", strawberry.lazy('extras.graphql.types')]] @@ -78,7 +93,7 @@ class CustomFieldType(ObjectType): @strawberry_django.type( models.CustomFieldChoiceSet, - exclude=('extra_choices', ), + exclude=['extra_choices'], filters=CustomFieldChoiceSetFilter ) class CustomFieldChoiceSetType(ObjectType): diff --git a/netbox/ipam/graphql/types.py b/netbox/ipam/graphql/types.py index e6ecca984a9..086d5980b6f 100644 --- a/netbox/ipam/graphql/types.py +++ b/netbox/ipam/graphql/types.py @@ -1,4 +1,4 @@ -from typing import Annotated, List, Union +from typing import Annotated, List, TYPE_CHECKING, Union import strawberry import strawberry_django @@ -11,6 +11,21 @@ from .filters import * from .mixins import IPAddressesMixin +if TYPE_CHECKING: + from dcim.graphql.types import ( + DeviceType, + InterfaceType, + LocationType, + RackType, + RegionType, + SiteGroupType, + SiteType, + ) + from tenancy.graphql.types import TenantType + from virtualization.graphql.types import ClusterGroupType, ClusterType, VMInterfaceType, VirtualMachineType + from vpn.graphql.types import L2VPNType, TunnelTerminationType + from wireless.graphql.types import WirelessLANType + __all__ = ( 'ASNType', 'ASNRangeType', @@ -101,7 +116,7 @@ class FHRPGroupType(NetBoxObjectType, IPAddressesMixin): @strawberry_django.type( models.FHRPGroupAssignment, - exclude=('interface_type', 'interface_id'), + exclude=['interface_type', 'interface_id'], filters=FHRPGroupAssignmentFilter ) class FHRPGroupAssignmentType(BaseObjectType): @@ -117,7 +132,7 @@ def interface(self) -> Annotated[Union[ @strawberry_django.type( models.IPAddress, - exclude=('assigned_object_type', 'assigned_object_id', 'address'), + exclude=['assigned_object_type', 'assigned_object_id', 'address'], filters=IPAddressFilter ) class IPAddressType(NetBoxObjectType, BaseIPAddressFamilyType): @@ -154,7 +169,7 @@ class IPRangeType(NetBoxObjectType): @strawberry_django.type( models.Prefix, - exclude=('scope_type', 'scope_id', '_location', '_region', '_site', '_site_group'), + exclude=['scope_type', 'scope_id', '_location', '_region', '_site', '_site_group'], filters=PrefixFilter ) class PrefixType(NetBoxObjectType, BaseIPAddressFamilyType): @@ -236,7 +251,7 @@ class ServiceTemplateType(NetBoxObjectType): @strawberry_django.type( models.VLAN, - exclude=('qinq_svlan',), + exclude=['qinq_svlan'], filters=VLANFilter ) class VLANType(NetBoxObjectType): @@ -259,7 +274,7 @@ def qinq_svlan(self) -> Annotated["VLANType", strawberry.lazy('ipam.graphql.type @strawberry_django.type( models.VLANGroup, - exclude=('scope_type', 'scope_id'), + exclude=['scope_type', 'scope_id'], filters=VLANGroupFilter ) class VLANGroupType(OrganizationalObjectType): diff --git a/netbox/netbox/graphql/types.py b/netbox/netbox/graphql/types.py index a4fc9908072..5df4cfd383e 100644 --- a/netbox/netbox/graphql/types.py +++ b/netbox/netbox/graphql/types.py @@ -8,6 +8,7 @@ __all__ = ( 'BaseObjectType', + 'ContentTypeType', 'ObjectType', 'OrganizationalObjectType', 'NetBoxObjectType', diff --git a/netbox/tenancy/graphql/types.py b/netbox/tenancy/graphql/types.py index a8b338578b9..c340cdf7c77 100644 --- a/netbox/tenancy/graphql/types.py +++ b/netbox/tenancy/graphql/types.py @@ -6,16 +6,36 @@ from extras.graphql.mixins import CustomFieldsMixin, TagsMixin from netbox.graphql.types import BaseObjectType, OrganizationalObjectType, NetBoxObjectType from tenancy import models -from .mixins import ContactAssignmentsMixin from .filters import * +from .mixins import ContactAssignmentsMixin if TYPE_CHECKING: - from circuits.graphql.types import * - from dcim.graphql.types import * - from ipam.graphql.types import * - from wireless.graphql.types import * - from virtualization.graphql.types import * - from vpn.graphql.types import * + from circuits.graphql.types import CircuitType + from dcim.graphql.types import ( + CableType, + DeviceType, + LocationType, + PowerFeedType, + RackType, + RackReservationType, + SiteType, + VirtualDeviceContextType, + ) + from ipam.graphql.types import ( + AggregateType, + ASNType, + ASNRangeType, + IPAddressType, + IPRangeType, + PrefixType, + RouteTargetType, + VLANType, + VRFType, + ) + from netbox.graphql.types import ContentTypeType + from wireless.graphql.types import WirelessLANType, WirelessLinkType + from virtualization.graphql.types import ClusterType, VirtualMachineType + from vpn.graphql.types import L2VPNType, TunnelType __all__ = ( 'ContactAssignmentType', diff --git a/netbox/virtualization/graphql/types.py b/netbox/virtualization/graphql/types.py index 33d6ce450b0..2fcffc20f49 100644 --- a/netbox/virtualization/graphql/types.py +++ b/netbox/virtualization/graphql/types.py @@ -1,4 +1,4 @@ -from typing import Annotated, List, Union +from typing import Annotated, List, TYPE_CHECKING, Union import strawberry import strawberry_django @@ -10,6 +10,21 @@ from virtualization import models from .filters import * +if TYPE_CHECKING: + from dcim.graphql.types import ( + DeviceRoleType, + DeviceType, + LocationType, + MACAddressType, + PlatformType, + RegionType, + SiteGroupType, + SiteType, + ) + from extras.graphql.types import ConfigTemplateType + from ipam.graphql.types import IPAddressType, ServiceType, VLANTranslationPolicyType, VLANType, VRFType + from tenancy.graphql.types import TenantType + __all__ = ( 'ClusterType', 'ClusterGroupType', @@ -30,7 +45,7 @@ class ComponentType(NetBoxObjectType): @strawberry_django.type( models.Cluster, - exclude=('scope_type', 'scope_id', '_location', '_region', '_site', '_site_group'), + exclude=['scope_type', 'scope_id', '_location', '_region', '_site', '_site_group'], filters=ClusterFilter ) class ClusterType(VLANGroupsMixin, NetBoxObjectType): diff --git a/netbox/vpn/graphql/types.py b/netbox/vpn/graphql/types.py index 7940bd326ba..cc133ee6f76 100644 --- a/netbox/vpn/graphql/types.py +++ b/netbox/vpn/graphql/types.py @@ -1,4 +1,4 @@ -from typing import Annotated, List, Union +from typing import Annotated, List, TYPE_CHECKING, Union import strawberry import strawberry_django @@ -8,6 +8,13 @@ from vpn import models from .filters import * +if TYPE_CHECKING: + from dcim.graphql.types import InterfaceType + from ipam.graphql.types import IPAddressType, RouteTargetType, VLANType + from netbox.graphql.types import ContentTypeType + from tenancy.graphql.types import TenantType + from virtualization.graphql.types import VMInterfaceType + __all__ = ( 'IKEPolicyType', 'IKEProposalType', @@ -125,7 +132,7 @@ class L2VPNType(ContactsMixin, NetBoxObjectType): @strawberry_django.type( models.L2VPNTermination, - exclude=('assigned_object_type', 'assigned_object_id'), + exclude=['assigned_object_type', 'assigned_object_id'], filters=L2VPNTerminationFilter ) class L2VPNTerminationType(NetBoxObjectType): diff --git a/netbox/wireless/graphql/types.py b/netbox/wireless/graphql/types.py index aa44e9b9fab..7c31dfec8c2 100644 --- a/netbox/wireless/graphql/types.py +++ b/netbox/wireless/graphql/types.py @@ -1,4 +1,4 @@ -from typing import Annotated, List, Union +from typing import Annotated, List, TYPE_CHECKING, Union import strawberry import strawberry_django @@ -7,6 +7,11 @@ from wireless import models from .filters import * +if TYPE_CHECKING: + from dcim.graphql.types import DeviceType, InterfaceType, LocationType, RegionType, SiteGroupType, SiteType + from ipam.graphql.types import VLANType + from tenancy.graphql.types import TenantType + __all__ = ( 'WirelessLANType', 'WirelessLANGroupType', @@ -28,7 +33,7 @@ class WirelessLANGroupType(OrganizationalObjectType): @strawberry_django.type( models.WirelessLAN, - exclude=('scope_type', 'scope_id', '_location', '_region', '_site', '_site_group'), + exclude=['scope_type', 'scope_id', '_location', '_region', '_site', '_site_group'], filters=WirelessLANFilter ) class WirelessLANType(NetBoxObjectType):