From 7d0f68c97f8f1095bff82f5ff7726b5dacc94902 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Tue, 21 Oct 2025 12:22:40 -0400 Subject: [PATCH] Update GraphQL types to support owner assignment --- netbox/circuits/graphql/types.py | 17 ++++------ netbox/core/graphql/types.py | 5 ++- netbox/dcim/graphql/types.py | 46 ++++++++++++++------------ netbox/extras/graphql/types.py | 25 +++++++------- netbox/ipam/graphql/types.py | 29 ++++++++-------- netbox/netbox/graphql/types.py | 42 +++++++++++++++++++---- netbox/tenancy/graphql/types.py | 10 +++--- netbox/users/graphql/mixins.py | 15 +++++++++ netbox/virtualization/graphql/types.py | 9 ++--- netbox/vpn/graphql/types.py | 20 +++++------ netbox/wireless/graphql/types.py | 8 ++--- 11 files changed, 133 insertions(+), 93 deletions(-) create mode 100644 netbox/users/graphql/mixins.py diff --git a/netbox/circuits/graphql/types.py b/netbox/circuits/graphql/types.py index 89d2a33b6..8592e929d 100644 --- a/netbox/circuits/graphql/types.py +++ b/netbox/circuits/graphql/types.py @@ -6,7 +6,7 @@ import strawberry_django from circuits import models from dcim.graphql.mixins import CabledObjectMixin from extras.graphql.mixins import ContactsMixin, CustomFieldsMixin, TagsMixin -from netbox.graphql.types import BaseObjectType, NetBoxObjectType, ObjectType, OrganizationalObjectType +from netbox.graphql.types import BaseObjectType, ObjectType, OrganizationalObjectType, PrimaryObjectType from tenancy.graphql.types import TenantType from .filters import * @@ -35,8 +35,7 @@ __all__ = ( filters=ProviderFilter, pagination=True ) -class ProviderType(NetBoxObjectType, ContactsMixin): - +class ProviderType(ContactsMixin, PrimaryObjectType): networks: List[Annotated["ProviderNetworkType", strawberry.lazy('circuits.graphql.types')]] circuits: List[Annotated["CircuitType", strawberry.lazy('circuits.graphql.types')]] asns: List[Annotated["ASNType", strawberry.lazy('ipam.graphql.types')]] @@ -49,9 +48,8 @@ class ProviderType(NetBoxObjectType, ContactsMixin): filters=ProviderAccountFilter, pagination=True ) -class ProviderAccountType(ContactsMixin, NetBoxObjectType): +class ProviderAccountType(ContactsMixin, PrimaryObjectType): provider: Annotated["ProviderType", strawberry.lazy('circuits.graphql.types')] - circuits: List[Annotated["CircuitType", strawberry.lazy('circuits.graphql.types')]] @@ -61,9 +59,8 @@ class ProviderAccountType(ContactsMixin, NetBoxObjectType): filters=ProviderNetworkFilter, pagination=True ) -class ProviderNetworkType(NetBoxObjectType): +class ProviderNetworkType(PrimaryObjectType): provider: Annotated["ProviderType", strawberry.lazy('circuits.graphql.types')] - circuit_terminations: List[Annotated["CircuitTerminationType", strawberry.lazy('circuits.graphql.types')]] @@ -105,14 +102,13 @@ class CircuitTypeType(OrganizationalObjectType): filters=CircuitFilter, pagination=True ) -class CircuitType(NetBoxObjectType, ContactsMixin): +class CircuitType(PrimaryObjectType, ContactsMixin): provider: ProviderType provider_account: ProviderAccountType | None termination_a: CircuitTerminationType | None termination_z: CircuitTerminationType | None type: CircuitTypeType tenant: TenantType | None - terminations: List[CircuitTerminationType] @@ -178,12 +174,11 @@ class VirtualCircuitTerminationType(CustomFieldsMixin, TagsMixin, ObjectType): filters=VirtualCircuitFilter, pagination=True ) -class VirtualCircuitType(NetBoxObjectType): +class VirtualCircuitType(PrimaryObjectType): provider_network: ProviderNetworkType = strawberry_django.field(select_related=["provider_network"]) provider_account: ProviderAccountType | None type: Annotated["VirtualCircuitTypeType", strawberry.lazy('circuits.graphql.types')] = strawberry_django.field( select_related=["type"] ) tenant: TenantType | None - terminations: List[VirtualCircuitTerminationType] diff --git a/netbox/core/graphql/types.py b/netbox/core/graphql/types.py index ffaa24411..12407b5c7 100644 --- a/netbox/core/graphql/types.py +++ b/netbox/core/graphql/types.py @@ -5,7 +5,7 @@ import strawberry_django from django.contrib.contenttypes.models import ContentType as DjangoContentType from core import models -from netbox.graphql.types import BaseObjectType, NetBoxObjectType +from netbox.graphql.types import BaseObjectType, PrimaryObjectType from .filters import * __all__ = ( @@ -32,8 +32,7 @@ class DataFileType(BaseObjectType): filters=DataSourceFilter, pagination=True ) -class DataSourceType(NetBoxObjectType): - +class DataSourceType(PrimaryObjectType): datafiles: List[Annotated["DataFileType", strawberry.lazy('core.graphql.types')]] diff --git a/netbox/dcim/graphql/types.py b/netbox/dcim/graphql/types.py index 0cd5e8fd1..568772247 100644 --- a/netbox/dcim/graphql/types.py +++ b/netbox/dcim/graphql/types.py @@ -14,7 +14,10 @@ from extras.graphql.mixins import ( ) from ipam.graphql.mixins import IPAddressesMixin, VLANGroupsMixin from netbox.graphql.scalars import BigInt -from netbox.graphql.types import BaseObjectType, NetBoxObjectType, OrganizationalObjectType +from netbox.graphql.types import ( + BaseObjectType, NestedGroupObjectType, NetBoxObjectType, OrganizationalObjectType, PrimaryObjectType, +) +from users.graphql.mixins import OwnerMixin from .filters import * from .mixins import CabledObjectMixin, PathEndpointMixin @@ -94,6 +97,7 @@ __all__ = ( class ComponentType( ChangelogMixin, CustomFieldsMixin, + OwnerMixin, TagsMixin, BaseObjectType ): @@ -159,7 +163,7 @@ class CableTerminationType(NetBoxObjectType): filters=CableFilter, pagination=True ) -class CableType(NetBoxObjectType): +class CableType(PrimaryObjectType): color: str tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None @@ -236,7 +240,7 @@ class ConsoleServerPortTemplateType(ModularComponentTemplateType): filters=DeviceFilter, pagination=True ) -class DeviceType(ConfigContextMixin, ImageAttachmentsMixin, ContactsMixin, NetBoxObjectType): +class DeviceType(ConfigContextMixin, ImageAttachmentsMixin, ContactsMixin, PrimaryObjectType): console_port_count: BigInt console_server_port_count: BigInt power_port_count: BigInt @@ -339,7 +343,7 @@ class InventoryItemTemplateType(ComponentTemplateType): filters=DeviceRoleFilter, pagination=True ) -class DeviceRoleType(OrganizationalObjectType): +class DeviceRoleType(NestedGroupObjectType): parent: Annotated['DeviceRoleType', strawberry.lazy('dcim.graphql.types')] | None children: List[Annotated['DeviceRoleType', strawberry.lazy('dcim.graphql.types')]] color: str @@ -355,7 +359,7 @@ class DeviceRoleType(OrganizationalObjectType): filters=DeviceTypeFilter, pagination=True ) -class DeviceTypeType(NetBoxObjectType): +class DeviceTypeType(PrimaryObjectType): console_port_template_count: BigInt console_server_port_template_count: BigInt power_port_template_count: BigInt @@ -412,7 +416,7 @@ class FrontPortTemplateType(ModularComponentTemplateType): filters=MACAddressFilter, pagination=True ) -class MACAddressType(NetBoxObjectType): +class MACAddressType(PrimaryObjectType): mac_address: str @strawberry_django.field @@ -512,7 +516,7 @@ class InventoryItemRoleType(OrganizationalObjectType): filters=LocationFilter, pagination=True ) -class LocationType(VLANGroupsMixin, ImageAttachmentsMixin, ContactsMixin, OrganizationalObjectType): +class LocationType(VLANGroupsMixin, ImageAttachmentsMixin, ContactsMixin, NestedGroupObjectType): site: Annotated["SiteType", strawberry.lazy('dcim.graphql.types')] tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None parent: Annotated["LocationType", strawberry.lazy('dcim.graphql.types')] | None @@ -555,7 +559,7 @@ class ManufacturerType(OrganizationalObjectType, ContactsMixin): filters=ModuleFilter, pagination=True ) -class ModuleType(NetBoxObjectType): +class ModuleType(PrimaryObjectType): device: Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')] module_bay: Annotated["ModuleBayType", strawberry.lazy('dcim.graphql.types')] module_type: Annotated["ModuleTypeType", strawberry.lazy('dcim.graphql.types')] @@ -602,7 +606,7 @@ class ModuleBayTemplateType(ModularComponentTemplateType): filters=ModuleTypeProfileFilter, pagination=True ) -class ModuleTypeProfileType(NetBoxObjectType): +class ModuleTypeProfileType(PrimaryObjectType): module_types: List[Annotated["ModuleType", strawberry.lazy('dcim.graphql.types')]] @@ -612,7 +616,7 @@ class ModuleTypeProfileType(NetBoxObjectType): filters=ModuleTypeFilter, pagination=True ) -class ModuleTypeType(NetBoxObjectType): +class ModuleTypeType(PrimaryObjectType): profile: Annotated["ModuleTypeProfileType", strawberry.lazy('dcim.graphql.types')] | None manufacturer: Annotated["ManufacturerType", strawberry.lazy('dcim.graphql.types')] @@ -632,7 +636,7 @@ class ModuleTypeType(NetBoxObjectType): filters=PlatformFilter, pagination=True ) -class PlatformType(OrganizationalObjectType): +class PlatformType(NestedGroupObjectType): parent: Annotated['PlatformType', strawberry.lazy('dcim.graphql.types')] | None children: List[Annotated['PlatformType', strawberry.lazy('dcim.graphql.types')]] manufacturer: Annotated["ManufacturerType", strawberry.lazy('dcim.graphql.types')] | None @@ -648,7 +652,7 @@ class PlatformType(OrganizationalObjectType): filters=PowerFeedFilter, pagination=True ) -class PowerFeedType(NetBoxObjectType, CabledObjectMixin, PathEndpointMixin): +class PowerFeedType(CabledObjectMixin, PathEndpointMixin, PrimaryObjectType): power_panel: Annotated["PowerPanelType", strawberry.lazy('dcim.graphql.types')] rack: Annotated["RackType", strawberry.lazy('dcim.graphql.types')] | None tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None @@ -681,7 +685,7 @@ class PowerOutletTemplateType(ModularComponentTemplateType): filters=PowerPanelFilter, pagination=True ) -class PowerPanelType(NetBoxObjectType, ContactsMixin): +class PowerPanelType(ContactsMixin, PrimaryObjectType): site: Annotated["SiteType", strawberry.lazy('dcim.graphql.types')] location: Annotated["LocationType", strawberry.lazy('dcim.graphql.types')] | None @@ -715,7 +719,7 @@ class PowerPortTemplateType(ModularComponentTemplateType): filters=RackTypeFilter, pagination=True ) -class RackTypeType(NetBoxObjectType): +class RackTypeType(PrimaryObjectType): manufacturer: Annotated["ManufacturerType", strawberry.lazy('dcim.graphql.types')] @@ -725,7 +729,7 @@ class RackTypeType(NetBoxObjectType): filters=RackFilter, pagination=True ) -class RackType(VLANGroupsMixin, ImageAttachmentsMixin, ContactsMixin, NetBoxObjectType): +class RackType(VLANGroupsMixin, ImageAttachmentsMixin, ContactsMixin, PrimaryObjectType): site: Annotated["SiteType", strawberry.lazy('dcim.graphql.types')] location: Annotated["LocationType", strawberry.lazy('dcim.graphql.types')] | None tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None @@ -744,7 +748,7 @@ class RackType(VLANGroupsMixin, ImageAttachmentsMixin, ContactsMixin, NetBoxObje filters=RackReservationFilter, pagination=True ) -class RackReservationType(NetBoxObjectType): +class RackReservationType(PrimaryObjectType): units: List[int] rack: Annotated["RackType", strawberry.lazy('dcim.graphql.types')] tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None @@ -793,7 +797,7 @@ class RearPortTemplateType(ModularComponentTemplateType): filters=RegionFilter, pagination=True ) -class RegionType(VLANGroupsMixin, ContactsMixin, OrganizationalObjectType): +class RegionType(VLANGroupsMixin, ContactsMixin, NestedGroupObjectType): sites: List[Annotated["SiteType", strawberry.lazy('dcim.graphql.types')]] children: List[Annotated["RegionType", strawberry.lazy('dcim.graphql.types')]] @@ -819,7 +823,7 @@ class RegionType(VLANGroupsMixin, ContactsMixin, OrganizationalObjectType): filters=SiteFilter, pagination=True ) -class SiteType(VLANGroupsMixin, ImageAttachmentsMixin, ContactsMixin, NetBoxObjectType): +class SiteType(VLANGroupsMixin, ImageAttachmentsMixin, ContactsMixin, PrimaryObjectType): time_zone: str | None region: Annotated["RegionType", strawberry.lazy('dcim.graphql.types')] | None group: Annotated["SiteGroupType", strawberry.lazy('dcim.graphql.types')] | None @@ -854,7 +858,7 @@ class SiteType(VLANGroupsMixin, ImageAttachmentsMixin, ContactsMixin, NetBoxObje filters=SiteGroupFilter, pagination=True ) -class SiteGroupType(VLANGroupsMixin, ContactsMixin, OrganizationalObjectType): +class SiteGroupType(VLANGroupsMixin, ContactsMixin, NestedGroupObjectType): sites: List[Annotated["SiteType", strawberry.lazy('dcim.graphql.types')]] children: List[Annotated["SiteGroupType", strawberry.lazy('dcim.graphql.types')]] @@ -880,7 +884,7 @@ class SiteGroupType(VLANGroupsMixin, ContactsMixin, OrganizationalObjectType): filters=VirtualChassisFilter, pagination=True ) -class VirtualChassisType(NetBoxObjectType): +class VirtualChassisType(PrimaryObjectType): member_count: BigInt master: Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')] | None @@ -893,7 +897,7 @@ class VirtualChassisType(NetBoxObjectType): filters=VirtualDeviceContextFilter, pagination=True ) -class VirtualDeviceContextType(NetBoxObjectType): +class VirtualDeviceContextType(PrimaryObjectType): device: Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')] | None primary_ip4: Annotated["IPAddressType", strawberry.lazy('ipam.graphql.types')] | None primary_ip6: Annotated["IPAddressType", strawberry.lazy('ipam.graphql.types')] | None diff --git a/netbox/extras/graphql/types.py b/netbox/extras/graphql/types.py index 97637684e..8230edea8 100644 --- a/netbox/extras/graphql/types.py +++ b/netbox/extras/graphql/types.py @@ -6,7 +6,8 @@ import strawberry_django from core.graphql.mixins import SyncedDataMixin from extras import models from extras.graphql.mixins import CustomFieldsMixin, TagsMixin -from netbox.graphql.types import BaseObjectType, ContentTypeType, NetBoxObjectType, ObjectType, OrganizationalObjectType +from netbox.graphql.types import BaseObjectType, ContentTypeType, ObjectType, PrimaryObjectType +from users.graphql.mixins import OwnerMixin from .filters import * if TYPE_CHECKING: @@ -51,7 +52,7 @@ __all__ = ( filters=ConfigContextProfileFilter, pagination=True ) -class ConfigContextProfileType(SyncedDataMixin, NetBoxObjectType): +class ConfigContextProfileType(SyncedDataMixin, PrimaryObjectType): pass @@ -61,7 +62,7 @@ class ConfigContextProfileType(SyncedDataMixin, NetBoxObjectType): filters=ConfigContextFilter, pagination=True ) -class ConfigContextType(SyncedDataMixin, ObjectType): +class ConfigContextType(SyncedDataMixin, OwnerMixin, ObjectType): profile: ConfigContextProfileType | None roles: List[Annotated["DeviceRoleType", strawberry.lazy('dcim.graphql.types')]] device_types: List[Annotated["DeviceTypeType", strawberry.lazy('dcim.graphql.types')]] @@ -84,7 +85,7 @@ class ConfigContextType(SyncedDataMixin, ObjectType): filters=ConfigTemplateFilter, pagination=True ) -class ConfigTemplateType(SyncedDataMixin, TagsMixin, ObjectType): +class ConfigTemplateType(SyncedDataMixin, OwnerMixin, TagsMixin, ObjectType): virtualmachines: List[Annotated["VirtualMachineType", strawberry.lazy('virtualization.graphql.types')]] devices: List[Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')]] platforms: List[Annotated["PlatformType", strawberry.lazy('dcim.graphql.types')]] @@ -97,7 +98,7 @@ class ConfigTemplateType(SyncedDataMixin, TagsMixin, ObjectType): filters=CustomFieldFilter, pagination=True ) -class CustomFieldType(ObjectType): +class CustomFieldType(OwnerMixin, ObjectType): related_object_type: Annotated["ContentTypeType", strawberry.lazy('netbox.graphql.types')] | None choice_set: Annotated["CustomFieldChoiceSetType", strawberry.lazy('extras.graphql.types')] | None @@ -108,7 +109,7 @@ class CustomFieldType(ObjectType): filters=CustomFieldChoiceSetFilter, pagination=True ) -class CustomFieldChoiceSetType(ObjectType): +class CustomFieldChoiceSetType(OwnerMixin, ObjectType): choices_for: List[Annotated["CustomFieldType", strawberry.lazy('extras.graphql.types')]] extra_choices: List[List[str]] | None @@ -120,7 +121,7 @@ class CustomFieldChoiceSetType(ObjectType): filters=CustomLinkFilter, pagination=True ) -class CustomLinkType(ObjectType): +class CustomLinkType(OwnerMixin, ObjectType): pass @@ -130,7 +131,7 @@ class CustomLinkType(ObjectType): filters=ExportTemplateFilter, pagination=True ) -class ExportTemplateType(SyncedDataMixin, ObjectType): +class ExportTemplateType(SyncedDataMixin, OwnerMixin, ObjectType): pass @@ -180,7 +181,7 @@ class NotificationGroupType(ObjectType): filters=SavedFilterFilter, pagination=True ) -class SavedFilterType(ObjectType): +class SavedFilterType(OwnerMixin, ObjectType): user: Annotated["UserType", strawberry.lazy('users.graphql.types')] | None @@ -209,7 +210,7 @@ class TableConfigType(ObjectType): filters=TagFilter, pagination=True ) -class TagType(ObjectType): +class TagType(OwnerMixin, ObjectType): color: str object_types: List[ContentTypeType] @@ -221,7 +222,7 @@ class TagType(ObjectType): filters=WebhookFilter, pagination=True ) -class WebhookType(OrganizationalObjectType): +class WebhookType(OwnerMixin, CustomFieldsMixin, TagsMixin, ObjectType): pass @@ -231,5 +232,5 @@ class WebhookType(OrganizationalObjectType): filters=EventRuleFilter, pagination=True ) -class EventRuleType(OrganizationalObjectType): +class EventRuleType(OwnerMixin, CustomFieldsMixin, TagsMixin, ObjectType): action_object_type: Annotated["ContentTypeType", strawberry.lazy('netbox.graphql.types')] | None diff --git a/netbox/ipam/graphql/types.py b/netbox/ipam/graphql/types.py index a07316fe4..e63f0bff3 100644 --- a/netbox/ipam/graphql/types.py +++ b/netbox/ipam/graphql/types.py @@ -8,7 +8,7 @@ from dcim.graphql.types import SiteType from extras.graphql.mixins import ContactsMixin from ipam import models from netbox.graphql.scalars import BigInt -from netbox.graphql.types import BaseObjectType, NetBoxObjectType, OrganizationalObjectType +from netbox.graphql.types import BaseObjectType, NetBoxObjectType, OrganizationalObjectType, PrimaryObjectType from .filters import * from .mixins import IPAddressesMixin @@ -74,7 +74,7 @@ class BaseIPAddressFamilyType: filters=ASNFilter, pagination=True ) -class ASNType(NetBoxObjectType, ContactsMixin): +class ASNType(ContactsMixin, PrimaryObjectType): asn: BigInt rir: Annotated["RIRType", strawberry.lazy('ipam.graphql.types')] | None tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None @@ -89,7 +89,7 @@ class ASNType(NetBoxObjectType, ContactsMixin): filters=ASNRangeFilter, pagination=True ) -class ASNRangeType(NetBoxObjectType): +class ASNRangeType(OrganizationalObjectType): start: BigInt end: BigInt rir: Annotated["RIRType", strawberry.lazy('ipam.graphql.types')] | None @@ -102,7 +102,7 @@ class ASNRangeType(NetBoxObjectType): filters=AggregateFilter, pagination=True ) -class AggregateType(NetBoxObjectType, ContactsMixin, BaseIPAddressFamilyType): +class AggregateType(ContactsMixin, BaseIPAddressFamilyType, PrimaryObjectType): prefix: str rir: Annotated["RIRType", strawberry.lazy('ipam.graphql.types')] | None tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None @@ -114,8 +114,7 @@ class AggregateType(NetBoxObjectType, ContactsMixin, BaseIPAddressFamilyType): filters=FHRPGroupFilter, pagination=True ) -class FHRPGroupType(NetBoxObjectType, IPAddressesMixin): - +class FHRPGroupType(IPAddressesMixin, PrimaryObjectType): fhrpgroupassignment_set: List[Annotated["FHRPGroupAssignmentType", strawberry.lazy('ipam.graphql.types')]] @@ -142,7 +141,7 @@ class FHRPGroupAssignmentType(BaseObjectType): filters=IPAddressFilter, pagination=True ) -class IPAddressType(NetBoxObjectType, ContactsMixin, BaseIPAddressFamilyType): +class IPAddressType(ContactsMixin, BaseIPAddressFamilyType, PrimaryObjectType): address: str vrf: Annotated["VRFType", strawberry.lazy('ipam.graphql.types')] | None tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None @@ -167,7 +166,7 @@ class IPAddressType(NetBoxObjectType, ContactsMixin, BaseIPAddressFamilyType): filters=IPRangeFilter, pagination=True ) -class IPRangeType(NetBoxObjectType, ContactsMixin): +class IPRangeType(ContactsMixin, PrimaryObjectType): start_address: str end_address: str vrf: Annotated["VRFType", strawberry.lazy('ipam.graphql.types')] | None @@ -181,7 +180,7 @@ class IPRangeType(NetBoxObjectType, ContactsMixin): filters=PrefixFilter, pagination=True ) -class PrefixType(NetBoxObjectType, ContactsMixin, BaseIPAddressFamilyType): +class PrefixType(ContactsMixin, BaseIPAddressFamilyType, PrimaryObjectType): prefix: str vrf: Annotated["VRFType", strawberry.lazy('ipam.graphql.types')] | None tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None @@ -230,7 +229,7 @@ class RoleType(OrganizationalObjectType): filters=RouteTargetFilter, pagination=True ) -class RouteTargetType(NetBoxObjectType): +class RouteTargetType(PrimaryObjectType): tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None importing_l2vpns: List[Annotated["L2VPNType", strawberry.lazy('vpn.graphql.types')]] @@ -245,7 +244,7 @@ class RouteTargetType(NetBoxObjectType): filters=ServiceFilter, pagination=True ) -class ServiceType(NetBoxObjectType, ContactsMixin): +class ServiceType(ContactsMixin, PrimaryObjectType): ports: List[int] ipaddresses: List[Annotated["IPAddressType", strawberry.lazy('ipam.graphql.types')]] @@ -264,7 +263,7 @@ class ServiceType(NetBoxObjectType, ContactsMixin): filters=ServiceTemplateFilter, pagination=True ) -class ServiceTemplateType(NetBoxObjectType): +class ServiceTemplateType(PrimaryObjectType): ports: List[int] @@ -274,7 +273,7 @@ class ServiceTemplateType(NetBoxObjectType): filters=VLANFilter, pagination=True ) -class VLANType(NetBoxObjectType): +class VLANType(PrimaryObjectType): site: Annotated["SiteType", strawberry.lazy('ipam.graphql.types')] | None group: Annotated["VLANGroupType", strawberry.lazy('ipam.graphql.types')] | None tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None @@ -323,7 +322,7 @@ class VLANGroupType(OrganizationalObjectType): filters=VLANTranslationPolicyFilter, pagination=True ) -class VLANTranslationPolicyType(NetBoxObjectType): +class VLANTranslationPolicyType(PrimaryObjectType): rules: List[Annotated["VLANTranslationRuleType", strawberry.lazy('ipam.graphql.types')]] @@ -346,7 +345,7 @@ class VLANTranslationRuleType(NetBoxObjectType): filters=VRFFilter, pagination=True ) -class VRFType(NetBoxObjectType): +class VRFType(PrimaryObjectType): tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None interfaces: List[Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')]] diff --git a/netbox/netbox/graphql/types.py b/netbox/netbox/graphql/types.py index bdc38b349..df569390d 100644 --- a/netbox/netbox/graphql/types.py +++ b/netbox/netbox/graphql/types.py @@ -6,13 +6,16 @@ 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 +from users.graphql.mixins import OwnerMixin __all__ = ( 'BaseObjectType', 'ContentTypeType', + 'NestedGroupObjectType', + 'NetBoxObjectType', 'ObjectType', 'OrganizationalObjectType', - 'NetBoxObjectType', + 'PrimaryObjectType', ) @@ -53,14 +56,44 @@ class ObjectType( pass -class OrganizationalObjectType( +class PrimaryObjectType( ChangelogMixin, CustomFieldsMixin, + JournalEntriesMixin, TagsMixin, + OwnerMixin, BaseObjectType ): """ - Base type for organizational models + Base GraphQL type for models which inherit from PrimaryModel. + """ + pass + + +class OrganizationalObjectType( + ChangelogMixin, + CustomFieldsMixin, + JournalEntriesMixin, + TagsMixin, + OwnerMixin, + BaseObjectType +): + """ + Base GraphQL type for models which inherit from OrganizationalModel. + """ + pass + + +class NestedGroupObjectType( + ChangelogMixin, + CustomFieldsMixin, + JournalEntriesMixin, + TagsMixin, + OwnerMixin, + BaseObjectType +): + """ + Base GraphQL type for models which inherit from NestedGroupModel. """ pass @@ -72,9 +105,6 @@ class NetBoxObjectType( TagsMixin, BaseObjectType ): - """ - GraphQL type for most NetBox models. Includes support for custom fields, change logging, journaling, and tags. - """ pass diff --git a/netbox/tenancy/graphql/types.py b/netbox/tenancy/graphql/types.py index a3713da93..89d2bb971 100644 --- a/netbox/tenancy/graphql/types.py +++ b/netbox/tenancy/graphql/types.py @@ -4,7 +4,7 @@ import strawberry import strawberry_django from extras.graphql.mixins import CustomFieldsMixin, TagsMixin, ContactsMixin -from netbox.graphql.types import BaseObjectType, OrganizationalObjectType, NetBoxObjectType +from netbox.graphql.types import BaseObjectType, NestedGroupObjectType, OrganizationalObjectType, PrimaryObjectType from tenancy import models from .filters import * from .mixins import ContactAssignmentsMixin @@ -57,7 +57,7 @@ __all__ = ( filters=TenantFilter, pagination=True ) -class TenantType(ContactsMixin, NetBoxObjectType): +class TenantType(ContactsMixin, PrimaryObjectType): group: Annotated['TenantGroupType', strawberry.lazy('tenancy.graphql.types')] | None asns: List[Annotated['ASNType', strawberry.lazy('ipam.graphql.types')]] circuits: List[Annotated['CircuitType', strawberry.lazy('circuits.graphql.types')]] @@ -91,7 +91,7 @@ class TenantType(ContactsMixin, NetBoxObjectType): filters=TenantGroupFilter, pagination=True ) -class TenantGroupType(OrganizationalObjectType): +class TenantGroupType(NestedGroupObjectType): parent: Annotated['TenantGroupType', strawberry.lazy('tenancy.graphql.types')] | None tenants: List[TenantType] @@ -108,7 +108,7 @@ class TenantGroupType(OrganizationalObjectType): filters=ContactFilter, pagination=True ) -class ContactType(ContactAssignmentsMixin, NetBoxObjectType): +class ContactType(ContactAssignmentsMixin, PrimaryObjectType): groups: List[Annotated['ContactGroupType', strawberry.lazy('tenancy.graphql.types')]] @@ -128,7 +128,7 @@ class ContactRoleType(ContactAssignmentsMixin, OrganizationalObjectType): filters=ContactGroupFilter, pagination=True ) -class ContactGroupType(OrganizationalObjectType): +class ContactGroupType(NestedGroupObjectType): parent: Annotated['ContactGroupType', strawberry.lazy('tenancy.graphql.types')] | None contacts: List[ContactType] diff --git a/netbox/users/graphql/mixins.py b/netbox/users/graphql/mixins.py new file mode 100644 index 000000000..f185eba66 --- /dev/null +++ b/netbox/users/graphql/mixins.py @@ -0,0 +1,15 @@ +from typing import Annotated, TYPE_CHECKING + +import strawberry + +if TYPE_CHECKING: + from users.graphql.types import OwnerType + +__all__ = ( + 'OwnerMixin', +) + + +@strawberry.type +class OwnerMixin: + owner: Annotated['OwnerType', strawberry.lazy('users.graphql.types')] | None diff --git a/netbox/virtualization/graphql/types.py b/netbox/virtualization/graphql/types.py index ba20e6844..b1a47a52a 100644 --- a/netbox/virtualization/graphql/types.py +++ b/netbox/virtualization/graphql/types.py @@ -6,7 +6,8 @@ import strawberry_django from extras.graphql.mixins import ConfigContextMixin, ContactsMixin from ipam.graphql.mixins import IPAddressesMixin, VLANGroupsMixin from netbox.graphql.scalars import BigInt -from netbox.graphql.types import OrganizationalObjectType, NetBoxObjectType +from netbox.graphql.types import NetBoxObjectType, OrganizationalObjectType, PrimaryObjectType +from users.graphql.mixins import OwnerMixin from virtualization import models from .filters import * @@ -36,7 +37,7 @@ __all__ = ( @strawberry.type -class ComponentType(NetBoxObjectType): +class ComponentType(OwnerMixin, NetBoxObjectType): """ Base type for device/VM components """ @@ -49,7 +50,7 @@ class ComponentType(NetBoxObjectType): filters=ClusterFilter, pagination=True ) -class ClusterType(ContactsMixin, VLANGroupsMixin, NetBoxObjectType): +class ClusterType(ContactsMixin, VLANGroupsMixin, PrimaryObjectType): type: Annotated["ClusterTypeType", strawberry.lazy('virtualization.graphql.types')] | None group: Annotated["ClusterGroupType", strawberry.lazy('virtualization.graphql.types')] | None tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None @@ -94,7 +95,7 @@ class ClusterTypeType(OrganizationalObjectType): filters=VirtualMachineFilter, pagination=True ) -class VirtualMachineType(ConfigContextMixin, ContactsMixin, NetBoxObjectType): +class VirtualMachineType(ConfigContextMixin, ContactsMixin, PrimaryObjectType): interface_count: BigInt virtual_disk_count: BigInt interface_count: BigInt diff --git a/netbox/vpn/graphql/types.py b/netbox/vpn/graphql/types.py index e1b46f9c4..902880511 100644 --- a/netbox/vpn/graphql/types.py +++ b/netbox/vpn/graphql/types.py @@ -4,7 +4,7 @@ import strawberry import strawberry_django from extras.graphql.mixins import ContactsMixin, CustomFieldsMixin, TagsMixin -from netbox.graphql.types import ObjectType, OrganizationalObjectType, NetBoxObjectType +from netbox.graphql.types import ObjectType, OrganizationalObjectType, NetBoxObjectType, PrimaryObjectType from vpn import models from .filters import * @@ -58,7 +58,7 @@ class TunnelTerminationType(CustomFieldsMixin, TagsMixin, ObjectType): filters=TunnelFilter, pagination=True ) -class TunnelType(ContactsMixin, NetBoxObjectType): +class TunnelType(ContactsMixin, PrimaryObjectType): group: Annotated["TunnelGroupType", strawberry.lazy('vpn.graphql.types')] | None ipsec_profile: Annotated["IPSecProfileType", strawberry.lazy('vpn.graphql.types')] | None tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None @@ -72,8 +72,7 @@ class TunnelType(ContactsMixin, NetBoxObjectType): filters=IKEProposalFilter, pagination=True ) -class IKEProposalType(OrganizationalObjectType): - +class IKEProposalType(PrimaryObjectType): ike_policies: List[Annotated["IKEPolicyType", strawberry.lazy('vpn.graphql.types')]] @@ -83,8 +82,7 @@ class IKEProposalType(OrganizationalObjectType): filters=IKEPolicyFilter, pagination=True ) -class IKEPolicyType(OrganizationalObjectType): - +class IKEPolicyType(PrimaryObjectType): proposals: List[Annotated["IKEProposalType", strawberry.lazy('vpn.graphql.types')]] ipsec_profiles: List[Annotated["IPSecProfileType", strawberry.lazy('vpn.graphql.types')]] @@ -95,8 +93,7 @@ class IKEPolicyType(OrganizationalObjectType): filters=IPSecProposalFilter, pagination=True ) -class IPSecProposalType(OrganizationalObjectType): - +class IPSecProposalType(PrimaryObjectType): ipsec_policies: List[Annotated["IPSecPolicyType", strawberry.lazy('vpn.graphql.types')]] @@ -106,8 +103,7 @@ class IPSecProposalType(OrganizationalObjectType): filters=IPSecPolicyFilter, pagination=True ) -class IPSecPolicyType(OrganizationalObjectType): - +class IPSecPolicyType(PrimaryObjectType): proposals: List[Annotated["IPSecProposalType", strawberry.lazy('vpn.graphql.types')]] ipsec_profiles: List[Annotated["IPSecProfileType", strawberry.lazy('vpn.graphql.types')]] @@ -118,7 +114,7 @@ class IPSecPolicyType(OrganizationalObjectType): filters=IPSecProfileFilter, pagination=True ) -class IPSecProfileType(OrganizationalObjectType): +class IPSecProfileType(PrimaryObjectType): ike_policy: Annotated["IKEPolicyType", strawberry.lazy('vpn.graphql.types')] ipsec_policy: Annotated["IPSecPolicyType", strawberry.lazy('vpn.graphql.types')] @@ -131,7 +127,7 @@ class IPSecProfileType(OrganizationalObjectType): filters=L2VPNFilter, pagination=True ) -class L2VPNType(ContactsMixin, NetBoxObjectType): +class L2VPNType(ContactsMixin, PrimaryObjectType): tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None export_targets: List[Annotated["RouteTargetType", strawberry.lazy('ipam.graphql.types')]] diff --git a/netbox/wireless/graphql/types.py b/netbox/wireless/graphql/types.py index eeca6a82b..124056b91 100644 --- a/netbox/wireless/graphql/types.py +++ b/netbox/wireless/graphql/types.py @@ -3,7 +3,7 @@ from typing import Annotated, List, TYPE_CHECKING, Union import strawberry import strawberry_django -from netbox.graphql.types import OrganizationalObjectType, NetBoxObjectType +from netbox.graphql.types import NestedGroupObjectType, PrimaryObjectType from wireless import models from .filters import * @@ -25,7 +25,7 @@ __all__ = ( filters=WirelessLANGroupFilter, pagination=True ) -class WirelessLANGroupType(OrganizationalObjectType): +class WirelessLANGroupType(NestedGroupObjectType): parent: Annotated["WirelessLANGroupType", strawberry.lazy('wireless.graphql.types')] | None wireless_lans: List[Annotated["WirelessLANType", strawberry.lazy('wireless.graphql.types')]] @@ -38,7 +38,7 @@ class WirelessLANGroupType(OrganizationalObjectType): filters=WirelessLANFilter, pagination=True ) -class WirelessLANType(NetBoxObjectType): +class WirelessLANType(PrimaryObjectType): group: Annotated["WirelessLANGroupType", strawberry.lazy('wireless.graphql.types')] | None vlan: Annotated["VLANType", strawberry.lazy('ipam.graphql.types')] | None tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None @@ -61,7 +61,7 @@ class WirelessLANType(NetBoxObjectType): filters=WirelessLinkFilter, pagination=True ) -class WirelessLinkType(NetBoxObjectType): +class WirelessLinkType(PrimaryObjectType): interface_a: Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')] interface_b: Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')] tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None