From fe7cc8cae9b1729169c20014922b38873cbb3722 Mon Sep 17 00:00:00 2001 From: Arthur Hanson Date: Thu, 20 Mar 2025 13:00:14 -0700 Subject: [PATCH] Closes #16224 GraphQL Pagination (#18903) * 16244 add pagination * 16244 add pagination * 16244 fix order_by pagination * 16224 document pagination * 16224 remove extraneous code * 16224 missing core types * 16224 review changes * 16224 review changes * 16224 review changes --- docs/integrations/graphql-api.md | 12 +++ netbox/circuits/graphql/types.py | 33 ++++--- netbox/core/graphql/types.py | 15 ++- netbox/dcim/graphql/types.py | 129 ++++++++++++++++--------- netbox/extras/graphql/types.py | 41 +++++--- netbox/ipam/graphql/types.py | 54 +++++++---- netbox/netbox/graphql/schema.py | 2 +- netbox/netbox/graphql/types.py | 2 + netbox/netbox/settings.py | 1 + netbox/tenancy/graphql/types.py | 42 ++++++-- netbox/users/graphql/types.py | 6 +- netbox/virtualization/graphql/types.py | 18 ++-- netbox/vpn/graphql/types.py | 30 ++++-- netbox/wireless/graphql/types.py | 9 +- 14 files changed, 277 insertions(+), 117 deletions(-) diff --git a/docs/integrations/graphql-api.md b/docs/integrations/graphql-api.md index 23824ad2b..cae046b6c 100644 --- a/docs/integrations/graphql-api.md +++ b/docs/integrations/graphql-api.md @@ -131,6 +131,18 @@ Certain queries can return multiple types of objects, for example cable terminat ``` The field "class_type" is an easy way to distinguish what type of object it is when viewing the returned data, or when filtering. It contains the class name, for example "CircuitTermination" or "ConsoleServerPort". +## Pagination + +Queries can be paginated by specifying pagination in the query and supplying an offset and optionaly a limit in the query. If no limit is given, a default of 100 is used. Queries are not paginated unless requested in the query. An example paginated query is shown below: + +``` +query { + device_list(pagination: { offset: 0, limit: 20 }) { + id + } +} +``` + ## Authentication NetBox's GraphQL API uses the same API authentication tokens as its REST API. Authentication tokens are included with requests by attaching an `Authorization` HTTP header in the following form: diff --git a/netbox/circuits/graphql/types.py b/netbox/circuits/graphql/types.py index 860c19852..cdd02c891 100644 --- a/netbox/circuits/graphql/types.py +++ b/netbox/circuits/graphql/types.py @@ -32,7 +32,8 @@ __all__ = ( @strawberry_django.type( models.Provider, fields='__all__', - filters=ProviderFilter + filters=ProviderFilter, + pagination=True ) class ProviderType(NetBoxObjectType, ContactsMixin): @@ -45,7 +46,8 @@ class ProviderType(NetBoxObjectType, ContactsMixin): @strawberry_django.type( models.ProviderAccount, fields='__all__', - filters=ProviderAccountFilter + filters=ProviderAccountFilter, + pagination=True ) class ProviderAccountType(NetBoxObjectType): provider: Annotated["ProviderType", strawberry.lazy('circuits.graphql.types')] @@ -56,7 +58,8 @@ class ProviderAccountType(NetBoxObjectType): @strawberry_django.type( models.ProviderNetwork, fields='__all__', - filters=ProviderNetworkFilter + filters=ProviderNetworkFilter, + pagination=True ) class ProviderNetworkType(NetBoxObjectType): provider: Annotated["ProviderType", strawberry.lazy('circuits.graphql.types')] @@ -67,7 +70,8 @@ class ProviderNetworkType(NetBoxObjectType): @strawberry_django.type( models.CircuitTermination, exclude=['termination_type', 'termination_id', '_location', '_region', '_site', '_site_group', '_provider_network'], - filters=CircuitTerminationFilter + filters=CircuitTerminationFilter, + pagination=True ) class CircuitTerminationType(CustomFieldsMixin, TagsMixin, CabledObjectMixin, ObjectType): circuit: Annotated["CircuitType", strawberry.lazy('circuits.graphql.types')] @@ -86,7 +90,8 @@ class CircuitTerminationType(CustomFieldsMixin, TagsMixin, CabledObjectMixin, Ob @strawberry_django.type( models.CircuitType, fields='__all__', - filters=CircuitTypeFilter + filters=CircuitTypeFilter, + pagination=True ) class CircuitTypeType(OrganizationalObjectType): color: str @@ -97,7 +102,8 @@ class CircuitTypeType(OrganizationalObjectType): @strawberry_django.type( models.Circuit, fields='__all__', - filters=CircuitFilter + filters=CircuitFilter, + pagination=True ) class CircuitType(NetBoxObjectType, ContactsMixin): provider: ProviderType @@ -113,7 +119,8 @@ class CircuitType(NetBoxObjectType, ContactsMixin): @strawberry_django.type( models.CircuitGroup, fields='__all__', - filters=CircuitGroupFilter + filters=CircuitGroupFilter, + pagination=True ) class CircuitGroupType(OrganizationalObjectType): tenant: TenantType | None @@ -122,7 +129,8 @@ class CircuitGroupType(OrganizationalObjectType): @strawberry_django.type( models.CircuitGroupAssignment, exclude=['member_type', 'member_id'], - filters=CircuitGroupAssignmentFilter + filters=CircuitGroupAssignmentFilter, + pagination=True ) class CircuitGroupAssignmentType(TagsMixin, BaseObjectType): group: Annotated["CircuitGroupType", strawberry.lazy('circuits.graphql.types')] @@ -138,7 +146,8 @@ class CircuitGroupAssignmentType(TagsMixin, BaseObjectType): @strawberry_django.type( models.VirtualCircuitType, fields='__all__', - filters=VirtualCircuitTypeFilter + filters=VirtualCircuitTypeFilter, + pagination=True ) class VirtualCircuitTypeType(OrganizationalObjectType): color: str @@ -149,7 +158,8 @@ class VirtualCircuitTypeType(OrganizationalObjectType): @strawberry_django.type( models.VirtualCircuitTermination, fields='__all__', - filters=VirtualCircuitTerminationFilter + filters=VirtualCircuitTerminationFilter, + pagination=True ) class VirtualCircuitTerminationType(CustomFieldsMixin, TagsMixin, ObjectType): virtual_circuit: Annotated[ @@ -165,7 +175,8 @@ class VirtualCircuitTerminationType(CustomFieldsMixin, TagsMixin, ObjectType): @strawberry_django.type( models.VirtualCircuit, fields='__all__', - filters=VirtualCircuitFilter + filters=VirtualCircuitFilter, + pagination=True ) class VirtualCircuitType(NetBoxObjectType): provider_network: ProviderNetworkType = strawberry_django.field(select_related=["provider_network"]) diff --git a/netbox/core/graphql/types.py b/netbox/core/graphql/types.py index 1c57a2dbc..ffaa24411 100644 --- a/netbox/core/graphql/types.py +++ b/netbox/core/graphql/types.py @@ -19,7 +19,8 @@ __all__ = ( @strawberry_django.type( models.DataFile, exclude=['data',], - filters=DataFileFilter + filters=DataFileFilter, + pagination=True ) class DataFileType(BaseObjectType): source: Annotated["DataSourceType", strawberry.lazy('core.graphql.types')] @@ -28,7 +29,8 @@ class DataFileType(BaseObjectType): @strawberry_django.type( models.DataSource, fields='__all__', - filters=DataSourceFilter + filters=DataSourceFilter, + pagination=True ) class DataSourceType(NetBoxObjectType): @@ -38,12 +40,17 @@ class DataSourceType(NetBoxObjectType): @strawberry_django.type( models.ObjectChange, fields='__all__', - filters=ObjectChangeFilter + filters=ObjectChangeFilter, + pagination=True ) class ObjectChangeType(BaseObjectType): pass -@strawberry_django.type(DjangoContentType, fields='__all__') +@strawberry_django.type( + DjangoContentType, + fields='__all__', + pagination=True +) class ContentType: pass diff --git a/netbox/dcim/graphql/types.py b/netbox/dcim/graphql/types.py index 6cd072479..9554a9f60 100644 --- a/netbox/dcim/graphql/types.py +++ b/netbox/dcim/graphql/types.py @@ -133,7 +133,8 @@ class ModularComponentTemplateType(ComponentTemplateType): @strawberry_django.type( models.CableTermination, exclude=['termination_type', 'termination_id', '_device', '_rack', '_location', '_site'], - filters=CableTerminationFilter + filters=CableTerminationFilter, + pagination=True ) class CableTerminationType(NetBoxObjectType): cable: Annotated["CableType", strawberry.lazy('dcim.graphql.types')] | None @@ -153,7 +154,8 @@ class CableTerminationType(NetBoxObjectType): @strawberry_django.type( models.Cable, fields='__all__', - filters=CableFilter + filters=CableFilter, + pagination=True ) class CableType(NetBoxObjectType): color: str @@ -189,7 +191,8 @@ class CableType(NetBoxObjectType): @strawberry_django.type( models.ConsolePort, exclude=['_path'], - filters=ConsolePortFilter + filters=ConsolePortFilter, + pagination=True ) class ConsolePortType(ModularComponentType, CabledObjectMixin, PathEndpointMixin): pass @@ -198,7 +201,8 @@ class ConsolePortType(ModularComponentType, CabledObjectMixin, PathEndpointMixin @strawberry_django.type( models.ConsolePortTemplate, fields='__all__', - filters=ConsolePortTemplateFilter + filters=ConsolePortTemplateFilter, + pagination=True ) class ConsolePortTemplateType(ModularComponentTemplateType): pass @@ -207,7 +211,8 @@ class ConsolePortTemplateType(ModularComponentTemplateType): @strawberry_django.type( models.ConsoleServerPort, exclude=['_path'], - filters=ConsoleServerPortFilter + filters=ConsoleServerPortFilter, + pagination=True ) class ConsoleServerPortType(ModularComponentType, CabledObjectMixin, PathEndpointMixin): pass @@ -216,7 +221,8 @@ class ConsoleServerPortType(ModularComponentType, CabledObjectMixin, PathEndpoin @strawberry_django.type( models.ConsoleServerPortTemplate, fields='__all__', - filters=ConsoleServerPortTemplateFilter + filters=ConsoleServerPortTemplateFilter, + pagination=True ) class ConsoleServerPortTemplateType(ModularComponentTemplateType): pass @@ -225,7 +231,8 @@ class ConsoleServerPortTemplateType(ModularComponentTemplateType): @strawberry_django.type( models.Device, fields='__all__', - filters=DeviceFilter + filters=DeviceFilter, + pagination=True ) class DeviceType(ConfigContextMixin, ImageAttachmentsMixin, ContactsMixin, NetBoxObjectType): console_port_count: BigInt @@ -280,7 +287,8 @@ class DeviceType(ConfigContextMixin, ImageAttachmentsMixin, ContactsMixin, NetBo @strawberry_django.type( models.DeviceBay, fields='__all__', - filters=DeviceBayFilter + filters=DeviceBayFilter, + pagination=True ) class DeviceBayType(ComponentType): installed_device: Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')] | None @@ -289,7 +297,8 @@ class DeviceBayType(ComponentType): @strawberry_django.type( models.DeviceBayTemplate, fields='__all__', - filters=DeviceBayTemplateFilter + filters=DeviceBayTemplateFilter, + pagination=True ) class DeviceBayTemplateType(ComponentTemplateType): pass @@ -298,7 +307,8 @@ class DeviceBayTemplateType(ComponentTemplateType): @strawberry_django.type( models.InventoryItemTemplate, exclude=['component_type', 'component_id', 'parent'], - filters=InventoryItemTemplateFilter + filters=InventoryItemTemplateFilter, + pagination=True ) class InventoryItemTemplateType(ComponentTemplateType): role: Annotated["InventoryItemRoleType", strawberry.lazy('dcim.graphql.types')] | None @@ -324,7 +334,8 @@ class InventoryItemTemplateType(ComponentTemplateType): @strawberry_django.type( models.DeviceRole, fields='__all__', - filters=DeviceRoleFilter + filters=DeviceRoleFilter, + pagination=True ) class DeviceRoleType(OrganizationalObjectType): color: str @@ -337,7 +348,8 @@ class DeviceRoleType(OrganizationalObjectType): @strawberry_django.type( models.DeviceType, fields='__all__', - filters=DeviceTypeFilter + filters=DeviceTypeFilter, + pagination=True ) class DeviceTypeType(NetBoxObjectType): console_port_template_count: BigInt @@ -371,7 +383,8 @@ class DeviceTypeType(NetBoxObjectType): @strawberry_django.type( models.FrontPort, fields='__all__', - filters=FrontPortFilter + filters=FrontPortFilter, + pagination=True ) class FrontPortType(ModularComponentType, CabledObjectMixin): color: str @@ -381,7 +394,8 @@ class FrontPortType(ModularComponentType, CabledObjectMixin): @strawberry_django.type( models.FrontPortTemplate, fields='__all__', - filters=FrontPortTemplateFilter + filters=FrontPortTemplateFilter, + pagination=True ) class FrontPortTemplateType(ModularComponentTemplateType): color: str @@ -391,7 +405,8 @@ class FrontPortTemplateType(ModularComponentTemplateType): @strawberry_django.type( models.MACAddress, exclude=['assigned_object_type', 'assigned_object_id'], - filters=MACAddressFilter + filters=MACAddressFilter, + pagination=True ) class MACAddressType(NetBoxObjectType): mac_address: str @@ -407,7 +422,8 @@ class MACAddressType(NetBoxObjectType): @strawberry_django.type( models.Interface, exclude=['_path'], - filters=InterfaceFilter + filters=InterfaceFilter, + pagination=True ) class InterfaceType(IPAddressesMixin, ModularComponentType, CabledObjectMixin, PathEndpointMixin): _name: str @@ -434,7 +450,8 @@ class InterfaceType(IPAddressesMixin, ModularComponentType, CabledObjectMixin, P @strawberry_django.type( models.InterfaceTemplate, fields='__all__', - filters=InterfaceTemplateFilter + filters=InterfaceTemplateFilter, + pagination=True ) class InterfaceTemplateType(ModularComponentTemplateType): _name: str @@ -446,7 +463,8 @@ class InterfaceTemplateType(ModularComponentTemplateType): @strawberry_django.type( models.InventoryItem, exclude=['component_type', 'component_id', 'parent'], - filters=InventoryItemFilter + filters=InventoryItemFilter, + pagination=True ) class InventoryItemType(ComponentType): role: Annotated["InventoryItemRoleType", strawberry.lazy('dcim.graphql.types')] | None @@ -472,7 +490,8 @@ class InventoryItemType(ComponentType): @strawberry_django.type( models.InventoryItemRole, fields='__all__', - filters=InventoryItemRoleFilter + filters=InventoryItemRoleFilter, + pagination=True ) class InventoryItemRoleType(OrganizationalObjectType): color: str @@ -485,7 +504,8 @@ class InventoryItemRoleType(OrganizationalObjectType): models.Location, # fields='__all__', exclude=['parent'], # bug - temp - filters=LocationFilter + filters=LocationFilter, + pagination=True ) class LocationType(VLANGroupsMixin, ImageAttachmentsMixin, ContactsMixin, OrganizationalObjectType): site: Annotated["SiteType", strawberry.lazy('dcim.graphql.types')] @@ -512,7 +532,8 @@ class LocationType(VLANGroupsMixin, ImageAttachmentsMixin, ContactsMixin, Organi @strawberry_django.type( models.Manufacturer, fields='__all__', - filters=ManufacturerFilter + filters=ManufacturerFilter, + pagination=True ) class ManufacturerType(OrganizationalObjectType, ContactsMixin): @@ -526,7 +547,8 @@ class ManufacturerType(OrganizationalObjectType, ContactsMixin): @strawberry_django.type( models.Module, fields='__all__', - filters=ModuleFilter + filters=ModuleFilter, + pagination=True ) class ModuleType(NetBoxObjectType): device: Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')] @@ -546,7 +568,8 @@ class ModuleType(NetBoxObjectType): models.ModuleBay, # fields='__all__', exclude=['parent'], - filters=ModuleBayFilter + filters=ModuleBayFilter, + pagination=True ) class ModuleBayType(ModularComponentType): @@ -561,7 +584,8 @@ class ModuleBayType(ModularComponentType): @strawberry_django.type( models.ModuleBayTemplate, fields='__all__', - filters=ModuleBayTemplateFilter + filters=ModuleBayTemplateFilter, + pagination=True ) class ModuleBayTemplateType(ModularComponentTemplateType): pass @@ -570,7 +594,8 @@ class ModuleBayTemplateType(ModularComponentTemplateType): @strawberry_django.type( models.ModuleType, fields='__all__', - filters=ModuleTypeFilter + filters=ModuleTypeFilter, + pagination=True ) class ModuleTypeType(NetBoxObjectType): manufacturer: Annotated["ManufacturerType", strawberry.lazy('dcim.graphql.types')] @@ -588,7 +613,8 @@ class ModuleTypeType(NetBoxObjectType): @strawberry_django.type( models.Platform, fields='__all__', - filters=PlatformFilter + filters=PlatformFilter, + pagination=True ) class PlatformType(OrganizationalObjectType): manufacturer: Annotated["ManufacturerType", strawberry.lazy('dcim.graphql.types')] | None @@ -601,7 +627,8 @@ class PlatformType(OrganizationalObjectType): @strawberry_django.type( models.PowerFeed, exclude=['_path'], - filters=PowerFeedFilter + filters=PowerFeedFilter, + pagination=True ) class PowerFeedType(NetBoxObjectType, CabledObjectMixin, PathEndpointMixin): power_panel: Annotated["PowerPanelType", strawberry.lazy('dcim.graphql.types')] @@ -612,7 +639,8 @@ class PowerFeedType(NetBoxObjectType, CabledObjectMixin, PathEndpointMixin): @strawberry_django.type( models.PowerOutlet, exclude=['_path'], - filters=PowerOutletFilter + filters=PowerOutletFilter, + pagination=True ) class PowerOutletType(ModularComponentType, CabledObjectMixin, PathEndpointMixin): power_port: Annotated["PowerPortType", strawberry.lazy('dcim.graphql.types')] | None @@ -622,7 +650,8 @@ class PowerOutletType(ModularComponentType, CabledObjectMixin, PathEndpointMixin @strawberry_django.type( models.PowerOutletTemplate, fields='__all__', - filters=PowerOutletTemplateFilter + filters=PowerOutletTemplateFilter, + pagination=True ) class PowerOutletTemplateType(ModularComponentTemplateType): power_port: Annotated["PowerPortTemplateType", strawberry.lazy('dcim.graphql.types')] | None @@ -631,7 +660,8 @@ class PowerOutletTemplateType(ModularComponentTemplateType): @strawberry_django.type( models.PowerPanel, fields='__all__', - filters=PowerPanelFilter + filters=PowerPanelFilter, + pagination=True ) class PowerPanelType(NetBoxObjectType, ContactsMixin): site: Annotated["SiteType", strawberry.lazy('dcim.graphql.types')] @@ -643,7 +673,8 @@ class PowerPanelType(NetBoxObjectType, ContactsMixin): @strawberry_django.type( models.PowerPort, exclude=['_path'], - filters=PowerPortFilter + filters=PowerPortFilter, + pagination=True ) class PowerPortType(ModularComponentType, CabledObjectMixin, PathEndpointMixin): @@ -653,7 +684,8 @@ class PowerPortType(ModularComponentType, CabledObjectMixin, PathEndpointMixin): @strawberry_django.type( models.PowerPortTemplate, fields='__all__', - filters=PowerPortTemplateFilter + filters=PowerPortTemplateFilter, + pagination=True ) class PowerPortTemplateType(ModularComponentTemplateType): poweroutlet_templates: List[Annotated["PowerOutletTemplateType", strawberry.lazy('dcim.graphql.types')]] @@ -662,7 +694,8 @@ class PowerPortTemplateType(ModularComponentTemplateType): @strawberry_django.type( models.RackType, fields='__all__', - filters=RackTypeFilter + filters=RackTypeFilter, + pagination=True ) class RackTypeType(NetBoxObjectType): manufacturer: Annotated["ManufacturerType", strawberry.lazy('dcim.graphql.types')] @@ -671,7 +704,8 @@ class RackTypeType(NetBoxObjectType): @strawberry_django.type( models.Rack, fields='__all__', - filters=RackFilter + filters=RackFilter, + pagination=True ) class RackType(VLANGroupsMixin, ImageAttachmentsMixin, ContactsMixin, NetBoxObjectType): site: Annotated["SiteType", strawberry.lazy('dcim.graphql.types')] @@ -689,7 +723,8 @@ class RackType(VLANGroupsMixin, ImageAttachmentsMixin, ContactsMixin, NetBoxObje @strawberry_django.type( models.RackReservation, fields='__all__', - filters=RackReservationFilter + filters=RackReservationFilter, + pagination=True ) class RackReservationType(NetBoxObjectType): units: List[int] @@ -701,7 +736,8 @@ class RackReservationType(NetBoxObjectType): @strawberry_django.type( models.RackRole, fields='__all__', - filters=RackRoleFilter + filters=RackRoleFilter, + pagination=True ) class RackRoleType(OrganizationalObjectType): color: str @@ -712,7 +748,8 @@ class RackRoleType(OrganizationalObjectType): @strawberry_django.type( models.RearPort, fields='__all__', - filters=RearPortFilter + filters=RearPortFilter, + pagination=True ) class RearPortType(ModularComponentType, CabledObjectMixin): color: str @@ -723,7 +760,8 @@ class RearPortType(ModularComponentType, CabledObjectMixin): @strawberry_django.type( models.RearPortTemplate, fields='__all__', - filters=RearPortTemplateFilter + filters=RearPortTemplateFilter, + pagination=True ) class RearPortTemplateType(ModularComponentTemplateType): color: str @@ -734,7 +772,8 @@ class RearPortTemplateType(ModularComponentTemplateType): @strawberry_django.type( models.Region, exclude=['parent'], - filters=RegionFilter + filters=RegionFilter, + pagination=True ) class RegionType(VLANGroupsMixin, ContactsMixin, OrganizationalObjectType): @@ -759,7 +798,8 @@ class RegionType(VLANGroupsMixin, ContactsMixin, OrganizationalObjectType): @strawberry_django.type( models.Site, fields='__all__', - filters=SiteFilter + filters=SiteFilter, + pagination=True ) class SiteType(VLANGroupsMixin, ImageAttachmentsMixin, ContactsMixin, NetBoxObjectType): time_zone: str | None @@ -793,7 +833,8 @@ class SiteType(VLANGroupsMixin, ImageAttachmentsMixin, ContactsMixin, NetBoxObje @strawberry_django.type( models.SiteGroup, exclude=['parent'], # bug - temp - filters=SiteGroupFilter + filters=SiteGroupFilter, + pagination=True ) class SiteGroupType(VLANGroupsMixin, ContactsMixin, OrganizationalObjectType): @@ -818,7 +859,8 @@ class SiteGroupType(VLANGroupsMixin, ContactsMixin, OrganizationalObjectType): @strawberry_django.type( models.VirtualChassis, fields='__all__', - filters=VirtualChassisFilter + filters=VirtualChassisFilter, + pagination=True ) class VirtualChassisType(NetBoxObjectType): member_count: BigInt @@ -830,7 +872,8 @@ class VirtualChassisType(NetBoxObjectType): @strawberry_django.type( models.VirtualDeviceContext, fields='__all__', - filters=VirtualDeviceContextFilter + filters=VirtualDeviceContextFilter, + pagination=True ) class VirtualDeviceContextType(NetBoxObjectType): device: Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')] | None diff --git a/netbox/extras/graphql/types.py b/netbox/extras/graphql/types.py index 1ceb2682c..f4a1a397f 100644 --- a/netbox/extras/graphql/types.py +++ b/netbox/extras/graphql/types.py @@ -46,7 +46,8 @@ __all__ = ( @strawberry_django.type( models.ConfigContext, fields='__all__', - filters=ConfigContextFilter + filters=ConfigContextFilter, + pagination=True ) class ConfigContextType(ObjectType): data_source: Annotated["DataSourceType", strawberry.lazy('core.graphql.types')] | None @@ -69,7 +70,8 @@ class ConfigContextType(ObjectType): @strawberry_django.type( models.ConfigTemplate, fields='__all__', - filters=ConfigTemplateFilter + filters=ConfigTemplateFilter, + pagination=True ) class ConfigTemplateType(TagsMixin, ObjectType): data_source: Annotated["DataSourceType", strawberry.lazy('core.graphql.types')] | None @@ -84,7 +86,8 @@ class ConfigTemplateType(TagsMixin, ObjectType): @strawberry_django.type( models.CustomField, fields='__all__', - filters=CustomFieldFilter + filters=CustomFieldFilter, + pagination=True ) class CustomFieldType(ObjectType): related_object_type: Annotated["ContentTypeType", strawberry.lazy('netbox.graphql.types')] | None @@ -94,7 +97,8 @@ class CustomFieldType(ObjectType): @strawberry_django.type( models.CustomFieldChoiceSet, exclude=['extra_choices'], - filters=CustomFieldChoiceSetFilter + filters=CustomFieldChoiceSetFilter, + pagination=True ) class CustomFieldChoiceSetType(ObjectType): @@ -105,7 +109,8 @@ class CustomFieldChoiceSetType(ObjectType): @strawberry_django.type( models.CustomLink, fields='__all__', - filters=CustomLinkFilter + filters=CustomLinkFilter, + pagination=True ) class CustomLinkType(ObjectType): pass @@ -114,7 +119,8 @@ class CustomLinkType(ObjectType): @strawberry_django.type( models.ExportTemplate, fields='__all__', - filters=ExportTemplateFilter + filters=ExportTemplateFilter, + pagination=True ) class ExportTemplateType(ObjectType): data_source: Annotated["DataSourceType", strawberry.lazy('core.graphql.types')] | None @@ -124,7 +130,8 @@ class ExportTemplateType(ObjectType): @strawberry_django.type( models.ImageAttachment, fields='__all__', - filters=ImageAttachmentFilter + filters=ImageAttachmentFilter, + pagination=True ) class ImageAttachmentType(BaseObjectType): object_type: Annotated["ContentTypeType", strawberry.lazy('netbox.graphql.types')] | None @@ -133,7 +140,8 @@ class ImageAttachmentType(BaseObjectType): @strawberry_django.type( models.JournalEntry, fields='__all__', - filters=JournalEntryFilter + filters=JournalEntryFilter, + pagination=True ) class JournalEntryType(CustomFieldsMixin, TagsMixin, ObjectType): assigned_object_type: Annotated["ContentTypeType", strawberry.lazy('netbox.graphql.types')] | None @@ -143,6 +151,7 @@ class JournalEntryType(CustomFieldsMixin, TagsMixin, ObjectType): @strawberry_django.type( models.Notification, # filters=NotificationFilter + pagination=True ) class NotificationType(ObjectType): user: Annotated["UserType", strawberry.lazy('users.graphql.types')] | None @@ -150,7 +159,8 @@ class NotificationType(ObjectType): @strawberry_django.type( models.NotificationGroup, - filters=NotificationGroupFilter + filters=NotificationGroupFilter, + pagination=True ) class NotificationGroupType(ObjectType): users: List[Annotated["UserType", strawberry.lazy('users.graphql.types')]] @@ -160,7 +170,8 @@ class NotificationGroupType(ObjectType): @strawberry_django.type( models.SavedFilter, exclude=['content_types',], - filters=SavedFilterFilter + filters=SavedFilterFilter, + pagination=True ) class SavedFilterType(ObjectType): user: Annotated["UserType", strawberry.lazy('users.graphql.types')] | None @@ -169,6 +180,7 @@ class SavedFilterType(ObjectType): @strawberry_django.type( models.Subscription, # filters=NotificationFilter + pagination=True ) class SubscriptionType(ObjectType): user: Annotated["UserType", strawberry.lazy('users.graphql.types')] | None @@ -177,7 +189,8 @@ class SubscriptionType(ObjectType): @strawberry_django.type( models.Tag, exclude=['extras_taggeditem_items', ], - filters=TagFilter + filters=TagFilter, + pagination=True ) class TagType(ObjectType): color: str @@ -188,7 +201,8 @@ class TagType(ObjectType): @strawberry_django.type( models.Webhook, exclude=['content_types',], - filters=WebhookFilter + filters=WebhookFilter, + pagination=True ) class WebhookType(OrganizationalObjectType): pass @@ -197,7 +211,8 @@ class WebhookType(OrganizationalObjectType): @strawberry_django.type( models.EventRule, exclude=['content_types',], - filters=EventRuleFilter + filters=EventRuleFilter, + pagination=True ) class EventRuleType(OrganizationalObjectType): 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 e63bebcb1..638451f4a 100644 --- a/netbox/ipam/graphql/types.py +++ b/netbox/ipam/graphql/types.py @@ -70,7 +70,8 @@ class BaseIPAddressFamilyType: @strawberry_django.type( models.ASN, fields='__all__', - filters=ASNFilter + filters=ASNFilter, + pagination=True ) class ASNType(NetBoxObjectType): asn: BigInt @@ -84,7 +85,8 @@ class ASNType(NetBoxObjectType): @strawberry_django.type( models.ASNRange, fields='__all__', - filters=ASNRangeFilter + filters=ASNRangeFilter, + pagination=True ) class ASNRangeType(NetBoxObjectType): start: BigInt @@ -96,7 +98,8 @@ class ASNRangeType(NetBoxObjectType): @strawberry_django.type( models.Aggregate, fields='__all__', - filters=AggregateFilter + filters=AggregateFilter, + pagination=True ) class AggregateType(NetBoxObjectType, BaseIPAddressFamilyType): prefix: str @@ -107,7 +110,8 @@ class AggregateType(NetBoxObjectType, BaseIPAddressFamilyType): @strawberry_django.type( models.FHRPGroup, fields='__all__', - filters=FHRPGroupFilter + filters=FHRPGroupFilter, + pagination=True ) class FHRPGroupType(NetBoxObjectType, IPAddressesMixin): @@ -117,7 +121,8 @@ class FHRPGroupType(NetBoxObjectType, IPAddressesMixin): @strawberry_django.type( models.FHRPGroupAssignment, exclude=['interface_type', 'interface_id'], - filters=FHRPGroupAssignmentFilter + filters=FHRPGroupAssignmentFilter, + pagination=True ) class FHRPGroupAssignmentType(BaseObjectType): group: Annotated["FHRPGroupType", strawberry.lazy('ipam.graphql.types')] @@ -133,7 +138,8 @@ class FHRPGroupAssignmentType(BaseObjectType): @strawberry_django.type( models.IPAddress, exclude=['assigned_object_type', 'assigned_object_id', 'address'], - filters=IPAddressFilter + filters=IPAddressFilter, + pagination=True ) class IPAddressType(NetBoxObjectType, BaseIPAddressFamilyType): address: str @@ -157,7 +163,8 @@ class IPAddressType(NetBoxObjectType, BaseIPAddressFamilyType): @strawberry_django.type( models.IPRange, fields='__all__', - filters=IPRangeFilter + filters=IPRangeFilter, + pagination=True ) class IPRangeType(NetBoxObjectType): start_address: str @@ -170,7 +177,8 @@ class IPRangeType(NetBoxObjectType): @strawberry_django.type( models.Prefix, exclude=['scope_type', 'scope_id', '_location', '_region', '_site', '_site_group'], - filters=PrefixFilter + filters=PrefixFilter, + pagination=True ) class PrefixType(NetBoxObjectType, BaseIPAddressFamilyType): prefix: str @@ -192,7 +200,8 @@ class PrefixType(NetBoxObjectType, BaseIPAddressFamilyType): @strawberry_django.type( models.RIR, fields='__all__', - filters=RIRFilter + filters=RIRFilter, + pagination=True ) class RIRType(OrganizationalObjectType): @@ -204,7 +213,8 @@ class RIRType(OrganizationalObjectType): @strawberry_django.type( models.Role, fields='__all__', - filters=RoleFilter + filters=RoleFilter, + pagination=True ) class RoleType(OrganizationalObjectType): @@ -216,7 +226,8 @@ class RoleType(OrganizationalObjectType): @strawberry_django.type( models.RouteTarget, fields='__all__', - filters=RouteTargetFilter + filters=RouteTargetFilter, + pagination=True ) class RouteTargetType(NetBoxObjectType): tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None @@ -230,7 +241,8 @@ class RouteTargetType(NetBoxObjectType): @strawberry_django.type( models.Service, fields='__all__', - filters=ServiceFilter + filters=ServiceFilter, + pagination=True ) class ServiceType(NetBoxObjectType): ports: List[int] @@ -243,7 +255,8 @@ class ServiceType(NetBoxObjectType): @strawberry_django.type( models.ServiceTemplate, fields='__all__', - filters=ServiceTemplateFilter + filters=ServiceTemplateFilter, + pagination=True ) class ServiceTemplateType(NetBoxObjectType): ports: List[int] @@ -252,7 +265,8 @@ class ServiceTemplateType(NetBoxObjectType): @strawberry_django.type( models.VLAN, exclude=['qinq_svlan'], - filters=VLANFilter + filters=VLANFilter, + pagination=True ) class VLANType(NetBoxObjectType): site: Annotated["SiteType", strawberry.lazy('ipam.graphql.types')] | None @@ -275,7 +289,8 @@ class VLANType(NetBoxObjectType): @strawberry_django.type( models.VLANGroup, exclude=['scope_type', 'scope_id'], - filters=VLANGroupFilter + filters=VLANGroupFilter, + pagination=True ) class VLANGroupType(OrganizationalObjectType): @@ -299,7 +314,8 @@ class VLANGroupType(OrganizationalObjectType): @strawberry_django.type( models.VLANTranslationPolicy, fields='__all__', - filters=VLANTranslationPolicyFilter + filters=VLANTranslationPolicyFilter, + pagination=True ) class VLANTranslationPolicyType(NetBoxObjectType): rules: List[Annotated["VLANTranslationRuleType", strawberry.lazy('ipam.graphql.types')]] @@ -308,7 +324,8 @@ class VLANTranslationPolicyType(NetBoxObjectType): @strawberry_django.type( models.VLANTranslationRule, fields='__all__', - filters=VLANTranslationRuleFilter + filters=VLANTranslationRuleFilter, + pagination=True ) class VLANTranslationRuleType(NetBoxObjectType): policy: Annotated[ @@ -320,7 +337,8 @@ class VLANTranslationRuleType(NetBoxObjectType): @strawberry_django.type( models.VRF, fields='__all__', - filters=VRFFilter + filters=VRFFilter, + pagination=True ) class VRFType(NetBoxObjectType): tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None diff --git a/netbox/netbox/graphql/schema.py b/netbox/netbox/graphql/schema.py index a7609c9d2..c840e769c 100644 --- a/netbox/netbox/graphql/schema.py +++ b/netbox/netbox/graphql/schema.py @@ -1,7 +1,7 @@ import strawberry from django.conf import settings from strawberry_django.optimizer import DjangoOptimizerExtension -from strawberry.extensions import MaxAliasesLimiter +from strawberry.extensions import MaxAliasesLimiter # , SchemaExtension from strawberry.schema.config import StrawberryConfig from circuits.graphql.schema import CircuitsQuery diff --git a/netbox/netbox/graphql/types.py b/netbox/netbox/graphql/types.py index 5df4cfd38..653462630 100644 --- a/netbox/netbox/graphql/types.py +++ b/netbox/netbox/graphql/types.py @@ -84,6 +84,7 @@ class NetBoxObjectType( @strawberry_django.type( ContentType, fields=['id', 'app_label', 'model'], + pagination=True ) class ContentTypeType: pass @@ -92,6 +93,7 @@ class ContentTypeType: @strawberry_django.type( ObjectType_, fields=['id', 'app_label', 'model'], + pagination=True ) class ObjectTypeType: pass diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index 9f9b25689..43dcaeed2 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -798,6 +798,7 @@ LOCALE_PATHS = ( STRAWBERRY_DJANGO = { "DEFAULT_PK_FIELD_NAME": "id", "TYPE_DESCRIPTION_FROM_MODEL_DOCSTRING": True, + "PAGINATION_DEFAULT_LIMIT": 100, } # diff --git a/netbox/tenancy/graphql/types.py b/netbox/tenancy/graphql/types.py index b47ac2da3..1a33b44ab 100644 --- a/netbox/tenancy/graphql/types.py +++ b/netbox/tenancy/graphql/types.py @@ -52,7 +52,12 @@ __all__ = ( # -@strawberry_django.type(models.Tenant, fields='__all__', filters=TenantFilter) +@strawberry_django.type( + models.Tenant, + fields='__all__', + filters=TenantFilter, + pagination=True +) class TenantType(NetBoxObjectType): group: Annotated['TenantGroupType', strawberry.lazy('tenancy.graphql.types')] | None contacts: List[Annotated['ContactType', strawberry.lazy('tenancy.graphql.types')]] @@ -82,7 +87,12 @@ class TenantType(NetBoxObjectType): l2vpns: List[Annotated['L2VPNType', strawberry.lazy('vpn.graphql.types')]] -@strawberry_django.type(models.TenantGroup, fields='__all__', filters=TenantGroupFilter) +@strawberry_django.type( + models.TenantGroup, + fields='__all__', + filters=TenantGroupFilter, + pagination=True +) class TenantGroupType(OrganizationalObjectType): parent: Annotated['TenantGroupType', strawberry.lazy('tenancy.graphql.types')] | None @@ -95,17 +105,32 @@ class TenantGroupType(OrganizationalObjectType): # -@strawberry_django.type(models.Contact, fields='__all__', filters=ContactFilter) +@strawberry_django.type( + models.Contact, + fields='__all__', + filters=ContactFilter, + pagination=True +) class ContactType(ContactAssignmentsMixin, NetBoxObjectType): groups: List[Annotated['ContactGroupType', strawberry.lazy('tenancy.graphql.types')]] -@strawberry_django.type(models.ContactRole, fields='__all__', filters=ContactRoleFilter) +@strawberry_django.type( + models.ContactRole, + fields='__all__', + filters=ContactRoleFilter, + pagination=True +) class ContactRoleType(ContactAssignmentsMixin, OrganizationalObjectType): pass -@strawberry_django.type(models.ContactGroup, fields='__all__', filters=ContactGroupFilter) +@strawberry_django.type( + models.ContactGroup, + fields='__all__', + filters=ContactGroupFilter, + pagination=True +) class ContactGroupType(OrganizationalObjectType): parent: Annotated['ContactGroupType', strawberry.lazy('tenancy.graphql.types')] | None @@ -113,7 +138,12 @@ class ContactGroupType(OrganizationalObjectType): children: List[Annotated['ContactGroupType', strawberry.lazy('tenancy.graphql.types')]] -@strawberry_django.type(models.ContactAssignment, fields='__all__', filters=ContactAssignmentFilter) +@strawberry_django.type( + models.ContactAssignment, + fields='__all__', + filters=ContactAssignmentFilter, + pagination=True +) class ContactAssignmentType(CustomFieldsMixin, TagsMixin, BaseObjectType): object_type: Annotated['ContentTypeType', strawberry.lazy('netbox.graphql.types')] | None contact: Annotated['ContactType', strawberry.lazy('tenancy.graphql.types')] | None diff --git a/netbox/users/graphql/types.py b/netbox/users/graphql/types.py index 526bf6e21..c5b338553 100644 --- a/netbox/users/graphql/types.py +++ b/netbox/users/graphql/types.py @@ -15,7 +15,8 @@ __all__ = ( @strawberry_django.type( Group, fields=['id', 'name'], - filters=GroupFilter + filters=GroupFilter, + pagination=True ) class GroupType(BaseObjectType): pass @@ -26,7 +27,8 @@ class GroupType(BaseObjectType): fields=[ 'id', 'username', 'first_name', 'last_name', 'email', 'is_staff', 'is_active', 'date_joined', 'groups', ], - filters=UserFilter + filters=UserFilter, + pagination=True ) class UserType(BaseObjectType): groups: List[GroupType] diff --git a/netbox/virtualization/graphql/types.py b/netbox/virtualization/graphql/types.py index 2fcffc20f..cfffa85e2 100644 --- a/netbox/virtualization/graphql/types.py +++ b/netbox/virtualization/graphql/types.py @@ -46,7 +46,8 @@ class ComponentType(NetBoxObjectType): @strawberry_django.type( models.Cluster, exclude=['scope_type', 'scope_id', '_location', '_region', '_site', '_site_group'], - filters=ClusterFilter + filters=ClusterFilter, + pagination=True ) class ClusterType(VLANGroupsMixin, NetBoxObjectType): type: Annotated["ClusterTypeType", strawberry.lazy('virtualization.graphql.types')] | None @@ -68,7 +69,8 @@ class ClusterType(VLANGroupsMixin, NetBoxObjectType): @strawberry_django.type( models.ClusterGroup, fields='__all__', - filters=ClusterGroupFilter + filters=ClusterGroupFilter, + pagination=True ) class ClusterGroupType(VLANGroupsMixin, OrganizationalObjectType): @@ -78,7 +80,8 @@ class ClusterGroupType(VLANGroupsMixin, OrganizationalObjectType): @strawberry_django.type( models.ClusterType, fields='__all__', - filters=ClusterTypeFilter + filters=ClusterTypeFilter, + pagination=True ) class ClusterTypeType(OrganizationalObjectType): @@ -88,7 +91,8 @@ class ClusterTypeType(OrganizationalObjectType): @strawberry_django.type( models.VirtualMachine, fields='__all__', - filters=VirtualMachineFilter + filters=VirtualMachineFilter, + pagination=True ) class VirtualMachineType(ConfigContextMixin, ContactsMixin, NetBoxObjectType): interface_count: BigInt @@ -112,7 +116,8 @@ class VirtualMachineType(ConfigContextMixin, ContactsMixin, NetBoxObjectType): @strawberry_django.type( models.VMInterface, fields='__all__', - filters=VMInterfaceFilter + filters=VMInterfaceFilter, + pagination=True ) class VMInterfaceType(IPAddressesMixin, ComponentType): _name: str @@ -134,7 +139,8 @@ class VMInterfaceType(IPAddressesMixin, ComponentType): @strawberry_django.type( models.VirtualDisk, fields='__all__', - filters=VirtualDiskFilter + filters=VirtualDiskFilter, + pagination=True ) class VirtualDiskType(ComponentType): pass diff --git a/netbox/vpn/graphql/types.py b/netbox/vpn/graphql/types.py index cc133ee6f..bbf84dd16 100644 --- a/netbox/vpn/graphql/types.py +++ b/netbox/vpn/graphql/types.py @@ -32,7 +32,8 @@ __all__ = ( @strawberry_django.type( models.TunnelGroup, fields='__all__', - filters=TunnelGroupFilter + filters=TunnelGroupFilter, + pagination=True ) class TunnelGroupType(OrganizationalObjectType): @@ -42,7 +43,8 @@ class TunnelGroupType(OrganizationalObjectType): @strawberry_django.type( models.TunnelTermination, fields='__all__', - filters=TunnelTerminationFilter + filters=TunnelTerminationFilter, + pagination=True ) class TunnelTerminationType(CustomFieldsMixin, TagsMixin, ObjectType): tunnel: Annotated["TunnelType", strawberry.lazy('vpn.graphql.types')] @@ -53,7 +55,8 @@ class TunnelTerminationType(CustomFieldsMixin, TagsMixin, ObjectType): @strawberry_django.type( models.Tunnel, fields='__all__', - filters=TunnelFilter + filters=TunnelFilter, + pagination=True ) class TunnelType(NetBoxObjectType): group: Annotated["TunnelGroupType", strawberry.lazy('vpn.graphql.types')] | None @@ -66,7 +69,8 @@ class TunnelType(NetBoxObjectType): @strawberry_django.type( models.IKEProposal, fields='__all__', - filters=IKEProposalFilter + filters=IKEProposalFilter, + pagination=True ) class IKEProposalType(OrganizationalObjectType): @@ -76,7 +80,8 @@ class IKEProposalType(OrganizationalObjectType): @strawberry_django.type( models.IKEPolicy, fields='__all__', - filters=IKEPolicyFilter + filters=IKEPolicyFilter, + pagination=True ) class IKEPolicyType(OrganizationalObjectType): @@ -87,7 +92,8 @@ class IKEPolicyType(OrganizationalObjectType): @strawberry_django.type( models.IPSecProposal, fields='__all__', - filters=IPSecProposalFilter + filters=IPSecProposalFilter, + pagination=True ) class IPSecProposalType(OrganizationalObjectType): @@ -97,7 +103,8 @@ class IPSecProposalType(OrganizationalObjectType): @strawberry_django.type( models.IPSecPolicy, fields='__all__', - filters=IPSecPolicyFilter + filters=IPSecPolicyFilter, + pagination=True ) class IPSecPolicyType(OrganizationalObjectType): @@ -108,7 +115,8 @@ class IPSecPolicyType(OrganizationalObjectType): @strawberry_django.type( models.IPSecProfile, fields='__all__', - filters=IPSecProfileFilter + filters=IPSecProfileFilter, + pagination=True ) class IPSecProfileType(OrganizationalObjectType): ike_policy: Annotated["IKEPolicyType", strawberry.lazy('vpn.graphql.types')] @@ -120,7 +128,8 @@ class IPSecProfileType(OrganizationalObjectType): @strawberry_django.type( models.L2VPN, fields='__all__', - filters=L2VPNFilter + filters=L2VPNFilter, + pagination=True ) class L2VPNType(ContactsMixin, NetBoxObjectType): tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None @@ -133,7 +142,8 @@ class L2VPNType(ContactsMixin, NetBoxObjectType): @strawberry_django.type( models.L2VPNTermination, exclude=['assigned_object_type', 'assigned_object_id'], - filters=L2VPNTerminationFilter + filters=L2VPNTerminationFilter, + pagination=True ) class L2VPNTerminationType(NetBoxObjectType): l2vpn: Annotated["L2VPNType", strawberry.lazy('vpn.graphql.types')] diff --git a/netbox/wireless/graphql/types.py b/netbox/wireless/graphql/types.py index 7c31dfec8..eeca6a82b 100644 --- a/netbox/wireless/graphql/types.py +++ b/netbox/wireless/graphql/types.py @@ -22,7 +22,8 @@ __all__ = ( @strawberry_django.type( models.WirelessLANGroup, fields='__all__', - filters=WirelessLANGroupFilter + filters=WirelessLANGroupFilter, + pagination=True ) class WirelessLANGroupType(OrganizationalObjectType): parent: Annotated["WirelessLANGroupType", strawberry.lazy('wireless.graphql.types')] | None @@ -34,7 +35,8 @@ class WirelessLANGroupType(OrganizationalObjectType): @strawberry_django.type( models.WirelessLAN, exclude=['scope_type', 'scope_id', '_location', '_region', '_site', '_site_group'], - filters=WirelessLANFilter + filters=WirelessLANFilter, + pagination=True ) class WirelessLANType(NetBoxObjectType): group: Annotated["WirelessLANGroupType", strawberry.lazy('wireless.graphql.types')] | None @@ -56,7 +58,8 @@ class WirelessLANType(NetBoxObjectType): @strawberry_django.type( models.WirelessLink, fields='__all__', - filters=WirelessLinkFilter + filters=WirelessLinkFilter, + pagination=True ) class WirelessLinkType(NetBoxObjectType): interface_a: Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')]