mirror of
https://github.com/netbox-community/netbox.git
synced 2026-01-10 13:52:17 -06:00
Compare commits
6 Commits
b0d7bb3d18
...
v4.5-beta1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6211a21423 | ||
|
|
cc935dbfab | ||
|
|
3483d979d4 | ||
|
|
ca43adf692 | ||
|
|
7eefb07554 | ||
|
|
20c260b126 |
@@ -6,12 +6,17 @@ Filter sets define the mechanisms available for filtering or searching through a
|
||||
|
||||
To support additional functionality standard to NetBox models, such as tag assignment and custom field support, the `NetBoxModelFilterSet` class is available for use by plugins. This should be used as the base filter set class for plugin models which inherit from `NetBoxModel`. Within this class, individual filters can be declared as directed by the `django-filters` documentation. An example is provided below.
|
||||
|
||||
!!! info "New in NetBox v4.5: FilterSet Registration"
|
||||
NetBox v4.5 introduced the `register_filterset()` utility function. This enables plugins to register their filtersets to receive advanced functionality, such as the automatic attachment of field-specific lookup modifiers on the filter form. Registration is optional: Unregistered filtersets will continue to work as before, but will not receive the enhanced functionality.
|
||||
|
||||
```python
|
||||
# filtersets.py
|
||||
import django_filters
|
||||
from netbox.filtersets import NetBoxModelFilterSet
|
||||
from utilities.filtersets import register_filterset
|
||||
from .models import MyModel
|
||||
|
||||
@register_filterset
|
||||
class MyFilterSet(NetBoxModelFilterSet):
|
||||
status = django_filters.MultipleChoiceFilter(
|
||||
choices=(
|
||||
@@ -42,7 +47,7 @@ class MyModelListView(ObjectListView):
|
||||
filterset = MyModelFilterSet
|
||||
```
|
||||
|
||||
To enable a filter set on a REST API endpoint, set the `filterset_class` attribute on the API view:
|
||||
To enable a filter set on a REST API endpoint, set the `filterset_class` attribute on the API view:
|
||||
|
||||
```python
|
||||
# api/views.py
|
||||
@@ -62,7 +67,9 @@ The `ObjectListView` has a field called Quick Search. For Quick Search to work t
|
||||
```python
|
||||
from django.db.models import Q
|
||||
from netbox.filtersets import NetBoxModelFilterSet
|
||||
from utilities.filtersets import register_filterset
|
||||
|
||||
@register_filterset
|
||||
class MyFilterSet(NetBoxModelFilterSet):
|
||||
...
|
||||
def search(self, queryset, name, value):
|
||||
@@ -90,7 +97,9 @@ This class filters `tags` using the `slug` field. For example:
|
||||
```python
|
||||
from django_filters import FilterSet
|
||||
from extras.filters import TagFilter
|
||||
from utilities.filtersets import register_filterset
|
||||
|
||||
@register_filterset
|
||||
class MyModelFilterSet(FilterSet):
|
||||
tag = TagFilter()
|
||||
```
|
||||
@@ -106,7 +115,9 @@ This class filters `tags` using the `id` field. For example:
|
||||
```python
|
||||
from django_filters import FilterSet
|
||||
from extras.filters import TagIDFilter
|
||||
from utilities.filtersets import register_filterset
|
||||
|
||||
@register_filterset
|
||||
class MyModelFilterSet(FilterSet):
|
||||
tag_id = TagIDFilter()
|
||||
```
|
||||
|
||||
@@ -325,14 +325,14 @@ class CircuitTypeType(OrganizationalObjectType):
|
||||
|
||||
### Change filters.py
|
||||
|
||||
Strawberry currently doesn't directly support django-filter, so an explicit filters.py file will need to be created. NetBox includes a new `autotype_decorator` used to automatically wrap FilterSets to reduce the required code to a minimum.
|
||||
Filter classes should inherit from `netbox.graphql.filters.BaseModelFilter`.
|
||||
|
||||
```python title="New"
|
||||
import strawberry
|
||||
import strawberry_django
|
||||
from circuits import filtersets, models
|
||||
|
||||
from netbox.graphql.filter_mixins import autotype_decorator, BaseFilterMixin
|
||||
from netbox.graphql.filters import BaseModelFilter
|
||||
|
||||
__all__ = (
|
||||
'CircuitFilter',
|
||||
@@ -340,8 +340,7 @@ __all__ = (
|
||||
|
||||
|
||||
@strawberry_django.filter(models.Circuit, lookups=True)
|
||||
@autotype_decorator(filtersets.CircuitFilterSet)
|
||||
class CircuitFilter(BaseFilterMixin):
|
||||
class CircuitFilter(BaseModelFilter):
|
||||
pass
|
||||
|
||||
```
|
||||
|
||||
111
docs/release-notes/version-4.5.md
Normal file
111
docs/release-notes/version-4.5.md
Normal file
@@ -0,0 +1,111 @@
|
||||
## v4.5.0 (FUTURE)
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* Python 3.10 and 3.11 are no longer supported. NetBox now requires Python 3.12 or later.
|
||||
* GraphQL API queries which filter by object IDs or enums must now specify a filter lookup similar to other fields. (For example, `id: 123` becomes `id: {exact: 123 }`.)
|
||||
* Rendering a device or virtual machine configuration is now restricted to users with the `render_config` permission for the applicable object type.
|
||||
* Retrieval of API token plaintexts is no longer supported. The `ALLOW_TOKEN_RETRIEVAL` config parameter has been removed.
|
||||
* The owner of an API token can no longer be changed once it has been created.
|
||||
* Config contexts now apply to all child platforms of a parent platform.
|
||||
* The `/api/extras/object-types/` REST API endpoint has been removed. (Use `/api/core/object-types/` instead.)
|
||||
* The `/api/dcim/cable-terminations/` REST API endpoint is now read-only. Cable terminations must be set on cables directly.
|
||||
* The UI view dedicated to swaping A/Z circuit terminations has been removed.
|
||||
* Webhooks no longer specify a `model` in payload data. (Reference `object_type` instead, which includes the parent app label.)
|
||||
* The obsolete module `core.models.contenttypes` has been removed (replaced in v4.4 by `core.models.object_types`).
|
||||
* The `load_yaml()` and `load_json()` utility methods have been removed from the base class for custom scripts.
|
||||
* The experimental HTMX navigation feature has been removed.
|
||||
* The obsolete field `is_staff` has been removed from the `User` model.
|
||||
|
||||
### New Features
|
||||
|
||||
#### Lookup Modifiers in Filter Forms ([#7604](https://github.com/netbox-community/netbox/issues/7604))
|
||||
|
||||
#### Improved API Authentication Tokens ([#20210](https://github.com/netbox-community/netbox/issues/20210))
|
||||
|
||||
#### Object Ownership ([#20304](https://github.com/netbox-community/netbox/issues/20304))
|
||||
|
||||
#### Advanced Port Mappings ([#20564](https://github.com/netbox-community/netbox/issues/20564))
|
||||
|
||||
#### Cable Profiles ([#20788](https://github.com/netbox-community/netbox/issues/20788))
|
||||
|
||||
### Enhancements
|
||||
|
||||
* [#16681](https://github.com/netbox-community/netbox/issues/16681) - Introduce a `render_config` permission, which is noq required to render a device or virtual machine configuration
|
||||
* [#18658](https://github.com/netbox-community/netbox/issues/18658) - Add a `start_on_boot` choice field for virtual machines
|
||||
* [#19095](https://github.com/netbox-community/netbox/issues/19095) - Add support for Python 3.13 and 3.14
|
||||
* [#19338](https://github.com/netbox-community/netbox/issues/19338) - Enable filter lookups for object IDs and enums in GraphQL API queries
|
||||
* [#19523](https://github.com/netbox-community/netbox/issues/19523) - Cache the number of instances for device, module, and rack types, and enable filtering by these counts
|
||||
* [#20417](https://github.com/netbox-community/netbox/issues/20417) - Add an optional `color` field for device type power outlets
|
||||
* [#20476](https://github.com/netbox-community/netbox/issues/20476) - Once provisioned, the owner of an API token cannot be changed
|
||||
* [#20492](https://github.com/netbox-community/netbox/issues/20492) - Completely disabled the means to retrieve legacy API token plaintexts (removed the `ALLOW_TOKEN_RETRIEVAL` config parameter)
|
||||
* [#20639](https://github.com/netbox-community/netbox/issues/20639) - Apply config contexts to devices/VMs assigned any child platform of the parent platform
|
||||
* [#20834](https://github.com/netbox-community/netbox/issues/20834) - Add an `enabled` boolean field to API tokens
|
||||
* [#20917](https://github.com/netbox-community/netbox/issues/20917) - Include usage reference on API token views
|
||||
* [#20925](https://github.com/netbox-community/netbox/issues/20925) - Add optional `comments` field to all subclasses of `OrganizationalModel`
|
||||
* [#20936](https://github.com/netbox-community/netbox/issues/20936) - Introduce the `/api/authentication-check/` REST API endpoint for validating authentication tokens
|
||||
|
||||
### Plugins
|
||||
|
||||
* [#13182](https://github.com/netbox-community/netbox/issues/13182) - Added `PrimaryModel`, `OrganizationalModel`, and `NestedGroupModel` to the plugins API, as well as their respective base classes for various resources
|
||||
|
||||
### Other Changes
|
||||
|
||||
* [#16137](https://github.com/netbox-community/netbox/issues/16137) - Remove the obsolete boolean field `is_staff` from the `User` model
|
||||
* [#17571](https://github.com/netbox-community/netbox/issues/17571) - Remove the experimental HTMX navigation feature
|
||||
* [#17936](https://github.com/netbox-community/netbox/issues/17936) - Introduce a dedicated `GFKSerializerField` for representing generic foreign keys in API serializers
|
||||
* [#19889](https://github.com/netbox-community/netbox/issues/19889) - Drop support for Python 3.10 and 3.11
|
||||
* [#19898](https://github.com/netbox-community/netbox/issues/19898) - Remove the obsolete REST API endpoint `/api/extras/object-types/`
|
||||
* [#20088](https://github.com/netbox-community/netbox/issues/20088) - Remove the non-deterministic `model` key from webhook payload data
|
||||
* [#20095](https://github.com/netbox-community/netbox/issues/20095) - Remove the obsolete module `core.models.contenttypes`
|
||||
* [#20096](https://github.com/netbox-community/netbox/issues/20096) - Remove the `load_yaml()` and `load_json()` utility methods from the `BaseScript` class
|
||||
* [#20204](https://github.com/netbox-community/netbox/issues/20204) - Started migrating object views from custom HTML templates to declarative layouts
|
||||
* [#20617](https://github.com/netbox-community/netbox/issues/20617) - Introduce `BaseModel` as the global base class for models
|
||||
* [#20683](https://github.com/netbox-community/netbox/issues/20683) - Remove the UI view dedicated to swaping A/Z circuit terminations
|
||||
* [#20926](https://github.com/netbox-community/netbox/issues/20926) - Standardize naming of GraphQL filters
|
||||
|
||||
### REST API Changes
|
||||
|
||||
* Most objects now include an optional `owner` foreign key field.
|
||||
* The `/api/dcim/cable-terminations` endpoint is now read-only.
|
||||
* Introduced the `/api/authentication-check/` endpoint.
|
||||
* `circuits.CircuitGroup`
|
||||
* Add optional `comments` field
|
||||
* `circuits.CircuitType`
|
||||
* Add optional `comments` field
|
||||
* `circuits.VirtualCircuitType`
|
||||
* Add optional `comments` field
|
||||
* `dcim.Cable`
|
||||
* Add the optional `profile` choice field
|
||||
* `dcim.InventoryItemRole`
|
||||
* Add optional `comments` field
|
||||
* `dcim.Manufacturer`
|
||||
* Add optional `comments` field
|
||||
* `dcim.ModuleType`
|
||||
* Add read-only `module_count` integer field
|
||||
* `dcim.PowerOutletTemplate`
|
||||
* Add optional `color` field
|
||||
* `dcim.RackRole`
|
||||
* Add optional `comments` field
|
||||
* `dcim.RackType`
|
||||
* Add read-only `rack_count` integer field
|
||||
* `ipam.ASNRange`
|
||||
* Add optional `comments` field
|
||||
* `ipam.RIR`
|
||||
* Add optional `comments` field
|
||||
* `ipam.Role`
|
||||
* Add optional `comments` field
|
||||
* `ipam.VLANGroup`
|
||||
* Add optional `comments` field
|
||||
* `tenancy.ContactRole`
|
||||
* Add optional `comments` field
|
||||
* `users.Token`
|
||||
* Add `enabled` boolean field
|
||||
* `virtualization.ClusterGroup`
|
||||
* Add optional `comments` field
|
||||
* `virtualization.ClusterType`
|
||||
* Add optional `comments` field
|
||||
* `virtualization.VirtualMachine`
|
||||
* Add optional `start_on_boot` choice field
|
||||
* `vpn.TunnelGroup`
|
||||
* Add optional `comments` field
|
||||
@@ -3,17 +3,18 @@ from typing import Annotated, TYPE_CHECKING
|
||||
|
||||
import strawberry
|
||||
import strawberry_django
|
||||
|
||||
from netbox.graphql.filter_mixins import OrganizationalModelFilterMixin
|
||||
from strawberry_django import BaseFilterLookup
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from netbox.graphql.enums import ColorEnum
|
||||
|
||||
__all__ = (
|
||||
'BaseCircuitTypeFilterMixin',
|
||||
'CircuitTypeFilterMixin',
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class BaseCircuitTypeFilterMixin(OrganizationalModelFilterMixin):
|
||||
color: Annotated['ColorEnum', strawberry.lazy('netbox.graphql.enums')] | None = strawberry_django.filter_field()
|
||||
class CircuitTypeFilterMixin:
|
||||
color: BaseFilterLookup[Annotated['ColorEnum', strawberry.lazy('netbox.graphql.enums')]] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
|
||||
@@ -7,17 +7,12 @@ from strawberry.scalars import ID
|
||||
from strawberry_django import BaseFilterLookup, FilterLookup, DateFilterLookup
|
||||
|
||||
from circuits import models
|
||||
from core.graphql.filter_mixins import BaseObjectTypeFilterMixin, ChangeLogFilterMixin
|
||||
from circuits.graphql.filter_mixins import CircuitTypeFilterMixin
|
||||
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 netbox.graphql.filter_mixins import DistanceFilterMixin, ImageAttachmentFilterMixin
|
||||
from netbox.graphql.filters import ChangeLoggedModelFilter, OrganizationalModelFilter, PrimaryModelFilter
|
||||
from tenancy.graphql.filter_mixins import ContactFilterMixin, TenancyFilterMixin
|
||||
from .filter_mixins import BaseCircuitTypeFilterMixin
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.graphql.filters import ContentTypeFilter
|
||||
@@ -43,10 +38,9 @@ __all__ = (
|
||||
|
||||
@strawberry_django.filter_type(models.CircuitTermination, lookups=True)
|
||||
class CircuitTerminationFilter(
|
||||
BaseObjectTypeFilterMixin,
|
||||
CustomFieldsFilterMixin,
|
||||
TagsFilterMixin,
|
||||
ChangeLogFilterMixin,
|
||||
ChangeLoggedModelFilter,
|
||||
CabledObjectModelFilterMixin,
|
||||
):
|
||||
circuit: Annotated['CircuitFilter', strawberry.lazy('circuits.graphql.filters')] | None = (
|
||||
@@ -95,7 +89,7 @@ class CircuitFilter(
|
||||
ImageAttachmentFilterMixin,
|
||||
DistanceFilterMixin,
|
||||
TenancyFilterMixin,
|
||||
PrimaryModelFilterMixin
|
||||
PrimaryModelFilter
|
||||
):
|
||||
cid: FilterLookup[str] | None = strawberry_django.filter_field()
|
||||
provider: Annotated['ProviderFilter', strawberry.lazy('circuits.graphql.filters')] | None = (
|
||||
@@ -124,19 +118,17 @@ class CircuitFilter(
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.CircuitType, lookups=True)
|
||||
class CircuitTypeFilter(BaseCircuitTypeFilterMixin):
|
||||
class CircuitTypeFilter(CircuitTypeFilterMixin, OrganizationalModelFilter):
|
||||
pass
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.CircuitGroup, lookups=True)
|
||||
class CircuitGroupFilter(TenancyFilterMixin, OrganizationalModelFilterMixin):
|
||||
class CircuitGroupFilter(TenancyFilterMixin, OrganizationalModelFilter):
|
||||
pass
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.CircuitGroupAssignment, lookups=True)
|
||||
class CircuitGroupAssignmentFilter(
|
||||
BaseObjectTypeFilterMixin, CustomFieldsFilterMixin, TagsFilterMixin, ChangeLogFilterMixin
|
||||
):
|
||||
class CircuitGroupAssignmentFilter(CustomFieldsFilterMixin, TagsFilterMixin, ChangeLoggedModelFilter):
|
||||
member_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
@@ -151,7 +143,7 @@ class CircuitGroupAssignmentFilter(
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.Provider, lookups=True)
|
||||
class ProviderFilter(ContactFilterMixin, PrimaryModelFilterMixin):
|
||||
class ProviderFilter(ContactFilterMixin, PrimaryModelFilter):
|
||||
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()
|
||||
@@ -161,7 +153,7 @@ class ProviderFilter(ContactFilterMixin, PrimaryModelFilterMixin):
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.ProviderAccount, lookups=True)
|
||||
class ProviderAccountFilter(ContactFilterMixin, PrimaryModelFilterMixin):
|
||||
class ProviderAccountFilter(ContactFilterMixin, PrimaryModelFilter):
|
||||
provider: Annotated['ProviderFilter', strawberry.lazy('circuits.graphql.filters')] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
@@ -171,7 +163,7 @@ class ProviderAccountFilter(ContactFilterMixin, PrimaryModelFilterMixin):
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.ProviderNetwork, lookups=True)
|
||||
class ProviderNetworkFilter(PrimaryModelFilterMixin):
|
||||
class ProviderNetworkFilter(PrimaryModelFilter):
|
||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
||||
provider: Annotated['ProviderFilter', strawberry.lazy('circuits.graphql.filters')] | None = (
|
||||
strawberry_django.filter_field()
|
||||
@@ -181,12 +173,12 @@ class ProviderNetworkFilter(PrimaryModelFilterMixin):
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.VirtualCircuitType, lookups=True)
|
||||
class VirtualCircuitTypeFilter(BaseCircuitTypeFilterMixin):
|
||||
class VirtualCircuitTypeFilter(CircuitTypeFilterMixin, OrganizationalModelFilter):
|
||||
pass
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.VirtualCircuit, lookups=True)
|
||||
class VirtualCircuitFilter(TenancyFilterMixin, PrimaryModelFilterMixin):
|
||||
class VirtualCircuitFilter(TenancyFilterMixin, PrimaryModelFilter):
|
||||
cid: FilterLookup[str] | None = strawberry_django.filter_field()
|
||||
provider_network: Annotated['ProviderNetworkFilter', strawberry.lazy('circuits.graphql.filters')] | None = (
|
||||
strawberry_django.filter_field()
|
||||
@@ -209,9 +201,7 @@ class VirtualCircuitFilter(TenancyFilterMixin, PrimaryModelFilterMixin):
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.VirtualCircuitTermination, lookups=True)
|
||||
class VirtualCircuitTerminationFilter(
|
||||
BaseObjectTypeFilterMixin, CustomFieldsFilterMixin, TagsFilterMixin, ChangeLogFilterMixin
|
||||
):
|
||||
class VirtualCircuitTerminationFilter(CustomFieldsFilterMixin, TagsFilterMixin, ChangeLoggedModelFilter):
|
||||
virtual_circuit: Annotated['VirtualCircuitFilter', strawberry.lazy('circuits.graphql.filters')] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
|
||||
@@ -4,31 +4,18 @@ from typing import Annotated, TYPE_CHECKING
|
||||
|
||||
import strawberry
|
||||
import strawberry_django
|
||||
from strawberry import ID
|
||||
from strawberry_django import FilterLookup, DatetimeFilterLookup
|
||||
from strawberry_django import DatetimeFilterLookup
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .filters import *
|
||||
|
||||
__all__ = (
|
||||
'BaseFilterMixin',
|
||||
'BaseObjectTypeFilterMixin',
|
||||
'ChangeLogFilterMixin',
|
||||
'ChangeLoggingMixin',
|
||||
)
|
||||
|
||||
|
||||
# @strawberry.input
|
||||
class BaseFilterMixin: ...
|
||||
|
||||
|
||||
@dataclass
|
||||
class BaseObjectTypeFilterMixin(BaseFilterMixin):
|
||||
id: FilterLookup[ID] | None = strawberry_django.filter_field()
|
||||
|
||||
|
||||
@dataclass
|
||||
class ChangeLogFilterMixin(BaseFilterMixin):
|
||||
id: FilterLookup[ID] | None = strawberry_django.filter_field()
|
||||
class ChangeLoggingMixin:
|
||||
# TODO: "changelog" is not a valid field name; needs to be updated for ObjectChange
|
||||
changelog: Annotated['ObjectChangeFilter', strawberry.lazy('core.graphql.filters')] | None = (
|
||||
strawberry_django.filter_field()
|
||||
|
||||
@@ -8,8 +8,7 @@ from strawberry.scalars import ID
|
||||
from strawberry_django import BaseFilterLookup, DatetimeFilterLookup, FilterLookup
|
||||
|
||||
from core import models
|
||||
from core.graphql.filter_mixins import BaseFilterMixin
|
||||
from netbox.graphql.filter_mixins import PrimaryModelFilterMixin
|
||||
from netbox.graphql.filters import BaseModelFilter, PrimaryModelFilter
|
||||
from .enums import *
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -25,8 +24,7 @@ __all__ = (
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.DataFile, lookups=True)
|
||||
class DataFileFilter(BaseFilterMixin):
|
||||
id: FilterLookup[ID] | None = strawberry_django.filter_field()
|
||||
class DataFileFilter(BaseModelFilter):
|
||||
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 = (
|
||||
@@ -41,7 +39,7 @@ class DataFileFilter(BaseFilterMixin):
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.DataSource, lookups=True)
|
||||
class DataSourceFilter(PrimaryModelFilterMixin):
|
||||
class DataSourceFilter(PrimaryModelFilter):
|
||||
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()
|
||||
@@ -60,8 +58,7 @@ class DataSourceFilter(PrimaryModelFilterMixin):
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.ObjectChange, lookups=True)
|
||||
class ObjectChangeFilter(BaseFilterMixin):
|
||||
id: FilterLookup[ID] | None = strawberry_django.filter_field()
|
||||
class ObjectChangeFilter(BaseModelFilter):
|
||||
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()
|
||||
@@ -88,7 +85,6 @@ class ObjectChangeFilter(BaseFilterMixin):
|
||||
|
||||
|
||||
@strawberry_django.filter_type(DjangoContentType, lookups=True)
|
||||
class ContentTypeFilter(BaseFilterMixin):
|
||||
id: FilterLookup[ID] | None = strawberry_django.filter_field()
|
||||
class ContentTypeFilter(BaseModelFilter):
|
||||
app_label: FilterLookup[str] | None = strawberry_django.filter_field()
|
||||
model: FilterLookup[str] | None = strawberry_django.filter_field()
|
||||
|
||||
@@ -6,9 +6,7 @@ import strawberry_django
|
||||
from strawberry import ID
|
||||
from strawberry_django import BaseFilterLookup, FilterLookup
|
||||
|
||||
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:
|
||||
@@ -22,16 +20,16 @@ __all__ = (
|
||||
'ComponentModelFilterMixin',
|
||||
'ComponentTemplateFilterMixin',
|
||||
'InterfaceBaseFilterMixin',
|
||||
'ModularComponentModelFilterMixin',
|
||||
'ModularComponentFilterMixin',
|
||||
'ModularComponentTemplateFilterMixin',
|
||||
'RackBaseFilterMixin',
|
||||
'RackFilterMixin',
|
||||
'RenderConfigFilterMixin',
|
||||
'ScopedFilterMixin',
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class ScopedFilterMixin(BaseFilterMixin):
|
||||
class ScopedFilterMixin:
|
||||
scope_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
@@ -39,7 +37,7 @@ class ScopedFilterMixin(BaseFilterMixin):
|
||||
|
||||
|
||||
@dataclass
|
||||
class ComponentModelFilterMixin(NetBoxModelFilterMixin):
|
||||
class ComponentModelFilterMixin:
|
||||
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()
|
||||
@@ -48,7 +46,7 @@ class ComponentModelFilterMixin(NetBoxModelFilterMixin):
|
||||
|
||||
|
||||
@dataclass
|
||||
class ModularComponentModelFilterMixin(ComponentModelFilterMixin):
|
||||
class ModularComponentFilterMixin(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 = (
|
||||
@@ -57,7 +55,7 @@ class ModularComponentModelFilterMixin(ComponentModelFilterMixin):
|
||||
|
||||
|
||||
@dataclass
|
||||
class CabledObjectModelFilterMixin(BaseFilterMixin):
|
||||
class CabledObjectModelFilterMixin:
|
||||
cable: Annotated['CableFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field()
|
||||
cable_id: ID | None = strawberry_django.filter_field()
|
||||
cable_end: (
|
||||
@@ -67,7 +65,7 @@ class CabledObjectModelFilterMixin(BaseFilterMixin):
|
||||
|
||||
|
||||
@dataclass
|
||||
class ComponentTemplateFilterMixin(ChangeLogFilterMixin):
|
||||
class ComponentTemplateFilterMixin:
|
||||
device_type: Annotated['DeviceTypeFilter', strawberry.lazy('dcim.graphql.filters')] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
@@ -85,7 +83,7 @@ class ModularComponentTemplateFilterMixin(ComponentTemplateFilterMixin):
|
||||
|
||||
|
||||
@dataclass
|
||||
class RenderConfigFilterMixin(BaseFilterMixin):
|
||||
class RenderConfigFilterMixin:
|
||||
config_template: Annotated['ConfigTemplateFilter', strawberry.lazy('extras.graphql.filters')] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
@@ -93,7 +91,7 @@ class RenderConfigFilterMixin(BaseFilterMixin):
|
||||
|
||||
|
||||
@dataclass
|
||||
class InterfaceBaseFilterMixin(BaseFilterMixin):
|
||||
class InterfaceBaseFilterMixin:
|
||||
enabled: FilterLookup[bool] | None = strawberry_django.filter_field()
|
||||
mtu: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
||||
strawberry_django.filter_field()
|
||||
@@ -124,7 +122,7 @@ class InterfaceBaseFilterMixin(BaseFilterMixin):
|
||||
|
||||
|
||||
@dataclass
|
||||
class RackBaseFilterMixin(WeightFilterMixin, PrimaryModelFilterMixin):
|
||||
class RackFilterMixin:
|
||||
width: BaseFilterLookup[Annotated['RackWidthEnum', strawberry.lazy('dcim.graphql.enums')]] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
|
||||
@@ -6,29 +6,24 @@ import strawberry_django
|
||||
from strawberry.scalars import ID
|
||||
from strawberry_django import BaseFilterLookup, ComparisonFilterLookup, FilterLookup
|
||||
|
||||
from core.graphql.filter_mixins import ChangeLogFilterMixin
|
||||
from dcim import models
|
||||
from dcim.constants import *
|
||||
from dcim.graphql.enums import InterfaceKindEnum
|
||||
from dcim.graphql.filter_mixins import (
|
||||
ComponentModelFilterMixin, ComponentTemplateFilterMixin, ModularComponentFilterMixin,
|
||||
ModularComponentTemplateFilterMixin, RackFilterMixin,
|
||||
)
|
||||
from extras.graphql.filter_mixins import ConfigContextFilterMixin
|
||||
from netbox.graphql.filter_mixins import (
|
||||
PrimaryModelFilterMixin,
|
||||
OrganizationalModelFilterMixin,
|
||||
NestedGroupModelFilterMixin,
|
||||
ImageAttachmentFilterMixin,
|
||||
WeightFilterMixin,
|
||||
from netbox.graphql.filter_mixins import ImageAttachmentFilterMixin, WeightFilterMixin
|
||||
from netbox.graphql.filters import (
|
||||
ChangeLoggedModelFilter, NestedGroupModelFilter, OrganizationalModelFilter, PrimaryModelFilter, NetBoxModelFilter,
|
||||
)
|
||||
from tenancy.graphql.filter_mixins import ContactFilterMixin, TenancyFilterMixin
|
||||
from virtualization.models import VMInterface
|
||||
|
||||
from .filter_mixins import (
|
||||
CabledObjectModelFilterMixin,
|
||||
ComponentModelFilterMixin,
|
||||
ComponentTemplateFilterMixin,
|
||||
InterfaceBaseFilterMixin,
|
||||
ModularComponentModelFilterMixin,
|
||||
ModularComponentTemplateFilterMixin,
|
||||
RackBaseFilterMixin,
|
||||
RenderConfigFilterMixin,
|
||||
)
|
||||
|
||||
@@ -96,7 +91,7 @@ __all__ = (
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.Cable, lookups=True)
|
||||
class CableFilter(PrimaryModelFilterMixin, TenancyFilterMixin):
|
||||
class CableFilter(TenancyFilterMixin, PrimaryModelFilter):
|
||||
type: BaseFilterLookup[Annotated['CableTypeEnum', strawberry.lazy('dcim.graphql.enums')]] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
@@ -119,7 +114,7 @@ class CableFilter(PrimaryModelFilterMixin, TenancyFilterMixin):
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.CableTermination, lookups=True)
|
||||
class CableTerminationFilter(ChangeLogFilterMixin):
|
||||
class CableTerminationFilter(ChangeLoggedModelFilter):
|
||||
cable: Annotated['CableFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field()
|
||||
cable_id: ID | None = strawberry_django.filter_field()
|
||||
cable_end: BaseFilterLookup[Annotated['CableEndEnum', strawberry.lazy('dcim.graphql.enums')]] | None = (
|
||||
@@ -132,7 +127,7 @@ class CableTerminationFilter(ChangeLogFilterMixin):
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.ConsolePort, lookups=True)
|
||||
class ConsolePortFilter(ModularComponentModelFilterMixin, CabledObjectModelFilterMixin):
|
||||
class ConsolePortFilter(ModularComponentFilterMixin, CabledObjectModelFilterMixin, NetBoxModelFilter):
|
||||
type: BaseFilterLookup[Annotated['ConsolePortTypeEnum', strawberry.lazy('dcim.graphql.enums')]] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
@@ -142,14 +137,14 @@ class ConsolePortFilter(ModularComponentModelFilterMixin, CabledObjectModelFilte
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.ConsolePortTemplate, lookups=True)
|
||||
class ConsolePortTemplateFilter(ModularComponentTemplateFilterMixin):
|
||||
class ConsolePortTemplateFilter(ModularComponentTemplateFilterMixin, ChangeLoggedModelFilter):
|
||||
type: BaseFilterLookup[Annotated['ConsolePortTypeEnum', strawberry.lazy('dcim.graphql.enums')]] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.ConsoleServerPort, lookups=True)
|
||||
class ConsoleServerPortFilter(ModularComponentModelFilterMixin, CabledObjectModelFilterMixin):
|
||||
class ConsoleServerPortFilter(ModularComponentFilterMixin, CabledObjectModelFilterMixin, NetBoxModelFilter):
|
||||
type: BaseFilterLookup[Annotated['ConsolePortTypeEnum', strawberry.lazy('dcim.graphql.enums')]] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
@@ -159,7 +154,7 @@ class ConsoleServerPortFilter(ModularComponentModelFilterMixin, CabledObjectMode
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.ConsoleServerPortTemplate, lookups=True)
|
||||
class ConsoleServerPortTemplateFilter(ModularComponentTemplateFilterMixin):
|
||||
class ConsoleServerPortTemplateFilter(ModularComponentTemplateFilterMixin, ChangeLoggedModelFilter):
|
||||
type: BaseFilterLookup[Annotated['ConsolePortTypeEnum', strawberry.lazy('dcim.graphql.enums')]] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
@@ -172,7 +167,7 @@ class DeviceFilter(
|
||||
ImageAttachmentFilterMixin,
|
||||
RenderConfigFilterMixin,
|
||||
ConfigContextFilterMixin,
|
||||
PrimaryModelFilterMixin,
|
||||
PrimaryModelFilter,
|
||||
):
|
||||
device_type: Annotated['DeviceTypeFilter', strawberry.lazy('dcim.graphql.filters')] | None = (
|
||||
strawberry_django.filter_field()
|
||||
@@ -285,7 +280,7 @@ class DeviceFilter(
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.DeviceBay, lookups=True)
|
||||
class DeviceBayFilter(ComponentModelFilterMixin):
|
||||
class DeviceBayFilter(ComponentModelFilterMixin, NetBoxModelFilter):
|
||||
installed_device: Annotated['DeviceFilter', strawberry.lazy('dcim.graphql.filters')] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
@@ -293,12 +288,12 @@ class DeviceBayFilter(ComponentModelFilterMixin):
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.DeviceBayTemplate, lookups=True)
|
||||
class DeviceBayTemplateFilter(ComponentTemplateFilterMixin):
|
||||
class DeviceBayTemplateFilter(ComponentTemplateFilterMixin, ChangeLoggedModelFilter):
|
||||
pass
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.InventoryItemTemplate, lookups=True)
|
||||
class InventoryItemTemplateFilter(ComponentTemplateFilterMixin):
|
||||
class InventoryItemTemplateFilter(ComponentTemplateFilterMixin, ChangeLoggedModelFilter):
|
||||
parent: Annotated['InventoryItemTemplateFilter', strawberry.lazy('dcim.graphql.filters')] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
@@ -318,7 +313,7 @@ class InventoryItemTemplateFilter(ComponentTemplateFilterMixin):
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.DeviceRole, lookups=True)
|
||||
class DeviceRoleFilter(OrganizationalModelFilterMixin, RenderConfigFilterMixin):
|
||||
class DeviceRoleFilter(RenderConfigFilterMixin, OrganizationalModelFilter):
|
||||
color: BaseFilterLookup[Annotated['ColorEnum', strawberry.lazy('netbox.graphql.enums')]] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
@@ -326,7 +321,7 @@ class DeviceRoleFilter(OrganizationalModelFilterMixin, RenderConfigFilterMixin):
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.DeviceType, lookups=True)
|
||||
class DeviceTypeFilter(ImageAttachmentFilterMixin, PrimaryModelFilterMixin, WeightFilterMixin):
|
||||
class DeviceTypeFilter(ImageAttachmentFilterMixin, WeightFilterMixin, PrimaryModelFilter):
|
||||
manufacturer: Annotated['ManufacturerFilter', strawberry.lazy('dcim.graphql.filters')] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
@@ -402,7 +397,7 @@ class DeviceTypeFilter(ImageAttachmentFilterMixin, PrimaryModelFilterMixin, Weig
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.FrontPort, lookups=True)
|
||||
class FrontPortFilter(ModularComponentModelFilterMixin, CabledObjectModelFilterMixin):
|
||||
class FrontPortFilter(ModularComponentFilterMixin, CabledObjectModelFilterMixin, NetBoxModelFilter):
|
||||
type: BaseFilterLookup[Annotated['PortTypeEnum', strawberry.lazy('dcim.graphql.enums')]] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
@@ -419,7 +414,7 @@ class FrontPortFilter(ModularComponentModelFilterMixin, CabledObjectModelFilterM
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.FrontPortTemplate, lookups=True)
|
||||
class FrontPortTemplateFilter(ModularComponentTemplateFilterMixin):
|
||||
class FrontPortTemplateFilter(ModularComponentTemplateFilterMixin, ChangeLoggedModelFilter):
|
||||
type: BaseFilterLookup[Annotated['PortTypeEnum', strawberry.lazy('dcim.graphql.enums')]] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
@@ -436,7 +431,7 @@ class FrontPortTemplateFilter(ModularComponentTemplateFilterMixin):
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.MACAddress, lookups=True)
|
||||
class MACAddressFilter(PrimaryModelFilterMixin):
|
||||
class MACAddressFilter(PrimaryModelFilter):
|
||||
mac_address: FilterLookup[str] | None = strawberry_django.filter_field()
|
||||
assigned_object_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = (
|
||||
strawberry_django.filter_field()
|
||||
@@ -463,7 +458,12 @@ class MACAddressFilter(PrimaryModelFilterMixin):
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.Interface, lookups=True)
|
||||
class InterfaceFilter(ModularComponentModelFilterMixin, InterfaceBaseFilterMixin, CabledObjectModelFilterMixin):
|
||||
class InterfaceFilter(
|
||||
ModularComponentFilterMixin,
|
||||
InterfaceBaseFilterMixin,
|
||||
CabledObjectModelFilterMixin,
|
||||
NetBoxModelFilter
|
||||
):
|
||||
vcdcs: Annotated['VirtualDeviceContextFilter', strawberry.lazy('dcim.graphql.filters')] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
@@ -553,7 +553,7 @@ class InterfaceFilter(ModularComponentModelFilterMixin, InterfaceBaseFilterMixin
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.InterfaceTemplate, lookups=True)
|
||||
class InterfaceTemplateFilter(ModularComponentTemplateFilterMixin):
|
||||
class InterfaceTemplateFilter(ModularComponentTemplateFilterMixin, ChangeLoggedModelFilter):
|
||||
type: BaseFilterLookup[Annotated['InterfaceTypeEnum', strawberry.lazy('dcim.graphql.enums')]] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
@@ -575,7 +575,7 @@ class InterfaceTemplateFilter(ModularComponentTemplateFilterMixin):
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.InventoryItem, lookups=True)
|
||||
class InventoryItemFilter(ComponentModelFilterMixin):
|
||||
class InventoryItemFilter(ComponentModelFilterMixin, NetBoxModelFilter):
|
||||
parent: Annotated['InventoryItemFilter', strawberry.lazy('dcim.graphql.filters')] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
@@ -602,14 +602,14 @@ class InventoryItemFilter(ComponentModelFilterMixin):
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.InventoryItemRole, lookups=True)
|
||||
class InventoryItemRoleFilter(OrganizationalModelFilterMixin):
|
||||
class InventoryItemRoleFilter(OrganizationalModelFilter):
|
||||
color: BaseFilterLookup[Annotated['ColorEnum', strawberry.lazy('netbox.graphql.enums')]] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.Location, lookups=True)
|
||||
class LocationFilter(ContactFilterMixin, ImageAttachmentFilterMixin, TenancyFilterMixin, NestedGroupModelFilterMixin):
|
||||
class LocationFilter(ContactFilterMixin, ImageAttachmentFilterMixin, TenancyFilterMixin, NestedGroupModelFilter):
|
||||
site: Annotated['SiteFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field()
|
||||
site_id: ID | None = strawberry_django.filter_field()
|
||||
status: BaseFilterLookup[Annotated['LocationStatusEnum', strawberry.lazy('dcim.graphql.enums')]] | None = (
|
||||
@@ -625,12 +625,12 @@ class LocationFilter(ContactFilterMixin, ImageAttachmentFilterMixin, TenancyFilt
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.Manufacturer, lookups=True)
|
||||
class ManufacturerFilter(ContactFilterMixin, OrganizationalModelFilterMixin):
|
||||
class ManufacturerFilter(ContactFilterMixin, OrganizationalModelFilter):
|
||||
pass
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.Module, lookups=True)
|
||||
class ModuleFilter(PrimaryModelFilterMixin, ConfigContextFilterMixin):
|
||||
class ModuleFilter(ConfigContextFilterMixin, PrimaryModelFilter):
|
||||
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 = (
|
||||
@@ -679,7 +679,7 @@ class ModuleFilter(PrimaryModelFilterMixin, ConfigContextFilterMixin):
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.ModuleBay, lookups=True)
|
||||
class ModuleBayFilter(ModularComponentModelFilterMixin):
|
||||
class ModuleBayFilter(ModularComponentFilterMixin, NetBoxModelFilter):
|
||||
parent: Annotated['ModuleBayFilter', strawberry.lazy('dcim.graphql.filters')] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
@@ -688,17 +688,17 @@ class ModuleBayFilter(ModularComponentModelFilterMixin):
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.ModuleBayTemplate, lookups=True)
|
||||
class ModuleBayTemplateFilter(ModularComponentTemplateFilterMixin):
|
||||
class ModuleBayTemplateFilter(ModularComponentTemplateFilterMixin, ChangeLoggedModelFilter):
|
||||
position: FilterLookup[str] | None = strawberry_django.filter_field()
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.ModuleTypeProfile, lookups=True)
|
||||
class ModuleTypeProfileFilter(PrimaryModelFilterMixin):
|
||||
class ModuleTypeProfileFilter(PrimaryModelFilter):
|
||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.ModuleType, lookups=True)
|
||||
class ModuleTypeFilter(ImageAttachmentFilterMixin, PrimaryModelFilterMixin, WeightFilterMixin):
|
||||
class ModuleTypeFilter(ImageAttachmentFilterMixin, WeightFilterMixin, PrimaryModelFilter):
|
||||
manufacturer: Annotated['ManufacturerFilter', strawberry.lazy('dcim.graphql.filters')] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
@@ -749,7 +749,7 @@ class ModuleTypeFilter(ImageAttachmentFilterMixin, PrimaryModelFilterMixin, Weig
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.Platform, lookups=True)
|
||||
class PlatformFilter(OrganizationalModelFilterMixin):
|
||||
class PlatformFilter(OrganizationalModelFilter):
|
||||
manufacturer: Annotated['ManufacturerFilter', strawberry.lazy('dcim.graphql.filters')] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
@@ -761,7 +761,7 @@ class PlatformFilter(OrganizationalModelFilterMixin):
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.PowerFeed, lookups=True)
|
||||
class PowerFeedFilter(CabledObjectModelFilterMixin, TenancyFilterMixin, PrimaryModelFilterMixin):
|
||||
class PowerFeedFilter(CabledObjectModelFilterMixin, TenancyFilterMixin, PrimaryModelFilter):
|
||||
power_panel: Annotated['PowerPanelFilter', strawberry.lazy('dcim.graphql.filters')] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
@@ -796,7 +796,7 @@ class PowerFeedFilter(CabledObjectModelFilterMixin, TenancyFilterMixin, PrimaryM
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.PowerOutlet, lookups=True)
|
||||
class PowerOutletFilter(ModularComponentModelFilterMixin, CabledObjectModelFilterMixin):
|
||||
class PowerOutletFilter(ModularComponentFilterMixin, CabledObjectModelFilterMixin, NetBoxModelFilter):
|
||||
type: BaseFilterLookup[Annotated['PowerOutletTypeEnum', strawberry.lazy('dcim.graphql.enums')]] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
@@ -816,7 +816,7 @@ class PowerOutletFilter(ModularComponentModelFilterMixin, CabledObjectModelFilte
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.PowerOutletTemplate, lookups=True)
|
||||
class PowerOutletTemplateFilter(ModularComponentModelFilterMixin):
|
||||
class PowerOutletTemplateFilter(ModularComponentTemplateFilterMixin, ChangeLoggedModelFilter):
|
||||
type: BaseFilterLookup[Annotated['PowerOutletTypeEnum', strawberry.lazy('dcim.graphql.enums')]] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
@@ -830,7 +830,7 @@ class PowerOutletTemplateFilter(ModularComponentModelFilterMixin):
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.PowerPanel, lookups=True)
|
||||
class PowerPanelFilter(ContactFilterMixin, ImageAttachmentFilterMixin, PrimaryModelFilterMixin):
|
||||
class PowerPanelFilter(ContactFilterMixin, ImageAttachmentFilterMixin, PrimaryModelFilter):
|
||||
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 = (
|
||||
@@ -843,7 +843,7 @@ class PowerPanelFilter(ContactFilterMixin, ImageAttachmentFilterMixin, PrimaryMo
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.PowerPort, lookups=True)
|
||||
class PowerPortFilter(ModularComponentModelFilterMixin, CabledObjectModelFilterMixin):
|
||||
class PowerPortFilter(ModularComponentFilterMixin, CabledObjectModelFilterMixin, NetBoxModelFilter):
|
||||
type: BaseFilterLookup[Annotated['PowerPortTypeEnum', strawberry.lazy('dcim.graphql.enums')]] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
@@ -856,7 +856,7 @@ class PowerPortFilter(ModularComponentModelFilterMixin, CabledObjectModelFilterM
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.PowerPortTemplate, lookups=True)
|
||||
class PowerPortTemplateFilter(ModularComponentTemplateFilterMixin):
|
||||
class PowerPortTemplateFilter(ModularComponentTemplateFilterMixin, ChangeLoggedModelFilter):
|
||||
type: BaseFilterLookup[Annotated['PowerPortTypeEnum', strawberry.lazy('dcim.graphql.enums')]] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
@@ -869,7 +869,7 @@ class PowerPortTemplateFilter(ModularComponentTemplateFilterMixin):
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.RackType, lookups=True)
|
||||
class RackTypeFilter(RackBaseFilterMixin):
|
||||
class RackTypeFilter(RackFilterMixin, WeightFilterMixin, PrimaryModelFilter):
|
||||
form_factor: BaseFilterLookup[Annotated['RackFormFactorEnum', strawberry.lazy('dcim.graphql.enums')]] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
@@ -884,7 +884,14 @@ class RackTypeFilter(RackBaseFilterMixin):
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.Rack, lookups=True)
|
||||
class RackFilter(ContactFilterMixin, ImageAttachmentFilterMixin, TenancyFilterMixin, RackBaseFilterMixin):
|
||||
class RackFilter(
|
||||
ContactFilterMixin,
|
||||
ImageAttachmentFilterMixin,
|
||||
TenancyFilterMixin,
|
||||
WeightFilterMixin,
|
||||
RackFilterMixin,
|
||||
PrimaryModelFilter
|
||||
):
|
||||
form_factor: BaseFilterLookup[Annotated['RackFormFactorEnum', strawberry.lazy('dcim.graphql.enums')]] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
@@ -918,7 +925,7 @@ class RackFilter(ContactFilterMixin, ImageAttachmentFilterMixin, TenancyFilterMi
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.RackReservation, lookups=True)
|
||||
class RackReservationFilter(TenancyFilterMixin, PrimaryModelFilterMixin):
|
||||
class RackReservationFilter(TenancyFilterMixin, PrimaryModelFilter):
|
||||
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 = (
|
||||
@@ -933,14 +940,14 @@ class RackReservationFilter(TenancyFilterMixin, PrimaryModelFilterMixin):
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.RackRole, lookups=True)
|
||||
class RackRoleFilter(OrganizationalModelFilterMixin):
|
||||
class RackRoleFilter(OrganizationalModelFilter):
|
||||
color: BaseFilterLookup[Annotated['ColorEnum', strawberry.lazy('netbox.graphql.enums')]] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.RearPort, lookups=True)
|
||||
class RearPortFilter(ModularComponentModelFilterMixin, CabledObjectModelFilterMixin):
|
||||
class RearPortFilter(ModularComponentFilterMixin, CabledObjectModelFilterMixin, NetBoxModelFilter):
|
||||
type: BaseFilterLookup[Annotated['PortTypeEnum', strawberry.lazy('dcim.graphql.enums')]] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
@@ -953,7 +960,7 @@ class RearPortFilter(ModularComponentModelFilterMixin, CabledObjectModelFilterMi
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.RearPortTemplate, lookups=True)
|
||||
class RearPortTemplateFilter(ModularComponentTemplateFilterMixin):
|
||||
class RearPortTemplateFilter(ModularComponentTemplateFilterMixin, ChangeLoggedModelFilter):
|
||||
type: BaseFilterLookup[Annotated['PortTypeEnum', strawberry.lazy('dcim.graphql.enums')]] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
@@ -966,7 +973,7 @@ class RearPortTemplateFilter(ModularComponentTemplateFilterMixin):
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.Region, lookups=True)
|
||||
class RegionFilter(ContactFilterMixin, NestedGroupModelFilterMixin):
|
||||
class RegionFilter(ContactFilterMixin, NestedGroupModelFilter):
|
||||
prefixes: Annotated['PrefixFilter', strawberry.lazy('ipam.graphql.filters')] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
@@ -976,7 +983,7 @@ class RegionFilter(ContactFilterMixin, NestedGroupModelFilterMixin):
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.Site, lookups=True)
|
||||
class SiteFilter(ContactFilterMixin, ImageAttachmentFilterMixin, TenancyFilterMixin, PrimaryModelFilterMixin):
|
||||
class SiteFilter(ContactFilterMixin, ImageAttachmentFilterMixin, TenancyFilterMixin, PrimaryModelFilter):
|
||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
||||
slug: FilterLookup[str] | None = strawberry_django.filter_field()
|
||||
status: BaseFilterLookup[Annotated['SiteStatusEnum', strawberry.lazy('dcim.graphql.enums')]] | None = (
|
||||
@@ -1012,7 +1019,7 @@ class SiteFilter(ContactFilterMixin, ImageAttachmentFilterMixin, TenancyFilterMi
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.SiteGroup, lookups=True)
|
||||
class SiteGroupFilter(ContactFilterMixin, NestedGroupModelFilterMixin):
|
||||
class SiteGroupFilter(ContactFilterMixin, NestedGroupModelFilter):
|
||||
prefixes: Annotated['PrefixFilter', strawberry.lazy('ipam.graphql.filters')] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
@@ -1022,7 +1029,7 @@ class SiteGroupFilter(ContactFilterMixin, NestedGroupModelFilterMixin):
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.VirtualChassis, lookups=True)
|
||||
class VirtualChassisFilter(PrimaryModelFilterMixin):
|
||||
class VirtualChassisFilter(PrimaryModelFilter):
|
||||
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()
|
||||
@@ -1034,7 +1041,7 @@ class VirtualChassisFilter(PrimaryModelFilterMixin):
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.VirtualDeviceContext, lookups=True)
|
||||
class VirtualDeviceContextFilter(TenancyFilterMixin, PrimaryModelFilterMixin):
|
||||
class VirtualDeviceContextFilter(TenancyFilterMixin, PrimaryModelFilter):
|
||||
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()
|
||||
|
||||
@@ -3,9 +3,6 @@ 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 JSONFilter
|
||||
@@ -16,37 +13,30 @@ __all__ = (
|
||||
'JournalEntriesFilterMixin',
|
||||
'TagsFilterMixin',
|
||||
'ConfigContextFilterMixin',
|
||||
'TagBaseFilterMixin',
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class CustomFieldsFilterMixin(BaseFilterMixin):
|
||||
class CustomFieldsFilterMixin:
|
||||
custom_field_data: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class JournalEntriesFilterMixin(BaseFilterMixin):
|
||||
class JournalEntriesFilterMixin:
|
||||
journal_entries: Annotated['JournalEntryFilter', strawberry.lazy('extras.graphql.filters')] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class TagsFilterMixin(BaseFilterMixin):
|
||||
class TagsFilterMixin:
|
||||
tags: Annotated['TagFilter', strawberry.lazy('extras.graphql.filters')] | None = strawberry_django.filter_field()
|
||||
|
||||
|
||||
@dataclass
|
||||
class ConfigContextFilterMixin(BaseFilterMixin):
|
||||
class ConfigContextFilterMixin:
|
||||
local_context_data: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | 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()
|
||||
|
||||
@@ -5,10 +5,10 @@ import strawberry_django
|
||||
from strawberry.scalars import ID
|
||||
from strawberry_django import BaseFilterLookup, 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 PrimaryModelFilterMixin, SyncedDataFilterMixin
|
||||
from extras.graphql.filter_mixins import CustomFieldsFilterMixin, TagsFilterMixin
|
||||
from netbox.graphql.filter_mixins import SyncedDataFilterMixin
|
||||
from netbox.graphql.filters import ChangeLoggedModelFilter, PrimaryModelFilter
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.graphql.filters import ContentTypeFilter
|
||||
@@ -42,7 +42,7 @@ __all__ = (
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.ConfigContext, lookups=True)
|
||||
class ConfigContextFilter(BaseObjectTypeFilterMixin, SyncedDataFilterMixin, ChangeLogFilterMixin):
|
||||
class ConfigContextFilter(SyncedDataFilterMixin, ChangeLoggedModelFilter):
|
||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
||||
weight: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
||||
strawberry_django.filter_field()
|
||||
@@ -99,14 +99,14 @@ class ConfigContextFilter(BaseObjectTypeFilterMixin, SyncedDataFilterMixin, Chan
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.ConfigContextProfile, lookups=True)
|
||||
class ConfigContextProfileFilter(SyncedDataFilterMixin, PrimaryModelFilterMixin):
|
||||
class ConfigContextProfileFilter(SyncedDataFilterMixin, PrimaryModelFilter):
|
||||
name: FilterLookup[str] = strawberry_django.filter_field()
|
||||
description: FilterLookup[str] = strawberry_django.filter_field()
|
||||
tags: Annotated['TagFilter', strawberry.lazy('extras.graphql.filters')] | None = strawberry_django.filter_field()
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.ConfigTemplate, lookups=True)
|
||||
class ConfigTemplateFilter(BaseObjectTypeFilterMixin, SyncedDataFilterMixin, ChangeLogFilterMixin):
|
||||
class ConfigTemplateFilter(SyncedDataFilterMixin, ChangeLoggedModelFilter):
|
||||
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()
|
||||
@@ -120,7 +120,7 @@ class ConfigTemplateFilter(BaseObjectTypeFilterMixin, SyncedDataFilterMixin, Cha
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.CustomField, lookups=True)
|
||||
class CustomFieldFilter(BaseObjectTypeFilterMixin, ChangeLogFilterMixin):
|
||||
class CustomFieldFilter(ChangeLoggedModelFilter):
|
||||
type: BaseFilterLookup[Annotated['CustomFieldTypeEnum', strawberry.lazy('extras.graphql.enums')]] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
@@ -179,7 +179,7 @@ class CustomFieldFilter(BaseObjectTypeFilterMixin, ChangeLogFilterMixin):
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.CustomFieldChoiceSet, lookups=True)
|
||||
class CustomFieldChoiceSetFilter(BaseObjectTypeFilterMixin, ChangeLogFilterMixin):
|
||||
class CustomFieldChoiceSetFilter(ChangeLoggedModelFilter):
|
||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
||||
description: FilterLookup[str] | None = strawberry_django.filter_field()
|
||||
base_choices: (
|
||||
@@ -194,7 +194,7 @@ class CustomFieldChoiceSetFilter(BaseObjectTypeFilterMixin, ChangeLogFilterMixin
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.CustomLink, lookups=True)
|
||||
class CustomLinkFilter(BaseObjectTypeFilterMixin, ChangeLogFilterMixin):
|
||||
class CustomLinkFilter(ChangeLoggedModelFilter):
|
||||
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()
|
||||
@@ -212,7 +212,7 @@ class CustomLinkFilter(BaseObjectTypeFilterMixin, ChangeLogFilterMixin):
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.ExportTemplate, lookups=True)
|
||||
class ExportTemplateFilter(BaseObjectTypeFilterMixin, SyncedDataFilterMixin, ChangeLogFilterMixin):
|
||||
class ExportTemplateFilter(SyncedDataFilterMixin, ChangeLoggedModelFilter):
|
||||
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()
|
||||
@@ -226,7 +226,7 @@ class ExportTemplateFilter(BaseObjectTypeFilterMixin, SyncedDataFilterMixin, Cha
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.ImageAttachment, lookups=True)
|
||||
class ImageAttachmentFilter(BaseObjectTypeFilterMixin, ChangeLogFilterMixin):
|
||||
class ImageAttachmentFilter(ChangeLoggedModelFilter):
|
||||
object_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
@@ -241,7 +241,7 @@ class ImageAttachmentFilter(BaseObjectTypeFilterMixin, ChangeLogFilterMixin):
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.JournalEntry, lookups=True)
|
||||
class JournalEntryFilter(BaseObjectTypeFilterMixin, CustomFieldsFilterMixin, TagsFilterMixin, ChangeLogFilterMixin):
|
||||
class JournalEntryFilter(CustomFieldsFilterMixin, TagsFilterMixin, ChangeLoggedModelFilter):
|
||||
assigned_object_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
@@ -257,7 +257,7 @@ class JournalEntryFilter(BaseObjectTypeFilterMixin, CustomFieldsFilterMixin, Tag
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.NotificationGroup, lookups=True)
|
||||
class NotificationGroupFilter(BaseObjectTypeFilterMixin, ChangeLogFilterMixin):
|
||||
class NotificationGroupFilter(ChangeLoggedModelFilter):
|
||||
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()
|
||||
@@ -265,7 +265,7 @@ class NotificationGroupFilter(BaseObjectTypeFilterMixin, ChangeLogFilterMixin):
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.SavedFilter, lookups=True)
|
||||
class SavedFilterFilter(BaseObjectTypeFilterMixin, ChangeLogFilterMixin):
|
||||
class SavedFilterFilter(ChangeLoggedModelFilter):
|
||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
||||
slug: FilterLookup[str] | None = strawberry_django.filter_field()
|
||||
description: FilterLookup[str] | None = strawberry_django.filter_field()
|
||||
@@ -282,7 +282,7 @@ class SavedFilterFilter(BaseObjectTypeFilterMixin, ChangeLogFilterMixin):
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.TableConfig, lookups=True)
|
||||
class TableConfigFilter(BaseObjectTypeFilterMixin, ChangeLogFilterMixin):
|
||||
class TableConfigFilter(ChangeLoggedModelFilter):
|
||||
name: 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()
|
||||
@@ -295,7 +295,9 @@ class TableConfigFilter(BaseObjectTypeFilterMixin, ChangeLogFilterMixin):
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.Tag, lookups=True)
|
||||
class TagFilter(BaseObjectTypeFilterMixin, ChangeLogFilterMixin, TagBaseFilterMixin):
|
||||
class TagFilter(ChangeLoggedModelFilter):
|
||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
||||
slug: FilterLookup[str] | None = strawberry_django.filter_field()
|
||||
color: BaseFilterLookup[Annotated['ColorEnum', strawberry.lazy('netbox.graphql.enums')]] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
@@ -303,7 +305,7 @@ class TagFilter(BaseObjectTypeFilterMixin, ChangeLogFilterMixin, TagBaseFilterMi
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.Webhook, lookups=True)
|
||||
class WebhookFilter(BaseObjectTypeFilterMixin, CustomFieldsFilterMixin, TagsFilterMixin, ChangeLogFilterMixin):
|
||||
class WebhookFilter(CustomFieldsFilterMixin, TagsFilterMixin, ChangeLoggedModelFilter):
|
||||
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()
|
||||
@@ -324,7 +326,7 @@ class WebhookFilter(BaseObjectTypeFilterMixin, CustomFieldsFilterMixin, TagsFilt
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.EventRule, lookups=True)
|
||||
class EventRuleFilter(BaseObjectTypeFilterMixin, CustomFieldsFilterMixin, TagsFilterMixin, ChangeLogFilterMixin):
|
||||
class EventRuleFilter(CustomFieldsFilterMixin, TagsFilterMixin, ChangeLoggedModelFilter):
|
||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
||||
description: FilterLookup[str] | None = strawberry_django.filter_field()
|
||||
event_types: Annotated['StringArrayLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import django_filters
|
||||
import netaddr
|
||||
from dcim.base_filtersets import ScopedFilterSet
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db.models import Q
|
||||
@@ -10,12 +9,12 @@ from drf_spectacular.utils import extend_schema_field
|
||||
from netaddr.core import AddrFormatError
|
||||
|
||||
from circuits.models import Provider
|
||||
from dcim.base_filtersets import ScopedFilterSet
|
||||
from dcim.models import Device, Interface, Region, Site, SiteGroup
|
||||
from netbox.filtersets import (
|
||||
ChangeLoggedModelFilterSet, OrganizationalModelFilterSet, NetBoxModelFilterSet, PrimaryModelFilterSet,
|
||||
)
|
||||
from tenancy.filtersets import ContactModelFilterSet, TenancyFilterSet
|
||||
|
||||
from utilities.filters import (
|
||||
ContentTypeFilter, MultiValueCharFilter, MultiValueNumberFilter, NumericArrayFilter, TreeNodeMultipleChoiceFilter,
|
||||
)
|
||||
|
||||
@@ -3,21 +3,20 @@ from typing import Annotated, TYPE_CHECKING
|
||||
|
||||
import strawberry
|
||||
import strawberry_django
|
||||
|
||||
from core.graphql.filter_mixins import BaseFilterMixin
|
||||
from strawberry_django import BaseFilterLookup
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from netbox.graphql.filter_lookups import IntegerLookup
|
||||
from .enums import *
|
||||
|
||||
__all__ = (
|
||||
'ServiceBaseFilterMixin',
|
||||
'ServiceFilterMixin',
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class ServiceBaseFilterMixin(BaseFilterMixin):
|
||||
protocol: Annotated['ServiceProtocolEnum', strawberry.lazy('ipam.graphql.enums')] | None = (
|
||||
class ServiceFilterMixin:
|
||||
protocol: BaseFilterLookup[Annotated['ServiceProtocolEnum', strawberry.lazy('ipam.graphql.enums')]] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
ports: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
||||
|
||||
@@ -9,12 +9,13 @@ from netaddr.core import AddrFormatError
|
||||
from strawberry.scalars import ID
|
||||
from strawberry_django import BaseFilterLookup, FilterLookup, DateFilterLookup
|
||||
|
||||
from core.graphql.filter_mixins import BaseObjectTypeFilterMixin, ChangeLogFilterMixin
|
||||
from dcim.graphql.filter_mixins import ScopedFilterMixin
|
||||
from dcim.models import Device
|
||||
from ipam import models
|
||||
from ipam.graphql.filter_mixins import ServiceBaseFilterMixin
|
||||
from netbox.graphql.filter_mixins import NetBoxModelFilterMixin, OrganizationalModelFilterMixin, PrimaryModelFilterMixin
|
||||
from ipam.graphql.filter_mixins import ServiceFilterMixin
|
||||
from netbox.graphql.filters import (
|
||||
ChangeLoggedModelFilter, NetBoxModelFilter, OrganizationalModelFilter, PrimaryModelFilter,
|
||||
)
|
||||
from tenancy.graphql.filter_mixins import ContactFilterMixin, TenancyFilterMixin
|
||||
from virtualization.models import VMInterface
|
||||
|
||||
@@ -49,7 +50,7 @@ __all__ = (
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.ASN, lookups=True)
|
||||
class ASNFilter(TenancyFilterMixin, PrimaryModelFilterMixin):
|
||||
class ASNFilter(TenancyFilterMixin, PrimaryModelFilter):
|
||||
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 = (
|
||||
@@ -64,7 +65,7 @@ class ASNFilter(TenancyFilterMixin, PrimaryModelFilterMixin):
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.ASNRange, lookups=True)
|
||||
class ASNRangeFilter(TenancyFilterMixin, OrganizationalModelFilterMixin):
|
||||
class ASNRangeFilter(TenancyFilterMixin, OrganizationalModelFilter):
|
||||
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()
|
||||
@@ -78,7 +79,7 @@ class ASNRangeFilter(TenancyFilterMixin, OrganizationalModelFilterMixin):
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.Aggregate, lookups=True)
|
||||
class AggregateFilter(ContactFilterMixin, TenancyFilterMixin, PrimaryModelFilterMixin):
|
||||
class AggregateFilter(ContactFilterMixin, TenancyFilterMixin, PrimaryModelFilter):
|
||||
prefix: 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()
|
||||
@@ -111,7 +112,7 @@ class AggregateFilter(ContactFilterMixin, TenancyFilterMixin, PrimaryModelFilter
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.FHRPGroup, lookups=True)
|
||||
class FHRPGroupFilter(PrimaryModelFilterMixin):
|
||||
class FHRPGroupFilter(PrimaryModelFilter):
|
||||
group_id: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
@@ -129,7 +130,7 @@ class FHRPGroupFilter(PrimaryModelFilterMixin):
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.FHRPGroupAssignment, lookups=True)
|
||||
class FHRPGroupAssignmentFilter(BaseObjectTypeFilterMixin, ChangeLogFilterMixin):
|
||||
class FHRPGroupAssignmentFilter(ChangeLoggedModelFilter):
|
||||
interface_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
@@ -168,7 +169,7 @@ class FHRPGroupAssignmentFilter(BaseObjectTypeFilterMixin, ChangeLogFilterMixin)
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.IPAddress, lookups=True)
|
||||
class IPAddressFilter(ContactFilterMixin, TenancyFilterMixin, PrimaryModelFilterMixin):
|
||||
class IPAddressFilter(ContactFilterMixin, TenancyFilterMixin, PrimaryModelFilter):
|
||||
address: FilterLookup[str] | None = strawberry_django.filter_field()
|
||||
vrf: Annotated['VRFFilter', strawberry.lazy('ipam.graphql.filters')] | None = strawberry_django.filter_field()
|
||||
vrf_id: ID | None = strawberry_django.filter_field()
|
||||
@@ -219,7 +220,7 @@ class IPAddressFilter(ContactFilterMixin, TenancyFilterMixin, PrimaryModelFilter
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.IPRange, lookups=True)
|
||||
class IPRangeFilter(ContactFilterMixin, TenancyFilterMixin, PrimaryModelFilterMixin):
|
||||
class IPRangeFilter(ContactFilterMixin, TenancyFilterMixin, PrimaryModelFilter):
|
||||
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 = (
|
||||
@@ -273,7 +274,7 @@ class IPRangeFilter(ContactFilterMixin, TenancyFilterMixin, PrimaryModelFilterMi
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.Prefix, lookups=True)
|
||||
class PrefixFilter(ContactFilterMixin, ScopedFilterMixin, TenancyFilterMixin, PrimaryModelFilterMixin):
|
||||
class PrefixFilter(ContactFilterMixin, ScopedFilterMixin, TenancyFilterMixin, PrimaryModelFilter):
|
||||
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()
|
||||
@@ -310,19 +311,19 @@ class PrefixFilter(ContactFilterMixin, ScopedFilterMixin, TenancyFilterMixin, Pr
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.RIR, lookups=True)
|
||||
class RIRFilter(OrganizationalModelFilterMixin):
|
||||
class RIRFilter(OrganizationalModelFilter):
|
||||
is_private: FilterLookup[bool] | None = strawberry_django.filter_field()
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.Role, lookups=True)
|
||||
class RoleFilter(OrganizationalModelFilterMixin):
|
||||
class RoleFilter(OrganizationalModelFilter):
|
||||
weight: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.RouteTarget, lookups=True)
|
||||
class RouteTargetFilter(TenancyFilterMixin, PrimaryModelFilterMixin):
|
||||
class RouteTargetFilter(TenancyFilterMixin, PrimaryModelFilter):
|
||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
||||
importing_vrfs: Annotated['VRFFilter', strawberry.lazy('ipam.graphql.filters')] | None = (
|
||||
strawberry_django.filter_field()
|
||||
@@ -339,7 +340,7 @@ class RouteTargetFilter(TenancyFilterMixin, PrimaryModelFilterMixin):
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.Service, lookups=True)
|
||||
class ServiceFilter(ContactFilterMixin, ServiceBaseFilterMixin, PrimaryModelFilterMixin):
|
||||
class ServiceFilter(ContactFilterMixin, ServiceFilterMixin, PrimaryModelFilter):
|
||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
||||
ip_addresses: Annotated['IPAddressFilter', strawberry.lazy('ipam.graphql.filters')] | None = (
|
||||
strawberry_django.filter_field()
|
||||
@@ -351,12 +352,12 @@ class ServiceFilter(ContactFilterMixin, ServiceBaseFilterMixin, PrimaryModelFilt
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.ServiceTemplate, lookups=True)
|
||||
class ServiceTemplateFilter(ServiceBaseFilterMixin, PrimaryModelFilterMixin):
|
||||
class ServiceTemplateFilter(ServiceFilterMixin, PrimaryModelFilter):
|
||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.VLAN, lookups=True)
|
||||
class VLANFilter(TenancyFilterMixin, PrimaryModelFilterMixin):
|
||||
class VLANFilter(TenancyFilterMixin, PrimaryModelFilter):
|
||||
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 = (
|
||||
@@ -388,19 +389,19 @@ class VLANFilter(TenancyFilterMixin, PrimaryModelFilterMixin):
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.VLANGroup, lookups=True)
|
||||
class VLANGroupFilter(ScopedFilterMixin, OrganizationalModelFilterMixin):
|
||||
class VLANGroupFilter(ScopedFilterMixin, OrganizationalModelFilter):
|
||||
vid_ranges: Annotated['IntegerRangeArrayLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.VLANTranslationPolicy, lookups=True)
|
||||
class VLANTranslationPolicyFilter(PrimaryModelFilterMixin):
|
||||
class VLANTranslationPolicyFilter(PrimaryModelFilter):
|
||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.VLANTranslationRule, lookups=True)
|
||||
class VLANTranslationRuleFilter(NetBoxModelFilterMixin):
|
||||
class VLANTranslationRuleFilter(NetBoxModelFilter):
|
||||
policy: Annotated['VLANTranslationPolicyFilter', strawberry.lazy('ipam.graphql.filters')] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
@@ -415,7 +416,7 @@ class VLANTranslationRuleFilter(NetBoxModelFilterMixin):
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.VRF, lookups=True)
|
||||
class VRFFilter(TenancyFilterMixin, PrimaryModelFilterMixin):
|
||||
class VRFFilter(TenancyFilterMixin, PrimaryModelFilter):
|
||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
||||
rd: FilterLookup[str] | None = strawberry_django.filter_field()
|
||||
enforce_unique: FilterLookup[bool] | None = strawberry_django.filter_field()
|
||||
|
||||
@@ -5,6 +5,7 @@ from django.conf import settings
|
||||
from django_rq.queues import get_connection
|
||||
from drf_spectacular.types import OpenApiTypes
|
||||
from drf_spectacular.utils import extend_schema
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.reverse import reverse
|
||||
from rest_framework.views import APIView
|
||||
@@ -12,6 +13,7 @@ from rq.worker import Worker
|
||||
|
||||
from netbox.api.authentication import IsAuthenticatedOrLoginNotRequired
|
||||
from netbox.plugins.utils import get_installed_plugins
|
||||
from users.api.serializers import UserSerializer
|
||||
from utilities.apps import get_installed_apps
|
||||
|
||||
|
||||
@@ -62,3 +64,15 @@ class StatusView(APIView):
|
||||
'python-version': platform.python_version(),
|
||||
'rq-workers-running': Worker.count(get_connection('default')),
|
||||
})
|
||||
|
||||
|
||||
class AuthenticationCheckView(APIView):
|
||||
"""
|
||||
Return the user making the request, if authenticated successfully.
|
||||
"""
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
@extend_schema(responses={200: OpenApiTypes.OBJECT})
|
||||
def get(self, request):
|
||||
serializer = UserSerializer(request.user, context={'request': request})
|
||||
return Response(serializer.data)
|
||||
|
||||
@@ -4,19 +4,11 @@ from typing import TypeVar, TYPE_CHECKING, Annotated
|
||||
|
||||
import strawberry
|
||||
import strawberry_django
|
||||
from strawberry import ID
|
||||
from strawberry_django import BaseFilterLookup, FilterLookup, DatetimeFilterLookup
|
||||
|
||||
from core.graphql.filter_mixins import BaseFilterMixin, BaseObjectTypeFilterMixin, ChangeLogFilterMixin
|
||||
from extras.graphql.filter_mixins import CustomFieldsFilterMixin, JournalEntriesFilterMixin, TagsFilterMixin
|
||||
|
||||
__all__ = (
|
||||
'DistanceFilterMixin',
|
||||
'ImageAttachmentFilterMixin',
|
||||
'NestedGroupModelFilterMixin',
|
||||
'NetBoxModelFilterMixin',
|
||||
'OrganizationalModelFilterMixin',
|
||||
'PrimaryModelFilterMixin',
|
||||
'SyncedDataFilterMixin',
|
||||
'WeightFilterMixin',
|
||||
)
|
||||
@@ -30,51 +22,15 @@ if TYPE_CHECKING:
|
||||
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()
|
||||
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):
|
||||
class ImageAttachmentFilterMixin:
|
||||
images: Annotated['ImageAttachmentFilter', strawberry.lazy('extras.graphql.filters')] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class WeightFilterMixin(BaseFilterMixin):
|
||||
class WeightFilterMixin:
|
||||
weight: FilterLookup[float] | None = strawberry_django.filter_field()
|
||||
weight_unit: BaseFilterLookup[Annotated['WeightUnitEnum', strawberry.lazy('netbox.graphql.enums')]] | None = (
|
||||
strawberry_django.filter_field()
|
||||
@@ -82,7 +38,7 @@ class WeightFilterMixin(BaseFilterMixin):
|
||||
|
||||
|
||||
@dataclass
|
||||
class SyncedDataFilterMixin(BaseFilterMixin):
|
||||
class SyncedDataFilterMixin:
|
||||
data_source: Annotated['DataSourceFilter', strawberry.lazy('core.graphql.filters')] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
@@ -97,7 +53,7 @@ class SyncedDataFilterMixin(BaseFilterMixin):
|
||||
|
||||
|
||||
@dataclass
|
||||
class DistanceFilterMixin(BaseFilterMixin):
|
||||
class DistanceFilterMixin:
|
||||
distance: FilterLookup[float] | None = strawberry_django.filter_field()
|
||||
distance_unit: BaseFilterLookup[Annotated['DistanceUnitEnum', strawberry.lazy('netbox.graphql.enums')]] | None = (
|
||||
strawberry_django.filter_field()
|
||||
|
||||
61
netbox/netbox/graphql/filters.py
Normal file
61
netbox/netbox/graphql/filters.py
Normal file
@@ -0,0 +1,61 @@
|
||||
from dataclasses import dataclass
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import strawberry_django
|
||||
from strawberry import ID
|
||||
from strawberry_django import FilterLookup
|
||||
|
||||
from core.graphql.filter_mixins import ChangeLoggingMixin
|
||||
from extras.graphql.filter_mixins import CustomFieldsFilterMixin, JournalEntriesFilterMixin, TagsFilterMixin
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .filters import *
|
||||
|
||||
__all__ = (
|
||||
'BaseModelFilter',
|
||||
'ChangeLoggedModelFilter',
|
||||
'NestedGroupModelFilter',
|
||||
'NetBoxModelFilter',
|
||||
'OrganizationalModelFilter',
|
||||
'PrimaryModelFilter',
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class BaseModelFilter:
|
||||
id: FilterLookup[ID] | None = strawberry_django.filter_field()
|
||||
|
||||
|
||||
class ChangeLoggedModelFilter(ChangeLoggingMixin, BaseModelFilter):
|
||||
pass
|
||||
|
||||
|
||||
class NetBoxModelFilter(
|
||||
CustomFieldsFilterMixin,
|
||||
JournalEntriesFilterMixin,
|
||||
TagsFilterMixin,
|
||||
ChangeLoggingMixin,
|
||||
BaseModelFilter
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
@dataclass
|
||||
class NestedGroupModelFilter(NetBoxModelFilter):
|
||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
||||
slug: FilterLookup[str] | None = strawberry_django.filter_field()
|
||||
description: FilterLookup[str] | None = strawberry_django.filter_field()
|
||||
parent_id: ID | None = strawberry_django.filter_field()
|
||||
|
||||
|
||||
@dataclass
|
||||
class OrganizationalModelFilter(NetBoxModelFilter):
|
||||
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 PrimaryModelFilter(NetBoxModelFilter):
|
||||
description: FilterLookup[str] | None = strawberry_django.filter_field()
|
||||
comments: FilterLookup[str] | None = strawberry_django.filter_field()
|
||||
@@ -32,6 +32,18 @@ class AppTest(APITestCase):
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_authentication_check(self):
|
||||
url = reverse('api-authentication-check')
|
||||
|
||||
# Test an unauthenticated request
|
||||
response = self.client.get(f'{url}')
|
||||
self.assertEqual(response.status_code, 403)
|
||||
|
||||
# Test an authenticated request
|
||||
response = self.client.get(f'{url}', **self.header)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.data['id'], self.user.pk)
|
||||
|
||||
|
||||
class OptionalLimitOffsetPaginationTest(TestCase):
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ from django.views.decorators.cache import cache_page
|
||||
from drf_spectacular.views import SpectacularAPIView, SpectacularRedocView, SpectacularSwaggerView
|
||||
|
||||
from account.views import LoginView, LogoutView
|
||||
from netbox.api.views import APIRootView, StatusView
|
||||
from netbox.api.views import APIRootView, AuthenticationCheckView, StatusView
|
||||
from netbox.graphql.schema import schema
|
||||
from netbox.graphql.views import NetBoxGraphQLView
|
||||
from netbox.plugins.urls import plugin_patterns, plugin_api_patterns
|
||||
@@ -53,6 +53,7 @@ _patterns = [
|
||||
path('api/vpn/', include('vpn.api.urls')),
|
||||
path('api/wireless/', include('wireless.api.urls')),
|
||||
path('api/status/', StatusView.as_view(), name='api-status'),
|
||||
path('api/authentication-check/', AuthenticationCheckView.as_view(), name='api-authentication-check'),
|
||||
|
||||
# REST API schema
|
||||
path(
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
dist
|
||||
node_modules
|
||||
.cache
|
||||
@@ -1,53 +0,0 @@
|
||||
{
|
||||
"root": true,
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:import/typescript",
|
||||
"plugin:@typescript-eslint/eslint-recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:prettier/recommended",
|
||||
"prettier"
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es6": true,
|
||||
"node": true
|
||||
},
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2020,
|
||||
"sourceType": "module",
|
||||
"ecmaFeatures": {
|
||||
"arrowFunctions": true
|
||||
}
|
||||
},
|
||||
"plugins": ["@typescript-eslint", "prettier"],
|
||||
"settings": {
|
||||
"import/parsers": {
|
||||
"@typescript-eslint/parser": [".ts", ".tsx"]
|
||||
},
|
||||
"import/resolver": {
|
||||
"typescript": {}
|
||||
}
|
||||
},
|
||||
"rules": {
|
||||
"@typescript-eslint/no-unused-vars": "error",
|
||||
"no-unused-vars": "off",
|
||||
"no-inner-declarations": "off",
|
||||
"comma-dangle": ["error", "always-multiline"],
|
||||
"global-require": "off",
|
||||
"import/no-dynamic-require": "off",
|
||||
"import/prefer-default-export": "off",
|
||||
"@typescript-eslint/no-inferrable-types": "off",
|
||||
"@typescript-eslint/explicit-function-return-type": "off",
|
||||
"@typescript-eslint/no-var-requires": "off",
|
||||
"@typescript-eslint/no-non-null-assertion": "off",
|
||||
"@typescript-eslint/no-namespace": "off",
|
||||
"@typescript-eslint/no-empty-interface": [
|
||||
"error",
|
||||
{
|
||||
"allowSingleExtends": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
2
netbox/project-static/dist/netbox.js
vendored
2
netbox/project-static/dist/netbox.js
vendored
File diff suppressed because one or more lines are too long
2
netbox/project-static/dist/netbox.js.map
vendored
2
netbox/project-static/dist/netbox.js.map
vendored
File diff suppressed because one or more lines are too long
86
netbox/project-static/eslint.config.js
Normal file
86
netbox/project-static/eslint.config.js
Normal file
@@ -0,0 +1,86 @@
|
||||
import { defineConfig, globalIgnores } from "eslint/config";
|
||||
import { fixupConfigRules, fixupPluginRules } from "@eslint/compat";
|
||||
import typescriptEslint from "@typescript-eslint/eslint-plugin";
|
||||
import prettier from "eslint-plugin-prettier";
|
||||
import globals from "globals";
|
||||
import tsParser from "@typescript-eslint/parser";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import js from "@eslint/js";
|
||||
import { FlatCompat } from "@eslint/eslintrc";
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
const compat = new FlatCompat({
|
||||
baseDirectory: __dirname,
|
||||
recommendedConfig: js.configs.recommended,
|
||||
allConfig: js.configs.all,
|
||||
});
|
||||
|
||||
export default defineConfig([
|
||||
globalIgnores(['**/dist', '**/node_modules', '**/.cache']),
|
||||
{
|
||||
extends: fixupConfigRules(
|
||||
compat.extends(
|
||||
'eslint:recommended',
|
||||
'plugin:import/typescript',
|
||||
'plugin:@typescript-eslint/eslint-recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:prettier/recommended',
|
||||
'prettier',
|
||||
),
|
||||
),
|
||||
|
||||
plugins: {
|
||||
'@typescript-eslint': fixupPluginRules(typescriptEslint),
|
||||
prettier: fixupPluginRules(prettier),
|
||||
},
|
||||
|
||||
languageOptions: {
|
||||
globals: {
|
||||
...globals.browser,
|
||||
...globals.node,
|
||||
},
|
||||
|
||||
parser: tsParser,
|
||||
ecmaVersion: 2020,
|
||||
sourceType: 'module',
|
||||
|
||||
parserOptions: {
|
||||
ecmaFeatures: {
|
||||
arrowFunctions: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
settings: {
|
||||
'import/parsers': {
|
||||
'@typescript-eslint/parser': ['.ts', '.tsx'],
|
||||
},
|
||||
'import/resolver': {
|
||||
typescript: {},
|
||||
},
|
||||
},
|
||||
|
||||
rules: {
|
||||
'@typescript-eslint/no-unused-vars': 'error',
|
||||
'no-unused-vars': 'off',
|
||||
'no-inner-declarations': 'off',
|
||||
'comma-dangle': ['error', 'always-multiline'],
|
||||
'global-require': 'off',
|
||||
'import/no-dynamic-require': 'off',
|
||||
'import/prefer-default-export': 'off',
|
||||
'@typescript-eslint/no-inferrable-types': 'off',
|
||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||
'@typescript-eslint/no-var-requires': 'off',
|
||||
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||
'@typescript-eslint/no-namespace': 'off',
|
||||
'@typescript-eslint/no-empty-interface': [
|
||||
'error',
|
||||
{
|
||||
allowSingleExtends: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
]);
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"name": "netbox",
|
||||
"version": "4.4.0",
|
||||
"type": "module",
|
||||
"version": "4.5.0",
|
||||
"main": "dist/netbox.js",
|
||||
"license": "Apache-2.0",
|
||||
"private": true,
|
||||
@@ -8,14 +9,14 @@
|
||||
"netbox-graphiql"
|
||||
],
|
||||
"scripts": {
|
||||
"bundle": "node bundle.js",
|
||||
"bundle:styles": "node bundle.js --styles",
|
||||
"bundle:scripts": "node bundle.js --scripts",
|
||||
"bundle": "node bundle.cjs",
|
||||
"bundle:styles": "node bundle.cjs --styles",
|
||||
"bundle:scripts": "node bundle.cjs --scripts",
|
||||
"format": "yarn format:scripts && yarn format:styles",
|
||||
"format:scripts": "prettier -w src/**/*.ts",
|
||||
"format:styles": "prettier -w styles/**/*.scss",
|
||||
"validate": "yarn validate:types && yarn validate:lint",
|
||||
"validate:lint": "eslint -c .eslintrc ./src/**/*.ts",
|
||||
"validate:lint": "eslint ./src/**/*.ts",
|
||||
"validate:types": "tsc --noEmit",
|
||||
"validate:formatting": "yarn validate:formatting:scripts && yarn validate:formatting:styles",
|
||||
"validate:formatting:styles": "prettier -c styles/**/*.scss",
|
||||
@@ -36,20 +37,24 @@
|
||||
"typeface-roboto-mono": "1.1.13"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/compat": "^2.0.0",
|
||||
"@eslint/eslintrc": "^3.3.3",
|
||||
"@eslint/js": "^9.39.1",
|
||||
"@types/bootstrap": "5.2.10",
|
||||
"@types/cookie": "^0.6.0",
|
||||
"@types/node": "^22.3.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.37.0",
|
||||
"@typescript-eslint/parser": "^8.37.0",
|
||||
"esbuild": "^0.25.11",
|
||||
"@types/cookie": "^1.0.0",
|
||||
"@types/node": "^24.10.1",
|
||||
"@typescript-eslint/eslint-plugin": "^8.48.1",
|
||||
"@typescript-eslint/parser": "^8.48.1",
|
||||
"esbuild": "^0.27.0",
|
||||
"esbuild-sass-plugin": "^3.3.1",
|
||||
"eslint": "<9.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-import-resolver-typescript": "^3.6.3",
|
||||
"eslint": "^9.39.1",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-import-resolver-typescript": "^4.4.4",
|
||||
"eslint-plugin-import": "^2.32.0",
|
||||
"eslint-plugin-prettier": "^5.5.1",
|
||||
"prettier": "^3.3.3",
|
||||
"typescript": "<5.5"
|
||||
"globals": "^16.5.0",
|
||||
"prettier": "^3.7.3",
|
||||
"typescript": "^5.9.3"
|
||||
},
|
||||
"resolutions": {
|
||||
"@types/bootstrap/**/@popperjs/core": "^2.11.6"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,8 +5,6 @@ import strawberry
|
||||
import strawberry_django
|
||||
from strawberry import ID
|
||||
|
||||
from core.graphql.filter_mixins import BaseFilterMixin
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from netbox.graphql.filter_lookups import TreeNodeFilter
|
||||
from .filters import ContactAssignmentFilter, TenantFilter, TenantGroupFilter
|
||||
@@ -18,14 +16,14 @@ __all__ = (
|
||||
|
||||
|
||||
@dataclass
|
||||
class ContactFilterMixin(BaseFilterMixin):
|
||||
class ContactFilterMixin:
|
||||
contacts: Annotated['ContactAssignmentFilter', strawberry.lazy('tenancy.graphql.filters')] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class TenancyFilterMixin(BaseFilterMixin):
|
||||
class TenancyFilterMixin:
|
||||
tenant: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
|
||||
@@ -5,12 +5,9 @@ import strawberry_django
|
||||
from strawberry.scalars import ID
|
||||
from strawberry_django import BaseFilterLookup, FilterLookup
|
||||
|
||||
from core.graphql.filter_mixins import ChangeLogFilterMixin
|
||||
from extras.graphql.filter_mixins import CustomFieldsFilterMixin, TagsFilterMixin
|
||||
from netbox.graphql.filter_mixins import (
|
||||
NestedGroupModelFilterMixin,
|
||||
OrganizationalModelFilterMixin,
|
||||
PrimaryModelFilterMixin,
|
||||
from netbox.graphql.filters import (
|
||||
ChangeLoggedModelFilter, NestedGroupModelFilter, OrganizationalModelFilter, PrimaryModelFilter,
|
||||
)
|
||||
from tenancy import models
|
||||
from .filter_mixins import ContactFilterMixin
|
||||
@@ -57,7 +54,7 @@ __all__ = (
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.Tenant, lookups=True)
|
||||
class TenantFilter(PrimaryModelFilterMixin, ContactFilterMixin):
|
||||
class TenantFilter(ContactFilterMixin, PrimaryModelFilter):
|
||||
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 = (
|
||||
@@ -136,7 +133,7 @@ class TenantFilter(PrimaryModelFilterMixin, ContactFilterMixin):
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.TenantGroup, lookups=True)
|
||||
class TenantGroupFilter(OrganizationalModelFilterMixin):
|
||||
class TenantGroupFilter(OrganizationalModelFilter):
|
||||
parent: Annotated['TenantGroupFilter', strawberry.lazy('tenancy.graphql.filters')] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
@@ -150,7 +147,7 @@ class TenantGroupFilter(OrganizationalModelFilterMixin):
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.Contact, lookups=True)
|
||||
class ContactFilter(PrimaryModelFilterMixin):
|
||||
class ContactFilter(PrimaryModelFilter):
|
||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
||||
title: FilterLookup[str] | None = strawberry_django.filter_field()
|
||||
phone: FilterLookup[str] | None = strawberry_django.filter_field()
|
||||
@@ -166,19 +163,19 @@ class ContactFilter(PrimaryModelFilterMixin):
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.ContactRole, lookups=True)
|
||||
class ContactRoleFilter(OrganizationalModelFilterMixin):
|
||||
class ContactRoleFilter(OrganizationalModelFilter):
|
||||
pass
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.ContactGroup, lookups=True)
|
||||
class ContactGroupFilter(NestedGroupModelFilterMixin):
|
||||
class ContactGroupFilter(NestedGroupModelFilter):
|
||||
parent: Annotated['ContactGroupFilter', strawberry.lazy('tenancy.graphql.filters')] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.ContactAssignment, lookups=True)
|
||||
class ContactAssignmentFilter(CustomFieldsFilterMixin, TagsFilterMixin, ChangeLogFilterMixin):
|
||||
class ContactAssignmentFilter(CustomFieldsFilterMixin, TagsFilterMixin, ChangeLoggedModelFilter):
|
||||
object_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import django_filters
|
||||
|
||||
from django.db.models import Q
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import strawberry
|
||||
import strawberry_django
|
||||
from strawberry_django import DatetimeFilterLookup, FilterLookup
|
||||
|
||||
from core.graphql.filter_mixins import BaseObjectTypeFilterMixin
|
||||
from netbox.graphql.filters import BaseModelFilter
|
||||
from users import models
|
||||
|
||||
__all__ = (
|
||||
@@ -17,13 +17,13 @@ __all__ = (
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.Group, lookups=True)
|
||||
class GroupFilter(BaseObjectTypeFilterMixin):
|
||||
class GroupFilter(BaseModelFilter):
|
||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
||||
description: FilterLookup[str] | None = strawberry_django.filter_field()
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.User, lookups=True)
|
||||
class UserFilter(BaseObjectTypeFilterMixin):
|
||||
class UserFilter(BaseModelFilter):
|
||||
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()
|
||||
@@ -36,7 +36,7 @@ class UserFilter(BaseObjectTypeFilterMixin):
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.Owner, lookups=True)
|
||||
class OwnerFilter(BaseObjectTypeFilterMixin):
|
||||
class OwnerFilter(BaseModelFilter):
|
||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
||||
description: FilterLookup[str] | None = strawberry_django.filter_field()
|
||||
group: Annotated['OwnerGroupFilter', strawberry.lazy('users.graphql.filters')] | None = (
|
||||
@@ -49,6 +49,6 @@ class OwnerFilter(BaseObjectTypeFilterMixin):
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.OwnerGroup, lookups=True)
|
||||
class OwnerGroupFilter(BaseObjectTypeFilterMixin):
|
||||
class OwnerGroupFilter(BaseModelFilter):
|
||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
||||
description: FilterLookup[str] | None = strawberry_django.filter_field()
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
from netbox.registry import registry
|
||||
|
||||
__all__ = (
|
||||
'register_filterset',
|
||||
)
|
||||
|
||||
|
||||
def register_filterset(filterset_class):
|
||||
"""
|
||||
|
||||
@@ -39,20 +39,20 @@ FORM_FIELD_LOOKUPS = {
|
||||
forms.IntegerField: [
|
||||
('exact', _('is')),
|
||||
('n', _('is not')),
|
||||
('gt', _('greater than (>)')),
|
||||
('gte', _('at least (≥)')),
|
||||
('lt', _('less than (<)')),
|
||||
('lte', _('at most (≤)')),
|
||||
('gt', _('greater than')),
|
||||
('gte', _('at least')),
|
||||
('lt', _('less than')),
|
||||
('lte', _('at most')),
|
||||
(MODIFIER_EMPTY_TRUE, _('is empty')),
|
||||
(MODIFIER_EMPTY_FALSE, _('is not empty')),
|
||||
],
|
||||
forms.DecimalField: [
|
||||
('exact', _('is')),
|
||||
('n', _('is not')),
|
||||
('gt', _('greater than (>)')),
|
||||
('gte', _('at least (≥)')),
|
||||
('lt', _('less than (<)')),
|
||||
('lte', _('at most (≤)')),
|
||||
('gt', _('greater than')),
|
||||
('gte', _('at least')),
|
||||
('lt', _('less than')),
|
||||
('lte', _('at most')),
|
||||
(MODIFIER_EMPTY_TRUE, _('is empty')),
|
||||
(MODIFIER_EMPTY_FALSE, _('is not empty')),
|
||||
],
|
||||
@@ -196,11 +196,11 @@ class FilterModifierMixin:
|
||||
if filterset:
|
||||
lookups = self._verify_lookups_with_filterset(field_name, lookups, filterset)
|
||||
|
||||
if len(lookups) > 1:
|
||||
field.widget = FilterModifierWidget(
|
||||
widget=field.widget,
|
||||
lookups=lookups
|
||||
)
|
||||
if len(lookups) > 1:
|
||||
field.widget = FilterModifierWidget(
|
||||
widget=field.widget,
|
||||
lookups=lookups
|
||||
)
|
||||
|
||||
def _get_lookup_choices(self, field):
|
||||
"""Determine the available lookup choices for a given field.
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
<div class="d-flex filter-modifier-group">
|
||||
{# Modifier dropdown - NO name attribute, just a UI control #}
|
||||
<select class="form-select modifier-select"
|
||||
data-field="{{ widget.field_name }}"
|
||||
data-empty-placeholder="{{ widget.empty_placeholder }}"
|
||||
aria-label="Modifier">
|
||||
{% for lookup, label in widget.lookups %}
|
||||
<option value="{{ lookup }}"{% if widget.current_modifier == lookup %} selected{% endif %}>{{ label }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
{% if widget.lookups %}
|
||||
{# Modifier dropdown - NO name attribute, just a UI control #}
|
||||
<select class="form-select modifier-select"
|
||||
data-field="{{ widget.field_name }}"
|
||||
data-empty-placeholder="{{ widget.empty_placeholder }}"
|
||||
aria-label="Modifier">
|
||||
{% for lookup, label in widget.lookups %}
|
||||
<option value="{{ lookup }}"{% if widget.current_modifier == lookup %} selected{% endif %}>{{ label }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
{% endif %}
|
||||
|
||||
{# Original widget - rendered exactly as it would be without our wrapper #}
|
||||
<div class="ms-2 flex-grow-1 filter-value-container">
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
from django import forms
|
||||
from django.db import models
|
||||
from django.http import QueryDict
|
||||
from django.template import Context
|
||||
from django.test import RequestFactory, TestCase
|
||||
|
||||
import dcim.filtersets # noqa: F401 - Import to register Device filterset
|
||||
from dcim.forms.filtersets import DeviceFilterForm
|
||||
from dcim.models import Device
|
||||
from netbox.filtersets import BaseFilterSet
|
||||
from utilities.filtersets import register_filterset
|
||||
from users.models import User
|
||||
from utilities.forms.fields import TagFilterField
|
||||
from utilities.forms.mixins import FilterModifierMixin
|
||||
@@ -12,6 +16,28 @@ from utilities.forms.widgets import FilterModifierWidget
|
||||
from utilities.templatetags.helpers import applied_filters
|
||||
|
||||
|
||||
# Test model for FilterModifierMixin tests
|
||||
class TestModel(models.Model):
|
||||
"""Dummy model for testing filter modifiers."""
|
||||
char_field = models.CharField(max_length=100, blank=True)
|
||||
integer_field = models.IntegerField(null=True, blank=True)
|
||||
decimal_field = models.DecimalField(max_digits=5, decimal_places=2, null=True, blank=True)
|
||||
date_field = models.DateField(null=True, blank=True)
|
||||
boolean_field = models.BooleanField(default=False)
|
||||
|
||||
class Meta:
|
||||
app_label = 'utilities'
|
||||
managed = False # Don't create actual database table
|
||||
|
||||
|
||||
# Test filterset using BaseFilterSet to automatically generate lookups
|
||||
@register_filterset
|
||||
class TestFilterSet(BaseFilterSet):
|
||||
class Meta:
|
||||
model = TestModel
|
||||
fields = ['char_field', 'integer_field', 'decimal_field', 'date_field', 'boolean_field']
|
||||
|
||||
|
||||
class FilterModifierWidgetTest(TestCase):
|
||||
"""Tests for FilterModifierWidget value extraction and rendering."""
|
||||
|
||||
@@ -85,10 +111,8 @@ class FilterModifierWidgetTest(TestCase):
|
||||
# Should contain modifier dropdown
|
||||
self.assertIn('class="form-select modifier-select"', html)
|
||||
self.assertIn('data-field="serial"', html)
|
||||
self.assertIn('value="exact"', html)
|
||||
self.assertIn('>Is</option>', html)
|
||||
self.assertIn('value="ic"', html)
|
||||
self.assertIn('>Contains</option>', html)
|
||||
self.assertIn('<option value="exact" selected>Is</option>', html)
|
||||
self.assertIn('<option value="ic">Contains</option>', html)
|
||||
|
||||
# Should contain original input
|
||||
self.assertIn('type="text"', html)
|
||||
@@ -102,127 +126,78 @@ class FilterModifierMixinTest(TestCase):
|
||||
def test_mixin_enhances_char_field_with_modifiers(self):
|
||||
"""CharField should be enhanced with contains/starts/ends modifiers."""
|
||||
class TestForm(FilterModifierMixin, forms.Form):
|
||||
name = forms.CharField(required=False)
|
||||
char_field = forms.CharField(required=False)
|
||||
model = TestModel
|
||||
|
||||
form = TestForm()
|
||||
|
||||
self.assertIsInstance(form.fields['name'].widget, FilterModifierWidget)
|
||||
self.assertGreater(len(form.fields['name'].widget.lookups), 1)
|
||||
# Should have exact, ic, isw, iew
|
||||
lookup_codes = [lookup[0] for lookup in form.fields['name'].widget.lookups]
|
||||
self.assertIn('exact', lookup_codes)
|
||||
self.assertIn('ic', lookup_codes)
|
||||
self.assertIsInstance(form.fields['char_field'].widget, FilterModifierWidget)
|
||||
lookup_codes = [lookup[0] for lookup in form.fields['char_field'].widget.lookups]
|
||||
expected_lookups = ['exact', 'n', 'ic', 'isw', 'iew', 'ie', 'regex', 'iregex', 'empty_true', 'empty_false']
|
||||
self.assertEqual(lookup_codes, expected_lookups)
|
||||
|
||||
def test_mixin_skips_boolean_fields(self):
|
||||
"""Boolean fields should not be enhanced."""
|
||||
class TestForm(FilterModifierMixin, forms.Form):
|
||||
active = forms.BooleanField(required=False)
|
||||
boolean_field = forms.BooleanField(required=False)
|
||||
model = TestModel
|
||||
|
||||
form = TestForm()
|
||||
|
||||
self.assertNotIsInstance(form.fields['active'].widget, FilterModifierWidget)
|
||||
self.assertNotIsInstance(form.fields['boolean_field'].widget, FilterModifierWidget)
|
||||
|
||||
def test_mixin_enhances_tag_filter_field(self):
|
||||
"""TagFilterField should be enhanced even though it's a MultipleChoiceField."""
|
||||
class TestForm(FilterModifierMixin, forms.Form):
|
||||
tag = TagFilterField(Device)
|
||||
model = Device
|
||||
|
||||
form = TestForm()
|
||||
|
||||
self.assertIsInstance(form.fields['tag'].widget, FilterModifierWidget)
|
||||
tag_lookups = [lookup[0] for lookup in form.fields['tag'].widget.lookups]
|
||||
self.assertIn('exact', tag_lookups)
|
||||
self.assertIn('n', tag_lookups)
|
||||
|
||||
def test_mixin_enhances_multi_choice_field(self):
|
||||
"""Plain MultipleChoiceField should be enhanced with choice-appropriate lookups."""
|
||||
class TestForm(FilterModifierMixin, forms.Form):
|
||||
status = forms.MultipleChoiceField(choices=[('a', 'A'), ('b', 'B')], required=False)
|
||||
|
||||
form = TestForm()
|
||||
|
||||
self.assertIsInstance(form.fields['status'].widget, FilterModifierWidget)
|
||||
status_lookups = [lookup[0] for lookup in form.fields['status'].widget.lookups]
|
||||
# Should have choice-based lookups (not text-based like contains/regex)
|
||||
self.assertIn('exact', status_lookups)
|
||||
self.assertIn('n', status_lookups)
|
||||
self.assertIn('empty_true', status_lookups)
|
||||
# Should NOT have text-based lookups
|
||||
self.assertNotIn('ic', status_lookups)
|
||||
self.assertNotIn('regex', status_lookups)
|
||||
# Device filterset has tag and tag__n but not tag__empty
|
||||
expected_lookups = ['exact', 'n']
|
||||
self.assertEqual(tag_lookups, expected_lookups)
|
||||
|
||||
def test_mixin_enhances_integer_field(self):
|
||||
"""IntegerField should be enhanced with comparison modifiers."""
|
||||
class TestForm(FilterModifierMixin, forms.Form):
|
||||
count = forms.IntegerField(required=False)
|
||||
integer_field = forms.IntegerField(required=False)
|
||||
model = TestModel
|
||||
|
||||
form = TestForm()
|
||||
|
||||
self.assertIsInstance(form.fields['count'].widget, FilterModifierWidget)
|
||||
lookup_codes = [lookup[0] for lookup in form.fields['count'].widget.lookups]
|
||||
self.assertIn('gte', lookup_codes)
|
||||
self.assertIn('lte', lookup_codes)
|
||||
self.assertIsInstance(form.fields['integer_field'].widget, FilterModifierWidget)
|
||||
lookup_codes = [lookup[0] for lookup in form.fields['integer_field'].widget.lookups]
|
||||
expected_lookups = ['exact', 'n', 'gt', 'gte', 'lt', 'lte', 'empty_true', 'empty_false']
|
||||
self.assertEqual(lookup_codes, expected_lookups)
|
||||
|
||||
def test_mixin_adds_isnull_lookup_to_all_fields(self):
|
||||
"""All field types should include isnull (empty/not empty) lookup."""
|
||||
def test_mixin_enhances_decimal_field(self):
|
||||
"""DecimalField should be enhanced with comparison modifiers."""
|
||||
class TestForm(FilterModifierMixin, forms.Form):
|
||||
name = forms.CharField(required=False)
|
||||
count = forms.IntegerField(required=False)
|
||||
created = forms.DateField(required=False)
|
||||
decimal_field = forms.DecimalField(required=False)
|
||||
model = TestModel
|
||||
|
||||
form = TestForm()
|
||||
|
||||
# CharField should have empty_true and empty_false
|
||||
char_lookups = [lookup[0] for lookup in form.fields['name'].widget.lookups]
|
||||
self.assertIn('empty_true', char_lookups)
|
||||
self.assertIn('empty_false', char_lookups)
|
||||
self.assertIsInstance(form.fields['decimal_field'].widget, FilterModifierWidget)
|
||||
lookup_codes = [lookup[0] for lookup in form.fields['decimal_field'].widget.lookups]
|
||||
expected_lookups = ['exact', 'n', 'gt', 'gte', 'lt', 'lte', 'empty_true', 'empty_false']
|
||||
self.assertEqual(lookup_codes, expected_lookups)
|
||||
|
||||
# IntegerField should have empty_true and empty_false
|
||||
int_lookups = [lookup[0] for lookup in form.fields['count'].widget.lookups]
|
||||
self.assertIn('empty_true', int_lookups)
|
||||
self.assertIn('empty_false', int_lookups)
|
||||
|
||||
# DateField should have empty_true and empty_false
|
||||
date_lookups = [lookup[0] for lookup in form.fields['created'].widget.lookups]
|
||||
self.assertIn('empty_true', date_lookups)
|
||||
self.assertIn('empty_false', date_lookups)
|
||||
|
||||
def test_char_field_includes_extended_lookups(self):
|
||||
"""CharField should include negation, iexact, and regex lookups."""
|
||||
def test_mixin_enhances_date_field(self):
|
||||
"""DateField should be enhanced with date-appropriate modifiers."""
|
||||
class TestForm(FilterModifierMixin, forms.Form):
|
||||
name = forms.CharField(required=False)
|
||||
date_field = forms.DateField(required=False)
|
||||
model = TestModel
|
||||
|
||||
form = TestForm()
|
||||
|
||||
char_lookups = [lookup[0] for lookup in form.fields['name'].widget.lookups]
|
||||
self.assertIn('n', char_lookups) # negation
|
||||
self.assertIn('ie', char_lookups) # iexact
|
||||
self.assertIn('regex', char_lookups) # regex
|
||||
self.assertIn('iregex', char_lookups) # case-insensitive regex
|
||||
|
||||
def test_numeric_fields_include_negation(self):
|
||||
"""IntegerField and DecimalField should include negation lookup."""
|
||||
class TestForm(FilterModifierMixin, forms.Form):
|
||||
count = forms.IntegerField(required=False)
|
||||
weight = forms.DecimalField(required=False)
|
||||
|
||||
form = TestForm()
|
||||
|
||||
int_lookups = [lookup[0] for lookup in form.fields['count'].widget.lookups]
|
||||
self.assertIn('n', int_lookups)
|
||||
|
||||
decimal_lookups = [lookup[0] for lookup in form.fields['weight'].widget.lookups]
|
||||
self.assertIn('n', decimal_lookups)
|
||||
|
||||
def test_date_field_includes_negation(self):
|
||||
"""DateField should include negation lookup."""
|
||||
class TestForm(FilterModifierMixin, forms.Form):
|
||||
created = forms.DateField(required=False)
|
||||
|
||||
form = TestForm()
|
||||
|
||||
date_lookups = [lookup[0] for lookup in form.fields['created'].widget.lookups]
|
||||
self.assertIn('n', date_lookups)
|
||||
self.assertIsInstance(form.fields['date_field'].widget, FilterModifierWidget)
|
||||
lookup_codes = [lookup[0] for lookup in form.fields['date_field'].widget.lookups]
|
||||
expected_lookups = ['exact', 'n', 'gt', 'gte', 'lt', 'lte', 'empty_true', 'empty_false']
|
||||
self.assertEqual(lookup_codes, expected_lookups)
|
||||
|
||||
|
||||
class ExtendedLookupFilterPillsTest(TestCase):
|
||||
@@ -261,6 +236,26 @@ class ExtendedLookupFilterPillsTest(TestCase):
|
||||
filter_pill = result['applied_filters'][0]
|
||||
self.assertIn('matches pattern', filter_pill['link_text'].lower())
|
||||
|
||||
def test_exact_lookup_filter_pill(self):
|
||||
"""Filter pill should show field label and value without lookup modifier for exact match."""
|
||||
query_params = QueryDict('serial=ABC123')
|
||||
form = DeviceFilterForm(query_params)
|
||||
|
||||
request = RequestFactory().get('/', query_params)
|
||||
request.user = self.user
|
||||
context = Context({'request': request})
|
||||
result = applied_filters(context, Device, form, query_params)
|
||||
|
||||
self.assertGreater(len(result['applied_filters']), 0)
|
||||
filter_pill = result['applied_filters'][0]
|
||||
# Should not contain lookup modifier text
|
||||
self.assertNotIn('is not', filter_pill['link_text'].lower())
|
||||
self.assertNotIn('matches pattern', filter_pill['link_text'].lower())
|
||||
self.assertNotIn('contains', filter_pill['link_text'].lower())
|
||||
# Should contain field label and value
|
||||
self.assertIn('Serial', filter_pill['link_text'])
|
||||
self.assertIn('ABC123', filter_pill['link_text'])
|
||||
|
||||
|
||||
class EmptyLookupTest(TestCase):
|
||||
"""Tests for empty (is empty/not empty) lookup support."""
|
||||
|
||||
@@ -11,7 +11,6 @@ from extras.models import ConfigTemplate
|
||||
from ipam.filtersets import PrimaryIPFilterSet
|
||||
from netbox.filtersets import NetBoxModelFilterSet, OrganizationalModelFilterSet, PrimaryModelFilterSet
|
||||
from tenancy.filtersets import TenancyFilterSet, ContactModelFilterSet
|
||||
|
||||
from users.filterset_mixins import OwnerFilterMixin
|
||||
from utilities.filters import MultiValueCharFilter, MultiValueMACAddressFilter, TreeNodeMultipleChoiceFilter
|
||||
from utilities.filtersets import register_filterset
|
||||
|
||||
@@ -3,11 +3,9 @@ from typing import Annotated, TYPE_CHECKING
|
||||
|
||||
import strawberry
|
||||
import strawberry_django
|
||||
from strawberry import ID
|
||||
from strawberry.scalars import ID
|
||||
from strawberry_django import FilterLookup
|
||||
|
||||
from netbox.graphql.filter_mixins import NetBoxModelFilterMixin
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .filters import VirtualMachineFilter
|
||||
|
||||
@@ -17,7 +15,7 @@ __all__ = (
|
||||
|
||||
|
||||
@dataclass
|
||||
class VMComponentFilterMixin(NetBoxModelFilterMixin):
|
||||
class VMComponentFilterMixin:
|
||||
virtual_machine: Annotated['VirtualMachineFilter', strawberry.lazy('virtualization.graphql.filters')] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
|
||||
@@ -7,11 +7,8 @@ from strawberry_django import BaseFilterLookup, 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 netbox.graphql.filter_mixins import ImageAttachmentFilterMixin
|
||||
from netbox.graphql.filters import NetBoxModelFilter, OrganizationalModelFilter, PrimaryModelFilter
|
||||
from tenancy.graphql.filter_mixins import ContactFilterMixin, TenancyFilterMixin
|
||||
from virtualization import models
|
||||
from virtualization.graphql.filter_mixins import VMComponentFilterMixin
|
||||
@@ -40,7 +37,7 @@ __all__ = (
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.Cluster, lookups=True)
|
||||
class ClusterFilter(ContactFilterMixin, ScopedFilterMixin, TenancyFilterMixin, PrimaryModelFilterMixin):
|
||||
class ClusterFilter(ContactFilterMixin, ScopedFilterMixin, TenancyFilterMixin, PrimaryModelFilter):
|
||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
||||
type: Annotated['ClusterTypeFilter', strawberry.lazy('virtualization.graphql.filters')] | None = (
|
||||
strawberry_django.filter_field()
|
||||
@@ -59,14 +56,14 @@ class ClusterFilter(ContactFilterMixin, ScopedFilterMixin, TenancyFilterMixin, P
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.ClusterGroup, lookups=True)
|
||||
class ClusterGroupFilter(ContactFilterMixin, OrganizationalModelFilterMixin):
|
||||
class ClusterGroupFilter(ContactFilterMixin, OrganizationalModelFilter):
|
||||
vlan_groups: Annotated['VLANGroupFilter', strawberry.lazy('ipam.graphql.filters')] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.ClusterType, lookups=True)
|
||||
class ClusterTypeFilter(OrganizationalModelFilterMixin):
|
||||
class ClusterTypeFilter(OrganizationalModelFilter):
|
||||
pass
|
||||
|
||||
|
||||
@@ -77,7 +74,7 @@ class VirtualMachineFilter(
|
||||
RenderConfigFilterMixin,
|
||||
ConfigContextFilterMixin,
|
||||
TenancyFilterMixin,
|
||||
PrimaryModelFilterMixin,
|
||||
PrimaryModelFilter,
|
||||
):
|
||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
||||
site: Annotated['SiteFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field()
|
||||
@@ -138,7 +135,7 @@ class VirtualMachineFilter(
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.VMInterface, lookups=True)
|
||||
class VMInterfaceFilter(VMComponentFilterMixin, InterfaceBaseFilterMixin):
|
||||
class VMInterfaceFilter(InterfaceBaseFilterMixin, VMComponentFilterMixin, NetBoxModelFilter):
|
||||
ip_addresses: Annotated['IPAddressFilter', strawberry.lazy('ipam.graphql.filters')] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
@@ -163,7 +160,7 @@ class VMInterfaceFilter(VMComponentFilterMixin, InterfaceBaseFilterMixin):
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.VirtualDisk, lookups=True)
|
||||
class VirtualDiskFilter(VMComponentFilterMixin):
|
||||
class VirtualDiskFilter(VMComponentFilterMixin, NetBoxModelFilter):
|
||||
size: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
|
||||
@@ -5,9 +5,10 @@ import strawberry_django
|
||||
from strawberry.scalars import ID
|
||||
from strawberry_django import BaseFilterLookup, 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 netbox.graphql.filters import (
|
||||
ChangeLoggedModelFilter, NetBoxModelFilter, OrganizationalModelFilter, PrimaryModelFilter,
|
||||
)
|
||||
from tenancy.graphql.filter_mixins import ContactFilterMixin, TenancyFilterMixin
|
||||
from vpn import models
|
||||
|
||||
@@ -32,14 +33,12 @@ __all__ = (
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.TunnelGroup, lookups=True)
|
||||
class TunnelGroupFilter(OrganizationalModelFilterMixin):
|
||||
class TunnelGroupFilter(OrganizationalModelFilter):
|
||||
pass
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.TunnelTermination, lookups=True)
|
||||
class TunnelTerminationFilter(
|
||||
BaseObjectTypeFilterMixin, CustomFieldsFilterMixin, TagsFilterMixin, ChangeLogFilterMixin
|
||||
):
|
||||
class TunnelTerminationFilter(CustomFieldsFilterMixin, TagsFilterMixin, ChangeLoggedModelFilter):
|
||||
tunnel: Annotated['TunnelFilter', strawberry.lazy('vpn.graphql.filters')] | None = strawberry_django.filter_field()
|
||||
tunnel_id: ID | None = strawberry_django.filter_field()
|
||||
role: BaseFilterLookup[Annotated['TunnelTerminationRoleEnum', strawberry.lazy('vpn.graphql.enums')]] | None = (
|
||||
@@ -59,7 +58,7 @@ class TunnelTerminationFilter(
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.Tunnel, lookups=True)
|
||||
class TunnelFilter(TenancyFilterMixin, PrimaryModelFilterMixin):
|
||||
class TunnelFilter(TenancyFilterMixin, PrimaryModelFilter):
|
||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
||||
status: BaseFilterLookup[Annotated['TunnelStatusEnum', strawberry.lazy('vpn.graphql.enums')]] | None = (
|
||||
strawberry_django.filter_field()
|
||||
@@ -85,7 +84,7 @@ class TunnelFilter(TenancyFilterMixin, PrimaryModelFilterMixin):
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.IKEProposal, lookups=True)
|
||||
class IKEProposalFilter(PrimaryModelFilterMixin):
|
||||
class IKEProposalFilter(PrimaryModelFilter):
|
||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
||||
authentication_method: (
|
||||
BaseFilterLookup[Annotated['AuthenticationMethodEnum', strawberry.lazy('vpn.graphql.enums')]] | None
|
||||
@@ -114,7 +113,7 @@ class IKEProposalFilter(PrimaryModelFilterMixin):
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.IKEPolicy, lookups=True)
|
||||
class IKEPolicyFilter(PrimaryModelFilterMixin):
|
||||
class IKEPolicyFilter(PrimaryModelFilter):
|
||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
||||
version: BaseFilterLookup[Annotated['IKEVersionEnum', strawberry.lazy('vpn.graphql.enums')]] | None = (
|
||||
strawberry_django.filter_field()
|
||||
@@ -129,7 +128,7 @@ class IKEPolicyFilter(PrimaryModelFilterMixin):
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.IPSecProposal, lookups=True)
|
||||
class IPSecProposalFilter(PrimaryModelFilterMixin):
|
||||
class IPSecProposalFilter(PrimaryModelFilter):
|
||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
||||
encryption_algorithm: (
|
||||
BaseFilterLookup[Annotated['EncryptionAlgorithmEnum', strawberry.lazy('vpn.graphql.enums')]] | None
|
||||
@@ -155,7 +154,7 @@ class IPSecProposalFilter(PrimaryModelFilterMixin):
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.IPSecPolicy, lookups=True)
|
||||
class IPSecPolicyFilter(PrimaryModelFilterMixin):
|
||||
class IPSecPolicyFilter(PrimaryModelFilter):
|
||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
||||
proposals: Annotated['IPSecProposalFilter', strawberry.lazy('vpn.graphql.filters')] | None = (
|
||||
strawberry_django.filter_field()
|
||||
@@ -166,7 +165,7 @@ class IPSecPolicyFilter(PrimaryModelFilterMixin):
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.IPSecProfile, lookups=True)
|
||||
class IPSecProfileFilter(PrimaryModelFilterMixin):
|
||||
class IPSecProfileFilter(PrimaryModelFilter):
|
||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
||||
mode: BaseFilterLookup[Annotated['IPSecModeEnum', strawberry.lazy('vpn.graphql.enums')]] | None = (
|
||||
strawberry_django.filter_field()
|
||||
@@ -182,7 +181,7 @@ class IPSecProfileFilter(PrimaryModelFilterMixin):
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.L2VPN, lookups=True)
|
||||
class L2VPNFilter(ContactFilterMixin, TenancyFilterMixin, PrimaryModelFilterMixin):
|
||||
class L2VPNFilter(ContactFilterMixin, TenancyFilterMixin, PrimaryModelFilter):
|
||||
name: FilterLookup[str] | None = strawberry_django.filter_field()
|
||||
slug: FilterLookup[str] | None = strawberry_django.filter_field()
|
||||
type: BaseFilterLookup[Annotated['L2VPNTypeEnum', strawberry.lazy('vpn.graphql.enums')]] | None = (
|
||||
@@ -206,7 +205,7 @@ class L2VPNFilter(ContactFilterMixin, TenancyFilterMixin, PrimaryModelFilterMixi
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.L2VPNTermination, lookups=True)
|
||||
class L2VPNTerminationFilter(NetBoxModelFilterMixin):
|
||||
class L2VPNTerminationFilter(NetBoxModelFilter):
|
||||
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 = (
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import django_filters
|
||||
from django.db.models import Q
|
||||
|
||||
from dcim.choices import LinkStatusChoices
|
||||
from dcim.base_filtersets import ScopedFilterSet
|
||||
from dcim.choices import LinkStatusChoices
|
||||
from dcim.models import Interface
|
||||
from ipam.models import VLAN
|
||||
from netbox.filtersets import NestedGroupModelFilterSet, PrimaryModelFilterSet
|
||||
|
||||
@@ -5,18 +5,16 @@ import strawberry
|
||||
import strawberry_django
|
||||
from strawberry_django import FilterLookup
|
||||
|
||||
from core.graphql.filter_mixins import BaseFilterMixin
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .enums import *
|
||||
|
||||
__all__ = (
|
||||
'WirelessAuthenticationBaseFilterMixin',
|
||||
'WirelessAuthenticationFilterMixin',
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class WirelessAuthenticationBaseFilterMixin(BaseFilterMixin):
|
||||
class WirelessAuthenticationFilterMixin:
|
||||
auth_type: Annotated['WirelessAuthTypeEnum', strawberry.lazy('wireless.graphql.enums')] | None = (
|
||||
strawberry_django.filter_field()
|
||||
)
|
||||
|
||||
@@ -6,10 +6,11 @@ from strawberry.scalars import ID
|
||||
from strawberry_django import BaseFilterLookup, FilterLookup
|
||||
|
||||
from dcim.graphql.filter_mixins import ScopedFilterMixin
|
||||
from netbox.graphql.filter_mixins import DistanceFilterMixin, PrimaryModelFilterMixin, NestedGroupModelFilterMixin
|
||||
from netbox.graphql.filter_mixins import DistanceFilterMixin
|
||||
from netbox.graphql.filters import PrimaryModelFilter, NestedGroupModelFilter
|
||||
from tenancy.graphql.filter_mixins import TenancyFilterMixin
|
||||
from wireless import models
|
||||
from .filter_mixins import WirelessAuthenticationBaseFilterMixin
|
||||
from .filter_mixins import WirelessAuthenticationFilterMixin
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from dcim.graphql.filters import InterfaceFilter
|
||||
@@ -24,16 +25,16 @@ __all__ = (
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.WirelessLANGroup, lookups=True)
|
||||
class WirelessLANGroupFilter(NestedGroupModelFilterMixin):
|
||||
class WirelessLANGroupFilter(NestedGroupModelFilter):
|
||||
pass
|
||||
|
||||
|
||||
@strawberry_django.filter_type(models.WirelessLAN, lookups=True)
|
||||
class WirelessLANFilter(
|
||||
WirelessAuthenticationBaseFilterMixin,
|
||||
WirelessAuthenticationFilterMixin,
|
||||
ScopedFilterMixin,
|
||||
TenancyFilterMixin,
|
||||
PrimaryModelFilterMixin
|
||||
PrimaryModelFilter
|
||||
):
|
||||
ssid: FilterLookup[str] | None = strawberry_django.filter_field()
|
||||
status: BaseFilterLookup[Annotated['WirelessLANStatusEnum', strawberry.lazy('wireless.graphql.enums')]] | None = (
|
||||
@@ -49,10 +50,10 @@ class WirelessLANFilter(
|
||||
|
||||
@strawberry_django.filter_type(models.WirelessLink, lookups=True)
|
||||
class WirelessLinkFilter(
|
||||
WirelessAuthenticationBaseFilterMixin,
|
||||
WirelessAuthenticationFilterMixin,
|
||||
DistanceFilterMixin,
|
||||
TenancyFilterMixin,
|
||||
PrimaryModelFilterMixin
|
||||
PrimaryModelFilter
|
||||
):
|
||||
interface_a: Annotated['InterfaceFilter', strawberry.lazy('dcim.graphql.filters')] | None = (
|
||||
strawberry_django.filter_field()
|
||||
|
||||
Reference in New Issue
Block a user