|
@@ -2,11 +2,12 @@ from typing import TYPE_CHECKING, Annotated
|
|
|
|
|
|
|
|
import strawberry
|
|
import strawberry
|
|
|
import strawberry_django
|
|
import strawberry_django
|
|
|
-from django.db.models import Func, IntegerField
|
|
|
|
|
|
|
+from django.db.models import Func, IntegerField, Prefetch
|
|
|
|
|
|
|
|
from circuits.models import CircuitTermination
|
|
from circuits.models import CircuitTermination
|
|
|
from core.graphql.mixins import ChangelogMixin
|
|
from core.graphql.mixins import ChangelogMixin
|
|
|
from dcim import models
|
|
from dcim import models
|
|
|
|
|
+from dcim.choices import CableEndChoices
|
|
|
from extras.graphql.mixins import ConfigContextMixin, ContactsMixin, ImageAttachmentsMixin
|
|
from extras.graphql.mixins import ConfigContextMixin, ContactsMixin, ImageAttachmentsMixin
|
|
|
from ipam.graphql.mixins import IPAddressesMixin, VLANGroupsMixin
|
|
from ipam.graphql.mixins import IPAddressesMixin, VLANGroupsMixin
|
|
|
from netbox.graphql.optimization import build_gfk_prefetch
|
|
from netbox.graphql.optimization import build_gfk_prefetch
|
|
@@ -94,6 +95,57 @@ __all__ = (
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+#
|
|
|
|
|
+# Cable termination prefetching
|
|
|
|
|
+#
|
|
|
|
|
+
|
|
|
|
|
+# The concrete models which may terminate a cable, mirroring dcim.constants.CABLE_TERMINATION_MODELS
|
|
|
|
|
+_CABLE_TERMINATION_MODELS = (
|
|
|
|
|
+ CircuitTermination,
|
|
|
|
|
+ models.ConsolePort,
|
|
|
|
|
+ models.ConsoleServerPort,
|
|
|
|
|
+ models.FrontPort,
|
|
|
|
|
+ models.Interface,
|
|
|
|
|
+ models.PowerFeed,
|
|
|
|
|
+ models.PowerOutlet,
|
|
|
|
|
+ models.PowerPort,
|
|
|
|
|
+ models.RearPort,
|
|
|
|
|
+)
|
|
|
|
|
+
|
|
|
|
|
+_termination_gfk_prefetch = build_gfk_prefetch('termination', _CABLE_TERMINATION_MODELS)
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def _cable_terminations_prefetch(side, to_attr):
|
|
|
|
|
+ """
|
|
|
|
|
+ Return a callable which builds a selection-aware Prefetch of a cable's terminations for the
|
|
|
|
|
+ given cable end.
|
|
|
|
|
+
|
|
|
|
|
+ Each end is prefetched under its own `to_attr`: two prefetches of the same relation cannot be
|
|
|
|
|
+ merged by the query optimizer, so a shared lookup would break any query selecting both ends.
|
|
|
|
|
+ """
|
|
|
|
|
+ def prefetch(info):
|
|
|
|
|
+ return Prefetch(
|
|
|
|
|
+ 'terminations',
|
|
|
|
|
+ queryset=models.CableTermination.objects.filter(cable_end=side).prefetch_related(
|
|
|
|
|
+ _termination_gfk_prefetch(info)
|
|
|
|
|
+ ),
|
|
|
|
|
+ to_attr=to_attr,
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ return prefetch
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def _resolve_cable_terminations(cable, side, to_attr):
|
|
|
|
|
+ """
|
|
|
|
|
+ Return the terminating objects for the given cable end, using the prefetched terminations if
|
|
|
|
|
+ available and falling back to the model property otherwise.
|
|
|
|
|
+ """
|
|
|
|
|
+ if (terminations := getattr(cable, to_attr, None)) is not None:
|
|
|
|
|
+ return [ct.termination for ct in terminations]
|
|
|
|
|
+
|
|
|
|
|
+ return cable._get_x_terminations(side)
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
#
|
|
#
|
|
|
# Base types
|
|
# Base types
|
|
|
#
|
|
#
|
|
@@ -153,20 +205,7 @@ class CableTerminationType(NetBoxObjectType):
|
|
|
cable: Annotated['CableType', strawberry.lazy('dcim.graphql.types')] | None
|
|
cable: Annotated['CableType', strawberry.lazy('dcim.graphql.types')] | None
|
|
|
|
|
|
|
|
@strawberry_django.field(
|
|
@strawberry_django.field(
|
|
|
- prefetch_related=build_gfk_prefetch(
|
|
|
|
|
- 'termination',
|
|
|
|
|
- [
|
|
|
|
|
- CircuitTermination,
|
|
|
|
|
- models.ConsolePort,
|
|
|
|
|
- models.ConsoleServerPort,
|
|
|
|
|
- models.FrontPort,
|
|
|
|
|
- models.Interface,
|
|
|
|
|
- models.PowerFeed,
|
|
|
|
|
- models.PowerOutlet,
|
|
|
|
|
- models.PowerPort,
|
|
|
|
|
- models.RearPort,
|
|
|
|
|
- ],
|
|
|
|
|
- ),
|
|
|
|
|
|
|
+ prefetch_related=_termination_gfk_prefetch,
|
|
|
only=['termination_type', 'termination_id'],
|
|
only=['termination_type', 'termination_id'],
|
|
|
)
|
|
)
|
|
|
def termination(self) -> Annotated[
|
|
def termination(self) -> Annotated[
|
|
@@ -197,7 +236,10 @@ class CableType(PrimaryObjectType):
|
|
|
|
|
|
|
|
terminations: list[CableTerminationType]
|
|
terminations: list[CableTerminationType]
|
|
|
|
|
|
|
|
- a_terminations: list[Annotated[
|
|
|
|
|
|
|
+ @strawberry_django.field(
|
|
|
|
|
+ prefetch_related=_cable_terminations_prefetch(CableEndChoices.SIDE_A, '_prefetched_a_terminations'),
|
|
|
|
|
+ )
|
|
|
|
|
+ def a_terminations(self) -> list[Annotated[
|
|
|
Annotated['CircuitTerminationType', strawberry.lazy('circuits.graphql.types')]
|
|
Annotated['CircuitTerminationType', strawberry.lazy('circuits.graphql.types')]
|
|
|
| Annotated['ConsolePortType', strawberry.lazy('dcim.graphql.types')]
|
|
| Annotated['ConsolePortType', strawberry.lazy('dcim.graphql.types')]
|
|
|
| Annotated['ConsoleServerPortType', strawberry.lazy('dcim.graphql.types')]
|
|
| Annotated['ConsoleServerPortType', strawberry.lazy('dcim.graphql.types')]
|
|
@@ -208,9 +250,13 @@ class CableType(PrimaryObjectType):
|
|
|
| Annotated['PowerPortType', strawberry.lazy('dcim.graphql.types')]
|
|
| Annotated['PowerPortType', strawberry.lazy('dcim.graphql.types')]
|
|
|
| Annotated['RearPortType', strawberry.lazy('dcim.graphql.types')],
|
|
| Annotated['RearPortType', strawberry.lazy('dcim.graphql.types')],
|
|
|
strawberry.union('CableTerminationTerminationType'),
|
|
strawberry.union('CableTerminationTerminationType'),
|
|
|
- ]]
|
|
|
|
|
|
|
+ ]]:
|
|
|
|
|
+ return _resolve_cable_terminations(self, CableEndChoices.SIDE_A, '_prefetched_a_terminations')
|
|
|
|
|
|
|
|
- b_terminations: list[Annotated[
|
|
|
|
|
|
|
+ @strawberry_django.field(
|
|
|
|
|
+ prefetch_related=_cable_terminations_prefetch(CableEndChoices.SIDE_B, '_prefetched_b_terminations'),
|
|
|
|
|
+ )
|
|
|
|
|
+ def b_terminations(self) -> list[Annotated[
|
|
|
Annotated['CircuitTerminationType', strawberry.lazy('circuits.graphql.types')]
|
|
Annotated['CircuitTerminationType', strawberry.lazy('circuits.graphql.types')]
|
|
|
| Annotated['ConsolePortType', strawberry.lazy('dcim.graphql.types')]
|
|
| Annotated['ConsolePortType', strawberry.lazy('dcim.graphql.types')]
|
|
|
| Annotated['ConsoleServerPortType', strawberry.lazy('dcim.graphql.types')]
|
|
| Annotated['ConsoleServerPortType', strawberry.lazy('dcim.graphql.types')]
|
|
@@ -221,7 +267,8 @@ class CableType(PrimaryObjectType):
|
|
|
| Annotated['PowerPortType', strawberry.lazy('dcim.graphql.types')]
|
|
| Annotated['PowerPortType', strawberry.lazy('dcim.graphql.types')]
|
|
|
| Annotated['RearPortType', strawberry.lazy('dcim.graphql.types')],
|
|
| Annotated['RearPortType', strawberry.lazy('dcim.graphql.types')],
|
|
|
strawberry.union('CableTerminationTerminationType'),
|
|
strawberry.union('CableTerminationTerminationType'),
|
|
|
- ]]
|
|
|
|
|
|
|
+ ]]:
|
|
|
|
|
+ return _resolve_cable_terminations(self, CableEndChoices.SIDE_B, '_prefetched_b_terminations')
|
|
|
|
|
|
|
|
|
|
|
|
|
@strawberry_django.type(
|
|
@strawberry_django.type(
|