Просмотр исходного кода

Clone all GraphQL objects to V1 versions

Brian Tiemann 3 месяцев назад
Родитель
Сommit
867a01fae5
47 измененных файлов с 6423 добавлено и 10 удалено
  1. 19 0
      netbox/circuits/graphql/filter_mixins_v1.py
  2. 228 0
      netbox/circuits/graphql/filters_v1.py
  3. 42 0
      netbox/circuits/graphql/schema_v1.py
  4. 189 0
      netbox/circuits/graphql/types_v1.py
  5. 36 0
      netbox/core/graphql/filter_mixins_v1.py
  6. 89 0
      netbox/core/graphql/filters_v1.py
  7. 35 0
      netbox/core/graphql/mixins_v1.py
  8. 15 0
      netbox/core/graphql/schema_v1.py
  9. 56 0
      netbox/core/graphql/types_v1.py
  10. 155 0
      netbox/dcim/graphql/filter_mixins_v1.py
  11. 1020 0
      netbox/dcim/graphql/filters_v1.py
  12. 137 0
      netbox/dcim/graphql/gfk_mixins_v1.py
  13. 43 0
      netbox/dcim/graphql/mixins_v1.py
  14. 138 0
      netbox/dcim/graphql/schema_v1.py
  15. 906 0
      netbox/dcim/graphql/types_v1.py
  16. 52 0
      netbox/extras/graphql/filter_mixins_v1.py
  17. 357 0
      netbox/extras/graphql/filters_v1.py
  18. 62 0
      netbox/extras/graphql/mixins_v1.py
  19. 60 0
      netbox/extras/graphql/schema_v1.py
  20. 239 0
      netbox/extras/graphql/types_v1.py
  21. 25 0
      netbox/ipam/graphql/filter_mixins_v1.py
  22. 392 0
      netbox/ipam/graphql/filters_v1.py
  23. 18 0
      netbox/ipam/graphql/mixins_v1.py
  24. 63 0
      netbox/ipam/graphql/schema_v1.py
  25. 360 0
      netbox/ipam/graphql/types_v1.py
  26. 104 0
      netbox/netbox/graphql/filter_mixins_v1.py
  27. 20 10
      netbox/netbox/graphql/schema.py
  28. 100 0
      netbox/netbox/graphql/types_v1.py
  29. 38 0
      netbox/tenancy/graphql/filter_mixins_v1.py
  30. 210 0
      netbox/tenancy/graphql/filters_v1.py
  31. 12 0
      netbox/tenancy/graphql/mixins_v1.py
  32. 27 0
      netbox/tenancy/graphql/schema_v1.py
  33. 147 0
      netbox/tenancy/graphql/types_v1.py
  34. 34 0
      netbox/users/graphql/filters_v1.py
  35. 15 0
      netbox/users/graphql/schema_v1.py
  36. 34 0
      netbox/users/graphql/types_v1.py
  37. 28 0
      netbox/virtualization/graphql/filter_mixins_v1.py
  38. 170 0
      netbox/virtualization/graphql/filters_v1.py
  39. 27 0
      netbox/virtualization/graphql/schema_v1.py
  40. 146 0
      netbox/virtualization/graphql/types_v1.py
  41. 192 0
      netbox/vpn/graphql/filters_v1.py
  42. 39 0
      netbox/vpn/graphql/schema_v1.py
  43. 157 0
      netbox/vpn/graphql/types_v1.py
  44. 26 0
      netbox/wireless/graphql/filter_mixins_v1.py
  45. 72 0
      netbox/wireless/graphql/filters_v1.py
  46. 18 0
      netbox/wireless/graphql/schema_v1.py
  47. 71 0
      netbox/wireless/graphql/types_v1.py

+ 19 - 0
netbox/circuits/graphql/filter_mixins_v1.py

@@ -0,0 +1,19 @@
+from dataclasses import dataclass
+from typing import Annotated, TYPE_CHECKING
+
+import strawberry
+import strawberry_django
+
+from netbox.graphql.filter_mixins_v1 import OrganizationalModelFilterMixinV1
+
+if TYPE_CHECKING:
+    from netbox.graphql.enums import ColorEnum
+
+__all__ = (
+    'BaseCircuitTypeFilterMixinV1',
+)
+
+
+@dataclass
+class BaseCircuitTypeFilterMixinV1(OrganizationalModelFilterMixinV1):
+    color: Annotated['ColorEnum', strawberry.lazy('netbox.graphql.enums')] | None = strawberry_django.filter_field()

+ 228 - 0
netbox/circuits/graphql/filters_v1.py

