diff --git a/netbox/circuits/graphql/filters.py b/netbox/circuits/graphql/filters.py index ce478f0aa..10887ce3f 100644 --- a/netbox/circuits/graphql/filters.py +++ b/netbox/circuits/graphql/filters.py @@ -1,16 +1,8 @@ -from typing import List - -import django_filters import strawberry import strawberry_django from circuits import filtersets, models -from functools import partial, partialmethod, wraps -from strawberry import auto -from strawberry_django.filters import FilterLookup -from tenancy.graphql.filter_mixins import ContactModelFilterMixin, TenancyFilterMixin -from utilities.filters import * -from netbox.graphql.filter_mixins import NetBoxModelFilterMixin +from netbox.graphql.filter_mixins import autotype_decorator, BaseFilterMixin __all__ = ( 'CircuitTerminationFilter', @@ -21,313 +13,38 @@ __all__ = ( 'ProviderNetworkFilter', ) -# def filter_by_filterset(self, queryset, key, cls, filterset): -# breakpoint() -# return filterset(data={key: getattr(cls, key)}, queryset=queryset).qs - - -def autotype_decorator(filterset): - - def wrapper(cls): - cls.filterset = filterset - fields = filterset.get_fields() - print(f"fields: {fields}") - for fieldname in fields.keys(): - if fieldname not in cls.__annotations__: - cls.__annotations__[fieldname] = auto - - # fields = list(filterset.get_fields().keys()) - declared_filters = filterset.declared_filters - print(f"declared_filters: {declared_filters}") - print("") - for fieldname, v in declared_filters.items(): - create_function = False - attr_type = None - print(f"{fieldname}: {v}") - - if isinstance(v, ContentTypeFilter): - print("ContentTypeFilter") - elif isinstance(v, MACAddressFilter): - print("MACAddressFilter") - elif isinstance(v, MultiValueArrayFilter): - print("MultiValueArrayFilter") - elif isinstance(v, MultiValueCharFilter): - print("MultiValueCharFilter") - elif isinstance(v, MultiValueDateFilter): - print("MultiValueDateFilter") - elif isinstance(v, MultiValueDateTimeFilter): - print("MultiValueDateTimeFilter") - elif isinstance(v, MultiValueDecimalFilter): - print("MultiValueDecimalFilter") - elif isinstance(v, MultiValueMACAddressFilter): - print("MultiValueMACAddressFilter") - elif isinstance(v, MultiValueNumberFilter): - print("MultiValueNumberFilter") - elif isinstance(v, MultiValueTimeFilter): - print("MultiValueTimeFilter") - elif isinstance(v, MultiValueWWNFilter): - print("MultiValueWWNFilter") - elif isinstance(v, NullableCharFieldFilter): - print("NullableCharFieldFilter") - elif isinstance(v, NumericArrayFilter): - print("NumericArrayFilter") - elif isinstance(v, TreeNodeMultipleChoiceFilter): - print("TreeNodeMultipleChoiceFilter") - - elif issubclass(type(v), django_filters.CharFilter): - print("CharFilter") - elif issubclass(type(v), django_filters.UUIDFilter): - print("UUIDFilter") - elif issubclass(type(v), django_filters.BooleanFilter): - print("BooleanFilter") - elif issubclass(type(v), django_filters.ChoiceFilter): - print("ChoiceFilter") - elif issubclass(type(v), django_filters.TypedChoiceFilter): - print("TypedChoiceFilter") - elif issubclass(type(v), django_filters.DateFilter): - print("DateFilter") - elif issubclass(type(v), django_filters.TimeFilter): - print("TimeFilter") - elif issubclass(type(v), django_filters.DateTimeFilter): - print("DateTimeFilter") - elif issubclass(type(v), django_filters.IsoDateTimeFilter): - print("IsoDateTimeFilter") - elif issubclass(type(v), django_filters.DurationFilter): - print("DurationFilter") - elif issubclass(type(v), django_filters.ModelChoiceFilter): - print("ModelChoiceFilter") - elif issubclass(type(v), django_filters.ModelMultipleChoiceFilter): - create_function = True - attr_type = List[str] | None - print("ModelMultipleChoiceFilter") - elif issubclass(type(v), django_filters.NumberFilter): - print("NumberFilter") - elif issubclass(type(v), django_filters.NumericRangeFilter): - print("NumericRangeFilter") - elif issubclass(type(v), django_filters.RangeFilter): - print("RangeFilter") - elif issubclass(type(v), django_filters.DateRangeFilter): - print("DateRangeFilter") - elif issubclass(type(v), django_filters.DateFromToRangeFilter): - print("DateFromToRangeFilter") - elif issubclass(type(v), django_filters.DateTimeFromToRangeFilter): - print("DateTimeFromToRangeFilter") - elif issubclass(type(v), django_filters.IsoDateTimeFromToRangeFilter): - print("IsoDateTimeFromToRangeFilter") - elif issubclass(type(v), django_filters.TimeRangeFilter): - print("TimeRangeFilter") - elif issubclass(type(v), django_filters.AllValuesFilter): - print("AllValuesFilter") - elif issubclass(type(v), django_filters.AllValuesMultipleFilter): - print("AllValuesMultipleFilter") - elif issubclass(type(v), django_filters.LookupChoiceFilter): - print("LookupChoiceFilter") - elif issubclass(type(v), django_filters.BaseInFilter): - print("BaseInFilter") - elif issubclass(type(v), django_filters.BaseRangeFilter): - print("BaseRangeFilter") - elif issubclass(type(v), django_filters.OrderingFilter): - print("OrderingFilter") - elif issubclass(type(v), django_filters.TypedMultipleChoiceFilter): - print("TypedMultipleChoiceFilter") - elif issubclass(type(v), django_filters.MultipleChoiceFilter): - print("MultipleChoiceFilter") - else: - print("unknown type!") - - if fieldname not in cls.__annotations__ and attr_type: - print(f"adding {fieldname} to class") - cls.__annotations__[fieldname] = attr_type - - fname = f"filter_{fieldname}" - if create_function and not hasattr(cls, fname): - print(f"creating function {fname}") - filter_by_filterset = getattr(cls, 'filter_by_filterset') - setattr(cls, fname, partialmethod(filter_by_filterset, key=fieldname)) - # setattr(cls, fname, partial(filter_by_filterset, key=fieldname, cls=cls, filterset=filterset)) - - print("") - return cls - - return wrapper - - -""" -class autotype_decorator(object): - def __init__(self, filterset): - self.filterset = filterset - def __call__(self, cls): - class Wrapped(cls): - ''' - cls.filterset = filterset - fields = filterset.get_fields() - print(fields) - fields = list(filterset.get_fields().keys()) - declared_filters = filterset.declared_filters - print(declared_filters) - fields.extend(list(filterset.declared_filters.keys())) - for field in fields: - print(field) - - ''' - print(f"cls: {cls}") - print(f"self: {self}") - vars()['cid'] = strawberry.unset.UnsetType - # setattr(cls, 'cid', strawberry.unset.UnsetType) - pass - - setattr(Wrapped, 'cid', strawberry.unset.UnsetType) - print(f"hasattr: {hasattr(Wrapped, 'cid')}") - print(Wrapped) - return Wrapped -""" - @strawberry_django.filter(models.CircuitTermination, lookups=True) -class CircuitTerminationFilter(filtersets.CircuitTerminationFilterSet): - id: auto - term_side: auto - port_speed: auto - upstream_speed: auto - xconnect_id: auto - description: auto - cable_end: auto - # q: auto - circuit_id: auto - site_id: auto - site: auto - # provider_network_id: auto +@autotype_decorator(filtersets.CircuitTerminationFilterSet) +class CircuitTerminationFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.Circuit, lookups=True) @autotype_decorator(filtersets.CircuitFilterSet) -class CircuitFilter: - # class CircuitFilter(NetBoxModelFilterMixin, TenancyFilterMixin, ContactModelFilterMixin): - - def filter_by_filterset(self, queryset, key): - return self.filterset(data={key: getattr(self, key)}, queryset=queryset).qs - +class CircuitFilter(BaseFilterMixin): pass - """ - # vars()['cid'] = strawberry.unset.UnsetType - # cid: auto - description: auto - install_date: auto - termination_date: auto - commit_rate: auto - - provider_id: List[str] | None - provider: List[str] | None - provider_account_id: List[str] | None - provider_network_id: List[str] | None - type_id: List[str] | None - type: List[str] | None - status: auto - region_id: List[str] | None - region: List[str] | None - site_group_id: List[str] | None - site_group: List[str] | None - site_id: List[str] | None - site: List[str] | None - - def filter_provider_id(self, queryset): - return self.filter_by_filterset(queryset, 'provider_id') - - def filter_provider(self, queryset): - return self.filter_by_filterset(queryset, 'provider') - - def filter_provider_account_id(self, queryset): - return self.filter_by_filterset(queryset, 'provider_account_id') - - def filter_provider_network_id(self, queryset): - return self.filter_by_filterset(queryset, 'provider_network_id') - - def filter_type_id(self, queryset): - return self.filter_by_filterset(queryset, 'type_id') - - def filter_type(self, queryset): - return self.filter_by_filterset(queryset, 'type') - - def filter_region_id(self, queryset): - return self.filter_by_filterset(queryset, 'region_id') - - def filter_region(self, queryset): - return self.filter_by_filterset(queryset, 'region') - - def filter_site_group_id(self, queryset): - return self.filter_by_filterset(queryset, 'site_group_id') - - def filter_site_group(self, queryset): - return self.filter_by_filterset(queryset, 'site_group') - - def filter_site_id(self, queryset): - return self.filter_by_filterset(queryset, 'site_id') - - def filter_site(self, queryset): - return self.filter_by_filterset(queryset, 'site') - """ - - -# @strawberry_django.filter(models.Circuit, lookups=True) -# class CircuitFilter(filtersets.CircuitFilterSet): -# id: auto -# cid: auto -# description: auto -# install_date: auto -# termination_date: auto -# commit_rate: auto -# provider_id: auto -# provider: auto -# provider_account_id: auto -# # provider_network_id: auto -# type_id: auto -# type: auto -# status: auto -# # region_id: auto -# # region: auto -# # site_group_id: auto -# # site_group: auto -# # site_id: auto -# # site: auto @strawberry_django.filter(models.CircuitType, lookups=True) -class CircuitTypeFilter(filtersets.CircuitTypeFilterSet): - id: auto - name: auto - slug: auto - description: auto +@autotype_decorator(filtersets.CircuitTypeFilterSet) +class CircuitTypeFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.Provider, lookups=True) -class ProviderFilter(filtersets.ProviderFilterSet): - id: auto - name: auto - slug: auto - # region_id: auto - # region: auto - # site_group_id: auto - # site_group: auto - # site_id: auto - # site: auto - # asn_id: auto +@autotype_decorator(filtersets.ProviderFilterSet) +class ProviderFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.ProviderAccount, lookups=True) -class ProviderAccountFilter(filtersets.ProviderAccountFilterSet): - id: auto - name: auto - account: auto - description: auto - # provider_id: auto - # provider: auto +@autotype_decorator(filtersets.ProviderAccountFilterSet) +class ProviderAccountFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.ProviderNetwork, lookups=True) -class ProviderNetworkFilter(filtersets.ProviderNetworkFilterSet): - id: auto - name: auto - service_id: auto - description: auto - # provider_id: auto - # provider: auto +@autotype_decorator(filtersets.ProviderNetworkFilterSet) +class ProviderNetworkFilter(BaseFilterMixin): + pass diff --git a/netbox/core/graphql/filters.py b/netbox/core/graphql/filters.py index 94a5eaf26..fd3f9e459 100644 --- a/netbox/core/graphql/filters.py +++ b/netbox/core/graphql/filters.py @@ -1,7 +1,8 @@ import strawberry import strawberry_django from core import filtersets, models -from strawberry import auto + +from netbox.graphql.filter_mixins import autotype_decorator, BaseFilterMixin __all__ = ( 'DataFileFilter', @@ -10,10 +11,12 @@ __all__ = ( @strawberry_django.filter(models.DataFile, lookups=True) -class DataFileFilter(filtersets.DataFileFilterSet): - id: auto +@autotype_decorator(filtersets.DataFileFilterSet) +class DataFileFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.DataSource, lookups=True) -class DataSourceFilter(filtersets.DataSourceFilterSet): - id: auto +@autotype_decorator(filtersets.DataSourceFilterSet) +class DataSourceFilter(BaseFilterMixin): + pass diff --git a/netbox/dcim/graphql/filters.py b/netbox/dcim/graphql/filters.py index b0e478a2b..2874b4418 100644 --- a/netbox/dcim/graphql/filters.py +++ b/netbox/dcim/graphql/filters.py @@ -1,7 +1,8 @@ import strawberry import strawberry_django from dcim import filtersets, models -from strawberry import auto + +from netbox.graphql.filter_mixins import autotype_decorator, BaseFilterMixin __all__ = ( 'CableFilter', @@ -49,205 +50,246 @@ __all__ = ( @strawberry_django.filter(models.Cable, lookups=True) -class CableFilter(filtersets.CableFilterSet): - id: auto +@autotype_decorator(filtersets.CableFilterSet) +class CableFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.CableTermination, lookups=True) -class CableTerminationFilter(filtersets.CableTerminationFilterSet): - id: auto +@autotype_decorator(filtersets.CableTerminationFilterSet) +class CableTerminationFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.ConsolePort, lookups=True) -class ConsolePortFilter(filtersets.ConsolePortFilterSet): - id: auto +@autotype_decorator(filtersets.ConsolePortFilterSet) +class ConsolePortFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.ConsolePortTemplate, lookups=True) -class ConsolePortTemplateFilter(filtersets.ConsolePortTemplateFilterSet): - id: auto +@autotype_decorator(filtersets.ConsolePortTemplateFilterSet) +class ConsolePortTemplateFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.ConsoleServerPort, lookups=True) -class ConsoleServerPortFilter(filtersets.ConsoleServerPortFilterSet): - id: auto +@autotype_decorator(filtersets.ConsoleServerPortFilterSet) +class ConsoleServerPortFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.ConsoleServerPortTemplate, lookups=True) -class ConsoleServerPortTemplateFilter(filtersets.ConsoleServerPortTemplateFilterSet): - id: auto +@autotype_decorator(filtersets.ConsoleServerPortTemplateFilterSet) +class ConsoleServerPortTemplateFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.Device, lookups=True) -class DeviceFilter(filtersets.DeviceFilterSet): - id: auto +@autotype_decorator(filtersets.DeviceFilterSet) +class DeviceFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.DeviceBay, lookups=True) -class DeviceBayFilter(filtersets.DeviceBayFilterSet): - id: auto +@autotype_decorator(filtersets.DeviceBayFilterSet) +class DeviceBayFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.DeviceBayTemplate, lookups=True) -class DeviceBayTemplateFilter(filtersets.DeviceBayTemplateFilterSet): - id: auto +@autotype_decorator(filtersets.DeviceBayTemplateFilterSet) +class DeviceBayTemplateFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.InventoryItemTemplate, lookups=True) -class InventoryItemTemplateFilter(filtersets.InventoryItemTemplateFilterSet): - id: auto +@autotype_decorator(filtersets.InventoryItemTemplateFilterSet) +class InventoryItemTemplateFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.DeviceRole, lookups=True) -class DeviceRoleFilter(filtersets.DeviceRoleFilterSet): - id: auto +@autotype_decorator(filtersets.DeviceRoleFilterSet) +class DeviceRoleFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.DeviceType, lookups=True) -class DeviceTypeFilter(filtersets.DeviceTypeFilterSet): - id: auto +@autotype_decorator(filtersets.DeviceTypeFilterSet) +class DeviceTypeFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.FrontPort, lookups=True) -class FrontPortFilter(filtersets.FrontPortFilterSet): - id: auto +@autotype_decorator(filtersets.FrontPortFilterSet) +class FrontPortFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.FrontPortTemplate, lookups=True) -class FrontPortTemplateFilter(filtersets.FrontPortTemplateFilterSet): - id: auto +@autotype_decorator(filtersets.FrontPortTemplateFilterSet) +class FrontPortTemplateFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.Interface, lookups=True) -class InterfaceFilter(filtersets.InterfaceFilterSet): - id: auto +@autotype_decorator(filtersets.InterfaceFilterSet) +class InterfaceFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.InterfaceTemplate, lookups=True) -class InterfaceTemplateFilter(filtersets.InterfaceTemplateFilterSet): - id: auto +@autotype_decorator(filtersets.InterfaceTemplateFilterSet) +class InterfaceTemplateFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.InventoryItem, lookups=True) -class InventoryItemFilter(filtersets.InventoryItemFilterSet): - id: auto +@autotype_decorator(filtersets.InventoryItemFilterSet) +class InventoryItemFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.InventoryItemRole, lookups=True) -class InventoryItemRoleFilter(filtersets.InventoryItemRoleFilterSet): - id: auto +@autotype_decorator(filtersets.InventoryItemRoleFilterSet) +class InventoryItemRoleFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.Location, lookups=True) -class LocationFilter(filtersets.LocationFilterSet): - id: auto +@autotype_decorator(filtersets.LocationFilterSet) +class LocationFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.Manufacturer, lookups=True) -class ManufacturerFilter(filtersets.ManufacturerFilterSet): - id: auto +@autotype_decorator(filtersets.ManufacturerFilterSet) +class ManufacturerFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.Module, lookups=True) -class ModuleFilter(filtersets.ModuleFilterSet): - id: auto +@autotype_decorator(filtersets.ModuleFilterSet) +class ModuleFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.ModuleBay, lookups=True) -class ModuleBayFilter(filtersets.ModuleBayFilterSet): - id: auto +@autotype_decorator(filtersets.ModuleBayFilterSet) +class ModuleBayFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.ModuleBayTemplate, lookups=True) -class ModuleBayTemplateFilter(filtersets.ModuleBayTemplateFilterSet): - id: auto +@autotype_decorator(filtersets.ModuleBayTemplateFilterSet) +class ModuleBayTemplateFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.ModuleType, lookups=True) -class ModuleTypeFilter(filtersets.ModuleTypeFilterSet): - id: auto +@autotype_decorator(filtersets.ModuleTypeFilterSet) +class ModuleTypeFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.Platform, lookups=True) -class PlatformFilter(filtersets.PlatformFilterSet): - id: auto +@autotype_decorator(filtersets.PlatformFilterSet) +class PlatformFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.PowerFeed, lookups=True) -class PowerFeedFilter(filtersets.PowerFeedFilterSet): - id: auto +@autotype_decorator(filtersets.PowerFeedFilterSet) +class PowerFeedFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.PowerOutlet, lookups=True) -class PowerOutletFilter(filtersets.PowerOutletFilterSet): - id: auto +@autotype_decorator(filtersets.PowerOutletFilterSet) +class PowerOutletFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.PowerOutletTemplate, lookups=True) -class PowerOutletTemplateFilter(filtersets.PowerOutletTemplateFilterSet): - id: auto +@autotype_decorator(filtersets.PowerOutletTemplateFilterSet) +class PowerOutletTemplateFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.PowerPanel, lookups=True) -class PowerPanelFilter(filtersets.PowerPanelFilterSet): - id: auto +@autotype_decorator(filtersets.PowerPanelFilterSet) +class PowerPanelFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.PowerPort, lookups=True) -class PowerPortFilter(filtersets.PowerPortFilterSet): - id: auto +@autotype_decorator(filtersets.PowerPortFilterSet) +class PowerPortFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.PowerPortTemplate, lookups=True) -class PowerPortTemplateFilter(filtersets.PowerPortTemplateFilterSet): - id: auto +@autotype_decorator(filtersets.PowerPortTemplateFilterSet) +class PowerPortTemplateFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.Rack, lookups=True) -class RackFilter(filtersets.RackFilterSet): - id: auto +@autotype_decorator(filtersets.RackFilterSet) +class RackFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.RackReservation, lookups=True) -class RackReservationFilter(filtersets.RackReservationFilterSet): - id: auto +@autotype_decorator(filtersets.RackReservationFilterSet) +class RackReservationFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.RackRole, lookups=True) -class RackRoleFilter(filtersets.RackRoleFilterSet): - id: auto +@autotype_decorator(filtersets.RackRoleFilterSet) +class RackRoleFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.RearPort, lookups=True) -class RearPortFilter(filtersets.RearPortFilterSet): - id: auto +@autotype_decorator(filtersets.RearPortFilterSet) +class RearPortFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.RearPortTemplate, lookups=True) -class RearPortTemplateFilter(filtersets.RearPortTemplateFilterSet): - id: auto +@autotype_decorator(filtersets.RearPortTemplateFilterSet) +class RearPortTemplateFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.Region, lookups=True) -class RegionFilter(filtersets.RegionFilterSet): - id: auto +@autotype_decorator(filtersets.RegionFilterSet) +class RegionFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.Site, lookups=True) -class SiteFilter(filtersets.SiteFilterSet): - id: auto +@autotype_decorator(filtersets.SiteFilterSet) +class SiteFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.SiteGroup, lookups=True) -class SiteGroupFilter(filtersets.SiteGroupFilterSet): - id: auto +@autotype_decorator(filtersets.SiteGroupFilterSet) +class SiteGroupFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.VirtualChassis, lookups=True) -class VirtualChassisFilter(filtersets.VirtualChassisFilterSet): - id: auto +@autotype_decorator(filtersets.VirtualChassisFilterSet) +class VirtualChassisFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.VirtualDeviceContext, lookups=True) -class VirtualDeviceContextFilter(filtersets.VirtualDeviceContextFilterSet): - id: auto +@autotype_decorator(filtersets.VirtualDeviceContextFilterSet) +class VirtualDeviceContextFilter(BaseFilterMixin): + pass diff --git a/netbox/extras/graphql/filters.py b/netbox/extras/graphql/filters.py index 568ec6ded..c20509fa3 100644 --- a/netbox/extras/graphql/filters.py +++ b/netbox/extras/graphql/filters.py @@ -1,7 +1,8 @@ import strawberry import strawberry_django -from strawberry import auto -from extras import models, filtersets +from extras import filtersets, models + +from netbox.graphql.filter_mixins import autotype_decorator, BaseFilterMixin __all__ = ( 'ConfigContextFilter', @@ -21,136 +22,78 @@ __all__ = ( @strawberry_django.filter(models.ConfigContext, lookups=True) -class ConfigContextFilter(filtersets.ConfigContextFilterSet): - id: auto - name: auto - is_active: auto - data_synced: auto +@autotype_decorator(filtersets.ConfigContextFilterSet) +class ConfigContextFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.ConfigTemplate, lookups=True) -class ConfigTemplateFilter(filtersets.ConfigTemplateFilterSet): - id: auto - name: auto - description: auto - data_synced: auto +@autotype_decorator(filtersets.ConfigTemplateFilterSet) +class ConfigTemplateFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.CustomField, lookups=True) -class CustomFieldFilter(filtersets.CustomFieldFilterSet): - id: auto - object_types: auto - name: auto - group_name: auto - required: auto - search_weight: auto - filter_logic: auto - weight: auto - is_cloneable: auto - description: auto +@autotype_decorator(filtersets.CustomFieldFilterSet) +class CustomFieldFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.CustomFieldChoiceSet, lookups=True) -class CustomFieldChoiceSetFilter(filtersets.CustomFieldChoiceSetFilterSet): - id: auto - name: auto - description: auto - base_choices: auto - order_alphabetically: auto +@autotype_decorator(filtersets.CustomFieldChoiceSetFilterSet) +class CustomFieldChoiceSetFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.CustomLink, lookups=True) -class CustomLinkFilter(filtersets.CustomLinkFilterSet): - id: auto - object_types: auto - name: auto - enabled: auto - link_text: auto - link_url: auto - weight: auto - group_name: auto - new_window: auto +@autotype_decorator(filtersets.CustomLinkFilterSet) +class CustomLinkFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.ExportTemplate, lookups=True) -class ExportTemplateFilter(filtersets.ExportTemplateFilterSet): - id: auto - object_types: auto - name: auto - description: auto - data_synced: auto +@autotype_decorator(filtersets.ExportTemplateFilterSet) +class ExportTemplateFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.ImageAttachment, lookups=True) -class ImageAttachmentFilter(filtersets.ImageAttachmentFilterSet): - id: auto - object_type_id: auto - object_id: auto - name: auto +@autotype_decorator(filtersets.ImageAttachmentFilterSet) +class ImageAttachmentFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.JournalEntry, lookups=True) -class JournalEntryFilter(filtersets.JournalEntryFilterSet): - id: auto - assigned_object_type_id: auto - assigned_object_id: auto - created: auto - kind: auto +@autotype_decorator(filtersets.JournalEntryFilterSet) +class JournalEntryFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.ObjectChange, lookups=True) -class ObjectChangeFilter(filtersets.ObjectChangeFilterSet): - id: auto - user: auto - user_name: auto - request_id: auto - action: auto - changed_object_type_id: auto - changed_object_id: auto - object_repr: auto +@autotype_decorator(filtersets.ObjectChangeFilterSet) +class ObjectChangeFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.SavedFilter, lookups=True) -class SavedFilterFilter(filtersets.SavedFilterFilterSet): - id: auto - object_types: auto - name: auto - slug: auto - description: auto - enabled: auto - shared: auto - weight: auto +@autotype_decorator(filtersets.SavedFilterFilterSet) +class SavedFilterFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.Tag, lookups=True) -class TagFilter(filtersets.TagFilterSet): - id: auto - name: auto - slug: auto - # color: auto - description: auto - object_types: auto +@autotype_decorator(filtersets.TagFilterSet) +class TagFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.Webhook, lookups=True) -class WebhookFilter(filtersets.WebhookFilterSet): - id: auto - name: auto - payload_url: auto - http_method: auto - http_content_type: auto - secret: auto - ssl_verification: auto - ca_file_path: auto +@autotype_decorator(filtersets.WebhookFilterSet) +class WebhookFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.EventRule, lookups=True) -class EventRuleFilter(filtersets.EventRuleFilterSet): - id: auto - name: auto - enabled: auto - type_create: auto - type_update: auto - type_delete: auto - type_job_start: auto - type_job_end: auto +@autotype_decorator(filtersets.EventRuleFilterSet) +class EventRuleFilter(BaseFilterMixin): + pass diff --git a/netbox/ipam/graphql/filters.py b/netbox/ipam/graphql/filters.py index 08a9adb93..80d8c8fec 100644 --- a/netbox/ipam/graphql/filters.py +++ b/netbox/ipam/graphql/filters.py @@ -1,7 +1,8 @@ import strawberry import strawberry_django -from strawberry import auto -from ipam import models, filtersets +from ipam import filtersets, models + +from netbox.graphql.filter_mixins import autotype_decorator, BaseFilterMixin __all__ = ( @@ -25,80 +26,94 @@ __all__ = ( @strawberry_django.filter(models.ASN, lookups=True) -class ASNFilter(filtersets.ASNFilterSet): - id: auto +class ASNFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.ASNRange, lookups=True) -class ASNRangeFilter(filtersets.ASNRangeFilterSet): - id: auto +class ASNRangeFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.Aggregate, lookups=True) -class AggregateFilter(filtersets.AggregateFilterSet): - id: auto +@autotype_decorator(filtersets.AggregateFilterSet) +class AggregateFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.FHRPGroup, lookups=True) -class FHRPGroupFilter(filtersets.FHRPGroupFilterSet): - id: auto +@autotype_decorator(filtersets.FHRPGroupFilterSet) +class FHRPGroupFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.FHRPGroupAssignment, lookups=True) -class FHRPGroupAssignmentFilter(filtersets.FHRPGroupAssignmentFilterSet): - id: auto +@autotype_decorator(filtersets.FHRPGroupAssignmentFilterSet) +class FHRPGroupAssignmentFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.IPAddress, lookups=True) -class IPAddressFilter(filtersets.IPAddressFilterSet): - id: auto +@autotype_decorator(filtersets.IPAddressFilterSet) +class IPAddressFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.IPRange, lookups=True) -class IPRangeFilter(filtersets.IPRangeFilterSet): - id: auto +@autotype_decorator(filtersets.IPRangeFilterSet) +class IPRangeFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.Prefix, lookups=True) -class PrefixFilter(filtersets.PrefixFilterSet): - id: auto +@autotype_decorator(filtersets.PrefixFilterSet) +class PrefixFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.RIR, lookups=True) -class RIRFilter(filtersets.RIRFilterSet): - id: auto +@autotype_decorator(filtersets.RIRFilterSet) +class RIRFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.Role, lookups=True) -class RoleFilter(filtersets.RoleFilterSet): - id: auto +@autotype_decorator(filtersets.RoleFilterSet) +class RoleFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.RouteTarget, lookups=True) -class RouteTargetFilter(filtersets.RouteTargetFilterSet): - id: auto +@autotype_decorator(filtersets.RouteTargetFilterSet) +class RouteTargetFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.Service, lookups=True) -class ServiceFilter(filtersets.ServiceFilterSet): - id: auto +@autotype_decorator(filtersets.ServiceFilterSet) +class ServiceFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.ServiceTemplate, lookups=True) -class ServiceTemplateFilter(filtersets.ServiceTemplateFilterSet): - id: auto +@autotype_decorator(filtersets.ServiceTemplateFilterSet) +class ServiceTemplateFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.VLAN, lookups=True) -class VLANFilter(filtersets.VLANFilterSet): - id: auto +@autotype_decorator(filtersets.VLANFilterSet) +class VLANFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.VLANGroup, lookups=True) -class VLANGroupFilter(filtersets.VLANGroupFilterSet): - id: auto +@autotype_decorator(filtersets.VLANGroupFilterSet) +class VLANGroupFilter(BaseFilterMixin): + pass @strawberry_django.filter(models.VRF, lookups=True) -class VRFFilter(filtersets.VRFFilterSet): - id: auto +@autotype_decorator(filtersets.VRFFilterSet) +class VRFFilter(BaseFilterMixin): + pass diff --git a/netbox/netbox/graphql/filter_mixins.py b/netbox/netbox/graphql/filter_mixins.py index af2f3e049..aba4ff791 100644 --- a/netbox/netbox/graphql/filter_mixins.py +++ b/netbox/netbox/graphql/filter_mixins.py @@ -1,12 +1,180 @@ +from functools import partial, partialmethod, wraps from typing import List + +import django_filters import strawberry import strawberry_django from strawberry import auto +from utilities.fields import ColorField +from utilities.filters import * + + +def autotype_decorator(filterset): + + def wrapper(cls): + print(f"cls: {cls}") + cls.filterset = filterset + fields = filterset.get_fields() + model = filterset._meta.model + for fieldname in fields.keys(): + attr_type = auto + if fieldname not in cls.__annotations__: + field = model._meta.get_field(fieldname) + if isinstance(field, ColorField): + attr_type = List[str] | None + + cls.__annotations__[fieldname] = attr_type + + declared_filters = filterset.declared_filters + for fieldname, v in declared_filters.items(): + create_function = False + attr_type = None + + # NetBox Filter types - put base classes after derived classes + if isinstance(v, ContentTypeFilter): + create_function = True + attr_type = str | None + elif isinstance(v, MACAddressFilter): + print(f"{fieldname}: {v}") + print("MACAddressFilter") + elif isinstance(v, MultiValueArrayFilter): + print(f"{fieldname}: {v}") + print("MultiValueArrayFilter") + elif isinstance(v, MultiValueCharFilter): + create_function = True + attr_type = List[str] | None + elif isinstance(v, MultiValueDateFilter): + attr_type = auto + elif isinstance(v, MultiValueDateTimeFilter): + attr_type = auto + elif isinstance(v, MultiValueDecimalFilter): + print(f"{fieldname}: {v}") + print("MultiValueDecimalFilter") + elif isinstance(v, MultiValueMACAddressFilter): + create_function = True + attr_type = List[str] | None + elif isinstance(v, MultiValueNumberFilter): + create_function = True + attr_type = List[str] | None + elif isinstance(v, MultiValueTimeFilter): + print(f"{fieldname}: {v}") + print("MultiValueTimeFilter") + elif isinstance(v, MultiValueWWNFilter): + create_function = True + attr_type = List[str] | None + elif isinstance(v, NullableCharFieldFilter): + print(f"{fieldname}: {v}") + print("NullableCharFieldFilter") + elif isinstance(v, NumericArrayFilter): + print(f"{fieldname}: {v}") + print("NumericArrayFilter") + elif isinstance(v, TreeNodeMultipleChoiceFilter): + 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 + elif issubclass(type(v), django_filters.OrderingFilter): + print(f"{fieldname}: {v}") + print("OrderingFilter") + elif issubclass(type(v), django_filters.BaseRangeFilter): + print(f"{fieldname}: {v}") + print("BaseRangeFilter") + elif issubclass(type(v), django_filters.BaseInFilter): + print(f"{fieldname}: {v}") + print("BaseInFilter") + elif issubclass(type(v), django_filters.LookupChoiceFilter): + print(f"{fieldname}: {v}") + print("LookupChoiceFilter") + elif issubclass(type(v), django_filters.AllValuesMultipleFilter): + print(f"{fieldname}: {v}") + print("AllValuesMultipleFilter") + elif issubclass(type(v), django_filters.AllValuesFilter): + print(f"{fieldname}: {v}") + print("AllValuesFilter") + elif issubclass(type(v), django_filters.TimeRangeFilter): + print(f"{fieldname}: {v}") + print("TimeRangeFilter") + elif issubclass(type(v), django_filters.IsoDateTimeFromToRangeFilter): + create_function = True + attr_type = str | None + elif issubclass(type(v), django_filters.DateTimeFromToRangeFilter): + create_function = True + attr_type = str | None + elif issubclass(type(v), django_filters.DateFromToRangeFilter): + create_function = True + attr_type = str | None + elif issubclass(type(v), django_filters.DateRangeFilter): + create_function = True + attr_type = str | None + elif issubclass(type(v), django_filters.RangeFilter): + print(f"{fieldname}: {v}") + print("RangeFilter") + elif issubclass(type(v), django_filters.NumericRangeFilter): + print(f"{fieldname}: {v}") + print("NumericRangeFilter") + elif issubclass(type(v), django_filters.NumberFilter): + print(f"{fieldname}: {v}") + print("NumberFilter") + elif issubclass(type(v), django_filters.ModelMultipleChoiceFilter): + create_function = True + attr_type = List[str] | None + elif issubclass(type(v), django_filters.ModelChoiceFilter): + create_function = True + attr_type = str | None + elif issubclass(type(v), django_filters.DurationFilter): + print(f"{fieldname}: {v}") + print("DurationFilter") + elif issubclass(type(v), django_filters.IsoDateTimeFilter): + print(f"{fieldname}: {v}") + print("IsoDateTimeFilter") + elif issubclass(type(v), django_filters.DateTimeFilter): + attr_type = auto + elif issubclass(type(v), django_filters.TimeFilter): + attr_type = auto + elif issubclass(type(v), django_filters.DateFilter): + attr_type = auto + elif issubclass(type(v), django_filters.TypedMultipleChoiceFilter): + print(f"{fieldname}: {v}") + print("TypedMultipleChoiceFilter") + elif issubclass(type(v), django_filters.MultipleChoiceFilter): + create_function = True + attr_type = List[str] | None + elif issubclass(type(v), django_filters.TypedChoiceFilter): + print(f"{fieldname}: {v}") + print("TypedChoiceFilter") + elif issubclass(type(v), django_filters.ChoiceFilter): + print(f"{fieldname}: {v}") + print("ChoiceFilter") + elif issubclass(type(v), django_filters.BooleanFilter): + create_function = True + attr_type = bool | None + elif issubclass(type(v), django_filters.UUIDFilter): + create_function = True + attr_type = str | None + elif issubclass(type(v), django_filters.CharFilter): + # looks like only used by 'q' + create_function = True + attr_type = str | None + else: + print(f"{fieldname}: {v}") + print("unknown type!") + + if fieldname not in cls.__annotations__ and attr_type: + cls.__annotations__[fieldname] = attr_type + + fname = f"filter_{fieldname}" + if create_function and not hasattr(cls, fname): + filter_by_filterset = getattr(cls, 'filter_by_filterset') + setattr(cls, fname, partialmethod(filter_by_filterset, key=fieldname)) + + return cls + + return wrapper @strawberry.input class BaseFilterMixin: - id: auto def filter_by_filterset(self, queryset, key): return self.filterset(data={key: getattr(self, key)}, queryset=queryset).qs