@@ -0,0 +1,228 @@
+from datetime import date
+from typing import Annotated, TYPE_CHECKING
+
+import strawberry
+import strawberry_django
+from strawberry.scalars import ID
+from strawberry_django import FilterLookup, DateFilterLookup
+
+from circuits import models
+from core.graphql.filter_mixins_v1 import BaseObjectTypeFilterMixinV1, ChangeLogFilterMixinV1
+from dcim.graphql.filter_mixins_v1 import CabledObjectModelFilterMixinV1
+from extras.graphql.filter_mixins_v1 import CustomFieldsFilterMixinV1, TagsFilterMixinV1
+from netbox.graphql.filter_mixins_v1 import (
+    DistanceFilterMixinV1,
+    ImageAttachmentFilterMixinV1,
+    OrganizationalModelFilterMixinV1,
+    PrimaryModelFilterMixinV1,
+)
+from tenancy.graphql.filter_mixins_v1 import ContactFilterMixinV1, TenancyFilterMixinV1
+from .filter_mixins_v1 import BaseCircuitTypeFilterMixinV1
+
+if TYPE_CHECKING:
+    from core.graphql.filters_v1 import ContentTypeFilterV1
+    from dcim.graphql.filters_v1 import (
+        InterfaceFilterV1, LocationFilterV1, RegionFilterV1, SiteFilterV1, SiteGroupFilterV1
+    )
+    from ipam.graphql.filters_v1 import ASNFilterV1
+    from netbox.graphql.filter_lookups import IntegerLookup
+    from .enums import *
+
+__all__ = (
+    'CircuitFilterV1',
+    'CircuitGroupAssignmentFilterV1',
+    'CircuitGroupFilterV1',
+    'CircuitTerminationFilterV1',
+    'CircuitTypeFilterV1',
+    'ProviderFilterV1',
+    'ProviderAccountFilterV1',
+    'ProviderNetworkFilterV1',
+    'VirtualCircuitFilterV1',
+    'VirtualCircuitTerminationFilterV1',
+    'VirtualCircuitTypeFilterV1',
+)
+
+
+@strawberry_django.filter_type(models.CircuitTermination, lookups=True)
+class CircuitTerminationFilterV1(
+    BaseObjectTypeFilterMixinV1,
+    CustomFieldsFilterMixinV1,
+    TagsFilterMixinV1,
+    ChangeLogFilterMixinV1,
+    CabledObjectModelFilterMixinV1,
+):
+    circuit: Annotated['CircuitFilterV1', strawberry.lazy('circuits.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    term_side: Annotated['CircuitTerminationSideEnum', strawberry.lazy('circuits.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    termination_type: Annotated['ContentTypeFilterV1', strawberry.lazy('core.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    termination_id: ID | None = strawberry_django.filter_field()
+    port_speed: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    upstream_speed: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    xconnect_id: FilterLookup[str] | None = strawberry_django.filter_field()
+    pp_info: FilterLookup[str] | None = strawberry_django.filter_field()
+    description: FilterLookup[str] | None = strawberry_django.filter_field()
+
+    # Cached relations
+    _provider_network: Annotated['ProviderNetworkFilterV1', strawberry.lazy('circuits.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field(name='provider_network')
+    )
+    _location: Annotated['LocationFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field(name='location')
+    )
+    _region: Annotated['RegionFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field(name='region')
+    )
+    _site_group: Annotated['SiteGroupFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field(name='site_group')
+    )
+    _site: Annotated['SiteFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field(name='site')
+    )
+
+
+@strawberry_django.filter_type(models.Circuit, lookups=True)
+class CircuitFilterV1(
+    ContactFilterMixinV1,
+    ImageAttachmentFilterMixinV1,
+    DistanceFilterMixinV1,
+    TenancyFilterMixinV1,
+    PrimaryModelFilterMixinV1
+):
+    cid: FilterLookup[str] | None = strawberry_django.filter_field()
+    provider: Annotated['ProviderFilterV1', strawberry.lazy('circuits.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    provider_id: ID | None = strawberry_django.filter_field()
+    provider_account: Annotated['ProviderAccountFilterV1', strawberry.lazy('circuits.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    provider_account_id: ID | None = strawberry_django.filter_field()
+    type: Annotated['CircuitTypeFilterV1', strawberry.lazy('circuits.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    type_id: ID | None = strawberry_django.filter_field()
+    status: Annotated['CircuitStatusEnum', strawberry.lazy('circuits.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    install_date: DateFilterLookup[date] | None = strawberry_django.filter_field()
+    termination_date: DateFilterLookup[date] | None = strawberry_django.filter_field()
+    commit_rate: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    terminations: Annotated['CircuitTerminationFilterV1', strawberry.lazy('circuits.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@strawberry_django.filter_type(models.CircuitType, lookups=True)
+class CircuitTypeFilterV1(BaseCircuitTypeFilterMixinV1):
+    pass
+
+
+@strawberry_django.filter_type(models.CircuitGroup, lookups=True)
+class CircuitGroupFilterV1(TenancyFilterMixinV1, OrganizationalModelFilterMixinV1):
+    pass
+
+
+@strawberry_django.filter_type(models.CircuitGroupAssignment, lookups=True)
+class CircuitGroupAssignmentFilterV1(
+    BaseObjectTypeFilterMixinV1, CustomFieldsFilterMixinV1, TagsFilterMixinV1, ChangeLogFilterMixinV1
+):
+    member_type: Annotated['ContentTypeFilterV1', strawberry.lazy('core.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    member_id: ID | None = strawberry_django.filter_field()
+    group: Annotated['CircuitGroupFilterV1', strawberry.lazy('circuits.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    group_id: ID | None = strawberry_django.filter_field()
+    priority: Annotated['CircuitPriorityEnum', strawberry.lazy('circuits.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@strawberry_django.filter_type(models.Provider, lookups=True)
+class ProviderFilterV1(ContactFilterMixinV1, PrimaryModelFilterMixinV1):
+    name: FilterLookup[str] | None = strawberry_django.filter_field()
+    slug: FilterLookup[str] | None = strawberry_django.filter_field()
+    asns: Annotated['ASNFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = strawberry_django.filter_field()
+    circuits: Annotated['CircuitFilterV1', strawberry.lazy('circuits.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@strawberry_django.filter_type(models.ProviderAccount, lookups=True)
+class ProviderAccountFilterV1(ContactFilterMixinV1, PrimaryModelFilterMixinV1):
+    provider: Annotated['ProviderFilterV1', strawberry.lazy('circuits.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    provider_id: ID | None = strawberry_django.filter_field()
+    account: FilterLookup[str] | None = strawberry_django.filter_field()
+    name: FilterLookup[str] | None = strawberry_django.filter_field()
+
+
+@strawberry_django.filter_type(models.ProviderNetwork, lookups=True)
+class ProviderNetworkFilterV1(PrimaryModelFilterMixinV1):
+    name: FilterLookup[str] | None = strawberry_django.filter_field()
+    provider: Annotated['ProviderFilterV1', strawberry.lazy('circuits.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    provider_id: ID | None = strawberry_django.filter_field()
+    service_id: FilterLookup[str] | None = strawberry_django.filter_field()
+
+
+@strawberry_django.filter_type(models.VirtualCircuitType, lookups=True)
+class VirtualCircuitTypeFilterV1(BaseCircuitTypeFilterMixinV1):
+    pass
+
+
+@strawberry_django.filter_type(models.VirtualCircuit, lookups=True)
+class VirtualCircuitFilterV1(TenancyFilterMixinV1, PrimaryModelFilterMixinV1):
+    cid: FilterLookup[str] | None = strawberry_django.filter_field()
+    provider_network: Annotated['ProviderNetworkFilterV1', strawberry.lazy('circuits.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    provider_network_id: ID | None = strawberry_django.filter_field()
+    provider_account: Annotated['ProviderAccountFilterV1', strawberry.lazy('circuits.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    provider_account_id: ID | None = strawberry_django.filter_field()
+    type: Annotated['VirtualCircuitTypeFilterV1', strawberry.lazy('circuits.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    type_id: ID | None = strawberry_django.filter_field()
+    status: Annotated['CircuitStatusEnum', strawberry.lazy('circuits.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    group_assignments: Annotated[
+        'CircuitGroupAssignmentFilterV1', strawberry.lazy('circuits.graphql.filters_v1')
+    ] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@strawberry_django.filter_type(models.VirtualCircuitTermination, lookups=True)
+class VirtualCircuitTerminationFilterV1(
+    BaseObjectTypeFilterMixinV1, CustomFieldsFilterMixinV1, TagsFilterMixinV1, ChangeLogFilterMixinV1
+):
+    virtual_circuit: Annotated['VirtualCircuitFilterV1', strawberry.lazy('circuits.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    virtual_circuit_id: ID | None = strawberry_django.filter_field()
+    role: Annotated['VirtualCircuitTerminationRoleEnum', strawberry.lazy('circuits.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    interface: Annotated['InterfaceFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    interface_id: ID | None = strawberry_django.filter_field()
+    description: FilterLookup[str] | None = strawberry_django.filter_field()

+ 42 - 0
netbox/circuits/graphql/schema_v1.py

@@ -0,0 +1,42 @@
+from typing import List
+
+import strawberry
+import strawberry_django
+
+from .types_v1 import *
+
+
+@strawberry.type(name="Query")
+class CircuitsQueryV1:
+    circuit: CircuitTypeV1 = strawberry_django.field()
+    circuit_list: List[CircuitTypeV1] = strawberry_django.field()
+
+    circuit_termination: CircuitTerminationTypeV1 = strawberry_django.field()
+    circuit_termination_list: List[CircuitTerminationTypeV1] = strawberry_django.field()
+
+    circuit_type: CircuitTypeTypeV1 = strawberry_django.field()
+    circuit_type_list: List[CircuitTypeTypeV1] = strawberry_django.field()
+
+    circuit_group: CircuitGroupTypeV1 = strawberry_django.field()
+    circuit_group_list: List[CircuitGroupTypeV1] = strawberry_django.field()
+
+    circuit_group_assignment: CircuitGroupAssignmentTypeV1 = strawberry_django.field()
+    circuit_group_assignment_list: List[CircuitGroupAssignmentTypeV1] = strawberry_django.field()
+
+    provider: ProviderTypeV1 = strawberry_django.field()
+    provider_list: List[ProviderTypeV1] = strawberry_django.field()
+
+    provider_account: ProviderAccountTypeV1 = strawberry_django.field()
+    provider_account_list: List[ProviderAccountTypeV1] = strawberry_django.field()
+
+    provider_network: ProviderNetworkTypeV1 = strawberry_django.field()
+    provider_network_list: List[ProviderNetworkTypeV1] = strawberry_django.field()
+
+    virtual_circuit: VirtualCircuitTypeV1 = strawberry_django.field()
+    virtual_circuit_list: List[VirtualCircuitTypeV1] = strawberry_django.field()
+
+    virtual_circuit_termination: VirtualCircuitTerminationTypeV1 = strawberry_django.field()
+    virtual_circuit_termination_list: List[VirtualCircuitTerminationTypeV1] = strawberry_django.field()
+
+    virtual_circuit_type: VirtualCircuitTypeTypeV1 = strawberry_django.field()
+    virtual_circuit_type_list: List[VirtualCircuitTypeTypeV1] = strawberry_django.field()

+ 189 - 0
netbox/circuits/graphql/types_v1.py

@@ -0,0 +1,189 @@
+from typing import Annotated, List, TYPE_CHECKING, Union
+
+import strawberry
+import strawberry_django
+
+from circuits import models
+from dcim.graphql.mixins_v1 import CabledObjectMixinV1
+from extras.graphql.mixins_v1 import ContactsMixinV1, CustomFieldsMixinV1, TagsMixinV1
+from netbox.graphql.types_v1 import BaseObjectTypeV1, NetBoxObjectTypeV1, ObjectTypeV1, OrganizationalObjectTypeV1
+from tenancy.graphql.types_v1 import TenantTypeV1
+from .filters_v1 import *
+
+if TYPE_CHECKING:
+    from dcim.graphql.types_v1 import InterfaceTypeV1, LocationTypeV1, RegionTypeV1, SiteGroupTypeV1, SiteTypeV1
+    from ipam.graphql.types_v1 import ASNTypeV1
+
+__all__ = (
+    'CircuitGroupAssignmentTypeV1',
+    'CircuitGroupTypeV1',
+    'CircuitTerminationTypeV1',
+    'CircuitTypeV1',
+    'CircuitTypeTypeV1',
+    'ProviderTypeV1',
+    'ProviderAccountTypeV1',
+    'ProviderNetworkTypeV1',
+    'VirtualCircuitTerminationTypeV1',
+    'VirtualCircuitTypeV1',
+    'VirtualCircuitTypeTypeV1',
+)
+
+
+@strawberry_django.type(
+    models.Provider,
+    fields='__all__',
+    filters=ProviderFilterV1,
+    pagination=True
+)
+class ProviderTypeV1(NetBoxObjectTypeV1, ContactsMixinV1):
+
+    networks: List[Annotated["ProviderNetworkTypeV1", strawberry.lazy('circuits.graphql.types_v1')]]
+    circuits: List[Annotated["CircuitTypeV1", strawberry.lazy('circuits.graphql.types_v1')]]
+    asns: List[Annotated["ASNTypeV1", strawberry.lazy('ipam.graphql.types_v1')]]
+    accounts: List[Annotated["ProviderAccountTypeV1", strawberry.lazy('circuits.graphql.types_v1')]]
+
+
+@strawberry_django.type(
+    models.ProviderAccount,
+    fields='__all__',
+    filters=ProviderAccountFilterV1,
+    pagination=True
+)
+class ProviderAccountTypeV1(ContactsMixinV1, NetBoxObjectTypeV1):
+    provider: Annotated["ProviderTypeV1", strawberry.lazy('circuits.graphql.types_v1')]
+
+    circuits: List[Annotated["CircuitTypeV1", strawberry.lazy('circuits.graphql.types_v1')]]
+
+
+@strawberry_django.type(
+    models.ProviderNetwork,
+    fields='__all__',
+    filters=ProviderNetworkFilterV1,
+    pagination=True
+)
+class ProviderNetworkTypeV1(NetBoxObjectTypeV1):
+    provider: Annotated["ProviderTypeV1", strawberry.lazy('circuits.graphql.types_v1')]
+
+    circuit_terminations: List[Annotated["CircuitTerminationTypeV1", strawberry.lazy('circuits.graphql.types_v1')]]
+
+
+@strawberry_django.type(
+    models.CircuitTermination,
+    exclude=['termination_type', 'termination_id', '_location', '_region', '_site', '_site_group', '_provider_network'],
+    filters=CircuitTerminationFilterV1,
+    pagination=True
+)
+class CircuitTerminationTypeV1(CustomFieldsMixinV1, TagsMixinV1, CabledObjectMixinV1, ObjectTypeV1):
+    circuit: Annotated["CircuitTypeV1", strawberry.lazy('circuits.graphql.types_v1')]
+
+    @strawberry_django.field
+    def termination(self) -> Annotated[Union[
+        Annotated["LocationTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+        Annotated["RegionTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+        Annotated["SiteGroupTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+        Annotated["SiteTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+        Annotated["ProviderNetworkTypeV1", strawberry.lazy('circuits.graphql.types_v1')],
+    ], strawberry.union("CircuitTerminationTerminationTypeV1")] | None:
+        return self.termination
+
+
+@strawberry_django.type(
+    models.CircuitType,
+    fields='__all__',
+    filters=CircuitTypeFilterV1,
+    pagination=True
+)
+class CircuitTypeTypeV1(OrganizationalObjectTypeV1):
+    color: str
+
+    circuits: List[Annotated["CircuitTypeV1", strawberry.lazy('circuits.graphql.types_v1')]]
+
+
+@strawberry_django.type(
+    models.Circuit,
+    fields='__all__',
+    filters=CircuitFilterV1,
+    pagination=True
+)
+class CircuitTypeV1(NetBoxObjectTypeV1, ContactsMixinV1):
+    provider: ProviderTypeV1
+    provider_account: ProviderAccountTypeV1 | None
+    termination_a: CircuitTerminationTypeV1 | None
+    termination_z: CircuitTerminationTypeV1 | None
+    type: CircuitTypeTypeV1
+    tenant: TenantTypeV1 | None
+
+    terminations: List[CircuitTerminationTypeV1]
+
+
+@strawberry_django.type(
+    models.CircuitGroup,
+    fields='__all__',
+    filters=CircuitGroupFilterV1,
+    pagination=True
+)
+class CircuitGroupTypeV1(OrganizationalObjectTypeV1):
+    tenant: TenantTypeV1 | None
+
+
+@strawberry_django.type(
+    models.CircuitGroupAssignment,
+    exclude=['member_type', 'member_id'],
+    filters=CircuitGroupAssignmentFilterV1,
+    pagination=True
+)
+class CircuitGroupAssignmentTypeV1(TagsMixinV1, BaseObjectTypeV1):
+    group: Annotated["CircuitGroupTypeV1", strawberry.lazy('circuits.graphql.types_v1')]
+
+    @strawberry_django.field
+    def member(self) -> Annotated[Union[
+        Annotated["CircuitTypeV1", strawberry.lazy('circuits.graphql.types_v1')],
+        Annotated["VirtualCircuitTypeV1", strawberry.lazy('circuits.graphql.types_v1')],
+    ], strawberry.union("CircuitGroupAssignmentMemberTypeV1")] | None:
+        return self.member
+
+
+@strawberry_django.type(
+    models.VirtualCircuitType,
+    fields='__all__',
+    filters=VirtualCircuitTypeFilterV1,
+    pagination=True
+)
+class VirtualCircuitTypeTypeV1(OrganizationalObjectTypeV1):
+    color: str
+
+    virtual_circuits: List[Annotated["VirtualCircuitTypeV1", strawberry.lazy('circuits.graphql.types_v1')]]
+
+
+@strawberry_django.type(
+    models.VirtualCircuitTermination,
+    fields='__all__',
+    filters=VirtualCircuitTerminationFilterV1,
+    pagination=True
+)
+class VirtualCircuitTerminationTypeV1(CustomFieldsMixinV1, TagsMixinV1, ObjectTypeV1):
+    virtual_circuit: Annotated[
+        "VirtualCircuitTypeV1",
+        strawberry.lazy('circuits.graphql.types_v1')
+    ] = strawberry_django.field(select_related=["virtual_circuit"])
+    interface: Annotated[
+        "InterfaceTypeV1",
+        strawberry.lazy('dcim.graphql.types_v1')
+    ] = strawberry_django.field(select_related=["interface"])
+
+
+@strawberry_django.type(
+    models.VirtualCircuit,
+    fields='__all__',
+    filters=VirtualCircuitFilterV1,
+    pagination=True
+)
+class VirtualCircuitTypeV1(NetBoxObjectTypeV1):
+    provider_network: ProviderNetworkTypeV1 = strawberry_django.field(select_related=["provider_network"])
+    provider_account: ProviderAccountTypeV1 | None
+    type: Annotated["VirtualCircuitTypeTypeV1", strawberry.lazy('circuits.graphql.types_v1')] = strawberry_django.field(
+        select_related=["type"]
+    )
+    tenant: TenantTypeV1 | None
+
+    terminations: List[VirtualCircuitTerminationTypeV1]

+ 36 - 0
netbox/core/graphql/filter_mixins_v1.py

@@ -0,0 +1,36 @@
+from dataclasses import dataclass
+from datetime import datetime
+from typing import Annotated, TYPE_CHECKING
+
+import strawberry
+import strawberry_django
+from strawberry import ID
+from strawberry_django import DatetimeFilterLookup
+
+if TYPE_CHECKING:
+    from .filters_v1 import *
+
+__all__ = (
+    'BaseFilterMixinV1',
+    'BaseObjectTypeFilterMixinV1',
+    'ChangeLogFilterMixinV1',
+)
+
+
+# @strawberry.input
+class BaseFilterMixinV1: ...
+
+
+@dataclass
+class BaseObjectTypeFilterMixinV1(BaseFilterMixinV1):
+    id: ID | None = strawberry.UNSET
+
+
+@dataclass
+class ChangeLogFilterMixinV1(BaseFilterMixinV1):
+    id: ID | None = strawberry.UNSET
+    changelog: Annotated['ObjectChangeFilterV1', strawberry.lazy('core.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    created: DatetimeFilterLookup[datetime] | None = strawberry_django.filter_field()
+    last_updated: DatetimeFilterLookup[datetime] | None = strawberry_django.filter_field()

+ 89 - 0
netbox/core/graphql/filters_v1.py

@@ -0,0 +1,89 @@
+from datetime import datetime
+from typing import Annotated, TYPE_CHECKING
+
+import strawberry
+import strawberry_django
+from django.contrib.contenttypes.models import ContentType as DjangoContentType
+from strawberry.scalars import ID
+from strawberry_django import DatetimeFilterLookup, FilterLookup
+
+from core import models
+from core.graphql.filter_mixins_v1 import BaseFilterMixinV1
+from netbox.graphql.filter_mixins import PrimaryModelFilterMixin
+
+if TYPE_CHECKING:
+    from netbox.graphql.filter_lookups import IntegerLookup, JSONFilter
+    from users.graphql.filters import UserFilter
+
+__all__ = (
+    'DataFileFilterV1',
+    'DataSourceFilterV1',
+    'ObjectChangeFilterV1',
+    'ContentTypeFilterV1',
+)
+
+
+@strawberry_django.filter_type(models.DataFile, lookups=True)
+class DataFileFilterV1(BaseFilterMixinV1):
+    id: ID | None = strawberry_django.filter_field()
+    created: DatetimeFilterLookup[datetime] | None = strawberry_django.filter_field()
+    last_updated: DatetimeFilterLookup[datetime] | None = strawberry_django.filter_field()
+    source: Annotated['DataSourceFilterV1', strawberry.lazy('core.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    source_id: ID | None = strawberry_django.filter_field()
+    path: FilterLookup[str] | None = strawberry_django.filter_field()
+    size: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    hash: FilterLookup[str] | None = strawberry_django.filter_field()
+
+
+@strawberry_django.filter_type(models.DataSource, lookups=True)
+class DataSourceFilterV1(PrimaryModelFilterMixin):
+    name: FilterLookup[str] | None = strawberry_django.filter_field()
+    type: FilterLookup[str] | None = strawberry_django.filter_field()
+    source_url: FilterLookup[str] | None = strawberry_django.filter_field()
+    status: FilterLookup[str] | None = strawberry_django.filter_field()
+    enabled: FilterLookup[bool] | None = strawberry_django.filter_field()
+    ignore_rules: FilterLookup[str] | None = strawberry_django.filter_field()
+    parameters: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    last_synced: DatetimeFilterLookup[datetime] | None = strawberry_django.filter_field()
+    datafiles: Annotated['DataFileFilterV1', strawberry.lazy('core.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@strawberry_django.filter_type(models.ObjectChange, lookups=True)
+class ObjectChangeFilterV1(BaseFilterMixinV1):
+    id: ID | None = strawberry_django.filter_field()
+    time: DatetimeFilterLookup[datetime] | None = strawberry_django.filter_field()
+    user: Annotated['UserFilter', strawberry.lazy('users.graphql.filters')] | None = strawberry_django.filter_field()
+    user_name: FilterLookup[str] | None = strawberry_django.filter_field()
+    request_id: FilterLookup[str] | None = strawberry_django.filter_field()
+    action: FilterLookup[str] | None = strawberry_django.filter_field()
+    changed_object_type: Annotated['ContentTypeFilterV1', strawberry.lazy('core.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    changed_object_type_id: ID | None = strawberry_django.filter_field()
+    changed_object_id: ID | None = strawberry_django.filter_field()
+    related_object_type: Annotated['ContentTypeFilterV1', strawberry.lazy('core.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    related_object_id: ID | None = strawberry_django.filter_field()
+    object_repr: FilterLookup[str] | None = strawberry_django.filter_field()
+    prechange_data: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    postchange_data: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@strawberry_django.filter_type(DjangoContentType, lookups=True)
+class ContentTypeFilterV1(BaseFilterMixinV1):
+    id: ID | None = strawberry_django.filter_field()
+    app_label: FilterLookup[str] | None = strawberry_django.filter_field()
+    model: FilterLookup[str] | None = strawberry_django.filter_field()

+ 35 - 0
netbox/core/graphql/mixins_v1.py

@@ -0,0 +1,35 @@
+from typing import Annotated, List, TYPE_CHECKING
+
+import strawberry
+import strawberry_django
+from django.contrib.contenttypes.models import ContentType
+from strawberry.types import Info
+
+from core.models import ObjectChange
+
+if TYPE_CHECKING:
+    from core.graphql.types_v1 import DataFileTypeV1, DataSourceTypeV1, ObjectChangeTypeV1
+
+__all__ = (
+    'ChangelogMixinV1',
+    'SyncedDataMixinV1',
+)
+
+
+@strawberry.type
+class ChangelogMixinV1:
+
+    @strawberry_django.field
+    def changelog(self, info: Info) -> List[Annotated['ObjectChangeTypeV1', strawberry.lazy('.types_v1')]]:  # noqa: F821
+        content_type = ContentType.objects.get_for_model(self)
+        object_changes = ObjectChange.objects.filter(
+            changed_object_type=content_type,
+            changed_object_id=self.pk
+        )
+        return object_changes.restrict(info.context.request.user, 'view')
+
+
+@strawberry.type
+class SyncedDataMixinV1:
+    data_source: Annotated['DataSourceTypeV1', strawberry.lazy('core.graphql.types_v1')] | None
+    data_file: Annotated['DataFileTypeV1', strawberry.lazy('core.graphql.types_v1')] | None

+ 15 - 0
netbox/core/graphql/schema_v1.py

@@ -0,0 +1,15 @@
+from typing import List
+
+import strawberry
+import strawberry_django
+
+from .types_v1 import *
+
+
+@strawberry.type(name="Query")
+class CoreQueryV1:
+    data_file: DataFileTypeV1 = strawberry_django.field()
+    data_file_list: List[DataFileTypeV1] = strawberry_django.field()
+
+    data_source: DataSourceTypeV1 = strawberry_django.field()
+    data_source_list: List[DataSourceTypeV1] = strawberry_django.field()

+ 56 - 0
netbox/core/graphql/types_v1.py

@@ -0,0 +1,56 @@
+from typing import Annotated, List
+
+import strawberry
+import strawberry_django
+from django.contrib.contenttypes.models import ContentType as DjangoContentType
+
+from core import models
+from netbox.graphql.types_v1 import BaseObjectTypeV1, NetBoxObjectTypeV1
+from .filters_v1 import *
+
+__all__ = (
+    'ContentTypeV1',
+    'DataFileTypeV1',
+    'DataSourceTypeV1',
+    'ObjectChangeTypeV1',
+)
+
+
+@strawberry_django.type(
+    models.DataFile,
+    exclude=['data',],
+    filters=DataFileFilterV1,
+    pagination=True
+)
+class DataFileTypeV1(BaseObjectTypeV1):
+    source: Annotated["DataSourceTypeV1", strawberry.lazy('core.graphql.types_v1')]
+
+
+@strawberry_django.type(
+    models.DataSource,
+    fields='__all__',
+    filters=DataSourceFilterV1,
+    pagination=True
+)
+class DataSourceTypeV1(NetBoxObjectTypeV1):
+
+    datafiles: List[Annotated["DataFileTypeV1", strawberry.lazy('core.graphql.types_v1')]]
+
+
+@strawberry_django.type(
+    models.ObjectChange,
+    fields='__all__',
+    filters=ObjectChangeFilterV1,
+    pagination=True
+)
+class ObjectChangeTypeV1(BaseObjectTypeV1):
+    pass
+
+
+@strawberry_django.type(
+    DjangoContentType,
+    fields='__all__',
+    pagination=True
+)
+class ContentTypeV1:
+    pass

+ 155 - 0
netbox/dcim/graphql/filter_mixins_v1.py

@@ -0,0 +1,155 @@
+from dataclasses import dataclass
+from typing import Annotated, TYPE_CHECKING
+
+import strawberry
+import strawberry_django
+from strawberry import ID
+from strawberry_django import FilterLookup
+
+from core.graphql.filter_mixins_v1 import BaseFilterMixinV1, ChangeLogFilterMixinV1
+from core.graphql.filters_v1 import ContentTypeFilterV1
+from netbox.graphql.filter_mixins_v1 import NetBoxModelFilterMixinV1, PrimaryModelFilterMixinV1, WeightFilterMixinV1
+from .enums import *
+
+if TYPE_CHECKING:
+    from netbox.graphql.filter_lookups import IntegerLookup
+    from extras.graphql.filters_v1 import ConfigTemplateFilterV1
+    from ipam.graphql.filters_v1 import VLANFilterV1, VLANTranslationPolicyFilterV1
+    from .filters_v1 import *
+
+__all__ = (
+    'CabledObjectModelFilterMixinV1',
+    'ComponentModelFilterMixinV1',
+    'ComponentTemplateFilterMixinV1',
+    'InterfaceBaseFilterMixinV1',
+    'ModularComponentModelFilterMixinV1',
+    'ModularComponentTemplateFilterMixinV1',
+    'RackBaseFilterMixinV1',
+    'RenderConfigFilterMixinV1',
+    'ScopedFilterMixinV1',
+)
+
+
+@dataclass
+class ScopedFilterMixinV1(BaseFilterMixinV1):
+    scope_type: Annotated['ContentTypeFilterV1', strawberry.lazy('core.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    scope_id: ID | None = strawberry_django.filter_field()
+
+
+@dataclass
+class ComponentModelFilterMixinV1(NetBoxModelFilterMixinV1):
+    device: Annotated['DeviceFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    device_id: ID | None = strawberry_django.filter_field()
+    name: FilterLookup[str] | None = strawberry_django.filter_field()
+    label: FilterLookup[str] | None = strawberry_django.filter_field()
+    description: FilterLookup[str] | None = strawberry_django.filter_field()
+
+
+@dataclass
+class ModularComponentModelFilterMixinV1(ComponentModelFilterMixinV1):
+    module: Annotated['ModuleFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    module_id: ID | None = strawberry_django.filter_field()
+    inventory_items: Annotated['InventoryItemFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@dataclass
+class CabledObjectModelFilterMixinV1(BaseFilterMixinV1):
+    cable: Annotated['CableFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    cable_id: ID | None = strawberry_django.filter_field()
+    cable_end: CableEndEnum | None = strawberry_django.filter_field()
+    mark_connected: FilterLookup[bool] | None = strawberry_django.filter_field()
+
+
+@dataclass
+class ComponentTemplateFilterMixinV1(ChangeLogFilterMixinV1):
+    device_type: Annotated['DeviceTypeFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    device_type_id: ID | None = strawberry_django.filter_field()
+    name: FilterLookup[str] | None = strawberry_django.filter_field()
+    label: FilterLookup[str] | None = strawberry_django.filter_field()
+    description: FilterLookup[str] | None = strawberry_django.filter_field()
+
+
+@dataclass
+class ModularComponentTemplateFilterMixinV1(ComponentTemplateFilterMixinV1):
+    module_type: Annotated['ModuleTypeFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@dataclass
+class RenderConfigFilterMixinV1(BaseFilterMixinV1):
+    config_template: Annotated['ConfigTemplateFilterV1', strawberry.lazy('extras.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    config_template_id: ID | None = strawberry_django.filter_field()
+
+
+@dataclass
+class InterfaceBaseFilterMixinV1(BaseFilterMixinV1):
+    enabled: FilterLookup[bool] | None = strawberry_django.filter_field()
+    mtu: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    mode: InterfaceModeEnum | None = strawberry_django.filter_field()
+    bridge: Annotated['InterfaceFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    bridge_id: ID | None = strawberry_django.filter_field()
+    untagged_vlan: Annotated['VLANFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    tagged_vlans: Annotated['VLANFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    qinq_svlan: Annotated['VLANFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    vlan_translation_policy: Annotated[
+        'VLANTranslationPolicyFilterV1', strawberry.lazy('ipam.graphql.filters_v1')
+    ] | None = strawberry_django.filter_field()
+    primary_mac_address: Annotated['MACAddressFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    primary_mac_address_id: ID | None = strawberry_django.filter_field()
+
+
+@dataclass
+class RackBaseFilterMixinV1(WeightFilterMixinV1, PrimaryModelFilterMixinV1):
+    width: Annotated['RackWidthEnum', strawberry.lazy('dcim.graphql.enums')] | None = strawberry_django.filter_field()
+    u_height: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    starting_unit: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    desc_units: FilterLookup[bool] | None = strawberry_django.filter_field()
+    outer_width: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    outer_height: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    outer_depth: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    outer_unit: Annotated['RackDimensionUnitEnum', strawberry.lazy('dcim.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    mounting_depth: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    max_weight: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )

+ 1020 - 0
netbox/dcim/graphql/filters_v1.py

@@ -0,0 +1,1020 @@
+from typing import Annotated, TYPE_CHECKING
+
+from django.db.models import Q
+import strawberry
+import strawberry_django
+from strawberry.scalars import ID
+from strawberry_django import FilterLookup
+
+from core.graphql.filter_mixins_v1 import ChangeLogFilterMixinV1
+from dcim import models
+from dcim.constants import *
+from dcim.graphql.enums import InterfaceKindEnum
+from extras.graphql.filter_mixins_v1 import ConfigContextFilterMixinV1
+from netbox.graphql.filter_mixins_v1 import (
+    PrimaryModelFilterMixinV1,
+    OrganizationalModelFilterMixinV1,
+    NestedGroupModelFilterMixinV1,
+    ImageAttachmentFilterMixinV1,
+    WeightFilterMixinV1,
+)
+from tenancy.graphql.filter_mixins_v1 import TenancyFilterMixinV1, ContactFilterMixinV1
+from .filter_mixins_v1 import (
+    CabledObjectModelFilterMixinV1,
+    ComponentModelFilterMixinV1,
+    ComponentTemplateFilterMixinV1,
+    InterfaceBaseFilterMixinV1,
+    ModularComponentModelFilterMixinV1,
+    ModularComponentTemplateFilterMixinV1,
+    RackBaseFilterMixinV1,
+    RenderConfigFilterMixinV1,
+)
+
+if TYPE_CHECKING:
+    from core.graphql.filters_v1 import ContentTypeFilterV1
+    from extras.graphql.filters_v1 import ConfigTemplateFilterV1, ImageAttachmentFilterV1
+    from ipam.graphql.filters_v1 import (
+        ASNFilterV1, FHRPGroupAssignmentFilterV1, IPAddressFilterV1, PrefixFilterV1, VLANGroupFilterV1, VRFFilterV1,
+    )
+    from netbox.graphql.enums import ColorEnum
+    from netbox.graphql.filter_lookups import FloatLookup, IntegerArrayLookup, IntegerLookup, TreeNodeFilter
+    from users.graphql.filters_v1 import UserFilterV1
+    from virtualization.graphql.filters_v1 import ClusterFilterV1
+    from vpn.graphql.filters_v1 import L2VPNFilterV1, TunnelTerminationFilterV1
+    from wireless.graphql.enums import WirelessChannelEnum, WirelessRoleEnum
+    from wireless.graphql.filters_v1 import WirelessLANFilterV1, WirelessLinkFilterV1
+    from .enums import *
+
+__all__ = (
+    'CableFilterV1',
+    'CableTerminationFilterV1',
+    'ConsolePortFilterV1',
+    'ConsolePortTemplateFilterV1',
+    'ConsoleServerPortFilterV1',
+    'ConsoleServerPortTemplateFilterV1',
+    'DeviceFilterV1',
+    'DeviceBayFilterV1',
+    'DeviceBayTemplateFilterV1',
+    'DeviceRoleFilterV1',
+    'DeviceTypeFilterV1',
+    'FrontPortFilterV1',
+    'FrontPortTemplateFilterV1',
+    'InterfaceFilterV1',
+    'InterfaceTemplateFilterV1',
+    'InventoryItemFilterV1',
+    'InventoryItemRoleFilterV1',
+    'InventoryItemTemplateFilterV1',
+    'LocationFilterV1',
+    'MACAddressFilterV1',
+    'ManufacturerFilterV1',
+    'ModuleFilterV1',
+    'ModuleBayFilterV1',
+    'ModuleBayTemplateFilterV1',
+    'ModuleTypeFilterV1',
+    'ModuleTypeProfileFilterV1',
+    'PlatformFilterV1',
+    'PowerFeedFilterV1',
+    'PowerOutletFilterV1',
+    'PowerOutletTemplateFilterV1',
+    'PowerPanelFilterV1',
+    'PowerPortFilterV1',
+    'PowerPortTemplateFilterV1',
+    'RackFilterV1',
+    'RackReservationFilterV1',
+    'RackRoleFilterV1',
+    'RackTypeFilterV1',
+    'RearPortFilterV1',
+    'RearPortTemplateFilterV1',
+    'RegionFilterV1',
+    'SiteFilterV1',
+    'SiteGroupFilterV1',
+    'VirtualChassisFilterV1',
+    'VirtualDeviceContextFilterV1',
+)
+
+
+@strawberry_django.filter_type(models.Cable, lookups=True)
+class CableFilterV1(PrimaryModelFilterMixinV1, TenancyFilterMixinV1):
+    type: Annotated['CableTypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = strawberry_django.filter_field()
+    status: Annotated['LinkStatusEnum', strawberry.lazy('dcim.graphql.enums')] | None = strawberry_django.filter_field()
+    label: FilterLookup[str] | None = strawberry_django.filter_field()
+    color: Annotated['ColorEnum', strawberry.lazy('netbox.graphql.enums')] | None = strawberry_django.filter_field()
+    length: Annotated['FloatLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    length_unit: Annotated['CableLengthUnitEnum', strawberry.lazy('dcim.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    terminations: Annotated['CableTerminationFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@strawberry_django.filter_type(models.CableTermination, lookups=True)
+class CableTerminationFilterV1(ChangeLogFilterMixinV1):
+    cable: Annotated['CableFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    cable_id: ID | None = strawberry_django.filter_field()
+    cable_end: Annotated['CableEndEnum', strawberry.lazy('dcim.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    termination_type: Annotated['CableTerminationFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    termination_id: ID | None = strawberry_django.filter_field()
+
+
+@strawberry_django.filter_type(models.ConsolePort, lookups=True)
+class ConsolePortFilterV1(ModularComponentModelFilterMixinV1, CabledObjectModelFilterMixinV1):
+    type: Annotated['ConsolePortTypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    speed: Annotated['ConsolePortSpeedEnum', strawberry.lazy('dcim.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@strawberry_django.filter_type(models.ConsolePortTemplate, lookups=True)
+class ConsolePortTemplateFilterV1(ModularComponentTemplateFilterMixinV1):
+    type: Annotated['ConsolePortTypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@strawberry_django.filter_type(models.ConsoleServerPort, lookups=True)
+class ConsoleServerPortFilterV1(ModularComponentModelFilterMixinV1, CabledObjectModelFilterMixinV1):
+    type: Annotated['ConsolePortTypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    speed: Annotated['ConsolePortSpeedEnum', strawberry.lazy('dcim.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@strawberry_django.filter_type(models.ConsoleServerPortTemplate, lookups=True)
+class ConsoleServerPortTemplateFilterV1(ModularComponentTemplateFilterMixinV1):
+    type: Annotated['ConsolePortTypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@strawberry_django.filter_type(models.Device, lookups=True)
+class DeviceFilterV1(
+    ContactFilterMixinV1,
+    TenancyFilterMixinV1,
+    ImageAttachmentFilterMixinV1,
+    RenderConfigFilterMixinV1,
+    ConfigContextFilterMixinV1,
+    PrimaryModelFilterMixinV1,
+):
+    device_type: Annotated['DeviceTypeFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    device_type_id: ID | None = strawberry_django.filter_field()
+    role: Annotated['DeviceRoleFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    role_id: ID | None = strawberry_django.filter_field()
+    platform: Annotated['PlatformFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    name: FilterLookup[str] | None = strawberry_django.filter_field()
+    serial: FilterLookup[str] | None = strawberry_django.filter_field()
+    asset_tag: FilterLookup[str] | None = strawberry_django.filter_field()
+    site: Annotated['SiteFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    site_id: ID | None = strawberry_django.filter_field()
+    location: Annotated['LocationFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    location_id: Annotated['TreeNodeFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    rack: Annotated['RackFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    rack_id: ID | None = strawberry_django.filter_field()
+    position: Annotated['FloatLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    face: Annotated['DeviceFaceEnum', strawberry.lazy('dcim.graphql.enums')] | None = strawberry_django.filter_field()
+    status: Annotated['DeviceStatusEnum', strawberry.lazy('dcim.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    airflow: Annotated['DeviceAirflowEnum', strawberry.lazy('dcim.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    primary_ip4: Annotated['IPAddressFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    primary_ip4_id: ID | None = strawberry_django.filter_field()
+    primary_ip6: Annotated['IPAddressFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    primary_ip6_id: ID | None = strawberry_django.filter_field()
+    oob_ip: Annotated['IPAddressFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    oob_ip_id: ID | None = strawberry_django.filter_field()
+    cluster: Annotated['ClusterFilterV1', strawberry.lazy('virtualization.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    cluster_id: ID | None = strawberry_django.filter_field()
+    virtual_chassis: Annotated['VirtualChassisFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    virtual_chassis_id: ID | None = strawberry_django.filter_field()
+    vc_position: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    vc_priority: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    latitude: Annotated['FloatLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    longitude: Annotated['FloatLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    console_ports: Annotated['ConsolePortFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    console_server_ports: Annotated['ConsoleServerPortFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    power_outlets: Annotated['PowerOutletFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    power_ports: Annotated['PowerPortFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    interfaces: Annotated['InterfaceFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    front_ports: Annotated['FrontPortFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    rear_ports: Annotated['RearPortFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    device_bays: Annotated['DeviceBayFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    module_bays: Annotated['ModuleBayFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    modules: Annotated['ModuleFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    console_port_count: FilterLookup[int] | None = strawberry_django.filter_field()
+    console_server_port_count: FilterLookup[int] | None = strawberry_django.filter_field()
+    power_port_count: FilterLookup[int] | None = strawberry_django.filter_field()
+    power_outlet_count: FilterLookup[int] | None = strawberry_django.filter_field()
+    interface_count: FilterLookup[int] | None = strawberry_django.filter_field()
+    front_port_count: FilterLookup[int] | None = strawberry_django.filter_field()
+    rear_port_count: FilterLookup[int] | None = strawberry_django.filter_field()
+    device_bay_count: FilterLookup[int] | None = strawberry_django.filter_field()
+    module_bay_count: FilterLookup[int] | None = strawberry_django.filter_field()
+    inventory_item_count: FilterLookup[int] | None = strawberry_django.filter_field()
+
+
+@strawberry_django.filter_type(models.DeviceBay, lookups=True)
+class DeviceBayFilterV1(ComponentModelFilterMixinV1):
+    installed_device: Annotated['DeviceFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    installed_device_id: ID | None = strawberry_django.filter_field()
+
+
+@strawberry_django.filter_type(models.DeviceBayTemplate, lookups=True)
+class DeviceBayTemplateFilterV1(ComponentTemplateFilterMixinV1):
+    pass
+
+
+@strawberry_django.filter_type(models.InventoryItemTemplate, lookups=True)
+class InventoryItemTemplateFilterV1(ComponentTemplateFilterMixinV1):
+    parent: Annotated['InventoryItemTemplateFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    component_type: Annotated['ContentTypeFilterV1', strawberry.lazy('core.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    component_id: ID | None = strawberry_django.filter_field()
+    role: Annotated['InventoryItemRoleFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    role_id: ID | None = strawberry_django.filter_field()
+    manufacturer: Annotated['ManufacturerFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    manufacturer_id: ID | None = strawberry_django.filter_field()
+    part_id: FilterLookup[str] | None = strawberry_django.filter_field()
+
+
+@strawberry_django.filter_type(models.DeviceRole, lookups=True)
+class DeviceRoleFilterV1(OrganizationalModelFilterMixinV1, RenderConfigFilterMixinV1):
+    color: Annotated['ColorEnum', strawberry.lazy('netbox.graphql.enums')] | None = strawberry_django.filter_field()
+    vm_role: FilterLookup[bool] | None = strawberry_django.filter_field()
+
+
+@strawberry_django.filter_type(models.DeviceType, lookups=True)
+class DeviceTypeFilterV1(ImageAttachmentFilterMixinV1, PrimaryModelFilterMixinV1, WeightFilterMixinV1):
+    manufacturer: Annotated['ManufacturerFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    manufacturer_id: ID | None = strawberry_django.filter_field()
+    model: FilterLookup[str] | None = strawberry_django.filter_field()
+    slug: FilterLookup[str] | None = strawberry_django.filter_field()
+    default_platform: Annotated['PlatformFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    default_platform_id: ID | None = strawberry_django.filter_field()
+    part_number: FilterLookup[str] | None = strawberry_django.filter_field()
+    u_height: Annotated['FloatLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    exclude_from_utilization: FilterLookup[bool] | None = strawberry_django.filter_field()
+    is_full_depth: FilterLookup[bool] | None = strawberry_django.filter_field()
+    subdevice_role: Annotated['SubdeviceRoleEnum', strawberry.lazy('dcim.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    airflow: Annotated['DeviceAirflowEnum', strawberry.lazy('dcim.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    front_image: Annotated['ImageAttachmentFilterV1', strawberry.lazy('extras.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    rear_image: Annotated['ImageAttachmentFilterV1', strawberry.lazy('extras.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    console_port_templates: (
+        Annotated['ConsolePortTemplateFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None
+    ) = strawberry_django.filter_field()
+    console_server_port_templates: (
+        Annotated['ConsoleServerPortTemplateFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None
+    ) = strawberry_django.filter_field()
+    power_port_templates: (
+        Annotated['PowerPortTemplateFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None
+    ) = strawberry_django.filter_field()
+    power_outlet_templates: (
+        Annotated['PowerOutletTemplateFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None
+    ) = strawberry_django.filter_field()
+    interface_templates: (
+        Annotated['InterfaceTemplateFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None
+    ) = strawberry_django.filter_field()
+    front_port_templates: (
+        Annotated['FrontPortTemplateFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None
+    ) = strawberry_django.filter_field()
+    rear_port_templates: (
+        Annotated['RearPortTemplateFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None
+    ) = strawberry_django.filter_field()
+    device_bay_templates: (
+        Annotated['DeviceBayTemplateFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None
+    ) = strawberry_django.filter_field()
+    module_bay_templates: (
+        Annotated['ModuleBayTemplateFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None
+    ) = strawberry_django.filter_field()
+    inventory_item_templates: (
+        Annotated['InventoryItemTemplateFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None
+    ) = strawberry_django.filter_field()
+    console_port_template_count: FilterLookup[int] | None = strawberry_django.filter_field()
+    console_server_port_template_count: FilterLookup[int] | None = strawberry_django.filter_field()
+    power_port_template_count: FilterLookup[int] | None = strawberry_django.filter_field()
+    power_outlet_template_count: FilterLookup[int] | None = strawberry_django.filter_field()
+    interface_template_count: FilterLookup[int] | None = strawberry_django.filter_field()
+    front_port_template_count: FilterLookup[int] | None = strawberry_django.filter_field()
+    rear_port_template_count: FilterLookup[int] | None = strawberry_django.filter_field()
+    device_bay_template_count: FilterLookup[int] | None = strawberry_django.filter_field()
+    module_bay_template_count: FilterLookup[int] | None = strawberry_django.filter_field()
+    inventory_item_template_count: FilterLookup[int] | None = strawberry_django.filter_field()
+
+
+@strawberry_django.filter_type(models.FrontPort, lookups=True)
+class FrontPortFilterV1(ModularComponentModelFilterMixinV1, CabledObjectModelFilterMixinV1):
+    type: Annotated['PortTypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = strawberry_django.filter_field()
+    color: Annotated['ColorEnum', strawberry.lazy('netbox.graphql.enums')] | None = strawberry_django.filter_field()
+    rear_port: Annotated['RearPortFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    rear_port_id: ID | None = strawberry_django.filter_field()
+    rear_port_position: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@strawberry_django.filter_type(models.FrontPortTemplate, lookups=True)
+class FrontPortTemplateFilterV1(ModularComponentTemplateFilterMixinV1):
+    type: Annotated['PortTypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = strawberry_django.filter_field()
+    color: Annotated['ColorEnum', strawberry.lazy('netbox.graphql.enums')] | None = strawberry_django.filter_field()
+    rear_port: Annotated['RearPortTemplateFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    rear_port_id: ID | None = strawberry_django.filter_field()
+    rear_port_position: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@strawberry_django.filter_type(models.MACAddress, lookups=True)
+class MACAddressFilterV1(PrimaryModelFilterMixinV1):
+    mac_address: FilterLookup[str] | None = strawberry_django.filter_field()
+    assigned_object_type: Annotated['ContentTypeFilterV1', strawberry.lazy('core.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    assigned_object_id: ID | None = strawberry_django.filter_field()
+
+
+@strawberry_django.filter_type(models.Interface, lookups=True)
+class InterfaceFilterV1(ModularComponentModelFilterMixinV1, InterfaceBaseFilterMixinV1, CabledObjectModelFilterMixinV1):
+    vcdcs: Annotated['VirtualDeviceContextFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    lag: Annotated['InterfaceFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    lag_id: ID | None = strawberry_django.filter_field()
+    type: Annotated['InterfaceTypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    mgmt_only: FilterLookup[bool] | None = strawberry_django.filter_field()
+    speed: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    duplex: Annotated['InterfaceDuplexEnum', strawberry.lazy('dcim.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    wwn: FilterLookup[str] | None = strawberry_django.filter_field()
+    parent: Annotated['InterfaceFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    parent_id: ID | None = strawberry_django.filter_field()
+    rf_role: Annotated['WirelessRoleEnum', strawberry.lazy('wireless.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    rf_channel: Annotated['WirelessChannelEnum', strawberry.lazy('wireless.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    rf_channel_frequency: Annotated['FloatLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    rf_channel_width: Annotated['FloatLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    tx_power: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    poe_mode: Annotated['InterfacePoEModeEnum', strawberry.lazy('dcim.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    poe_type: Annotated['InterfacePoETypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    wireless_link: Annotated['WirelessLinkFilterV1', strawberry.lazy('wireless.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    wireless_link_id: ID | None = strawberry_django.filter_field()
+    wireless_lans: Annotated['WirelessLANFilterV1', strawberry.lazy('wireless.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    vrf: Annotated['VRFFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = strawberry_django.filter_field()
+    vrf_id: ID | None = strawberry_django.filter_field()
+    ip_addresses: Annotated['IPAddressFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    mac_addresses: Annotated['MACAddressFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    fhrp_group_assignments: Annotated[
+        'FHRPGroupAssignmentFilterV1', strawberry.lazy('ipam.graphql.filters_v1')
+    ] | None = (
+        strawberry_django.filter_field()
+    )
+    tunnel_terminations: Annotated['TunnelTerminationFilterV1', strawberry.lazy('vpn.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    l2vpn_terminations: Annotated['L2VPNFilterV1', strawberry.lazy('vpn.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+
+    @strawberry_django.filter_field
+    def connected(self, queryset, value: bool, prefix: str):
+        if value is True:
+            return queryset, Q(**{f"{prefix}_path__is_active": True})
+        else:
+            return queryset, Q(**{f"{prefix}_path__isnull": True}) | Q(**{f"{prefix}_path__is_active": False})
+
+    @strawberry_django.filter_field
+    def kind(
+        self,
+        queryset,
+        value: Annotated['InterfaceKindEnum', strawberry.lazy('dcim.graphql.enums')],
+        prefix: str
+    ):
+        if value == InterfaceKindEnum.KIND_PHYSICAL:
+            return queryset, ~Q(**{f"{prefix}type__in": NONCONNECTABLE_IFACE_TYPES})
+        elif value == InterfaceKindEnum.KIND_VIRTUAL:
+            return queryset, Q(**{f"{prefix}type__in": VIRTUAL_IFACE_TYPES})
+        elif value == InterfaceKindEnum.KIND_WIRELESS:
+            return queryset, Q(**{f"{prefix}type__in": WIRELESS_IFACE_TYPES})
+
+
+@strawberry_django.filter_type(models.InterfaceTemplate, lookups=True)
+class InterfaceTemplateFilterV1(ModularComponentTemplateFilterMixinV1):
+    type: Annotated['InterfaceTypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    enabled: FilterLookup[bool] | None = strawberry_django.filter_field()
+    mgmt_only: FilterLookup[bool] | None = strawberry_django.filter_field()
+    bridge: Annotated['InterfaceTemplateFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    bridge_id: ID | None = strawberry_django.filter_field()
+    poe_mode: Annotated['InterfacePoEModeEnum', strawberry.lazy('dcim.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    poe_type: Annotated['InterfacePoETypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    rf_role: Annotated['WirelessRoleEnum', strawberry.lazy('wireless.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@strawberry_django.filter_type(models.InventoryItem, lookups=True)
+class InventoryItemFilterV1(ComponentModelFilterMixinV1):
+    parent: Annotated['InventoryItemFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    parent_id: ID | None = strawberry_django.filter_field()
+    component_type: Annotated['ContentTypeFilterV1', strawberry.lazy('core.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    component_id: ID | None = strawberry_django.filter_field()
+    status: Annotated['InventoryItemStatusEnum', strawberry.lazy('dcim.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    role: Annotated['InventoryItemRoleFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    role_id: ID | None = strawberry_django.filter_field()
+    manufacturer: Annotated['ManufacturerFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    manufacturer_id: ID | None = strawberry_django.filter_field()
+    part_id: FilterLookup[str] | None = strawberry_django.filter_field()
+    serial: FilterLookup[str] | None = strawberry_django.filter_field()
+    asset_tag: FilterLookup[str] | None = strawberry_django.filter_field()
+    discovered: FilterLookup[bool] | None = strawberry_django.filter_field()
+
+
+@strawberry_django.filter_type(models.InventoryItemRole, lookups=True)
+class InventoryItemRoleFilterV1(OrganizationalModelFilterMixinV1):
+    color: Annotated['ColorEnum', strawberry.lazy('netbox.graphql.enums')] | None = strawberry_django.filter_field()
+
+
+@strawberry_django.filter_type(models.Location, lookups=True)
+class LocationFilterV1(
+    ContactFilterMixinV1, ImageAttachmentFilterMixinV1, TenancyFilterMixinV1, NestedGroupModelFilterMixinV1
+):
+    site: Annotated['SiteFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    site_id: ID | None = strawberry_django.filter_field()
+    status: Annotated['LocationStatusEnum', strawberry.lazy('dcim.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    facility: FilterLookup[str] | None = strawberry_django.filter_field()
+    prefixes: Annotated['PrefixFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    vlan_groups: Annotated['VLANGroupFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@strawberry_django.filter_type(models.Manufacturer, lookups=True)
+class ManufacturerFilterV1(ContactFilterMixinV1, OrganizationalModelFilterMixinV1):
+    pass
+
+
+@strawberry_django.filter_type(models.Module, lookups=True)
+class ModuleFilterV1(PrimaryModelFilterMixinV1, ConfigContextFilterMixinV1):
+    device: Annotated['DeviceFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    device_id: ID | None = strawberry_django.filter_field()
+    module_bay: Annotated['ModuleBayFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    module_bay_id: ID | None = strawberry_django.filter_field()
+    module_type: Annotated['ModuleTypeFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    module_type_id: ID | None = strawberry_django.filter_field()
+    status: Annotated['ModuleStatusEnum', strawberry.lazy('dcim.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    serial: FilterLookup[str] | None = strawberry_django.filter_field()
+    asset_tag: FilterLookup[str] | None = strawberry_django.filter_field()
+    console_ports: Annotated['ConsolePortFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    console_server_ports: Annotated['ConsoleServerPortFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    power_outlets: Annotated['PowerOutletFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    power_ports: Annotated['PowerPortFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    interfaces: Annotated['InterfaceFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    front_ports: Annotated['FrontPortFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    rear_ports: Annotated['RearPortFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    device_bays: Annotated['DeviceBayFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    module_bays: Annotated['ModuleBayFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    modules: Annotated['ModuleFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@strawberry_django.filter_type(models.ModuleBay, lookups=True)
+class ModuleBayFilterV1(ModularComponentModelFilterMixinV1):
+    parent: Annotated['ModuleBayFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    parent_id: ID | None = strawberry_django.filter_field()
+    position: FilterLookup[str] | None = strawberry_django.filter_field()
+
+
+@strawberry_django.filter_type(models.ModuleBayTemplate, lookups=True)
+class ModuleBayTemplateFilterV1(ModularComponentTemplateFilterMixinV1):
+    position: FilterLookup[str] | None = strawberry_django.filter_field()
+
+
+@strawberry_django.filter_type(models.ModuleTypeProfile, lookups=True)
+class ModuleTypeProfileFilterV1(PrimaryModelFilterMixinV1):
+    name: FilterLookup[str] | None = strawberry_django.filter_field()
+
+
+@strawberry_django.filter_type(models.ModuleType, lookups=True)
+class ModuleTypeFilterV1(ImageAttachmentFilterMixinV1, PrimaryModelFilterMixinV1, WeightFilterMixinV1):
+    manufacturer: Annotated['ManufacturerFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    manufacturer_id: ID | None = strawberry_django.filter_field()
+    profile: Annotated['ModuleTypeProfileFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    profile_id: ID | None = strawberry_django.filter_field()
+    model: FilterLookup[str] | None = strawberry_django.filter_field()
+    part_number: FilterLookup[str] | None = strawberry_django.filter_field()
+    airflow: Annotated['ModuleAirflowEnum', strawberry.lazy('dcim.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    console_port_templates: (
+        Annotated['ConsolePortTemplateFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None
+    ) = strawberry_django.filter_field()
+    console_server_port_templates: (
+        Annotated['ConsoleServerPortTemplateFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None
+    ) = strawberry_django.filter_field()
+    power_port_templates: (
+        Annotated['PowerPortTemplateFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None
+    ) = strawberry_django.filter_field()
+    power_outlet_templates: (
+        Annotated['PowerOutletTemplateFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None
+    ) = strawberry_django.filter_field()
+    interface_templates: (
+        Annotated['InterfaceTemplateFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None
+    ) = strawberry_django.filter_field()
+    front_port_templates: (
+        Annotated['FrontPortTemplateFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None
+    ) = strawberry_django.filter_field()
+    rear_port_templates: (
+        Annotated['RearPortTemplateFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None
+    ) = strawberry_django.filter_field()
+    device_bay_templates: (
+        Annotated['DeviceBayTemplateFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None
+    ) = strawberry_django.filter_field()
+    module_bay_templates: (
+        Annotated['ModuleBayTemplateFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None
+    ) = strawberry_django.filter_field()
+    inventory_item_templates: (
+        Annotated['InventoryItemTemplateFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None
+    ) = strawberry_django.filter_field()
+
+
+@strawberry_django.filter_type(models.Platform, lookups=True)
+class PlatformFilterV1(OrganizationalModelFilterMixinV1):
+    manufacturer: Annotated['ManufacturerFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    manufacturer_id: ID | None = strawberry_django.filter_field()
+    config_template: Annotated['ConfigTemplateFilterV1', strawberry.lazy('extras.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    config_template_id: ID | None = strawberry_django.filter_field()
+
+
+@strawberry_django.filter_type(models.PowerFeed, lookups=True)
+class PowerFeedFilterV1(CabledObjectModelFilterMixinV1, TenancyFilterMixinV1, PrimaryModelFilterMixinV1):
+    power_panel: Annotated['PowerPanelFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    power_panel_id: ID | None = strawberry_django.filter_field()
+    rack: Annotated['RackFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    rack_id: ID | None = strawberry_django.filter_field()
+    name: FilterLookup[str] | None = strawberry_django.filter_field()
+    status: Annotated['PowerFeedStatusEnum', strawberry.lazy('dcim.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    type: Annotated['PowerFeedTypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    supply: Annotated['PowerFeedSupplyEnum', strawberry.lazy('dcim.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    phase: Annotated['PowerFeedPhaseEnum', strawberry.lazy('dcim.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    voltage: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    amperage: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    max_utilization: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    available_power: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@strawberry_django.filter_type(models.PowerOutlet, lookups=True)
+class PowerOutletFilterV1(ModularComponentModelFilterMixinV1, CabledObjectModelFilterMixinV1):
+    type: Annotated['PowerOutletTypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    power_port: Annotated['PowerPortFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    power_port_id: ID | None = strawberry_django.filter_field()
+    feed_leg: Annotated['PowerOutletFeedLegEnum', strawberry.lazy('dcim.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    color: Annotated['ColorEnum', strawberry.lazy('netbox.graphql.enums')] | None = strawberry_django.filter_field()
+
+
+@strawberry_django.filter_type(models.PowerOutletTemplate, lookups=True)
+class PowerOutletTemplateFilterV1(ModularComponentModelFilterMixinV1):
+    type: Annotated['PowerOutletTypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    power_port: Annotated['PowerPortTemplateFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    power_port_id: ID | None = strawberry_django.filter_field()
+    feed_leg: Annotated['PowerOutletFeedLegEnum', strawberry.lazy('dcim.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@strawberry_django.filter_type(models.PowerPanel, lookups=True)
+class PowerPanelFilterV1(ContactFilterMixinV1, ImageAttachmentFilterMixinV1, PrimaryModelFilterMixinV1):
+    site: Annotated['SiteFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    site_id: ID | None = strawberry_django.filter_field()
+    location: Annotated['LocationFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    location_id: Annotated['TreeNodeFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    name: FilterLookup[str] | None = strawberry_django.filter_field()
+
+
+@strawberry_django.filter_type(models.PowerPort, lookups=True)
+class PowerPortFilterV1(ModularComponentModelFilterMixinV1, CabledObjectModelFilterMixinV1):
+    type: Annotated['PowerPortTypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    maximum_draw: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    allocated_draw: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@strawberry_django.filter_type(models.PowerPortTemplate, lookups=True)
+class PowerPortTemplateFilterV1(ModularComponentTemplateFilterMixinV1):
+    type: Annotated['PowerPortTypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    maximum_draw: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    allocated_draw: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@strawberry_django.filter_type(models.RackType, lookups=True)
+class RackTypeFilterV1(RackBaseFilterMixinV1):
+    form_factor: Annotated['RackFormFactorEnum', strawberry.lazy('dcim.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    manufacturer: Annotated['ManufacturerFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    manufacturer_id: ID | None = strawberry_django.filter_field()
+    model: FilterLookup[str] | None = strawberry_django.filter_field()
+    slug: FilterLookup[str] | None = strawberry_django.filter_field()
+
+
+@strawberry_django.filter_type(models.Rack, lookups=True)
+class RackFilterV1(ContactFilterMixinV1, ImageAttachmentFilterMixinV1, TenancyFilterMixinV1, RackBaseFilterMixinV1):
+    form_factor: Annotated['RackFormFactorEnum', strawberry.lazy('dcim.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    rack_type: Annotated['RackTypeFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    rack_type_id: ID | None = strawberry_django.filter_field()
+    name: FilterLookup[str] | None = strawberry_django.filter_field()
+    facility_id: FilterLookup[str] | None = strawberry_django.filter_field()
+    site: Annotated['SiteFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    site_id: ID | None = strawberry_django.filter_field()
+    location: Annotated['LocationFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    location_id: Annotated['TreeNodeFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    status: Annotated['RackStatusEnum', strawberry.lazy('dcim.graphql.enums')] | None = strawberry_django.filter_field()
+    role: Annotated['RackRoleFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    role_id: ID | None = strawberry_django.filter_field()
+    serial: FilterLookup[str] | None = strawberry_django.filter_field()
+    asset_tag: FilterLookup[str] | None = strawberry_django.filter_field()
+    airflow: Annotated['RackAirflowEnum', strawberry.lazy('dcim.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    vlan_groups: Annotated['VLANGroupFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@strawberry_django.filter_type(models.RackReservation, lookups=True)
+class RackReservationFilterV1(TenancyFilterMixinV1, PrimaryModelFilterMixinV1):
+    rack: Annotated['RackFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    rack_id: ID | None = strawberry_django.filter_field()
+    units: Annotated['IntegerArrayLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    user: Annotated['UserFilterV1', strawberry.lazy('users.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    user_id: ID | None = strawberry_django.filter_field()
+    description: FilterLookup[str] | None = strawberry_django.filter_field()
+
+
+@strawberry_django.filter_type(models.RackRole, lookups=True)
+class RackRoleFilterV1(OrganizationalModelFilterMixinV1):
+    color: Annotated['ColorEnum', strawberry.lazy('netbox.graphql.enums')] | None = strawberry_django.filter_field()
+
+
+@strawberry_django.filter_type(models.RearPort, lookups=True)
+class RearPortFilterV1(ModularComponentModelFilterMixinV1, CabledObjectModelFilterMixinV1):
+    type: Annotated['PortTypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = strawberry_django.filter_field()
+    color: Annotated['ColorEnum', strawberry.lazy('netbox.graphql.enums')] | None = strawberry_django.filter_field()
+    positions: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@strawberry_django.filter_type(models.RearPortTemplate, lookups=True)
+class RearPortTemplateFilterV1(ModularComponentTemplateFilterMixinV1):
+    type: Annotated['PortTypeEnum', strawberry.lazy('dcim.graphql.enums')] | None = strawberry_django.filter_field()
+    color: Annotated['ColorEnum', strawberry.lazy('netbox.graphql.enums')] | None = strawberry_django.filter_field()
+    positions: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@strawberry_django.filter_type(models.Region, lookups=True)
+class RegionFilterV1(ContactFilterMixinV1, NestedGroupModelFilterMixinV1):
+    prefixes: Annotated['PrefixFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    vlan_groups: Annotated['VLANGroupFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@strawberry_django.filter_type(models.Site, lookups=True)
+class SiteFilterV1(ContactFilterMixinV1, ImageAttachmentFilterMixinV1, TenancyFilterMixinV1, PrimaryModelFilterMixinV1):
+    name: FilterLookup[str] | None = strawberry_django.filter_field()
+    slug: FilterLookup[str] | None = strawberry_django.filter_field()
+    status: Annotated['SiteStatusEnum', strawberry.lazy('dcim.graphql.enums')] | None = strawberry_django.filter_field()
+    region: Annotated['RegionFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    region_id: Annotated['TreeNodeFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    group: Annotated['SiteGroupFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    group_id: Annotated['TreeNodeFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    facility: FilterLookup[str] | None = strawberry_django.filter_field()
+    asns: Annotated['ASNFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = strawberry_django.filter_field()
+    time_zone: FilterLookup[str] | None = strawberry_django.filter_field()
+    physical_address: FilterLookup[str] | None = strawberry_django.filter_field()
+    shipping_address: FilterLookup[str] | None = strawberry_django.filter_field()
+    latitude: Annotated['FloatLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    longitude: Annotated['FloatLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    prefixes: Annotated['PrefixFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    vlan_groups: Annotated['VLANGroupFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@strawberry_django.filter_type(models.SiteGroup, lookups=True)
+class SiteGroupFilterV1(ContactFilterMixinV1, NestedGroupModelFilterMixinV1):
+    prefixes: Annotated['PrefixFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    vlan_groups: Annotated['VLANGroupFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@strawberry_django.filter_type(models.VirtualChassis, lookups=True)
+class VirtualChassisFilterV1(PrimaryModelFilterMixinV1):
+    master: Annotated['DeviceFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    master_id: ID | None = strawberry_django.filter_field()
+    name: FilterLookup[str] | None = strawberry_django.filter_field()
+    domain: FilterLookup[str] | None = strawberry_django.filter_field()
+    members: (
+        Annotated['DeviceFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None
+    ) = strawberry_django.filter_field()
+    member_count: FilterLookup[int] | None = strawberry_django.filter_field()
+
+
+@strawberry_django.filter_type(models.VirtualDeviceContext, lookups=True)
+class VirtualDeviceContextFilterV1(TenancyFilterMixinV1, PrimaryModelFilterMixinV1):
+    device: Annotated['DeviceFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    device_id: ID | None = strawberry_django.filter_field()
+    name: FilterLookup[str] | None = strawberry_django.filter_field()
+    status: Annotated['VirtualDeviceContextStatusEnum', strawberry.lazy('dcim.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    identifier: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    primary_ip4: Annotated['IPAddressFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    primary_ip4_id: ID | None = strawberry_django.filter_field()
+    primary_ip6: Annotated['IPAddressFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    primary_ip6_id: ID | None = strawberry_django.filter_field()
+    comments: FilterLookup[str] | None = strawberry_django.filter_field()
+    interfaces: (
+        Annotated['InterfaceFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None
+    ) = strawberry_django.filter_field()

+ 137 - 0
netbox/dcim/graphql/gfk_mixins_v1.py

@@ -0,0 +1,137 @@
+from strawberry.types import Info
+
+from circuits.graphql.types_v1 import CircuitTerminationTypeV1, ProviderNetworkTypeV1
+from circuits.models import CircuitTermination, ProviderNetwork
+from dcim.graphql.types_v1 import (
+    ConsolePortTemplateTypeV1,
+    ConsolePortTypeV1,
+    ConsoleServerPortTemplateTypeV1,
+    ConsoleServerPortTypeV1,
+    FrontPortTemplateTypeV1,
+    FrontPortTypeV1,
+    InterfaceTemplateTypeV1,
+    InterfaceTypeV1,
+    PowerFeedTypeV1,
+    PowerOutletTemplateTypeV1,
+    PowerOutletTypeV1,
+    PowerPortTemplateTypeV1,
+    PowerPortTypeV1,
+    RearPortTemplateTypeV1,
+    RearPortTypeV1,
+)
+from dcim.models import (
+    ConsolePort,
+    ConsolePortTemplate,
+    ConsoleServerPort,
+    ConsoleServerPortTemplate,
+    FrontPort,
+    FrontPortTemplate,
+    Interface,
+    InterfaceTemplate,
+    PowerFeed,
+    PowerOutlet,
+    PowerOutletTemplate,
+    PowerPort,
+    PowerPortTemplate,
+    RearPort,
+    RearPortTemplate,
+)
+
+
+class InventoryItemTemplateComponentTypeV1:
+    class Meta:
+        types = (
+            ConsolePortTemplateTypeV1,
+            ConsoleServerPortTemplateTypeV1,
+            FrontPortTemplateTypeV1,
+            InterfaceTemplateTypeV1,
+            PowerOutletTemplateTypeV1,
+            PowerPortTemplateTypeV1,
+            RearPortTemplateTypeV1,
+        )
+
+    @classmethod
+    def resolve_type(cls, instance, info: Info):
+        if type(instance) is ConsolePortTemplate:
+            return ConsolePortTemplateTypeV1
+        if type(instance) is ConsoleServerPortTemplate:
+            return ConsoleServerPortTemplateTypeV1
+        if type(instance) is FrontPortTemplate:
+            return FrontPortTemplateTypeV1
+        if type(instance) is InterfaceTemplate:
+            return InterfaceTemplateTypeV1
+        if type(instance) is PowerOutletTemplate:
+            return PowerOutletTemplateTypeV1
+        if type(instance) is PowerPortTemplate:
+            return PowerPortTemplateTypeV1
+        if type(instance) is RearPortTemplate:
+            return RearPortTemplateTypeV1
+
+
+class InventoryItemComponentTypeV1:
+    class Meta:
+        types = (
+            ConsolePortTypeV1,
+            ConsoleServerPortTypeV1,
+            FrontPortTypeV1,
+            InterfaceTypeV1,
+            PowerOutletTypeV1,
+            PowerPortTypeV1,
+            RearPortTypeV1,
+        )
+
+    @classmethod
+    def resolve_type(cls, instance, info: Info):
+        if type(instance) is ConsolePort:
+            return ConsolePortTypeV1
+        if type(instance) is ConsoleServerPort:
+            return ConsoleServerPortTypeV1
+        if type(instance) is FrontPort:
+            return FrontPortTypeV1
+        if type(instance) is Interface:
+            return InterfaceTypeV1
+        if type(instance) is PowerOutlet:
+            return PowerOutletTypeV1
+        if type(instance) is PowerPort:
+            return PowerPortTypeV1
+        if type(instance) is RearPort:
+            return RearPortTypeV1
+
+
+class ConnectedEndpointTypeV1:
+    class Meta:
+        types = (
+            CircuitTerminationTypeV1,
+            ConsolePortTypeV1,
+            ConsoleServerPortTypeV1,
+            FrontPortTypeV1,
+            InterfaceTypeV1,
+            PowerFeedTypeV1,
+            PowerOutletTypeV1,
+            PowerPortTypeV1,
+            ProviderNetworkTypeV1,
+            RearPortTypeV1,
+        )
+
+    @classmethod
+    def resolve_type(cls, instance, info: Info):
+        if type(instance) is CircuitTermination:
+            return CircuitTerminationTypeV1
+        if type(instance) is ConsolePort:
+            return ConsolePortTypeV1
+        if type(instance) is ConsoleServerPort:
+            return ConsoleServerPortTypeV1
+        if type(instance) is FrontPort:
+            return FrontPortTypeV1
+        if type(instance) is Interface:
+            return InterfaceTypeV1
+        if type(instance) is PowerFeed:
+            return PowerFeedTypeV1
+        if type(instance) is PowerOutlet:
+            return PowerOutletTypeV1
+        if type(instance) is PowerPort:
+            return PowerPortTypeV1
+        if type(instance) is ProviderNetwork:
+            return ProviderNetworkTypeV1
+        if type(instance) is RearPort:
+            return RearPortTypeV1

+ 43 - 0
netbox/dcim/graphql/mixins_v1.py

@@ -0,0 +1,43 @@
+from typing import Annotated, List, Union
+
+import strawberry
+
+__all__ = (
+    'CabledObjectMixinV1',
+    'PathEndpointMixinV1',
+)
+
+
+@strawberry.type
+class CabledObjectMixinV1:
+    cable: Annotated["CableTypeV1", strawberry.lazy('dcim.graphql.types_v1')] | None  # noqa: F821
+
+    link_peers: List[Annotated[Union[
+        Annotated["CircuitTerminationTypeV1", strawberry.lazy('circuits.graphql.types_v1')],  # noqa: F821
+        Annotated["ConsolePortTypeV1", strawberry.lazy('dcim.graphql.types_v1')],  # noqa: F821
+        Annotated["ConsoleServerPortTypeV1", strawberry.lazy('dcim.graphql.types_v1')],  # noqa: F821
+        Annotated["FrontPortTypeV1", strawberry.lazy('dcim.graphql.types_v1')],  # noqa: F821
+        Annotated["InterfaceTypeV1", strawberry.lazy('dcim.graphql.types_v1')],  # noqa: F821
+        Annotated["PowerFeedTypeV1", strawberry.lazy('dcim.graphql.types_v1')],  # noqa: F821
+        Annotated["PowerOutletTypeV1", strawberry.lazy('dcim.graphql.types_v1')],  # noqa: F821
+        Annotated["PowerPortTypeV1", strawberry.lazy('dcim.graphql.types_v1')],  # noqa: F821
+        Annotated["RearPortTypeV1", strawberry.lazy('dcim.graphql.types_v1')],  # noqa: F821
+    ], strawberry.union("LinkPeerType")]]
+
+
+@strawberry.type
+class PathEndpointMixinV1:
+
+    connected_endpoints: List[Annotated[Union[
+        Annotated["CircuitTerminationTypeV1", strawberry.lazy('circuits.graphql.types_v1')],  # noqa: F821
+        Annotated["VirtualCircuitTerminationTypeV1", strawberry.lazy('circuits.graphql.types_v1')],  # noqa: F821
+        Annotated["ConsolePortTypeV1", strawberry.lazy('dcim.graphql.types_v1')],  # noqa: F821
+        Annotated["ConsoleServerPortTypeV1", strawberry.lazy('dcim.graphql.types_v1')],  # noqa: F821
+        Annotated["FrontPortTypeV1", strawberry.lazy('dcim.graphql.types_v1')],  # noqa: F821
+        Annotated["InterfaceTypeV1", strawberry.lazy('dcim.graphql.types_v1')],  # noqa: F821
+        Annotated["PowerFeedTypeV1", strawberry.lazy('dcim.graphql.types_v1')],  # noqa: F821
+        Annotated["PowerOutletTypeV1", strawberry.lazy('dcim.graphql.types_v1')],  # noqa: F821
+        Annotated["PowerPortTypeV1", strawberry.lazy('dcim.graphql.types_v1')],  # noqa: F821
+        Annotated["ProviderNetworkTypeV1", strawberry.lazy('circuits.graphql.types_v1')],  # noqa: F821
+        Annotated["RearPortTypeV1", strawberry.lazy('dcim.graphql.types_v1')],  # noqa: F821
+    ], strawberry.union("ConnectedEndpointTypeV1")]]

+ 138 - 0
netbox/dcim/graphql/schema_v1.py

@@ -0,0 +1,138 @@
+from typing import List
+
+import strawberry
+import strawberry_django
+
+from .types_v1 import *
+
+
+@strawberry.type(name="Query")
+class DCIMQueryV1:
+    cable: CableTypeV1 = strawberry_django.field()
+    cable_list: List[CableTypeV1] = strawberry_django.field()
+
+    console_port: ConsolePortTypeV1 = strawberry_django.field()
+    console_port_list: List[ConsolePortTypeV1] = strawberry_django.field()
+
+    console_port_template: ConsolePortTemplateTypeV1 = strawberry_django.field()
+    console_port_template_list: List[ConsolePortTemplateTypeV1] = strawberry_django.field()
+
+    console_server_port: ConsoleServerPortTypeV1 = strawberry_django.field()
+    console_server_port_list: List[ConsoleServerPortTypeV1] = strawberry_django.field()
+
+    console_server_port_template: ConsoleServerPortTemplateTypeV1 = strawberry_django.field()
+    console_server_port_template_list: List[ConsoleServerPortTemplateTypeV1] = strawberry_django.field()
+
+    device: DeviceTypeV1 = strawberry_django.field()
+    device_list: List[DeviceTypeV1] = strawberry_django.field()
+
+    device_bay: DeviceBayTypeV1 = strawberry_django.field()
+    device_bay_list: List[DeviceBayTypeV1] = strawberry_django.field()
+
+    device_bay_template: DeviceBayTemplateTypeV1 = strawberry_django.field()
+    device_bay_template_list: List[DeviceBayTemplateTypeV1] = strawberry_django.field()
+
+    device_role: DeviceRoleTypeV1 = strawberry_django.field()
+    device_role_list: List[DeviceRoleTypeV1] = strawberry_django.field()
+
+    device_type: DeviceTypeTypeV1 = strawberry_django.field()
+    device_type_list: List[DeviceTypeTypeV1] = strawberry_django.field()
+
+    front_port: FrontPortTypeV1 = strawberry_django.field()
+    front_port_list: List[FrontPortTypeV1] = strawberry_django.field()
+
+    front_port_template: FrontPortTemplateTypeV1 = strawberry_django.field()
+    front_port_template_list: List[FrontPortTemplateTypeV1] = strawberry_django.field()
+
+    mac_address: MACAddressTypeV1 = strawberry_django.field()
+    mac_address_list: List[MACAddressTypeV1] = strawberry_django.field()
+
+    interface: InterfaceTypeV1 = strawberry_django.field()
+    interface_list: List[InterfaceTypeV1] = strawberry_django.field()
+
+    interface_template: InterfaceTemplateTypeV1 = strawberry_django.field()
+    interface_template_list: List[InterfaceTemplateTypeV1] = strawberry_django.field()
+
+    inventory_item: InventoryItemTypeV1 = strawberry_django.field()
+    inventory_item_list: List[InventoryItemTypeV1] = strawberry_django.field()
+
+    inventory_item_role: InventoryItemRoleTypeV1 = strawberry_django.field()
+    inventory_item_role_list: List[InventoryItemRoleTypeV1] = strawberry_django.field()
+
+    inventory_item_template: InventoryItemTemplateTypeV1 = strawberry_django.field()
+    inventory_item_template_list: List[InventoryItemTemplateTypeV1] = strawberry_django.field()
+
+    location: LocationTypeV1 = strawberry_django.field()
+    location_list: List[LocationTypeV1] = strawberry_django.field()
+
+    manufacturer: ManufacturerTypeV1 = strawberry_django.field()
+    manufacturer_list: List[ManufacturerTypeV1] = strawberry_django.field()
+
+    module: ModuleTypeV1 = strawberry_django.field()
+    module_list: List[ModuleTypeV1] = strawberry_django.field()
+
+    module_bay: ModuleBayTypeV1 = strawberry_django.field()
+    module_bay_list: List[ModuleBayTypeV1] = strawberry_django.field()
+
+    module_bay_template: ModuleBayTemplateTypeV1 = strawberry_django.field()
+    module_bay_template_list: List[ModuleBayTemplateTypeV1] = strawberry_django.field()
+
+    module_type_profile: ModuleTypeProfileTypeV1 = strawberry_django.field()
+    module_type_profile_list: List[ModuleTypeProfileTypeV1] = strawberry_django.field()
+
+    module_type: ModuleTypeTypeV1 = strawberry_django.field()
+    module_type_list: List[ModuleTypeTypeV1] = strawberry_django.field()
+
+    platform: PlatformTypeV1 = strawberry_django.field()
+    platform_list: List[PlatformTypeV1] = strawberry_django.field()
+
+    power_feed: PowerFeedTypeV1 = strawberry_django.field()
+    power_feed_list: List[PowerFeedTypeV1] = strawberry_django.field()
+
+    power_outlet: PowerOutletTypeV1 = strawberry_django.field()
+    power_outlet_list: List[PowerOutletTypeV1] = strawberry_django.field()
+
+    power_outlet_template: PowerOutletTemplateTypeV1 = strawberry_django.field()
+    power_outlet_template_list: List[PowerOutletTemplateTypeV1] = strawberry_django.field()
+
+    power_panel: PowerPanelTypeV1 = strawberry_django.field()
+    power_panel_list: List[PowerPanelTypeV1] = strawberry_django.field()
+
+    power_port: PowerPortTypeV1 = strawberry_django.field()
+    power_port_list: List[PowerPortTypeV1] = strawberry_django.field()
+
+    power_port_template: PowerPortTemplateTypeV1 = strawberry_django.field()
+    power_port_template_list: List[PowerPortTemplateTypeV1] = strawberry_django.field()
+
+    rack_type: RackTypeTypeV1 = strawberry_django.field()
+    rack_type_list: List[RackTypeTypeV1] = strawberry_django.field()
+
+    rack: RackTypeV1 = strawberry_django.field()
+    rack_list: List[RackTypeV1] = strawberry_django.field()
+
+    rack_reservation: RackReservationTypeV1 = strawberry_django.field()
+    rack_reservation_list: List[RackReservationTypeV1] = strawberry_django.field()
+
+    rack_role: RackRoleTypeV1 = strawberry_django.field()
+    rack_role_list: List[RackRoleTypeV1] = strawberry_django.field()
+
+    rear_port: RearPortTypeV1 = strawberry_django.field()
+    rear_port_list: List[RearPortTypeV1] = strawberry_django.field()
+
+    rear_port_template: RearPortTemplateTypeV1 = strawberry_django.field()
+    rear_port_template_list: List[RearPortTemplateTypeV1] = strawberry_django.field()
+
+    region: RegionTypeV1 = strawberry_django.field()
+    region_list: List[RegionTypeV1] = strawberry_django.field()
+
+    site: SiteTypeV1 = strawberry_django.field()
+    site_list: List[SiteTypeV1] = strawberry_django.field()
+
+    site_group: SiteGroupTypeV1 = strawberry_django.field()
+    site_group_list: List[SiteGroupTypeV1] = strawberry_django.field()
+
+    virtual_chassis: VirtualChassisTypeV1 = strawberry_django.field()
+    virtual_chassis_list: List[VirtualChassisTypeV1] = strawberry_django.field()
+
+    virtual_device_context: VirtualDeviceContextTypeV1 = strawberry_django.field()
+    virtual_device_context_list: List[VirtualDeviceContextTypeV1] = strawberry_django.field()

+ 906 - 0
netbox/dcim/graphql/types_v1.py

@@ -0,0 +1,906 @@
+from typing import Annotated, List, TYPE_CHECKING, Union
+
+import strawberry
+import strawberry_django
+
+from core.graphql.mixins_v1 import ChangelogMixinV1
+from dcim import models
+from extras.graphql.mixins_v1 import (
+    ConfigContextMixinV1,
+    ContactsMixinV1,
+    CustomFieldsMixinV1,
+    ImageAttachmentsMixinV1,
+    TagsMixinV1,
+)
+from ipam.graphql.mixins_v1 import IPAddressesMixinV1, VLANGroupsMixinV1
+from netbox.graphql.scalars import BigInt
+from netbox.graphql.types_v1 import BaseObjectTypeV1, NetBoxObjectTypeV1, OrganizationalObjectTypeV1
+from .filters_v1 import *
+from .mixins_v1 import CabledObjectMixinV1, PathEndpointMixinV1
+
+if TYPE_CHECKING:
+    from circuits.graphql.types_v1 import CircuitTerminationTypeV1
+    from extras.graphql.types_v1 import ConfigTemplateTypeV1
+    from ipam.graphql.types_v1 import (
+        ASNTypeV1,
+        IPAddressTypeV1,
+        PrefixTypeV1,
+        ServiceTypeV1,
+        VLANTranslationPolicyTypeV1,
+        VLANTypeV1,
+        VRFTypeV1,
+    )
+    from tenancy.graphql.types_v1 import TenantTypeV1
+    from users.graphql.types_v1 import UserTypeV1
+    from virtualization.graphql.types_v1 import ClusterTypeV1, VMInterfaceTypeV1, VirtualMachineTypeV1
+    from vpn.graphql.types_v1 import L2VPNTerminationTypeV1
+    from wireless.graphql.types_v1 import WirelessLANTypeV1, WirelessLinkTypeV1
+
+__all__ = (
+    'CableTypeV1',
+    'ComponentTypeV1',
+    'ConsolePortTypeV1',
+    'ConsolePortTemplateTypeV1',
+    'ConsoleServerPortTypeV1',
+    'ConsoleServerPortTemplateTypeV1',
+    'DeviceTypeV1',
+    'DeviceBayTypeV1',
+    'DeviceBayTemplateTypeV1',
+    'DeviceRoleTypeV1',
+    'DeviceTypeTypeV1',
+    'FrontPortTypeV1',
+    'FrontPortTemplateTypeV1',
+    'InterfaceTypeV1',
+    'InterfaceTemplateTypeV1',
+    'InventoryItemTypeV1',
+    'InventoryItemRoleTypeV1',
+    'InventoryItemTemplateTypeV1',
+    'LocationTypeV1',
+    'MACAddressTypeV1',
+    'ManufacturerTypeV1',
+    'ModularComponentTypeV1',
+    'ModuleTypeV1',
+    'ModuleBayTypeV1',
+    'ModuleBayTemplateTypeV1',
+    'ModuleTypeProfileTypeV1',
+    'ModuleTypeTypeV1',
+    'PlatformTypeV1',
+    'PowerFeedTypeV1',
+    'PowerOutletTypeV1',
+    'PowerOutletTemplateTypeV1',
+    'PowerPanelTypeV1',
+    'PowerPortTypeV1',
+    'PowerPortTemplateTypeV1',
+    'RackTypeV1',
+    'RackReservationTypeV1',
+    'RackRoleTypeV1',
+    'RackTypeTypeV1',
+    'RearPortTypeV1',
+    'RearPortTemplateTypeV1',
+    'RegionTypeV1',
+    'SiteTypeV1',
+    'SiteGroupTypeV1',
+    'VirtualChassisTypeV1',
+    'VirtualDeviceContextTypeV1',
+)
+
+
+#
+# Base types
+#
+
+
+@strawberry.type
+class ComponentTypeV1(
+    ChangelogMixinV1,
+    CustomFieldsMixinV1,
+    TagsMixinV1,
+    BaseObjectTypeV1
+):
+    """
+    Base type for device/VM components
+    """
+    device: Annotated["DeviceTypeV1", strawberry.lazy('dcim.graphql.types_v1')]
+
+
+@strawberry.type
+class ModularComponentTypeV1(ComponentTypeV1):
+    module: Annotated["ModuleTypeV1", strawberry.lazy('dcim.graphql.types_v1')] | None
+
+
+@strawberry.type
+class ComponentTemplateTypeV1(
+    ChangelogMixinV1,
+    BaseObjectTypeV1
+):
+    """
+    Base type for device/VM components
+    """
+    device_type: Annotated["DeviceTypeTypeV1", strawberry.lazy('dcim.graphql.types_v1')]
+
+
+@strawberry.type
+class ModularComponentTemplateTypeV1(ComponentTemplateTypeV1):
+    """
+    Base type for ComponentTemplateModel which supports optional assignment to a ModuleType.
+    """
+    device_type: Annotated["DeviceTypeTypeV1", strawberry.lazy('dcim.graphql.types_v1')] | None
+    module_type: Annotated["ModuleTypeTypeV1", strawberry.lazy('dcim.graphql.types_v1')] | None
+
+#
+# Model types
+#
+
+
+@strawberry_django.type(
+    models.CableTermination,
+    exclude=['termination_type', 'termination_id', '_device', '_rack', '_location', '_site'],
+    filters=CableTerminationFilterV1,
+    pagination=True
+)
+class CableTerminationTypeV1(NetBoxObjectTypeV1):
+    cable: Annotated["CableTypeV1", strawberry.lazy('dcim.graphql.types_v1')] | None
+    termination: Annotated[Union[
+        Annotated["CircuitTerminationTypeV1", strawberry.lazy('circuits.graphql.types_v1')],
+        Annotated["ConsolePortTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+        Annotated["ConsoleServerPortTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+        Annotated["FrontPortTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+        Annotated["InterfaceTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+        Annotated["PowerFeedTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+        Annotated["PowerOutletTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+        Annotated["PowerPortTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+        Annotated["RearPortTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+    ], strawberry.union("CableTerminationTerminationTypeV1")] | None
+
+
+@strawberry_django.type(
+    models.Cable,
+    fields='__all__',
+    filters=CableFilterV1,
+    pagination=True
+)
+class CableTypeV1(NetBoxObjectTypeV1):
+    color: str
+    tenant: Annotated["TenantTypeV1", strawberry.lazy('tenancy.graphql.types_v1')] | None
+
+    terminations: List[CableTerminationTypeV1]
+
+    a_terminations: List[Annotated[Union[
+        Annotated["CircuitTerminationTypeV1", strawberry.lazy('circuits.graphql.types_v1')],
+        Annotated["ConsolePortTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+        Annotated["ConsoleServerPortTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+        Annotated["FrontPortTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+        Annotated["InterfaceTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+        Annotated["PowerFeedTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+        Annotated["PowerOutletTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+        Annotated["PowerPortTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+        Annotated["RearPortTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+    ], strawberry.union("CableTerminationTerminationTypeV1")]]
+
+    b_terminations: List[Annotated[Union[
+        Annotated["CircuitTerminationTypeV1", strawberry.lazy('circuits.graphql.types_v1')],
+        Annotated["ConsolePortTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+        Annotated["ConsoleServerPortTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+        Annotated["FrontPortTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+        Annotated["InterfaceTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+        Annotated["PowerFeedTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+        Annotated["PowerOutletTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+        Annotated["PowerPortTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+        Annotated["RearPortTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+    ], strawberry.union("CableTerminationTerminationTypeV1")]]
+
+
+@strawberry_django.type(
+    models.ConsolePort,
+    exclude=['_path'],
+    filters=ConsolePortFilterV1,
+    pagination=True
+)
+class ConsolePortTypeV1(ModularComponentTypeV1, CabledObjectMixinV1, PathEndpointMixinV1):
+    pass
+
+
+@strawberry_django.type(
+    models.ConsolePortTemplate,
+    fields='__all__',
+    filters=ConsolePortTemplateFilterV1,
+    pagination=True
+)
+class ConsolePortTemplateTypeV1(ModularComponentTemplateTypeV1):
+    pass
+
+
+@strawberry_django.type(
+    models.ConsoleServerPort,
+    exclude=['_path'],
+    filters=ConsoleServerPortFilterV1,
+    pagination=True
+)
+class ConsoleServerPortTypeV1(ModularComponentTypeV1, CabledObjectMixinV1, PathEndpointMixinV1):
+    pass
+
+
+@strawberry_django.type(
+    models.ConsoleServerPortTemplate,
+    fields='__all__',
+    filters=ConsoleServerPortTemplateFilterV1,
+    pagination=True
+)
+class ConsoleServerPortTemplateTypeV1(ModularComponentTemplateTypeV1):
+    pass
+
+
+@strawberry_django.type(
+    models.Device,
+    fields='__all__',
+    filters=DeviceFilterV1,
+    pagination=True
+)
+class DeviceTypeV1(ConfigContextMixinV1, ImageAttachmentsMixinV1, ContactsMixinV1, NetBoxObjectTypeV1):
+    console_port_count: BigInt
+    console_server_port_count: BigInt
+    power_port_count: BigInt
+    power_outlet_count: BigInt
+    interface_count: BigInt
+    front_port_count: BigInt
+    rear_port_count: BigInt
+    device_bay_count: BigInt
+    module_bay_count: BigInt
+    inventory_item_count: BigInt
+    config_template: Annotated["ConfigTemplateTypeV1", strawberry.lazy('extras.graphql.types_v1')] | None
+    device_type: Annotated["DeviceTypeTypeV1", strawberry.lazy('dcim.graphql.types_v1')]
+    role: Annotated["DeviceRoleTypeV1", strawberry.lazy('dcim.graphql.types_v1')]
+    tenant: Annotated["TenantTypeV1", strawberry.lazy('tenancy.graphql.types_v1')] | None
+    platform: Annotated["PlatformTypeV1", strawberry.lazy('dcim.graphql.types_v1')] | None
+    site: Annotated["SiteTypeV1", strawberry.lazy('dcim.graphql.types_v1')]
+    location: Annotated["LocationTypeV1", strawberry.lazy('dcim.graphql.types_v1')] | None
+    rack: Annotated["RackTypeV1", strawberry.lazy('dcim.graphql.types_v1')] | None
+    primary_ip4: Annotated["IPAddressTypeV1", strawberry.lazy('ipam.graphql.types_v1')] | None
+    primary_ip6: Annotated["IPAddressTypeV1", strawberry.lazy('ipam.graphql.types_v1')] | None
+    oob_ip: Annotated["IPAddressTypeV1", strawberry.lazy('ipam.graphql.types_v1')] | None
+    cluster: Annotated["ClusterTypeV1", strawberry.lazy('virtualization.graphql.types_v1')] | None
+    virtual_chassis: Annotated["VirtualChassisTypeV1", strawberry.lazy('dcim.graphql.types_v1')] | None
+
+    virtual_machines: List[Annotated["VirtualMachineTypeV1", strawberry.lazy('virtualization.graphql.types_v1')]]
+    modules: List[Annotated["ModuleTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    interfaces: List[Annotated["InterfaceTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    rearports: List[Annotated["RearPortTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    consoleports: List[Annotated["ConsolePortTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    powerports: List[Annotated["PowerPortTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    cabletermination_set: List[Annotated["CableTerminationTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    consoleserverports: List[Annotated["ConsoleServerPortTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    poweroutlets: List[Annotated["PowerOutletTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    frontports: List[Annotated["FrontPortTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    devicebays: List[Annotated["DeviceBayTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    modulebays: List[Annotated["ModuleBayTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    services: List[Annotated["ServiceTypeV1", strawberry.lazy('ipam.graphql.types_v1')]]
+    inventoryitems: List[Annotated["InventoryItemTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    vdcs: List[Annotated["VirtualDeviceContextTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+
+    @strawberry_django.field
+    def vc_master_for(self) -> Annotated["VirtualChassisTypeV1", strawberry.lazy('dcim.graphql.types_v1')] | None:
+        return self.vc_master_for if hasattr(self, 'vc_master_for') else None
+
+    @strawberry_django.field
+    def parent_bay(self) -> Annotated["DeviceBayTypeV1", strawberry.lazy('dcim.graphql.types_v1')] | None:
+        return self.parent_bay if hasattr(self, 'parent_bay') else None
+
+
+@strawberry_django.type(
+    models.DeviceBay,
+    fields='__all__',
+    filters=DeviceBayFilterV1,
+    pagination=True
+)
+class DeviceBayTypeV1(ComponentTypeV1):
+    installed_device: Annotated["DeviceTypeV1", strawberry.lazy('dcim.graphql.types_v1')] | None
+
+
+@strawberry_django.type(
+    models.DeviceBayTemplate,
+    fields='__all__',
+    filters=DeviceBayTemplateFilterV1,
+    pagination=True
+)
+class DeviceBayTemplateTypeV1(ComponentTemplateTypeV1):
+    pass
+
+
+@strawberry_django.type(
+    models.InventoryItemTemplate,
+    exclude=['component_type', 'component_id', 'parent'],
+    filters=InventoryItemTemplateFilterV1,
+    pagination=True
+)
+class InventoryItemTemplateTypeV1(ComponentTemplateTypeV1):
+    role: Annotated["InventoryItemRoleTypeV1", strawberry.lazy('dcim.graphql.types_v1')] | None
+    manufacturer: Annotated["ManufacturerTypeV1", strawberry.lazy('dcim.graphql.types_v1')]
+
+    @strawberry_django.field
+    def parent(self) -> Annotated["InventoryItemTemplateTypeV1", strawberry.lazy('dcim.graphql.types_v1')] | None:
+        return self.parent
+
+    child_items: List[Annotated["InventoryItemTemplateTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+
+    component: Annotated[Union[
+        Annotated["ConsolePortTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+        Annotated["ConsoleServerPortTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+        Annotated["FrontPortTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+        Annotated["InterfaceTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+        Annotated["PowerOutletTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+        Annotated["PowerPortTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+        Annotated["RearPortTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+    ], strawberry.union("InventoryItemTemplateComponentTypeV1")] | None
+
+
+@strawberry_django.type(
+    models.DeviceRole,
+    fields='__all__',
+    filters=DeviceRoleFilterV1,
+    pagination=True
+)
+class DeviceRoleTypeV1(OrganizationalObjectTypeV1):
+    parent: Annotated['DeviceRoleTypeV1', strawberry.lazy('dcim.graphql.types_v1')] | None
+    children: List[Annotated['DeviceRoleTypeV1', strawberry.lazy('dcim.graphql.types_v1')]]
+    color: str
+    config_template: Annotated["ConfigTemplateTypeV1", strawberry.lazy('extras.graphql.types_v1')] | None
+
+    virtual_machines: List[Annotated["VirtualMachineTypeV1", strawberry.lazy('virtualization.graphql.types_v1')]]
+    devices: List[Annotated["DeviceTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+
+
+@strawberry_django.type(
+    models.DeviceType,
+    fields='__all__',
+    filters=DeviceTypeFilterV1,
+    pagination=True
+)
+class DeviceTypeTypeV1(NetBoxObjectTypeV1):
+    console_port_template_count: BigInt
+    console_server_port_template_count: BigInt
+    power_port_template_count: BigInt
+    power_outlet_template_count: BigInt
+    interface_template_count: BigInt
+    front_port_template_count: BigInt
+    rear_port_template_count: BigInt
+    device_bay_template_count: BigInt
+    module_bay_template_count: BigInt
+    inventory_item_template_count: BigInt
+    front_image: strawberry_django.fields.types.DjangoImageType | None
+    rear_image: strawberry_django.fields.types.DjangoImageType | None
+    manufacturer: Annotated["ManufacturerTypeV1", strawberry.lazy('dcim.graphql.types_v1')]
+    default_platform: Annotated["PlatformTypeV1", strawberry.lazy('dcim.graphql.types_v1')] | None
+
+    frontporttemplates: List[Annotated["FrontPortTemplateTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    modulebaytemplates: List[Annotated["ModuleBayTemplateTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    instances: List[Annotated["DeviceTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    poweroutlettemplates: List[Annotated["PowerOutletTemplateTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    powerporttemplates: List[Annotated["PowerPortTemplateTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    inventoryitemtemplates: List[Annotated["InventoryItemTemplateTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    rearporttemplates: List[Annotated["RearPortTemplateTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    consoleserverporttemplates: List[
+        Annotated["ConsoleServerPortTemplateTypeV1", strawberry.lazy('dcim.graphql.types_v1')]
+    ]
+    interfacetemplates: List[Annotated["InterfaceTemplateTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    devicebaytemplates: List[Annotated["DeviceBayTemplateTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    consoleporttemplates: List[Annotated["ConsolePortTemplateTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+
+
+@strawberry_django.type(
+    models.FrontPort,
+    fields='__all__',
+    filters=FrontPortFilterV1,
+    pagination=True
+)
+class FrontPortTypeV1(ModularComponentTypeV1, CabledObjectMixinV1):
+    color: str
+    rear_port: Annotated["RearPortTypeV1", strawberry.lazy('dcim.graphql.types_v1')]
+
+
+@strawberry_django.type(
+    models.FrontPortTemplate,
+    fields='__all__',
+    filters=FrontPortTemplateFilterV1,
+    pagination=True
+)
+class FrontPortTemplateTypeV1(ModularComponentTemplateTypeV1):
+    color: str
+    rear_port: Annotated["RearPortTemplateTypeV1", strawberry.lazy('dcim.graphql.types_v1')]
+
+
+@strawberry_django.type(
+    models.MACAddress,
+    exclude=['assigned_object_type', 'assigned_object_id'],
+    filters=MACAddressFilterV1,
+    pagination=True
+)
+class MACAddressTypeV1(NetBoxObjectTypeV1):
+    mac_address: str
+
+    @strawberry_django.field
+    def assigned_object(self) -> Annotated[Union[
+        Annotated["InterfaceTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+        Annotated["VMInterfaceTypeV1", strawberry.lazy('virtualization.graphql.types_v1')],
+    ], strawberry.union("MACAddressAssignmentTypeV1")] | None:
+        return self.assigned_object
+
+
+@strawberry_django.type(
+    models.Interface,
+    exclude=['_path'],
+    filters=InterfaceFilterV1,
+    pagination=True
+)
+class InterfaceTypeV1(IPAddressesMixinV1, ModularComponentTypeV1, CabledObjectMixinV1, PathEndpointMixinV1):
+    _name: str
+    wwn: str | None
+    parent: Annotated["InterfaceTypeV1", strawberry.lazy('dcim.graphql.types_v1')] | None
+    bridge: Annotated["InterfaceTypeV1", strawberry.lazy('dcim.graphql.types_v1')] | None
+    lag: Annotated["InterfaceTypeV1", strawberry.lazy('dcim.graphql.types_v1')] | None
+    wireless_link: Annotated["WirelessLinkTypeV1", strawberry.lazy('wireless.graphql.types_v1')] | None
+    untagged_vlan: Annotated["VLANTypeV1", strawberry.lazy('ipam.graphql.types_v1')] | None
+    vrf: Annotated["VRFTypeV1", strawberry.lazy('ipam.graphql.types_v1')] | None
+    primary_mac_address: Annotated["MACAddressTypeV1", strawberry.lazy('dcim.graphql.types_v1')] | None
+    qinq_svlan: Annotated["VLANTypeV1", strawberry.lazy('ipam.graphql.types_v1')] | None
+    vlan_translation_policy: Annotated["VLANTranslationPolicyTypeV1", strawberry.lazy('ipam.graphql.types_v1')] | None
+    l2vpn_termination: Annotated["L2VPNTerminationTypeV1", strawberry.lazy('vpn.graphql.types_v1')] | None
+
+    vdcs: List[Annotated["VirtualDeviceContextTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    tagged_vlans: List[Annotated["VLANTypeV1", strawberry.lazy('ipam.graphql.types_v1')]]
+    bridge_interfaces: List[Annotated["InterfaceTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    wireless_lans: List[Annotated["WirelessLANTypeV1", strawberry.lazy('wireless.graphql.types_v1')]]
+    member_interfaces: List[Annotated["InterfaceTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    child_interfaces: List[Annotated["InterfaceTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    mac_addresses: List[Annotated["MACAddressTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+
+
+@strawberry_django.type(
+    models.InterfaceTemplate,
+    fields='__all__',
+    filters=InterfaceTemplateFilterV1,
+    pagination=True
+)
+class InterfaceTemplateTypeV1(ModularComponentTemplateTypeV1):
+    _name: str
+    bridge: Annotated["InterfaceTemplateTypeV1", strawberry.lazy('dcim.graphql.types_v1')] | None
+
+    bridge_interfaces: List[Annotated["InterfaceTemplateTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+
+
+@strawberry_django.type(
+    models.InventoryItem,
+    exclude=['component_type', 'component_id', 'parent'],
+    filters=InventoryItemFilterV1,
+    pagination=True
+)
+class InventoryItemTypeV1(ComponentTypeV1):
+    role: Annotated["InventoryItemRoleTypeV1", strawberry.lazy('dcim.graphql.types_v1')] | None
+    manufacturer: Annotated["ManufacturerTypeV1", strawberry.lazy('dcim.graphql.types_v1')] | None
+
+    child_items: List[Annotated["InventoryItemTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+
+    @strawberry_django.field
+    def parent(self) -> Annotated["InventoryItemTypeV1", strawberry.lazy('dcim.graphql.types_v1')] | None:
+        return self.parent
+
+    component: Annotated[Union[
+        Annotated["ConsolePortTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+        Annotated["ConsoleServerPortTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+        Annotated["FrontPortTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+        Annotated["InterfaceTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+        Annotated["PowerOutletTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+        Annotated["PowerPortTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+        Annotated["RearPortTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+    ], strawberry.union("InventoryItemComponentTypeV1")] | None
+
+
+@strawberry_django.type(
+    models.InventoryItemRole,
+    fields='__all__',
+    filters=InventoryItemRoleFilterV1,
+    pagination=True
+)
+class InventoryItemRoleTypeV1(OrganizationalObjectTypeV1):
+    color: str
+
+    inventory_items: List[Annotated["InventoryItemTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    inventory_item_templates: List[Annotated["InventoryItemTemplateTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+
+
+@strawberry_django.type(
+    models.Location,
+    # fields='__all__',
+    exclude=['parent'],  # bug - temp
+    filters=LocationFilterV1,
+    pagination=True
+)
+class LocationTypeV1(VLANGroupsMixinV1, ImageAttachmentsMixinV1, ContactsMixinV1, OrganizationalObjectTypeV1):
+    site: Annotated["SiteTypeV1", strawberry.lazy('dcim.graphql.types_v1')]
+    tenant: Annotated["TenantTypeV1", strawberry.lazy('tenancy.graphql.types_v1')] | None
+    parent: Annotated["LocationTypeV1", strawberry.lazy('dcim.graphql.types_v1')] | None
+
+    powerpanel_set: List[Annotated["PowerPanelTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    cabletermination_set: List[Annotated["CableTerminationTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    racks: List[Annotated["RackTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    devices: List[Annotated["DeviceTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    children: List[Annotated["LocationTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+
+    @strawberry_django.field
+    def clusters(self) -> List[Annotated["ClusterTypeV1", strawberry.lazy('virtualization.graphql.types_v1')]]:
+        return self.cluster_set.all()
+
+    @strawberry_django.field
+    def circuit_terminations(self) -> List[
+        Annotated["CircuitTerminationTypeV1", strawberry.lazy('circuits.graphql.types_v1')]
+    ]:
+        return self.circuit_terminations.all()
+
+
+@strawberry_django.type(
+    models.Manufacturer,
+    fields='__all__',
+    filters=ManufacturerFilterV1,
+    pagination=True
+)
+class ManufacturerTypeV1(OrganizationalObjectTypeV1, ContactsMixinV1):
+
+    platforms: List[Annotated["PlatformTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    device_types: List[Annotated["DeviceTypeTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    inventory_item_templates: List[Annotated["InventoryItemTemplateTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    inventory_items: List[Annotated["InventoryItemTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    module_types: List[Annotated["ModuleTypeTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+
+
+@strawberry_django.type(
+    models.Module,
+    fields='__all__',
+    filters=ModuleFilterV1,
+    pagination=True
+)
+class ModuleTypeV1(NetBoxObjectTypeV1):
+    device: Annotated["DeviceTypeV1", strawberry.lazy('dcim.graphql.types_v1')]
+    module_bay: Annotated["ModuleBayTypeV1", strawberry.lazy('dcim.graphql.types_v1')]
+    module_type: Annotated["ModuleTypeTypeV1", strawberry.lazy('dcim.graphql.types_v1')]
+
+    interfaces: List[Annotated["InterfaceTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    powerports: List[Annotated["PowerPortTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    consoleserverports: List[Annotated["ConsoleServerPortTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    consoleports: List[Annotated["ConsolePortTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    poweroutlets: List[Annotated["PowerOutletTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    rearports: List[Annotated["RearPortTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    frontports: List[Annotated["FrontPortTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+
+
+@strawberry_django.type(
+    models.ModuleBay,
+    # fields='__all__',
+    exclude=['parent'],
+    filters=ModuleBayFilterV1,
+    pagination=True
+)
+class ModuleBayTypeV1(ModularComponentTypeV1):
+
+    installed_module: Annotated["ModuleTypeV1", strawberry.lazy('dcim.graphql.types_v1')] | None
+    children: List[Annotated["ModuleBayTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+
+    @strawberry_django.field
+    def parent(self) -> Annotated["ModuleBayTypeV1", strawberry.lazy('dcim.graphql.types_v1')] | None:
+        return self.parent
+
+
+@strawberry_django.type(
+    models.ModuleBayTemplate,
+    fields='__all__',
+    filters=ModuleBayTemplateFilterV1,
+    pagination=True
+)
+class ModuleBayTemplateTypeV1(ModularComponentTemplateTypeV1):
+    pass
+
+
+@strawberry_django.type(
+    models.ModuleTypeProfile,
+    fields='__all__',
+    filters=ModuleTypeProfileFilterV1,
+    pagination=True
+)
+class ModuleTypeProfileTypeV1(NetBoxObjectTypeV1):
+    module_types: List[Annotated["ModuleTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+
+
+@strawberry_django.type(
+    models.ModuleType,
+    fields='__all__',
+    filters=ModuleTypeFilterV1,
+    pagination=True
+)
+class ModuleTypeTypeV1(NetBoxObjectTypeV1):
+    profile: Annotated["ModuleTypeProfileTypeV1", strawberry.lazy('dcim.graphql.types_v1')] | None
+    manufacturer: Annotated["ManufacturerTypeV1", strawberry.lazy('dcim.graphql.types_v1')]
+
+    frontporttemplates: List[Annotated["FrontPortTemplateTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    consoleserverporttemplates: List[
+        Annotated["ConsoleServerPortTemplateTypeV1", strawberry.lazy('dcim.graphql.types_v1')]
+    ]
+    interfacetemplates: List[Annotated["InterfaceTemplateTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    powerporttemplates: List[Annotated["PowerPortTemplateTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    poweroutlettemplates: List[Annotated["PowerOutletTemplateTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    rearporttemplates: List[Annotated["RearPortTemplateTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    instances: List[Annotated["ModuleTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    consoleporttemplates: List[Annotated["ConsolePortTemplateTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+
+
+@strawberry_django.type(
+    models.Platform,
+    fields='__all__',
+    filters=PlatformFilterV1,
+    pagination=True
+)
+class PlatformTypeV1(OrganizationalObjectTypeV1):
+    parent: Annotated['PlatformTypeV1', strawberry.lazy('dcim.graphql.types_v1')] | None
+    children: List[Annotated['PlatformTypeV1', strawberry.lazy('dcim.graphql.types_v1')]]
+    manufacturer: Annotated["ManufacturerTypeV1", strawberry.lazy('dcim.graphql.types_v1')] | None
+    config_template: Annotated["ConfigTemplateTypeV1", strawberry.lazy('extras.graphql.types_v1')] | None
+
+    virtual_machines: List[Annotated["VirtualMachineTypeV1", strawberry.lazy('virtualization.graphql.types_v1')]]
+    devices: List[Annotated["DeviceTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+
+
+@strawberry_django.type(
+    models.PowerFeed,
+    exclude=['_path'],
+    filters=PowerFeedFilterV1,
+    pagination=True
+)
+class PowerFeedTypeV1(NetBoxObjectTypeV1, CabledObjectMixinV1, PathEndpointMixinV1):
+    power_panel: Annotated["PowerPanelTypeV1", strawberry.lazy('dcim.graphql.types_v1')]
+    rack: Annotated["RackTypeV1", strawberry.lazy('dcim.graphql.types_v1')] | None
+    tenant: Annotated["TenantTypeV1", strawberry.lazy('tenancy.graphql.types_v1')] | None
+
+
+@strawberry_django.type(
+    models.PowerOutlet,
+    exclude=['_path'],
+    filters=PowerOutletFilterV1,
+    pagination=True
+)
+class PowerOutletTypeV1(ModularComponentTypeV1, CabledObjectMixinV1, PathEndpointMixinV1):
+    power_port: Annotated["PowerPortTypeV1", strawberry.lazy('dcim.graphql.types_v1')] | None
+    color: str
+
+
+@strawberry_django.type(
+    models.PowerOutletTemplate,
+    fields='__all__',
+    filters=PowerOutletTemplateFilterV1,
+    pagination=True
+)
+class PowerOutletTemplateTypeV1(ModularComponentTemplateTypeV1):
+    power_port: Annotated["PowerPortTemplateTypeV1", strawberry.lazy('dcim.graphql.types_v1')] | None
+
+
+@strawberry_django.type(
+    models.PowerPanel,
+    fields='__all__',
+    filters=PowerPanelFilterV1,
+    pagination=True
+)
+class PowerPanelTypeV1(NetBoxObjectTypeV1, ContactsMixinV1):
+    site: Annotated["SiteTypeV1", strawberry.lazy('dcim.graphql.types_v1')]
+    location: Annotated["LocationTypeV1", strawberry.lazy('dcim.graphql.types_v1')] | None
+
+    powerfeeds: List[Annotated["PowerFeedTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+
+
+@strawberry_django.type(
+    models.PowerPort,
+    exclude=['_path'],
+    filters=PowerPortFilterV1,
+    pagination=True
+)
+class PowerPortTypeV1(ModularComponentTypeV1, CabledObjectMixinV1, PathEndpointMixinV1):
+
+    poweroutlets: List[Annotated["PowerOutletTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+
+
+@strawberry_django.type(
+    models.PowerPortTemplate,
+    fields='__all__',
+    filters=PowerPortTemplateFilterV1,
+    pagination=True
+)
+class PowerPortTemplateTypeV1(ModularComponentTemplateTypeV1):
+    poweroutlet_templates: List[Annotated["PowerOutletTemplateTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+
+
+@strawberry_django.type(
+    models.RackType,
+    fields='__all__',
+    filters=RackTypeFilterV1,
+    pagination=True
+)
+class RackTypeTypeV1(NetBoxObjectTypeV1):
+    manufacturer: Annotated["ManufacturerTypeV1", strawberry.lazy('dcim.graphql.types_v1')]
+
+
+@strawberry_django.type(
+    models.Rack,
+    fields='__all__',
+    filters=RackFilterV1,
+    pagination=True
+)
+class RackTypeV1(VLANGroupsMixinV1, ImageAttachmentsMixinV1, ContactsMixinV1, NetBoxObjectTypeV1):
+    site: Annotated["SiteTypeV1", strawberry.lazy('dcim.graphql.types_v1')]
+    location: Annotated["LocationTypeV1", strawberry.lazy('dcim.graphql.types_v1')] | None
+    tenant: Annotated["TenantTypeV1", strawberry.lazy('tenancy.graphql.types_v1')] | None
+    role: Annotated["RackRoleTypeV1", strawberry.lazy('dcim.graphql.types_v1')] | None
+
+    rack_type: Annotated["RackTypeTypeV1", strawberry.lazy('dcim.graphql.types_v1')] | None
+    reservations: List[Annotated["RackReservationTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    devices: List[Annotated["DeviceTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    powerfeeds: List[Annotated["PowerFeedTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    cabletermination_set: List[Annotated["CableTerminationTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+
+
+@strawberry_django.type(
+    models.RackReservation,
+    fields='__all__',
+    filters=RackReservationFilterV1,
+    pagination=True
+)
+class RackReservationTypeV1(NetBoxObjectTypeV1):
+    units: List[int]
+    rack: Annotated["RackTypeV1", strawberry.lazy('dcim.graphql.types_v1')]
+    tenant: Annotated["TenantTypeV1", strawberry.lazy('tenancy.graphql.types_v1')] | None
+    user: Annotated["UserTypeV1", strawberry.lazy('users.graphql.types_v1')]
+
+
+@strawberry_django.type(
+    models.RackRole,
+    fields='__all__',
+    filters=RackRoleFilterV1,
+    pagination=True
+)
+class RackRoleTypeV1(OrganizationalObjectTypeV1):
+    color: str
+
+    racks: List[Annotated["RackTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+
+
+@strawberry_django.type(
+    models.RearPort,
+    fields='__all__',
+    filters=RearPortFilterV1,
+    pagination=True
+)
+class RearPortTypeV1(ModularComponentTypeV1, CabledObjectMixinV1):
+    color: str
+
+    frontports: List[Annotated["FrontPortTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+
+
+@strawberry_django.type(
+    models.RearPortTemplate,
+    fields='__all__',
+    filters=RearPortTemplateFilterV1,
+    pagination=True
+)
+class RearPortTemplateTypeV1(ModularComponentTemplateTypeV1):
+    color: str
+
+    frontport_templates: List[Annotated["FrontPortTemplateTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+
+
+@strawberry_django.type(
+    models.Region,
+    exclude=['parent'],
+    filters=RegionFilterV1,
+    pagination=True
+)
+class RegionTypeV1(VLANGroupsMixinV1, ContactsMixinV1, OrganizationalObjectTypeV1):
+
+    sites: List[Annotated["SiteTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    children: List[Annotated["RegionTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+
+    @strawberry_django.field
+    def parent(self) -> Annotated["RegionTypeV1", strawberry.lazy('dcim.graphql.types_v1')] | None:
+        return self.parent
+
+    @strawberry_django.field
+    def clusters(self) -> List[Annotated["ClusterTypeV1", strawberry.lazy('virtualization.graphql.types_v1')]]:
+        return self.cluster_set.all()
+
+    @strawberry_django.field
+    def circuit_terminations(self) -> List[
+        Annotated["CircuitTerminationTypeV1", strawberry.lazy('circuits.graphql.types_v1')]
+    ]:
+        return self.circuit_terminations.all()
+
+
+@strawberry_django.type(
+    models.Site,
+    fields='__all__',
+    filters=SiteFilterV1,
+    pagination=True
+)
+class SiteTypeV1(VLANGroupsMixinV1, ImageAttachmentsMixinV1, ContactsMixinV1, NetBoxObjectTypeV1):
+    time_zone: str | None
+    region: Annotated["RegionTypeV1", strawberry.lazy('dcim.graphql.types_v1')] | None
+    group: Annotated["SiteGroupTypeV1", strawberry.lazy('dcim.graphql.types_v1')] | None
+    tenant: Annotated["TenantTypeV1", strawberry.lazy('tenancy.graphql.types_v1')] | None
+
+    prefixes: List[Annotated["PrefixTypeV1", strawberry.lazy('ipam.graphql.types_v1')]]
+    virtual_machines: List[Annotated["VirtualMachineTypeV1", strawberry.lazy('virtualization.graphql.types_v1')]]
+    racks: List[Annotated["RackTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    cabletermination_set: List[Annotated["CableTerminationTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    powerpanel_set: List[Annotated["PowerPanelTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    devices: List[Annotated["DeviceTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    locations: List[Annotated["LocationTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    asns: List[Annotated["ASNTypeV1", strawberry.lazy('ipam.graphql.types_v1')]]
+    circuit_terminations: List[Annotated["CircuitTerminationTypeV1", strawberry.lazy('circuits.graphql.types_v1')]]
+    clusters: List[Annotated["ClusterTypeV1", strawberry.lazy('virtualization.graphql.types_v1')]]
+    vlans: List[Annotated["VLANTypeV1", strawberry.lazy('ipam.graphql.types_v1')]]
+
+    @strawberry_django.field
+    def clusters(self) -> List[Annotated["ClusterTypeV1", strawberry.lazy('virtualization.graphql.types_v1')]]:
+        return self.cluster_set.all()
+
+    @strawberry_django.field
+    def circuit_terminations(self) -> List[
+        Annotated["CircuitTerminationTypeV1", strawberry.lazy('circuits.graphql.types_v1')]
+    ]:
+        return self.circuit_terminations.all()
+
+
+@strawberry_django.type(
+    models.SiteGroup,
+    exclude=['parent'],  # bug - temp
+    filters=SiteGroupFilterV1,
+    pagination=True
+)
+class SiteGroupTypeV1(VLANGroupsMixinV1, ContactsMixinV1, OrganizationalObjectTypeV1):
+
+    sites: List[Annotated["SiteTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    children: List[Annotated["SiteGroupTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+
+    @strawberry_django.field
+    def parent(self) -> Annotated["SiteGroupTypeV1", strawberry.lazy('dcim.graphql.types_v1')] | None:
+        return self.parent
+
+    @strawberry_django.field
+    def clusters(self) -> List[Annotated["ClusterTypeV1", strawberry.lazy('virtualization.graphql.types_v1')]]:
+        return self.cluster_set.all()
+
+    @strawberry_django.field
+    def circuit_terminations(self) -> List[
+        Annotated["CircuitTerminationTypeV1", strawberry.lazy('circuits.graphql.types_v1')]
+    ]:
+        return self.circuit_terminations.all()
+
+
+@strawberry_django.type(
+    models.VirtualChassis,
+    fields='__all__',
+    filters=VirtualChassisFilterV1,
+    pagination=True
+)
+class VirtualChassisTypeV1(NetBoxObjectTypeV1):
+    member_count: BigInt
+    master: Annotated["DeviceTypeV1", strawberry.lazy('dcim.graphql.types_v1')] | None
+
+    members: List[Annotated["DeviceTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+
+
+@strawberry_django.type(
+    models.VirtualDeviceContext,
+    fields='__all__',
+    filters=VirtualDeviceContextFilterV1,
+    pagination=True
+)
+class VirtualDeviceContextTypeV1(NetBoxObjectTypeV1):
+    device: Annotated["DeviceTypeV1", strawberry.lazy('dcim.graphql.types_v1')] | None
+    primary_ip4: Annotated["IPAddressTypeV1", strawberry.lazy('ipam.graphql.types_v1')] | None
+    primary_ip6: Annotated["IPAddressTypeV1", strawberry.lazy('ipam.graphql.types_v1')] | None
+    tenant: Annotated["TenantTypeV1", strawberry.lazy('tenancy.graphql.types_v1')] | None
+
+    interfaces: List[Annotated["InterfaceTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]

+ 52 - 0
netbox/extras/graphql/filter_mixins_v1.py

@@ -0,0 +1,52 @@
+from dataclasses import dataclass
+from typing import Annotated, TYPE_CHECKING
+
+import strawberry
+import strawberry_django
+from strawberry_django import FilterLookup
+
+from core.graphql.filter_mixins_v1 import BaseFilterMixinV1
+
+if TYPE_CHECKING:
+    from netbox.graphql.filter_lookups import JSONFilter
+    from .filters_v1 import *
+
+__all__ = (
+    'CustomFieldsFilterMixinV1',
+    'JournalEntriesFilterMixinV1',
+    'TagsFilterMixinV1',
+    'ConfigContextFilterMixinV1',
+    'TagBaseFilterMixinV1',
+)
+
+
+@dataclass
+class CustomFieldsFilterMixinV1(BaseFilterMixinV1):
+    custom_field_data: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@dataclass
+class JournalEntriesFilterMixinV1(BaseFilterMixinV1):
+    journal_entries: Annotated['JournalEntryFilterV1', strawberry.lazy('extras.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@dataclass
+class TagsFilterMixinV1(BaseFilterMixinV1):
+    tags: Annotated['TagFilter', strawberry.lazy('extras.graphql.filters')] | None = strawberry_django.filter_field()
+
+
+@dataclass
+class ConfigContextFilterMixinV1(BaseFilterMixinV1):
+    local_context_data: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@dataclass
+class TagBaseFilterMixinV1(BaseFilterMixinV1):
+    name: FilterLookup[str] | None = strawberry_django.filter_field()
+    slug: FilterLookup[str] | None = strawberry_django.filter_field()

+ 357 - 0
netbox/extras/graphql/filters_v1.py

@@ -0,0 +1,357 @@
+from typing import Annotated, TYPE_CHECKING
+
+import strawberry
+import strawberry_django
+from strawberry.scalars import ID
+from strawberry_django import FilterLookup
+
+from core.graphql.filter_mixins_v1 import BaseObjectTypeFilterMixinV1, ChangeLogFilterMixinV1
+from extras import models
+from extras.graphql.filter_mixins_v1 import TagBaseFilterMixinV1, CustomFieldsFilterMixinV1, TagsFilterMixinV1
+from netbox.graphql.filter_mixins_v1 import PrimaryModelFilterMixinV1, SyncedDataFilterMixinV1
+
+if TYPE_CHECKING:
+    from core.graphql.filters_v1 import ContentTypeFilterV1
+    from dcim.graphql.filters_v1 import (
+        DeviceRoleFilterV1,
+        DeviceTypeFilterV1,
+        LocationFilterV1,
+        PlatformFilterV1,
+        RegionFilterV1,
+        SiteFilterV1,
+        SiteGroupFilterV1,
+    )
+    from tenancy.graphql.filters_v1 import TenantFilterV1, TenantGroupFilterV1
+    from netbox.graphql.enums import ColorEnum
+    from netbox.graphql.filter_lookups import FloatLookup, IntegerLookup, JSONFilter, StringArrayLookup, TreeNodeFilter
+    from virtualization.graphql.filters_v1 import ClusterFilterV1, ClusterGroupFilterV1, ClusterTypeFilterV1
+    from .enums import *
+
+__all__ = (
+    'ConfigContextFilterV1',
+    'ConfigContextProfileFilterV1',
+    'ConfigTemplateFilterV1',
+    'CustomFieldFilterV1',
+    'CustomFieldChoiceSetFilterV1',
+    'CustomLinkFilterV1',
+    'EventRuleFilterV1',
+    'ExportTemplateFilterV1',
+    'ImageAttachmentFilterV1',
+    'JournalEntryFilterV1',
+    'NotificationGroupFilterV1',
+    'SavedFilterFilterV1',
+    'TableConfigFilterV1',
+    'TagFilterV1',
+    'WebhookFilterV1',
+)
+
+
+@strawberry_django.filter_type(models.ConfigContext, lookups=True)
+class ConfigContextFilterV1(BaseObjectTypeFilterMixinV1, SyncedDataFilterMixinV1, ChangeLogFilterMixinV1):
+    name: FilterLookup[str] | None = strawberry_django.filter_field()
+    weight: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    description: FilterLookup[str] | None = strawberry_django.filter_field()
+    is_active: FilterLookup[bool] | None = strawberry_django.filter_field()
+    regions: Annotated['RegionFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    region_id: Annotated['TreeNodeFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    site_groups: Annotated['SiteGroupFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    site_group_id: Annotated['TreeNodeFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    sites: Annotated['SiteFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    locations: Annotated['LocationFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    device_types: Annotated['DeviceTypeFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    roles: Annotated['DeviceRoleFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    platforms: Annotated['PlatformFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    cluster_types: Annotated['ClusterTypeFilterV1', strawberry.lazy('virtualization.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    cluster_groups: Annotated['ClusterGroupFilterV1', strawberry.lazy('virtualization.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    clusters: Annotated['ClusterFilterV1', strawberry.lazy('virtualization.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    tenant_groups: Annotated['TenantGroupFilterV1', strawberry.lazy('tenancy.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    tenant_group_id: Annotated['TreeNodeFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    tenants: Annotated['TenantFilterV1', strawberry.lazy('tenancy.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    tags: Annotated['TagFilterV1', strawberry.lazy('extras.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    data: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@strawberry_django.filter_type(models.ConfigContextProfile, lookups=True)
+class ConfigContextProfileFilterV1(SyncedDataFilterMixinV1, PrimaryModelFilterMixinV1):
+    name: FilterLookup[str] = strawberry_django.filter_field()
+    description: FilterLookup[str] = strawberry_django.filter_field()
+    tags: Annotated['TagFilterV1', strawberry.lazy('extras.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@strawberry_django.filter_type(models.ConfigTemplate, lookups=True)
+class ConfigTemplateFilterV1(BaseObjectTypeFilterMixinV1, SyncedDataFilterMixinV1, ChangeLogFilterMixinV1):
+    name: FilterLookup[str] | None = strawberry_django.filter_field()
+    description: FilterLookup[str] | None = strawberry_django.filter_field()
+    template_code: FilterLookup[str] | None = strawberry_django.filter_field()
+    environment_params: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    mime_type: FilterLookup[str] | None = strawberry_django.filter_field()
+    file_name: FilterLookup[str] | None = strawberry_django.filter_field()
+    file_extension: FilterLookup[str] | None = strawberry_django.filter_field()
+    as_attachment: FilterLookup[bool] | None = strawberry_django.filter_field()
+
+
+@strawberry_django.filter_type(models.CustomField, lookups=True)
+class CustomFieldFilterV1(BaseObjectTypeFilterMixinV1, ChangeLogFilterMixinV1):
+    type: Annotated['CustomFieldTypeEnum', strawberry.lazy('extras.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    object_types: Annotated['ContentTypeFilterV1', strawberry.lazy('core.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    related_object_type: Annotated['ContentTypeFilterV1', strawberry.lazy('core.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    name: FilterLookup[str] | None = strawberry_django.filter_field()
+    label: FilterLookup[str] | None = strawberry_django.filter_field()
+    group_name: FilterLookup[str] | None = strawberry_django.filter_field()
+    description: FilterLookup[str] | None = strawberry_django.filter_field()
+    required: FilterLookup[bool] | None = strawberry_django.filter_field()
+    unique: FilterLookup[bool] | None = strawberry_django.filter_field()
+    search_weight: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    filter_logic: Annotated['CustomFieldFilterLogicEnum', strawberry.lazy('extras.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    default: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    related_object_filter: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    weight: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    validation_minimum: Annotated['FloatLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    validation_maximum: Annotated['FloatLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    validation_regex: FilterLookup[str] | None = strawberry_django.filter_field()
+    choice_set: Annotated['CustomFieldChoiceSetFilterV1', strawberry.lazy('extras.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    choice_set_id: ID | None = strawberry_django.filter_field()
+    ui_visible: Annotated['CustomFieldUIVisibleEnum', strawberry.lazy('extras.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    ui_editable: Annotated['CustomFieldUIEditableEnum', strawberry.lazy('extras.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    is_cloneable: FilterLookup[bool] | None = strawberry_django.filter_field()
+    comments: FilterLookup[str] | None = strawberry_django.filter_field()
+
+
+@strawberry_django.filter_type(models.CustomFieldChoiceSet, lookups=True)
+class CustomFieldChoiceSetFilterV1(BaseObjectTypeFilterMixinV1, ChangeLogFilterMixinV1):
+    name: FilterLookup[str] | None = strawberry_django.filter_field()
+    description: FilterLookup[str] | None = strawberry_django.filter_field()
+    base_choices: Annotated['CustomFieldChoiceSetBaseEnum', strawberry.lazy('extras.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    extra_choices: Annotated['StringArrayLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    order_alphabetically: FilterLookup[bool] | None = strawberry_django.filter_field()
+
+
+@strawberry_django.filter_type(models.CustomLink, lookups=True)
+class CustomLinkFilterV1(BaseObjectTypeFilterMixinV1, ChangeLogFilterMixinV1):
+    name: FilterLookup[str] | None = strawberry_django.filter_field()
+    enabled: FilterLookup[bool] | None = strawberry_django.filter_field()
+    link_text: FilterLookup[str] | None = strawberry_django.filter_field()
+    link_url: FilterLookup[str] | None = strawberry_django.filter_field()
+    weight: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    group_name: FilterLookup[str] | None = strawberry_django.filter_field()
+    button_class: Annotated['CustomLinkButtonClassEnum', strawberry.lazy('extras.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    new_window: FilterLookup[bool] | None = strawberry_django.filter_field()
+
+
+@strawberry_django.filter_type(models.ExportTemplate, lookups=True)
+class ExportTemplateFilterV1(BaseObjectTypeFilterMixinV1, SyncedDataFilterMixinV1, ChangeLogFilterMixinV1):
+    name: FilterLookup[str] | None = strawberry_django.filter_field()
+    description: FilterLookup[str] | None = strawberry_django.filter_field()
+    template_code: FilterLookup[str] | None = strawberry_django.filter_field()
+    environment_params: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    mime_type: FilterLookup[str] | None = strawberry_django.filter_field()
+    file_name: FilterLookup[str] | None = strawberry_django.filter_field()
+    file_extension: FilterLookup[str] | None = strawberry_django.filter_field()
+    as_attachment: FilterLookup[bool] | None = strawberry_django.filter_field()
+
+
+@strawberry_django.filter_type(models.ImageAttachment, lookups=True)
+class ImageAttachmentFilterV1(BaseObjectTypeFilterMixinV1, ChangeLogFilterMixinV1):
+    object_type: Annotated['ContentTypeFilterV1', strawberry.lazy('core.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    object_id: ID | None = strawberry_django.filter_field()
+    image_height: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    image_width: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    name: FilterLookup[str] | None = strawberry_django.filter_field()
+
+
+@strawberry_django.filter_type(models.JournalEntry, lookups=True)
+class JournalEntryFilterV1(
+    BaseObjectTypeFilterMixinV1, CustomFieldsFilterMixinV1, TagsFilterMixinV1, ChangeLogFilterMixinV1
+):
+    assigned_object_type: Annotated['ContentTypeFilterV1', strawberry.lazy('core.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    assigned_object_type_id: ID | None = strawberry_django.filter_field()
+    assigned_object_id: ID | None = strawberry_django.filter_field()
+    created_by: Annotated['UserFilterV1', strawberry.lazy('users.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    kind: Annotated['JournalEntryKindEnum', strawberry.lazy('extras.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    comments: FilterLookup[str] | None = strawberry_django.filter_field()
+
+
+@strawberry_django.filter_type(models.NotificationGroup, lookups=True)
+class NotificationGroupFilterV1(BaseObjectTypeFilterMixinV1, ChangeLogFilterMixinV1):
+    name: FilterLookup[str] | None = strawberry_django.filter_field()
+    description: FilterLookup[str] | None = strawberry_django.filter_field()
+    groups: Annotated['GroupFilterV1', strawberry.lazy('users.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    users: Annotated['UserFilterV1', strawberry.lazy('users.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@strawberry_django.filter_type(models.SavedFilter, lookups=True)
+class SavedFilterFilterV1(BaseObjectTypeFilterMixinV1, ChangeLogFilterMixinV1):
+    name: FilterLookup[str] | None = strawberry_django.filter_field()
+    slug: FilterLookup[str] | None = strawberry_django.filter_field()
+    description: FilterLookup[str] | None = strawberry_django.filter_field()
+    user: Annotated['UserFilterV1', strawberry.lazy('users.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    user_id: ID | None = strawberry_django.filter_field()
+    weight: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    enabled: FilterLookup[bool] | None = strawberry_django.filter_field()
+    shared: FilterLookup[bool] | None = strawberry_django.filter_field()
+    parameters: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@strawberry_django.filter_type(models.TableConfig, lookups=True)
+class TableConfigFilterV1(BaseObjectTypeFilterMixinV1, ChangeLogFilterMixinV1):
+    name: FilterLookup[str] | None = strawberry_django.filter_field()
+    description: FilterLookup[str] | None = strawberry_django.filter_field()
+    user: Annotated['UserFilterV1', strawberry.lazy('users.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    user_id: ID | None = strawberry_django.filter_field()
+    weight: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    enabled: FilterLookup[bool] | None = strawberry_django.filter_field()
+    shared: FilterLookup[bool] | None = strawberry_django.filter_field()
+
+
+@strawberry_django.filter_type(models.Tag, lookups=True)
+class TagFilterV1(BaseObjectTypeFilterMixinV1, ChangeLogFilterMixinV1, TagBaseFilterMixinV1):
+    color: Annotated['ColorEnum', strawberry.lazy('netbox.graphql.enums')] | None = strawberry_django.filter_field()
+    description: FilterLookup[str] | None = strawberry_django.filter_field()
+
+
+@strawberry_django.filter_type(models.Webhook, lookups=True)
+class WebhookFilterV1(
+    BaseObjectTypeFilterMixinV1, CustomFieldsFilterMixinV1, TagsFilterMixinV1, ChangeLogFilterMixinV1
+):
+    name: FilterLookup[str] | None = strawberry_django.filter_field()
+    description: FilterLookup[str] | None = strawberry_django.filter_field()
+    payload_url: FilterLookup[str] | None = strawberry_django.filter_field()
+    http_method: Annotated['WebhookHttpMethodEnum', strawberry.lazy('extras.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    http_content_type: FilterLookup[str] | None = strawberry_django.filter_field()
+    additional_headers: FilterLookup[str] | None = strawberry_django.filter_field()
+    body_template: FilterLookup[str] | None = strawberry_django.filter_field()
+    secret: FilterLookup[str] | None = strawberry_django.filter_field()
+    ssl_verification: FilterLookup[bool] | None = strawberry_django.filter_field()
+    ca_file_path: FilterLookup[str] | None = strawberry_django.filter_field()
+    events: Annotated['EventRuleFilterV1', strawberry.lazy('extras.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@strawberry_django.filter_type(models.EventRule, lookups=True)
+class EventRuleFilterV1(
+    BaseObjectTypeFilterMixinV1, CustomFieldsFilterMixinV1, TagsFilterMixinV1, ChangeLogFilterMixinV1
+):
+    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 = (
+        strawberry_django.filter_field()
+    )
+    enabled: FilterLookup[bool] | None = strawberry_django.filter_field()
+    conditions: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    action_type: Annotated['EventRuleActionEnum', strawberry.lazy('extras.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    action_object_type: FilterLookup[str] | None = strawberry_django.filter_field()
+    action_object_type_id: ID | None = strawberry_django.filter_field()
+    action_object_id: ID | None = strawberry_django.filter_field()
+    action_data: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    comments: FilterLookup[str] | None = strawberry_django.filter_field()

+ 62 - 0
netbox/extras/graphql/mixins_v1.py

@@ -0,0 +1,62 @@
+from typing import TYPE_CHECKING, Annotated, List
+
+import strawberry
+import strawberry_django
+from strawberry.types import Info
+
+__all__ = (
+    'ConfigContextMixinV1',
+    'ContactsMixinV1',
+    'CustomFieldsMixinV1',
+    'ImageAttachmentsMixinV1',
+    'JournalEntriesMixinV1',
+    'TagsMixinV1',
+)
+
+if TYPE_CHECKING:
+    from .types_v1 import ImageAttachmentTypeV1, JournalEntryTypeV1, TagTypeV1
+    from tenancy.graphql.types_v1 import ContactAssignmentTypeV1
+
+
+@strawberry.type
+class ConfigContextMixinV1:
+
+    @strawberry_django.field
+    def config_context(self) -> strawberry.scalars.JSON:
+        return self.get_config_context()
+
+
+@strawberry.type
+class CustomFieldsMixinV1:
+
+    @strawberry_django.field
+    def custom_fields(self) -> strawberry.scalars.JSON:
+        return self.custom_field_data
+
+
+@strawberry.type
+class ImageAttachmentsMixinV1:
+
+    @strawberry_django.field
+    def image_attachments(self, info: Info) -> List[Annotated['ImageAttachmentTypeV1', strawberry.lazy('.types_v1')]]:
+        return self.images.restrict(info.context.request.user, 'view')
+
+
+@strawberry.type
+class JournalEntriesMixinV1:
+
+    @strawberry_django.field
+    def journal_entries(self, info: Info) -> List[Annotated['JournalEntryTypeV1', strawberry.lazy('.types_v1')]]:
+        return self.journal_entries.all()
+
+
+@strawberry.type
+class TagsMixinV1:
+
+    tags: List[Annotated['TagTypeV1', strawberry.lazy('.types_v1')]]
+
+
+@strawberry.type
+class ContactsMixinV1:
+
+    contacts: List[Annotated['ContactAssignmentTypeV1', strawberry.lazy('tenancy.graphql.types_v1')]]

+ 60 - 0
netbox/extras/graphql/schema_v1.py

@@ -0,0 +1,60 @@
+from typing import List
+
+import strawberry
+import strawberry_django
+
+from .types_v1 import *
+
+
+@strawberry.type(name="Query")
+class ExtrasQueryV1:
+    config_context: ConfigContextTypeV1 = strawberry_django.field()
+    config_context_list: List[ConfigContextTypeV1] = strawberry_django.field()
+
+    config_context_profile: ConfigContextProfileTypeV1 = strawberry_django.field()
+    config_context_profile_list: List[ConfigContextProfileTypeV1] = strawberry_django.field()
+
+    config_template: ConfigTemplateTypeV1 = strawberry_django.field()
+    config_template_list: List[ConfigTemplateTypeV1] = strawberry_django.field()
+
+    custom_field: CustomFieldTypeV1 = strawberry_django.field()
+    custom_field_list: List[CustomFieldTypeV1] = strawberry_django.field()
+
+    custom_field_choice_set: CustomFieldChoiceSetTypeV1 = strawberry_django.field()
+    custom_field_choice_set_list: List[CustomFieldChoiceSetTypeV1] = strawberry_django.field()
+
+    custom_link: CustomLinkTypeV1 = strawberry_django.field()
+    custom_link_list: List[CustomLinkTypeV1] = strawberry_django.field()
+
+    export_template: ExportTemplateTypeV1 = strawberry_django.field()
+    export_template_list: List[ExportTemplateTypeV1] = strawberry_django.field()
+
+    image_attachment: ImageAttachmentTypeV1 = strawberry_django.field()
+    image_attachment_list: List[ImageAttachmentTypeV1] = strawberry_django.field()
+
+    saved_filter: SavedFilterTypeV1 = strawberry_django.field()
+    saved_filter_list: List[SavedFilterTypeV1] = strawberry_django.field()
+
+    table_config: TableConfigTypeV1 = strawberry_django.field()
+    table_config_list: List[TableConfigTypeV1] = strawberry_django.field()
+
+    journal_entry: JournalEntryTypeV1 = strawberry_django.field()
+    journal_entry_list: List[JournalEntryTypeV1] = strawberry_django.field()
+
+    notification: NotificationTypeV1 = strawberry_django.field()
+    notification_list: List[NotificationTypeV1] = strawberry_django.field()
+
+    notification_group: NotificationGroupTypeV1 = strawberry_django.field()
+    notification_group_list: List[NotificationGroupTypeV1] = strawberry_django.field()
+
+    subscription: SubscriptionTypeV1 = strawberry_django.field()
+    subscription_list: List[SubscriptionTypeV1] = strawberry_django.field()
+
+    tag: TagTypeV1 = strawberry_django.field()
+    tag_list: List[TagTypeV1] = strawberry_django.field()
+
+    webhook: WebhookTypeV1 = strawberry_django.field()
+    webhook_list: List[WebhookTypeV1] = strawberry_django.field()
+
+    event_rule: EventRuleTypeV1 = strawberry_django.field()
+    event_rule_list: List[EventRuleTypeV1] = strawberry_django.field()

+ 239 - 0
netbox/extras/graphql/types_v1.py

@@ -0,0 +1,239 @@
+from typing import Annotated, List, TYPE_CHECKING
+
+import strawberry
+import strawberry_django
+
+from core.graphql.mixins_v1 import SyncedDataMixinV1
+from extras import models
+from extras.graphql.mixins_v1 import CustomFieldsMixinV1, TagsMixinV1
+from netbox.graphql.types_v1 import (
+    BaseObjectTypeV1, ContentTypeTypeV1, NetBoxObjectTypeV1, ObjectTypeV1, OrganizationalObjectTypeV1
+)
+from .filters_v1 import *
+
+if TYPE_CHECKING:
+    from dcim.graphql.types_v1 import (
+        DeviceRoleTypeV1,
+        DeviceTypeV1,
+        DeviceTypeTypeV1,
+        LocationTypeV1,
+        PlatformTypeV1,
+        RegionTypeV1,
+        SiteGroupTypeV1,
+        SiteTypeV1,
+    )
+    from tenancy.graphql.types_v1 import TenantGroupTypeV1, TenantTypeV1
+    from users.graphql.types_v1 import GroupTypeV1, UserTypeV1
+    from virtualization.graphql.types_v1 import (
+        ClusterGroupTypeV1, ClusterTypeV1, ClusterTypeTypeV1, VirtualMachineTypeV1
+    )
+
+__all__ = (
+    'ConfigContextProfileTypeV1',
+    'ConfigContextTypeV1',
+    'ConfigTemplateTypeV1',
+    'CustomFieldChoiceSetTypeV1',
+    'CustomFieldTypeV1',
+    'CustomLinkTypeV1',
+    'EventRuleTypeV1',
+    'ExportTemplateTypeV1',
+    'ImageAttachmentTypeV1',
+    'JournalEntryTypeV1',
+    'NotificationGroupTypeV1',
+    'NotificationTypeV1',
+    'SavedFilterTypeV1',
+    'SubscriptionTypeV1',
+    'TableConfigTypeV1',
+    'TagTypeV1',
+    'WebhookTypeV1',
+)
+
+
+@strawberry_django.type(
+    models.ConfigContextProfile,
+    fields='__all__',
+    filters=ConfigContextProfileFilterV1,
+    pagination=True
+)
+class ConfigContextProfileTypeV1(SyncedDataMixinV1, NetBoxObjectTypeV1):
+    pass
+
+
+@strawberry_django.type(
+    models.ConfigContext,
+    fields='__all__',
+    filters=ConfigContextFilterV1,
+    pagination=True
+)
+class ConfigContextTypeV1(SyncedDataMixinV1, ObjectTypeV1):
+    profile: ConfigContextProfileTypeV1 | None
+    roles: List[Annotated["DeviceRoleTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    device_types: List[Annotated["DeviceTypeTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    tags: List[Annotated["TagTypeV1", strawberry.lazy('extras.graphql.types_v1')]]
+    platforms: List[Annotated["PlatformTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    regions: List[Annotated["RegionTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    cluster_groups: List[Annotated["ClusterGroupTypeV1", strawberry.lazy('virtualization.graphql.types_v1')]]
+    tenant_groups: List[Annotated["TenantGroupTypeV1", strawberry.lazy('tenancy.graphql.types_v1')]]
+    cluster_types: List[Annotated["ClusterTypeTypeV1", strawberry.lazy('virtualization.graphql.types_v1')]]
+    clusters: List[Annotated["ClusterTypeV1", strawberry.lazy('virtualization.graphql.types_v1')]]
+    locations: List[Annotated["LocationTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    sites: List[Annotated["SiteTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    tenants: List[Annotated["TenantTypeV1", strawberry.lazy('tenancy.graphql.types_v1')]]
+    site_groups: List[Annotated["SiteGroupTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+
+
+@strawberry_django.type(
+    models.ConfigTemplate,
+    fields='__all__',
+    filters=ConfigTemplateFilterV1,
+    pagination=True
+)
+class ConfigTemplateTypeV1(SyncedDataMixinV1, TagsMixinV1, ObjectTypeV1):
+    virtualmachines: List[Annotated["VirtualMachineTypeV1", strawberry.lazy('virtualization.graphql.types_v1')]]
+    devices: List[Annotated["DeviceTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    platforms: List[Annotated["PlatformTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    device_roles: List[Annotated["DeviceRoleTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+
+
+@strawberry_django.type(
+    models.CustomField,
+    fields='__all__',
+    filters=CustomFieldFilterV1,
+    pagination=True
+)
+class CustomFieldTypeV1(ObjectTypeV1):
+    related_object_type: Annotated["ContentTypeTypeV1", strawberry.lazy('netbox.graphql.types_v1')] | None
+    choice_set: Annotated["CustomFieldChoiceSetTypeV1", strawberry.lazy('extras.graphql.types_v1')] | None
+
+
+@strawberry_django.type(
+    models.CustomFieldChoiceSet,
+    exclude=['extra_choices'],
+    filters=CustomFieldChoiceSetFilterV1,
+    pagination=True
+)
+class CustomFieldChoiceSetTypeV1(ObjectTypeV1):
+
+    choices_for: List[Annotated["CustomFieldTypeV1", strawberry.lazy('extras.graphql.types_v1')]]
+    extra_choices: List[List[str]] | None
+
+
+@strawberry_django.type(
+    models.CustomLink,
+    fields='__all__',
+    filters=CustomLinkFilterV1,
+    pagination=True
+)
+class CustomLinkTypeV1(ObjectTypeV1):
+    pass
+
+
+@strawberry_django.type(
+    models.ExportTemplate,
+    fields='__all__',
+    filters=ExportTemplateFilterV1,
+    pagination=True
+)
+class ExportTemplateTypeV1(SyncedDataMixinV1, ObjectTypeV1):
+    pass
+
+
+@strawberry_django.type(
+    models.ImageAttachment,
+    fields='__all__',
+    filters=ImageAttachmentFilterV1,
+    pagination=True
+)
+class ImageAttachmentTypeV1(BaseObjectTypeV1):
+    object_type: Annotated["ContentTypeTypeV1", strawberry.lazy('netbox.graphql.types_v1')] | None
+
+
+@strawberry_django.type(
+    models.JournalEntry,
+    fields='__all__',
+    filters=JournalEntryFilterV1,
+    pagination=True
+)
+class JournalEntryTypeV1(CustomFieldsMixinV1, TagsMixinV1, ObjectTypeV1):
+    assigned_object_type: Annotated["ContentTypeTypeV1", strawberry.lazy('netbox.graphql.types_v1')] | None
+    created_by: Annotated["UserTypeV1", strawberry.lazy('users.graphql.types_v1')] | None
+
+
+@strawberry_django.type(
+    models.Notification,
+    # filters=NotificationFilter
+    pagination=True
+)
+class NotificationTypeV1(ObjectTypeV1):
+    user: Annotated["UserTypeV1", strawberry.lazy('users.graphql.types_v1')] | None
+
+
+@strawberry_django.type(
+    models.NotificationGroup,
+    filters=NotificationGroupFilterV1,
+    pagination=True
+)
+class NotificationGroupTypeV1(ObjectTypeV1):
+    users: List[Annotated["UserTypeV1", strawberry.lazy('users.graphql.types_v1')]]
+    groups: List[Annotated["GroupTypeV1", strawberry.lazy('users.graphql.types_v1')]]
+
+
+@strawberry_django.type(
+    models.SavedFilter,
+    exclude=['content_types',],
+    filters=SavedFilterFilterV1,
+    pagination=True
+)
+class SavedFilterTypeV1(ObjectTypeV1):
+    user: Annotated["UserTypeV1", strawberry.lazy('users.graphql.types_v1')] | None
+
+
+@strawberry_django.type(
+    models.Subscription,
+    # filters=NotificationFilter
+    pagination=True
+)
+class SubscriptionTypeV1(ObjectTypeV1):
+    user: Annotated["UserTypeV1", strawberry.lazy('users.graphql.types_v1')] | None
+
+
+@strawberry_django.type(
+    models.TableConfig,
+    fields='__all__',
+    filters=TableConfigFilterV1,
+    pagination=True
+)
+class TableConfigTypeV1(ObjectTypeV1):
+    user: Annotated["UserTypeV1", strawberry.lazy('users.graphql.types_v1')] | None
+
+
+@strawberry_django.type(
+    models.Tag,
+    exclude=['extras_taggeditem_items', ],
+    filters=TagFilterV1,
+    pagination=True
+)
+class TagTypeV1(ObjectTypeV1):
+    color: str
+
+    object_types: List[ContentTypeTypeV1]
+
+
+@strawberry_django.type(
+    models.Webhook,
+    exclude=['content_types',],
+    filters=WebhookFilterV1,
+    pagination=True
+)
+class WebhookTypeV1(OrganizationalObjectTypeV1):
+    pass
+
+
+@strawberry_django.type(
+    models.EventRule,
+    exclude=['content_types',],
+    filters=EventRuleFilterV1,
+    pagination=True
+)
+class EventRuleTypeV1(OrganizationalObjectTypeV1):
+    action_object_type: Annotated["ContentTypeTypeV1", strawberry.lazy('netbox.graphql.types_v1')] | None

+ 25 - 0
netbox/ipam/graphql/filter_mixins_v1.py

@@ -0,0 +1,25 @@
+from dataclasses import dataclass
+from typing import Annotated, TYPE_CHECKING
+
+import strawberry
+import strawberry_django
+
+from core.graphql.filter_mixins_v1 import BaseFilterMixinV1
+
+if TYPE_CHECKING:
+    from netbox.graphql.filter_lookups import IntegerLookup
+    from .enums import *
+
+__all__ = (
+    'ServiceBaseFilterMixinV1',
+)
+
+
+@dataclass
+class ServiceBaseFilterMixinV1(BaseFilterMixinV1):
+    protocol: Annotated['ServiceProtocolEnum', strawberry.lazy('ipam.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    ports: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )

+ 392 - 0
netbox/ipam/graphql/filters_v1.py

@@ -0,0 +1,392 @@
+from datetime import date
+from typing import Annotated, TYPE_CHECKING
+
+import netaddr
+import strawberry
+import strawberry_django
+from django.db.models import Q
+from netaddr.core import AddrFormatError
+from strawberry.scalars import ID
+from strawberry_django import FilterLookup, DateFilterLookup
+
+from core.graphql.filter_mixins_v1 import BaseObjectTypeFilterMixinV1, ChangeLogFilterMixinV1
+from dcim.graphql.filter_mixins_v1 import ScopedFilterMixinV1
+from dcim.models import Device
+from ipam import models
+from ipam.graphql.filter_mixins_v1 import ServiceBaseFilterMixinV1
+from netbox.graphql.filter_mixins_v1 import (
+    NetBoxModelFilterMixinV1, OrganizationalModelFilterMixinV1, PrimaryModelFilterMixinV1
+)
+from tenancy.graphql.filter_mixins_v1 import ContactFilterMixinV1, TenancyFilterMixinV1
+from virtualization.models import VMInterface
+
+if TYPE_CHECKING:
+    from netbox.graphql.filter_lookups import IntegerLookup, IntegerRangeArrayLookup
+    from circuits.graphql.filters_v1 import ProviderFilterV1
+    from core.graphql.filters_v1 import ContentTypeFilterV1
+    from dcim.graphql.filters_v1 import SiteFilterV1
+    from vpn.graphql.filters_v1 import L2VPNFilterV1
+    from .enums import *
+
+__all__ = (
+    'ASNFilterV1',
+    'ASNRangeFilterV1',
+    'AggregateFilterV1',
+    'FHRPGroupFilterV1',
+    'FHRPGroupAssignmentFilterV1',
+    'IPAddressFilterV1',
+    'IPRangeFilterV1',
+    'PrefixFilterV1',
+    'RIRFilterV1',
+    'RoleFilterV1',
+    'RouteTargetFilterV1',
+    'ServiceFilterV1',
+    'ServiceTemplateFilterV1',
+    'VLANFilterV1',
+    'VLANGroupFilterV1',
+    'VLANTranslationPolicyFilterV1',
+    'VLANTranslationRuleFilterV1',
+    'VRFFilterV1',
+)
+
+
+@strawberry_django.filter_type(models.ASN, lookups=True)
+class ASNFilterV1(TenancyFilterMixinV1, PrimaryModelFilterMixinV1):
+    rir: Annotated['RIRFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = strawberry_django.filter_field()
+    rir_id: ID | None = strawberry_django.filter_field()
+    asn: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    sites: (
+        Annotated['SiteFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None
+    ) = strawberry_django.filter_field()
+    providers: (
+        Annotated['ProviderFilterV1', strawberry.lazy('circuits.graphql.filters_v1')] | None
+    ) = strawberry_django.filter_field()
+
+
+@strawberry_django.filter_type(models.ASNRange, lookups=True)
+class ASNRangeFilterV1(TenancyFilterMixinV1, OrganizationalModelFilterMixinV1):
+    name: FilterLookup[str] | None = strawberry_django.filter_field()
+    slug: FilterLookup[str] | None = strawberry_django.filter_field()
+    rir: Annotated['RIRFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = strawberry_django.filter_field()
+    rir_id: ID | None = strawberry_django.filter_field()
+    start: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    end: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@strawberry_django.filter_type(models.Aggregate, lookups=True)
+class AggregateFilterV1(ContactFilterMixinV1, TenancyFilterMixinV1, PrimaryModelFilterMixinV1):
+    prefix: Annotated['PrefixFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    prefix_id: ID | None = strawberry_django.filter_field()
+    rir: Annotated['RIRFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = strawberry_django.filter_field()
+    rir_id: ID | None = strawberry_django.filter_field()
+    date_added: DateFilterLookup[date] | None = strawberry_django.filter_field()
+
+
+@strawberry_django.filter_type(models.FHRPGroup, lookups=True)
+class FHRPGroupFilterV1(PrimaryModelFilterMixinV1):
+    group_id: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    name: FilterLookup[str] | None = strawberry_django.filter_field()
+    protocol: Annotated['FHRPGroupProtocolEnum', strawberry.lazy('ipam.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    auth_type: Annotated['FHRPGroupAuthTypeEnum', strawberry.lazy('ipam.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    auth_key: FilterLookup[str] | None = strawberry_django.filter_field()
+    ip_addresses: Annotated['IPAddressFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@strawberry_django.filter_type(models.FHRPGroupAssignment, lookups=True)
+class FHRPGroupAssignmentFilterV1(BaseObjectTypeFilterMixinV1, ChangeLogFilterMixinV1):
+    interface_type: Annotated['ContentTypeFilterV1', strawberry.lazy('core.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    interface_id: FilterLookup[str] | None = strawberry_django.filter_field()
+    group: Annotated['FHRPGroupFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    group_id: ID | None = strawberry_django.filter_field()
+    priority: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+
+    @strawberry_django.filter_field()
+    def device_id(self, queryset, value: list[str], prefix) -> Q:
+        return self.filter_device('id', value)
+
+    @strawberry_django.filter_field()
+    def device(self, value: list[str], prefix) -> Q:
+        return self.filter_device('name', value)
+
+    @strawberry_django.filter_field()
+    def virtual_machine_id(self, value: list[str], prefix) -> Q:
+        return Q(interface_id__in=VMInterface.objects.filter(virtual_machine_id__in=value))
+
+    @strawberry_django.filter_field()
+    def virtual_machine(self, value: list[str], prefix) -> Q:
+        return Q(interface_id__in=VMInterface.objects.filter(virtual_machine__name__in=value))
+
+    def filter_device(self, field, value) -> Q:
+        """Helper to standardize logic for device and device_id filters"""
+        devices = Device.objects.filter(**{f'{field}__in': value})
+        interface_ids = []
+        for device in devices:
+            interface_ids.extend(device.vc_interfaces().values_list('id', flat=True))
+        return Q(interface_id__in=interface_ids)
+
+
+@strawberry_django.filter_type(models.IPAddress, lookups=True)
+class IPAddressFilterV1(ContactFilterMixinV1, TenancyFilterMixinV1, PrimaryModelFilterMixinV1):
+    address: FilterLookup[str] | None = strawberry_django.filter_field()
+    vrf: Annotated['VRFFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = strawberry_django.filter_field()
+    vrf_id: ID | None = strawberry_django.filter_field()
+    status: Annotated['IPAddressStatusEnum', strawberry.lazy('ipam.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    role: Annotated['IPAddressRoleEnum', strawberry.lazy('ipam.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    assigned_object_type: Annotated['ContentTypeFilterV1', strawberry.lazy('core.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    assigned_object_id: ID | None = strawberry_django.filter_field()
+    nat_inside: Annotated['IPAddressFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    nat_inside_id: ID | None = strawberry_django.filter_field()
+    nat_outside: Annotated['IPAddressFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    nat_outside_id: ID | None = strawberry_django.filter_field()
+    dns_name: FilterLookup[str] | None = strawberry_django.filter_field()
+
+    @strawberry_django.filter_field()
+    def assigned(self, value: bool, prefix) -> Q:
+        return Q(assigned_object_id__isnull=(not value))
+
+    @strawberry_django.filter_field()
+    def parent(self, value: list[str], prefix) -> Q:
+        if not value:
+            return Q()
+        q = Q()
+        for subnet in value:
+            try:
+                query = str(netaddr.IPNetwork(subnet.strip()).cidr)
+                q |= Q(address__net_host_contained=query)
+            except (AddrFormatError, ValueError):
+                return Q()
+        return q
+
+    @strawberry_django.filter_field()
+    def family(
+        self,
+        value: Annotated['IPAddressFamilyEnum', strawberry.lazy('ipam.graphql.enums')],
+        prefix,
+    ) -> Q:
+        return Q(**{f"{prefix}address__family": value.value})
+
+
+@strawberry_django.filter_type(models.IPRange, lookups=True)
+class IPRangeFilterV1(ContactFilterMixinV1, TenancyFilterMixinV1, PrimaryModelFilterMixinV1):
+    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 = (
+        strawberry_django.filter_field()
+    )
+    vrf: Annotated['VRFFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = strawberry_django.filter_field()
+    vrf_id: ID | None = strawberry_django.filter_field()
+    status: Annotated['IPRangeStatusEnum', strawberry.lazy('ipam.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    role: Annotated['RoleFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    mark_utilized: FilterLookup[bool] | None = strawberry_django.filter_field()
+
+    @strawberry_django.filter_field()
+    def parent(self, value: list[str], prefix) -> Q:
+        if not value:
+            return Q()
+        q = Q()
+        for subnet in value:
+            try:
+                query = str(netaddr.IPNetwork(subnet.strip()).cidr)
+                q |= Q(start_address__net_host_contained=query, end_address__net_host_contained=query)
+            except (AddrFormatError, ValueError):
+                return Q()
+        return q
+
+    @strawberry_django.filter_field()
+    def contains(self, value: list[str], prefix) -> Q:
+        if not value:
+            return Q()
+        q = Q()
+        for subnet in value:
+            net = netaddr.IPNetwork(subnet.strip())
+            q |= Q(
+                start_address__host__inet__lte=str(netaddr.IPAddress(net.first)),
+                end_address__host__inet__gte=str(netaddr.IPAddress(net.last)),
+            )
+        return q
+
+
+@strawberry_django.filter_type(models.Prefix, lookups=True)
+class PrefixFilterV1(ContactFilterMixinV1, ScopedFilterMixinV1, TenancyFilterMixinV1, PrimaryModelFilterMixinV1):
+    prefix: FilterLookup[str] | None = strawberry_django.filter_field()
+    vrf: Annotated['VRFFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = strawberry_django.filter_field()
+    vrf_id: ID | None = strawberry_django.filter_field()
+    vlan: Annotated['VLANFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    vlan_id: ID | None = strawberry_django.filter_field()
+    status: Annotated['PrefixStatusEnum', strawberry.lazy('ipam.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    role: Annotated['RoleFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    role_id: ID | None = strawberry_django.filter_field()
+    is_pool: FilterLookup[bool] | None = strawberry_django.filter_field()
+    mark_utilized: FilterLookup[bool] | None = strawberry_django.filter_field()
+
+    @strawberry_django.filter_field()
+    def contains(self, value: list[str], prefix) -> Q:
+        if not value:
+            return Q()
+        q = Q()
+        for subnet in value:
+            query = str(netaddr.IPNetwork(subnet.strip()).cidr)
+            q |= Q(prefix__net_contains=query)
+        return q
+
+
+@strawberry_django.filter_type(models.RIR, lookups=True)
+class RIRFilterV1(OrganizationalModelFilterMixinV1):
+    is_private: FilterLookup[bool] | None = strawberry_django.filter_field()
+
+
+@strawberry_django.filter_type(models.Role, lookups=True)
+class RoleFilterV1(OrganizationalModelFilterMixinV1):
+    weight: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@strawberry_django.filter_type(models.RouteTarget, lookups=True)
+class RouteTargetFilterV1(TenancyFilterMixinV1, PrimaryModelFilterMixinV1):
+    name: FilterLookup[str] | None = strawberry_django.filter_field()
+    importing_vrfs: Annotated['VRFFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    exporting_vrfs: Annotated['VRFFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    importing_l2vpns: Annotated['L2VPNFilterV1', strawberry.lazy('vpn.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    exporting_l2vpns: Annotated['L2VPNFilterV1', strawberry.lazy('vpn.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@strawberry_django.filter_type(models.Service, lookups=True)
+class ServiceFilterV1(ContactFilterMixinV1, ServiceBaseFilterMixinV1, PrimaryModelFilterMixinV1):
+    name: FilterLookup[str] | None = strawberry_django.filter_field()
+    ip_addresses: Annotated['IPAddressFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    parent_object_type: Annotated['ContentTypeFilterV1', strawberry.lazy('core.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    parent_object_id: ID | None = strawberry_django.filter_field()
+
+
+@strawberry_django.filter_type(models.ServiceTemplate, lookups=True)
+class ServiceTemplateFilterV1(ServiceBaseFilterMixinV1, PrimaryModelFilterMixinV1):
+    name: FilterLookup[str] | None = strawberry_django.filter_field()
+
+
+@strawberry_django.filter_type(models.VLAN, lookups=True)
+class VLANFilterV1(TenancyFilterMixinV1, PrimaryModelFilterMixinV1):
+    site: Annotated['SiteFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    site_id: ID | None = strawberry_django.filter_field()
+    group: Annotated['VLANGroupFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    group_id: ID | None = strawberry_django.filter_field()
+    vid: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    name: FilterLookup[str] | None = strawberry_django.filter_field()
+    status: Annotated['VLANStatusEnum', strawberry.lazy('ipam.graphql.enums')] | None = strawberry_django.filter_field()
+    role: Annotated['RoleFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    role_id: ID | None = strawberry_django.filter_field()
+    qinq_svlan: Annotated['VLANFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    qinq_svlan_id: ID | None = strawberry_django.filter_field()
+    qinq_cvlans: Annotated['VLANFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    qinq_role: Annotated['VLANQinQRoleEnum', strawberry.lazy('ipam.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    l2vpn_terminations: Annotated['L2VPNFilterV1', strawberry.lazy('vpn.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@strawberry_django.filter_type(models.VLANGroup, lookups=True)
+class VLANGroupFilterV1(ScopedFilterMixinV1, OrganizationalModelFilterMixinV1):
+    vid_ranges: Annotated['IntegerRangeArrayLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@strawberry_django.filter_type(models.VLANTranslationPolicy, lookups=True)
+class VLANTranslationPolicyFilterV1(PrimaryModelFilterMixinV1):
+    name: FilterLookup[str] | None = strawberry_django.filter_field()
+
+
+@strawberry_django.filter_type(models.VLANTranslationRule, lookups=True)
+class VLANTranslationRuleFilterV1(NetBoxModelFilterMixinV1):
+    policy: Annotated['VLANTranslationPolicyFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    policy_id: ID | None = strawberry_django.filter_field()
+    description: FilterLookup[str] | None = strawberry_django.filter_field()
+    local_vid: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    remote_vid: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@strawberry_django.filter_type(models.VRF, lookups=True)
+class VRFFilterV1(TenancyFilterMixinV1, PrimaryModelFilterMixinV1):
+    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()
+    import_targets: Annotated['RouteTargetFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    export_targets: Annotated['RouteTargetFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )

+ 18 - 0
netbox/ipam/graphql/mixins_v1.py

@@ -0,0 +1,18 @@
+from typing import Annotated, List
+
+import strawberry
+
+__all__ = (
+    'IPAddressesMixinV1',
+    'VLANGroupsMixinV1',
+)
+
+
+@strawberry.type
+class IPAddressesMixinV1:
+    ip_addresses: List[Annotated["IPAddressTypeV1", strawberry.lazy('ipam.graphql.types_v1')]]  # noqa: F821
+
+
+@strawberry.type
+class VLANGroupsMixinV1:
+    vlan_groups: List[Annotated["VLANGroupTypeV1", strawberry.lazy('ipam.graphql.types_v1')]]  # noqa: F821

+ 63 - 0
netbox/ipam/graphql/schema_v1.py

@@ -0,0 +1,63 @@
+from typing import List
+
+import strawberry
+import strawberry_django
+
+from .types_v1 import *
+
+
+@strawberry.type(name="Query")
+class IPAMQueryV1:
+    asn: ASNTypeV1 = strawberry_django.field()
+    asn_list: List[ASNTypeV1] = strawberry_django.field()
+
+    asn_range: ASNRangeTypeV1 = strawberry_django.field()
+    asn_range_list: List[ASNRangeTypeV1] = strawberry_django.field()
+
+    aggregate: AggregateTypeV1 = strawberry_django.field()
+    aggregate_list: List[AggregateTypeV1] = strawberry_django.field()
+
+    ip_address: IPAddressTypeV1 = strawberry_django.field()
+    ip_address_list: List[IPAddressTypeV1] = strawberry_django.field()
+
+    ip_range: IPRangeTypeV1 = strawberry_django.field()
+    ip_range_list: List[IPRangeTypeV1] = strawberry_django.field()
+
+    prefix: PrefixTypeV1 = strawberry_django.field()
+    prefix_list: List[PrefixTypeV1] = strawberry_django.field()
+
+    rir: RIRTypeV1 = strawberry_django.field()
+    rir_list: List[RIRTypeV1] = strawberry_django.field()
+
+    role: RoleTypeV1 = strawberry_django.field()
+    role_list: List[RoleTypeV1] = strawberry_django.field()
+
+    route_target: RouteTargetTypeV1 = strawberry_django.field()
+    route_target_list: List[RouteTargetTypeV1] = strawberry_django.field()
+
+    service: ServiceTypeV1 = strawberry_django.field()
+    service_list: List[ServiceTypeV1] = strawberry_django.field()
+
+    service_template: ServiceTemplateTypeV1 = strawberry_django.field()
+    service_template_list: List[ServiceTemplateTypeV1] = strawberry_django.field()
+
+    fhrp_group: FHRPGroupTypeV1 = strawberry_django.field()
+    fhrp_group_list: List[FHRPGroupTypeV1] = strawberry_django.field()
+
+    fhrp_group_assignment: FHRPGroupAssignmentTypeV1 = strawberry_django.field()
+    fhrp_group_assignment_list: List[FHRPGroupAssignmentTypeV1] = strawberry_django.field()
+
+    vlan: VLANTypeV1 = strawberry_django.field()
+    vlan_list: List[VLANTypeV1] = strawberry_django.field()
+
+    vlan_group: VLANGroupTypeV1 = strawberry_django.field()
+    vlan_group_list: List[VLANGroupTypeV1] = strawberry_django.field()
+
+    vlan_translation_policy: VLANTranslationPolicyTypeV1 = strawberry_django.field()
+    vlan_translation_policy_list: List[VLANTranslationPolicyTypeV1] = strawberry_django.field()
+
+    vlan_translation_rule: VLANTranslationRuleTypeV1 = strawberry_django.field()
+    vlan_translation_rule_list: List[VLANTranslationRuleTypeV1] = strawberry_django.field()
+
+    vrf: VRFTypeV1 = strawberry_django.field()
+    vrf_list: List[VRFTypeV1] = strawberry_django.field()

+ 360 - 0
netbox/ipam/graphql/types_v1.py

@@ -0,0 +1,360 @@
+from typing import Annotated, List, TYPE_CHECKING, Union
+
+import strawberry
+import strawberry_django
+
+from circuits.graphql.types_v1 import ProviderTypeV1
+from dcim.graphql.types_v1 import SiteTypeV1
+from extras.graphql.mixins_v1 import ContactsMixinV1
+from ipam import models
+from netbox.graphql.scalars import BigInt
+from netbox.graphql.types_v1 import BaseObjectTypeV1, NetBoxObjectTypeV1, OrganizationalObjectTypeV1
+from .filters_v1 import *
+from .mixins_v1 import IPAddressesMixinV1
+
+if TYPE_CHECKING:
+    from dcim.graphql.types_v1 import (
+        DeviceTypeV1,
+        InterfaceTypeV1,
+        LocationTypeV1,
+        RackTypeV1,
+        RegionTypeV1,
+        SiteGroupTypeV1,
+        SiteTypeV1,
+    )
+    from tenancy.graphql.types_v1 import TenantTypeV1
+    from virtualization.graphql.types_v1 import (
+        ClusterGroupTypeV1, ClusterTypeV1, VMInterfaceTypeV1, VirtualMachineTypeV1
+    )
+    from vpn.graphql.types_v1 import L2VPNTypeV1, TunnelTerminationTypeV1
+    from wireless.graphql.types_v1 import WirelessLANTypeV1
+
+__all__ = (
+    'ASNTypeV1',
+    'ASNRangeTypeV1',
+    'AggregateTypeV1',
+    'FHRPGroupTypeV1',
+    'FHRPGroupAssignmentTypeV1',
+    'IPAddressTypeV1',
+    'IPRangeTypeV1',
+    'PrefixTypeV1',
+    'RIRTypeV1',
+    'RoleTypeV1',
+    'RouteTargetTypeV1',
+    'ServiceTypeV1',
+    'ServiceTemplateTypeV1',
+    'VLANTypeV1',
+    'VLANGroupTypeV1',
+    'VLANTranslationPolicyTypeV1',
+    'VLANTranslationRuleTypeV1',
+    'VRFTypeV1',
+)
+
+
+@strawberry.type
+class IPAddressFamilyTypeV1:
+    value: int
+    label: str
+
+
+@strawberry.type
+class BaseIPAddressFamilyTypeV1:
+    """
+    Base type for models that need to expose their IPAddress family type.
+    """
+
+    @strawberry.field
+    def family(self) -> IPAddressFamilyTypeV1:
+        # Note that self, is an instance of models.IPAddress
+        # thus resolves to the address family value.
+        return IPAddressFamilyTypeV1(value=self.family, label=f'IPv{self.family}')
+
+
+@strawberry_django.type(
+    models.ASN,
+    fields='__all__',
+    filters=ASNFilterV1,
+    pagination=True
+)
+class ASNTypeV1(NetBoxObjectTypeV1, ContactsMixinV1):
+    asn: BigInt
+    rir: Annotated["RIRTypeV1", strawberry.lazy('ipam.graphql.types_v1')] | None
+    tenant: Annotated["TenantTypeV1", strawberry.lazy('tenancy.graphql.types_v1')] | None
+
+    sites: List[SiteTypeV1]
+    providers: List[ProviderTypeV1]
+
+
+@strawberry_django.type(
+    models.ASNRange,
+    fields='__all__',
+    filters=ASNRangeFilterV1,
+    pagination=True
+)
+class ASNRangeTypeV1(NetBoxObjectTypeV1):
+    start: BigInt
+    end: BigInt
+    rir: Annotated["RIRTypeV1", strawberry.lazy('ipam.graphql.types_v1')] | None
+    tenant: Annotated["TenantTypeV1", strawberry.lazy('tenancy.graphql.types_v1')] | None
+
+
+@strawberry_django.type(
+    models.Aggregate,
+    fields='__all__',
+    filters=AggregateFilterV1,
+    pagination=True
+)
+class AggregateTypeV1(NetBoxObjectTypeV1, ContactsMixinV1, BaseIPAddressFamilyTypeV1):
+    prefix: str
+    rir: Annotated["RIRTypeV1", strawberry.lazy('ipam.graphql.types_v1')] | None
+    tenant: Annotated["TenantTypeV1", strawberry.lazy('tenancy.graphql.types_v1')] | None
+
+
+@strawberry_django.type(
+    models.FHRPGroup,
+    fields='__all__',
+    filters=FHRPGroupFilterV1,
+    pagination=True
+)
+class FHRPGroupTypeV1(NetBoxObjectTypeV1, IPAddressesMixinV1):
+
+    fhrpgroupassignment_set: List[Annotated["FHRPGroupAssignmentTypeV1", strawberry.lazy('ipam.graphql.types_v1')]]
+
+
+@strawberry_django.type(
+    models.FHRPGroupAssignment,
+    exclude=['interface_type', 'interface_id'],
+    filters=FHRPGroupAssignmentFilterV1,
+    pagination=True
+)
+class FHRPGroupAssignmentTypeV1(BaseObjectTypeV1):
+    group: Annotated["FHRPGroupTypeV1", strawberry.lazy('ipam.graphql.types_v1')]
+
+    @strawberry_django.field
+    def interface(self) -> Annotated[Union[
+        Annotated["InterfaceTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+        Annotated["VMInterfaceTypeV1", strawberry.lazy('virtualization.graphql.types_v1')],
+    ], strawberry.union("FHRPGroupInterfaceTypeV1")]:
+        return self.interface
+
+
+@strawberry_django.type(
+    models.IPAddress,
+    exclude=['assigned_object_type', 'assigned_object_id', 'address'],
+    filters=IPAddressFilterV1,
+    pagination=True
+)
+class IPAddressTypeV1(NetBoxObjectTypeV1, ContactsMixinV1, BaseIPAddressFamilyTypeV1):
+    address: str
+    vrf: Annotated["VRFTypeV1", strawberry.lazy('ipam.graphql.types_v1')] | None
+    tenant: Annotated["TenantTypeV1", strawberry.lazy('tenancy.graphql.types_v1')] | None
+    nat_inside: Annotated["IPAddressTypeV1", strawberry.lazy('ipam.graphql.types_v1')] | None
+
+    nat_outside: List[Annotated["IPAddressTypeV1", strawberry.lazy('ipam.graphql.types_v1')]]
+    tunnel_terminations: List[Annotated["TunnelTerminationTypeV1", strawberry.lazy('vpn.graphql.types_v1')]]
+    services: List[Annotated["ServiceTypeV1", strawberry.lazy('ipam.graphql.types_v1')]]
+
+    @strawberry_django.field
+    def assigned_object(self) -> Annotated[Union[
+        Annotated["InterfaceTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+        Annotated["FHRPGroupTypeV1", strawberry.lazy('ipam.graphql.types_v1')],
+        Annotated["VMInterfaceTypeV1", strawberry.lazy('virtualization.graphql.types_v1')],
+    ], strawberry.union("IPAddressAssignmentTypeV1")] | None:
+        return self.assigned_object
+
+
+@strawberry_django.type(
+    models.IPRange,
+    fields='__all__',
+    filters=IPRangeFilterV1,
+    pagination=True
+)
+class IPRangeTypeV1(NetBoxObjectTypeV1, ContactsMixinV1):
+    start_address: str
+    end_address: str
+    vrf: Annotated["VRFTypeV1", strawberry.lazy('ipam.graphql.types_v1')] | None
+    tenant: Annotated["TenantTypeV1", strawberry.lazy('tenancy.graphql.types_v1')] | None
+    role: Annotated["RoleTypeV1", strawberry.lazy('ipam.graphql.types_v1')] | None
+
+
+@strawberry_django.type(
+    models.Prefix,
+    exclude=['scope_type', 'scope_id', '_location', '_region', '_site', '_site_group'],
+    filters=PrefixFilterV1,
+    pagination=True
+)
+class PrefixTypeV1(NetBoxObjectTypeV1, ContactsMixinV1, BaseIPAddressFamilyTypeV1):
+    prefix: str
+    vrf: Annotated["VRFTypeV1", strawberry.lazy('ipam.graphql.types_v1')] | None
+    tenant: Annotated["TenantTypeV1", strawberry.lazy('tenancy.graphql.types_v1')] | None
+    vlan: Annotated["VLANTypeV1", strawberry.lazy('ipam.graphql.types_v1')] | None
+    role: Annotated["RoleTypeV1", strawberry.lazy('ipam.graphql.types_v1')] | None
+
+    @strawberry_django.field
+    def scope(self) -> Annotated[Union[
+        Annotated["LocationTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+        Annotated["RegionTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+        Annotated["SiteGroupTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+        Annotated["SiteTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+    ], strawberry.union("PrefixScopeTypeV1")] | None:
+        return self.scope
+
+
+@strawberry_django.type(
+    models.RIR,
+    fields='__all__',
+    filters=RIRFilterV1,
+    pagination=True
+)
+class RIRTypeV1(OrganizationalObjectTypeV1):
+
+    asn_ranges: List[Annotated["ASNRangeTypeV1", strawberry.lazy('ipam.graphql.types_v1')]]
+    asns: List[Annotated["ASNTypeV1", strawberry.lazy('ipam.graphql.types_v1')]]
+    aggregates: List[Annotated["AggregateTypeV1", strawberry.lazy('ipam.graphql.types_v1')]]
+
+
+@strawberry_django.type(
+    models.Role,
+    fields='__all__',
+    filters=RoleFilterV1,
+    pagination=True
+)
+class RoleTypeV1(OrganizationalObjectTypeV1):
+
+    prefixes: List[Annotated["PrefixTypeV1", strawberry.lazy('ipam.graphql.types_v1')]]
+    ip_ranges: List[Annotated["IPRangeTypeV1", strawberry.lazy('ipam.graphql.types_v1')]]
+    vlans: List[Annotated["VLANTypeV1", strawberry.lazy('ipam.graphql.types_v1')]]
+
+
+@strawberry_django.type(
+    models.RouteTarget,
+    fields='__all__',
+    filters=RouteTargetFilterV1,
+    pagination=True
+)
+class RouteTargetTypeV1(NetBoxObjectTypeV1):
+    tenant: Annotated["TenantTypeV1", strawberry.lazy('tenancy.graphql.types_v1')] | None
+
+    importing_l2vpns: List[Annotated["L2VPNTypeV1", strawberry.lazy('vpn.graphql.types_v1')]]
+    exporting_l2vpns: List[Annotated["L2VPNTypeV1", strawberry.lazy('vpn.graphql.types_v1')]]
+    importing_vrfs: List[Annotated["VRFTypeV1", strawberry.lazy('ipam.graphql.types_v1')]]
+    exporting_vrfs: List[Annotated["VRFTypeV1", strawberry.lazy('ipam.graphql.types_v1')]]
+
+
+@strawberry_django.type(
+    models.Service,
+    exclude=('parent_object_type', 'parent_object_id'),
+    filters=ServiceFilterV1,
+    pagination=True
+)
+class ServiceTypeV1(NetBoxObjectTypeV1, ContactsMixinV1):
+    ports: List[int]
+    ipaddresses: List[Annotated["IPAddressTypeV1", strawberry.lazy('ipam.graphql.types_v1')]]
+
+    @strawberry_django.field
+    def parent(self) -> Annotated[Union[
+        Annotated["DeviceTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+        Annotated["VirtualMachineTypeV1", strawberry.lazy('virtualization.graphql.types_v1')],
+        Annotated["FHRPGroupTypeV1", strawberry.lazy('ipam.graphql.types_v1')],
+    ], strawberry.union("ServiceParentTypeV1")] | None:
+        return self.parent
+
+
+@strawberry_django.type(
+    models.ServiceTemplate,
+    fields='__all__',
+    filters=ServiceTemplateFilterV1,
+    pagination=True
+)
+class ServiceTemplateTypeV1(NetBoxObjectTypeV1):
+    ports: List[int]
+
+
+@strawberry_django.type(
+    models.VLAN,
+    exclude=['qinq_svlan'],
+    filters=VLANFilterV1,
+    pagination=True
+)
+class VLANTypeV1(NetBoxObjectTypeV1):
+    site: Annotated["SiteTypeV1", strawberry.lazy('ipam.graphql.types_v1')] | None
+    group: Annotated["VLANGroupTypeV1", strawberry.lazy('ipam.graphql.types_v1')] | None
+    tenant: Annotated["TenantTypeV1", strawberry.lazy('tenancy.graphql.types_v1')] | None
+    role: Annotated["RoleTypeV1", strawberry.lazy('ipam.graphql.types_v1')] | None
+
+    interfaces_as_untagged: List[Annotated["InterfaceTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    vminterfaces_as_untagged: List[Annotated["VMInterfaceTypeV1", strawberry.lazy('virtualization.graphql.types_v1')]]
+    wirelesslan_set: List[Annotated["WirelessLANTypeV1", strawberry.lazy('wireless.graphql.types_v1')]]
+    prefixes: List[Annotated["PrefixTypeV1", strawberry.lazy('ipam.graphql.types_v1')]]
+    interfaces_as_tagged: List[Annotated["InterfaceTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    vminterfaces_as_tagged: List[Annotated["VMInterfaceTypeV1", strawberry.lazy('virtualization.graphql.types_v1')]]
+
+    @strawberry_django.field
+    def qinq_svlan(self) -> Annotated["VLANTypeV1", strawberry.lazy('ipam.graphql.types_v1')] | None:
+        return self.qinq_svlan
+
+
+@strawberry_django.type(
+    models.VLANGroup,
+    exclude=['scope_type', 'scope_id'],
+    filters=VLANGroupFilterV1,
+    pagination=True
+)
+class VLANGroupTypeV1(OrganizationalObjectTypeV1):
+
+    vlans: List[VLANTypeV1]
+    vid_ranges: List[str]
+    tenant: Annotated["TenantTypeV1", strawberry.lazy('tenancy.graphql.types_v1')] | None
+
+    @strawberry_django.field
+    def scope(self) -> Annotated[Union[
+        Annotated["ClusterTypeV1", strawberry.lazy('virtualization.graphql.types_v1')],
+        Annotated["ClusterGroupTypeV1", strawberry.lazy('virtualization.graphql.types_v1')],
+        Annotated["LocationTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+        Annotated["RackTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+        Annotated["RegionTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+        Annotated["SiteTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+        Annotated["SiteGroupTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+    ], strawberry.union("VLANGroupScopeTypeV1")] | None:
+        return self.scope
+
+
+@strawberry_django.type(
+    models.VLANTranslationPolicy,
+    fields='__all__',
+    filters=VLANTranslationPolicyFilterV1,
+    pagination=True
+)
+class VLANTranslationPolicyTypeV1(NetBoxObjectTypeV1):
+    rules: List[Annotated["VLANTranslationRuleTypeV1", strawberry.lazy('ipam.graphql.types_v1')]]
+
+
+@strawberry_django.type(
+    models.VLANTranslationRule,
+    fields='__all__',
+    filters=VLANTranslationRuleFilterV1,
+    pagination=True
+)
+class VLANTranslationRuleTypeV1(NetBoxObjectTypeV1):
+    policy: Annotated[
+        "VLANTranslationPolicyTypeV1",
+        strawberry.lazy('ipam.graphql.types_v1')
+    ] = strawberry_django.field(select_related=["policy"])
+
+
+@strawberry_django.type(
+    models.VRF,
+    fields='__all__',
+    filters=VRFFilterV1,
+    pagination=True
+)
+class VRFTypeV1(NetBoxObjectTypeV1):
+    tenant: Annotated["TenantTypeV1", strawberry.lazy('tenancy.graphql.types_v1')] | None
+
+    interfaces: List[Annotated["InterfaceTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+    ip_addresses: List[Annotated["IPAddressTypeV1", strawberry.lazy('ipam.graphql.types_v1')]]
+    vminterfaces: List[Annotated["VMInterfaceTypeV1", strawberry.lazy('virtualization.graphql.types_v1')]]
+    ip_ranges: List[Annotated["IPRangeTypeV1", strawberry.lazy('ipam.graphql.types_v1')]]
+    export_targets: List[Annotated["RouteTargetTypeV1", strawberry.lazy('ipam.graphql.types_v1')]]
+    import_targets: List[Annotated["RouteTargetTypeV1", strawberry.lazy('ipam.graphql.types_v1')]]
+    prefixes: List[Annotated["PrefixTypeV1", strawberry.lazy('ipam.graphql.types_v1')]]

+ 104 - 0
netbox/netbox/graphql/filter_mixins_v1.py

@@ -0,0 +1,104 @@
+from dataclasses import dataclass
+from datetime import datetime
+from typing import TypeVar, TYPE_CHECKING, Annotated
+
+import strawberry
+import strawberry_django
+from strawberry import ID
+from strawberry_django import FilterLookup, DatetimeFilterLookup
+
+from core.graphql.filter_mixins_v1 import BaseFilterMixinV1, BaseObjectTypeFilterMixinV1, ChangeLogFilterMixinV1
+from extras.graphql.filter_mixins_v1 import CustomFieldsFilterMixinV1, JournalEntriesFilterMixinV1, TagsFilterMixinV1
+
+__all__ = (
+    'DistanceFilterMixinV1',
+    'ImageAttachmentFilterMixinV1',
+    'NestedGroupModelFilterMixinV1',
+    'NetBoxModelFilterMixinV1',
+    'OrganizationalModelFilterMixinV1',
+    'PrimaryModelFilterMixinV1',
+    'SyncedDataFilterMixinV1',
+    'WeightFilterMixinV1',
+)
+
+T = TypeVar('T')
+
+
+if TYPE_CHECKING:
+    from .enums import *
+    from core.graphql.filters_v1 import *
+    from extras.graphql.filters_v1 import *
+
+
+class NetBoxModelFilterMixinV1(
+    ChangeLogFilterMixinV1,
+    CustomFieldsFilterMixinV1,
+    JournalEntriesFilterMixinV1,
+    TagsFilterMixinV1,
+    BaseObjectTypeFilterMixinV1,
+):
+    pass
+
+
+@dataclass
+class NestedGroupModelFilterMixinV1(NetBoxModelFilterMixinV1):
+    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 OrganizationalModelFilterMixinV1(
+    ChangeLogFilterMixinV1,
+    CustomFieldsFilterMixinV1,
+    TagsFilterMixinV1,
+    BaseObjectTypeFilterMixinV1,
+):
+    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 PrimaryModelFilterMixinV1(NetBoxModelFilterMixinV1):
+    description: FilterLookup[str] | None = strawberry_django.filter_field()
+    comments: FilterLookup[str] | None = strawberry_django.filter_field()
+
+
+@dataclass
+class ImageAttachmentFilterMixinV1(BaseFilterMixinV1):
+    images: Annotated['ImageAttachmentFilterV1', strawberry.lazy('extras.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@dataclass
+class WeightFilterMixinV1(BaseFilterMixinV1):
+    weight: FilterLookup[float] | None = strawberry_django.filter_field()
+    weight_unit: Annotated['WeightUnitEnum', strawberry.lazy('netbox.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@dataclass
+class SyncedDataFilterMixinV1(BaseFilterMixinV1):
+    data_source: Annotated['DataSourceFilterV1', strawberry.lazy('core.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    data_source_id: FilterLookup[int] | None = strawberry_django.filter_field()
+    data_file: Annotated['DataFileFilterV1', strawberry.lazy('core.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    data_file_id: FilterLookup[int] | None = strawberry_django.filter_field()
+    data_path: FilterLookup[str] | None = strawberry_django.filter_field()
+    auto_sync_enabled: FilterLookup[bool] | None = strawberry_django.filter_field()
+    data_synced: DatetimeFilterLookup[datetime] | None = strawberry_django.filter_field()
+
+
+@dataclass
+class DistanceFilterMixinV1(BaseFilterMixinV1):
+    distance: FilterLookup[float] | None = strawberry_django.filter_field()
+    distance_unit: Annotated['DistanceUnitEnum', strawberry.lazy('netbox.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )

+ 20 - 10
netbox/netbox/graphql/schema.py

@@ -4,16 +4,26 @@ from strawberry_django.optimizer import DjangoOptimizerExtension
 from strawberry.extensions import MaxAliasesLimiter
 from strawberry.schema.config import StrawberryConfig
 
+from circuits.graphql.schema_v1 import CircuitsQueryV1
 from circuits.graphql.schema import CircuitsQuery
+from core.graphql.schema_v1 import CoreQueryV1
 from core.graphql.schema import CoreQuery
+from dcim.graphql.schema_v1 import DCIMQueryV1
 from dcim.graphql.schema import DCIMQuery
+from extras.graphql.schema_v1 import ExtrasQueryV1
 from extras.graphql.schema import ExtrasQuery
+from ipam.graphql.schema_v1 import IPAMQueryV1
 from ipam.graphql.schema import IPAMQuery
 from netbox.registry import registry
+from tenancy.graphql.schema_v1 import TenancyQueryV1
 from tenancy.graphql.schema import TenancyQuery
+from users.graphql.schema_v1 import UsersQueryV1
 from users.graphql.schema import UsersQuery
+from virtualization.graphql.schema_v1 import VirtualizationQueryV1
 from virtualization.graphql.schema import VirtualizationQuery
+from vpn.graphql.schema_v1 import VPNQueryV1
 from vpn.graphql.schema import VPNQuery
+from wireless.graphql.schema_v1 import WirelessQueryV1
 from wireless.graphql.schema import WirelessQuery
 
 __all__ = (
@@ -27,16 +37,16 @@ __all__ = (
 
 @strawberry.type
 class QueryV1(
-    UsersQuery,
-    CircuitsQuery,
-    CoreQuery,
-    DCIMQuery,
-    ExtrasQuery,
-    IPAMQuery,
-    TenancyQuery,
-    VirtualizationQuery,
-    VPNQuery,
-    WirelessQuery,
+    UsersQueryV1,
+    CircuitsQueryV1,
+    CoreQueryV1,
+    DCIMQueryV1,
+    ExtrasQueryV1,
+    IPAMQueryV1,
+    TenancyQueryV1,
+    VirtualizationQueryV1,
+    VPNQueryV1,
+    WirelessQueryV1,
     *registry['plugins']['graphql_schemas'],  # Append plugin schemas
 ):
     """Query class for GraphQL API v1"""

+ 100 - 0
netbox/netbox/graphql/types_v1.py

@@ -0,0 +1,100 @@
+import strawberry
+import strawberry_django
+from strawberry.types import Info
+from django.contrib.contenttypes.models import ContentType
+
+from core.graphql.mixins import ChangelogMixin
+from core.models import ObjectType as ObjectType_
+from extras.graphql.mixins import CustomFieldsMixin, JournalEntriesMixin, TagsMixin
+
+__all__ = (
+    'BaseObjectTypeV1',
+    'ContentTypeTypeV1',
+    'ObjectTypeV1',
+    'OrganizationalObjectTypeV1',
+    'NetBoxObjectTypeV1',
+)
+
+
+#
+# Base types
+#
+
+@strawberry.type
+class BaseObjectTypeV1:
+    """
+    Base GraphQL object type for all NetBox objects. Restricts the model queryset to enforce object permissions.
+    """
+
+    @classmethod
+    def get_queryset(cls, queryset, info: Info, **kwargs):
+        # Enforce object permissions on the queryset
+        if hasattr(queryset, 'restrict'):
+            return queryset.restrict(info.context.request.user, 'view')
+        else:
+            return queryset
+
+    @strawberry_django.field
+    def display(self) -> str:
+        return str(self)
+
+    @strawberry_django.field
+    def class_type(self) -> str:
+        return self.__class__.__name__
+
+
+class ObjectTypeV1(
+    ChangelogMixin,
+    BaseObjectTypeV1
+):
+    """
+    Base GraphQL object type for unclassified models which support change logging
+    """
+    pass
+
+
+class OrganizationalObjectTypeV1(
+    ChangelogMixin,
+    CustomFieldsMixin,
+    TagsMixin,
+    BaseObjectTypeV1
+):
+    """
+    Base type for organizational models
+    """
+    pass
+
+
+class NetBoxObjectTypeV1(
+    ChangelogMixin,
+    CustomFieldsMixin,
+    JournalEntriesMixin,
+    TagsMixin,
+    BaseObjectTypeV1
+):
+    """
+    GraphQL type for most NetBox models. Includes support for custom fields, change logging, journaling, and tags.
+    """
+    pass
+
+
+#
+# Miscellaneous types
+#
+
+@strawberry_django.type(
+    ContentType,
+    fields=['id', 'app_label', 'model'],
+    pagination=True
+)
+class ContentTypeTypeV1:
+    pass
+
+
+@strawberry_django.type(
+    ObjectType_,
+    fields=['id', 'app_label', 'model'],
+    pagination=True
+)
+class ObjectTypeTypeV1:
+    pass

+ 38 - 0
netbox/tenancy/graphql/filter_mixins_v1.py

@@ -0,0 +1,38 @@
+from dataclasses import dataclass
+from typing import Annotated, TYPE_CHECKING
+
+import strawberry
+import strawberry_django
+from strawberry import ID
+
+from core.graphql.filter_mixins_v1 import BaseFilterMixinV1
+
+if TYPE_CHECKING:
+    from netbox.graphql.filter_lookups import TreeNodeFilter
+    from .filters_v1 import ContactAssignmentFilterV1, TenantFilterV1, TenantGroupFilterV1
+
+__all__ = (
+    'ContactFilterMixinV1',
+    'TenancyFilterMixinV1',
+)
+
+
+@dataclass
+class ContactFilterMixinV1(BaseFilterMixinV1):
+    contacts: Annotated['ContactAssignmentFilterV1', strawberry.lazy('tenancy.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@dataclass
+class TenancyFilterMixinV1(BaseFilterMixinV1):
+    tenant: Annotated['TenantFilterV1', strawberry.lazy('tenancy.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    tenant_id: ID | None = strawberry_django.filter_field()
+    tenant_group: Annotated['TenantGroupFilterV1', strawberry.lazy('tenancy.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    tenant_group_id: Annotated['TreeNodeFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )

+ 210 - 0
netbox/tenancy/graphql/filters_v1.py

@@ -0,0 +1,210 @@
+from typing import Annotated, TYPE_CHECKING
+
+import strawberry
+import strawberry_django
+from strawberry.scalars import ID
+from strawberry_django import FilterLookup
+
+from core.graphql.filter_mixins_v1 import ChangeLogFilterMixinV1
+from extras.graphql.filter_mixins_v1 import CustomFieldsFilterMixinV1, TagsFilterMixinV1
+from netbox.graphql.filter_mixins_v1 import (
+    NestedGroupModelFilterMixinV1,
+    OrganizationalModelFilterMixinV1,
+    PrimaryModelFilterMixinV1,
+)
+from tenancy import models
+from .filter_mixins_v1 import ContactFilterMixinV1
+
+if TYPE_CHECKING:
+    from core.graphql.filters_v1 import ContentTypeFilterV1
+    from circuits.graphql.filters_v1 import CircuitFilterV1, CircuitGroupFilterV1, VirtualCircuitFilterV1
+    from dcim.graphql.filters_v1 import (
+        CableFilterV1,
+        DeviceFilterV1,
+        LocationFilterV1,
+        PowerFeedFilterV1,
+        RackFilterV1,
+        RackReservationFilterV1,
+        SiteFilterV1,
+        VirtualDeviceContextFilterV1,
+    )
+    from ipam.graphql.filters_v1 import (
+        AggregateFilterV1,
+        ASNFilterV1,
+        ASNRangeFilterV1,
+        IPAddressFilterV1,
+        IPRangeFilterV1,
+        PrefixFilterV1,
+        RouteTargetFilterV1,
+        VLANFilterV1,
+        VLANGroupFilterV1,
+        VRFFilterV1,
+    )
+    from netbox.graphql.filter_lookups import TreeNodeFilter
+    from wireless.graphql.filters_v1 import WirelessLANFilterV1, WirelessLinkFilterV1
+    from virtualization.graphql.filters_v1 import ClusterFilterV1, VirtualMachineFilterV1
+    from vpn.graphql.filters_v1 import L2VPNFilterV1, TunnelFilterV1
+    from .enums import *
+
+__all__ = (
+    'TenantFilterV1',
+    'TenantGroupFilterV1',
+    'ContactFilterV1',
+    'ContactRoleFilterV1',
+    'ContactGroupFilterV1',
+    'ContactAssignmentFilterV1',
+)
+
+
+@strawberry_django.filter_type(models.Tenant, lookups=True)
+class TenantFilterV1(PrimaryModelFilterMixinV1, ContactFilterMixinV1):
+    name: FilterLookup[str] | None = strawberry_django.filter_field()
+    slug: FilterLookup[str] | None = strawberry_django.filter_field()
+    group: Annotated['TenantGroupFilterV1', strawberry.lazy('tenancy.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    group_id: Annotated['TreeNodeFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+
+    # Reverse relations
+    aggregates: Annotated['AggregateFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    asns: Annotated['ASNFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = strawberry_django.filter_field()
+    asn_ranges: Annotated['ASNRangeFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    cables: Annotated['CableFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    circuit_groups: Annotated['CircuitGroupFilterV1', strawberry.lazy('circuits.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    circuits: Annotated['CircuitFilterV1', strawberry.lazy('circuits.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    clusters: Annotated['ClusterFilterV1', strawberry.lazy('virtualization.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    devices: Annotated['DeviceFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    ip_addresses: Annotated['IPAddressFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    ip_ranges: Annotated['IPRangeFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    l2vpns: Annotated['L2VPNFilterV1', strawberry.lazy('vpn.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    locations: Annotated['LocationFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    power_feeds: Annotated['PowerFeedFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    prefixes: Annotated['PrefixFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    racks: Annotated['RackFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    rackreservations: Annotated['RackReservationFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    route_targets: Annotated['RouteTargetFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    sites: Annotated['SiteFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    tunnels: Annotated['TunnelFilterV1', strawberry.lazy('vpn.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    vdcs: Annotated['VirtualDeviceContextFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    virtual_machines: Annotated[
+        'VirtualMachineFilterV1', strawberry.lazy('virtualization.graphql.filters_v1')
+    ] | None = (
+        strawberry_django.filter_field()
+    )
+    vlan_groups: Annotated['VLANGroupFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    vlans: Annotated['VLANFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    virtual_circuits: Annotated['VirtualCircuitFilterV1', strawberry.lazy('circuits.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    vrfs: Annotated['VRFFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = strawberry_django.filter_field()
+    wireless_lans: Annotated['WirelessLANFilterV1', strawberry.lazy('wireless.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    wireless_links: Annotated['WirelessLinkFilterV1', strawberry.lazy('wireless.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@strawberry_django.filter_type(models.TenantGroup, lookups=True)
+class TenantGroupFilterV1(OrganizationalModelFilterMixinV1):
+    parent: Annotated['TenantGroupFilterV1', strawberry.lazy('tenancy.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    parent_id: ID | None = strawberry.UNSET
+    tenants: Annotated['TenantFilterV1', strawberry.lazy('tenancy.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    children: Annotated['TenantGroupFilterV1', strawberry.lazy('tenancy.graphql.filters_v1'), True] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@strawberry_django.filter_type(models.Contact, lookups=True)
+class ContactFilterV1(PrimaryModelFilterMixinV1):
+    name: FilterLookup[str] | None = strawberry_django.filter_field()
+    title: FilterLookup[str] | None = strawberry_django.filter_field()
+    phone: FilterLookup[str] | None = strawberry_django.filter_field()
+    email: FilterLookup[str] | None = strawberry_django.filter_field()
+    address: FilterLookup[str] | None = strawberry_django.filter_field()
+    link: FilterLookup[str] | None = strawberry_django.filter_field()
+    groups: Annotated['ContactGroupFilterV1', strawberry.lazy('tenancy.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    assignments: Annotated['ContactAssignmentFilterV1', strawberry.lazy('tenancy.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@strawberry_django.filter_type(models.ContactRole, lookups=True)
+class ContactRoleFilterV1(OrganizationalModelFilterMixinV1):
+    pass
+
+
+@strawberry_django.filter_type(models.ContactGroup, lookups=True)
+class ContactGroupFilterV1(NestedGroupModelFilterMixinV1):
+    parent: Annotated['ContactGroupFilterV1', strawberry.lazy('tenancy.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@strawberry_django.filter_type(models.ContactAssignment, lookups=True)
+class ContactAssignmentFilterV1(CustomFieldsFilterMixinV1, TagsFilterMixinV1, ChangeLogFilterMixinV1):
+    object_type: Annotated['ContentTypeFilterV1', strawberry.lazy('core.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    object_id: ID | None = strawberry_django.filter_field()
+    contact: Annotated['ContactFilterV1', strawberry.lazy('tenancy.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    contact_id: ID | None = strawberry_django.filter_field()
+    role: Annotated['ContactRoleFilterV1', strawberry.lazy('tenancy.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    role_id: ID | None = strawberry_django.filter_field()
+    priority: Annotated['ContactPriorityEnum', strawberry.lazy('tenancy.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )

+ 12 - 0
netbox/tenancy/graphql/mixins_v1.py

@@ -0,0 +1,12 @@
+from typing import Annotated, List
+
+import strawberry
+
+__all__ = (
+    'ContactAssignmentsMixinV1',
+)
+
+
+@strawberry.type
+class ContactAssignmentsMixinV1:
+    assignments: List[Annotated["ContactAssignmentTypeV1", strawberry.lazy('tenancy.graphql.types_v1')]]  # noqa: F821

+ 27 - 0
netbox/tenancy/graphql/schema_v1.py

@@ -0,0 +1,27 @@
+from typing import List
+
+import strawberry
+import strawberry_django
+
+from .types_v1 import *
+
+
+@strawberry.type(name="Query")
+class TenancyQueryV1:
+    tenant: TenantTypeV1 = strawberry_django.field()
+    tenant_list: List[TenantTypeV1] = strawberry_django.field()
+
+    tenant_group: TenantGroupTypeV1 = strawberry_django.field()
+    tenant_group_list: List[TenantGroupTypeV1] = strawberry_django.field()
+
+    contact: ContactTypeV1 = strawberry_django.field()
+    contact_list: List[ContactTypeV1] = strawberry_django.field()
+
+    contact_role: ContactRoleTypeV1 = strawberry_django.field()
+    contact_role_list: List[ContactRoleTypeV1] = strawberry_django.field()
+
+    contact_group: ContactGroupTypeV1 = strawberry_django.field()
+    contact_group_list: List[ContactGroupTypeV1] = strawberry_django.field()
+
+    contact_assignment: ContactAssignmentTypeV1 = strawberry_django.field()
+    contact_assignment_list: List[ContactAssignmentTypeV1] = strawberry_django.field()

+ 147 - 0
netbox/tenancy/graphql/types_v1.py

@@ -0,0 +1,147 @@
+from typing import Annotated, List, TYPE_CHECKING
+
+import strawberry
+import strawberry_django
+
+from extras.graphql.mixins_v1 import CustomFieldsMixinV1, TagsMixinV1, ContactsMixinV1
+from netbox.graphql.types_v1 import BaseObjectTypeV1, OrganizationalObjectTypeV1, NetBoxObjectTypeV1
+from tenancy import models
+from .filters_v1 import *
+from .mixins_v1 import ContactAssignmentsMixinV1
+
+if TYPE_CHECKING:
+    from circuits.graphql.types_v1 import CircuitTypeV1
+    from dcim.graphql.types_v1 import (
+        CableTypeV1,
+        DeviceTypeV1,
+        LocationTypeV1,
+        PowerFeedTypeV1,
+        RackTypeV1,
+        RackReservationTypeV1,
+        SiteTypeV1,
+        VirtualDeviceContextTypeV1,
+    )
+    from ipam.graphql.types_v1 import (
+        AggregateTypeV1,
+        ASNTypeV1,
+        ASNRangeTypeV1,
+        IPAddressTypeV1,
+        IPRangeTypeV1,
+        PrefixTypeV1,
+        RouteTargetTypeV1,
+        VLANTypeV1,
+        VRFTypeV1,
+    )
+    from netbox.graphql.types_v1 import ContentTypeTypeV1
+    from wireless.graphql.types_v1 import WirelessLANTypeV1, WirelessLinkTypeV1
+    from virtualization.graphql.types_v1 import ClusterTypeV1, VirtualMachineTypeV1
+    from vpn.graphql.types_v1 import L2VPNTypeV1, TunnelTypeV1
+
+__all__ = (
+    'ContactAssignmentTypeV1',
+    'ContactGroupTypeV1',
+    'ContactRoleTypeV1',
+    'ContactTypeV1',
+    'TenantTypeV1',
+    'TenantGroupTypeV1',
+)
+
+
+#
+# Tenants
+#
+
+@strawberry_django.type(
+    models.Tenant,
+    fields='__all__',
+    filters=TenantFilterV1,
+    pagination=True
+)
+class TenantTypeV1(ContactsMixinV1, NetBoxObjectTypeV1):
+    group: Annotated['TenantGroupTypeV1', strawberry.lazy('tenancy.graphql.types_v1')] | None
+    asns: List[Annotated['ASNTypeV1', strawberry.lazy('ipam.graphql.types_v1')]]
+    circuits: List[Annotated['CircuitTypeV1', strawberry.lazy('circuits.graphql.types_v1')]]
+    sites: List[Annotated['SiteTypeV1', strawberry.lazy('dcim.graphql.types_v1')]]
+    vlans: List[Annotated['VLANTypeV1', strawberry.lazy('ipam.graphql.types_v1')]]
+    wireless_lans: List[Annotated['WirelessLANTypeV1', strawberry.lazy('wireless.graphql.types_v1')]]
+    route_targets: List[Annotated['RouteTargetTypeV1', strawberry.lazy('ipam.graphql.types_v1')]]
+    locations: List[Annotated['LocationTypeV1', strawberry.lazy('dcim.graphql.types_v1')]]
+    ip_ranges: List[Annotated['IPRangeTypeV1', strawberry.lazy('ipam.graphql.types_v1')]]
+    rackreservations: List[Annotated['RackReservationTypeV1', strawberry.lazy('dcim.graphql.types_v1')]]
+    racks: List[Annotated['RackTypeV1', strawberry.lazy('dcim.graphql.types_v1')]]
+    vdcs: List[Annotated['VirtualDeviceContextTypeV1', strawberry.lazy('dcim.graphql.types_v1')]]
+    prefixes: List[Annotated['PrefixTypeV1', strawberry.lazy('ipam.graphql.types_v1')]]
+    cables: List[Annotated['CableTypeV1', strawberry.lazy('dcim.graphql.types_v1')]]
+    virtual_machines: List[Annotated['VirtualMachineTypeV1', strawberry.lazy('virtualization.graphql.types_v1')]]
+    vrfs: List[Annotated['VRFTypeV1', strawberry.lazy('ipam.graphql.types_v1')]]
+    asn_ranges: List[Annotated['ASNRangeTypeV1', strawberry.lazy('ipam.graphql.types_v1')]]
+    wireless_links: List[Annotated['WirelessLinkTypeV1', strawberry.lazy('wireless.graphql.types_v1')]]
+    aggregates: List[Annotated['AggregateTypeV1', strawberry.lazy('ipam.graphql.types_v1')]]
+    power_feeds: List[Annotated['PowerFeedTypeV1', strawberry.lazy('dcim.graphql.types_v1')]]
+    devices: List[Annotated['DeviceTypeV1', strawberry.lazy('dcim.graphql.types_v1')]]
+    tunnels: List[Annotated['TunnelTypeV1', strawberry.lazy('vpn.graphql.types_v1')]]
+    ip_addresses: List[Annotated['IPAddressTypeV1', strawberry.lazy('ipam.graphql.types_v1')]]
+    clusters: List[Annotated['ClusterTypeV1', strawberry.lazy('virtualization.graphql.types_v1')]]
+    l2vpns: List[Annotated['L2VPNTypeV1', strawberry.lazy('vpn.graphql.types_v1')]]
+
+
+@strawberry_django.type(
+    models.TenantGroup,
+    fields='__all__',
+    filters=TenantGroupFilterV1,
+    pagination=True
+)
+class TenantGroupTypeV1(OrganizationalObjectTypeV1):
+    parent: Annotated['TenantGroupTypeV1', strawberry.lazy('tenancy.graphql.types_v1')] | None
+
+    tenants: List[TenantTypeV1]
+    children: List[Annotated['TenantGroupTypeV1', strawberry.lazy('tenancy.graphql.types_v1')]]
+
+
+#
+# Contacts
+#
+
+@strawberry_django.type(
+    models.Contact,
+    fields='__all__',
+    filters=ContactFilterV1,
+    pagination=True
+)
+class ContactTypeV1(ContactAssignmentsMixinV1, NetBoxObjectTypeV1):
+    groups: List[Annotated['ContactGroupTypeV1', strawberry.lazy('tenancy.graphql.types_v1')]]
+
+
+@strawberry_django.type(
+    models.ContactRole,
+    fields='__all__',
+    filters=ContactRoleFilterV1,
+    pagination=True
+)
+class ContactRoleTypeV1(ContactAssignmentsMixinV1, OrganizationalObjectTypeV1):
+    pass
+
+
+@strawberry_django.type(
+    models.ContactGroup,
+    fields='__all__',
+    filters=ContactGroupFilterV1,
+    pagination=True
+)
+class ContactGroupTypeV1(OrganizationalObjectTypeV1):
+    parent: Annotated['ContactGroupTypeV1', strawberry.lazy('tenancy.graphql.types_v1')] | None
+
+    contacts: List[ContactTypeV1]
+    children: List[Annotated['ContactGroupTypeV1', strawberry.lazy('tenancy.graphql.types_v1')]]
+
+
+@strawberry_django.type(
+    models.ContactAssignment,
+    fields='__all__',
+    filters=ContactAssignmentFilterV1,
+    pagination=True
+)
+class ContactAssignmentTypeV1(CustomFieldsMixinV1, TagsMixinV1, BaseObjectTypeV1):
+    object_type: Annotated['ContentTypeTypeV1', strawberry.lazy('netbox.graphql.types_v1')] | None
+    contact: Annotated['ContactTypeV1', strawberry.lazy('tenancy.graphql.types_v1')] | None
+    role: Annotated['ContactRoleTypeV1', strawberry.lazy('tenancy.graphql.types_v1')] | None

+ 34 - 0
netbox/users/graphql/filters_v1.py

@@ -0,0 +1,34 @@
+from datetime import datetime
+from typing import Annotated
+
+import strawberry
+import strawberry_django
+from strawberry_django import DatetimeFilterLookup, FilterLookup
+
+from core.graphql.filter_mixins_v1 import BaseObjectTypeFilterMixinV1
+from users import models
+
+__all__ = (
+    'GroupFilterV1',
+    'UserFilterV1',
+)
+
+
+@strawberry_django.filter_type(models.Group, lookups=True)
+class GroupFilterV1(BaseObjectTypeFilterMixinV1):
+    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 UserFilterV1(BaseObjectTypeFilterMixinV1):
+    username: FilterLookup[str] | None = strawberry_django.filter_field()
+    first_name: FilterLookup[str] | None = strawberry_django.filter_field()
+    last_name: FilterLookup[str] | None = strawberry_django.filter_field()
+    email: FilterLookup[str] | None = strawberry_django.filter_field()
+    is_superuser: FilterLookup[bool] | None = strawberry_django.filter_field()
+    is_active: FilterLookup[bool] | None = strawberry_django.filter_field()
+    date_joined: DatetimeFilterLookup[datetime] | None = strawberry_django.filter_field()
+    last_login: DatetimeFilterLookup[datetime] | None = strawberry_django.filter_field()
+    groups: Annotated['GroupFilterV1', strawberry.lazy('users.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field())

+ 15 - 0
netbox/users/graphql/schema_v1.py

@@ -0,0 +1,15 @@
+from typing import List
+
+import strawberry
+import strawberry_django
+
+from .types_v1 import *
+
+
+@strawberry.type(name="Query")
+class UsersQueryV1:
+    group: GroupTypeV1 = strawberry_django.field()
+    group_list: List[GroupTypeV1] = strawberry_django.field()
+
+    user: UserTypeV1 = strawberry_django.field()
+    user_list: List[UserTypeV1] = strawberry_django.field()

+ 34 - 0
netbox/users/graphql/types_v1.py

@@ -0,0 +1,34 @@
+from typing import List
+
+import strawberry_django
+
+from netbox.graphql.types_v1 import BaseObjectTypeV1
+from users.models import Group, User
+from .filters_v1 import *
+
+__all__ = (
+    'GroupTypeV1',
+    'UserTypeV1',
+)
+
+
+@strawberry_django.type(
+    Group,
+    fields=['id', 'name'],
+    filters=GroupFilterV1,
+    pagination=True
+)
+class GroupTypeV1(BaseObjectTypeV1):
+    pass
+
+
+@strawberry_django.type(
+    User,
+    fields=[
+        'id', 'username', 'first_name', 'last_name', 'email', 'is_active', 'date_joined', 'groups',
+    ],
+    filters=UserFilterV1,
+    pagination=True
+)
+class UserTypeV1(BaseObjectTypeV1):
+    groups: List[GroupTypeV1]

+ 28 - 0
netbox/virtualization/graphql/filter_mixins_v1.py

@@ -0,0 +1,28 @@
+from dataclasses import dataclass
+from typing import Annotated, TYPE_CHECKING
+
+import strawberry
+import strawberry_django
+from strawberry import ID
+from strawberry_django import FilterLookup
+
+from netbox.graphql.filter_mixins_v1 import NetBoxModelFilterMixinV1
+
+if TYPE_CHECKING:
+    from .filters_v1 import VirtualMachineFilterV1
+
+__all__ = (
+    'VMComponentFilterMixinV1',
+)
+
+
+@dataclass
+class VMComponentFilterMixinV1(NetBoxModelFilterMixinV1):
+    virtual_machine: Annotated[
+        'VirtualMachineFilterV1', strawberry.lazy('virtualization.graphql.filters_v1')
+    ] | None = (
+        strawberry_django.filter_field()
+    )
+    virtual_machine_id: ID | None = strawberry_django.filter_field()
+    name: FilterLookup[str] | None = strawberry_django.filter_field()
+    description: FilterLookup[str] | None = strawberry_django.filter_field()

+ 170 - 0
netbox/virtualization/graphql/filters_v1.py

@@ -0,0 +1,170 @@
+from typing import Annotated, TYPE_CHECKING
+
+import strawberry
+import strawberry_django
+from strawberry.scalars import ID
+from strawberry_django import FilterLookup
+
+from dcim.graphql.filter_mixins_v1 import InterfaceBaseFilterMixinV1, RenderConfigFilterMixinV1, ScopedFilterMixinV1
+from extras.graphql.filter_mixins_v1 import ConfigContextFilterMixinV1
+from netbox.graphql.filter_mixins_v1 import (
+    ImageAttachmentFilterMixinV1,
+    OrganizationalModelFilterMixinV1,
+    PrimaryModelFilterMixinV1,
+)
+from tenancy.graphql.filter_mixins_v1 import ContactFilterMixinV1, TenancyFilterMixinV1
+from virtualization import models
+from virtualization.graphql.filter_mixins_v1 import VMComponentFilterMixinV1
+
+if TYPE_CHECKING:
+    from .enums import *
+    from netbox.graphql.filter_lookups import FloatLookup, IntegerLookup
+    from dcim.graphql.filters_v1 import (
+        DeviceFilterV1, DeviceRoleFilterV1, MACAddressFilterV1, PlatformFilterV1, SiteFilterV1
+    )
+    from ipam.graphql.filters_v1 import (
+        FHRPGroupAssignmentFilterV1,
+        IPAddressFilterV1,
+        ServiceFilterV1,
+        VLANGroupFilterV1,
+        VRFFilterV1,
+    )
+    from vpn.graphql.filters_v1 import L2VPNFilterV1, TunnelTerminationFilterV1
+
+__all__ = (
+    'ClusterFilterV1',
+    'ClusterGroupFilterV1',
+    'ClusterTypeFilterV1',
+    'VirtualMachineFilterV1',
+    'VMInterfaceFilterV1',
+    'VirtualDiskFilterV1',
+)
+
+
+@strawberry_django.filter_type(models.Cluster, lookups=True)
+class ClusterFilterV1(ContactFilterMixinV1, ScopedFilterMixinV1, TenancyFilterMixinV1, PrimaryModelFilterMixinV1):
+    name: FilterLookup[str] | None = strawberry_django.filter_field()
+    type: Annotated['ClusterTypeFilterV1', strawberry.lazy('virtualization.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    type_id: ID | None = strawberry_django.filter_field()
+    group: Annotated['ClusterGroupFilterV1', strawberry.lazy('virtualization.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    group_id: ID | None = strawberry_django.filter_field()
+    status: Annotated['ClusterStatusEnum', strawberry.lazy('virtualization.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    vlan_groups: Annotated['VLANGroupFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@strawberry_django.filter_type(models.ClusterGroup, lookups=True)
+class ClusterGroupFilterV1(ContactFilterMixinV1, OrganizationalModelFilterMixinV1):
+    vlan_groups: Annotated['VLANGroupFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@strawberry_django.filter_type(models.ClusterType, lookups=True)
+class ClusterTypeFilterV1(OrganizationalModelFilterMixinV1):
+    pass
+
+
+@strawberry_django.filter_type(models.VirtualMachine, lookups=True)
+class VirtualMachineFilterV1(
+    ContactFilterMixinV1,
+    ImageAttachmentFilterMixinV1,
+    RenderConfigFilterMixinV1,
+    ConfigContextFilterMixinV1,
+    TenancyFilterMixinV1,
+    PrimaryModelFilterMixinV1,
+):
+    name: FilterLookup[str] | None = strawberry_django.filter_field()
+    site: Annotated['SiteFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    site_id: ID | None = strawberry_django.filter_field()
+    cluster: Annotated['ClusterFilterV1', strawberry.lazy('virtualization.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    cluster_id: ID | None = strawberry_django.filter_field()
+    device: Annotated['DeviceFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    device_id: ID | None = strawberry_django.filter_field()
+    platform: Annotated['PlatformFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    platform_id: ID | None = strawberry_django.filter_field()
+    status: Annotated['VirtualMachineStatusEnum', strawberry.lazy('virtualization.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    role: Annotated['DeviceRoleFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    role_id: ID | None = strawberry_django.filter_field()
+    primary_ip4: Annotated['IPAddressFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    primary_ip4_id: ID | None = strawberry_django.filter_field()
+    primary_ip6: Annotated['IPAddressFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    primary_ip6_id: ID | None = strawberry_django.filter_field()
+    vcpus: Annotated['FloatLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    memory: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    disk: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    serial: FilterLookup[str] | None = strawberry_django.filter_field()
+    interface_count: FilterLookup[int] | None = strawberry_django.filter_field()
+    virtual_disk_count: FilterLookup[int] | None = strawberry_django.filter_field()
+    interfaces: Annotated['VMInterfaceFilterV1', strawberry.lazy('virtualization.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    services: Annotated['ServiceFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    virtual_disks: Annotated['VirtualDiskFilterV1', strawberry.lazy('virtualization.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@strawberry_django.filter_type(models.VMInterface, lookups=True)
+class VMInterfaceFilterV1(VMComponentFilterMixinV1, InterfaceBaseFilterMixinV1):
+    ip_addresses: Annotated['IPAddressFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    vrf: Annotated['VRFFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = strawberry_django.filter_field()
+    vrf_id: ID | None = strawberry_django.filter_field()
+    parent: Annotated['VMInterfaceFilterV1', strawberry.lazy('virtualization.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    parent_id: ID | None = strawberry_django.filter_field()
+    fhrp_group_assignments: Annotated[
+        'FHRPGroupAssignmentFilterV1', strawberry.lazy('ipam.graphql.filters_v1')
+    ] | None = (
+        strawberry_django.filter_field()
+    )
+    tunnel_terminations: Annotated['TunnelTerminationFilterV1', strawberry.lazy('vpn.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    l2vpn_terminations: Annotated['L2VPNFilterV1', strawberry.lazy('vpn.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    mac_addresses: Annotated['MACAddressFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@strawberry_django.filter_type(models.VirtualDisk, lookups=True)
+class VirtualDiskFilterV1(VMComponentFilterMixinV1):
+    size: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )

+ 27 - 0
netbox/virtualization/graphql/schema_v1.py

@@ -0,0 +1,27 @@
+from typing import List
+
+import strawberry
+import strawberry_django
+
+from .types_v1 import *
+
+
+@strawberry.type(name="Query")
+class VirtualizationQueryV1:
+    cluster: ClusterTypeV1 = strawberry_django.field()
+    cluster_list: List[ClusterTypeV1] = strawberry_django.field()
+
+    cluster_group: ClusterGroupTypeV1 = strawberry_django.field()
+    cluster_group_list: List[ClusterGroupTypeV1] = strawberry_django.field()
+
+    cluster_type: ClusterTypeTypeV1 = strawberry_django.field()
+    cluster_type_list: List[ClusterTypeTypeV1] = strawberry_django.field()
+
+    virtual_machine: VirtualMachineTypeV1 = strawberry_django.field()
+    virtual_machine_list: List[VirtualMachineTypeV1] = strawberry_django.field()
+
+    vm_interface: VMInterfaceTypeV1 = strawberry_django.field()
+    vm_interface_list: List[VMInterfaceTypeV1] = strawberry_django.field()
+
+    virtual_disk: VirtualDiskTypeV1 = strawberry_django.field()
+    virtual_disk_list: List[VirtualDiskTypeV1] = strawberry_django.field()

+ 146 - 0
netbox/virtualization/graphql/types_v1.py

@@ -0,0 +1,146 @@
+from typing import Annotated, List, TYPE_CHECKING, Union
+
+import strawberry
+import strawberry_django
+
+from extras.graphql.mixins_v1 import ConfigContextMixinV1, ContactsMixinV1
+from ipam.graphql.mixins_v1 import IPAddressesMixinV1, VLANGroupsMixinV1
+from netbox.graphql.scalars import BigInt
+from netbox.graphql.types_v1 import OrganizationalObjectTypeV1, NetBoxObjectTypeV1
+from virtualization import models
+from .filters_v1 import *
+
+if TYPE_CHECKING:
+    from dcim.graphql.types_v1 import (
+        DeviceRoleTypeV1,
+        DeviceTypeV1,
+        LocationTypeV1,
+        MACAddressTypeV1,
+        PlatformTypeV1,
+        RegionTypeV1,
+        SiteGroupTypeV1,
+        SiteTypeV1,
+    )
+    from extras.graphql.types_v1 import ConfigTemplateTypeV1
+    from ipam.graphql.types_v1 import IPAddressTypeV1, ServiceTypeV1, VLANTranslationPolicyTypeV1, VLANTypeV1, VRFTypeV1
+    from tenancy.graphql.types_v1 import TenantTypeV1
+
+__all__ = (
+    'ClusterTypeV1',
+    'ClusterGroupTypeV1',
+    'ClusterTypeTypeV1',
+    'VirtualDiskTypeV1',
+    'VirtualMachineTypeV1',
+    'VMInterfaceTypeV1',
+)
+
+
+@strawberry.type
+class ComponentTypeV1(NetBoxObjectTypeV1):
+    """
+    Base type for device/VM components
+    """
+    virtual_machine: Annotated["VirtualMachineTypeV1", strawberry.lazy('virtualization.graphql.types_v1')]
+
+
+@strawberry_django.type(
+    models.Cluster,
+    exclude=['scope_type', 'scope_id', '_location', '_region', '_site', '_site_group'],
+    filters=ClusterFilterV1,
+    pagination=True
+)
+class ClusterTypeV1(ContactsMixinV1, VLANGroupsMixinV1, NetBoxObjectTypeV1):
+    type: Annotated["ClusterTypeTypeV1", strawberry.lazy('virtualization.graphql.types_v1')] | None
+    group: Annotated["ClusterGroupTypeV1", strawberry.lazy('virtualization.graphql.types_v1')] | None
+    tenant: Annotated["TenantTypeV1", strawberry.lazy('tenancy.graphql.types_v1')] | None
+    virtual_machines: List[Annotated["VirtualMachineTypeV1", strawberry.lazy('virtualization.graphql.types_v1')]]
+    devices: List[Annotated["DeviceTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+
+    @strawberry_django.field
+    def scope(self) -> Annotated[Union[
+        Annotated["LocationTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+        Annotated["RegionTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+        Annotated["SiteGroupTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+        Annotated["SiteTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+    ], strawberry.union("ClusterScopeTypeV1")] | None:
+        return self.scope
+
+
+@strawberry_django.type(
+    models.ClusterGroup,
+    fields='__all__',
+    filters=ClusterGroupFilterV1,
+    pagination=True
+)
+class ClusterGroupTypeV1(ContactsMixinV1, VLANGroupsMixinV1, OrganizationalObjectTypeV1):
+
+    clusters: List[Annotated["ClusterTypeV1", strawberry.lazy('virtualization.graphql.types_v1')]]
+
+
+@strawberry_django.type(
+    models.ClusterType,
+    fields='__all__',
+    filters=ClusterTypeFilterV1,
+    pagination=True
+)
+class ClusterTypeTypeV1(OrganizationalObjectTypeV1):
+
+    clusters: List[ClusterTypeV1]
+
+
+@strawberry_django.type(
+    models.VirtualMachine,
+    fields='__all__',
+    filters=VirtualMachineFilterV1,
+    pagination=True
+)
+class VirtualMachineTypeV1(ConfigContextMixinV1, ContactsMixinV1, NetBoxObjectTypeV1):
+    interface_count: BigInt
+    virtual_disk_count: BigInt
+    interface_count: BigInt
+    config_template: Annotated["ConfigTemplateTypeV1", strawberry.lazy('extras.graphql.types_v1')] | None
+    site: Annotated["SiteTypeV1", strawberry.lazy('dcim.graphql.types_v1')] | None
+    cluster: Annotated["ClusterTypeV1", strawberry.lazy('virtualization.graphql.types_v1')] | None
+    device: Annotated["DeviceTypeV1", strawberry.lazy('dcim.graphql.types_v1')] | None
+    tenant: Annotated["TenantTypeV1", strawberry.lazy('tenancy.graphql.types_v1')] | None
+    platform: Annotated["PlatformTypeV1", strawberry.lazy('dcim.graphql.types_v1')] | None
+    role: Annotated["DeviceRoleTypeV1", strawberry.lazy('dcim.graphql.types_v1')] | None
+    primary_ip4: Annotated["IPAddressTypeV1", strawberry.lazy('ipam.graphql.types_v1')] | None
+    primary_ip6: Annotated["IPAddressTypeV1", strawberry.lazy('ipam.graphql.types_v1')] | None
+
+    interfaces: List[Annotated["VMInterfaceTypeV1", strawberry.lazy('virtualization.graphql.types_v1')]]
+    services: List[Annotated["ServiceTypeV1", strawberry.lazy('ipam.graphql.types_v1')]]
+    virtualdisks: List[Annotated["VirtualDiskTypeV1", strawberry.lazy('virtualization.graphql.types_v1')]]
+
+
+@strawberry_django.type(
+    models.VMInterface,
+    fields='__all__',
+    filters=VMInterfaceFilterV1,
+    pagination=True
+)
+class VMInterfaceTypeV1(IPAddressesMixinV1, ComponentTypeV1):
+    _name: str
+    mac_address: str | None
+    parent: Annotated["VMInterfaceTypeV1", strawberry.lazy('virtualization.graphql.types_v1')] | None
+    bridge: Annotated["VMInterfaceTypeV1", strawberry.lazy('virtualization.graphql.types_v1')] | None
+    untagged_vlan: Annotated["VLANTypeV1", strawberry.lazy('ipam.graphql.types_v1')] | None
+    vrf: Annotated["VRFTypeV1", strawberry.lazy('ipam.graphql.types_v1')] | None
+    primary_mac_address: Annotated["MACAddressTypeV1", strawberry.lazy('dcim.graphql.types_v1')] | None
+    qinq_svlan: Annotated["VLANTypeV1", strawberry.lazy('ipam.graphql.types_v1')] | None
+    vlan_translation_policy: Annotated["VLANTranslationPolicyTypeV1", strawberry.lazy('ipam.graphql.types_v1')] | None
+
+    tagged_vlans: List[Annotated["VLANTypeV1", strawberry.lazy('ipam.graphql.types_v1')]]
+    bridge_interfaces: List[Annotated["VMInterfaceTypeV1", strawberry.lazy('virtualization.graphql.types_v1')]]
+    child_interfaces: List[Annotated["VMInterfaceTypeV1", strawberry.lazy('virtualization.graphql.types_v1')]]
+    mac_addresses: List[Annotated["MACAddressTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+
+
+@strawberry_django.type(
+    models.VirtualDisk,
+    fields='__all__',
+    filters=VirtualDiskFilterV1,
+    pagination=True
+)
+class VirtualDiskTypeV1(ComponentTypeV1):
+    pass

+ 192 - 0
netbox/vpn/graphql/filters_v1.py

@@ -0,0 +1,192 @@
+from typing import Annotated, TYPE_CHECKING
+
+import strawberry
+import strawberry_django
+from strawberry.scalars import ID
+from strawberry_django import FilterLookup
+
+from core.graphql.filter_mixins_v1 import BaseObjectTypeFilterMixinV1, ChangeLogFilterMixinV1
+from extras.graphql.filter_mixins_v1 import CustomFieldsFilterMixinV1, TagsFilterMixinV1
+from netbox.graphql.filter_mixins_v1 import (
+    NetBoxModelFilterMixinV1, OrganizationalModelFilterMixinV1, PrimaryModelFilterMixinV1
+)
+from tenancy.graphql.filter_mixins_v1 import ContactFilterMixinV1, TenancyFilterMixinV1
+from vpn import models
+
+if TYPE_CHECKING:
+    from core.graphql.filters_v1 import ContentTypeFilterV1
+    from ipam.graphql.filters_v1 import IPAddressFilterV1, RouteTargetFilterV1
+    from netbox.graphql.filter_lookups import IntegerLookup
+    from .enums import *
+
+__all__ = (
+    'TunnelGroupFilterV1',
+    'TunnelTerminationFilterV1',
+    'TunnelFilterV1',
+    'IKEProposalFilterV1',
+    'IKEPolicyFilterV1',
+    'IPSecProposalFilterV1',
+    'IPSecPolicyFilterV1',
+    'IPSecProfileFilterV1',
+    'L2VPNFilterV1',
+    'L2VPNTerminationFilterV1',
+)
+
+
+@strawberry_django.filter_type(models.TunnelGroup, lookups=True)
+class TunnelGroupFilterV1(OrganizationalModelFilterMixinV1):
+    pass
+
+
+@strawberry_django.filter_type(models.TunnelTermination, lookups=True)
+class TunnelTerminationFilterV1(
+    BaseObjectTypeFilterMixinV1, CustomFieldsFilterMixinV1, TagsFilterMixinV1, ChangeLogFilterMixinV1
+):
+    tunnel: Annotated['TunnelFilterV1', strawberry.lazy('vpn.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    tunnel_id: ID | None = strawberry_django.filter_field()
+    role: Annotated['TunnelTerminationRoleEnum', strawberry.lazy('vpn.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    termination_type: Annotated['TunnelTerminationTypeEnum', strawberry.lazy('vpn.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    termination_type_id: ID | None = strawberry_django.filter_field()
+    termination_id: ID | None = strawberry_django.filter_field()
+    outside_ip: Annotated['IPAddressFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    outside_ip_id: ID | None = strawberry_django.filter_field()
+
+
+@strawberry_django.filter_type(models.Tunnel, lookups=True)
+class TunnelFilterV1(TenancyFilterMixinV1, PrimaryModelFilterMixinV1):
+    name: FilterLookup[str] | None = strawberry_django.filter_field()
+    status: Annotated['TunnelStatusEnum', strawberry.lazy('vpn.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    group: Annotated['TunnelGroupFilterV1', strawberry.lazy('vpn.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    group_id: ID | None = strawberry_django.filter_field()
+    encapsulation: Annotated['TunnelEncapsulationEnum', strawberry.lazy('vpn.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    ipsec_profile: Annotated['IPSecProfileFilterV1', strawberry.lazy('vpn.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    tunnel_id: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    terminations: Annotated['TunnelTerminationFilterV1', strawberry.lazy('vpn.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@strawberry_django.filter_type(models.IKEProposal, lookups=True)
+class IKEProposalFilterV1(PrimaryModelFilterMixinV1):
+    name: FilterLookup[str] | None = strawberry_django.filter_field()
+    authentication_method: Annotated['AuthenticationMethodEnum', strawberry.lazy('vpn.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    encryption_algorithm: Annotated['EncryptionAlgorithmEnum', strawberry.lazy('vpn.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    authentication_algorithm: Annotated['AuthenticationAlgorithmEnum', strawberry.lazy('vpn.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    group: Annotated['DHGroupEnum', strawberry.lazy('vpn.graphql.enums')] | None = strawberry_django.filter_field()
+    sa_lifetime: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    ike_policies: Annotated['IKEPolicyFilterV1', strawberry.lazy('vpn.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@strawberry_django.filter_type(models.IKEPolicy, lookups=True)
+class IKEPolicyFilterV1(PrimaryModelFilterMixinV1):
+    name: FilterLookup[str] | None = strawberry_django.filter_field()
+    version: Annotated['IKEVersionEnum', strawberry.lazy('vpn.graphql.enums')] | None = strawberry_django.filter_field()
+    mode: Annotated['IKEModeEnum', strawberry.lazy('vpn.graphql.enums')] | None = strawberry_django.filter_field()
+    proposals: Annotated['IKEProposalFilterV1', strawberry.lazy('vpn.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    preshared_key: FilterLookup[str] | None = strawberry_django.filter_field()
+
+
+@strawberry_django.filter_type(models.IPSecProposal, lookups=True)
+class IPSecProposalFilterV1(PrimaryModelFilterMixinV1):
+    name: FilterLookup[str] | None = strawberry_django.filter_field()
+    encryption_algorithm: Annotated['EncryptionAlgorithmEnum', strawberry.lazy('vpn.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    authentication_algorithm: Annotated['AuthenticationAlgorithmEnum', strawberry.lazy('vpn.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    sa_lifetime_seconds: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    sa_lifetime_data: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    ipsec_policies: Annotated['IPSecPolicyFilterV1', strawberry.lazy('vpn.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@strawberry_django.filter_type(models.IPSecPolicy, lookups=True)
+class IPSecPolicyFilterV1(PrimaryModelFilterMixinV1):
+    name: FilterLookup[str] | None = strawberry_django.filter_field()
+    proposals: Annotated['IPSecProposalFilterV1', strawberry.lazy('vpn.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    pfs_group: Annotated['DHGroupEnum', strawberry.lazy('vpn.graphql.enums')] | None = strawberry_django.filter_field()
+
+
+@strawberry_django.filter_type(models.IPSecProfile, lookups=True)
+class IPSecProfileFilterV1(PrimaryModelFilterMixinV1):
+    name: FilterLookup[str] | None = strawberry_django.filter_field()
+    mode: Annotated['IPSecModeEnum', strawberry.lazy('vpn.graphql.enums')] | None = strawberry_django.filter_field()
+    ike_policy: Annotated['IKEPolicyFilterV1', strawberry.lazy('vpn.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    ike_policy_id: ID | None = strawberry_django.filter_field()
+    ipsec_policy: Annotated['IPSecPolicyFilterV1', strawberry.lazy('vpn.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    ipsec_policy_id: ID | None = strawberry_django.filter_field()
+
+
+@strawberry_django.filter_type(models.L2VPN, lookups=True)
+class L2VPNFilterV1(ContactFilterMixinV1, TenancyFilterMixinV1, PrimaryModelFilterMixinV1):
+    name: FilterLookup[str] | None = strawberry_django.filter_field()
+    slug: FilterLookup[str] | None = strawberry_django.filter_field()
+    type: Annotated['L2VPNTypeEnum', strawberry.lazy('vpn.graphql.enums')] | None = strawberry_django.filter_field()
+    identifier: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )
+    import_targets: Annotated['RouteTargetFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    export_targets: Annotated['RouteTargetFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    terminations: Annotated['L2VPNTerminationFilterV1', strawberry.lazy('vpn.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+
+
+@strawberry_django.filter_type(models.L2VPNTermination, lookups=True)
+class L2VPNTerminationFilterV1(NetBoxModelFilterMixinV1):
+    l2vpn: Annotated['L2VPNFilterV1', strawberry.lazy('vpn.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    l2vpn_id: ID | None = strawberry_django.filter_field()
+    assigned_object_type: Annotated['ContentTypeFilterV1', strawberry.lazy('core.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    assigned_object_id: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
+        strawberry_django.filter_field()
+    )

+ 39 - 0
netbox/vpn/graphql/schema_v1.py

@@ -0,0 +1,39 @@
+from typing import List
+
+import strawberry
+import strawberry_django
+
+from .types_v1 import *
+
+
+@strawberry.type(name="Query")
+class VPNQueryV1:
+    ike_policy: IKEPolicyTypeV1 = strawberry_django.field()
+    ike_policy_list: List[IKEPolicyTypeV1] = strawberry_django.field()
+
+    ike_proposal: IKEProposalTypeV1 = strawberry_django.field()
+    ike_proposal_list: List[IKEProposalTypeV1] = strawberry_django.field()
+
+    ipsec_policy: IPSecPolicyTypeV1 = strawberry_django.field()
+    ipsec_policy_list: List[IPSecPolicyTypeV1] = strawberry_django.field()
+
+    ipsec_profile: IPSecProfileTypeV1 = strawberry_django.field()
+    ipsec_profile_list: List[IPSecProfileTypeV1] = strawberry_django.field()
+
+    ipsec_proposal: IPSecProposalTypeV1 = strawberry_django.field()
+    ipsec_proposal_list: List[IPSecProposalTypeV1] = strawberry_django.field()
+
+    l2vpn: L2VPNTypeV1 = strawberry_django.field()
+    l2vpn_list: List[L2VPNTypeV1] = strawberry_django.field()
+
+    l2vpn_termination: L2VPNTerminationTypeV1 = strawberry_django.field()
+    l2vpn_termination_list: List[L2VPNTerminationTypeV1] = strawberry_django.field()
+
+    tunnel: TunnelTypeV1 = strawberry_django.field()
+    tunnel_list: List[TunnelTypeV1] = strawberry_django.field()
+
+    tunnel_group: TunnelGroupTypeV1 = strawberry_django.field()
+    tunnel_group_list: List[TunnelGroupTypeV1] = strawberry_django.field()
+
+    tunnel_termination: TunnelTerminationTypeV1 = strawberry_django.field()
+    tunnel_termination_list: List[TunnelTerminationTypeV1] = strawberry_django.field()

+ 157 - 0
netbox/vpn/graphql/types_v1.py

@@ -0,0 +1,157 @@
+from typing import Annotated, List, TYPE_CHECKING, Union
+
+import strawberry
+import strawberry_django
+
+from extras.graphql.mixins_v1 import ContactsMixinV1, CustomFieldsMixinV1, TagsMixinV1
+from netbox.graphql.types_v1 import ObjectTypeV1, OrganizationalObjectTypeV1, NetBoxObjectTypeV1
+from vpn import models
+from .filters_v1 import *
+
+if TYPE_CHECKING:
+    from dcim.graphql.types_v1 import InterfaceTypeV1
+    from ipam.graphql.types_v1 import IPAddressTypeV1, RouteTargetTypeV1, VLANTypeV1
+    from netbox.graphql.types_v1 import ContentTypeTypeV1
+    from tenancy.graphql.types_v1 import TenantTypeV1
+    from virtualization.graphql.types_v1 import VMInterfaceTypeV1
+
+__all__ = (
+    'IKEPolicyTypeV1',
+    'IKEProposalTypeV1',
+    'IPSecPolicyTypeV1',
+    'IPSecProfileTypeV1',
+    'IPSecProposalTypeV1',
+    'L2VPNTypeV1',
+    'L2VPNTerminationTypeV1',
+    'TunnelGroupTypeV1',
+    'TunnelTerminationTypeV1',
+    'TunnelTypeV1',
+)
+
+
+@strawberry_django.type(
+    models.TunnelGroup,
+    fields='__all__',
+    filters=TunnelGroupFilterV1,
+    pagination=True
+)
+class TunnelGroupTypeV1(ContactsMixinV1, OrganizationalObjectTypeV1):
+
+    tunnels: List[Annotated["TunnelTypeV1", strawberry.lazy('vpn.graphql.types_v1')]]
+
+
+@strawberry_django.type(
+    models.TunnelTermination,
+    fields='__all__',
+    filters=TunnelTerminationFilterV1,
+    pagination=True
+)
+class TunnelTerminationTypeV1(CustomFieldsMixinV1, TagsMixinV1, ObjectTypeV1):
+    tunnel: Annotated["TunnelTypeV1", strawberry.lazy('vpn.graphql.types_v1')]
+    termination_type: Annotated["ContentTypeTypeV1", strawberry.lazy('netbox.graphql.types_v1')] | None
+    outside_ip: Annotated["IPAddressTypeV1", strawberry.lazy('ipam.graphql.types_v1')] | None
+
+
+@strawberry_django.type(
+    models.Tunnel,
+    fields='__all__',
+    filters=TunnelFilterV1,
+    pagination=True
+)
+class TunnelTypeV1(ContactsMixinV1, NetBoxObjectTypeV1):
+    group: Annotated["TunnelGroupTypeV1", strawberry.lazy('vpn.graphql.types_v1')] | None
+    ipsec_profile: Annotated["IPSecProfileTypeV1", strawberry.lazy('vpn.graphql.types_v1')] | None
+    tenant: Annotated["TenantTypeV1", strawberry.lazy('tenancy.graphql.types_v1')] | None
+
+    terminations: List[Annotated["TunnelTerminationTypeV1", strawberry.lazy('vpn.graphql.types_v1')]]
+
+
+@strawberry_django.type(
+    models.IKEProposal,
+    fields='__all__',
+    filters=IKEProposalFilterV1,
+    pagination=True
+)
+class IKEProposalTypeV1(OrganizationalObjectTypeV1):
+
+    ike_policies: List[Annotated["IKEPolicyTypeV1", strawberry.lazy('vpn.graphql.types_v1')]]
+
+
+@strawberry_django.type(
+    models.IKEPolicy,
+    fields='__all__',
+    filters=IKEPolicyFilterV1,
+    pagination=True
+)
+class IKEPolicyTypeV1(OrganizationalObjectTypeV1):
+
+    proposals: List[Annotated["IKEProposalTypeV1", strawberry.lazy('vpn.graphql.types_v1')]]
+    ipsec_profiles: List[Annotated["IPSecProfileTypeV1", strawberry.lazy('vpn.graphql.types_v1')]]
+
+
+@strawberry_django.type(
+    models.IPSecProposal,
+    fields='__all__',
+    filters=IPSecProposalFilterV1,
+    pagination=True
+)
+class IPSecProposalTypeV1(OrganizationalObjectTypeV1):
+
+    ipsec_policies: List[Annotated["IPSecPolicyTypeV1", strawberry.lazy('vpn.graphql.types_v1')]]
+
+
+@strawberry_django.type(
+    models.IPSecPolicy,
+    fields='__all__',
+    filters=IPSecPolicyFilterV1,
+    pagination=True
+)
+class IPSecPolicyTypeV1(OrganizationalObjectTypeV1):
+
+    proposals: List[Annotated["IPSecProposalTypeV1", strawberry.lazy('vpn.graphql.types_v1')]]
+    ipsec_profiles: List[Annotated["IPSecProfileTypeV1", strawberry.lazy('vpn.graphql.types_v1')]]
+
+
+@strawberry_django.type(
+    models.IPSecProfile,
+    fields='__all__',
+    filters=IPSecProfileFilterV1,
+    pagination=True
+)
+class IPSecProfileTypeV1(OrganizationalObjectTypeV1):
+    ike_policy: Annotated["IKEPolicyTypeV1", strawberry.lazy('vpn.graphql.types_v1')]
+    ipsec_policy: Annotated["IPSecPolicyTypeV1", strawberry.lazy('vpn.graphql.types_v1')]
+
+    tunnels: List[Annotated["TunnelTypeV1", strawberry.lazy('vpn.graphql.types_v1')]]
+
+
+@strawberry_django.type(
+    models.L2VPN,
+    fields='__all__',
+    filters=L2VPNFilterV1,
+    pagination=True
+)
+class L2VPNTypeV1(ContactsMixinV1, NetBoxObjectTypeV1):
+    tenant: Annotated["TenantTypeV1", strawberry.lazy('tenancy.graphql.types_v1')] | None
+
+    export_targets: List[Annotated["RouteTargetTypeV1", strawberry.lazy('ipam.graphql.types_v1')]]
+    terminations: List[Annotated["L2VPNTerminationTypeV1", strawberry.lazy('vpn.graphql.types_v1')]]
+    import_targets: List[Annotated["RouteTargetTypeV1", strawberry.lazy('ipam.graphql.types_v1')]]
+
+
+@strawberry_django.type(
+    models.L2VPNTermination,
+    exclude=['assigned_object_type', 'assigned_object_id'],
+    filters=L2VPNTerminationFilterV1,
+    pagination=True
+)
+class L2VPNTerminationTypeV1(NetBoxObjectTypeV1):
+    l2vpn: Annotated["L2VPNTypeV1", strawberry.lazy('vpn.graphql.types_v1')]
+
+    @strawberry_django.field
+    def assigned_object(self) -> Annotated[Union[
+        Annotated["InterfaceTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+        Annotated["VLANTypeV1", strawberry.lazy('ipam.graphql.types_v1')],
+        Annotated["VMInterfaceTypeV1", strawberry.lazy('virtualization.graphql.types_v1')],
+    ], strawberry.union("L2VPNAssignmentTypeV1")]:
+        return self.assigned_object

+ 26 - 0
netbox/wireless/graphql/filter_mixins_v1.py

@@ -0,0 +1,26 @@
+from dataclasses import dataclass
+from typing import Annotated, TYPE_CHECKING
+
+import strawberry
+import strawberry_django
+from strawberry_django import FilterLookup
+
+from core.graphql.filter_mixins_v1 import BaseFilterMixinV1
+
+if TYPE_CHECKING:
+    from .enums import *
+
+__all__ = (
+    'WirelessAuthenticationBaseFilterMixinV1',
+)
+
+
+@dataclass
+class WirelessAuthenticationBaseFilterMixinV1(BaseFilterMixinV1):
+    auth_type: Annotated['WirelessAuthTypeEnum', strawberry.lazy('wireless.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    auth_cipher: Annotated['WirelessAuthCipherEnum', strawberry.lazy('wireless.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    auth_psk: FilterLookup[str] | None = strawberry_django.filter_field()

+ 72 - 0
netbox/wireless/graphql/filters_v1.py

@@ -0,0 +1,72 @@
+from typing import Annotated, TYPE_CHECKING
+
+import strawberry
+import strawberry_django
+from strawberry.scalars import ID
+from strawberry_django import FilterLookup
+
+from dcim.graphql.filter_mixins_v1 import ScopedFilterMixinV1
+from netbox.graphql.filter_mixins_v1 import (
+    DistanceFilterMixinV1, PrimaryModelFilterMixinV1, NestedGroupModelFilterMixinV1
+)
+from tenancy.graphql.filter_mixins_v1 import TenancyFilterMixinV1
+from wireless import models
+from .filter_mixins_v1 import WirelessAuthenticationBaseFilterMixinV1
+
+if TYPE_CHECKING:
+    from dcim.graphql.filters_v1 import InterfaceFilterV1
+    from ipam.graphql.filters_v1 import VLANFilterV1
+    from .enums import *
+
+__all__ = (
+    'WirelessLANGroupFilterV1',
+    'WirelessLANFilterV1',
+    'WirelessLinkFilterV1',
+)
+
+
+@strawberry_django.filter_type(models.WirelessLANGroup, lookups=True)
+class WirelessLANGroupFilterV1(NestedGroupModelFilterMixinV1):
+    pass
+
+
+@strawberry_django.filter_type(models.WirelessLAN, lookups=True)
+class WirelessLANFilterV1(
+    WirelessAuthenticationBaseFilterMixinV1,
+    ScopedFilterMixinV1,
+    TenancyFilterMixinV1,
+    PrimaryModelFilterMixinV1
+):
+    ssid: FilterLookup[str] | None = strawberry_django.filter_field()
+    status: Annotated['WirelessLANStatusEnum', strawberry.lazy('wireless.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )
+    group: Annotated['WirelessLANGroupFilterV1', strawberry.lazy('wireless.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    group_id: ID | None = strawberry_django.filter_field()
+    vlan: Annotated['VLANFilterV1', strawberry.lazy('ipam.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    vlan_id: ID | None = strawberry_django.filter_field()
+
+
+@strawberry_django.filter_type(models.WirelessLink, lookups=True)
+class WirelessLinkFilterV1(
+    WirelessAuthenticationBaseFilterMixinV1,
+    DistanceFilterMixinV1,
+    TenancyFilterMixinV1,
+    PrimaryModelFilterMixinV1
+):
+    interface_a: Annotated['InterfaceFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    interface_a_id: ID | None = strawberry_django.filter_field()
+    interface_b: Annotated['InterfaceFilterV1', strawberry.lazy('dcim.graphql.filters_v1')] | None = (
+        strawberry_django.filter_field()
+    )
+    interface_b_id: ID | None = strawberry_django.filter_field()
+    ssid: FilterLookup[str] | None = strawberry_django.filter_field()
+    status: Annotated['WirelessLANStatusEnum', strawberry.lazy('wireless.graphql.enums')] | None = (
+        strawberry_django.filter_field()
+    )

+ 18 - 0
netbox/wireless/graphql/schema_v1.py

@@ -0,0 +1,18 @@
+from typing import List
+
+import strawberry
+import strawberry_django
+
+from .types_v1 import *
+
+
+@strawberry.type(name="Query")
+class WirelessQueryV1:
+    wireless_lan: WirelessLANTypeV1 = strawberry_django.field()
+    wireless_lan_list: List[WirelessLANTypeV1] = strawberry_django.field()
+
+    wireless_lan_group: WirelessLANGroupTypeV1 = strawberry_django.field()
+    wireless_lan_group_list: List[WirelessLANGroupTypeV1] = strawberry_django.field()
+
+    wireless_link: WirelessLinkTypeV1 = strawberry_django.field()
+    wireless_link_list: List[WirelessLinkTypeV1] = strawberry_django.field()

+ 71 - 0
netbox/wireless/graphql/types_v1.py

@@ -0,0 +1,71 @@
+from typing import Annotated, List, TYPE_CHECKING, Union
+
+import strawberry
+import strawberry_django
+
+from netbox.graphql.types_v1 import OrganizationalObjectTypeV1, NetBoxObjectTypeV1
+from wireless import models
+from .filters_v1 import *
+
+if TYPE_CHECKING:
+    from dcim.graphql.types_v1 import (
+        DeviceTypeV1, InterfaceTypeV1, LocationTypeV1, RegionTypeV1, SiteGroupTypeV1, SiteTypeV1
+    )
+    from ipam.graphql.types_v1 import VLANTypeV1
+    from tenancy.graphql.types_v1 import TenantTypeV1
+
+__all__ = (
+    'WirelessLANTypeV1',
+    'WirelessLANGroupTypeV1',
+    'WirelessLinkTypeV1',
+)
+
+
+@strawberry_django.type(
+    models.WirelessLANGroup,
+    fields='__all__',
+    filters=WirelessLANGroupFilterV1,
+    pagination=True
+)
+class WirelessLANGroupTypeV1(OrganizationalObjectTypeV1):
+    parent: Annotated["WirelessLANGroupTypeV1", strawberry.lazy('wireless.graphql.types_v1')] | None
+
+    wireless_lans: List[Annotated["WirelessLANTypeV1", strawberry.lazy('wireless.graphql.types_v1')]]
+    children: List[Annotated["WirelessLANGroupTypeV1", strawberry.lazy('wireless.graphql.types_v1')]]
+
+
+@strawberry_django.type(
+    models.WirelessLAN,
+    exclude=['scope_type', 'scope_id', '_location', '_region', '_site', '_site_group'],
+    filters=WirelessLANFilterV1,
+    pagination=True
+)
+class WirelessLANTypeV1(NetBoxObjectTypeV1):
+    group: Annotated["WirelessLANGroupTypeV1", strawberry.lazy('wireless.graphql.types_v1')] | None
+    vlan: Annotated["VLANTypeV1", strawberry.lazy('ipam.graphql.types_v1')] | None
+    tenant: Annotated["TenantTypeV1", strawberry.lazy('tenancy.graphql.types_v1')] | None
+
+    interfaces: List[Annotated["InterfaceTypeV1", strawberry.lazy('dcim.graphql.types_v1')]]
+
+    @strawberry_django.field
+    def scope(self) -> Annotated[Union[
+        Annotated["LocationTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+        Annotated["RegionTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+        Annotated["SiteGroupTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+        Annotated["SiteTypeV1", strawberry.lazy('dcim.graphql.types_v1')],
+    ], strawberry.union("WirelessLANScopeTypeV1")] | None:
+        return self.scope
+
+
+@strawberry_django.type(
+    models.WirelessLink,
+    fields='__all__',
+    filters=WirelessLinkFilterV1,
+    pagination=True
+)
+class WirelessLinkTypeV1(NetBoxObjectTypeV1):
+    interface_a: Annotated["InterfaceTypeV1", strawberry.lazy('dcim.graphql.types_v1')]
+    interface_b: Annotated["InterfaceTypeV1", strawberry.lazy('dcim.graphql.types_v1')]
+    tenant: Annotated["TenantTypeV1", strawberry.lazy('tenancy.graphql.types_v1')] | None
+    _interface_a_device: Annotated["DeviceTypeV1", strawberry.lazy('dcim.graphql.types_v1')] | None
+    _interface_b_device: Annotated["DeviceTypeV1", strawberry.lazy('dcim.graphql.types_v1')] | None