15154 merge feature

This commit is contained in:
Arthur 2024-03-25 09:38:18 -07:00
commit 269b7c2254
80 changed files with 97894 additions and 2402 deletions

View File

@ -14,10 +14,6 @@ django-debug-toolbar
# https://github.com/carltongibson/django-filter/blob/main/CHANGES.rst # https://github.com/carltongibson/django-filter/blob/main/CHANGES.rst
django-filter django-filter
# Django debug toolbar extension with support for GraphiQL
# https://github.com/flavors/django-graphiql-debug-toolbar/blob/main/CHANGES.rst
django-graphiql-debug-toolbar
# HTMX utilities for Django # HTMX utilities for Django
# https://django-htmx.readthedocs.io/en/latest/changelog.html # https://django-htmx.readthedocs.io/en/latest/changelog.html
django-htmx django-htmx
@ -75,11 +71,6 @@ drf-spectacular-sidecar
# https://github.com/kurtmckee/feedparser/blob/develop/CHANGELOG.rst # https://github.com/kurtmckee/feedparser/blob/develop/CHANGELOG.rst
feedparser feedparser
# Django wrapper for Graphene (GraphQL support)
# https://github.com/graphql-python/graphene-django/releases
# Pinned to v3.0.0 for GraphiQL UI issue (see #12762)
graphene_django==3.0.0
# Platform-agnostic template rendering engine # Platform-agnostic template rendering engine
# https://jinja.palletsprojects.com/changes/ # https://jinja.palletsprojects.com/changes/
Jinja2 Jinja2
@ -136,8 +127,16 @@ social-auth-core
# https://github.com/python-social-auth/social-app-django/blob/master/CHANGELOG.md # https://github.com/python-social-auth/social-app-django/blob/master/CHANGELOG.md
social-auth-app-django social-auth-app-django
# Strawberry GraphQL
# https://github.com/strawberry-graphql/strawberry/blob/main/CHANGELOG.md
strawberry-graphql
# Strawberry GraphQL Django extension
# https://github.com/strawberry-graphql/strawberry-django/blob/main/CHANGELOG.md
strawberry-graphql-django
# SVG image rendering (used for rack elevations) # SVG image rendering (used for rack elevations)
# hhttps://github.com/mozman/svgwrite/blob/master/NEWS.rst # https://github.com/mozman/svgwrite/blob/master/NEWS.rst
svgwrite svgwrite
# Tabular dataset library (for table-based exports) # Tabular dataset library (for table-based exports)

View File

@ -8,23 +8,32 @@ A plugin can extend NetBox's GraphQL API by registering its own schema class. By
```python ```python
# graphql.py # graphql.py
import graphene from typing import List
from netbox.graphql.types import NetBoxObjectType import strawberry
from netbox.graphql.fields import ObjectField, ObjectListField import strawberry_django
from . import filtersets, models
class MyModelType(NetBoxObjectType): from . import models
class Meta:
model = models.MyModel
fields = '__all__'
filterset_class = filtersets.MyModelFilterSet
class MyQuery(graphene.ObjectType): @strawberry_django.type(
mymodel = ObjectField(MyModelType) models.MyModel,
mymodel_list = ObjectListField(MyModelType) fields='__all__',
)
class MyModelType:
pass
schema = MyQuery
@strawberry.type
class MyQuery:
@strawberry.field
def dummymodel(self, id: int) -> DummyModelType:
return None
dummymodel_list: List[DummyModelType] = strawberry_django.field()
schema = [
MyQuery,
]
``` ```
## GraphQL Objects ## GraphQL Objects
@ -38,15 +47,3 @@ NetBox provides two object type classes for use by plugins.
::: netbox.graphql.types.NetBoxObjectType ::: netbox.graphql.types.NetBoxObjectType
options: options:
members: false members: false
## GraphQL Fields
NetBox provides two field classes for use by plugins.
::: netbox.graphql.fields.ObjectField
options:
members: false
::: netbox.graphql.fields.ObjectListField
options:
members: false

View File

@ -0,0 +1,50 @@
import strawberry
import strawberry_django
from circuits import filtersets, models
from netbox.graphql.filter_mixins import autotype_decorator, BaseFilterMixin
__all__ = (
'CircuitTerminationFilter',
'CircuitFilter',
'CircuitTypeFilter',
'ProviderFilter',
'ProviderAccountFilter',
'ProviderNetworkFilter',
)
@strawberry_django.filter(models.CircuitTermination, lookups=True)
@autotype_decorator(filtersets.CircuitTerminationFilterSet)
class CircuitTerminationFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.Circuit, lookups=True)
@autotype_decorator(filtersets.CircuitFilterSet)
class CircuitFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.CircuitType, lookups=True)
@autotype_decorator(filtersets.CircuitTypeFilterSet)
class CircuitTypeFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.Provider, lookups=True)
@autotype_decorator(filtersets.ProviderFilterSet)
class ProviderFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.ProviderAccount, lookups=True)
@autotype_decorator(filtersets.ProviderAccountFilterSet)
class ProviderAccountFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.ProviderNetwork, lookups=True)
@autotype_decorator(filtersets.ProviderNetworkFilterSet)
class ProviderNetworkFilter(BaseFilterMixin):
pass

View File

@ -1,41 +1,40 @@
import graphene from typing import List
import strawberry
import strawberry_django
from circuits import models from circuits import models
from netbox.graphql.fields import ObjectField, ObjectListField
from .types import * from .types import *
from utilities.graphql_optimizer import gql_query_optimizer
class CircuitsQuery(graphene.ObjectType): @strawberry.type
circuit = ObjectField(CircuitType) class CircuitsQuery:
circuit_list = ObjectListField(CircuitType) @strawberry.field
def circuit(self, id: int) -> CircuitType:
return models.Circuit.objects.get(pk=id)
circuit_list: List[CircuitType] = strawberry_django.field()
def resolve_circuit_list(root, info, **kwargs): @strawberry.field
return gql_query_optimizer(models.Circuit.objects.all(), info) def circuit_termination(self, id: int) -> CircuitTerminationType:
return models.CircuitTermination.objects.get(pk=id)
circuit_termination_list: List[CircuitTerminationType] = strawberry_django.field()
circuit_termination = ObjectField(CircuitTerminationType) @strawberry.field
circuit_termination_list = ObjectListField(CircuitTerminationType) def circuit_type(self, id: int) -> CircuitTypeType:
return models.CircuitType.objects.get(pk=id)
circuit_type_list: List[CircuitTypeType] = strawberry_django.field()
def resolve_circuit_termination_list(root, info, **kwargs): @strawberry.field
return gql_query_optimizer(models.CircuitTermination.objects.all(), info) def provider(self, id: int) -> ProviderType:
return models.Provider.objects.get(pk=id)
provider_list: List[ProviderType] = strawberry_django.field()
circuit_type = ObjectField(CircuitTypeType) @strawberry.field
circuit_type_list = ObjectListField(CircuitTypeType) def provider_account(self, id: int) -> ProviderAccountType:
return models.ProviderAccount.objects.get(pk=id)
provider_account_list: List[ProviderAccountType] = strawberry_django.field()
def resolve_circuit_type_list(root, info, **kwargs): @strawberry.field
return gql_query_optimizer(models.CircuitType.objects.all(), info) def provider_network(self, id: int) -> ProviderNetworkType:
return models.ProviderNetwork.objects.get(pk=id)
provider = ObjectField(ProviderType) provider_network_list: List[ProviderNetworkType] = strawberry_django.field()
provider_list = ObjectListField(ProviderType)
def resolve_provider_list(root, info, **kwargs):
return gql_query_optimizer(models.Provider.objects.all(), info)
provider_account = ObjectField(ProviderAccountType)
provider_account_list = ObjectListField(ProviderAccountType)
provider_network = ObjectField(ProviderNetworkType)
provider_network_list = ObjectListField(ProviderNetworkType)
def resolve_provider_network_list(root, info, **kwargs):
return gql_query_optimizer(models.ProviderNetwork.objects.all(), info)

View File

@ -1,9 +1,14 @@
import graphene from typing import Annotated, List
from circuits import filtersets, models import strawberry
import strawberry_django
from circuits import models
from dcim.graphql.mixins import CabledObjectMixin from dcim.graphql.mixins import CabledObjectMixin
from extras.graphql.mixins import CustomFieldsMixin, TagsMixin, ContactsMixin from extras.graphql.mixins import ContactsMixin, CustomFieldsMixin, TagsMixin
from netbox.graphql.types import ObjectType, OrganizationalObjectType, NetBoxObjectType from netbox.graphql.types import NetBoxObjectType, ObjectType, OrganizationalObjectType
from tenancy.graphql.types import TenantType
from .filters import *
__all__ = ( __all__ = (
'CircuitTerminationType', 'CircuitTerminationType',
@ -15,48 +20,93 @@ __all__ = (
) )
class CircuitTerminationType(CustomFieldsMixin, TagsMixin, CabledObjectMixin, ObjectType): @strawberry_django.type(
models.Provider,
class Meta: fields='__all__',
model = models.CircuitTermination filters=ProviderFilter
fields = '__all__' )
filterset_class = filtersets.CircuitTerminationFilterSet
class CircuitType(NetBoxObjectType, ContactsMixin):
class Meta:
model = models.Circuit
fields = '__all__'
filterset_class = filtersets.CircuitFilterSet
class CircuitTypeType(OrganizationalObjectType):
class Meta:
model = models.CircuitType
fields = '__all__'
filterset_class = filtersets.CircuitTypeFilterSet
class ProviderType(NetBoxObjectType, ContactsMixin): class ProviderType(NetBoxObjectType, ContactsMixin):
class Meta: @strawberry_django.field
model = models.Provider def networks(self) -> List[Annotated["ProviderNetworkType", strawberry.lazy('circuits.graphql.types')]]:
fields = '__all__' return self.networks.all()
filterset_class = filtersets.ProviderFilterSet
@strawberry_django.field
def circuits(self) -> List[Annotated["CircuitType", strawberry.lazy('circuits.graphql.types')]]:
return self.circuits.all()
@strawberry_django.field
def asns(self) -> List[Annotated["ASNType", strawberry.lazy('ipam.graphql.types')]]:
return self.asns.all()
@strawberry_django.field
def accounts(self) -> List[Annotated["ProviderAccountType", strawberry.lazy('circuits.graphql.types')]]:
return self.accounts.all()
@strawberry_django.type(
models.ProviderAccount,
fields='__all__',
filters=ProviderAccountFilter
)
class ProviderAccountType(NetBoxObjectType): class ProviderAccountType(NetBoxObjectType):
provider: Annotated["ProviderType", strawberry.lazy('circuits.graphql.types')]
class Meta: @strawberry_django.field
model = models.ProviderAccount def circuits(self) -> List[Annotated["CircuitType", strawberry.lazy('circuits.graphql.types')]]:
fields = '__all__' return self.circuits.all()
filterset_class = filtersets.ProviderAccountFilterSet
@strawberry_django.type(
models.ProviderNetwork,
fields='__all__',
filters=ProviderNetworkFilter
)
class ProviderNetworkType(NetBoxObjectType): class ProviderNetworkType(NetBoxObjectType):
provider: Annotated["ProviderType", strawberry.lazy('circuits.graphql.types')]
class Meta: @strawberry_django.field
model = models.ProviderNetwork def circuit_terminations(self) -> List[Annotated["CircuitTerminationType", strawberry.lazy('circuits.graphql.types')]]:
fields = '__all__' return self.circuit_terminations.all()
filterset_class = filtersets.ProviderNetworkFilterSet
@strawberry_django.type(
models.CircuitTermination,
fields='__all__',
filters=CircuitTerminationFilter
)
class CircuitTerminationType(CustomFieldsMixin, TagsMixin, CabledObjectMixin, ObjectType):
circuit: Annotated["CircuitType", strawberry.lazy('circuits.graphql.types')]
provider_network: Annotated["ProviderNetworkType", strawberry.lazy('circuits.graphql.types')] | None
site: Annotated["SiteType", strawberry.lazy('dcim.graphql.types')] | None
@strawberry_django.type(
models.CircuitType,
fields='__all__',
filters=CircuitTypeFilter
)
class CircuitTypeType(OrganizationalObjectType):
color: str
@strawberry_django.field
def circuits(self) -> List[Annotated["CircuitType", strawberry.lazy('circuits.graphql.types')]]:
return self.circuits.all()
@strawberry_django.type(
models.Circuit,
fields='__all__',
filters=CircuitFilter
)
class CircuitType(NetBoxObjectType, ContactsMixin):
provider: ProviderType
provider_account: ProviderAccountType | None
termination_a: CircuitTerminationType | None
termination_z: CircuitTerminationType | None
type: CircuitTypeType
tenant: TenantType | None
@strawberry_django.field
def terminations(self) -> List[CircuitTerminationType]:
return self.terminations.all()

View File

@ -0,0 +1,21 @@
import strawberry_django
from core import filtersets, models
from netbox.graphql.filter_mixins import autotype_decorator, BaseFilterMixin
__all__ = (
'DataFileFilter',
'DataSourceFilter',
)
@strawberry_django.filter(models.DataFile, lookups=True)
@autotype_decorator(filtersets.DataFileFilterSet)
class DataFileFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.DataSource, lookups=True)
@autotype_decorator(filtersets.DataSourceFilterSet)
class DataSourceFilter(BaseFilterMixin):
pass

View File

@ -1,20 +1,20 @@
import graphene from typing import List
import strawberry
import strawberry_django
from core import models from core import models
from netbox.graphql.fields import ObjectField, ObjectListField
from .types import * from .types import *
from utilities.graphql_optimizer import gql_query_optimizer
class CoreQuery(graphene.ObjectType): @strawberry.type
data_file = ObjectField(DataFileType) class CoreQuery:
data_file_list = ObjectListField(DataFileType) @strawberry.field
def data_file(self, id: int) -> DataFileType:
return models.DataFile.objects.get(pk=id)
data_file_list: List[DataFileType] = strawberry_django.field()
def resolve_data_file_list(root, info, **kwargs): @strawberry.field
return gql_query_optimizer(models.DataFile.objects.all(), info) def data_source(self, id: int) -> DataSourceType:
return models.DataSource.objects.get(pk=id)
data_source = ObjectField(DataSourceType) data_source_list: List[DataSourceType] = strawberry_django.field()
data_source_list = ObjectListField(DataSourceType)
def resolve_data_source_list(root, info, **kwargs):
return gql_query_optimizer(models.DataSource.objects.all(), info)

View File

@ -1,5 +1,11 @@
from core import filtersets, models from typing import Annotated, List
import strawberry
import strawberry_django
from core import models
from netbox.graphql.types import BaseObjectType, NetBoxObjectType from netbox.graphql.types import BaseObjectType, NetBoxObjectType
from .filters import *
__all__ = ( __all__ = (
'DataFileType', 'DataFileType',
@ -7,15 +13,22 @@ __all__ = (
) )
@strawberry_django.type(
models.DataFile,
exclude=['data',],
filters=DataFileFilter
)
class DataFileType(BaseObjectType): class DataFileType(BaseObjectType):
class Meta: source: Annotated["DataSourceType", strawberry.lazy('core.graphql.types')]
model = models.DataFile
exclude = ('data',)
filterset_class = filtersets.DataFileFilterSet
@strawberry_django.type(
models.DataSource,
fields='__all__',
filters=DataSourceFilter
)
class DataSourceType(NetBoxObjectType): class DataSourceType(NetBoxObjectType):
class Meta:
model = models.DataSource @strawberry_django.field
fields = '__all__' def datafiles(self) -> List[Annotated["DataFileType", strawberry.lazy('core.graphql.types')]]:
filterset_class = filtersets.DataSourceFilterSet return self.datafiles.all()

View File

@ -0,0 +1,294 @@
import strawberry_django
from dcim import filtersets, models
from netbox.graphql.filter_mixins import autotype_decorator, BaseFilterMixin
__all__ = (
'CableFilter',
'CableTerminationFilter',
'ConsolePortFilter',
'ConsolePortTemplateFilter',
'ConsoleServerPortFilter',
'ConsoleServerPortTemplateFilter',
'DeviceFilter',
'DeviceBayFilter',
'DeviceBayTemplateFilter',
'InventoryItemTemplateFilter',
'DeviceRoleFilter',
'DeviceTypeFilter',
'FrontPortFilter',
'FrontPortTemplateFilter',
'InterfaceFilter',
'InterfaceTemplateFilter',
'InventoryItemFilter',
'InventoryItemRoleFilter',
'LocationFilter',
'ManufacturerFilter',
'ModuleFilter',
'ModuleBayFilter',
'ModuleBayTemplateFilter',
'ModuleTypeFilter',
'PlatformFilter',
'PowerFeedFilter',
'PowerOutletFilter',
'PowerOutletTemplateFilter',
'PowerPanelFilter',
'PowerPortFilter',
'PowerPortTemplateFilter',
'RackFilter',
'RackReservationFilter',
'RackRoleFilter',
'RearPortFilter',
'RearPortTemplateFilter',
'RegionFilter',
'SiteFilter',
'SiteGroupFilter',
'VirtualChassisFilter',
'VirtualDeviceContextFilter',
)
@strawberry_django.filter(models.Cable, lookups=True)
@autotype_decorator(filtersets.CableFilterSet)
class CableFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.CableTermination, lookups=True)
@autotype_decorator(filtersets.CableTerminationFilterSet)
class CableTerminationFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.ConsolePort, lookups=True)
@autotype_decorator(filtersets.ConsolePortFilterSet)
class ConsolePortFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.ConsolePortTemplate, lookups=True)
@autotype_decorator(filtersets.ConsolePortTemplateFilterSet)
class ConsolePortTemplateFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.ConsoleServerPort, lookups=True)
@autotype_decorator(filtersets.ConsoleServerPortFilterSet)
class ConsoleServerPortFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.ConsoleServerPortTemplate, lookups=True)
@autotype_decorator(filtersets.ConsoleServerPortTemplateFilterSet)
class ConsoleServerPortTemplateFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.Device, lookups=True)
@autotype_decorator(filtersets.DeviceFilterSet)
class DeviceFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.DeviceBay, lookups=True)
@autotype_decorator(filtersets.DeviceBayFilterSet)
class DeviceBayFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.DeviceBayTemplate, lookups=True)
@autotype_decorator(filtersets.DeviceBayTemplateFilterSet)
class DeviceBayTemplateFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.InventoryItemTemplate, lookups=True)
@autotype_decorator(filtersets.InventoryItemTemplateFilterSet)
class InventoryItemTemplateFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.DeviceRole, lookups=True)
@autotype_decorator(filtersets.DeviceRoleFilterSet)
class DeviceRoleFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.DeviceType, lookups=True)
@autotype_decorator(filtersets.DeviceTypeFilterSet)
class DeviceTypeFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.FrontPort, lookups=True)
@autotype_decorator(filtersets.FrontPortFilterSet)
class FrontPortFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.FrontPortTemplate, lookups=True)
@autotype_decorator(filtersets.FrontPortTemplateFilterSet)
class FrontPortTemplateFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.Interface, lookups=True)
@autotype_decorator(filtersets.InterfaceFilterSet)
class InterfaceFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.InterfaceTemplate, lookups=True)
@autotype_decorator(filtersets.InterfaceTemplateFilterSet)
class InterfaceTemplateFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.InventoryItem, lookups=True)
@autotype_decorator(filtersets.InventoryItemFilterSet)
class InventoryItemFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.InventoryItemRole, lookups=True)
@autotype_decorator(filtersets.InventoryItemRoleFilterSet)
class InventoryItemRoleFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.Location, lookups=True)
@autotype_decorator(filtersets.LocationFilterSet)
class LocationFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.Manufacturer, lookups=True)
@autotype_decorator(filtersets.ManufacturerFilterSet)
class ManufacturerFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.Module, lookups=True)
@autotype_decorator(filtersets.ModuleFilterSet)
class ModuleFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.ModuleBay, lookups=True)
@autotype_decorator(filtersets.ModuleBayFilterSet)
class ModuleBayFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.ModuleBayTemplate, lookups=True)
@autotype_decorator(filtersets.ModuleBayTemplateFilterSet)
class ModuleBayTemplateFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.ModuleType, lookups=True)
@autotype_decorator(filtersets.ModuleTypeFilterSet)
class ModuleTypeFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.Platform, lookups=True)
@autotype_decorator(filtersets.PlatformFilterSet)
class PlatformFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.PowerFeed, lookups=True)
@autotype_decorator(filtersets.PowerFeedFilterSet)
class PowerFeedFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.PowerOutlet, lookups=True)
@autotype_decorator(filtersets.PowerOutletFilterSet)
class PowerOutletFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.PowerOutletTemplate, lookups=True)
@autotype_decorator(filtersets.PowerOutletTemplateFilterSet)
class PowerOutletTemplateFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.PowerPanel, lookups=True)
@autotype_decorator(filtersets.PowerPanelFilterSet)
class PowerPanelFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.PowerPort, lookups=True)
@autotype_decorator(filtersets.PowerPortFilterSet)
class PowerPortFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.PowerPortTemplate, lookups=True)
@autotype_decorator(filtersets.PowerPortTemplateFilterSet)
class PowerPortTemplateFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.Rack, lookups=True)
@autotype_decorator(filtersets.RackFilterSet)
class RackFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.RackReservation, lookups=True)
@autotype_decorator(filtersets.RackReservationFilterSet)
class RackReservationFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.RackRole, lookups=True)
@autotype_decorator(filtersets.RackRoleFilterSet)
class RackRoleFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.RearPort, lookups=True)
@autotype_decorator(filtersets.RearPortFilterSet)
class RearPortFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.RearPortTemplate, lookups=True)
@autotype_decorator(filtersets.RearPortTemplateFilterSet)
class RearPortTemplateFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.Region, lookups=True)
@autotype_decorator(filtersets.RegionFilterSet)
class RegionFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.Site, lookups=True)
@autotype_decorator(filtersets.SiteFilterSet)
class SiteFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.SiteGroup, lookups=True)
@autotype_decorator(filtersets.SiteGroupFilterSet)
class SiteGroupFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.VirtualChassis, lookups=True)
@autotype_decorator(filtersets.VirtualChassisFilterSet)
class VirtualChassisFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.VirtualDeviceContext, lookups=True)
@autotype_decorator(filtersets.VirtualDeviceContextFilterSet)
class VirtualDeviceContextFilter(BaseFilterMixin):
pass

View File

@ -1,4 +1,3 @@
import graphene
from circuits.graphql.types import CircuitTerminationType, ProviderNetworkType from circuits.graphql.types import CircuitTerminationType, ProviderNetworkType
from circuits.models import CircuitTermination, ProviderNetwork from circuits.models import CircuitTermination, ProviderNetwork
from dcim.graphql.types import ( from dcim.graphql.types import (
@ -37,79 +36,7 @@ from dcim.models import (
) )
class LinkPeerType(graphene.Union): class InventoryItemTemplateComponentType:
class Meta:
types = (
CircuitTerminationType,
ConsolePortType,
ConsoleServerPortType,
FrontPortType,
InterfaceType,
PowerFeedType,
PowerOutletType,
PowerPortType,
RearPortType,
)
@classmethod
def resolve_type(cls, instance, info):
if type(instance) is CircuitTermination:
return CircuitTerminationType
if type(instance) is ConsolePortType:
return ConsolePortType
if type(instance) is ConsoleServerPort:
return ConsoleServerPortType
if type(instance) is FrontPort:
return FrontPortType
if type(instance) is Interface:
return InterfaceType
if type(instance) is PowerFeed:
return PowerFeedType
if type(instance) is PowerOutlet:
return PowerOutletType
if type(instance) is PowerPort:
return PowerPortType
if type(instance) is RearPort:
return RearPortType
class CableTerminationTerminationType(graphene.Union):
class Meta:
types = (
CircuitTerminationType,
ConsolePortType,
ConsoleServerPortType,
FrontPortType,
InterfaceType,
PowerFeedType,
PowerOutletType,
PowerPortType,
RearPortType,
)
@classmethod
def resolve_type(cls, instance, info):
if type(instance) is CircuitTermination:
return CircuitTerminationType
if type(instance) is ConsolePortType:
return ConsolePortType
if type(instance) is ConsoleServerPort:
return ConsoleServerPortType
if type(instance) is FrontPort:
return FrontPortType
if type(instance) is Interface:
return InterfaceType
if type(instance) is PowerFeed:
return PowerFeedType
if type(instance) is PowerOutlet:
return PowerOutletType
if type(instance) is PowerPort:
return PowerPortType
if type(instance) is RearPort:
return RearPortType
class InventoryItemTemplateComponentType(graphene.Union):
class Meta: class Meta:
types = ( types = (
ConsolePortTemplateType, ConsolePortTemplateType,
@ -139,7 +66,7 @@ class InventoryItemTemplateComponentType(graphene.Union):
return RearPortTemplateType return RearPortTemplateType
class InventoryItemComponentType(graphene.Union): class InventoryItemComponentType:
class Meta: class Meta:
types = ( types = (
ConsolePortType, ConsolePortType,
@ -169,7 +96,7 @@ class InventoryItemComponentType(graphene.Union):
return RearPortType return RearPortType
class ConnectedEndpointType(graphene.Union): class ConnectedEndpointType:
class Meta: class Meta:
types = ( types = (
CircuitTerminationType, CircuitTerminationType,

View File

@ -1,20 +1,47 @@
import graphene from typing import Annotated, List, Union
import strawberry
import strawberry_django
__all__ = (
'CabledObjectMixin',
'PathEndpointMixin',
)
@strawberry.type
class CabledObjectMixin: class CabledObjectMixin:
link_peers = graphene.List('dcim.graphql.gfk_mixins.LinkPeerType') cable: Annotated["CableType", strawberry.lazy('dcim.graphql.types')] | None
def resolve_cable_end(self, info): @strawberry_django.field
# Handle empty values def link_peers(self) -> List[Annotated[Union[
return self.cable_end or None Annotated["CircuitTerminationType", strawberry.lazy('circuits.graphql.types')],
Annotated["ConsolePortType", strawberry.lazy('dcim.graphql.types')],
def resolve_link_peers(self, info): Annotated["ConsoleServerPortType", strawberry.lazy('dcim.graphql.types')],
Annotated["FrontPortType", strawberry.lazy('dcim.graphql.types')],
Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')],
Annotated["PowerFeedType", strawberry.lazy('dcim.graphql.types')],
Annotated["PowerOutletType", strawberry.lazy('dcim.graphql.types')],
Annotated["PowerPortType", strawberry.lazy('dcim.graphql.types')],
Annotated["RearPortType", strawberry.lazy('dcim.graphql.types')],
], strawberry.union("LinkPeerType")]]:
return self.link_peers return self.link_peers
@strawberry.type
class PathEndpointMixin: class PathEndpointMixin:
connected_endpoints = graphene.List('dcim.graphql.gfk_mixins.ConnectedEndpointType')
def resolve_connected_endpoints(self, info): @strawberry_django.field
# Handle empty values def connected_endpoints(self) -> List[Annotated[Union[
Annotated["CircuitTerminationType", strawberry.lazy('circuits.graphql.types')],
Annotated["ConsolePortType", strawberry.lazy('dcim.graphql.types')],
Annotated["ConsoleServerPortType", strawberry.lazy('dcim.graphql.types')],
Annotated["FrontPortType", strawberry.lazy('dcim.graphql.types')],
Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')],
Annotated["PowerFeedType", strawberry.lazy('dcim.graphql.types')],
Annotated["PowerOutletType", strawberry.lazy('dcim.graphql.types')],
Annotated["PowerPortType", strawberry.lazy('dcim.graphql.types')],
Annotated["ProviderNetworkType", strawberry.lazy('circuits.graphql.types')],
Annotated["RearPortType", strawberry.lazy('dcim.graphql.types')],
], strawberry.union("ConnectedEndpointType")]]:
return self.connected_endpoints or None return self.connected_endpoints or None

View File

@ -1,249 +1,210 @@
import graphene from typing import List
import strawberry
import strawberry_django
from netbox.graphql.fields import ObjectField, ObjectListField
from .types import *
from dcim import models from dcim import models
from .types import VirtualDeviceContextType from .types import *
from utilities.graphql_optimizer import gql_query_optimizer
@strawberry.type
class DCIMQuery(graphene.ObjectType): class DCIMQuery:
cable = ObjectField(CableType) @strawberry.field
cable_list = ObjectListField(CableType) def cable(self, id: int) -> CableType:
return models.Cable.objects.get(pk=id)
def resolve_cable_list(root, info, **kwargs): cable_list: List[CableType] = strawberry_django.field()
return gql_query_optimizer(models.Cable.objects.all(), info)
@strawberry.field
console_port = ObjectField(ConsolePortType) def console_port(self, id: int) -> ConsolePortType:
console_port_list = ObjectListField(ConsolePortType) return models.ConsolePort.objects.get(pk=id)
console_port_list: List[ConsolePortType] = strawberry_django.field()
def resolve_console_port_list(root, info, **kwargs):
return gql_query_optimizer(models.ConsolePort.objects.all(), info) @strawberry.field
def console_port_template(self, id: int) -> ConsolePortTemplateType:
console_port_template = ObjectField(ConsolePortTemplateType) return models.ConsolePortTemplate.objects.get(pk=id)
console_port_template_list = ObjectListField(ConsolePortTemplateType) console_port_template_list: List[ConsolePortTemplateType] = strawberry_django.field()
def resolve_console_port_template_list(root, info, **kwargs): @strawberry.field
return gql_query_optimizer(models.ConsolePortTemplate.objects.all(), info) def console_server_port(self, id: int) -> ConsoleServerPortType:
return models.ConsoleServerPort.objects.get(pk=id)
console_server_port = ObjectField(ConsoleServerPortType) console_server_port_list: List[ConsoleServerPortType] = strawberry_django.field()
console_server_port_list = ObjectListField(ConsoleServerPortType)
@strawberry.field
def resolve_console_server_port_list(root, info, **kwargs): def console_server_port_template(self, id: int) -> ConsoleServerPortTemplateType:
return gql_query_optimizer(models.ConsoleServerPort.objects.all(), info) return models.ConsoleServerPortTemplate.objects.get(pk=id)
console_server_port_template_list: List[ConsoleServerPortTemplateType] = strawberry_django.field()
console_server_port_template = ObjectField(ConsoleServerPortTemplateType)
console_server_port_template_list = ObjectListField(ConsoleServerPortTemplateType) @strawberry.field
def device(self, id: int) -> DeviceType:
def resolve_console_server_port_template_list(root, info, **kwargs): return models.Device.objects.get(pk=id)
return gql_query_optimizer(models.ConsoleServerPortTemplate.objects.all(), info) device_list: List[DeviceType] = strawberry_django.field()
device = ObjectField(DeviceType) @strawberry.field
device_list = ObjectListField(DeviceType) def device_bay(self, id: int) -> DeviceBayType:
return models.DeviceBay.objects.get(pk=id)
def resolve_device_list(root, info, **kwargs): device_bay_list: List[DeviceBayType] = strawberry_django.field()
return gql_query_optimizer(models.Device.objects.all(), info)
@strawberry.field
device_bay = ObjectField(DeviceBayType) def device_bay_template(self, id: int) -> DeviceBayTemplateType:
device_bay_list = ObjectListField(DeviceBayType) return models.DeviceBayTemplate.objects.get(pk=id)
device_bay_template_list: List[DeviceBayTemplateType] = strawberry_django.field()
def resolve_device_bay_list(root, info, **kwargs):
return gql_query_optimizer(models.DeviceBay.objects.all(), info) @strawberry.field
def device_role(self, id: int) -> DeviceRoleType:
device_bay_template = ObjectField(DeviceBayTemplateType) return models.DeviceRole.objects.get(pk=id)
device_bay_template_list = ObjectListField(DeviceBayTemplateType) device_role_list: List[DeviceRoleType] = strawberry_django.field()
def resolve_device_bay_template_list(root, info, **kwargs): @strawberry.field
return gql_query_optimizer(models.DeviceBayTemplate.objects.all(), info) def device_type(self, id: int) -> DeviceTypeType:
return models.DeviceType.objects.get(pk=id)
device_role = ObjectField(DeviceRoleType) device_type_list: List[DeviceTypeType] = strawberry_django.field()
device_role_list = ObjectListField(DeviceRoleType)
@strawberry.field
def resolve_device_role_list(root, info, **kwargs): def front_port(self, id: int) -> FrontPortType:
return gql_query_optimizer(models.DeviceRole.objects.all(), info) return models.FrontPort.objects.get(pk=id)
front_port_list: List[FrontPortType] = strawberry_django.field()
device_type = ObjectField(DeviceTypeType)
device_type_list = ObjectListField(DeviceTypeType) @strawberry.field
def front_port_template(self, id: int) -> FrontPortTemplateType:
def resolve_device_type_list(root, info, **kwargs): return models.FrontPortTemplate.objects.get(pk=id)
return gql_query_optimizer(models.DeviceType.objects.all(), info) front_port_template_list: List[FrontPortTemplateType] = strawberry_django.field()
front_port = ObjectField(FrontPortType) @strawberry.field
front_port_list = ObjectListField(FrontPortType) def interface(self, id: int) -> InterfaceType:
return models.Interface.objects.get(pk=id)
def resolve_front_port_list(root, info, **kwargs): interface_list: List[InterfaceType] = strawberry_django.field()
return gql_query_optimizer(models.FrontPort.objects.all(), info)
@strawberry.field
front_port_template = ObjectField(FrontPortTemplateType) def interface_template(self, id: int) -> InterfaceTemplateType:
front_port_template_list = ObjectListField(FrontPortTemplateType) return models.InterfaceTemplate.objects.get(pk=id)
interface_template_list: List[InterfaceTemplateType] = strawberry_django.field()
def resolve_front_port_template_list(root, info, **kwargs):
return gql_query_optimizer(models.FrontPortTemplate.objects.all(), info) @strawberry.field
def inventory_item(self, id: int) -> InventoryItemType:
interface = ObjectField(InterfaceType) return models.InventoryItem.objects.get(pk=id)
interface_list = ObjectListField(InterfaceType) inventory_item_list: List[InventoryItemType] = strawberry_django.field()
def resolve_interface_list(root, info, **kwargs): @strawberry.field
return gql_query_optimizer(models.Interface.objects.all(), info) def inventory_item_role(self, id: int) -> InventoryItemRoleType:
return models.InventoryItemRole.objects.get(pk=id)
interface_template = ObjectField(InterfaceTemplateType) inventory_item_role_list: List[InventoryItemRoleType] = strawberry_django.field()
interface_template_list = ObjectListField(InterfaceTemplateType)
@strawberry.field
def resolve_interface_template_list(root, info, **kwargs): def inventory_item_template(self, id: int) -> InventoryItemTemplateType:
return gql_query_optimizer(models.InterfaceTemplate.objects.all(), info) return models.InventoryItemTemplate.objects.get(pk=id)
inventory_item_template_list: List[InventoryItemTemplateType] = strawberry_django.field()
inventory_item = ObjectField(InventoryItemType)
inventory_item_list = ObjectListField(InventoryItemType) @strawberry.field
def location(self, id: int) -> LocationType:
def resolve_inventory_item_list(root, info, **kwargs): return models.Location.objects.get(pk=id)
return gql_query_optimizer(models.InventoryItem.objects.all(), info) location_list: List[LocationType] = strawberry_django.field()
inventory_item_role = ObjectField(InventoryItemRoleType) @strawberry.field
inventory_item_role_list = ObjectListField(InventoryItemRoleType) def manufacturer(self, id: int) -> ManufacturerType:
return models.Manufacturer.objects.get(pk=id)
def resolve_inventory_item_role_list(root, info, **kwargs): manufacturer_list: List[ManufacturerType] = strawberry_django.field()
return gql_query_optimizer(models.InventoryItemRole.objects.all(), info)
@strawberry.field
inventory_item_template = ObjectField(InventoryItemTemplateType) def module(self, id: int) -> ModuleType:
inventory_item_template_list = ObjectListField(InventoryItemTemplateType) return models.Module.objects.get(pk=id)
module_list: List[ModuleType] = strawberry_django.field()
def resolve_inventory_item_template_list(root, info, **kwargs):
return gql_query_optimizer(models.InventoryItemTemplate.objects.all(), info) @strawberry.field
def module_bay(self, id: int) -> ModuleBayType:
location = ObjectField(LocationType) return models.ModuleBay.objects.get(pk=id)
location_list = ObjectListField(LocationType) module_bay_list: List[ModuleBayType] = strawberry_django.field()
def resolve_location_list(root, info, **kwargs): @strawberry.field
return gql_query_optimizer(models.Location.objects.all(), info) def module_bay_template(self, id: int) -> ModuleBayTemplateType:
return models.ModuleBayTemplate.objects.get(pk=id)
manufacturer = ObjectField(ManufacturerType) module_bay_template_list: List[ModuleBayTemplateType] = strawberry_django.field()
manufacturer_list = ObjectListField(ManufacturerType)
@strawberry.field
def resolve_manufacturer_list(root, info, **kwargs): def module_type(self, id: int) -> ModuleTypeType:
return gql_query_optimizer(models.Manufacturer.objects.all(), info) return models.ModuleType.objects.get(pk=id)
module_type_list: List[ModuleTypeType] = strawberry_django.field()
module = ObjectField(ModuleType)
module_list = ObjectListField(ModuleType) @strawberry.field
def platform(self, id: int) -> PlatformType:
def resolve_module_list(root, info, **kwargs): return models.Platform.objects.get(pk=id)
return gql_query_optimizer(models.Module.objects.all(), info) platform_list: List[PlatformType] = strawberry_django.field()
module_bay = ObjectField(ModuleBayType) @strawberry.field
module_bay_list = ObjectListField(ModuleBayType) def power_feed(self, id: int) -> PowerFeedType:
return models.PowerFeed.objects.get(pk=id)
def resolve_module_bay_list(root, info, **kwargs): power_feed_list: List[PowerFeedType] = strawberry_django.field()
return gql_query_optimizer(models.ModuleBay.objects.all(), info)
@strawberry.field
module_bay_template = ObjectField(ModuleBayTemplateType) def power_outlet(self, id: int) -> PowerOutletType:
module_bay_template_list = ObjectListField(ModuleBayTemplateType) return models.PowerOutlet.objects.get(pk=id)
power_outlet_list: List[PowerOutletType] = strawberry_django.field()
def resolve_module_bay_template_list(root, info, **kwargs):
return gql_query_optimizer(models.ModuleBayTemplate.objects.all(), info) @strawberry.field
def power_outlet_template(self, id: int) -> PowerOutletTemplateType:
module_type = ObjectField(ModuleTypeType) return models.PowerOutletTemplate.objects.get(pk=id)
module_type_list = ObjectListField(ModuleTypeType) power_outlet_template_list: List[PowerOutletTemplateType] = strawberry_django.field()
def resolve_module_type_list(root, info, **kwargs): @strawberry.field
return gql_query_optimizer(models.ModuleType.objects.all(), info) def power_panel(self, id: int) -> PowerPanelType:
return models.PowerPanel.objects.get(id=id)
platform = ObjectField(PlatformType) power_panel_list: List[PowerPanelType] = strawberry_django.field()
platform_list = ObjectListField(PlatformType)
@strawberry.field
def resolve_platform_list(root, info, **kwargs): def power_port(self, id: int) -> PowerPortType:
return gql_query_optimizer(models.Platform.objects.all(), info) return models.PowerPort.objects.get(id=id)
power_port_list: List[PowerPortType] = strawberry_django.field()
power_feed = ObjectField(PowerFeedType)
power_feed_list = ObjectListField(PowerFeedType) @strawberry.field
def power_port_template(self, id: int) -> PowerPortTemplateType:
def resolve_power_feed_list(root, info, **kwargs): return models.PowerPortTemplate.objects.get(id=id)
return gql_query_optimizer(models.PowerFeed.objects.all(), info) power_port_template_list: List[PowerPortTemplateType] = strawberry_django.field()
power_outlet = ObjectField(PowerOutletType) @strawberry.field
power_outlet_list = ObjectListField(PowerOutletType) def rack(self, id: int) -> RackType:
return models.Rack.objects.get(id=id)
def resolve_power_outlet_list(root, info, **kwargs): rack_list: List[RackType] = strawberry_django.field()
return gql_query_optimizer(models.PowerOutlet.objects.all(), info)
@strawberry.field
power_outlet_template = ObjectField(PowerOutletTemplateType) def rack_reservation(self, id: int) -> RackReservationType:
power_outlet_template_list = ObjectListField(PowerOutletTemplateType) return models.RackReservation.objects.get(id=id)
rack_reservation_list: List[RackReservationType] = strawberry_django.field()
def resolve_power_outlet_template_list(root, info, **kwargs):
return gql_query_optimizer(models.PowerOutletTemplate.objects.all(), info) @strawberry.field
def rack_role(self, id: int) -> RackRoleType:
power_panel = ObjectField(PowerPanelType) return models.RackRole.objects.get(id=id)
power_panel_list = ObjectListField(PowerPanelType) rack_role_list: List[RackRoleType] = strawberry_django.field()
def resolve_power_panel_list(root, info, **kwargs): @strawberry.field
return gql_query_optimizer(models.PowerPanel.objects.all(), info) def rear_port(self, id: int) -> RearPortType:
return models.RearPort.objects.get(id=id)
power_port = ObjectField(PowerPortType) rear_port_list: List[RearPortType] = strawberry_django.field()
power_port_list = ObjectListField(PowerPortType)
@strawberry.field
def resolve_power_port_list(root, info, **kwargs): def rear_port_template(self, id: int) -> RearPortTemplateType:
return gql_query_optimizer(models.PowerPort.objects.all(), info) return models.RearPortTemplate.objects.get(id=id)
rear_port_template_list: List[RearPortTemplateType] = strawberry_django.field()
power_port_template = ObjectField(PowerPortTemplateType)
power_port_template_list = ObjectListField(PowerPortTemplateType) @strawberry.field
def region(self, id: int) -> RegionType:
def resolve_power_port_template_list(root, info, **kwargs): return models.Region.objects.get(id=id)
return gql_query_optimizer(models.PowerPortTemplate.objects.all(), info) region_list: List[RegionType] = strawberry_django.field()
rack = ObjectField(RackType) @strawberry.field
rack_list = ObjectListField(RackType) def site(self, id: int) -> SiteType:
return models.Site.objects.get(id=id)
def resolve_rack_list(root, info, **kwargs): site_list: List[SiteType] = strawberry_django.field()
return gql_query_optimizer(models.Rack.objects.all(), info)
@strawberry.field
rack_reservation = ObjectField(RackReservationType) def site_group(self, id: int) -> SiteGroupType:
rack_reservation_list = ObjectListField(RackReservationType) return models.SiteGroup.objects.get(id=id)
site_group_list: List[SiteGroupType] = strawberry_django.field()
def resolve_rack_reservation_list(root, info, **kwargs):
return gql_query_optimizer(models.RackReservation.objects.all(), info) @strawberry.field
def virtual_chassis(self, id: int) -> VirtualChassisType:
rack_role = ObjectField(RackRoleType) return models.VirtualChassis.objects.get(id=id)
rack_role_list = ObjectListField(RackRoleType) virtual_chassis_list: List[VirtualChassisType] = strawberry_django.field()
def resolve_rack_role_list(root, info, **kwargs): @strawberry.field
return gql_query_optimizer(models.RackRole.objects.all(), info) def virtual_device_context(self, id: int) -> VirtualDeviceContextType:
return models.VirtualDeviceContext.objects.get(id=id)
rear_port = ObjectField(RearPortType) virtual_device_context_list: List[VirtualDeviceContextType] = strawberry_django.field()
rear_port_list = ObjectListField(RearPortType)
def resolve_rear_port_list(root, info, **kwargs):
return gql_query_optimizer(models.RearPort.objects.all(), info)
rear_port_template = ObjectField(RearPortTemplateType)
rear_port_template_list = ObjectListField(RearPortTemplateType)
def resolve_rear_port_template_list(root, info, **kwargs):
return gql_query_optimizer(models.RearPortTemplate.objects.all(), info)
region = ObjectField(RegionType)
region_list = ObjectListField(RegionType)
def resolve_region_list(root, info, **kwargs):
return gql_query_optimizer(models.Region.objects.all(), info)
site = ObjectField(SiteType)
site_list = ObjectListField(SiteType)
def resolve_site_list(root, info, **kwargs):
return gql_query_optimizer(models.Site.objects.all(), info)
site_group = ObjectField(SiteGroupType)
site_group_list = ObjectListField(SiteGroupType)
def resolve_site_group_list(root, info, **kwargs):
return gql_query_optimizer(models.SiteGroup.objects.all(), info)
virtual_chassis = ObjectField(VirtualChassisType)
virtual_chassis_list = ObjectListField(VirtualChassisType)
def resolve_virtual_chassis_list(root, info, **kwargs):
return gql_query_optimizer(models.VirtualChassis.objects.all(), info)
virtual_device_context = ObjectField(VirtualDeviceContextType)
virtual_device_context_list = ObjectListField(VirtualDeviceContextType)
def resolve_virtual_device_context_list(root, info, **kwargs):
return gql_query_optimizer(models.VirtualDeviceContext.objects.all(), info)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,98 @@
import strawberry_django
from extras import filtersets, models
from netbox.graphql.filter_mixins import autotype_decorator, BaseFilterMixin
__all__ = (
'ConfigContextFilter',
'ConfigTemplateFilter',
'CustomFieldFilter',
'CustomFieldChoiceSetFilter',
'CustomLinkFilter',
'EventRuleFilter',
'ExportTemplateFilter',
'ImageAttachmentFilter',
'JournalEntryFilter',
'ObjectChangeFilter',
'SavedFilterFilter',
'TagFilter',
'WebhookFilter',
)
@strawberry_django.filter(models.ConfigContext, lookups=True)
@autotype_decorator(filtersets.ConfigContextFilterSet)
class ConfigContextFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.ConfigTemplate, lookups=True)
@autotype_decorator(filtersets.ConfigTemplateFilterSet)
class ConfigTemplateFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.CustomField, lookups=True)
@autotype_decorator(filtersets.CustomFieldFilterSet)
class CustomFieldFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.CustomFieldChoiceSet, lookups=True)
@autotype_decorator(filtersets.CustomFieldChoiceSetFilterSet)
class CustomFieldChoiceSetFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.CustomLink, lookups=True)
@autotype_decorator(filtersets.CustomLinkFilterSet)
class CustomLinkFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.ExportTemplate, lookups=True)
@autotype_decorator(filtersets.ExportTemplateFilterSet)
class ExportTemplateFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.ImageAttachment, lookups=True)
@autotype_decorator(filtersets.ImageAttachmentFilterSet)
class ImageAttachmentFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.JournalEntry, lookups=True)
@autotype_decorator(filtersets.JournalEntryFilterSet)
class JournalEntryFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.ObjectChange, lookups=True)
@autotype_decorator(filtersets.ObjectChangeFilterSet)
class ObjectChangeFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.SavedFilter, lookups=True)
@autotype_decorator(filtersets.SavedFilterFilterSet)
class SavedFilterFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.Tag, lookups=True)
@autotype_decorator(filtersets.TagFilterSet)
class TagFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.Webhook, lookups=True)
@autotype_decorator(filtersets.WebhookFilterSet)
class WebhookFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.EventRule, lookups=True)
@autotype_decorator(filtersets.EventRuleFilterSet)
class EventRuleFilter(BaseFilterMixin):
pass

View File

@ -1,6 +1,8 @@
import graphene from typing import TYPE_CHECKING, Annotated, List
import strawberry
import strawberry_django
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from graphene.types.generic import GenericScalar
from extras.models import ObjectChange from extras.models import ObjectChange
@ -14,56 +16,67 @@ __all__ = (
'TagsMixin', 'TagsMixin',
) )
if TYPE_CHECKING:
from .types import ImageAttachmentType, JournalEntryType, ObjectChangeType, TagType
from tenancy.graphql.types import ContactAssignmentType
@strawberry.type
class ChangelogMixin: class ChangelogMixin:
changelog = graphene.List('extras.graphql.types.ObjectChangeType')
def resolve_changelog(self, info): @strawberry_django.field
def changelog(self, info) -> List[Annotated["ObjectChangeType", strawberry.lazy('.types')]]:
content_type = ContentType.objects.get_for_model(self) content_type = ContentType.objects.get_for_model(self)
object_changes = ObjectChange.objects.filter( object_changes = ObjectChange.objects.filter(
changed_object_type=content_type, changed_object_type=content_type,
changed_object_id=self.pk changed_object_id=self.pk
) )
return object_changes.restrict(info.context.user, 'view') return object_changes.restrict(info.context.request.user, 'view')
@strawberry.type
class ConfigContextMixin: class ConfigContextMixin:
config_context = GenericScalar()
def resolve_config_context(self, info): @strawberry_django.field
def config_context(self) -> strawberry.scalars.JSON:
return self.get_config_context() return self.get_config_context()
@strawberry.type
class CustomFieldsMixin: class CustomFieldsMixin:
custom_fields = GenericScalar()
def resolve_custom_fields(self, info): @strawberry_django.field
def custom_fields(self) -> strawberry.scalars.JSON:
return self.custom_field_data return self.custom_field_data
@strawberry.type
class ImageAttachmentsMixin: class ImageAttachmentsMixin:
image_attachments = graphene.List('extras.graphql.types.ImageAttachmentType')
def resolve_image_attachments(self, info): @strawberry_django.field
return self.images.restrict(info.context.user, 'view') def image_attachments(self, info) -> List[Annotated["ImageAttachmentType", strawberry.lazy('.types')]]:
return self.images.restrict(info.context.request.user, 'view')
@strawberry.type
class JournalEntriesMixin: class JournalEntriesMixin:
journal_entries = graphene.List('extras.graphql.types.JournalEntryType')
def resolve_journal_entries(self, info): @strawberry_django.field
return self.journal_entries.restrict(info.context.user, 'view') def journal_entries(self, info) -> List[Annotated["JournalEntryType", strawberry.lazy('.types')]]:
return self.journal_entries.all()
@strawberry.type
class TagsMixin: class TagsMixin:
tags = graphene.List('extras.graphql.types.TagType')
def resolve_tags(self, info): @strawberry_django.field
def tags(self) -> List[Annotated["TagType", strawberry.lazy('.types')]]:
return self.tags.all() return self.tags.all()
@strawberry.type
class ContactsMixin: class ContactsMixin:
contacts = graphene.List('tenancy.graphql.types.ContactAssignmentType')
def resolve_contacts(self, info): @strawberry_django.field
def contacts(self) -> List[Annotated["ContactAssignmentType", strawberry.lazy('tenancy.graphql.types')]]:
return list(self.contacts.all()) return list(self.contacts.all())

View File

@ -1,80 +1,70 @@
import graphene from typing import List
import strawberry
import strawberry_django
from extras import models from extras import models
from netbox.graphql.fields import ObjectField, ObjectListField
from .types import * from .types import *
from utilities.graphql_optimizer import gql_query_optimizer
class ExtrasQuery(graphene.ObjectType): @strawberry.type
config_context = ObjectField(ConfigContextType) class ExtrasQuery:
config_context_list = ObjectListField(ConfigContextType) @strawberry.field
def config_context(self, id: int) -> ConfigContextType:
return models.ConfigContext.objects.get(pk=id)
config_context_list: List[ConfigContextType] = strawberry_django.field()
def resolve_config_context_list(root, info, **kwargs): @strawberry.field
return gql_query_optimizer(models.ConfigContext.objects.all(), info) def config_template(self, id: int) -> ConfigTemplateType:
return models.ConfigTemplate.objects.get(pk=id)
config_template_list: List[ConfigTemplateType] = strawberry_django.field()
config_template = ObjectField(ConfigTemplateType) @strawberry.field
config_template_list = ObjectListField(ConfigTemplateType) def custom_field(self, id: int) -> CustomFieldType:
return models.CustomField.objects.get(pk=id)
custom_field_list: List[CustomFieldType] = strawberry_django.field()
def resolve_config_template_list(root, info, **kwargs): @strawberry.field
return gql_query_optimizer(models.ConfigTemplate.objects.all(), info) def custom_field_choice_set(self, id: int) -> CustomFieldChoiceSetType:
return models.CustomFieldChoiceSet.objects.get(pk=id)
custom_field_choice_set_list: List[CustomFieldChoiceSetType] = strawberry_django.field()
custom_field = ObjectField(CustomFieldType) @strawberry.field
custom_field_list = ObjectListField(CustomFieldType) def custom_link(self, id: int) -> CustomLinkType:
return models.CustomLink.objects.get(pk=id)
custom_link_list: List[CustomLinkType] = strawberry_django.field()
def resolve_custom_field_list(root, info, **kwargs): @strawberry.field
return gql_query_optimizer(models.CustomField.objects.all(), info) def export_template(self, id: int) -> ExportTemplateType:
return models.ExportTemplate.objects.get(pk=id)
export_template_list: List[ExportTemplateType] = strawberry_django.field()
custom_field_choice_set = ObjectField(CustomFieldChoiceSetType) @strawberry.field
custom_field_choice_set_list = ObjectListField(CustomFieldChoiceSetType) def image_attachment(self, id: int) -> ImageAttachmentType:
return models.ImageAttachment.objects.get(pk=id)
image_attachment_list: List[ImageAttachmentType] = strawberry_django.field()
def resolve_custom_field_choices_list(root, info, **kwargs): @strawberry.field
return gql_query_optimizer(models.CustomFieldChoiceSet.objects.all(), info) def saved_filter(self, id: int) -> SavedFilterType:
return models.SavedFilter.objects.get(pk=id)
saved_filter_list: List[SavedFilterType] = strawberry_django.field()
custom_link = ObjectField(CustomLinkType) @strawberry.field
custom_link_list = ObjectListField(CustomLinkType) def journal_entry(self, id: int) -> JournalEntryType:
return models.JournalEntry.objects.get(pk=id)
journal_entry_list: List[JournalEntryType] = strawberry_django.field()
def resolve_custom_link_list(root, info, **kwargs): @strawberry.field
return gql_query_optimizer(models.CustomLink.objects.all(), info) def tag(self, id: int) -> TagType:
return models.Tag.objects.get(pk=id)
tag_list: List[TagType] = strawberry_django.field()
export_template = ObjectField(ExportTemplateType) @strawberry.field
export_template_list = ObjectListField(ExportTemplateType) def webhook(self, id: int) -> WebhookType:
return models.Webhook.objects.get(pk=id)
webhook_list: List[WebhookType] = strawberry_django.field()
def resolve_export_template_list(root, info, **kwargs): @strawberry.field
return gql_query_optimizer(models.ExportTemplate.objects.all(), info) def event_rule(self, id: int) -> EventRuleType:
return models.EventRule.objects.get(pk=id)
image_attachment = ObjectField(ImageAttachmentType) event_rule_list: List[EventRuleType] = strawberry_django.field()
image_attachment_list = ObjectListField(ImageAttachmentType)
def resolve_image_attachment_list(root, info, **kwargs):
return gql_query_optimizer(models.ImageAttachment.objects.all(), info)
saved_filter = ObjectField(SavedFilterType)
saved_filter_list = ObjectListField(SavedFilterType)
def resolve_saved_filter_list(root, info, **kwargs):
return gql_query_optimizer(models.SavedFilter.objects.all(), info)
journal_entry = ObjectField(JournalEntryType)
journal_entry_list = ObjectListField(JournalEntryType)
def resolve_journal_entry_list(root, info, **kwargs):
return gql_query_optimizer(models.JournalEntry.objects.all(), info)
tag = ObjectField(TagType)
tag_list = ObjectListField(TagType)
def resolve_tag_list(root, info, **kwargs):
return gql_query_optimizer(models.Tag.objects.all(), info)
webhook = ObjectField(WebhookType)
webhook_list = ObjectListField(WebhookType)
def resolve_webhook_list(root, info, **kwargs):
return gql_query_optimizer(models.Webhook.objects.all(), info)
event_rule = ObjectField(EventRuleType)
event_rule_list = ObjectListField(EventRuleType)
def resolve_eventrule_list(root, info, **kwargs):
return gql_query_optimizer(models.EventRule.objects.all(), info)

View File

@ -1,6 +1,12 @@
from extras import filtersets, models from typing import Annotated, List
import strawberry
import strawberry_django
from extras import models
from extras.graphql.mixins import CustomFieldsMixin, TagsMixin from extras.graphql.mixins import CustomFieldsMixin, TagsMixin
from netbox.graphql.types import BaseObjectType, ObjectType, OrganizationalObjectType from netbox.graphql.types import BaseObjectType, ContentTypeType, ObjectType, OrganizationalObjectType
from .filters import *
__all__ = ( __all__ = (
'ConfigContextType', 'ConfigContextType',
@ -19,104 +25,202 @@ __all__ = (
) )
@strawberry_django.type(
models.ConfigContext,
fields='__all__',
filters=ConfigContextFilter
)
class ConfigContextType(ObjectType): class ConfigContextType(ObjectType):
data_source: Annotated["DataSourceType", strawberry.lazy('core.graphql.types')] | None
data_file: Annotated["DataFileType", strawberry.lazy('core.graphql.types')] | None
class Meta: @strawberry_django.field
model = models.ConfigContext def roles(self) -> List[Annotated["DeviceRoleType", strawberry.lazy('dcim.graphql.types')]]:
fields = '__all__' return self.roles.all()
filterset_class = filtersets.ConfigContextFilterSet
@strawberry_django.field
def device_types(self) -> List[Annotated["DeviceTypeType", strawberry.lazy('dcim.graphql.types')]]:
return self.device_types.all()
@strawberry_django.field
def tags(self) -> List[Annotated["TagType", strawberry.lazy('extras.graphql.types')]]:
return self.tags.all()
@strawberry_django.field
def platforms(self) -> List[Annotated["PlatformType", strawberry.lazy('dcim.graphql.types')]]:
return self.platforms.all()
@strawberry_django.field
def regions(self) -> List[Annotated["RegionType", strawberry.lazy('dcim.graphql.types')]]:
return self.regions.all()
@strawberry_django.field
def cluster_groups(self) -> List[Annotated["ClusterGroupType", strawberry.lazy('virtualization.graphql.types')]]:
return self.cluster_groups.all()
@strawberry_django.field
def tenant_groups(self) -> List[Annotated["TenantGroupType", strawberry.lazy('tenancy.graphql.types')]]:
return self.tenant_groups.all()
@strawberry_django.field
def cluster_types(self) -> List[Annotated["ClusterTypeType", strawberry.lazy('virtualization.graphql.types')]]:
return self.cluster_types.all()
@strawberry_django.field
def clusters(self) -> List[Annotated["ClusterType", strawberry.lazy('virtualization.graphql.types')]]:
return self.clusters.all()
@strawberry_django.field
def locations(self) -> List[Annotated["LocationType", strawberry.lazy('dcim.graphql.types')]]:
return self.locations.all()
@strawberry_django.field
def sites(self) -> List[Annotated["SiteType", strawberry.lazy('dcim.graphql.types')]]:
return self.sites.all()
@strawberry_django.field
def tenants(self) -> List[Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')]]:
return self.tenants.all()
@strawberry_django.field
def site_groups(self) -> List[Annotated["SiteGroupType", strawberry.lazy('dcim.graphql.types')]]:
return self.site_groups.all()
@strawberry_django.type(
models.ConfigTemplate,
fields='__all__',
filters=ConfigTemplateFilter
)
class ConfigTemplateType(TagsMixin, ObjectType): class ConfigTemplateType(TagsMixin, ObjectType):
data_source: Annotated["DataSourceType", strawberry.lazy('core.graphql.types')] | None
data_file: Annotated["DataFileType", strawberry.lazy('core.graphql.types')] | None
class Meta: @strawberry_django.field
model = models.ConfigTemplate def virtualmachines(self) -> List[Annotated["VirtualMachineType", strawberry.lazy('virtualization.graphql.types')]]:
fields = '__all__' return self.virtualmachines.all()
filterset_class = filtersets.ConfigTemplateFilterSet
@strawberry_django.field
def devices(self) -> List[Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')]]:
return self.devices.all()
@strawberry_django.field
def platforms(self) -> List[Annotated["PlatformType", strawberry.lazy('dcim.graphql.types')]]:
return self.platforms.all()
@strawberry_django.field
def device_roles(self) -> List[Annotated["DeviceRoleType", strawberry.lazy('dcim.graphql.types')]]:
return self.device_roles.all()
@strawberry_django.type(
models.CustomField,
fields='__all__',
filters=CustomFieldFilter
)
class CustomFieldType(ObjectType): class CustomFieldType(ObjectType):
related_object_type: Annotated["ContentTypeType", strawberry.lazy('netbox.graphql.types')] | None
class Meta: choice_set: Annotated["CustomFieldChoiceSetType", strawberry.lazy('extras.graphql.types')] | None
model = models.CustomField
fields = '__all__'
filterset_class = filtersets.CustomFieldFilterSet
@strawberry_django.type(
models.CustomFieldChoiceSet,
exclude=('extra_choices', ),
filters=CustomFieldChoiceSetFilter
)
class CustomFieldChoiceSetType(ObjectType): class CustomFieldChoiceSetType(ObjectType):
class Meta: @strawberry_django.field
model = models.CustomFieldChoiceSet def choices_for(self) -> List[Annotated["CustomFieldType", strawberry.lazy('extras.graphql.types')]]:
fields = '__all__' return self.choices_for.all()
filterset_class = filtersets.CustomFieldChoiceSetFilterSet
@strawberry_django.field
def extra_choices(self) -> List[str] | None:
return list(self.extra_choices)
@strawberry_django.type(
models.CustomLink,
fields='__all__',
filters=CustomLinkFilter
)
class CustomLinkType(ObjectType): class CustomLinkType(ObjectType):
pass
class Meta:
model = models.CustomLink
fields = '__all__'
filterset_class = filtersets.CustomLinkFilterSet
class EventRuleType(OrganizationalObjectType):
class Meta:
model = models.EventRule
fields = '__all__'
filterset_class = filtersets.EventRuleFilterSet
@strawberry_django.type(
models.ExportTemplate,
fields='__all__',
filters=ExportTemplateFilter
)
class ExportTemplateType(ObjectType): class ExportTemplateType(ObjectType):
data_source: Annotated["DataSourceType", strawberry.lazy('core.graphql.types')] | None
class Meta: data_file: Annotated["DataFileType", strawberry.lazy('core.graphql.types')] | None
model = models.ExportTemplate
fields = '__all__'
filterset_class = filtersets.ExportTemplateFilterSet
@strawberry_django.type(
models.ImageAttachment,
fields='__all__',
filters=ImageAttachmentFilter
)
class ImageAttachmentType(BaseObjectType): class ImageAttachmentType(BaseObjectType):
object_type: Annotated["ContentTypeType", strawberry.lazy('netbox.graphql.types')] | None
class Meta:
model = models.ImageAttachment
fields = '__all__'
filterset_class = filtersets.ImageAttachmentFilterSet
@strawberry_django.type(
models.JournalEntry,
fields='__all__',
filters=JournalEntryFilter
)
class JournalEntryType(CustomFieldsMixin, TagsMixin, ObjectType): class JournalEntryType(CustomFieldsMixin, TagsMixin, ObjectType):
assigned_object_type: Annotated["ContentTypeType", strawberry.lazy('netbox.graphql.types')] | None
class Meta: created_by: Annotated["UserType", strawberry.lazy('users.graphql.types')] | None
model = models.JournalEntry
fields = '__all__'
filterset_class = filtersets.JournalEntryFilterSet
@strawberry_django.type(
models.ObjectChange,
fields='__all__',
filters=ObjectChangeFilter
)
class ObjectChangeType(BaseObjectType): class ObjectChangeType(BaseObjectType):
pass
class Meta:
model = models.ObjectChange
fields = '__all__'
filterset_class = filtersets.ObjectChangeFilterSet
@strawberry_django.type(
models.SavedFilter,
exclude=['content_types',],
filters=SavedFilterFilter
)
class SavedFilterType(ObjectType): class SavedFilterType(ObjectType):
user: Annotated["UserType", strawberry.lazy('users.graphql.types')] | None
class Meta:
model = models.SavedFilter
fields = '__all__'
filterset_class = filtersets.SavedFilterFilterSet
@strawberry_django.type(
models.Tag,
exclude=['extras_taggeditem_items', ],
filters=TagFilter
)
class TagType(ObjectType): class TagType(ObjectType):
color: str
class Meta: @strawberry_django.field
model = models.Tag def object_types(self) -> List[ContentTypeType]:
exclude = ('extras_taggeditem_items',) return self.object_types.all()
filterset_class = filtersets.TagFilterSet
@strawberry_django.type(
models.Webhook,
exclude=['content_types',],
filters=WebhookFilter
)
class WebhookType(OrganizationalObjectType): class WebhookType(OrganizationalObjectType):
pass
class Meta:
model = models.Webhook @strawberry_django.type(
filterset_class = filtersets.WebhookFilterSet models.EventRule,
exclude=['content_types',],
filters=EventRuleFilter
)
class EventRuleType(OrganizationalObjectType):
action_object_type: Annotated["ContentTypeType", strawberry.lazy('netbox.graphql.types')] | None

View File

@ -4,7 +4,7 @@ from django.db import transaction
from django.test import TestCase, override_settings from django.test import TestCase, override_settings
from dcim.choices import SiteStatusChoices from dcim.choices import SiteStatusChoices
from dcim.models import Site from dcim.models import Site, Region
from extras.validators import CustomValidator from extras.validators import CustomValidator
from ipam.models import ASN, RIR from ipam.models import ASN, RIR
from users.models import User from users.models import User
@ -82,6 +82,13 @@ prohibited_validator = CustomValidator({
}) })
region_validator = CustomValidator({
'region.name': {
'eq': 'Bar',
}
})
request_validator = CustomValidator({ request_validator = CustomValidator({
'request.user.username': { 'request.user.username': {
'eq': 'Bob' 'eq': 'Bob'
@ -154,6 +161,20 @@ class CustomValidatorTest(TestCase):
def test_valid(self): def test_valid(self):
Site(name='abcdef123', slug='abcdef123').clean() Site(name='abcdef123', slug='abcdef123').clean()
@override_settings(CUSTOM_VALIDATORS={'dcim.site': [region_validator]})
def test_valid(self):
region1 = Region(name='Foo', slug='foo')
region1.save()
region2 = Region(name='Bar', slug='bar')
region2.save()
# Invalid region
with self.assertRaises(ValidationError):
Site(name='abcdef123', slug='abcdef123', region=region1).clean()
# Valid region
Site(name='abcdef123', slug='abcdef123', region=region2).clean()
@override_settings(CUSTOM_VALIDATORS={'dcim.site': [custom_validator]}) @override_settings(CUSTOM_VALIDATORS={'dcim.site': [custom_validator]})
def test_custom_invalid(self): def test_custom_invalid(self):
with self.assertRaises(ValidationError): with self.assertRaises(ValidationError):
@ -207,7 +228,7 @@ class CustomValidatorConfigTest(TestCase):
@override_settings( @override_settings(
CUSTOM_VALIDATORS={ CUSTOM_VALIDATORS={
'dcim.site': ( 'dcim.site': (
'extras.tests.test_customvalidation.MyValidator', 'extras.tests.test_customvalidators.MyValidator',
) )
} }
) )
@ -254,7 +275,7 @@ class ProtectionRulesConfigTest(TestCase):
@override_settings( @override_settings(
PROTECTION_RULES={ PROTECTION_RULES={
'dcim.site': ( 'dcim.site': (
'extras.tests.test_customvalidation.MyValidator', 'extras.tests.test_customvalidators.MyValidator',
) )
} }
) )

View File

@ -151,14 +151,14 @@ class CustomValidator:
return [] return []
# Raise a ValidationError for unknown attributes # Raise a ValidationError for unknown attributes
if not hasattr(instance, name): try:
return operator.attrgetter(name)(instance)
except AttributeError:
raise ValidationError(_('Invalid attribute "{name}" for {model}').format( raise ValidationError(_('Invalid attribute "{name}" for {model}').format(
name=name, name=name,
model=instance.__class__.__name__ model=instance.__class__.__name__
)) ))
return getattr(instance, name)
def get_validator(self, descriptor, value): def get_validator(self, descriptor, value):
""" """
Instantiate and return the appropriate validator based on the descriptor given. For Instantiate and return the appropriate validator based on the descriptor given. For

View File

@ -0,0 +1,119 @@
import strawberry_django
from ipam import filtersets, models
from netbox.graphql.filter_mixins import autotype_decorator, BaseFilterMixin
__all__ = (
'ASNFilter',
'ASNRangeFilter',
'AggregateFilter',
'FHRPGroupFilter',
'FHRPGroupAssignmentFilter',
'IPAddressFilter',
'IPRangeFilter',
'PrefixFilter',
'RIRFilter',
'RoleFilter',
'RouteTargetFilter',
'ServiceFilter',
'ServiceTemplateFilter',
'VLANFilter',
'VLANGroupFilter',
'VRFFilter',
)
@strawberry_django.filter(models.ASN, lookups=True)
@autotype_decorator(filtersets.ASNFilterSet)
class ASNFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.ASNRange, lookups=True)
@autotype_decorator(filtersets.ASNRangeFilterSet)
class ASNRangeFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.Aggregate, lookups=True)
@autotype_decorator(filtersets.AggregateFilterSet)
class AggregateFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.FHRPGroup, lookups=True)
@autotype_decorator(filtersets.FHRPGroupFilterSet)
class FHRPGroupFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.FHRPGroupAssignment, lookups=True)
@autotype_decorator(filtersets.FHRPGroupAssignmentFilterSet)
class FHRPGroupAssignmentFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.IPAddress, lookups=True)
@autotype_decorator(filtersets.IPAddressFilterSet)
class IPAddressFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.IPRange, lookups=True)
@autotype_decorator(filtersets.IPRangeFilterSet)
class IPRangeFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.Prefix, lookups=True)
@autotype_decorator(filtersets.PrefixFilterSet)
class PrefixFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.RIR, lookups=True)
@autotype_decorator(filtersets.RIRFilterSet)
class RIRFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.Role, lookups=True)
@autotype_decorator(filtersets.RoleFilterSet)
class RoleFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.RouteTarget, lookups=True)
@autotype_decorator(filtersets.RouteTargetFilterSet)
class RouteTargetFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.Service, lookups=True)
@autotype_decorator(filtersets.ServiceFilterSet)
class ServiceFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.ServiceTemplate, lookups=True)
@autotype_decorator(filtersets.ServiceTemplateFilterSet)
class ServiceTemplateFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.VLAN, lookups=True)
@autotype_decorator(filtersets.VLANFilterSet)
class VLANFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.VLANGroup, lookups=True)
@autotype_decorator(filtersets.VLANGroupFilterSet)
class VLANGroupFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.VRF, lookups=True)
@autotype_decorator(filtersets.VRFFilterSet)
class VRFFilter(BaseFilterMixin):
pass

View File

@ -1,95 +0,0 @@
import graphene
from dcim.graphql.types import (
InterfaceType,
LocationType,
RackType,
RegionType,
SiteGroupType,
SiteType,
)
from dcim.models import Interface, Location, Rack, Region, Site, SiteGroup
from ipam.graphql.types import FHRPGroupType, VLANType
from ipam.models import VLAN, FHRPGroup
from virtualization.graphql.types import ClusterGroupType, ClusterType, VMInterfaceType
from virtualization.models import Cluster, ClusterGroup, VMInterface
class IPAddressAssignmentType(graphene.Union):
class Meta:
types = (
InterfaceType,
FHRPGroupType,
VMInterfaceType,
)
@classmethod
def resolve_type(cls, instance, info):
if type(instance) is Interface:
return InterfaceType
if type(instance) is FHRPGroup:
return FHRPGroupType
if type(instance) is VMInterface:
return VMInterfaceType
class L2VPNAssignmentType(graphene.Union):
class Meta:
types = (
InterfaceType,
VLANType,
VMInterfaceType,
)
@classmethod
def resolve_type(cls, instance, info):
if type(instance) is Interface:
return InterfaceType
if type(instance) is VLAN:
return VLANType
if type(instance) is VMInterface:
return VMInterfaceType
class FHRPGroupInterfaceType(graphene.Union):
class Meta:
types = (
InterfaceType,
VMInterfaceType,
)
@classmethod
def resolve_type(cls, instance, info):
if type(instance) is Interface:
return InterfaceType
if type(instance) is VMInterface:
return VMInterfaceType
class VLANGroupScopeType(graphene.Union):
class Meta:
types = (
ClusterType,
ClusterGroupType,
LocationType,
RackType,
RegionType,
SiteType,
SiteGroupType,
)
@classmethod
def resolve_type(cls, instance, info):
if type(instance) is Cluster:
return ClusterType
if type(instance) is ClusterGroup:
return ClusterGroupType
if type(instance) is Location:
return LocationType
if type(instance) is Rack:
return RackType
if type(instance) is Region:
return RegionType
if type(instance) is Site:
return SiteType
if type(instance) is SiteGroup:
return SiteGroupType

View File

@ -1,4 +1,7 @@
import graphene from typing import Annotated, List
import strawberry
import strawberry_django
__all__ = ( __all__ = (
'IPAddressesMixin', 'IPAddressesMixin',
@ -6,15 +9,15 @@ __all__ = (
) )
@strawberry.type
class IPAddressesMixin: class IPAddressesMixin:
ip_addresses = graphene.List('ipam.graphql.types.IPAddressType') @strawberry_django.field
def ip_addresses(self) -> List[Annotated["IPAddressType", strawberry.lazy('ipam.graphql.types')]]:
def resolve_ip_addresses(self, info): return self.ip_addresses.all()
return self.ip_addresses.restrict(info.context.user, 'view')
@strawberry.type
class VLANGroupsMixin: class VLANGroupsMixin:
vlan_groups = graphene.List('ipam.graphql.types.VLANGroupType') @strawberry_django.field
def vlan_groups(self) -> List[Annotated["VLANGroupType", strawberry.lazy('ipam.graphql.types')]]:
def resolve_vlan_groups(self, info): return self.vlan_groups.all()
return self.vlan_groups.restrict(info.context.user, 'view')

View File

@ -1,104 +1,90 @@
import graphene from typing import List
import strawberry
import strawberry_django
from ipam import models from ipam import models
from netbox.graphql.fields import ObjectField, ObjectListField
from utilities.graphql_optimizer import gql_query_optimizer
from .types import * from .types import *
class IPAMQuery(graphene.ObjectType): @strawberry.type
asn = ObjectField(ASNType) class IPAMQuery:
asn_list = ObjectListField(ASNType) @strawberry.field
def asn(self, id: int) -> ASNType:
return models.ASN.objects.get(pk=id)
asn_list: List[ASNType] = strawberry_django.field()
def resolve_asn_list(root, info, **kwargs): @strawberry.field
return gql_query_optimizer(models.ASN.objects.all(), info) def asn_range(self, id: int) -> ASNRangeType:
return models.ASNRange.objects.get(pk=id)
asn_range_list: List[ASNRangeType] = strawberry_django.field()
asn_range = ObjectField(ASNRangeType) @strawberry.field
asn_range_list = ObjectListField(ASNRangeType) def aggregate(self, id: int) -> AggregateType:
return models.Aggregate.objects.get(pk=id)
aggregate_list: List[AggregateType] = strawberry_django.field()
def resolve_asn_range_list(root, info, **kwargs): @strawberry.field
return gql_query_optimizer(models.ASNRange.objects.all(), info) def ip_address(self, id: int) -> IPAddressType:
return models.IPAddress.objects.get(pk=id)
ip_address_list: List[IPAddressType] = strawberry_django.field()
aggregate = ObjectField(AggregateType) @strawberry.field
aggregate_list = ObjectListField(AggregateType) def ip_range(self, id: int) -> IPRangeType:
return models.IPRange.objects.get(pk=id)
ip_range_list: List[IPRangeType] = strawberry_django.field()
def resolve_aggregate_list(root, info, **kwargs): @strawberry.field
return gql_query_optimizer(models.Aggregate.objects.all(), info) def prefix(self, id: int) -> PrefixType:
return models.Prefix.objects.get(pk=id)
prefix_list: List[PrefixType] = strawberry_django.field()
ip_address = ObjectField(IPAddressType) @strawberry.field
ip_address_list = ObjectListField(IPAddressType) def rir(self, id: int) -> RIRType:
return models.RIR.objects.get(pk=id)
rir_list: List[RIRType] = strawberry_django.field()
def resolve_ip_address_list(root, info, **kwargs): @strawberry.field
return gql_query_optimizer(models.IPAddress.objects.all(), info) def role(self, id: int) -> RoleType:
return models.Role.objects.get(pk=id)
role_list: List[RoleType] = strawberry_django.field()
ip_range = ObjectField(IPRangeType) @strawberry.field
ip_range_list = ObjectListField(IPRangeType) def route_target(self, id: int) -> RouteTargetType:
return models.RouteTarget.objects.get(pk=id)
route_target_list: List[RouteTargetType] = strawberry_django.field()
def resolve_ip_range_list(root, info, **kwargs): @strawberry.field
return gql_query_optimizer(models.IPRange.objects.all(), info) def service(self, id: int) -> ServiceType:
return models.Service.objects.get(pk=id)
service_list: List[ServiceType] = strawberry_django.field()
prefix = ObjectField(PrefixType) @strawberry.field
prefix_list = ObjectListField(PrefixType) def service_template(self, id: int) -> ServiceTemplateType:
return models.ServiceTemplate.objects.get(pk=id)
service_template_list: List[ServiceTemplateType] = strawberry_django.field()
def resolve_prefix_list(root, info, **kwargs): @strawberry.field
return gql_query_optimizer(models.Prefix.objects.all(), info) def fhrp_group(self, id: int) -> FHRPGroupType:
return models.FHRPGroup.objects.get(pk=id)
fhrp_group_list: List[FHRPGroupType] = strawberry_django.field()
rir = ObjectField(RIRType) @strawberry.field
rir_list = ObjectListField(RIRType) def fhrp_group_assignment(self, id: int) -> FHRPGroupAssignmentType:
return models.FHRPGroupAssignment.objects.get(pk=id)
fhrp_group_assignment_list: List[FHRPGroupAssignmentType] = strawberry_django.field()
def resolve_rir_list(root, info, **kwargs): @strawberry.field
return gql_query_optimizer(models.RIR.objects.all(), info) def vlan(self, id: int) -> VLANType:
return models.VLAN.objects.get(pk=id)
vlan_list: List[VLANType] = strawberry_django.field()
role = ObjectField(RoleType) @strawberry.field
role_list = ObjectListField(RoleType) def vlan_group(self, id: int) -> VLANGroupType:
return models.VLANGroup.objects.get(pk=id)
vlan_group_list: List[VLANGroupType] = strawberry_django.field()
def resolve_role_list(root, info, **kwargs): @strawberry.field
return gql_query_optimizer(models.Role.objects.all(), info) def vrf(self, id: int) -> VRFType:
return models.VRF.objects.get(pk=id)
route_target = ObjectField(RouteTargetType) vrf_list: List[VRFType] = strawberry_django.field()
route_target_list = ObjectListField(RouteTargetType)
def resolve_route_target_list(root, info, **kwargs):
return gql_query_optimizer(models.RouteTarget.objects.all(), info)
service = ObjectField(ServiceType)
service_list = ObjectListField(ServiceType)
def resolve_service_list(root, info, **kwargs):
return gql_query_optimizer(models.Service.objects.all(), info)
service_template = ObjectField(ServiceTemplateType)
service_template_list = ObjectListField(ServiceTemplateType)
def resolve_service_template_list(root, info, **kwargs):
return gql_query_optimizer(models.ServiceTemplate.objects.all(), info)
fhrp_group = ObjectField(FHRPGroupType)
fhrp_group_list = ObjectListField(FHRPGroupType)
def resolve_fhrp_group_list(root, info, **kwargs):
return gql_query_optimizer(models.FHRPGroup.objects.all(), info)
fhrp_group_assignment = ObjectField(FHRPGroupAssignmentType)
fhrp_group_assignment_list = ObjectListField(FHRPGroupAssignmentType)
def resolve_fhrp_group_assignment_list(root, info, **kwargs):
return gql_query_optimizer(models.FHRPGroupAssignment.objects.all(), info)
vlan = ObjectField(VLANType)
vlan_list = ObjectListField(VLANType)
def resolve_vlan_list(root, info, **kwargs):
return gql_query_optimizer(models.VLAN.objects.all(), info)
vlan_group = ObjectField(VLANGroupType)
vlan_group_list = ObjectListField(VLANGroupType)
def resolve_vlan_group_list(root, info, **kwargs):
return gql_query_optimizer(models.VLANGroup.objects.all(), info)
vrf = ObjectField(VRFType)
vrf_list = ObjectListField(VRFType)
def resolve_vrf_list(root, info, **kwargs):
return gql_query_optimizer(models.VRF.objects.all(), info)

View File

@ -1,9 +1,15 @@
import graphene from typing import Annotated, List, Union
from ipam import filtersets, models import strawberry
from .mixins import IPAddressesMixin import strawberry_django
from circuits.graphql.types import ProviderType
from dcim.graphql.types import SiteType
from ipam import models
from netbox.graphql.scalars import BigInt from netbox.graphql.scalars import BigInt
from netbox.graphql.types import BaseObjectType, OrganizationalObjectType, NetBoxObjectType from netbox.graphql.types import BaseObjectType, NetBoxObjectType, OrganizationalObjectType
from .filters import *
from .mixins import IPAddressesMixin
__all__ = ( __all__ = (
'ASNType', 'ASNType',
@ -25,164 +31,335 @@ __all__ = (
) )
class IPAddressFamilyType(graphene.ObjectType): @strawberry.type
class IPAddressFamilyType:
value = graphene.Int() value: int
label = graphene.String() label: str
def __init__(self, value):
self.value = value
self.label = f'IPv{value}'
@strawberry.type
class BaseIPAddressFamilyType: class BaseIPAddressFamilyType:
""" """
Base type for models that need to expose their IPAddress family type. Base type for models that need to expose their IPAddress family type.
""" """
family = graphene.Field(IPAddressFamilyType)
def resolve_family(self, _): @strawberry.field
def family(self) -> IPAddressFamilyType:
# Note that self, is an instance of models.IPAddress # Note that self, is an instance of models.IPAddress
# thus resolves to the address family value. # thus resolves to the address family value.
return IPAddressFamilyType(self.family) return IPAddressFamilyType(value=self.family, label=f'IPv{self.family}')
@strawberry_django.type(
models.ASN,
fields='__all__',
filters=ASNFilter
)
class ASNType(NetBoxObjectType): class ASNType(NetBoxObjectType):
asn = graphene.Field(BigInt) asn: BigInt
rir: Annotated["RIRType", strawberry.lazy('ipam.graphql.types')] | None
tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None
class Meta: @strawberry_django.field
model = models.ASN def sites(self) -> List[SiteType]:
fields = '__all__' return self.sites.all()
filterset_class = filtersets.ASNFilterSet
@strawberry_django.field
def providers(self) -> List[ProviderType]:
return self.providers.all()
@strawberry_django.type(
models.ASNRange,
fields='__all__',
filters=ASNRangeFilter
)
class ASNRangeType(NetBoxObjectType): class ASNRangeType(NetBoxObjectType):
start: BigInt
class Meta: end: BigInt
model = models.ASNRange rir: Annotated["RIRType", strawberry.lazy('ipam.graphql.types')] | None
fields = '__all__' tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None
filterset_class = filtersets.ASNRangeFilterSet
@strawberry_django.type(
models.Aggregate,
fields='__all__',
filters=AggregateFilter
)
class AggregateType(NetBoxObjectType, BaseIPAddressFamilyType): class AggregateType(NetBoxObjectType, BaseIPAddressFamilyType):
prefix: str
class Meta: rir: Annotated["RIRType", strawberry.lazy('ipam.graphql.types')] | None
model = models.Aggregate tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None
fields = '__all__'
filterset_class = filtersets.AggregateFilterSet
@strawberry_django.type(
models.FHRPGroup,
fields='__all__',
filters=FHRPGroupFilter
)
class FHRPGroupType(NetBoxObjectType, IPAddressesMixin): class FHRPGroupType(NetBoxObjectType, IPAddressesMixin):
class Meta: @strawberry_django.field
model = models.FHRPGroup def fhrpgroupassignment_set(self) -> List[Annotated["FHRPGroupAssignmentType", strawberry.lazy('ipam.graphql.types')]]:
fields = '__all__' return self.fhrpgroupassignment_set.all()
filterset_class = filtersets.FHRPGroupFilterSet
def resolve_auth_type(self, info):
return self.auth_type or None
@strawberry_django.type(
models.FHRPGroupAssignment,
exclude=('interface_type', 'interface_id'),
filters=FHRPGroupAssignmentFilter
)
class FHRPGroupAssignmentType(BaseObjectType): class FHRPGroupAssignmentType(BaseObjectType):
interface = graphene.Field('ipam.graphql.gfk_mixins.FHRPGroupInterfaceType') group: Annotated["FHRPGroupType", strawberry.lazy('ipam.graphql.types')]
class Meta: @strawberry_django.field
model = models.FHRPGroupAssignment def interface(self) -> Annotated[Union[
exclude = ('interface_type', 'interface_id') Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')],
filterset_class = filtersets.FHRPGroupAssignmentFilterSet Annotated["VMInterfaceType", strawberry.lazy('virtualization.graphql.types')],
], strawberry.union("FHRPGroupInterfaceType")]:
return self.interface
@strawberry_django.type(
models.IPAddress,
exclude=('assigned_object_type', 'assigned_object_id', 'address'),
filters=IPAddressFilter
)
class IPAddressType(NetBoxObjectType, BaseIPAddressFamilyType): class IPAddressType(NetBoxObjectType, BaseIPAddressFamilyType):
assigned_object = graphene.Field('ipam.graphql.gfk_mixins.IPAddressAssignmentType') address: str
vrf: Annotated["VRFType", strawberry.lazy('ipam.graphql.types')] | None
tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None
nat_inside: Annotated["IPAddressType", strawberry.lazy('ipam.graphql.types')] | None
class Meta: @strawberry_django.field
model = models.IPAddress def nat_outside(self) -> List[Annotated["IPAddressType", strawberry.lazy('ipam.graphql.types')]]:
exclude = ('assigned_object_type', 'assigned_object_id') return self.nat_outside.all()
filterset_class = filtersets.IPAddressFilterSet
def resolve_role(self, info): @strawberry_django.field
return self.role or None def tunnel_terminations(self) -> List[Annotated["TunnelTerminationType", strawberry.lazy('vpn.graphql.types')]]:
return self.tunnel_terminations.all()
@strawberry_django.field
def services(self) -> List[Annotated["ServiceType", strawberry.lazy('ipam.graphql.types')]]:
return self.services.all()
@strawberry_django.field
def assigned_object(self) -> Annotated[Union[
Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')],
Annotated["FHRPGroupType", strawberry.lazy('ipam.graphql.types')],
Annotated["VMInterfaceType", strawberry.lazy('virtualization.graphql.types')],
], strawberry.union("IPAddressAssignmentType")]:
return self.assigned_object
@strawberry_django.type(
models.IPRange,
fields='__all__',
filters=IPRangeFilter
)
class IPRangeType(NetBoxObjectType): class IPRangeType(NetBoxObjectType):
start_address: str
class Meta: end_address: str
model = models.IPRange vrf: Annotated["VRFType", strawberry.lazy('ipam.graphql.types')] | None
fields = '__all__' tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None
filterset_class = filtersets.IPRangeFilterSet role: Annotated["RoleType", strawberry.lazy('ipam.graphql.types')] | None
def resolve_role(self, info):
return self.role or None
@strawberry_django.type(
models.Prefix,
fields='__all__',
filters=PrefixFilter
)
class PrefixType(NetBoxObjectType, BaseIPAddressFamilyType): class PrefixType(NetBoxObjectType, BaseIPAddressFamilyType):
prefix: str
class Meta: site: Annotated["SiteType", strawberry.lazy('dcim.graphql.types')] | None
model = models.Prefix vrf: Annotated["VRFType", strawberry.lazy('ipam.graphql.types')] | None
fields = '__all__' tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None
filterset_class = filtersets.PrefixFilterSet vlan: Annotated["VLANType", strawberry.lazy('ipam.graphql.types')] | None
role: Annotated["RoleType", strawberry.lazy('ipam.graphql.types')] | None
@strawberry_django.type(
models.RIR,
fields='__all__',
filters=RIRFilter
)
class RIRType(OrganizationalObjectType): class RIRType(OrganizationalObjectType):
class Meta: @strawberry_django.field
model = models.RIR def asn_ranges(self) -> List[Annotated["ASNRangeType", strawberry.lazy('ipam.graphql.types')]]:
fields = '__all__' return self.asn_ranges.all()
filterset_class = filtersets.RIRFilterSet
@strawberry_django.field
def asns(self) -> List[Annotated["ASNType", strawberry.lazy('ipam.graphql.types')]]:
return self.asns.all()
@strawberry_django.field
def aggregates(self) -> List[Annotated["AggregateType", strawberry.lazy('ipam.graphql.types')]]:
return self.aggregates.all()
@strawberry_django.type(
models.Role,
fields='__all__',
filters=RoleFilter
)
class RoleType(OrganizationalObjectType): class RoleType(OrganizationalObjectType):
class Meta: @strawberry_django.field
model = models.Role def prefixes(self) -> List[Annotated["PrefixType", strawberry.lazy('ipam.graphql.types')]]:
fields = '__all__' return self.prefixes.all()
filterset_class = filtersets.RoleFilterSet
@strawberry_django.field
def ip_ranges(self) -> List[Annotated["IPRangeType", strawberry.lazy('ipam.graphql.types')]]:
return self.ip_ranges.all()
@strawberry_django.field
def vlans(self) -> List[Annotated["VLANType", strawberry.lazy('ipam.graphql.types')]]:
return self.vlans.all()
@strawberry_django.type(
models.RouteTarget,
fields='__all__',
filters=RouteTargetFilter
)
class RouteTargetType(NetBoxObjectType): class RouteTargetType(NetBoxObjectType):
tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None
class Meta: @strawberry_django.field
model = models.RouteTarget def exporting_l2vpns(self) -> List[Annotated["L2VPNType", strawberry.lazy('vpn.graphql.types')]]:
fields = '__all__' return self.exporting_l2vpns.all()
filterset_class = filtersets.RouteTargetFilterSet
@strawberry_django.field
def exporting_vrfs(self) -> List[Annotated["VRFType", strawberry.lazy('ipam.graphql.types')]]:
return self.exporting_vrfs.all()
@strawberry_django.field
def importing_vrfs(self) -> List[Annotated["VRFType", strawberry.lazy('ipam.graphql.types')]]:
return self.importing_vrfs.all()
@strawberry_django.field
def importing_l2vpns(self) -> List[Annotated["L2VPNType", strawberry.lazy('vpn.graphql.types')]]:
return self.importing_l2vpns.all()
@strawberry_django.type(
models.Service,
fields='__all__',
filters=ServiceFilter
)
class ServiceType(NetBoxObjectType): class ServiceType(NetBoxObjectType):
ports: List[int]
device: Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')] | None
virtual_machine: Annotated["VirtualMachineType", strawberry.lazy('virtualization.graphql.types')] | None
class Meta: @strawberry_django.field
model = models.Service def ipaddresses(self) -> List[Annotated["IPAddressType", strawberry.lazy('ipam.graphql.types')]]:
fields = '__all__' return self.ipaddresses.all()
filterset_class = filtersets.ServiceFilterSet
@strawberry_django.type(
models.ServiceTemplate,
fields='__all__',
filters=ServiceTemplateFilter
)
class ServiceTemplateType(NetBoxObjectType): class ServiceTemplateType(NetBoxObjectType):
ports: List[int]
class Meta:
model = models.ServiceTemplate
fields = '__all__'
filterset_class = filtersets.ServiceTemplateFilterSet
@strawberry_django.type(
models.VLAN,
fields='__all__',
filters=VLANFilter
)
class VLANType(NetBoxObjectType): class VLANType(NetBoxObjectType):
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
role: Annotated["RoleType", strawberry.lazy('ipam.graphql.types')] | None
class Meta: @strawberry_django.field
model = models.VLAN def interfaces_as_untagged(self) -> List[Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')]]:
fields = '__all__' return self.interfaces_as_untagged.all()
filterset_class = filtersets.VLANFilterSet
@strawberry_django.field
def vminterfaces_as_untagged(self) -> List[Annotated["VMInterfaceType", strawberry.lazy('virtualization.graphql.types')]]:
return self.vminterfaces_as_untagged.all()
@strawberry_django.field
def wirelesslan_set(self) -> List[Annotated["WirelessLANType", strawberry.lazy('wireless.graphql.types')]]:
return self.wirelesslan_set.all()
@strawberry_django.field
def prefixes(self) -> List[Annotated["PrefixType", strawberry.lazy('ipam.graphql.types')]]:
return self.prefixes.all()
@strawberry_django.field
def interfaces_as_tagged(self) -> List[Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')]]:
return self.interfaces_as_tagged.all()
@strawberry_django.field
def vminterfaces_as_tagged(self) -> List[Annotated["VMInterfaceType", strawberry.lazy('virtualization.graphql.types')]]:
return self.vminterfaces_as_tagged.all()
@strawberry_django.type(
models.VLANGroup,
exclude=('scope_type', 'scope_id'),
filters=VLANGroupFilter
)
class VLANGroupType(OrganizationalObjectType): class VLANGroupType(OrganizationalObjectType):
scope = graphene.Field('ipam.graphql.gfk_mixins.VLANGroupScopeType')
class Meta: @strawberry_django.field
model = models.VLANGroup def vlans(self) -> List[VLANType]:
exclude = ('scope_type', 'scope_id') return self.vlans.all()
filterset_class = filtersets.VLANGroupFilterSet
@strawberry_django.field
def scope(self) -> Annotated[Union[
Annotated["ClusterType", strawberry.lazy('virtualization.graphql.types')],
Annotated["ClusterGroupType", strawberry.lazy('virtualization.graphql.types')],
Annotated["LocationType", strawberry.lazy('dcim.graphql.types')],
Annotated["RackType", strawberry.lazy('dcim.graphql.types')],
Annotated["RegionType", strawberry.lazy('dcim.graphql.types')],
Annotated["SiteType", strawberry.lazy('dcim.graphql.types')],
Annotated["SiteGroupType", strawberry.lazy('dcim.graphql.types')],
], strawberry.union("VLANGroupScopeType")]:
return self.scope
@strawberry_django.type(
models.VRF,
fields='__all__',
filters=VRFFilter
)
class VRFType(NetBoxObjectType): class VRFType(NetBoxObjectType):
tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None
class Meta: @strawberry_django.field
model = models.VRF def interfaces(self) -> List[Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')]]:
fields = '__all__' return self.interfaces.all()
filterset_class = filtersets.VRFFilterSet
@strawberry_django.field
def ip_addresses(self) -> List[Annotated["IPAddressType", strawberry.lazy('ipam.graphql.types')]]:
return self.ip_addresses.all()
@strawberry_django.field
def vminterfaces(self) -> List[Annotated["VMInterfaceType", strawberry.lazy('virtualization.graphql.types')]]:
return self.vminterfaces.all()
@strawberry_django.field
def ip_ranges(self) -> List[Annotated["IPRangeType", strawberry.lazy('ipam.graphql.types')]]:
return self.ip_ranges.all()
@strawberry_django.field
def export_targets(self) -> List[Annotated["RouteTargetType", strawberry.lazy('ipam.graphql.types')]]:
return self.export_targets.all()
@strawberry_django.field
def import_targets(self) -> List[Annotated["RouteTargetType", strawberry.lazy('ipam.graphql.types')]]:
return self.import_targets.all()
@strawberry_django.field
def prefixes(self) -> List[Annotated["PrefixType", strawberry.lazy('ipam.graphql.types')]]:
return self.prefixes.all()

View File

@ -1,69 +0,0 @@
import graphene
from dcim.fields import MACAddressField, WWNField
from django.db import models
from graphene import Dynamic
from graphene_django.converter import convert_django_field, get_django_field_description
from graphene_django.fields import DjangoConnectionField
from ipam.fields import IPAddressField, IPNetworkField
from taggit.managers import TaggableManager
from .fields import ObjectListField
@convert_django_field.register(TaggableManager)
def convert_field_to_tags_list(field, registry=None):
"""
Register conversion handler for django-taggit's TaggableManager
"""
return graphene.List(graphene.String)
@convert_django_field.register(IPAddressField)
@convert_django_field.register(IPNetworkField)
@convert_django_field.register(MACAddressField)
@convert_django_field.register(WWNField)
def convert_field_to_string(field, registry=None):
# TODO: Update to use get_django_field_description under django_graphene v3.0
return graphene.String(description=field.help_text, required=not field.null)
@convert_django_field.register(models.ManyToManyField)
@convert_django_field.register(models.ManyToManyRel)
@convert_django_field.register(models.ManyToOneRel)
def convert_field_to_list_or_connection(field, registry=None):
"""
From graphene_django.converter.py we need to monkey-patch this to return
our ObjectListField with filtering support instead of DjangoListField
"""
model = field.related_model
def dynamic_type():
_type = registry.get_type_for_model(model)
if not _type:
return
if isinstance(field, models.ManyToManyField):
description = get_django_field_description(field)
else:
description = get_django_field_description(field.field)
# If there is a connection, we should transform the field
# into a DjangoConnectionField
if _type._meta.connection:
# Use a DjangoFilterConnectionField if there are
# defined filter_fields or a filterset_class in the
# DjangoObjectType Meta
if _type._meta.filter_fields or _type._meta.filterset_class:
from .filter.fields import DjangoFilterConnectionField
return DjangoFilterConnectionField(_type, required=True, description=description)
return DjangoConnectionField(_type, required=True, description=description)
return ObjectListField(
_type,
required=True, # A Set is always returned, never None.
description=description,
)
return Dynamic(dynamic_type)

View File

@ -1,70 +0,0 @@
from functools import partial
import graphene
from graphene_django import DjangoListField
from .utils import get_graphene_type
__all__ = (
'ObjectField',
'ObjectListField',
)
class ObjectField(graphene.Field):
"""
Retrieve a single object, identified by its numeric ID.
"""
def __init__(self, *args, **kwargs):
if 'id' not in kwargs:
kwargs['id'] = graphene.Int(required=True)
super().__init__(*args, **kwargs)
@staticmethod
def object_resolver(django_object_type, root, info, **args):
"""
Return an object given its numeric ID.
"""
manager = django_object_type._meta.model._default_manager
queryset = django_object_type.get_queryset(manager, info)
return queryset.get(**args)
def get_resolver(self, parent_resolver):
return partial(self.object_resolver, self._type)
class ObjectListField(DjangoListField):
"""
Retrieve a list of objects, optionally filtered by one or more FilterSet filters.
"""
def __init__(self, _type, *args, **kwargs):
filter_kwargs = {}
# Get FilterSet kwargs
filterset_class = getattr(_type._meta, 'filterset_class', None)
if filterset_class:
for filter_name, filter_field in filterset_class.get_filters().items():
field_type = get_graphene_type(type(filter_field))
filter_kwargs[filter_name] = graphene.Argument(field_type)
super().__init__(_type, args=filter_kwargs, *args, **kwargs)
@staticmethod
def list_resolver(django_object_type, resolver, default_manager, root, info, **args):
queryset = super(ObjectListField, ObjectListField).list_resolver(django_object_type, resolver, default_manager, root, info, **args)
# if there are no filter params then don't need to filter
if not args:
return queryset
filterset_class = django_object_type._meta.filterset_class
if filterset_class:
filterset = filterset_class(data=args if args else None, queryset=queryset, request=info.context)
if not filterset.is_valid():
return queryset.none()
return filterset.qs
return queryset

View File

@ -0,0 +1,198 @@
from functools import partial, partialmethod, wraps
from typing import List
import django_filters
import strawberry
import strawberry_django
from strawberry import auto
from ipam.fields import ASNField
from netbox.graphql.scalars import BigInt
from utilities.fields import ColorField, CounterCacheField
from utilities.filters import *
def map_strawberry_type(field):
should_create_function = False
attr_type = None
# NetBox Filter types - put base classes after derived classes
if isinstance(field, ContentTypeFilter):
should_create_function = True
attr_type = str | None
elif isinstance(field, MultiValueArrayFilter):
pass
elif isinstance(field, MultiValueCharFilter):
should_create_function = True
attr_type = List[str] | None
elif isinstance(field, MultiValueDateFilter):
attr_type = auto
elif isinstance(field, MultiValueDateTimeFilter):
attr_type = auto
elif isinstance(field, MultiValueDecimalFilter):
pass
elif isinstance(field, MultiValueMACAddressFilter):
should_create_function = True
attr_type = List[str] | None
elif isinstance(field, MultiValueNumberFilter):
should_create_function = True
attr_type = List[str] | None
elif isinstance(field, MultiValueTimeFilter):
pass
elif isinstance(field, MultiValueWWNFilter):
should_create_function = True
attr_type = List[str] | None
elif isinstance(field, NullableCharFieldFilter):
pass
elif isinstance(field, NumericArrayFilter):
should_create_function = True
attr_type = int
elif isinstance(field, TreeNodeMultipleChoiceFilter):
should_create_function = True
attr_type = List[str] | None
# From django_filters - ordering of these matters as base classes must
# come after derived classes so the base class doesn't get matched first
# a pass for the check (no attr_type) means we don't currently handle
# or use that type
elif issubclass(type(field), django_filters.OrderingFilter):
pass
elif issubclass(type(field), django_filters.BaseRangeFilter):
pass
elif issubclass(type(field), django_filters.BaseInFilter):
pass
elif issubclass(type(field), django_filters.LookupChoiceFilter):
pass
elif issubclass(type(field), django_filters.AllValuesMultipleFilter):
pass
elif issubclass(type(field), django_filters.AllValuesFilter):
pass
elif issubclass(type(field), django_filters.TimeRangeFilter):
pass
elif issubclass(type(field), django_filters.IsoDateTimeFromToRangeFilter):
should_create_function = True
attr_type = str | None
elif issubclass(type(field), django_filters.DateTimeFromToRangeFilter):
should_create_function = True
attr_type = str | None
elif issubclass(type(field), django_filters.DateFromToRangeFilter):
should_create_function = True
attr_type = str | None
elif issubclass(type(field), django_filters.DateRangeFilter):
should_create_function = True
attr_type = str | None
elif issubclass(type(field), django_filters.RangeFilter):
pass
elif issubclass(type(field), django_filters.NumericRangeFilter):
pass
elif issubclass(type(field), django_filters.NumberFilter):
should_create_function = True
attr_type = int
elif issubclass(type(field), django_filters.ModelMultipleChoiceFilter):
should_create_function = True
attr_type = List[str] | None
elif issubclass(type(field), django_filters.ModelChoiceFilter):
should_create_function = True
attr_type = str | None
elif issubclass(type(field), django_filters.DurationFilter):
pass
elif issubclass(type(field), django_filters.IsoDateTimeFilter):
pass
elif issubclass(type(field), django_filters.DateTimeFilter):
attr_type = auto
elif issubclass(type(field), django_filters.TimeFilter):
attr_type = auto
elif issubclass(type(field), django_filters.DateFilter):
attr_type = auto
elif issubclass(type(field), django_filters.TypedMultipleChoiceFilter):
pass
elif issubclass(type(field), django_filters.MultipleChoiceFilter):
should_create_function = True
attr_type = List[str] | None
elif issubclass(type(field), django_filters.TypedChoiceFilter):
pass
elif issubclass(type(field), django_filters.ChoiceFilter):
pass
elif issubclass(type(field), django_filters.BooleanFilter):
should_create_function = True
attr_type = bool | None
elif issubclass(type(field), django_filters.UUIDFilter):
should_create_function = True
attr_type = str | None
elif issubclass(type(field), django_filters.CharFilter):
# looks like only used by 'q'
should_create_function = True
attr_type = str | None
return should_create_function, attr_type
def autotype_decorator(filterset):
"""
Decorator used to auto creates a dataclass used by Strawberry based on a filterset.
Must go after the Strawberry decorator as follows:
@strawberry_django.filter(models.Example, lookups=True)
@autotype_decorator(filtersets.ExampleFilterSet)
class ExampleFilter(BaseFilterMixin):
pass
The Filter itself must be derived from BaseFilterMixin. For items listed in meta.fields
of the filterset, usually just a type specifier is generated, so for
`fields = [created, ]` the dataclass would be:
class ExampleFilter(BaseFilterMixin):
created: auto
For other filter fields a function needs to be created for Strawberry with the
naming convention `filter_{fieldname}` which is auto detected and called by
Strawberry, this function uses the filterset to handle the query.
"""
def create_attribute_and_function(cls, fieldname, attr_type, should_create_function):
if fieldname not in cls.__annotations__ and attr_type:
cls.__annotations__[fieldname] = attr_type
filter_name = f"filter_{fieldname}"
if should_create_function and not hasattr(cls, filter_name):
filter_by_filterset = getattr(cls, 'filter_by_filterset')
setattr(cls, filter_name, partialmethod(filter_by_filterset, key=fieldname))
def wrapper(cls):
cls.filterset = filterset
fields = filterset.get_fields()
model = filterset._meta.model
for fieldname in fields.keys():
should_create_function = False
attr_type = auto
if fieldname not in cls.__annotations__:
field = model._meta.get_field(fieldname)
if isinstance(field, CounterCacheField):
should_create_function = True
attr_type = BigInt | None
elif isinstance(field, ASNField):
should_create_function = True
attr_type = List[str] | None
elif isinstance(field, ColorField):
should_create_function = True
attr_type = List[str] | None
create_attribute_and_function(cls, fieldname, attr_type, should_create_function)
declared_filters = filterset.declared_filters
for fieldname, field in declared_filters.items():
should_create_function, attr_type = map_strawberry_type(field)
if attr_type is None:
raise NotImplementedError(f"GraphQL Filter field unknown: {fieldname}: {field}")
create_attribute_and_function(cls, fieldname, attr_type, should_create_function)
return cls
return wrapper
@strawberry.input
class BaseFilterMixin:
def filter_by_filterset(self, queryset, key):
return self.filterset(data={key: getattr(self, key)}, queryset=queryset).qs

View File

@ -1,23 +1,10 @@
from graphene import Scalar from typing import Union
from graphql.language import ast
from graphene.types.scalars import MAX_INT, MIN_INT
import strawberry
class BigInt(Scalar): BigInt = strawberry.scalar(
""" Union[int, str], # type: ignore
Handle any BigInts serialize=lambda v: int(v),
""" parse_value=lambda v: str(v),
@staticmethod description="BigInt field",
def to_float(value): )
num = int(value)
if num > MAX_INT or num < MIN_INT:
return float(num)
return num
serialize = to_float
parse_value = to_float
@staticmethod
def parse_literal(node):
if isinstance(node, ast.IntValue):
return BigInt.to_float(node.value)

View File

@ -1,4 +1,6 @@
import graphene import strawberry
from strawberry_django.optimizer import DjangoOptimizerExtension
from strawberry.schema.config import StrawberryConfig
from circuits.graphql.schema import CircuitsQuery from circuits.graphql.schema import CircuitsQuery
from core.graphql.schema import CoreQuery from core.graphql.schema import CoreQuery
@ -13,6 +15,7 @@ from vpn.graphql.schema import VPNQuery
from wireless.graphql.schema import WirelessQuery from wireless.graphql.schema import WirelessQuery
@strawberry.type
class Query( class Query(
UsersQuery, UsersQuery,
CircuitsQuery, CircuitsQuery,
@ -25,9 +28,14 @@ class Query(
VPNQuery, VPNQuery,
WirelessQuery, WirelessQuery,
*registry['plugins']['graphql_schemas'], # Append plugin schemas *registry['plugins']['graphql_schemas'], # Append plugin schemas
graphene.ObjectType
): ):
pass pass
schema = graphene.Schema(query=Query, auto_camelcase=False) schema = strawberry.Schema(
query=Query,
config=StrawberryConfig(auto_camel_case=False),
extensions=[
DjangoOptimizerExtension,
]
)

View File

@ -1,4 +1,8 @@
import graphene from typing import Annotated, List
import strawberry
from strawberry import auto
import strawberry_django
from core.models import ObjectType as ObjectType_ from core.models import ObjectType as ObjectType_
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
@ -8,13 +12,10 @@ from extras.graphql.mixins import (
JournalEntriesMixin, JournalEntriesMixin,
TagsMixin, TagsMixin,
) )
from graphene_django import DjangoObjectType
__all__ = ( __all__ = (
'BaseObjectType', 'BaseObjectType',
'ContentTypeType',
'ObjectType', 'ObjectType',
'ObjectTypeType',
'OrganizationalObjectType', 'OrganizationalObjectType',
'NetBoxObjectType', 'NetBoxObjectType',
) )
@ -24,26 +25,27 @@ __all__ = (
# Base types # Base types
# #
class BaseObjectType(DjangoObjectType): @strawberry.type
class BaseObjectType:
""" """
Base GraphQL object type for all NetBox objects. Restricts the model queryset to enforce object permissions. Base GraphQL object type for all NetBox objects. Restricts the model queryset to enforce object permissions.
""" """
display = graphene.String()
class_type = graphene.String()
class Meta:
abstract = True
@classmethod @classmethod
def get_queryset(cls, queryset, info): def get_queryset(cls, queryset, info, **kwargs):
# Enforce object permissions on the queryset # Enforce object permissions on the queryset
return queryset.restrict(info.context.user, 'view') if hasattr(queryset, 'restrict'):
return queryset.restrict(info.context.request.user, 'view')
else:
return queryset
def resolve_display(parent, info, **kwargs): @strawberry_django.field
return str(parent) def display(self) -> str:
return str(self)
def resolve_class_type(parent, info, **kwargs): @strawberry_django.field
return parent.__class__.__name__ def class_type(self) -> str:
return self.__class__.__name__
class ObjectType( class ObjectType(
@ -53,8 +55,7 @@ class ObjectType(
""" """
Base GraphQL object type for unclassified models which support change logging Base GraphQL object type for unclassified models which support change logging
""" """
class Meta: pass
abstract = True
class OrganizationalObjectType( class OrganizationalObjectType(
@ -66,8 +67,7 @@ class OrganizationalObjectType(
""" """
Base type for organizational models Base type for organizational models
""" """
class Meta: pass
abstract = True
class NetBoxObjectType( class NetBoxObjectType(
@ -80,23 +80,24 @@ class NetBoxObjectType(
""" """
GraphQL type for most NetBox models. Includes support for custom fields, change logging, journaling, and tags. GraphQL type for most NetBox models. Includes support for custom fields, change logging, journaling, and tags.
""" """
class Meta: pass
abstract = True
# #
# Miscellaneous types # Miscellaneous types
# #
class ContentTypeType(DjangoObjectType): @strawberry_django.type(
ContentType,
class Meta: fields=['id', 'app_label', 'model'],
model = ContentType )
fields = ('id', 'app_label', 'model') class ContentTypeType:
pass
class ObjectTypeType(DjangoObjectType): @strawberry_django.type(
ObjectType_,
class Meta: fields=['id', 'app_label', 'model'],
model = ObjectType_ )
fields = ('id', 'app_label', 'model') class ObjectTypeType:
pass

View File

@ -1,25 +0,0 @@
import graphene
from django_filters import filters
def get_graphene_type(filter_cls):
"""
Return the appropriate Graphene scalar type for a django_filters Filter
"""
if issubclass(filter_cls, filters.BooleanFilter):
field_type = graphene.Boolean
elif issubclass(filter_cls, filters.NumberFilter):
# TODO: Floats? BigInts?
field_type = graphene.Int
elif issubclass(filter_cls, filters.DateFilter):
field_type = graphene.Date
elif issubclass(filter_cls, filters.DateTimeFilter):
field_type = graphene.DateTime
else:
field_type = graphene.String
# Multi-value filters should be handled as lists
if issubclass(filter_cls, filters.MultipleChoiceFilter):
return graphene.List(field_type)
return field_type

View File

@ -1,20 +1,26 @@
import json
from django.conf import settings from django.conf import settings
from django.contrib.auth.views import redirect_to_login from django.contrib.auth.views import redirect_to_login
from django.http import HttpResponseNotFound, HttpResponseForbidden from django.http import HttpResponseNotFound, HttpResponseForbidden
from django.http import HttpResponse
from django.template import loader
from django.urls import reverse from django.urls import reverse
from graphene_django.views import GraphQLView as GraphQLView_ from django.views.decorators.csrf import csrf_exempt
from rest_framework.exceptions import AuthenticationFailed from rest_framework.exceptions import AuthenticationFailed
from strawberry.django.views import GraphQLView
from netbox.api.authentication import TokenAuthentication from netbox.api.authentication import TokenAuthentication
from netbox.config import get_config from netbox.config import get_config
class GraphQLView(GraphQLView_): class NetBoxGraphQLView(GraphQLView):
""" """
Extends graphene_django's GraphQLView to support DRF's token-based authentication. Extends strawberry's GraphQLView to support DRF's token-based authentication.
""" """
graphiql_template = 'graphiql.html' graphiql_template = 'graphiql.html'
@csrf_exempt
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
config = get_config() config = get_config()
@ -34,11 +40,15 @@ class GraphQLView(GraphQLView_):
# Enforce LOGIN_REQUIRED # Enforce LOGIN_REQUIRED
if settings.LOGIN_REQUIRED and not request.user.is_authenticated: if settings.LOGIN_REQUIRED and not request.user.is_authenticated:
if request.accepts("text/html"):
# If this is a human user, send a redirect to the login page
if self.request_wants_html(request):
return redirect_to_login(reverse('graphql')) return redirect_to_login(reverse('graphql'))
else:
return HttpResponseForbidden("No credentials provided.") return HttpResponseForbidden("No credentials provided.")
return super().dispatch(request, *args, **kwargs) return super().dispatch(request, *args, **kwargs)
def render_graphql_ide(self, request):
template = loader.get_template("graphiql.html")
context = {"SUBSCRIPTION_ENABLED": json.dumps(self.subscriptions_enabled)}
return HttpResponse(template.render(context, request))

View File

@ -73,7 +73,7 @@ def register_graphql_schema(graphql_schema):
""" """
Register a GraphQL schema class for inclusion in NetBox's GraphQL API. Register a GraphQL schema class for inclusion in NetBox's GraphQL API.
""" """
registry['plugins']['graphql_schemas'].append(graphql_schema) registry['plugins']['graphql_schemas'].extend(graphql_schema)
def register_user_preferences(plugin_name, preferences): def register_user_preferences(plugin_name, preferences):

View File

@ -15,25 +15,17 @@ from django.core.validators import URLValidator
from django.utils.encoding import force_str from django.utils.encoding import force_str
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
try: from netbox.config import PARAMS as CONFIG_PARAMS
import sentry_sdk
except ModuleNotFoundError:
pass
from netbox.config import PARAMS
from netbox.constants import RQ_QUEUE_DEFAULT, RQ_QUEUE_HIGH, RQ_QUEUE_LOW from netbox.constants import RQ_QUEUE_DEFAULT, RQ_QUEUE_HIGH, RQ_QUEUE_LOW
from netbox.plugins import PluginConfig from netbox.plugins import PluginConfig
from utilities.string import trailing_slash
# #
# Environment setup # Environment setup
# #
VERSION = '4.0.0-dev' VERSION = '4.0.0-dev'
# Hostname
HOSTNAME = platform.node() HOSTNAME = platform.node()
# Set the base directory two levels up # Set the base directory two levels up
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
@ -47,7 +39,7 @@ if sys.version_info < (3, 10):
# Configuration import # Configuration import
# #
# Import configuration parameters # Import the configuration module
config_path = os.getenv('NETBOX_CONFIGURATION', 'netbox.configuration') config_path = os.getenv('NETBOX_CONFIGURATION', 'netbox.configuration')
try: try:
configuration = importlib.import_module(config_path) configuration = importlib.import_module(config_path)
@ -59,45 +51,28 @@ except ModuleNotFoundError as e:
) )
raise raise
# Enforce required configuration parameters # Check for missing required configuration parameters
for parameter in ['ALLOWED_HOSTS', 'DATABASE', 'SECRET_KEY', 'REDIS']: for parameter in ('ALLOWED_HOSTS', 'DATABASE', 'SECRET_KEY', 'REDIS'):
if not hasattr(configuration, parameter): if not hasattr(configuration, parameter):
raise ImproperlyConfigured(f"Required parameter {parameter} is missing from configuration.") raise ImproperlyConfigured(f"Required parameter {parameter} is missing from configuration.")
# Set required parameters
ALLOWED_HOSTS = getattr(configuration, 'ALLOWED_HOSTS')
DATABASE = getattr(configuration, 'DATABASE')
REDIS = getattr(configuration, 'REDIS')
SECRET_KEY = getattr(configuration, 'SECRET_KEY')
# Enforce minimum length for SECRET_KEY
if type(SECRET_KEY) is not str:
raise ImproperlyConfigured(f"SECRET_KEY must be a string (found {type(SECRET_KEY).__name__})")
if len(SECRET_KEY) < 50:
raise ImproperlyConfigured(
f"SECRET_KEY must be at least 50 characters in length. To generate a suitable key, run the following command:\n"
f" python {BASE_DIR}/generate_secret_key.py"
)
# Calculate a unique deployment ID from the secret key
DEPLOYMENT_ID = hashlib.sha256(SECRET_KEY.encode('utf-8')).hexdigest()[:16]
# Set static config parameters # Set static config parameters
ADMINS = getattr(configuration, 'ADMINS', []) ADMINS = getattr(configuration, 'ADMINS', [])
ALLOW_TOKEN_RETRIEVAL = getattr(configuration, 'ALLOW_TOKEN_RETRIEVAL', True) ALLOW_TOKEN_RETRIEVAL = getattr(configuration, 'ALLOW_TOKEN_RETRIEVAL', True)
ALLOWED_HOSTS = getattr(configuration, 'ALLOWED_HOSTS') # Required
AUTH_PASSWORD_VALIDATORS = getattr(configuration, 'AUTH_PASSWORD_VALIDATORS', []) AUTH_PASSWORD_VALIDATORS = getattr(configuration, 'AUTH_PASSWORD_VALIDATORS', [])
BASE_PATH = getattr(configuration, 'BASE_PATH', '') BASE_PATH = trailing_slash(getattr(configuration, 'BASE_PATH', ''))
if BASE_PATH: CHANGELOG_SKIP_EMPTY_CHANGES = getattr(configuration, 'CHANGELOG_SKIP_EMPTY_CHANGES', True)
BASE_PATH = BASE_PATH.strip('/') + '/' # Enforce trailing slash only
CSRF_COOKIE_PATH = LANGUAGE_COOKIE_PATH = SESSION_COOKIE_PATH = f'/{BASE_PATH.rstrip("/")}'
CENSUS_REPORTING_ENABLED = getattr(configuration, 'CENSUS_REPORTING_ENABLED', True) CENSUS_REPORTING_ENABLED = getattr(configuration, 'CENSUS_REPORTING_ENABLED', True)
CORS_ORIGIN_ALLOW_ALL = getattr(configuration, 'CORS_ORIGIN_ALLOW_ALL', False) CORS_ORIGIN_ALLOW_ALL = getattr(configuration, 'CORS_ORIGIN_ALLOW_ALL', False)
CORS_ORIGIN_REGEX_WHITELIST = getattr(configuration, 'CORS_ORIGIN_REGEX_WHITELIST', []) CORS_ORIGIN_REGEX_WHITELIST = getattr(configuration, 'CORS_ORIGIN_REGEX_WHITELIST', [])
CORS_ORIGIN_WHITELIST = getattr(configuration, 'CORS_ORIGIN_WHITELIST', []) CORS_ORIGIN_WHITELIST = getattr(configuration, 'CORS_ORIGIN_WHITELIST', [])
CSRF_COOKIE_NAME = getattr(configuration, 'CSRF_COOKIE_NAME', 'csrftoken') CSRF_COOKIE_NAME = getattr(configuration, 'CSRF_COOKIE_NAME', 'csrftoken')
CSRF_COOKIE_PATH = f'/{BASE_PATH.rstrip("/")}'
CSRF_COOKIE_SECURE = getattr(configuration, 'CSRF_COOKIE_SECURE', False) CSRF_COOKIE_SECURE = getattr(configuration, 'CSRF_COOKIE_SECURE', False)
CSRF_TRUSTED_ORIGINS = getattr(configuration, 'CSRF_TRUSTED_ORIGINS', []) CSRF_TRUSTED_ORIGINS = getattr(configuration, 'CSRF_TRUSTED_ORIGINS', [])
DATA_UPLOAD_MAX_MEMORY_SIZE = getattr(configuration, 'DATA_UPLOAD_MAX_MEMORY_SIZE', 2621440) DATA_UPLOAD_MAX_MEMORY_SIZE = getattr(configuration, 'DATA_UPLOAD_MAX_MEMORY_SIZE', 2621440)
DATABASE = getattr(configuration, 'DATABASE') # Required
DATE_FORMAT = getattr(configuration, 'DATE_FORMAT', 'N j, Y') DATE_FORMAT = getattr(configuration, 'DATE_FORMAT', 'N j, Y')
DATETIME_FORMAT = getattr(configuration, 'DATETIME_FORMAT', 'N j, Y g:i a') DATETIME_FORMAT = getattr(configuration, 'DATETIME_FORMAT', 'N j, Y g:i a')
DEBUG = getattr(configuration, 'DEBUG', False) DEBUG = getattr(configuration, 'DEBUG', False)
@ -118,6 +93,7 @@ DEVELOPER = getattr(configuration, 'DEVELOPER', False)
DJANGO_ADMIN_ENABLED = getattr(configuration, 'DJANGO_ADMIN_ENABLED', False) DJANGO_ADMIN_ENABLED = getattr(configuration, 'DJANGO_ADMIN_ENABLED', False)
DOCS_ROOT = getattr(configuration, 'DOCS_ROOT', os.path.join(os.path.dirname(BASE_DIR), 'docs')) DOCS_ROOT = getattr(configuration, 'DOCS_ROOT', os.path.join(os.path.dirname(BASE_DIR), 'docs'))
EMAIL = getattr(configuration, 'EMAIL', {}) EMAIL = getattr(configuration, 'EMAIL', {})
ENABLE_LOCALIZATION = getattr(configuration, 'ENABLE_LOCALIZATION', False)
EVENTS_PIPELINE = getattr(configuration, 'EVENTS_PIPELINE', ( EVENTS_PIPELINE = getattr(configuration, 'EVENTS_PIPELINE', (
'extras.events.process_event_queue', 'extras.events.process_event_queue',
)) ))
@ -128,6 +104,7 @@ HTTP_PROXIES = getattr(configuration, 'HTTP_PROXIES', None)
INTERNAL_IPS = getattr(configuration, 'INTERNAL_IPS', ('127.0.0.1', '::1')) INTERNAL_IPS = getattr(configuration, 'INTERNAL_IPS', ('127.0.0.1', '::1'))
JINJA2_FILTERS = getattr(configuration, 'JINJA2_FILTERS', {}) JINJA2_FILTERS = getattr(configuration, 'JINJA2_FILTERS', {})
LANGUAGE_CODE = getattr(configuration, 'DEFAULT_LANGUAGE', 'en-us') LANGUAGE_CODE = getattr(configuration, 'DEFAULT_LANGUAGE', 'en-us')
LANGUAGE_COOKIE_PATH = CSRF_COOKIE_PATH
LOGGING = getattr(configuration, 'LOGGING', {}) LOGGING = getattr(configuration, 'LOGGING', {})
LOGIN_PERSISTENCE = getattr(configuration, 'LOGIN_PERSISTENCE', False) LOGIN_PERSISTENCE = getattr(configuration, 'LOGIN_PERSISTENCE', False)
LOGIN_REQUIRED = getattr(configuration, 'LOGIN_REQUIRED', False) LOGIN_REQUIRED = getattr(configuration, 'LOGIN_REQUIRED', False)
@ -138,24 +115,25 @@ METRICS_ENABLED = getattr(configuration, 'METRICS_ENABLED', False)
PLUGINS = getattr(configuration, 'PLUGINS', []) PLUGINS = getattr(configuration, 'PLUGINS', [])
PLUGINS_CONFIG = getattr(configuration, 'PLUGINS_CONFIG', {}) PLUGINS_CONFIG = getattr(configuration, 'PLUGINS_CONFIG', {})
QUEUE_MAPPINGS = getattr(configuration, 'QUEUE_MAPPINGS', {}) QUEUE_MAPPINGS = getattr(configuration, 'QUEUE_MAPPINGS', {})
REDIS = getattr(configuration, 'REDIS') # Required
RELEASE_CHECK_URL = getattr(configuration, 'RELEASE_CHECK_URL', None) RELEASE_CHECK_URL = getattr(configuration, 'RELEASE_CHECK_URL', None)
REMOTE_AUTH_AUTO_CREATE_USER = getattr(configuration, 'REMOTE_AUTH_AUTO_CREATE_USER', False)
REMOTE_AUTH_AUTO_CREATE_GROUPS = getattr(configuration, 'REMOTE_AUTH_AUTO_CREATE_GROUPS', False) REMOTE_AUTH_AUTO_CREATE_GROUPS = getattr(configuration, 'REMOTE_AUTH_AUTO_CREATE_GROUPS', False)
REMOTE_AUTH_AUTO_CREATE_USER = getattr(configuration, 'REMOTE_AUTH_AUTO_CREATE_USER', False)
REMOTE_AUTH_BACKEND = getattr(configuration, 'REMOTE_AUTH_BACKEND', 'netbox.authentication.RemoteUserBackend') REMOTE_AUTH_BACKEND = getattr(configuration, 'REMOTE_AUTH_BACKEND', 'netbox.authentication.RemoteUserBackend')
REMOTE_AUTH_DEFAULT_GROUPS = getattr(configuration, 'REMOTE_AUTH_DEFAULT_GROUPS', []) REMOTE_AUTH_DEFAULT_GROUPS = getattr(configuration, 'REMOTE_AUTH_DEFAULT_GROUPS', [])
REMOTE_AUTH_DEFAULT_PERMISSIONS = getattr(configuration, 'REMOTE_AUTH_DEFAULT_PERMISSIONS', {}) REMOTE_AUTH_DEFAULT_PERMISSIONS = getattr(configuration, 'REMOTE_AUTH_DEFAULT_PERMISSIONS', {})
REMOTE_AUTH_ENABLED = getattr(configuration, 'REMOTE_AUTH_ENABLED', False) REMOTE_AUTH_ENABLED = getattr(configuration, 'REMOTE_AUTH_ENABLED', False)
REMOTE_AUTH_HEADER = getattr(configuration, 'REMOTE_AUTH_HEADER', 'HTTP_REMOTE_USER')
REMOTE_AUTH_USER_FIRST_NAME = getattr(configuration, 'REMOTE_AUTH_USER_FIRST_NAME', 'HTTP_REMOTE_USER_FIRST_NAME')
REMOTE_AUTH_USER_LAST_NAME = getattr(configuration, 'REMOTE_AUTH_USER_LAST_NAME', 'HTTP_REMOTE_USER_LAST_NAME')
REMOTE_AUTH_USER_EMAIL = getattr(configuration, 'REMOTE_AUTH_USER_EMAIL', 'HTTP_REMOTE_USER_EMAIL')
REMOTE_AUTH_GROUP_HEADER = getattr(configuration, 'REMOTE_AUTH_GROUP_HEADER', 'HTTP_REMOTE_USER_GROUP') REMOTE_AUTH_GROUP_HEADER = getattr(configuration, 'REMOTE_AUTH_GROUP_HEADER', 'HTTP_REMOTE_USER_GROUP')
REMOTE_AUTH_GROUP_SEPARATOR = getattr(configuration, 'REMOTE_AUTH_GROUP_SEPARATOR', '|')
REMOTE_AUTH_GROUP_SYNC_ENABLED = getattr(configuration, 'REMOTE_AUTH_GROUP_SYNC_ENABLED', False) REMOTE_AUTH_GROUP_SYNC_ENABLED = getattr(configuration, 'REMOTE_AUTH_GROUP_SYNC_ENABLED', False)
REMOTE_AUTH_HEADER = getattr(configuration, 'REMOTE_AUTH_HEADER', 'HTTP_REMOTE_USER')
REMOTE_AUTH_SUPERUSER_GROUPS = getattr(configuration, 'REMOTE_AUTH_SUPERUSER_GROUPS', []) REMOTE_AUTH_SUPERUSER_GROUPS = getattr(configuration, 'REMOTE_AUTH_SUPERUSER_GROUPS', [])
REMOTE_AUTH_SUPERUSERS = getattr(configuration, 'REMOTE_AUTH_SUPERUSERS', []) REMOTE_AUTH_SUPERUSERS = getattr(configuration, 'REMOTE_AUTH_SUPERUSERS', [])
REMOTE_AUTH_USER_EMAIL = getattr(configuration, 'REMOTE_AUTH_USER_EMAIL', 'HTTP_REMOTE_USER_EMAIL')
REMOTE_AUTH_USER_FIRST_NAME = getattr(configuration, 'REMOTE_AUTH_USER_FIRST_NAME', 'HTTP_REMOTE_USER_FIRST_NAME')
REMOTE_AUTH_USER_LAST_NAME = getattr(configuration, 'REMOTE_AUTH_USER_LAST_NAME', 'HTTP_REMOTE_USER_LAST_NAME')
REMOTE_AUTH_STAFF_GROUPS = getattr(configuration, 'REMOTE_AUTH_STAFF_GROUPS', []) REMOTE_AUTH_STAFF_GROUPS = getattr(configuration, 'REMOTE_AUTH_STAFF_GROUPS', [])
REMOTE_AUTH_STAFF_USERS = getattr(configuration, 'REMOTE_AUTH_STAFF_USERS', []) REMOTE_AUTH_STAFF_USERS = getattr(configuration, 'REMOTE_AUTH_STAFF_USERS', [])
REMOTE_AUTH_GROUP_SEPARATOR = getattr(configuration, 'REMOTE_AUTH_GROUP_SEPARATOR', '|')
# Required by extras/migrations/0109_script_models.py # Required by extras/migrations/0109_script_models.py
REPORTS_ROOT = getattr(configuration, 'REPORTS_ROOT', os.path.join(BASE_DIR, 'reports')).rstrip('/') REPORTS_ROOT = getattr(configuration, 'REPORTS_ROOT', os.path.join(BASE_DIR, 'reports')).rstrip('/')
RQ_DEFAULT_TIMEOUT = getattr(configuration, 'RQ_DEFAULT_TIMEOUT', 300) RQ_DEFAULT_TIMEOUT = getattr(configuration, 'RQ_DEFAULT_TIMEOUT', 300)
@ -163,15 +141,17 @@ RQ_RETRY_INTERVAL = getattr(configuration, 'RQ_RETRY_INTERVAL', 60)
RQ_RETRY_MAX = getattr(configuration, 'RQ_RETRY_MAX', 0) RQ_RETRY_MAX = getattr(configuration, 'RQ_RETRY_MAX', 0)
SCRIPTS_ROOT = getattr(configuration, 'SCRIPTS_ROOT', os.path.join(BASE_DIR, 'scripts')).rstrip('/') SCRIPTS_ROOT = getattr(configuration, 'SCRIPTS_ROOT', os.path.join(BASE_DIR, 'scripts')).rstrip('/')
SEARCH_BACKEND = getattr(configuration, 'SEARCH_BACKEND', 'netbox.search.backends.CachedValueSearchBackend') SEARCH_BACKEND = getattr(configuration, 'SEARCH_BACKEND', 'netbox.search.backends.CachedValueSearchBackend')
SECRET_KEY = getattr(configuration, 'SECRET_KEY') # Required
SECURE_SSL_REDIRECT = getattr(configuration, 'SECURE_SSL_REDIRECT', False) SECURE_SSL_REDIRECT = getattr(configuration, 'SECURE_SSL_REDIRECT', False)
SENTRY_DSN = getattr(configuration, 'SENTRY_DSN', None) SENTRY_DSN = getattr(configuration, 'SENTRY_DSN', None)
SENTRY_ENABLED = getattr(configuration, 'SENTRY_ENABLED', False) SENTRY_ENABLED = getattr(configuration, 'SENTRY_ENABLED', False)
SENTRY_SAMPLE_RATE = getattr(configuration, 'SENTRY_SAMPLE_RATE', 1.0) SENTRY_SAMPLE_RATE = getattr(configuration, 'SENTRY_SAMPLE_RATE', 1.0)
SENTRY_TRACES_SAMPLE_RATE = getattr(configuration, 'SENTRY_TRACES_SAMPLE_RATE', 0)
SENTRY_TAGS = getattr(configuration, 'SENTRY_TAGS', {}) SENTRY_TAGS = getattr(configuration, 'SENTRY_TAGS', {})
SESSION_FILE_PATH = getattr(configuration, 'SESSION_FILE_PATH', None) SENTRY_TRACES_SAMPLE_RATE = getattr(configuration, 'SENTRY_TRACES_SAMPLE_RATE', 0)
SESSION_COOKIE_NAME = getattr(configuration, 'SESSION_COOKIE_NAME', 'sessionid') SESSION_COOKIE_NAME = getattr(configuration, 'SESSION_COOKIE_NAME', 'sessionid')
SESSION_COOKIE_PATH = CSRF_COOKIE_PATH
SESSION_COOKIE_SECURE = getattr(configuration, 'SESSION_COOKIE_SECURE', False) SESSION_COOKIE_SECURE = getattr(configuration, 'SESSION_COOKIE_SECURE', False)
SESSION_FILE_PATH = getattr(configuration, 'SESSION_FILE_PATH', None)
SHORT_DATE_FORMAT = getattr(configuration, 'SHORT_DATE_FORMAT', 'Y-m-d') SHORT_DATE_FORMAT = getattr(configuration, 'SHORT_DATE_FORMAT', 'Y-m-d')
SHORT_DATETIME_FORMAT = getattr(configuration, 'SHORT_DATETIME_FORMAT', 'Y-m-d H:i') SHORT_DATETIME_FORMAT = getattr(configuration, 'SHORT_DATETIME_FORMAT', 'Y-m-d H:i')
SHORT_TIME_FORMAT = getattr(configuration, 'SHORT_TIME_FORMAT', 'H:i:s') SHORT_TIME_FORMAT = getattr(configuration, 'SHORT_TIME_FORMAT', 'H:i:s')
@ -179,50 +159,50 @@ STORAGE_BACKEND = getattr(configuration, 'STORAGE_BACKEND', None)
STORAGE_CONFIG = getattr(configuration, 'STORAGE_CONFIG', {}) STORAGE_CONFIG = getattr(configuration, 'STORAGE_CONFIG', {})
TIME_FORMAT = getattr(configuration, 'TIME_FORMAT', 'g:i a') TIME_FORMAT = getattr(configuration, 'TIME_FORMAT', 'g:i a')
TIME_ZONE = getattr(configuration, 'TIME_ZONE', 'UTC') TIME_ZONE = getattr(configuration, 'TIME_ZONE', 'UTC')
ENABLE_LOCALIZATION = getattr(configuration, 'ENABLE_LOCALIZATION', False)
CHANGELOG_SKIP_EMPTY_CHANGES = getattr(configuration, 'CHANGELOG_SKIP_EMPTY_CHANGES', True)
# Check for hard-coded dynamic config parameters # Load any dynamic configuration parameters which have been hard-coded in the configuration file
for param in PARAMS: for param in CONFIG_PARAMS:
if hasattr(configuration, param.name): if hasattr(configuration, param.name):
globals()[param.name] = getattr(configuration, param.name) globals()[param.name] = getattr(configuration, param.name)
# Enforce minimum length for SECRET_KEY
if type(SECRET_KEY) is not str:
raise ImproperlyConfigured(f"SECRET_KEY must be a string (found {type(SECRET_KEY).__name__})")
if len(SECRET_KEY) < 50:
raise ImproperlyConfigured(
f"SECRET_KEY must be at least 50 characters in length. To generate a suitable key, run the following command:\n"
f" python {BASE_DIR}/generate_secret_key.py"
)
# Validate update repo URL and timeout # Validate update repo URL and timeout
if RELEASE_CHECK_URL: if RELEASE_CHECK_URL:
validator = URLValidator(
message=(
"RELEASE_CHECK_URL must be a valid API URL. Example: "
"https://api.github.com/repos/netbox-community/netbox"
)
)
try: try:
validator(RELEASE_CHECK_URL) URLValidator()(RELEASE_CHECK_URL)
except ValidationError as err: except ValidationError as e:
raise ImproperlyConfigured(str(err)) raise ImproperlyConfigured(
"RELEASE_CHECK_URL must be a valid URL. Example: https://api.github.com/repos/netbox-community/netbox"
)
# #
# Database # Database
# #
# Set the database engine
if 'ENGINE' not in DATABASE: if 'ENGINE' not in DATABASE:
# Only PostgreSQL is supported
if METRICS_ENABLED: if METRICS_ENABLED:
DATABASE.update({ DATABASE.update({'ENGINE': 'django_prometheus.db.backends.postgresql'})
'ENGINE': 'django_prometheus.db.backends.postgresql'
})
else: else:
DATABASE.update({ DATABASE.update({'ENGINE': 'django.db.backends.postgresql'})
'ENGINE': 'django.db.backends.postgresql'
})
# Define the DATABASES setting for Django
DATABASES = { DATABASES = {
'default': DATABASE, 'default': DATABASE,
} }
# #
# Media storage # Storage backend
# #
if STORAGE_BACKEND is not None: if STORAGE_BACKEND is not None:
@ -230,7 +210,6 @@ if STORAGE_BACKEND is not None:
# django-storages # django-storages
if STORAGE_BACKEND.startswith('storages.'): if STORAGE_BACKEND.startswith('storages.'):
try: try:
import storages.utils # type: ignore import storages.utils # type: ignore
except ModuleNotFoundError as e: except ModuleNotFoundError as e:
@ -261,9 +240,7 @@ if STORAGE_CONFIG and STORAGE_BACKEND is None:
# Background task queuing # Background task queuing
if 'tasks' not in REDIS: if 'tasks' not in REDIS:
raise ImproperlyConfigured( raise ImproperlyConfigured("REDIS section in configuration.py is missing the 'tasks' subsection.")
"REDIS section in configuration.py is missing the 'tasks' subsection."
)
TASKS_REDIS = REDIS['tasks'] TASKS_REDIS = REDIS['tasks']
TASKS_REDIS_HOST = TASKS_REDIS.get('HOST', 'localhost') TASKS_REDIS_HOST = TASKS_REDIS.get('HOST', 'localhost')
TASKS_REDIS_PORT = TASKS_REDIS.get('PORT', 6379) TASKS_REDIS_PORT = TASKS_REDIS.get('PORT', 6379)
@ -283,9 +260,7 @@ TASKS_REDIS_CA_CERT_PATH = TASKS_REDIS.get('CA_CERT_PATH', False)
# Caching # Caching
if 'caching' not in REDIS: if 'caching' not in REDIS:
raise ImproperlyConfigured( raise ImproperlyConfigured("REDIS section in configuration.py is missing caching subsection.")
"REDIS section in configuration.py is missing caching subsection."
)
CACHING_REDIS_HOST = REDIS['caching'].get('HOST', 'localhost') CACHING_REDIS_HOST = REDIS['caching'].get('HOST', 'localhost')
CACHING_REDIS_PORT = REDIS['caching'].get('PORT', 6379) CACHING_REDIS_PORT = REDIS['caching'].get('PORT', 6379)
CACHING_REDIS_DATABASE = REDIS['caching'].get('DATABASE', 0) CACHING_REDIS_DATABASE = REDIS['caching'].get('DATABASE', 0)
@ -297,11 +272,13 @@ CACHING_REDIS_SENTINEL_SERVICE = REDIS['caching'].get('SENTINEL_SERVICE', 'defau
CACHING_REDIS_PROTO = 'rediss' if REDIS['caching'].get('SSL', False) else 'redis' CACHING_REDIS_PROTO = 'rediss' if REDIS['caching'].get('SSL', False) else 'redis'
CACHING_REDIS_SKIP_TLS_VERIFY = REDIS['caching'].get('INSECURE_SKIP_TLS_VERIFY', False) CACHING_REDIS_SKIP_TLS_VERIFY = REDIS['caching'].get('INSECURE_SKIP_TLS_VERIFY', False)
CACHING_REDIS_CA_CERT_PATH = REDIS['caching'].get('CA_CERT_PATH', False) CACHING_REDIS_CA_CERT_PATH = REDIS['caching'].get('CA_CERT_PATH', False)
CACHING_REDIS_URL = f'{CACHING_REDIS_PROTO}://{CACHING_REDIS_USERNAME_HOST}:{CACHING_REDIS_PORT}/{CACHING_REDIS_DATABASE}'
# Configure Django's default cache to use Redis
CACHES = { CACHES = {
'default': { 'default': {
'BACKEND': 'django_redis.cache.RedisCache', 'BACKEND': 'django_redis.cache.RedisCache',
'LOCATION': f'{CACHING_REDIS_PROTO}://{CACHING_REDIS_USERNAME_HOST}:{CACHING_REDIS_PORT}/{CACHING_REDIS_DATABASE}', 'LOCATION': CACHING_REDIS_URL,
'OPTIONS': { 'OPTIONS': {
'CLIENT_CLASS': 'django_redis.client.DefaultClient', 'CLIENT_CLASS': 'django_redis.client.DefaultClient',
'PASSWORD': CACHING_REDIS_PASSWORD, 'PASSWORD': CACHING_REDIS_PASSWORD,
@ -309,7 +286,6 @@ CACHES = {
} }
} }
if CACHING_REDIS_SENTINELS: if CACHING_REDIS_SENTINELS:
DJANGO_REDIS_CONNECTION_FACTORY = 'django_redis.pool.SentinelConnectionFactory' DJANGO_REDIS_CONNECTION_FACTORY = 'django_redis.pool.SentinelConnectionFactory'
CACHES['default']['LOCATION'] = f'{CACHING_REDIS_PROTO}://{CACHING_REDIS_SENTINEL_SERVICE}/{CACHING_REDIS_DATABASE}' CACHES['default']['LOCATION'] = f'{CACHING_REDIS_PROTO}://{CACHING_REDIS_SENTINEL_SERVICE}/{CACHING_REDIS_DATABASE}'
@ -322,6 +298,7 @@ if CACHING_REDIS_CA_CERT_PATH:
CACHES['default']['OPTIONS'].setdefault('CONNECTION_POOL_KWARGS', {}) CACHES['default']['OPTIONS'].setdefault('CONNECTION_POOL_KWARGS', {})
CACHES['default']['OPTIONS']['CONNECTION_POOL_KWARGS']['ssl_ca_certs'] = CACHING_REDIS_CA_CERT_PATH CACHES['default']['OPTIONS']['CONNECTION_POOL_KWARGS']['ssl_ca_certs'] = CACHING_REDIS_CA_CERT_PATH
# #
# Sessions # Sessions
# #
@ -352,10 +329,11 @@ SERVER_EMAIL = EMAIL.get('FROM_EMAIL')
# #
# Django # Django core settings
# #
INSTALLED_APPS = [ INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth', 'django.contrib.auth',
'django.contrib.contenttypes', 'django.contrib.contenttypes',
'django.contrib.sessions', 'django.contrib.sessions',
@ -365,12 +343,11 @@ INSTALLED_APPS = [
'django.forms', 'django.forms',
'corsheaders', 'corsheaders',
'debug_toolbar', 'debug_toolbar',
'graphiql_debug_toolbar',
'django_filters', 'django_filters',
'django_htmx', 'django_htmx',
'django_tables2', 'django_tables2',
'django_prometheus', 'django_prometheus',
'graphene_django', 'strawberry_django',
'mptt', 'mptt',
'rest_framework', 'rest_framework',
'social_django', 'social_django',
@ -392,13 +369,12 @@ INSTALLED_APPS = [
'drf_spectacular', 'drf_spectacular',
'drf_spectacular_sidecar', 'drf_spectacular_sidecar',
] ]
if not DJANGO_ADMIN_ENABLED:
if DJANGO_ADMIN_ENABLED: INSTALLED_APPS.remove('django.contrib.admin')
INSTALLED_APPS.insert(0, 'django.contrib.admin')
# Middleware # Middleware
MIDDLEWARE = [ MIDDLEWARE = [
'graphiql_debug_toolbar.middleware.DebugToolbarMiddleware', "strawberry_django.middlewares.debug_toolbar.DebugToolbarMiddleware",
'django_prometheus.middleware.PrometheusBeforeMiddleware', 'django_prometheus.middleware.PrometheusBeforeMiddleware',
'corsheaders.middleware.CorsMiddleware', 'corsheaders.middleware.CorsMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware',
@ -415,12 +391,13 @@ MIDDLEWARE = [
'netbox.middleware.MaintenanceModeMiddleware', 'netbox.middleware.MaintenanceModeMiddleware',
'django_prometheus.middleware.PrometheusAfterMiddleware', 'django_prometheus.middleware.PrometheusAfterMiddleware',
] ]
if not ENABLE_LOCALIZATION: if not ENABLE_LOCALIZATION:
MIDDLEWARE.remove("django.middleware.locale.LocaleMiddleware") MIDDLEWARE.remove('django.middleware.locale.LocaleMiddleware')
# URLs
ROOT_URLCONF = 'netbox.urls' ROOT_URLCONF = 'netbox.urls'
# Templates
TEMPLATES_DIR = BASE_DIR + '/templates' TEMPLATES_DIR = BASE_DIR + '/templates'
TEMPLATES = [ TEMPLATES = [
{ {
@ -455,9 +432,14 @@ AUTHENTICATION_BACKENDS = [
'netbox.authentication.ObjectPermissionBackend', 'netbox.authentication.ObjectPermissionBackend',
] ]
# Use our custom User model
AUTH_USER_MODEL = 'users.User' AUTH_USER_MODEL = 'users.User'
# Time zones # Authentication URLs
LOGIN_URL = f'/{BASE_PATH}login/'
LOGIN_REDIRECT_URL = f'/{BASE_PATH}'
# Use timezone-aware datetime objects
USE_TZ = True USE_TZ = True
# WSGI # WSGI
@ -476,8 +458,8 @@ STATICFILES_DIRS = (
('docs', os.path.join(BASE_DIR, 'project-static', 'docs')), # Prefix with /docs ('docs', os.path.join(BASE_DIR, 'project-static', 'docs')), # Prefix with /docs
) )
# Media # Media URL
MEDIA_URL = '/{}media/'.format(BASE_PATH) MEDIA_URL = f'/{BASE_PATH}media/'
# Disable default limit of 1000 fields per request. Needed for bulk deletion of objects. (Added in Django 1.10.) # Disable default limit of 1000 fields per request. Needed for bulk deletion of objects. (Added in Django 1.10.)
DATA_UPLOAD_MAX_NUMBER_FIELDS = None DATA_UPLOAD_MAX_NUMBER_FIELDS = None
@ -487,12 +469,17 @@ MESSAGE_TAGS = {
messages.ERROR: 'danger', messages.ERROR: 'danger',
} }
# Authentication URLs
LOGIN_URL = f'/{BASE_PATH}login/'
LOGIN_REDIRECT_URL = f'/{BASE_PATH}'
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
SERIALIZATION_MODULES = {
'json': 'utilities.serializers.json',
}
#
# Permissions & authentication
#
# Exclude potentially sensitive models from wildcard view exemption. These may still be exempted # Exclude potentially sensitive models from wildcard view exemption. These may still be exempted
# by specifying the model individually in the EXEMPT_VIEW_PERMISSIONS configuration parameter. # by specifying the model individually in the EXEMPT_VIEW_PERMISSIONS configuration parameter.
EXEMPT_EXCLUDE_MODELS = ( EXEMPT_EXCLUDE_MODELS = (
@ -521,10 +508,6 @@ MAINTENANCE_EXEMPT_PATHS = (
LOGOUT_REDIRECT_URL LOGOUT_REDIRECT_URL
) )
SERIALIZATION_MODULES = {
'json': 'utilities.serializers.json',
}
# #
# Sentry # Sentry
@ -532,7 +515,7 @@ SERIALIZATION_MODULES = {
if SENTRY_ENABLED: if SENTRY_ENABLED:
try: try:
from sentry_sdk.integrations.django import DjangoIntegration import sentry_sdk
except ModuleNotFoundError: except ModuleNotFoundError:
raise ImproperlyConfigured("SENTRY_ENABLED is True but the sentry-sdk package is not installed.") raise ImproperlyConfigured("SENTRY_ENABLED is True but the sentry-sdk package is not installed.")
if not SENTRY_DSN: if not SENTRY_DSN:
@ -541,7 +524,7 @@ if SENTRY_ENABLED:
sentry_sdk.init( sentry_sdk.init(
dsn=SENTRY_DSN, dsn=SENTRY_DSN,
release=VERSION, release=VERSION,
integrations=[DjangoIntegration()], integrations=[sentry_sdk.integrations.django.DjangoIntegration()],
sample_rate=SENTRY_SAMPLE_RATE, sample_rate=SENTRY_SAMPLE_RATE,
traces_sample_rate=SENTRY_TRACES_SAMPLE_RATE, traces_sample_rate=SENTRY_TRACES_SAMPLE_RATE,
send_default_pii=True, send_default_pii=True,
@ -557,6 +540,8 @@ if SENTRY_ENABLED:
# Census collection # Census collection
# #
# Calculate a unique deployment ID from the secret key
DEPLOYMENT_ID = hashlib.sha256(SECRET_KEY.encode('utf-8')).hexdigest()[:16]
CENSUS_URL = 'https://census.netbox.dev/api/v1/' CENSUS_URL = 'https://census.netbox.dev/api/v1/'
CENSUS_PARAMS = { CENSUS_PARAMS = {
'version': VERSION, 'version': VERSION,
@ -674,17 +659,6 @@ SPECTACULAR_SETTINGS = {
'POSTPROCESSING_HOOKS': [], 'POSTPROCESSING_HOOKS': [],
} }
#
# Graphene
#
GRAPHENE = {
# Avoids naming collision on models with 'type' field; see
# https://github.com/graphql-python/graphene-django/issues/185
'DJANGO_CHOICE_FIELD_ENUM_V3_NAMING': True,
}
# #
# Django RQ (events backend) # Django RQ (events backend)
# #
@ -711,17 +685,16 @@ RQ_PARAMS.update({
'PASSWORD': TASKS_REDIS_PASSWORD, 'PASSWORD': TASKS_REDIS_PASSWORD,
'DEFAULT_TIMEOUT': RQ_DEFAULT_TIMEOUT, 'DEFAULT_TIMEOUT': RQ_DEFAULT_TIMEOUT,
}) })
if TASKS_REDIS_CA_CERT_PATH: if TASKS_REDIS_CA_CERT_PATH:
RQ_PARAMS.setdefault('REDIS_CLIENT_KWARGS', {}) RQ_PARAMS.setdefault('REDIS_CLIENT_KWARGS', {})
RQ_PARAMS['REDIS_CLIENT_KWARGS']['ssl_ca_certs'] = TASKS_REDIS_CA_CERT_PATH RQ_PARAMS['REDIS_CLIENT_KWARGS']['ssl_ca_certs'] = TASKS_REDIS_CA_CERT_PATH
# Define named RQ queues
RQ_QUEUES = { RQ_QUEUES = {
RQ_QUEUE_HIGH: RQ_PARAMS, RQ_QUEUE_HIGH: RQ_PARAMS,
RQ_QUEUE_DEFAULT: RQ_PARAMS, RQ_QUEUE_DEFAULT: RQ_PARAMS,
RQ_QUEUE_LOW: RQ_PARAMS, RQ_QUEUE_LOW: RQ_PARAMS,
} }
# Add any queues defined in QUEUE_MAPPINGS # Add any queues defined in QUEUE_MAPPINGS
RQ_QUEUES.update({ RQ_QUEUES.update({
queue: RQ_PARAMS for queue in set(QUEUE_MAPPINGS.values()) if queue not in RQ_QUEUES queue: RQ_PARAMS for queue in set(QUEUE_MAPPINGS.values()) if queue not in RQ_QUEUES
@ -731,6 +704,7 @@ RQ_QUEUES.update({
# Localization # Localization
# #
# Supported translation languages
LANGUAGES = ( LANGUAGES = (
('en', _('English')), ('en', _('English')),
('es', _('Spanish')), ('es', _('Spanish')),
@ -740,38 +714,44 @@ LANGUAGES = (
('ru', _('Russian')), ('ru', _('Russian')),
('tr', _('Turkish')), ('tr', _('Turkish')),
) )
LOCALE_PATHS = ( LOCALE_PATHS = (
BASE_DIR + '/translations', BASE_DIR + '/translations',
) )
if not ENABLE_LOCALIZATION: if not ENABLE_LOCALIZATION:
USE_I18N = False USE_I18N = False
USE_L10N = False USE_L10N = False
#
# Strawberry (GraphQL)
#
STRAWBERRY_DJANGO = {
"TYPE_DESCRIPTION_FROM_MODEL_DOCSTRING": True,
}
# #
# Plugins # Plugins
# #
# Register any configured plugins
for plugin_name in PLUGINS: for plugin_name in PLUGINS:
# Import plugin module
try: try:
# Import the plugin module
plugin = importlib.import_module(plugin_name) plugin = importlib.import_module(plugin_name)
except ModuleNotFoundError as e: except ModuleNotFoundError as e:
if getattr(e, 'name') == plugin_name: if getattr(e, 'name') == plugin_name:
raise ImproperlyConfigured( raise ImproperlyConfigured(
"Unable to import plugin {}: Module not found. Check that the plugin module has been installed within the " f"Unable to import plugin {plugin_name}: Module not found. Check that the plugin module has been "
"correct Python environment.".format(plugin_name) f"installed within the correct Python environment."
) )
raise e raise e
# Determine plugin config and add to INSTALLED_APPS.
try: try:
# Load the PluginConfig
plugin_config: PluginConfig = plugin.config plugin_config: PluginConfig = plugin.config
except AttributeError: except AttributeError:
raise ImproperlyConfigured( raise ImproperlyConfigured(
"Plugin {} does not provide a 'config' variable. This should be defined in the plugin's __init__.py file " f"Plugin {plugin_name} does not provide a 'config' variable. This should be defined in the plugin's "
"and point to the PluginConfig subclass.".format(plugin_name) f"__init__.py file and point to the PluginConfig subclass."
) )
plugin_module = "{}.{}".format(plugin_config.__module__, plugin_config.__name__) # type: ignore plugin_module = "{}.{}".format(plugin_config.__module__, plugin_config.__name__) # type: ignore
@ -794,12 +774,12 @@ for plugin_name in PLUGINS:
raise ImproperlyConfigured( raise ImproperlyConfigured(
f"Failed to load django_apps specified by plugin {plugin_name}: {django_apps} " f"Failed to load django_apps specified by plugin {plugin_name}: {django_apps} "
f"The module {app} cannot be imported. Check that the necessary package has been " f"The module {app} cannot be imported. Check that the necessary package has been "
"installed within the correct Python environment." f"installed within the correct Python environment."
) )
INSTALLED_APPS.extend(django_apps) INSTALLED_APPS.extend(django_apps)
# Preserve uniqueness of the INSTALLED_APPS list, we keep the last occurence # Preserve uniqueness of the INSTALLED_APPS list, we keep the last occurrence
sorted_apps = reversed(list(dict.fromkeys(reversed(INSTALLED_APPS)))) sorted_apps = reversed(list(dict.fromkeys(reversed(INSTALLED_APPS))))
INSTALLED_APPS = list(sorted_apps) INSTALLED_APPS = list(sorted_apps)
@ -817,9 +797,7 @@ for plugin_name in PLUGINS:
# we use the plugin name as a prefix for queue name's defined in the plugin config # we use the plugin name as a prefix for queue name's defined in the plugin config
# ex: mysuperplugin.mysuperqueue1 # ex: mysuperplugin.mysuperqueue1
if type(plugin_config.queues) is not list: if type(plugin_config.queues) is not list:
raise ImproperlyConfigured( raise ImproperlyConfigured(f"Plugin {plugin_name} queues must be a list.")
"Plugin {} queues must be a list.".format(plugin_name)
)
RQ_QUEUES.update({ RQ_QUEUES.update({
f"{plugin_name}.{queue}": RQ_PARAMS for queue in plugin_config.queues f"{plugin_name}.{queue}": RQ_PARAMS for queue in plugin_config.queues
}) })

View File

@ -1,21 +1,26 @@
import graphene from typing import List
from graphene_django import DjangoObjectType import strawberry
import strawberry_django
from netbox.graphql.fields import ObjectField, ObjectListField
from . import models from . import models
class DummyModelType(DjangoObjectType): @strawberry_django.type(
models.DummyModel,
class Meta: fields='__all__',
model = models.DummyModel )
fields = '__all__' class DummyModelType:
pass
class DummyQuery(graphene.ObjectType): @strawberry.type
dummymodel = ObjectField(DummyModelType) class DummyQuery:
dummymodel_list = ObjectListField(DummyModelType) @strawberry.field
def dummymodel(self, id: int) -> DummyModelType:
return None
dummymodel_list: List[DummyModelType] = strawberry_django.field()
schema = DummyQuery schema = [
DummyQuery,
]

View File

@ -1,16 +1,16 @@
from django.conf import settings from django.conf import settings
from django.conf.urls import include from django.conf.urls import include
from django.urls import path from django.urls import path
from django.views.decorators.csrf import csrf_exempt
from django.views.static import serve from django.views.static import serve
from drf_spectacular.views import SpectacularAPIView, SpectacularRedocView, SpectacularSwaggerView from drf_spectacular.views import SpectacularAPIView, SpectacularRedocView, SpectacularSwaggerView
from account.views import LoginView, LogoutView from account.views import LoginView, LogoutView
from netbox.api.views import APIRootView, StatusView from netbox.api.views import APIRootView, StatusView
from netbox.graphql.schema import schema from netbox.graphql.schema import schema
from netbox.graphql.views import GraphQLView from netbox.graphql.views import NetBoxGraphQLView
from netbox.plugins.urls import plugin_patterns, plugin_api_patterns from netbox.plugins.urls import plugin_patterns, plugin_api_patterns
from netbox.views import HomeView, StaticMediaFailureView, SearchView, htmx from netbox.views import HomeView, StaticMediaFailureView, SearchView, htmx
from strawberry.django.views import GraphQLView
_patterns = [ _patterns = [
@ -60,7 +60,7 @@ _patterns = [
path('api/schema/redoc/', SpectacularRedocView.as_view(url_name='schema'), name='api_redocs'), path('api/schema/redoc/', SpectacularRedocView.as_view(url_name='schema'), name='api_redocs'),
# GraphQL # GraphQL
path('graphql/', csrf_exempt(GraphQLView.as_view(graphiql=True, schema=schema)), name='graphql'), path('graphql/', NetBoxGraphQLView.as_view(schema=schema), name='graphql'),
# Serving static media in Django to pipe it through LoginRequiredMiddleware # Serving static media in Django to pipe it through LoginRequiredMiddleware
path('media/<path:path>', serve, {'document_root': settings.MEDIA_ROOT}), path('media/<path:path>', serve, {'document_root': settings.MEDIA_ROOT}),

View File

@ -1,5 +1,8 @@
const esbuild = require('esbuild'); const esbuild = require('esbuild');
const { sassPlugin } = require('esbuild-sass-plugin'); const { sassPlugin } = require('esbuild-sass-plugin');
const util = require('util');
const fs = require('fs');
const copyFilePromise = util.promisify(fs.copyFile);
// Bundler options common to all bundle jobs. // Bundler options common to all bundle jobs.
const options = { const options = {
@ -14,24 +17,57 @@ const options = {
// Get CLI arguments for optional overrides. // Get CLI arguments for optional overrides.
const ARGS = process.argv.slice(2); const ARGS = process.argv.slice(2);
function copyFiles(files) {
return Promise.all(files.map(f => {
return copyFilePromise(f.source, f.dest);
}));
}
async function bundleGraphIQL() { async function bundleGraphIQL() {
let fileMap = [
{
source: './node_modules/react/umd/react.production.min.js',
dest: './dist/graphiql/react.production.min.js'
},
{
source: './node_modules/react-dom/umd/react-dom.production.min.js',
dest: './dist/graphiql/react-dom.production.min.js'
},
{
source: './node_modules/js-cookie/dist/js.cookie.min.js',
dest: './dist/graphiql/js.cookie.min.js'
},
{
source: './node_modules/graphiql/graphiql.min.js',
dest: './dist/graphiql/graphiql.min.js'
},
{
source: './node_modules/@graphiql/plugin-explorer/dist/index.umd.js',
dest: './dist/graphiql/index.umd.js'
},
{
source: './node_modules/graphiql/graphiql.min.css',
dest: './dist/graphiql/graphiql.min.css'
},
{
source: './node_modules/@graphiql/plugin-explorer/dist/style.css',
dest: './dist/graphiql/plugin-explorer-style.css'
}
];
try { try {
const result = await esbuild.build({ if (!fs.existsSync('./dist/graphiql/')) {
...options, fs.mkdirSync('./dist/graphiql/');
entryPoints: {
graphiql: 'netbox-graphiql/index.ts',
},
target: 'es2016',
define: {
global: 'window',
},
});
if (result.errors.length === 0) {
console.log(`✅ Bundled source file 'netbox-graphiql/index.ts' to 'graphiql.js'`);
} }
} catch (err) { } catch (err) {
console.error(err); console.error(err);
} }
copyFiles(fileMap).then(() => {
console.log('✅ Copied graphiql files');
}).catch(err => {
console.error(err);
});
} }
/** /**
@ -77,7 +113,6 @@ async function bundleStyles() {
'netbox': 'styles/netbox.scss', 'netbox': 'styles/netbox.scss',
rack_elevation: 'styles/svg/rack_elevation.scss', rack_elevation: 'styles/svg/rack_elevation.scss',
cable_trace: 'styles/svg/cable_trace.scss', cable_trace: 'styles/svg/cable_trace.scss',
graphiql: 'netbox-graphiql/graphiql.scss',
}; };
const pluginOptions = { outputStyle: 'compressed' }; const pluginOptions = { outputStyle: 'compressed' };
// Allow cache disabling. // Allow cache disabling.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,2 @@
/*! js-cookie v3.0.5 | MIT */
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self,function(){var n=e.Cookies,o=e.Cookies=t();o.noConflict=function(){return e.Cookies=n,o}}())}(this,(function(){"use strict";function e(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var o in n)e[o]=n[o]}return e}var t=function t(n,o){function r(t,r,i){if("undefined"!=typeof document){"number"==typeof(i=e({},o,i)).expires&&(i.expires=new Date(Date.now()+864e5*i.expires)),i.expires&&(i.expires=i.expires.toUTCString()),t=encodeURIComponent(t).replace(/%(2[346B]|5E|60|7C)/g,decodeURIComponent).replace(/[()]/g,escape);var c="";for(var u in i)i[u]&&(c+="; "+u,!0!==i[u]&&(c+="="+i[u].split(";")[0]));return document.cookie=t+"="+n.write(r,t)+c}}return Object.create({set:r,get:function(e){if("undefined"!=typeof document&&(!arguments.length||e)){for(var t=document.cookie?document.cookie.split("; "):[],o={},r=0;r<t.length;r++){var i=t[r].split("="),c=i.slice(1).join("=");try{var u=decodeURIComponent(i[0]);if(o[u]=n.read(c,u),e===u)break}catch(e){}}return e?o[e]:o}},remove:function(t,n){r(t,"",e({},n,{expires:-1}))},withAttributes:function(n){return t(this.converter,e({},this.attributes,n))},withConverter:function(n){return t(e({},this.converter,n),this.attributes)}},{attributes:{value:Object.freeze(o)},converter:{value:Object.freeze(n)}})}({read:function(e){return'"'===e[0]&&(e=e.slice(1,-1)),e.replace(/(%[\dA-F]{2})+/gi,decodeURIComponent)},write:function(e){return encodeURIComponent(e).replace(/%(2[346BF]|3[AC-F]|40|5[BDE]|60|7[BCD])/g,decodeURIComponent)}},{path:"/"});return t}));

View File

@ -0,0 +1 @@
.docExplorerWrap{height:unset!important;min-width:unset!important;width:unset!important}.doc-explorer-title{font-size:var(--font-size-h2);font-weight:var(--font-weight-medium)}.doc-explorer-rhs{display:none}.graphiql-explorer-root{font-family:var(--font-family-mono)!important;font-size:var(--font-size-body)!important;padding:0!important}.graphiql-explorer-root>div:first-child{padding-left:var(--px-8);overflow:hidden!important}.graphiql-explorer-root input{background:hsl(var(--color-base))}.graphiql-explorer-root select{background-color:hsl(var(--color-base));border:1px solid hsla(var(--color-neutral),var(--alpha-secondary));border-radius:var(--border-radius-4);color:hsl(var(--color-neutral));margin:0 var(--px-4);padding:var(--px-4) var(--px-6)}

View File

@ -0,0 +1,267 @@
/**
* @license React
* react-dom.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
(function(){/*
Modernizr 3.0.0pre (Custom Build) | MIT
*/
'use strict';(function(Q,mb){"object"===typeof exports&&"undefined"!==typeof module?mb(exports,require("react")):"function"===typeof define&&define.amd?define(["exports","react"],mb):(Q=Q||self,mb(Q.ReactDOM={},Q.React))})(this,function(Q,mb){function n(a){for(var b="https://reactjs.org/docs/error-decoder.html?invariant="+a,c=1;c<arguments.length;c++)b+="&args[]="+encodeURIComponent(arguments[c]);return"Minified React error #"+a+"; visit "+b+" for the full message or use the non-minified dev environment for full errors and additional helpful warnings."}
function nb(a,b){Ab(a,b);Ab(a+"Capture",b)}function Ab(a,b){$b[a]=b;for(a=0;a<b.length;a++)cg.add(b[a])}function cj(a){if(Zd.call(dg,a))return!0;if(Zd.call(eg,a))return!1;if(dj.test(a))return dg[a]=!0;eg[a]=!0;return!1}function ej(a,b,c,d){if(null!==c&&0===c.type)return!1;switch(typeof b){case "function":case "symbol":return!0;case "boolean":if(d)return!1;if(null!==c)return!c.acceptsBooleans;a=a.toLowerCase().slice(0,5);return"data-"!==a&&"aria-"!==a;default:return!1}}function fj(a,b,c,d){if(null===
b||"undefined"===typeof b||ej(a,b,c,d))return!0;if(d)return!1;if(null!==c)switch(c.type){case 3:return!b;case 4:return!1===b;case 5:return isNaN(b);case 6:return isNaN(b)||1>b}return!1}function Y(a,b,c,d,e,f,g){this.acceptsBooleans=2===b||3===b||4===b;this.attributeName=d;this.attributeNamespace=e;this.mustUseProperty=c;this.propertyName=a;this.type=b;this.sanitizeURL=f;this.removeEmptyString=g}function $d(a,b,c,d){var e=R.hasOwnProperty(b)?R[b]:null;if(null!==e?0!==e.type:d||!(2<b.length)||"o"!==
b[0]&&"O"!==b[0]||"n"!==b[1]&&"N"!==b[1])fj(b,c,e,d)&&(c=null),d||null===e?cj(b)&&(null===c?a.removeAttribute(b):a.setAttribute(b,""+c)):e.mustUseProperty?a[e.propertyName]=null===c?3===e.type?!1:"":c:(b=e.attributeName,d=e.attributeNamespace,null===c?a.removeAttribute(b):(e=e.type,c=3===e||4===e&&!0===c?"":""+c,d?a.setAttributeNS(d,b,c):a.setAttribute(b,c)))}function ac(a){if(null===a||"object"!==typeof a)return null;a=fg&&a[fg]||a["@@iterator"];return"function"===typeof a?a:null}function bc(a,b,
c){if(void 0===ae)try{throw Error();}catch(d){ae=(b=d.stack.trim().match(/\n( *(at )?)/))&&b[1]||""}return"\n"+ae+a}function be(a,b){if(!a||ce)return"";ce=!0;var c=Error.prepareStackTrace;Error.prepareStackTrace=void 0;try{if(b)if(b=function(){throw Error();},Object.defineProperty(b.prototype,"props",{set:function(){throw Error();}}),"object"===typeof Reflect&&Reflect.construct){try{Reflect.construct(b,[])}catch(m){var d=m}Reflect.construct(a,[],b)}else{try{b.call()}catch(m){d=m}a.call(b.prototype)}else{try{throw Error();
}catch(m){d=m}a()}}catch(m){if(m&&d&&"string"===typeof m.stack){for(var e=m.stack.split("\n"),f=d.stack.split("\n"),g=e.length-1,h=f.length-1;1<=g&&0<=h&&e[g]!==f[h];)h--;for(;1<=g&&0<=h;g--,h--)if(e[g]!==f[h]){if(1!==g||1!==h){do if(g--,h--,0>h||e[g]!==f[h]){var k="\n"+e[g].replace(" at new "," at ");a.displayName&&k.includes("<anonymous>")&&(k=k.replace("<anonymous>",a.displayName));return k}while(1<=g&&0<=h)}break}}}finally{ce=!1,Error.prepareStackTrace=c}return(a=a?a.displayName||a.name:"")?bc(a):
""}function gj(a){switch(a.tag){case 5:return bc(a.type);case 16:return bc("Lazy");case 13:return bc("Suspense");case 19:return bc("SuspenseList");case 0:case 2:case 15:return a=be(a.type,!1),a;case 11:return a=be(a.type.render,!1),a;case 1:return a=be(a.type,!0),a;default:return""}}function de(a){if(null==a)return null;if("function"===typeof a)return a.displayName||a.name||null;if("string"===typeof a)return a;switch(a){case Bb:return"Fragment";case Cb:return"Portal";case ee:return"Profiler";case fe:return"StrictMode";
case ge:return"Suspense";case he:return"SuspenseList"}if("object"===typeof a)switch(a.$$typeof){case gg:return(a.displayName||"Context")+".Consumer";case hg:return(a._context.displayName||"Context")+".Provider";case ie:var b=a.render;a=a.displayName;a||(a=b.displayName||b.name||"",a=""!==a?"ForwardRef("+a+")":"ForwardRef");return a;case je:return b=a.displayName||null,null!==b?b:de(a.type)||"Memo";case Ta:b=a._payload;a=a._init;try{return de(a(b))}catch(c){}}return null}function hj(a){var b=a.type;
switch(a.tag){case 24:return"Cache";case 9:return(b.displayName||"Context")+".Consumer";case 10:return(b._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return a=b.render,a=a.displayName||a.name||"",b.displayName||(""!==a?"ForwardRef("+a+")":"ForwardRef");case 7:return"Fragment";case 5:return b;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return de(b);case 8:return b===fe?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler";
case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if("function"===typeof b)return b.displayName||b.name||null;if("string"===typeof b)return b}return null}function Ua(a){switch(typeof a){case "boolean":case "number":case "string":case "undefined":return a;case "object":return a;default:return""}}function ig(a){var b=a.type;return(a=a.nodeName)&&"input"===a.toLowerCase()&&("checkbox"===b||"radio"===
b)}function ij(a){var b=ig(a)?"checked":"value",c=Object.getOwnPropertyDescriptor(a.constructor.prototype,b),d=""+a[b];if(!a.hasOwnProperty(b)&&"undefined"!==typeof c&&"function"===typeof c.get&&"function"===typeof c.set){var e=c.get,f=c.set;Object.defineProperty(a,b,{configurable:!0,get:function(){return e.call(this)},set:function(a){d=""+a;f.call(this,a)}});Object.defineProperty(a,b,{enumerable:c.enumerable});return{getValue:function(){return d},setValue:function(a){d=""+a},stopTracking:function(){a._valueTracker=
null;delete a[b]}}}}function Pc(a){a._valueTracker||(a._valueTracker=ij(a))}function jg(a){if(!a)return!1;var b=a._valueTracker;if(!b)return!0;var c=b.getValue();var d="";a&&(d=ig(a)?a.checked?"true":"false":a.value);a=d;return a!==c?(b.setValue(a),!0):!1}function Qc(a){a=a||("undefined"!==typeof document?document:void 0);if("undefined"===typeof a)return null;try{return a.activeElement||a.body}catch(b){return a.body}}function ke(a,b){var c=b.checked;return E({},b,{defaultChecked:void 0,defaultValue:void 0,
value:void 0,checked:null!=c?c:a._wrapperState.initialChecked})}function kg(a,b){var c=null==b.defaultValue?"":b.defaultValue,d=null!=b.checked?b.checked:b.defaultChecked;c=Ua(null!=b.value?b.value:c);a._wrapperState={initialChecked:d,initialValue:c,controlled:"checkbox"===b.type||"radio"===b.type?null!=b.checked:null!=b.value}}function lg(a,b){b=b.checked;null!=b&&$d(a,"checked",b,!1)}function le(a,b){lg(a,b);var c=Ua(b.value),d=b.type;if(null!=c)if("number"===d){if(0===c&&""===a.value||a.value!=
c)a.value=""+c}else a.value!==""+c&&(a.value=""+c);else if("submit"===d||"reset"===d){a.removeAttribute("value");return}b.hasOwnProperty("value")?me(a,b.type,c):b.hasOwnProperty("defaultValue")&&me(a,b.type,Ua(b.defaultValue));null==b.checked&&null!=b.defaultChecked&&(a.defaultChecked=!!b.defaultChecked)}function mg(a,b,c){if(b.hasOwnProperty("value")||b.hasOwnProperty("defaultValue")){var d=b.type;if(!("submit"!==d&&"reset"!==d||void 0!==b.value&&null!==b.value))return;b=""+a._wrapperState.initialValue;
c||b===a.value||(a.value=b);a.defaultValue=b}c=a.name;""!==c&&(a.name="");a.defaultChecked=!!a._wrapperState.initialChecked;""!==c&&(a.name=c)}function me(a,b,c){if("number"!==b||Qc(a.ownerDocument)!==a)null==c?a.defaultValue=""+a._wrapperState.initialValue:a.defaultValue!==""+c&&(a.defaultValue=""+c)}function Db(a,b,c,d){a=a.options;if(b){b={};for(var e=0;e<c.length;e++)b["$"+c[e]]=!0;for(c=0;c<a.length;c++)e=b.hasOwnProperty("$"+a[c].value),a[c].selected!==e&&(a[c].selected=e),e&&d&&(a[c].defaultSelected=
!0)}else{c=""+Ua(c);b=null;for(e=0;e<a.length;e++){if(a[e].value===c){a[e].selected=!0;d&&(a[e].defaultSelected=!0);return}null!==b||a[e].disabled||(b=a[e])}null!==b&&(b.selected=!0)}}function ne(a,b){if(null!=b.dangerouslySetInnerHTML)throw Error(n(91));return E({},b,{value:void 0,defaultValue:void 0,children:""+a._wrapperState.initialValue})}function ng(a,b){var c=b.value;if(null==c){c=b.children;b=b.defaultValue;if(null!=c){if(null!=b)throw Error(n(92));if(cc(c)){if(1<c.length)throw Error(n(93));
c=c[0]}b=c}null==b&&(b="");c=b}a._wrapperState={initialValue:Ua(c)}}function og(a,b){var c=Ua(b.value),d=Ua(b.defaultValue);null!=c&&(c=""+c,c!==a.value&&(a.value=c),null==b.defaultValue&&a.defaultValue!==c&&(a.defaultValue=c));null!=d&&(a.defaultValue=""+d)}function pg(a,b){b=a.textContent;b===a._wrapperState.initialValue&&""!==b&&null!==b&&(a.value=b)}function qg(a){switch(a){case "svg":return"http://www.w3.org/2000/svg";case "math":return"http://www.w3.org/1998/Math/MathML";default:return"http://www.w3.org/1999/xhtml"}}
function oe(a,b){return null==a||"http://www.w3.org/1999/xhtml"===a?qg(b):"http://www.w3.org/2000/svg"===a&&"foreignObject"===b?"http://www.w3.org/1999/xhtml":a}function rg(a,b,c){return null==b||"boolean"===typeof b||""===b?"":c||"number"!==typeof b||0===b||dc.hasOwnProperty(a)&&dc[a]?(""+b).trim():b+"px"}function sg(a,b){a=a.style;for(var c in b)if(b.hasOwnProperty(c)){var d=0===c.indexOf("--"),e=rg(c,b[c],d);"float"===c&&(c="cssFloat");d?a.setProperty(c,e):a[c]=e}}function pe(a,b){if(b){if(jj[a]&&
(null!=b.children||null!=b.dangerouslySetInnerHTML))throw Error(n(137,a));if(null!=b.dangerouslySetInnerHTML){if(null!=b.children)throw Error(n(60));if("object"!==typeof b.dangerouslySetInnerHTML||!("__html"in b.dangerouslySetInnerHTML))throw Error(n(61));}if(null!=b.style&&"object"!==typeof b.style)throw Error(n(62));}}function qe(a,b){if(-1===a.indexOf("-"))return"string"===typeof b.is;switch(a){case "annotation-xml":case "color-profile":case "font-face":case "font-face-src":case "font-face-uri":case "font-face-format":case "font-face-name":case "missing-glyph":return!1;
default:return!0}}function re(a){a=a.target||a.srcElement||window;a.correspondingUseElement&&(a=a.correspondingUseElement);return 3===a.nodeType?a.parentNode:a}function tg(a){if(a=ec(a)){if("function"!==typeof se)throw Error(n(280));var b=a.stateNode;b&&(b=Rc(b),se(a.stateNode,a.type,b))}}function ug(a){Eb?Fb?Fb.push(a):Fb=[a]:Eb=a}function vg(){if(Eb){var a=Eb,b=Fb;Fb=Eb=null;tg(a);if(b)for(a=0;a<b.length;a++)tg(b[a])}}function wg(a,b,c){if(te)return a(b,c);te=!0;try{return xg(a,b,c)}finally{if(te=
!1,null!==Eb||null!==Fb)yg(),vg()}}function fc(a,b){var c=a.stateNode;if(null===c)return null;var d=Rc(c);if(null===d)return null;c=d[b];a:switch(b){case "onClick":case "onClickCapture":case "onDoubleClick":case "onDoubleClickCapture":case "onMouseDown":case "onMouseDownCapture":case "onMouseMove":case "onMouseMoveCapture":case "onMouseUp":case "onMouseUpCapture":case "onMouseEnter":(d=!d.disabled)||(a=a.type,d=!("button"===a||"input"===a||"select"===a||"textarea"===a));a=!d;break a;default:a=!1}if(a)return null;
if(c&&"function"!==typeof c)throw Error(n(231,b,typeof c));return c}function kj(a,b,c,d,e,f,g,h,k){gc=!1;Sc=null;lj.apply(mj,arguments)}function nj(a,b,c,d,e,f,g,h,k){kj.apply(this,arguments);if(gc){if(gc){var m=Sc;gc=!1;Sc=null}else throw Error(n(198));Tc||(Tc=!0,ue=m)}}function ob(a){var b=a,c=a;if(a.alternate)for(;b.return;)b=b.return;else{a=b;do b=a,0!==(b.flags&4098)&&(c=b.return),a=b.return;while(a)}return 3===b.tag?c:null}function zg(a){if(13===a.tag){var b=a.memoizedState;null===b&&(a=a.alternate,
null!==a&&(b=a.memoizedState));if(null!==b)return b.dehydrated}return null}function Ag(a){if(ob(a)!==a)throw Error(n(188));}function oj(a){var b=a.alternate;if(!b){b=ob(a);if(null===b)throw Error(n(188));return b!==a?null:a}for(var c=a,d=b;;){var e=c.return;if(null===e)break;var f=e.alternate;if(null===f){d=e.return;if(null!==d){c=d;continue}break}if(e.child===f.child){for(f=e.child;f;){if(f===c)return Ag(e),a;if(f===d)return Ag(e),b;f=f.sibling}throw Error(n(188));}if(c.return!==d.return)c=e,d=f;
else{for(var g=!1,h=e.child;h;){if(h===c){g=!0;c=e;d=f;break}if(h===d){g=!0;d=e;c=f;break}h=h.sibling}if(!g){for(h=f.child;h;){if(h===c){g=!0;c=f;d=e;break}if(h===d){g=!0;d=f;c=e;break}h=h.sibling}if(!g)throw Error(n(189));}}if(c.alternate!==d)throw Error(n(190));}if(3!==c.tag)throw Error(n(188));return c.stateNode.current===c?a:b}function Bg(a){a=oj(a);return null!==a?Cg(a):null}function Cg(a){if(5===a.tag||6===a.tag)return a;for(a=a.child;null!==a;){var b=Cg(a);if(null!==b)return b;a=a.sibling}return null}
function pj(a,b){if(Ca&&"function"===typeof Ca.onCommitFiberRoot)try{Ca.onCommitFiberRoot(Uc,a,void 0,128===(a.current.flags&128))}catch(c){}}function qj(a){a>>>=0;return 0===a?32:31-(rj(a)/sj|0)|0}function hc(a){switch(a&-a){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return a&
4194240;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return a&130023424;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 1073741824;default:return a}}function Vc(a,b){var c=a.pendingLanes;if(0===c)return 0;var d=0,e=a.suspendedLanes,f=a.pingedLanes,g=c&268435455;if(0!==g){var h=g&~e;0!==h?d=hc(h):(f&=g,0!==f&&(d=hc(f)))}else g=c&~e,0!==g?d=hc(g):0!==f&&(d=hc(f));if(0===d)return 0;if(0!==b&&b!==d&&0===(b&e)&&
(e=d&-d,f=b&-b,e>=f||16===e&&0!==(f&4194240)))return b;0!==(d&4)&&(d|=c&16);b=a.entangledLanes;if(0!==b)for(a=a.entanglements,b&=d;0<b;)c=31-ta(b),e=1<<c,d|=a[c],b&=~e;return d}function tj(a,b){switch(a){case 1:case 2:case 4:return b+250;case 8:case 16:case 32:case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return b+5E3;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return-1;
case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function uj(a,b){for(var c=a.suspendedLanes,d=a.pingedLanes,e=a.expirationTimes,f=a.pendingLanes;0<f;){var g=31-ta(f),h=1<<g,k=e[g];if(-1===k){if(0===(h&c)||0!==(h&d))e[g]=tj(h,b)}else k<=b&&(a.expiredLanes|=h);f&=~h}}function ve(a){a=a.pendingLanes&-1073741825;return 0!==a?a:a&1073741824?1073741824:0}function Dg(){var a=Wc;Wc<<=1;0===(Wc&4194240)&&(Wc=64);return a}function we(a){for(var b=[],c=0;31>c;c++)b.push(a);
return b}function ic(a,b,c){a.pendingLanes|=b;536870912!==b&&(a.suspendedLanes=0,a.pingedLanes=0);a=a.eventTimes;b=31-ta(b);a[b]=c}function vj(a,b){var c=a.pendingLanes&~b;a.pendingLanes=b;a.suspendedLanes=0;a.pingedLanes=0;a.expiredLanes&=b;a.mutableReadLanes&=b;a.entangledLanes&=b;b=a.entanglements;var d=a.eventTimes;for(a=a.expirationTimes;0<c;){var e=31-ta(c),f=1<<e;b[e]=0;d[e]=-1;a[e]=-1;c&=~f}}function xe(a,b){var c=a.entangledLanes|=b;for(a=a.entanglements;c;){var d=31-ta(c),e=1<<d;e&b|a[d]&
b&&(a[d]|=b);c&=~e}}function Eg(a){a&=-a;return 1<a?4<a?0!==(a&268435455)?16:536870912:4:1}function Fg(a,b){switch(a){case "focusin":case "focusout":Va=null;break;case "dragenter":case "dragleave":Wa=null;break;case "mouseover":case "mouseout":Xa=null;break;case "pointerover":case "pointerout":jc.delete(b.pointerId);break;case "gotpointercapture":case "lostpointercapture":kc.delete(b.pointerId)}}function lc(a,b,c,d,e,f){if(null===a||a.nativeEvent!==f)return a={blockedOn:b,domEventName:c,eventSystemFlags:d,
nativeEvent:f,targetContainers:[e]},null!==b&&(b=ec(b),null!==b&&Gg(b)),a;a.eventSystemFlags|=d;b=a.targetContainers;null!==e&&-1===b.indexOf(e)&&b.push(e);return a}function wj(a,b,c,d,e){switch(b){case "focusin":return Va=lc(Va,a,b,c,d,e),!0;case "dragenter":return Wa=lc(Wa,a,b,c,d,e),!0;case "mouseover":return Xa=lc(Xa,a,b,c,d,e),!0;case "pointerover":var f=e.pointerId;jc.set(f,lc(jc.get(f)||null,a,b,c,d,e));return!0;case "gotpointercapture":return f=e.pointerId,kc.set(f,lc(kc.get(f)||null,a,b,
c,d,e)),!0}return!1}function Hg(a){var b=pb(a.target);if(null!==b){var c=ob(b);if(null!==c)if(b=c.tag,13===b){if(b=zg(c),null!==b){a.blockedOn=b;xj(a.priority,function(){yj(c)});return}}else if(3===b&&c.stateNode.current.memoizedState.isDehydrated){a.blockedOn=3===c.tag?c.stateNode.containerInfo:null;return}}a.blockedOn=null}function Xc(a){if(null!==a.blockedOn)return!1;for(var b=a.targetContainers;0<b.length;){var c=ye(a.domEventName,a.eventSystemFlags,b[0],a.nativeEvent);if(null===c){c=a.nativeEvent;
var d=new c.constructor(c.type,c);ze=d;c.target.dispatchEvent(d);ze=null}else return b=ec(c),null!==b&&Gg(b),a.blockedOn=c,!1;b.shift()}return!0}function Ig(a,b,c){Xc(a)&&c.delete(b)}function zj(){Ae=!1;null!==Va&&Xc(Va)&&(Va=null);null!==Wa&&Xc(Wa)&&(Wa=null);null!==Xa&&Xc(Xa)&&(Xa=null);jc.forEach(Ig);kc.forEach(Ig)}function mc(a,b){a.blockedOn===b&&(a.blockedOn=null,Ae||(Ae=!0,Jg(Kg,zj)))}function nc(a){if(0<Yc.length){mc(Yc[0],a);for(var b=1;b<Yc.length;b++){var c=Yc[b];c.blockedOn===a&&(c.blockedOn=
null)}}null!==Va&&mc(Va,a);null!==Wa&&mc(Wa,a);null!==Xa&&mc(Xa,a);b=function(b){return mc(b,a)};jc.forEach(b);kc.forEach(b);for(b=0;b<Ya.length;b++)c=Ya[b],c.blockedOn===a&&(c.blockedOn=null);for(;0<Ya.length&&(b=Ya[0],null===b.blockedOn);)Hg(b),null===b.blockedOn&&Ya.shift()}function Aj(a,b,c,d){var e=z,f=Gb.transition;Gb.transition=null;try{z=1,Be(a,b,c,d)}finally{z=e,Gb.transition=f}}function Bj(a,b,c,d){var e=z,f=Gb.transition;Gb.transition=null;try{z=4,Be(a,b,c,d)}finally{z=e,Gb.transition=
f}}function Be(a,b,c,d){if(Zc){var e=ye(a,b,c,d);if(null===e)Ce(a,b,d,$c,c),Fg(a,d);else if(wj(e,a,b,c,d))d.stopPropagation();else if(Fg(a,d),b&4&&-1<Cj.indexOf(a)){for(;null!==e;){var f=ec(e);null!==f&&Dj(f);f=ye(a,b,c,d);null===f&&Ce(a,b,d,$c,c);if(f===e)break;e=f}null!==e&&d.stopPropagation()}else Ce(a,b,d,null,c)}}function ye(a,b,c,d){$c=null;a=re(d);a=pb(a);if(null!==a)if(b=ob(a),null===b)a=null;else if(c=b.tag,13===c){a=zg(b);if(null!==a)return a;a=null}else if(3===c){if(b.stateNode.current.memoizedState.isDehydrated)return 3===
b.tag?b.stateNode.containerInfo:null;a=null}else b!==a&&(a=null);$c=a;return null}function Lg(a){switch(a){case "cancel":case "click":case "close":case "contextmenu":case "copy":case "cut":case "auxclick":case "dblclick":case "dragend":case "dragstart":case "drop":case "focusin":case "focusout":case "input":case "invalid":case "keydown":case "keypress":case "keyup":case "mousedown":case "mouseup":case "paste":case "pause":case "play":case "pointercancel":case "pointerdown":case "pointerup":case "ratechange":case "reset":case "resize":case "seeked":case "submit":case "touchcancel":case "touchend":case "touchstart":case "volumechange":case "change":case "selectionchange":case "textInput":case "compositionstart":case "compositionend":case "compositionupdate":case "beforeblur":case "afterblur":case "beforeinput":case "blur":case "fullscreenchange":case "focus":case "hashchange":case "popstate":case "select":case "selectstart":return 1;
case "drag":case "dragenter":case "dragexit":case "dragleave":case "dragover":case "mousemove":case "mouseout":case "mouseover":case "pointermove":case "pointerout":case "pointerover":case "scroll":case "toggle":case "touchmove":case "wheel":case "mouseenter":case "mouseleave":case "pointerenter":case "pointerleave":return 4;case "message":switch(Ej()){case De:return 1;case Mg:return 4;case ad:case Fj:return 16;case Ng:return 536870912;default:return 16}default:return 16}}function Og(){if(bd)return bd;
var a,b=Ee,c=b.length,d,e="value"in Za?Za.value:Za.textContent,f=e.length;for(a=0;a<c&&b[a]===e[a];a++);var g=c-a;for(d=1;d<=g&&b[c-d]===e[f-d];d++);return bd=e.slice(a,1<d?1-d:void 0)}function cd(a){var b=a.keyCode;"charCode"in a?(a=a.charCode,0===a&&13===b&&(a=13)):a=b;10===a&&(a=13);return 32<=a||13===a?a:0}function dd(){return!0}function Pg(){return!1}function ka(a){function b(b,d,e,f,g){this._reactName=b;this._targetInst=e;this.type=d;this.nativeEvent=f;this.target=g;this.currentTarget=null;
for(var c in a)a.hasOwnProperty(c)&&(b=a[c],this[c]=b?b(f):f[c]);this.isDefaultPrevented=(null!=f.defaultPrevented?f.defaultPrevented:!1===f.returnValue)?dd:Pg;this.isPropagationStopped=Pg;return this}E(b.prototype,{preventDefault:function(){this.defaultPrevented=!0;var a=this.nativeEvent;a&&(a.preventDefault?a.preventDefault():"unknown"!==typeof a.returnValue&&(a.returnValue=!1),this.isDefaultPrevented=dd)},stopPropagation:function(){var a=this.nativeEvent;a&&(a.stopPropagation?a.stopPropagation():
"unknown"!==typeof a.cancelBubble&&(a.cancelBubble=!0),this.isPropagationStopped=dd)},persist:function(){},isPersistent:dd});return b}function Gj(a){var b=this.nativeEvent;return b.getModifierState?b.getModifierState(a):(a=Hj[a])?!!b[a]:!1}function Fe(a){return Gj}function Qg(a,b){switch(a){case "keyup":return-1!==Ij.indexOf(b.keyCode);case "keydown":return 229!==b.keyCode;case "keypress":case "mousedown":case "focusout":return!0;default:return!1}}function Rg(a){a=a.detail;return"object"===typeof a&&
"data"in a?a.data:null}function Jj(a,b){switch(a){case "compositionend":return Rg(b);case "keypress":if(32!==b.which)return null;Sg=!0;return Tg;case "textInput":return a=b.data,a===Tg&&Sg?null:a;default:return null}}function Kj(a,b){if(Hb)return"compositionend"===a||!Ge&&Qg(a,b)?(a=Og(),bd=Ee=Za=null,Hb=!1,a):null;switch(a){case "paste":return null;case "keypress":if(!(b.ctrlKey||b.altKey||b.metaKey)||b.ctrlKey&&b.altKey){if(b.char&&1<b.char.length)return b.char;if(b.which)return String.fromCharCode(b.which)}return null;
case "compositionend":return Ug&&"ko"!==b.locale?null:b.data;default:return null}}function Vg(a){var b=a&&a.nodeName&&a.nodeName.toLowerCase();return"input"===b?!!Lj[a.type]:"textarea"===b?!0:!1}function Mj(a){if(!Ia)return!1;a="on"+a;var b=a in document;b||(b=document.createElement("div"),b.setAttribute(a,"return;"),b="function"===typeof b[a]);return b}function Wg(a,b,c,d){ug(d);b=ed(b,"onChange");0<b.length&&(c=new He("onChange","change",null,c,d),a.push({event:c,listeners:b}))}function Nj(a){Xg(a,
0)}function fd(a){var b=Ib(a);if(jg(b))return a}function Oj(a,b){if("change"===a)return b}function Yg(){oc&&(oc.detachEvent("onpropertychange",Zg),pc=oc=null)}function Zg(a){if("value"===a.propertyName&&fd(pc)){var b=[];Wg(b,pc,a,re(a));wg(Nj,b)}}function Pj(a,b,c){"focusin"===a?(Yg(),oc=b,pc=c,oc.attachEvent("onpropertychange",Zg)):"focusout"===a&&Yg()}function Qj(a,b){if("selectionchange"===a||"keyup"===a||"keydown"===a)return fd(pc)}function Rj(a,b){if("click"===a)return fd(b)}function Sj(a,b){if("input"===
a||"change"===a)return fd(b)}function Tj(a,b){return a===b&&(0!==a||1/a===1/b)||a!==a&&b!==b}function qc(a,b){if(ua(a,b))return!0;if("object"!==typeof a||null===a||"object"!==typeof b||null===b)return!1;var c=Object.keys(a),d=Object.keys(b);if(c.length!==d.length)return!1;for(d=0;d<c.length;d++){var e=c[d];if(!Zd.call(b,e)||!ua(a[e],b[e]))return!1}return!0}function $g(a){for(;a&&a.firstChild;)a=a.firstChild;return a}function ah(a,b){var c=$g(a);a=0;for(var d;c;){if(3===c.nodeType){d=a+c.textContent.length;
if(a<=b&&d>=b)return{node:c,offset:b-a};a=d}a:{for(;c;){if(c.nextSibling){c=c.nextSibling;break a}c=c.parentNode}c=void 0}c=$g(c)}}function bh(a,b){return a&&b?a===b?!0:a&&3===a.nodeType?!1:b&&3===b.nodeType?bh(a,b.parentNode):"contains"in a?a.contains(b):a.compareDocumentPosition?!!(a.compareDocumentPosition(b)&16):!1:!1}function ch(){for(var a=window,b=Qc();b instanceof a.HTMLIFrameElement;){try{var c="string"===typeof b.contentWindow.location.href}catch(d){c=!1}if(c)a=b.contentWindow;else break;
b=Qc(a.document)}return b}function Ie(a){var b=a&&a.nodeName&&a.nodeName.toLowerCase();return b&&("input"===b&&("text"===a.type||"search"===a.type||"tel"===a.type||"url"===a.type||"password"===a.type)||"textarea"===b||"true"===a.contentEditable)}function Uj(a){var b=ch(),c=a.focusedElem,d=a.selectionRange;if(b!==c&&c&&c.ownerDocument&&bh(c.ownerDocument.documentElement,c)){if(null!==d&&Ie(c))if(b=d.start,a=d.end,void 0===a&&(a=b),"selectionStart"in c)c.selectionStart=b,c.selectionEnd=Math.min(a,c.value.length);
else if(a=(b=c.ownerDocument||document)&&b.defaultView||window,a.getSelection){a=a.getSelection();var e=c.textContent.length,f=Math.min(d.start,e);d=void 0===d.end?f:Math.min(d.end,e);!a.extend&&f>d&&(e=d,d=f,f=e);e=ah(c,f);var g=ah(c,d);e&&g&&(1!==a.rangeCount||a.anchorNode!==e.node||a.anchorOffset!==e.offset||a.focusNode!==g.node||a.focusOffset!==g.offset)&&(b=b.createRange(),b.setStart(e.node,e.offset),a.removeAllRanges(),f>d?(a.addRange(b),a.extend(g.node,g.offset)):(b.setEnd(g.node,g.offset),
a.addRange(b)))}b=[];for(a=c;a=a.parentNode;)1===a.nodeType&&b.push({element:a,left:a.scrollLeft,top:a.scrollTop});"function"===typeof c.focus&&c.focus();for(c=0;c<b.length;c++)a=b[c],a.element.scrollLeft=a.left,a.element.scrollTop=a.top}}function dh(a,b,c){var d=c.window===c?c.document:9===c.nodeType?c:c.ownerDocument;Je||null==Jb||Jb!==Qc(d)||(d=Jb,"selectionStart"in d&&Ie(d)?d={start:d.selectionStart,end:d.selectionEnd}:(d=(d.ownerDocument&&d.ownerDocument.defaultView||window).getSelection(),d=
{anchorNode:d.anchorNode,anchorOffset:d.anchorOffset,focusNode:d.focusNode,focusOffset:d.focusOffset}),rc&&qc(rc,d)||(rc=d,d=ed(Ke,"onSelect"),0<d.length&&(b=new He("onSelect","select",null,b,c),a.push({event:b,listeners:d}),b.target=Jb)))}function gd(a,b){var c={};c[a.toLowerCase()]=b.toLowerCase();c["Webkit"+a]="webkit"+b;c["Moz"+a]="moz"+b;return c}function hd(a){if(Le[a])return Le[a];if(!Kb[a])return a;var b=Kb[a],c;for(c in b)if(b.hasOwnProperty(c)&&c in eh)return Le[a]=b[c];return a}function $a(a,
b){fh.set(a,b);nb(b,[a])}function gh(a,b,c){var d=a.type||"unknown-event";a.currentTarget=c;nj(d,b,void 0,a);a.currentTarget=null}function Xg(a,b){b=0!==(b&4);for(var c=0;c<a.length;c++){var d=a[c],e=d.event;d=d.listeners;a:{var f=void 0;if(b)for(var g=d.length-1;0<=g;g--){var h=d[g],k=h.instance,m=h.currentTarget;h=h.listener;if(k!==f&&e.isPropagationStopped())break a;gh(e,h,m);f=k}else for(g=0;g<d.length;g++){h=d[g];k=h.instance;m=h.currentTarget;h=h.listener;if(k!==f&&e.isPropagationStopped())break a;
gh(e,h,m);f=k}}}if(Tc)throw a=ue,Tc=!1,ue=null,a;}function B(a,b){var c=b[Me];void 0===c&&(c=b[Me]=new Set);var d=a+"__bubble";c.has(d)||(hh(b,a,2,!1),c.add(d))}function Ne(a,b,c){var d=0;b&&(d|=4);hh(c,a,d,b)}function sc(a){if(!a[id]){a[id]=!0;cg.forEach(function(b){"selectionchange"!==b&&(Vj.has(b)||Ne(b,!1,a),Ne(b,!0,a))});var b=9===a.nodeType?a:a.ownerDocument;null===b||b[id]||(b[id]=!0,Ne("selectionchange",!1,b))}}function hh(a,b,c,d,e){switch(Lg(b)){case 1:e=Aj;break;case 4:e=Bj;break;default:e=
Be}c=e.bind(null,b,c,a);e=void 0;!Oe||"touchstart"!==b&&"touchmove"!==b&&"wheel"!==b||(e=!0);d?void 0!==e?a.addEventListener(b,c,{capture:!0,passive:e}):a.addEventListener(b,c,!0):void 0!==e?a.addEventListener(b,c,{passive:e}):a.addEventListener(b,c,!1)}function Ce(a,b,c,d,e){var f=d;if(0===(b&1)&&0===(b&2)&&null!==d)a:for(;;){if(null===d)return;var g=d.tag;if(3===g||4===g){var h=d.stateNode.containerInfo;if(h===e||8===h.nodeType&&h.parentNode===e)break;if(4===g)for(g=d.return;null!==g;){var k=g.tag;
if(3===k||4===k)if(k=g.stateNode.containerInfo,k===e||8===k.nodeType&&k.parentNode===e)return;g=g.return}for(;null!==h;){g=pb(h);if(null===g)return;k=g.tag;if(5===k||6===k){d=f=g;continue a}h=h.parentNode}}d=d.return}wg(function(){var d=f,e=re(c),g=[];a:{var h=fh.get(a);if(void 0!==h){var k=He,n=a;switch(a){case "keypress":if(0===cd(c))break a;case "keydown":case "keyup":k=Wj;break;case "focusin":n="focus";k=Pe;break;case "focusout":n="blur";k=Pe;break;case "beforeblur":case "afterblur":k=Pe;break;
case "click":if(2===c.button)break a;case "auxclick":case "dblclick":case "mousedown":case "mousemove":case "mouseup":case "mouseout":case "mouseover":case "contextmenu":k=ih;break;case "drag":case "dragend":case "dragenter":case "dragexit":case "dragleave":case "dragover":case "dragstart":case "drop":k=Xj;break;case "touchcancel":case "touchend":case "touchmove":case "touchstart":k=Yj;break;case jh:case kh:case lh:k=Zj;break;case mh:k=ak;break;case "scroll":k=bk;break;case "wheel":k=ck;break;case "copy":case "cut":case "paste":k=
dk;break;case "gotpointercapture":case "lostpointercapture":case "pointercancel":case "pointerdown":case "pointermove":case "pointerout":case "pointerover":case "pointerup":k=nh}var l=0!==(b&4),p=!l&&"scroll"===a,A=l?null!==h?h+"Capture":null:h;l=[];for(var v=d,q;null!==v;){q=v;var M=q.stateNode;5===q.tag&&null!==M&&(q=M,null!==A&&(M=fc(v,A),null!=M&&l.push(tc(v,M,q))));if(p)break;v=v.return}0<l.length&&(h=new k(h,n,null,c,e),g.push({event:h,listeners:l}))}}if(0===(b&7)){a:{h="mouseover"===a||"pointerover"===
a;k="mouseout"===a||"pointerout"===a;if(h&&c!==ze&&(n=c.relatedTarget||c.fromElement)&&(pb(n)||n[Ja]))break a;if(k||h){h=e.window===e?e:(h=e.ownerDocument)?h.defaultView||h.parentWindow:window;if(k){if(n=c.relatedTarget||c.toElement,k=d,n=n?pb(n):null,null!==n&&(p=ob(n),n!==p||5!==n.tag&&6!==n.tag))n=null}else k=null,n=d;if(k!==n){l=ih;M="onMouseLeave";A="onMouseEnter";v="mouse";if("pointerout"===a||"pointerover"===a)l=nh,M="onPointerLeave",A="onPointerEnter",v="pointer";p=null==k?h:Ib(k);q=null==
n?h:Ib(n);h=new l(M,v+"leave",k,c,e);h.target=p;h.relatedTarget=q;M=null;pb(e)===d&&(l=new l(A,v+"enter",n,c,e),l.target=q,l.relatedTarget=p,M=l);p=M;if(k&&n)b:{l=k;A=n;v=0;for(q=l;q;q=Lb(q))v++;q=0;for(M=A;M;M=Lb(M))q++;for(;0<v-q;)l=Lb(l),v--;for(;0<q-v;)A=Lb(A),q--;for(;v--;){if(l===A||null!==A&&l===A.alternate)break b;l=Lb(l);A=Lb(A)}l=null}else l=null;null!==k&&oh(g,h,k,l,!1);null!==n&&null!==p&&oh(g,p,n,l,!0)}}}a:{h=d?Ib(d):window;k=h.nodeName&&h.nodeName.toLowerCase();if("select"===k||"input"===
k&&"file"===h.type)var ma=Oj;else if(Vg(h))if(ph)ma=Sj;else{ma=Qj;var va=Pj}else(k=h.nodeName)&&"input"===k.toLowerCase()&&("checkbox"===h.type||"radio"===h.type)&&(ma=Rj);if(ma&&(ma=ma(a,d))){Wg(g,ma,c,e);break a}va&&va(a,h,d);"focusout"===a&&(va=h._wrapperState)&&va.controlled&&"number"===h.type&&me(h,"number",h.value)}va=d?Ib(d):window;switch(a){case "focusin":if(Vg(va)||"true"===va.contentEditable)Jb=va,Ke=d,rc=null;break;case "focusout":rc=Ke=Jb=null;break;case "mousedown":Je=!0;break;case "contextmenu":case "mouseup":case "dragend":Je=
!1;dh(g,c,e);break;case "selectionchange":if(ek)break;case "keydown":case "keyup":dh(g,c,e)}var ab;if(Ge)b:{switch(a){case "compositionstart":var da="onCompositionStart";break b;case "compositionend":da="onCompositionEnd";break b;case "compositionupdate":da="onCompositionUpdate";break b}da=void 0}else Hb?Qg(a,c)&&(da="onCompositionEnd"):"keydown"===a&&229===c.keyCode&&(da="onCompositionStart");da&&(Ug&&"ko"!==c.locale&&(Hb||"onCompositionStart"!==da?"onCompositionEnd"===da&&Hb&&(ab=Og()):(Za=e,Ee=
"value"in Za?Za.value:Za.textContent,Hb=!0)),va=ed(d,da),0<va.length&&(da=new qh(da,a,null,c,e),g.push({event:da,listeners:va}),ab?da.data=ab:(ab=Rg(c),null!==ab&&(da.data=ab))));if(ab=fk?Jj(a,c):Kj(a,c))d=ed(d,"onBeforeInput"),0<d.length&&(e=new gk("onBeforeInput","beforeinput",null,c,e),g.push({event:e,listeners:d}),e.data=ab)}Xg(g,b)})}function tc(a,b,c){return{instance:a,listener:b,currentTarget:c}}function ed(a,b){for(var c=b+"Capture",d=[];null!==a;){var e=a,f=e.stateNode;5===e.tag&&null!==
f&&(e=f,f=fc(a,c),null!=f&&d.unshift(tc(a,f,e)),f=fc(a,b),null!=f&&d.push(tc(a,f,e)));a=a.return}return d}function Lb(a){if(null===a)return null;do a=a.return;while(a&&5!==a.tag);return a?a:null}function oh(a,b,c,d,e){for(var f=b._reactName,g=[];null!==c&&c!==d;){var h=c,k=h.alternate,m=h.stateNode;if(null!==k&&k===d)break;5===h.tag&&null!==m&&(h=m,e?(k=fc(c,f),null!=k&&g.unshift(tc(c,k,h))):e||(k=fc(c,f),null!=k&&g.push(tc(c,k,h))));c=c.return}0!==g.length&&a.push({event:b,listeners:g})}function rh(a){return("string"===
typeof a?a:""+a).replace(hk,"\n").replace(ik,"")}function jd(a,b,c,d){b=rh(b);if(rh(a)!==b&&c)throw Error(n(425));}function kd(){}function Qe(a,b){return"textarea"===a||"noscript"===a||"string"===typeof b.children||"number"===typeof b.children||"object"===typeof b.dangerouslySetInnerHTML&&null!==b.dangerouslySetInnerHTML&&null!=b.dangerouslySetInnerHTML.__html}function jk(a){setTimeout(function(){throw a;})}function Re(a,b){var c=b,d=0;do{var e=c.nextSibling;a.removeChild(c);if(e&&8===e.nodeType)if(c=
e.data,"/$"===c){if(0===d){a.removeChild(e);nc(b);return}d--}else"$"!==c&&"$?"!==c&&"$!"!==c||d++;c=e}while(c);nc(b)}function Ka(a){for(;null!=a;a=a.nextSibling){var b=a.nodeType;if(1===b||3===b)break;if(8===b){b=a.data;if("$"===b||"$!"===b||"$?"===b)break;if("/$"===b)return null}}return a}function sh(a){a=a.previousSibling;for(var b=0;a;){if(8===a.nodeType){var c=a.data;if("$"===c||"$!"===c||"$?"===c){if(0===b)return a;b--}else"/$"===c&&b++}a=a.previousSibling}return null}function pb(a){var b=a[Da];
if(b)return b;for(var c=a.parentNode;c;){if(b=c[Ja]||c[Da]){c=b.alternate;if(null!==b.child||null!==c&&null!==c.child)for(a=sh(a);null!==a;){if(c=a[Da])return c;a=sh(a)}return b}a=c;c=a.parentNode}return null}function ec(a){a=a[Da]||a[Ja];return!a||5!==a.tag&&6!==a.tag&&13!==a.tag&&3!==a.tag?null:a}function Ib(a){if(5===a.tag||6===a.tag)return a.stateNode;throw Error(n(33));}function Rc(a){return a[uc]||null}function bb(a){return{current:a}}function w(a,b){0>Mb||(a.current=Se[Mb],Se[Mb]=null,Mb--)}
function y(a,b,c){Mb++;Se[Mb]=a.current;a.current=b}function Nb(a,b){var c=a.type.contextTypes;if(!c)return cb;var d=a.stateNode;if(d&&d.__reactInternalMemoizedUnmaskedChildContext===b)return d.__reactInternalMemoizedMaskedChildContext;var e={},f;for(f in c)e[f]=b[f];d&&(a=a.stateNode,a.__reactInternalMemoizedUnmaskedChildContext=b,a.__reactInternalMemoizedMaskedChildContext=e);return e}function ea(a){a=a.childContextTypes;return null!==a&&void 0!==a}function th(a,b,c){if(J.current!==cb)throw Error(n(168));
y(J,b);y(S,c)}function uh(a,b,c){var d=a.stateNode;b=b.childContextTypes;if("function"!==typeof d.getChildContext)return c;d=d.getChildContext();for(var e in d)if(!(e in b))throw Error(n(108,hj(a)||"Unknown",e));return E({},c,d)}function ld(a){a=(a=a.stateNode)&&a.__reactInternalMemoizedMergedChildContext||cb;qb=J.current;y(J,a);y(S,S.current);return!0}function vh(a,b,c){var d=a.stateNode;if(!d)throw Error(n(169));c?(a=uh(a,b,qb),d.__reactInternalMemoizedMergedChildContext=a,w(S),w(J),y(J,a)):w(S);
y(S,c)}function wh(a){null===La?La=[a]:La.push(a)}function kk(a){md=!0;wh(a)}function db(){if(!Te&&null!==La){Te=!0;var a=0,b=z;try{var c=La;for(z=1;a<c.length;a++){var d=c[a];do d=d(!0);while(null!==d)}La=null;md=!1}catch(e){throw null!==La&&(La=La.slice(a+1)),xh(De,db),e;}finally{z=b,Te=!1}}return null}function rb(a,b){Ob[Pb++]=nd;Ob[Pb++]=od;od=a;nd=b}function yh(a,b,c){na[oa++]=Ma;na[oa++]=Na;na[oa++]=sb;sb=a;var d=Ma;a=Na;var e=32-ta(d)-1;d&=~(1<<e);c+=1;var f=32-ta(b)+e;if(30<f){var g=e-e%5;
f=(d&(1<<g)-1).toString(32);d>>=g;e-=g;Ma=1<<32-ta(b)+e|c<<e|d;Na=f+a}else Ma=1<<f|c<<e|d,Na=a}function Ue(a){null!==a.return&&(rb(a,1),yh(a,1,0))}function Ve(a){for(;a===od;)od=Ob[--Pb],Ob[Pb]=null,nd=Ob[--Pb],Ob[Pb]=null;for(;a===sb;)sb=na[--oa],na[oa]=null,Na=na[--oa],na[oa]=null,Ma=na[--oa],na[oa]=null}function zh(a,b){var c=pa(5,null,null,0);c.elementType="DELETED";c.stateNode=b;c.return=a;b=a.deletions;null===b?(a.deletions=[c],a.flags|=16):b.push(c)}function Ah(a,b){switch(a.tag){case 5:var c=
a.type;b=1!==b.nodeType||c.toLowerCase()!==b.nodeName.toLowerCase()?null:b;return null!==b?(a.stateNode=b,la=a,fa=Ka(b.firstChild),!0):!1;case 6:return b=""===a.pendingProps||3!==b.nodeType?null:b,null!==b?(a.stateNode=b,la=a,fa=null,!0):!1;case 13:return b=8!==b.nodeType?null:b,null!==b?(c=null!==sb?{id:Ma,overflow:Na}:null,a.memoizedState={dehydrated:b,treeContext:c,retryLane:1073741824},c=pa(18,null,null,0),c.stateNode=b,c.return=a,a.child=c,la=a,fa=null,!0):!1;default:return!1}}function We(a){return 0!==
(a.mode&1)&&0===(a.flags&128)}function Xe(a){if(D){var b=fa;if(b){var c=b;if(!Ah(a,b)){if(We(a))throw Error(n(418));b=Ka(c.nextSibling);var d=la;b&&Ah(a,b)?zh(d,c):(a.flags=a.flags&-4097|2,D=!1,la=a)}}else{if(We(a))throw Error(n(418));a.flags=a.flags&-4097|2;D=!1;la=a}}}function Bh(a){for(a=a.return;null!==a&&5!==a.tag&&3!==a.tag&&13!==a.tag;)a=a.return;la=a}function pd(a){if(a!==la)return!1;if(!D)return Bh(a),D=!0,!1;var b;(b=3!==a.tag)&&!(b=5!==a.tag)&&(b=a.type,b="head"!==b&&"body"!==b&&!Qe(a.type,
a.memoizedProps));if(b&&(b=fa)){if(We(a)){for(a=fa;a;)a=Ka(a.nextSibling);throw Error(n(418));}for(;b;)zh(a,b),b=Ka(b.nextSibling)}Bh(a);if(13===a.tag){a=a.memoizedState;a=null!==a?a.dehydrated:null;if(!a)throw Error(n(317));a:{a=a.nextSibling;for(b=0;a;){if(8===a.nodeType){var c=a.data;if("/$"===c){if(0===b){fa=Ka(a.nextSibling);break a}b--}else"$"!==c&&"$!"!==c&&"$?"!==c||b++}a=a.nextSibling}fa=null}}else fa=la?Ka(a.stateNode.nextSibling):null;return!0}function Qb(){fa=la=null;D=!1}function Ye(a){null===
wa?wa=[a]:wa.push(a)}function xa(a,b){if(a&&a.defaultProps){b=E({},b);a=a.defaultProps;for(var c in a)void 0===b[c]&&(b[c]=a[c]);return b}return b}function Ze(){$e=Rb=qd=null}function af(a,b){b=rd.current;w(rd);a._currentValue=b}function bf(a,b,c){for(;null!==a;){var d=a.alternate;(a.childLanes&b)!==b?(a.childLanes|=b,null!==d&&(d.childLanes|=b)):null!==d&&(d.childLanes&b)!==b&&(d.childLanes|=b);if(a===c)break;a=a.return}}function Sb(a,b){qd=a;$e=Rb=null;a=a.dependencies;null!==a&&null!==a.firstContext&&
(0!==(a.lanes&b)&&(ha=!0),a.firstContext=null)}function qa(a){var b=a._currentValue;if($e!==a)if(a={context:a,memoizedValue:b,next:null},null===Rb){if(null===qd)throw Error(n(308));Rb=a;qd.dependencies={lanes:0,firstContext:a}}else Rb=Rb.next=a;return b}function cf(a){null===tb?tb=[a]:tb.push(a)}function Ch(a,b,c,d){var e=b.interleaved;null===e?(c.next=c,cf(b)):(c.next=e.next,e.next=c);b.interleaved=c;return Oa(a,d)}function Oa(a,b){a.lanes|=b;var c=a.alternate;null!==c&&(c.lanes|=b);c=a;for(a=a.return;null!==
a;)a.childLanes|=b,c=a.alternate,null!==c&&(c.childLanes|=b),c=a,a=a.return;return 3===c.tag?c.stateNode:null}function df(a){a.updateQueue={baseState:a.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,interleaved:null,lanes:0},effects:null}}function Dh(a,b){a=a.updateQueue;b.updateQueue===a&&(b.updateQueue={baseState:a.baseState,firstBaseUpdate:a.firstBaseUpdate,lastBaseUpdate:a.lastBaseUpdate,shared:a.shared,effects:a.effects})}function Pa(a,b){return{eventTime:a,lane:b,
tag:0,payload:null,callback:null,next:null}}function eb(a,b,c){var d=a.updateQueue;if(null===d)return null;d=d.shared;if(0!==(p&2)){var e=d.pending;null===e?b.next=b:(b.next=e.next,e.next=b);d.pending=b;return lk(a,c)}e=d.interleaved;null===e?(b.next=b,cf(d)):(b.next=e.next,e.next=b);d.interleaved=b;return Oa(a,c)}function sd(a,b,c){b=b.updateQueue;if(null!==b&&(b=b.shared,0!==(c&4194240))){var d=b.lanes;d&=a.pendingLanes;c|=d;b.lanes=c;xe(a,c)}}function Eh(a,b){var c=a.updateQueue,d=a.alternate;
if(null!==d&&(d=d.updateQueue,c===d)){var e=null,f=null;c=c.firstBaseUpdate;if(null!==c){do{var g={eventTime:c.eventTime,lane:c.lane,tag:c.tag,payload:c.payload,callback:c.callback,next:null};null===f?e=f=g:f=f.next=g;c=c.next}while(null!==c);null===f?e=f=b:f=f.next=b}else e=f=b;c={baseState:d.baseState,firstBaseUpdate:e,lastBaseUpdate:f,shared:d.shared,effects:d.effects};a.updateQueue=c;return}a=c.lastBaseUpdate;null===a?c.firstBaseUpdate=b:a.next=b;c.lastBaseUpdate=b}function td(a,b,c,d){var e=
a.updateQueue;fb=!1;var f=e.firstBaseUpdate,g=e.lastBaseUpdate,h=e.shared.pending;if(null!==h){e.shared.pending=null;var k=h,m=k.next;k.next=null;null===g?f=m:g.next=m;g=k;var n=a.alternate;null!==n&&(n=n.updateQueue,h=n.lastBaseUpdate,h!==g&&(null===h?n.firstBaseUpdate=m:h.next=m,n.lastBaseUpdate=k))}if(null!==f){var l=e.baseState;g=0;n=m=k=null;h=f;do{var r=h.lane,p=h.eventTime;if((d&r)===r){null!==n&&(n=n.next={eventTime:p,lane:0,tag:h.tag,payload:h.payload,callback:h.callback,next:null});a:{var x=
a,F=h;r=b;p=c;switch(F.tag){case 1:x=F.payload;if("function"===typeof x){l=x.call(p,l,r);break a}l=x;break a;case 3:x.flags=x.flags&-65537|128;case 0:x=F.payload;r="function"===typeof x?x.call(p,l,r):x;if(null===r||void 0===r)break a;l=E({},l,r);break a;case 2:fb=!0}}null!==h.callback&&0!==h.lane&&(a.flags|=64,r=e.effects,null===r?e.effects=[h]:r.push(h))}else p={eventTime:p,lane:r,tag:h.tag,payload:h.payload,callback:h.callback,next:null},null===n?(m=n=p,k=l):n=n.next=p,g|=r;h=h.next;if(null===h)if(h=
e.shared.pending,null===h)break;else r=h,h=r.next,r.next=null,e.lastBaseUpdate=r,e.shared.pending=null}while(1);null===n&&(k=l);e.baseState=k;e.firstBaseUpdate=m;e.lastBaseUpdate=n;b=e.shared.interleaved;if(null!==b){e=b;do g|=e.lane,e=e.next;while(e!==b)}else null===f&&(e.shared.lanes=0);ra|=g;a.lanes=g;a.memoizedState=l}}function Fh(a,b,c){a=b.effects;b.effects=null;if(null!==a)for(b=0;b<a.length;b++){var d=a[b],e=d.callback;if(null!==e){d.callback=null;d=c;if("function"!==typeof e)throw Error(n(191,
e));e.call(d)}}}function ef(a,b,c,d){b=a.memoizedState;c=c(d,b);c=null===c||void 0===c?b:E({},b,c);a.memoizedState=c;0===a.lanes&&(a.updateQueue.baseState=c)}function Gh(a,b,c,d,e,f,g){a=a.stateNode;return"function"===typeof a.shouldComponentUpdate?a.shouldComponentUpdate(d,f,g):b.prototype&&b.prototype.isPureReactComponent?!qc(c,d)||!qc(e,f):!0}function Hh(a,b,c){var d=!1,e=cb;var f=b.contextType;"object"===typeof f&&null!==f?f=qa(f):(e=ea(b)?qb:J.current,d=b.contextTypes,f=(d=null!==d&&void 0!==
d)?Nb(a,e):cb);b=new b(c,f);a.memoizedState=null!==b.state&&void 0!==b.state?b.state:null;b.updater=ud;a.stateNode=b;b._reactInternals=a;d&&(a=a.stateNode,a.__reactInternalMemoizedUnmaskedChildContext=e,a.__reactInternalMemoizedMaskedChildContext=f);return b}function Ih(a,b,c,d){a=b.state;"function"===typeof b.componentWillReceiveProps&&b.componentWillReceiveProps(c,d);"function"===typeof b.UNSAFE_componentWillReceiveProps&&b.UNSAFE_componentWillReceiveProps(c,d);b.state!==a&&ud.enqueueReplaceState(b,
b.state,null)}function ff(a,b,c,d){var e=a.stateNode;e.props=c;e.state=a.memoizedState;e.refs=Jh;df(a);var f=b.contextType;"object"===typeof f&&null!==f?e.context=qa(f):(f=ea(b)?qb:J.current,e.context=Nb(a,f));e.state=a.memoizedState;f=b.getDerivedStateFromProps;"function"===typeof f&&(ef(a,b,f,c),e.state=a.memoizedState);"function"===typeof b.getDerivedStateFromProps||"function"===typeof e.getSnapshotBeforeUpdate||"function"!==typeof e.UNSAFE_componentWillMount&&"function"!==typeof e.componentWillMount||
(b=e.state,"function"===typeof e.componentWillMount&&e.componentWillMount(),"function"===typeof e.UNSAFE_componentWillMount&&e.UNSAFE_componentWillMount(),b!==e.state&&ud.enqueueReplaceState(e,e.state,null),td(a,c,e,d),e.state=a.memoizedState);"function"===typeof e.componentDidMount&&(a.flags|=4194308)}function vc(a,b,c){a=c.ref;if(null!==a&&"function"!==typeof a&&"object"!==typeof a){if(c._owner){c=c._owner;if(c){if(1!==c.tag)throw Error(n(309));var d=c.stateNode}if(!d)throw Error(n(147,a));var e=
d,f=""+a;if(null!==b&&null!==b.ref&&"function"===typeof b.ref&&b.ref._stringRef===f)return b.ref;b=function(a){var b=e.refs;b===Jh&&(b=e.refs={});null===a?delete b[f]:b[f]=a};b._stringRef=f;return b}if("string"!==typeof a)throw Error(n(284));if(!c._owner)throw Error(n(290,a));}return a}function vd(a,b){a=Object.prototype.toString.call(b);throw Error(n(31,"[object Object]"===a?"object with keys {"+Object.keys(b).join(", ")+"}":a));}function Kh(a){var b=a._init;return b(a._payload)}function Lh(a){function b(b,
c){if(a){var d=b.deletions;null===d?(b.deletions=[c],b.flags|=16):d.push(c)}}function c(c,d){if(!a)return null;for(;null!==d;)b(c,d),d=d.sibling;return null}function d(a,b){for(a=new Map;null!==b;)null!==b.key?a.set(b.key,b):a.set(b.index,b),b=b.sibling;return a}function e(a,b){a=gb(a,b);a.index=0;a.sibling=null;return a}function f(b,c,d){b.index=d;if(!a)return b.flags|=1048576,c;d=b.alternate;if(null!==d)return d=d.index,d<c?(b.flags|=2,c):d;b.flags|=2;return c}function g(b){a&&null===b.alternate&&
(b.flags|=2);return b}function h(a,b,c,d){if(null===b||6!==b.tag)return b=gf(c,a.mode,d),b.return=a,b;b=e(b,c);b.return=a;return b}function k(a,b,c,d){var f=c.type;if(f===Bb)return l(a,b,c.props.children,d,c.key);if(null!==b&&(b.elementType===f||"object"===typeof f&&null!==f&&f.$$typeof===Ta&&Kh(f)===b.type))return d=e(b,c.props),d.ref=vc(a,b,c),d.return=a,d;d=wd(c.type,c.key,c.props,null,a.mode,d);d.ref=vc(a,b,c);d.return=a;return d}function m(a,b,c,d){if(null===b||4!==b.tag||b.stateNode.containerInfo!==
c.containerInfo||b.stateNode.implementation!==c.implementation)return b=hf(c,a.mode,d),b.return=a,b;b=e(b,c.children||[]);b.return=a;return b}function l(a,b,c,d,f){if(null===b||7!==b.tag)return b=ub(c,a.mode,d,f),b.return=a,b;b=e(b,c);b.return=a;return b}function u(a,b,c){if("string"===typeof b&&""!==b||"number"===typeof b)return b=gf(""+b,a.mode,c),b.return=a,b;if("object"===typeof b&&null!==b){switch(b.$$typeof){case xd:return c=wd(b.type,b.key,b.props,null,a.mode,c),c.ref=vc(a,null,b),c.return=
a,c;case Cb:return b=hf(b,a.mode,c),b.return=a,b;case Ta:var d=b._init;return u(a,d(b._payload),c)}if(cc(b)||ac(b))return b=ub(b,a.mode,c,null),b.return=a,b;vd(a,b)}return null}function r(a,b,c,d){var e=null!==b?b.key:null;if("string"===typeof c&&""!==c||"number"===typeof c)return null!==e?null:h(a,b,""+c,d);if("object"===typeof c&&null!==c){switch(c.$$typeof){case xd:return c.key===e?k(a,b,c,d):null;case Cb:return c.key===e?m(a,b,c,d):null;case Ta:return e=c._init,r(a,b,e(c._payload),d)}if(cc(c)||
ac(c))return null!==e?null:l(a,b,c,d,null);vd(a,c)}return null}function p(a,b,c,d,e){if("string"===typeof d&&""!==d||"number"===typeof d)return a=a.get(c)||null,h(b,a,""+d,e);if("object"===typeof d&&null!==d){switch(d.$$typeof){case xd:return a=a.get(null===d.key?c:d.key)||null,k(b,a,d,e);case Cb:return a=a.get(null===d.key?c:d.key)||null,m(b,a,d,e);case Ta:var f=d._init;return p(a,b,c,f(d._payload),e)}if(cc(d)||ac(d))return a=a.get(c)||null,l(b,a,d,e,null);vd(b,d)}return null}function x(e,g,h,k){for(var n=
null,m=null,l=g,q=g=0,v=null;null!==l&&q<h.length;q++){l.index>q?(v=l,l=null):v=l.sibling;var A=r(e,l,h[q],k);if(null===A){null===l&&(l=v);break}a&&l&&null===A.alternate&&b(e,l);g=f(A,g,q);null===m?n=A:m.sibling=A;m=A;l=v}if(q===h.length)return c(e,l),D&&rb(e,q),n;if(null===l){for(;q<h.length;q++)l=u(e,h[q],k),null!==l&&(g=f(l,g,q),null===m?n=l:m.sibling=l,m=l);D&&rb(e,q);return n}for(l=d(e,l);q<h.length;q++)v=p(l,e,q,h[q],k),null!==v&&(a&&null!==v.alternate&&l.delete(null===v.key?q:v.key),g=f(v,
g,q),null===m?n=v:m.sibling=v,m=v);a&&l.forEach(function(a){return b(e,a)});D&&rb(e,q);return n}function F(e,g,h,k){var m=ac(h);if("function"!==typeof m)throw Error(n(150));h=m.call(h);if(null==h)throw Error(n(151));for(var l=m=null,q=g,v=g=0,A=null,t=h.next();null!==q&&!t.done;v++,t=h.next()){q.index>v?(A=q,q=null):A=q.sibling;var x=r(e,q,t.value,k);if(null===x){null===q&&(q=A);break}a&&q&&null===x.alternate&&b(e,q);g=f(x,g,v);null===l?m=x:l.sibling=x;l=x;q=A}if(t.done)return c(e,q),D&&rb(e,v),m;
if(null===q){for(;!t.done;v++,t=h.next())t=u(e,t.value,k),null!==t&&(g=f(t,g,v),null===l?m=t:l.sibling=t,l=t);D&&rb(e,v);return m}for(q=d(e,q);!t.done;v++,t=h.next())t=p(q,e,v,t.value,k),null!==t&&(a&&null!==t.alternate&&q.delete(null===t.key?v:t.key),g=f(t,g,v),null===l?m=t:l.sibling=t,l=t);a&&q.forEach(function(a){return b(e,a)});D&&rb(e,v);return m}function w(a,d,f,h){"object"===typeof f&&null!==f&&f.type===Bb&&null===f.key&&(f=f.props.children);if("object"===typeof f&&null!==f){switch(f.$$typeof){case xd:a:{for(var k=
f.key,m=d;null!==m;){if(m.key===k){k=f.type;if(k===Bb){if(7===m.tag){c(a,m.sibling);d=e(m,f.props.children);d.return=a;a=d;break a}}else if(m.elementType===k||"object"===typeof k&&null!==k&&k.$$typeof===Ta&&Kh(k)===m.type){c(a,m.sibling);d=e(m,f.props);d.ref=vc(a,m,f);d.return=a;a=d;break a}c(a,m);break}else b(a,m);m=m.sibling}f.type===Bb?(d=ub(f.props.children,a.mode,h,f.key),d.return=a,a=d):(h=wd(f.type,f.key,f.props,null,a.mode,h),h.ref=vc(a,d,f),h.return=a,a=h)}return g(a);case Cb:a:{for(m=f.key;null!==
d;){if(d.key===m)if(4===d.tag&&d.stateNode.containerInfo===f.containerInfo&&d.stateNode.implementation===f.implementation){c(a,d.sibling);d=e(d,f.children||[]);d.return=a;a=d;break a}else{c(a,d);break}else b(a,d);d=d.sibling}d=hf(f,a.mode,h);d.return=a;a=d}return g(a);case Ta:return m=f._init,w(a,d,m(f._payload),h)}if(cc(f))return x(a,d,f,h);if(ac(f))return F(a,d,f,h);vd(a,f)}return"string"===typeof f&&""!==f||"number"===typeof f?(f=""+f,null!==d&&6===d.tag?(c(a,d.sibling),d=e(d,f),d.return=a,a=d):
(c(a,d),d=gf(f,a.mode,h),d.return=a,a=d),g(a)):c(a,d)}return w}function vb(a){if(a===wc)throw Error(n(174));return a}function jf(a,b){y(xc,b);y(yc,a);y(Ea,wc);a=b.nodeType;switch(a){case 9:case 11:b=(b=b.documentElement)?b.namespaceURI:oe(null,"");break;default:a=8===a?b.parentNode:b,b=a.namespaceURI||null,a=a.tagName,b=oe(b,a)}w(Ea);y(Ea,b)}function Tb(a){w(Ea);w(yc);w(xc)}function Mh(a){vb(xc.current);var b=vb(Ea.current);var c=oe(b,a.type);b!==c&&(y(yc,a),y(Ea,c))}function kf(a){yc.current===a&&
(w(Ea),w(yc))}function yd(a){for(var b=a;null!==b;){if(13===b.tag){var c=b.memoizedState;if(null!==c&&(c=c.dehydrated,null===c||"$?"===c.data||"$!"===c.data))return b}else if(19===b.tag&&void 0!==b.memoizedProps.revealOrder){if(0!==(b.flags&128))return b}else if(null!==b.child){b.child.return=b;b=b.child;continue}if(b===a)break;for(;null===b.sibling;){if(null===b.return||b.return===a)return null;b=b.return}b.sibling.return=b.return;b=b.sibling}return null}function lf(){for(var a=0;a<mf.length;a++)mf[a]._workInProgressVersionPrimary=
null;mf.length=0}function V(){throw Error(n(321));}function nf(a,b){if(null===b)return!1;for(var c=0;c<b.length&&c<a.length;c++)if(!ua(a[c],b[c]))return!1;return!0}function of(a,b,c,d,e,f){wb=f;C=b;b.memoizedState=null;b.updateQueue=null;b.lanes=0;zd.current=null===a||null===a.memoizedState?mk:nk;a=c(d,e);if(zc){f=0;do{zc=!1;Ac=0;if(25<=f)throw Error(n(301));f+=1;N=K=null;b.updateQueue=null;zd.current=ok;a=c(d,e)}while(zc)}zd.current=Ad;b=null!==K&&null!==K.next;wb=0;N=K=C=null;Bd=!1;if(b)throw Error(n(300));
return a}function pf(){var a=0!==Ac;Ac=0;return a}function Fa(){var a={memoizedState:null,baseState:null,baseQueue:null,queue:null,next:null};null===N?C.memoizedState=N=a:N=N.next=a;return N}function sa(){if(null===K){var a=C.alternate;a=null!==a?a.memoizedState:null}else a=K.next;var b=null===N?C.memoizedState:N.next;if(null!==b)N=b,K=a;else{if(null===a)throw Error(n(310));K=a;a={memoizedState:K.memoizedState,baseState:K.baseState,baseQueue:K.baseQueue,queue:K.queue,next:null};null===N?C.memoizedState=
N=a:N=N.next=a}return N}function Bc(a,b){return"function"===typeof b?b(a):b}function qf(a,b,c){b=sa();c=b.queue;if(null===c)throw Error(n(311));c.lastRenderedReducer=a;var d=K,e=d.baseQueue,f=c.pending;if(null!==f){if(null!==e){var g=e.next;e.next=f.next;f.next=g}d.baseQueue=e=f;c.pending=null}if(null!==e){f=e.next;d=d.baseState;var h=g=null,k=null,m=f;do{var l=m.lane;if((wb&l)===l)null!==k&&(k=k.next={lane:0,action:m.action,hasEagerState:m.hasEagerState,eagerState:m.eagerState,next:null}),d=m.hasEagerState?
m.eagerState:a(d,m.action);else{var u={lane:l,action:m.action,hasEagerState:m.hasEagerState,eagerState:m.eagerState,next:null};null===k?(h=k=u,g=d):k=k.next=u;C.lanes|=l;ra|=l}m=m.next}while(null!==m&&m!==f);null===k?g=d:k.next=h;ua(d,b.memoizedState)||(ha=!0);b.memoizedState=d;b.baseState=g;b.baseQueue=k;c.lastRenderedState=d}a=c.interleaved;if(null!==a){e=a;do f=e.lane,C.lanes|=f,ra|=f,e=e.next;while(e!==a)}else null===e&&(c.lanes=0);return[b.memoizedState,c.dispatch]}function rf(a,b,c){b=sa();
c=b.queue;if(null===c)throw Error(n(311));c.lastRenderedReducer=a;var d=c.dispatch,e=c.pending,f=b.memoizedState;if(null!==e){c.pending=null;var g=e=e.next;do f=a(f,g.action),g=g.next;while(g!==e);ua(f,b.memoizedState)||(ha=!0);b.memoizedState=f;null===b.baseQueue&&(b.baseState=f);c.lastRenderedState=f}return[f,d]}function Nh(a,b,c){}function Oh(a,b,c){c=C;var d=sa(),e=b(),f=!ua(d.memoizedState,e);f&&(d.memoizedState=e,ha=!0);d=d.queue;sf(Ph.bind(null,c,d,a),[a]);if(d.getSnapshot!==b||f||null!==N&&
N.memoizedState.tag&1){c.flags|=2048;Cc(9,Qh.bind(null,c,d,e,b),void 0,null);if(null===O)throw Error(n(349));0!==(wb&30)||Rh(c,b,e)}return e}function Rh(a,b,c){a.flags|=16384;a={getSnapshot:b,value:c};b=C.updateQueue;null===b?(b={lastEffect:null,stores:null},C.updateQueue=b,b.stores=[a]):(c=b.stores,null===c?b.stores=[a]:c.push(a))}function Qh(a,b,c,d){b.value=c;b.getSnapshot=d;Sh(b)&&Th(a)}function Ph(a,b,c){return c(function(){Sh(b)&&Th(a)})}function Sh(a){var b=a.getSnapshot;a=a.value;try{var c=
b();return!ua(a,c)}catch(d){return!0}}function Th(a){var b=Oa(a,1);null!==b&&ya(b,a,1,-1)}function Uh(a){var b=Fa();"function"===typeof a&&(a=a());b.memoizedState=b.baseState=a;a={pending:null,interleaved:null,lanes:0,dispatch:null,lastRenderedReducer:Bc,lastRenderedState:a};b.queue=a;a=a.dispatch=pk.bind(null,C,a);return[b.memoizedState,a]}function Cc(a,b,c,d){a={tag:a,create:b,destroy:c,deps:d,next:null};b=C.updateQueue;null===b?(b={lastEffect:null,stores:null},C.updateQueue=b,b.lastEffect=a.next=
a):(c=b.lastEffect,null===c?b.lastEffect=a.next=a:(d=c.next,c.next=a,a.next=d,b.lastEffect=a));return a}function Vh(a){return sa().memoizedState}function Cd(a,b,c,d){var e=Fa();C.flags|=a;e.memoizedState=Cc(1|b,c,void 0,void 0===d?null:d)}function Dd(a,b,c,d){var e=sa();d=void 0===d?null:d;var f=void 0;if(null!==K){var g=K.memoizedState;f=g.destroy;if(null!==d&&nf(d,g.deps)){e.memoizedState=Cc(b,c,f,d);return}}C.flags|=a;e.memoizedState=Cc(1|b,c,f,d)}function Wh(a,b){return Cd(8390656,8,a,b)}function sf(a,
b){return Dd(2048,8,a,b)}function Xh(a,b){return Dd(4,2,a,b)}function Yh(a,b){return Dd(4,4,a,b)}function Zh(a,b){if("function"===typeof b)return a=a(),b(a),function(){b(null)};if(null!==b&&void 0!==b)return a=a(),b.current=a,function(){b.current=null}}function $h(a,b,c){c=null!==c&&void 0!==c?c.concat([a]):null;return Dd(4,4,Zh.bind(null,b,a),c)}function tf(a,b){}function ai(a,b){var c=sa();b=void 0===b?null:b;var d=c.memoizedState;if(null!==d&&null!==b&&nf(b,d[1]))return d[0];c.memoizedState=[a,
b];return a}function bi(a,b){var c=sa();b=void 0===b?null:b;var d=c.memoizedState;if(null!==d&&null!==b&&nf(b,d[1]))return d[0];a=a();c.memoizedState=[a,b];return a}function ci(a,b,c){if(0===(wb&21))return a.baseState&&(a.baseState=!1,ha=!0),a.memoizedState=c;ua(c,b)||(c=Dg(),C.lanes|=c,ra|=c,a.baseState=!0);return b}function qk(a,b,c){c=z;z=0!==c&&4>c?c:4;a(!0);var d=uf.transition;uf.transition={};try{a(!1),b()}finally{z=c,uf.transition=d}}function di(){return sa().memoizedState}function rk(a,b,
c){var d=hb(a);c={lane:d,action:c,hasEagerState:!1,eagerState:null,next:null};if(ei(a))fi(b,c);else if(c=Ch(a,b,c,d),null!==c){var e=Z();ya(c,a,d,e);gi(c,b,d)}}function pk(a,b,c){var d=hb(a),e={lane:d,action:c,hasEagerState:!1,eagerState:null,next:null};if(ei(a))fi(b,e);else{var f=a.alternate;if(0===a.lanes&&(null===f||0===f.lanes)&&(f=b.lastRenderedReducer,null!==f))try{var g=b.lastRenderedState,h=f(g,c);e.hasEagerState=!0;e.eagerState=h;if(ua(h,g)){var k=b.interleaved;null===k?(e.next=e,cf(b)):
(e.next=k.next,k.next=e);b.interleaved=e;return}}catch(m){}finally{}c=Ch(a,b,e,d);null!==c&&(e=Z(),ya(c,a,d,e),gi(c,b,d))}}function ei(a){var b=a.alternate;return a===C||null!==b&&b===C}function fi(a,b){zc=Bd=!0;var c=a.pending;null===c?b.next=b:(b.next=c.next,c.next=b);a.pending=b}function gi(a,b,c){if(0!==(c&4194240)){var d=b.lanes;d&=a.pendingLanes;c|=d;b.lanes=c;xe(a,c)}}function Ub(a,b){try{var c="",d=b;do c+=gj(d),d=d.return;while(d);var e=c}catch(f){e="\nError generating stack: "+f.message+
"\n"+f.stack}return{value:a,source:b,stack:e,digest:null}}function vf(a,b,c){return{value:a,source:null,stack:null!=c?c:null,digest:null!=b?b:null}}function wf(a,b){try{console.error(b.value)}catch(c){setTimeout(function(){throw c;})}}function hi(a,b,c){c=Pa(-1,c);c.tag=3;c.payload={element:null};var d=b.value;c.callback=function(){Ed||(Ed=!0,xf=d);wf(a,b)};return c}function ii(a,b,c){c=Pa(-1,c);c.tag=3;var d=a.type.getDerivedStateFromError;if("function"===typeof d){var e=b.value;c.payload=function(){return d(e)};
c.callback=function(){wf(a,b)}}var f=a.stateNode;null!==f&&"function"===typeof f.componentDidCatch&&(c.callback=function(){wf(a,b);"function"!==typeof d&&(null===ib?ib=new Set([this]):ib.add(this));var c=b.stack;this.componentDidCatch(b.value,{componentStack:null!==c?c:""})});return c}function ji(a,b,c){var d=a.pingCache;if(null===d){d=a.pingCache=new sk;var e=new Set;d.set(b,e)}else e=d.get(b),void 0===e&&(e=new Set,d.set(b,e));e.has(c)||(e.add(c),a=tk.bind(null,a,b,c),b.then(a,a))}function ki(a){do{var b;
if(b=13===a.tag)b=a.memoizedState,b=null!==b?null!==b.dehydrated?!0:!1:!0;if(b)return a;a=a.return}while(null!==a);return null}function li(a,b,c,d,e){if(0===(a.mode&1))return a===b?a.flags|=65536:(a.flags|=128,c.flags|=131072,c.flags&=-52805,1===c.tag&&(null===c.alternate?c.tag=17:(b=Pa(-1,1),b.tag=2,eb(c,b,1))),c.lanes|=1),a;a.flags|=65536;a.lanes=e;return a}function aa(a,b,c,d){b.child=null===a?mi(b,null,c,d):Vb(b,a.child,c,d)}function ni(a,b,c,d,e){c=c.render;var f=b.ref;Sb(b,e);d=of(a,b,c,d,f,
e);c=pf();if(null!==a&&!ha)return b.updateQueue=a.updateQueue,b.flags&=-2053,a.lanes&=~e,Qa(a,b,e);D&&c&&Ue(b);b.flags|=1;aa(a,b,d,e);return b.child}function oi(a,b,c,d,e){if(null===a){var f=c.type;if("function"===typeof f&&!yf(f)&&void 0===f.defaultProps&&null===c.compare&&void 0===c.defaultProps)return b.tag=15,b.type=f,pi(a,b,f,d,e);a=wd(c.type,null,d,b,b.mode,e);a.ref=b.ref;a.return=b;return b.child=a}f=a.child;if(0===(a.lanes&e)){var g=f.memoizedProps;c=c.compare;c=null!==c?c:qc;if(c(g,d)&&a.ref===
b.ref)return Qa(a,b,e)}b.flags|=1;a=gb(f,d);a.ref=b.ref;a.return=b;return b.child=a}function pi(a,b,c,d,e){if(null!==a){var f=a.memoizedProps;if(qc(f,d)&&a.ref===b.ref)if(ha=!1,b.pendingProps=d=f,0!==(a.lanes&e))0!==(a.flags&131072)&&(ha=!0);else return b.lanes=a.lanes,Qa(a,b,e)}return zf(a,b,c,d,e)}function qi(a,b,c){var d=b.pendingProps,e=d.children,f=null!==a?a.memoizedState:null;if("hidden"===d.mode)if(0===(b.mode&1))b.memoizedState={baseLanes:0,cachePool:null,transitions:null},y(Ga,ba),ba|=c;
else{if(0===(c&1073741824))return a=null!==f?f.baseLanes|c:c,b.lanes=b.childLanes=1073741824,b.memoizedState={baseLanes:a,cachePool:null,transitions:null},b.updateQueue=null,y(Ga,ba),ba|=a,null;b.memoizedState={baseLanes:0,cachePool:null,transitions:null};d=null!==f?f.baseLanes:c;y(Ga,ba);ba|=d}else null!==f?(d=f.baseLanes|c,b.memoizedState=null):d=c,y(Ga,ba),ba|=d;aa(a,b,e,c);return b.child}function ri(a,b){var c=b.ref;if(null===a&&null!==c||null!==a&&a.ref!==c)b.flags|=512,b.flags|=2097152}function zf(a,
b,c,d,e){var f=ea(c)?qb:J.current;f=Nb(b,f);Sb(b,e);c=of(a,b,c,d,f,e);d=pf();if(null!==a&&!ha)return b.updateQueue=a.updateQueue,b.flags&=-2053,a.lanes&=~e,Qa(a,b,e);D&&d&&Ue(b);b.flags|=1;aa(a,b,c,e);return b.child}function si(a,b,c,d,e){if(ea(c)){var f=!0;ld(b)}else f=!1;Sb(b,e);if(null===b.stateNode)Fd(a,b),Hh(b,c,d),ff(b,c,d,e),d=!0;else if(null===a){var g=b.stateNode,h=b.memoizedProps;g.props=h;var k=g.context,m=c.contextType;"object"===typeof m&&null!==m?m=qa(m):(m=ea(c)?qb:J.current,m=Nb(b,
m));var l=c.getDerivedStateFromProps,n="function"===typeof l||"function"===typeof g.getSnapshotBeforeUpdate;n||"function"!==typeof g.UNSAFE_componentWillReceiveProps&&"function"!==typeof g.componentWillReceiveProps||(h!==d||k!==m)&&Ih(b,g,d,m);fb=!1;var r=b.memoizedState;g.state=r;td(b,d,g,e);k=b.memoizedState;h!==d||r!==k||S.current||fb?("function"===typeof l&&(ef(b,c,l,d),k=b.memoizedState),(h=fb||Gh(b,c,h,d,r,k,m))?(n||"function"!==typeof g.UNSAFE_componentWillMount&&"function"!==typeof g.componentWillMount||
("function"===typeof g.componentWillMount&&g.componentWillMount(),"function"===typeof g.UNSAFE_componentWillMount&&g.UNSAFE_componentWillMount()),"function"===typeof g.componentDidMount&&(b.flags|=4194308)):("function"===typeof g.componentDidMount&&(b.flags|=4194308),b.memoizedProps=d,b.memoizedState=k),g.props=d,g.state=k,g.context=m,d=h):("function"===typeof g.componentDidMount&&(b.flags|=4194308),d=!1)}else{g=b.stateNode;Dh(a,b);h=b.memoizedProps;m=b.type===b.elementType?h:xa(b.type,h);g.props=
m;n=b.pendingProps;r=g.context;k=c.contextType;"object"===typeof k&&null!==k?k=qa(k):(k=ea(c)?qb:J.current,k=Nb(b,k));var p=c.getDerivedStateFromProps;(l="function"===typeof p||"function"===typeof g.getSnapshotBeforeUpdate)||"function"!==typeof g.UNSAFE_componentWillReceiveProps&&"function"!==typeof g.componentWillReceiveProps||(h!==n||r!==k)&&Ih(b,g,d,k);fb=!1;r=b.memoizedState;g.state=r;td(b,d,g,e);var x=b.memoizedState;h!==n||r!==x||S.current||fb?("function"===typeof p&&(ef(b,c,p,d),x=b.memoizedState),
(m=fb||Gh(b,c,m,d,r,x,k)||!1)?(l||"function"!==typeof g.UNSAFE_componentWillUpdate&&"function"!==typeof g.componentWillUpdate||("function"===typeof g.componentWillUpdate&&g.componentWillUpdate(d,x,k),"function"===typeof g.UNSAFE_componentWillUpdate&&g.UNSAFE_componentWillUpdate(d,x,k)),"function"===typeof g.componentDidUpdate&&(b.flags|=4),"function"===typeof g.getSnapshotBeforeUpdate&&(b.flags|=1024)):("function"!==typeof g.componentDidUpdate||h===a.memoizedProps&&r===a.memoizedState||(b.flags|=
4),"function"!==typeof g.getSnapshotBeforeUpdate||h===a.memoizedProps&&r===a.memoizedState||(b.flags|=1024),b.memoizedProps=d,b.memoizedState=x),g.props=d,g.state=x,g.context=k,d=m):("function"!==typeof g.componentDidUpdate||h===a.memoizedProps&&r===a.memoizedState||(b.flags|=4),"function"!==typeof g.getSnapshotBeforeUpdate||h===a.memoizedProps&&r===a.memoizedState||(b.flags|=1024),d=!1)}return Af(a,b,c,d,f,e)}function Af(a,b,c,d,e,f){ri(a,b);var g=0!==(b.flags&128);if(!d&&!g)return e&&vh(b,c,!1),
Qa(a,b,f);d=b.stateNode;uk.current=b;var h=g&&"function"!==typeof c.getDerivedStateFromError?null:d.render();b.flags|=1;null!==a&&g?(b.child=Vb(b,a.child,null,f),b.child=Vb(b,null,h,f)):aa(a,b,h,f);b.memoizedState=d.state;e&&vh(b,c,!0);return b.child}function ti(a){var b=a.stateNode;b.pendingContext?th(a,b.pendingContext,b.pendingContext!==b.context):b.context&&th(a,b.context,!1);jf(a,b.containerInfo)}function ui(a,b,c,d,e){Qb();Ye(e);b.flags|=256;aa(a,b,c,d);return b.child}function Bf(a){return{baseLanes:a,
cachePool:null,transitions:null}}function vi(a,b,c){var d=b.pendingProps,e=G.current,f=!1,g=0!==(b.flags&128),h;(h=g)||(h=null!==a&&null===a.memoizedState?!1:0!==(e&2));if(h)f=!0,b.flags&=-129;else if(null===a||null!==a.memoizedState)e|=1;y(G,e&1);if(null===a){Xe(b);a=b.memoizedState;if(null!==a&&(a=a.dehydrated,null!==a))return 0===(b.mode&1)?b.lanes=1:"$!"===a.data?b.lanes=8:b.lanes=1073741824,null;g=d.children;a=d.fallback;return f?(d=b.mode,f=b.child,g={mode:"hidden",children:g},0===(d&1)&&null!==
f?(f.childLanes=0,f.pendingProps=g):f=Gd(g,d,0,null),a=ub(a,d,c,null),f.return=b,a.return=b,f.sibling=a,b.child=f,b.child.memoizedState=Bf(c),b.memoizedState=Cf,a):Df(b,g)}e=a.memoizedState;if(null!==e&&(h=e.dehydrated,null!==h))return vk(a,b,g,d,h,e,c);if(f){f=d.fallback;g=b.mode;e=a.child;h=e.sibling;var k={mode:"hidden",children:d.children};0===(g&1)&&b.child!==e?(d=b.child,d.childLanes=0,d.pendingProps=k,b.deletions=null):(d=gb(e,k),d.subtreeFlags=e.subtreeFlags&14680064);null!==h?f=gb(h,f):(f=
ub(f,g,c,null),f.flags|=2);f.return=b;d.return=b;d.sibling=f;b.child=d;d=f;f=b.child;g=a.child.memoizedState;g=null===g?Bf(c):{baseLanes:g.baseLanes|c,cachePool:null,transitions:g.transitions};f.memoizedState=g;f.childLanes=a.childLanes&~c;b.memoizedState=Cf;return d}f=a.child;a=f.sibling;d=gb(f,{mode:"visible",children:d.children});0===(b.mode&1)&&(d.lanes=c);d.return=b;d.sibling=null;null!==a&&(c=b.deletions,null===c?(b.deletions=[a],b.flags|=16):c.push(a));b.child=d;b.memoizedState=null;return d}
function Df(a,b,c){b=Gd({mode:"visible",children:b},a.mode,0,null);b.return=a;return a.child=b}function Hd(a,b,c,d){null!==d&&Ye(d);Vb(b,a.child,null,c);a=Df(b,b.pendingProps.children);a.flags|=2;b.memoizedState=null;return a}function vk(a,b,c,d,e,f,g){if(c){if(b.flags&256)return b.flags&=-257,d=vf(Error(n(422))),Hd(a,b,g,d);if(null!==b.memoizedState)return b.child=a.child,b.flags|=128,null;f=d.fallback;e=b.mode;d=Gd({mode:"visible",children:d.children},e,0,null);f=ub(f,e,g,null);f.flags|=2;d.return=
b;f.return=b;d.sibling=f;b.child=d;0!==(b.mode&1)&&Vb(b,a.child,null,g);b.child.memoizedState=Bf(g);b.memoizedState=Cf;return f}if(0===(b.mode&1))return Hd(a,b,g,null);if("$!"===e.data){d=e.nextSibling&&e.nextSibling.dataset;if(d)var h=d.dgst;d=h;f=Error(n(419));d=vf(f,d,void 0);return Hd(a,b,g,d)}h=0!==(g&a.childLanes);if(ha||h){d=O;if(null!==d){switch(g&-g){case 4:e=2;break;case 16:e=8;break;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:e=
32;break;case 536870912:e=268435456;break;default:e=0}e=0!==(e&(d.suspendedLanes|g))?0:e;0!==e&&e!==f.retryLane&&(f.retryLane=e,Oa(a,e),ya(d,a,e,-1))}Ef();d=vf(Error(n(421)));return Hd(a,b,g,d)}if("$?"===e.data)return b.flags|=128,b.child=a.child,b=wk.bind(null,a),e._reactRetry=b,null;a=f.treeContext;fa=Ka(e.nextSibling);la=b;D=!0;wa=null;null!==a&&(na[oa++]=Ma,na[oa++]=Na,na[oa++]=sb,Ma=a.id,Na=a.overflow,sb=b);b=Df(b,d.children);b.flags|=4096;return b}function wi(a,b,c){a.lanes|=b;var d=a.alternate;
null!==d&&(d.lanes|=b);bf(a.return,b,c)}function Ff(a,b,c,d,e){var f=a.memoizedState;null===f?a.memoizedState={isBackwards:b,rendering:null,renderingStartTime:0,last:d,tail:c,tailMode:e}:(f.isBackwards=b,f.rendering=null,f.renderingStartTime=0,f.last=d,f.tail=c,f.tailMode=e)}function xi(a,b,c){var d=b.pendingProps,e=d.revealOrder,f=d.tail;aa(a,b,d.children,c);d=G.current;if(0!==(d&2))d=d&1|2,b.flags|=128;else{if(null!==a&&0!==(a.flags&128))a:for(a=b.child;null!==a;){if(13===a.tag)null!==a.memoizedState&&
wi(a,c,b);else if(19===a.tag)wi(a,c,b);else if(null!==a.child){a.child.return=a;a=a.child;continue}if(a===b)break a;for(;null===a.sibling;){if(null===a.return||a.return===b)break a;a=a.return}a.sibling.return=a.return;a=a.sibling}d&=1}y(G,d);if(0===(b.mode&1))b.memoizedState=null;else switch(e){case "forwards":c=b.child;for(e=null;null!==c;)a=c.alternate,null!==a&&null===yd(a)&&(e=c),c=c.sibling;c=e;null===c?(e=b.child,b.child=null):(e=c.sibling,c.sibling=null);Ff(b,!1,e,c,f);break;case "backwards":c=
null;e=b.child;for(b.child=null;null!==e;){a=e.alternate;if(null!==a&&null===yd(a)){b.child=e;break}a=e.sibling;e.sibling=c;c=e;e=a}Ff(b,!0,c,null,f);break;case "together":Ff(b,!1,null,null,void 0);break;default:b.memoizedState=null}return b.child}function Fd(a,b){0===(b.mode&1)&&null!==a&&(a.alternate=null,b.alternate=null,b.flags|=2)}function Qa(a,b,c){null!==a&&(b.dependencies=a.dependencies);ra|=b.lanes;if(0===(c&b.childLanes))return null;if(null!==a&&b.child!==a.child)throw Error(n(153));if(null!==
b.child){a=b.child;c=gb(a,a.pendingProps);b.child=c;for(c.return=b;null!==a.sibling;)a=a.sibling,c=c.sibling=gb(a,a.pendingProps),c.return=b;c.sibling=null}return b.child}function xk(a,b,c){switch(b.tag){case 3:ti(b);Qb();break;case 5:Mh(b);break;case 1:ea(b.type)&&ld(b);break;case 4:jf(b,b.stateNode.containerInfo);break;case 10:var d=b.type._context,e=b.memoizedProps.value;y(rd,d._currentValue);d._currentValue=e;break;case 13:d=b.memoizedState;if(null!==d){if(null!==d.dehydrated)return y(G,G.current&
1),b.flags|=128,null;if(0!==(c&b.child.childLanes))return vi(a,b,c);y(G,G.current&1);a=Qa(a,b,c);return null!==a?a.sibling:null}y(G,G.current&1);break;case 19:d=0!==(c&b.childLanes);if(0!==(a.flags&128)){if(d)return xi(a,b,c);b.flags|=128}e=b.memoizedState;null!==e&&(e.rendering=null,e.tail=null,e.lastEffect=null);y(G,G.current);if(d)break;else return null;case 22:case 23:return b.lanes=0,qi(a,b,c)}return Qa(a,b,c)}function Dc(a,b){if(!D)switch(a.tailMode){case "hidden":b=a.tail;for(var c=null;null!==
b;)null!==b.alternate&&(c=b),b=b.sibling;null===c?a.tail=null:c.sibling=null;break;case "collapsed":c=a.tail;for(var d=null;null!==c;)null!==c.alternate&&(d=c),c=c.sibling;null===d?b||null===a.tail?a.tail=null:a.tail.sibling=null:d.sibling=null}}function W(a){var b=null!==a.alternate&&a.alternate.child===a.child,c=0,d=0;if(b)for(var e=a.child;null!==e;)c|=e.lanes|e.childLanes,d|=e.subtreeFlags&14680064,d|=e.flags&14680064,e.return=a,e=e.sibling;else for(e=a.child;null!==e;)c|=e.lanes|e.childLanes,
d|=e.subtreeFlags,d|=e.flags,e.return=a,e=e.sibling;a.subtreeFlags|=d;a.childLanes=c;return b}function yk(a,b,c){var d=b.pendingProps;Ve(b);switch(b.tag){case 2:case 16:case 15:case 0:case 11:case 7:case 8:case 12:case 9:case 14:return W(b),null;case 1:return ea(b.type)&&(w(S),w(J)),W(b),null;case 3:d=b.stateNode;Tb();w(S);w(J);lf();d.pendingContext&&(d.context=d.pendingContext,d.pendingContext=null);if(null===a||null===a.child)pd(b)?b.flags|=4:null===a||a.memoizedState.isDehydrated&&0===(b.flags&
256)||(b.flags|=1024,null!==wa&&(Gf(wa),wa=null));yi(a,b);W(b);return null;case 5:kf(b);var e=vb(xc.current);c=b.type;if(null!==a&&null!=b.stateNode)zk(a,b,c,d,e),a.ref!==b.ref&&(b.flags|=512,b.flags|=2097152);else{if(!d){if(null===b.stateNode)throw Error(n(166));W(b);return null}a=vb(Ea.current);if(pd(b)){d=b.stateNode;c=b.type;var f=b.memoizedProps;d[Da]=b;d[uc]=f;a=0!==(b.mode&1);switch(c){case "dialog":B("cancel",d);B("close",d);break;case "iframe":case "object":case "embed":B("load",d);break;
case "video":case "audio":for(e=0;e<Ec.length;e++)B(Ec[e],d);break;case "source":B("error",d);break;case "img":case "image":case "link":B("error",d);B("load",d);break;case "details":B("toggle",d);break;case "input":kg(d,f);B("invalid",d);break;case "select":d._wrapperState={wasMultiple:!!f.multiple};B("invalid",d);break;case "textarea":ng(d,f),B("invalid",d)}pe(c,f);e=null;for(var g in f)if(f.hasOwnProperty(g)){var h=f[g];"children"===g?"string"===typeof h?d.textContent!==h&&(!0!==f.suppressHydrationWarning&&
jd(d.textContent,h,a),e=["children",h]):"number"===typeof h&&d.textContent!==""+h&&(!0!==f.suppressHydrationWarning&&jd(d.textContent,h,a),e=["children",""+h]):$b.hasOwnProperty(g)&&null!=h&&"onScroll"===g&&B("scroll",d)}switch(c){case "input":Pc(d);mg(d,f,!0);break;case "textarea":Pc(d);pg(d);break;case "select":case "option":break;default:"function"===typeof f.onClick&&(d.onclick=kd)}d=e;b.updateQueue=d;null!==d&&(b.flags|=4)}else{g=9===e.nodeType?e:e.ownerDocument;"http://www.w3.org/1999/xhtml"===
a&&(a=qg(c));"http://www.w3.org/1999/xhtml"===a?"script"===c?(a=g.createElement("div"),a.innerHTML="<script>\x3c/script>",a=a.removeChild(a.firstChild)):"string"===typeof d.is?a=g.createElement(c,{is:d.is}):(a=g.createElement(c),"select"===c&&(g=a,d.multiple?g.multiple=!0:d.size&&(g.size=d.size))):a=g.createElementNS(a,c);a[Da]=b;a[uc]=d;Ak(a,b,!1,!1);b.stateNode=a;a:{g=qe(c,d);switch(c){case "dialog":B("cancel",a);B("close",a);e=d;break;case "iframe":case "object":case "embed":B("load",a);e=d;break;
case "video":case "audio":for(e=0;e<Ec.length;e++)B(Ec[e],a);e=d;break;case "source":B("error",a);e=d;break;case "img":case "image":case "link":B("error",a);B("load",a);e=d;break;case "details":B("toggle",a);e=d;break;case "input":kg(a,d);e=ke(a,d);B("invalid",a);break;case "option":e=d;break;case "select":a._wrapperState={wasMultiple:!!d.multiple};e=E({},d,{value:void 0});B("invalid",a);break;case "textarea":ng(a,d);e=ne(a,d);B("invalid",a);break;default:e=d}pe(c,e);h=e;for(f in h)if(h.hasOwnProperty(f)){var k=
h[f];"style"===f?sg(a,k):"dangerouslySetInnerHTML"===f?(k=k?k.__html:void 0,null!=k&&zi(a,k)):"children"===f?"string"===typeof k?("textarea"!==c||""!==k)&&Fc(a,k):"number"===typeof k&&Fc(a,""+k):"suppressContentEditableWarning"!==f&&"suppressHydrationWarning"!==f&&"autoFocus"!==f&&($b.hasOwnProperty(f)?null!=k&&"onScroll"===f&&B("scroll",a):null!=k&&$d(a,f,k,g))}switch(c){case "input":Pc(a);mg(a,d,!1);break;case "textarea":Pc(a);pg(a);break;case "option":null!=d.value&&a.setAttribute("value",""+Ua(d.value));
break;case "select":a.multiple=!!d.multiple;f=d.value;null!=f?Db(a,!!d.multiple,f,!1):null!=d.defaultValue&&Db(a,!!d.multiple,d.defaultValue,!0);break;default:"function"===typeof e.onClick&&(a.onclick=kd)}switch(c){case "button":case "input":case "select":case "textarea":d=!!d.autoFocus;break a;case "img":d=!0;break a;default:d=!1}}d&&(b.flags|=4)}null!==b.ref&&(b.flags|=512,b.flags|=2097152)}W(b);return null;case 6:if(a&&null!=b.stateNode)Bk(a,b,a.memoizedProps,d);else{if("string"!==typeof d&&null===
b.stateNode)throw Error(n(166));c=vb(xc.current);vb(Ea.current);if(pd(b)){d=b.stateNode;c=b.memoizedProps;d[Da]=b;if(f=d.nodeValue!==c)if(a=la,null!==a)switch(a.tag){case 3:jd(d.nodeValue,c,0!==(a.mode&1));break;case 5:!0!==a.memoizedProps.suppressHydrationWarning&&jd(d.nodeValue,c,0!==(a.mode&1))}f&&(b.flags|=4)}else d=(9===c.nodeType?c:c.ownerDocument).createTextNode(d),d[Da]=b,b.stateNode=d}W(b);return null;case 13:w(G);d=b.memoizedState;if(null===a||null!==a.memoizedState&&null!==a.memoizedState.dehydrated){if(D&&
null!==fa&&0!==(b.mode&1)&&0===(b.flags&128)){for(f=fa;f;)f=Ka(f.nextSibling);Qb();b.flags|=98560;f=!1}else if(f=pd(b),null!==d&&null!==d.dehydrated){if(null===a){if(!f)throw Error(n(318));f=b.memoizedState;f=null!==f?f.dehydrated:null;if(!f)throw Error(n(317));f[Da]=b}else Qb(),0===(b.flags&128)&&(b.memoizedState=null),b.flags|=4;W(b);f=!1}else null!==wa&&(Gf(wa),wa=null),f=!0;if(!f)return b.flags&65536?b:null}if(0!==(b.flags&128))return b.lanes=c,b;d=null!==d;d!==(null!==a&&null!==a.memoizedState)&&
d&&(b.child.flags|=8192,0!==(b.mode&1)&&(null===a||0!==(G.current&1)?0===L&&(L=3):Ef()));null!==b.updateQueue&&(b.flags|=4);W(b);return null;case 4:return Tb(),yi(a,b),null===a&&sc(b.stateNode.containerInfo),W(b),null;case 10:return af(b.type._context),W(b),null;case 17:return ea(b.type)&&(w(S),w(J)),W(b),null;case 19:w(G);f=b.memoizedState;if(null===f)return W(b),null;d=0!==(b.flags&128);g=f.rendering;if(null===g)if(d)Dc(f,!1);else{if(0!==L||null!==a&&0!==(a.flags&128))for(a=b.child;null!==a;){g=
yd(a);if(null!==g){b.flags|=128;Dc(f,!1);d=g.updateQueue;null!==d&&(b.updateQueue=d,b.flags|=4);b.subtreeFlags=0;d=c;for(c=b.child;null!==c;)f=c,a=d,f.flags&=14680066,g=f.alternate,null===g?(f.childLanes=0,f.lanes=a,f.child=null,f.subtreeFlags=0,f.memoizedProps=null,f.memoizedState=null,f.updateQueue=null,f.dependencies=null,f.stateNode=null):(f.childLanes=g.childLanes,f.lanes=g.lanes,f.child=g.child,f.subtreeFlags=0,f.deletions=null,f.memoizedProps=g.memoizedProps,f.memoizedState=g.memoizedState,
f.updateQueue=g.updateQueue,f.type=g.type,a=g.dependencies,f.dependencies=null===a?null:{lanes:a.lanes,firstContext:a.firstContext}),c=c.sibling;y(G,G.current&1|2);return b.child}a=a.sibling}null!==f.tail&&P()>Hf&&(b.flags|=128,d=!0,Dc(f,!1),b.lanes=4194304)}else{if(!d)if(a=yd(g),null!==a){if(b.flags|=128,d=!0,c=a.updateQueue,null!==c&&(b.updateQueue=c,b.flags|=4),Dc(f,!0),null===f.tail&&"hidden"===f.tailMode&&!g.alternate&&!D)return W(b),null}else 2*P()-f.renderingStartTime>Hf&&1073741824!==c&&(b.flags|=
128,d=!0,Dc(f,!1),b.lanes=4194304);f.isBackwards?(g.sibling=b.child,b.child=g):(c=f.last,null!==c?c.sibling=g:b.child=g,f.last=g)}if(null!==f.tail)return b=f.tail,f.rendering=b,f.tail=b.sibling,f.renderingStartTime=P(),b.sibling=null,c=G.current,y(G,d?c&1|2:c&1),b;W(b);return null;case 22:case 23:return ba=Ga.current,w(Ga),d=null!==b.memoizedState,null!==a&&null!==a.memoizedState!==d&&(b.flags|=8192),d&&0!==(b.mode&1)?0!==(ba&1073741824)&&(W(b),b.subtreeFlags&6&&(b.flags|=8192)):W(b),null;case 24:return null;
case 25:return null}throw Error(n(156,b.tag));}function Ck(a,b,c){Ve(b);switch(b.tag){case 1:return ea(b.type)&&(w(S),w(J)),a=b.flags,a&65536?(b.flags=a&-65537|128,b):null;case 3:return Tb(),w(S),w(J),lf(),a=b.flags,0!==(a&65536)&&0===(a&128)?(b.flags=a&-65537|128,b):null;case 5:return kf(b),null;case 13:w(G);a=b.memoizedState;if(null!==a&&null!==a.dehydrated){if(null===b.alternate)throw Error(n(340));Qb()}a=b.flags;return a&65536?(b.flags=a&-65537|128,b):null;case 19:return w(G),null;case 4:return Tb(),
null;case 10:return af(b.type._context),null;case 22:case 23:return ba=Ga.current,w(Ga),null;case 24:return null;default:return null}}function Wb(a,b){var c=a.ref;if(null!==c)if("function"===typeof c)try{c(null)}catch(d){H(a,b,d)}else c.current=null}function If(a,b,c){try{c()}catch(d){H(a,b,d)}}function Dk(a,b){Jf=Zc;a=ch();if(Ie(a)){if("selectionStart"in a)var c={start:a.selectionStart,end:a.selectionEnd};else a:{c=(c=a.ownerDocument)&&c.defaultView||window;var d=c.getSelection&&c.getSelection();
if(d&&0!==d.rangeCount){c=d.anchorNode;var e=d.anchorOffset,f=d.focusNode;d=d.focusOffset;try{c.nodeType,f.nodeType}catch(M){c=null;break a}var g=0,h=-1,k=-1,m=0,t=0,u=a,r=null;b:for(;;){for(var p;;){u!==c||0!==e&&3!==u.nodeType||(h=g+e);u!==f||0!==d&&3!==u.nodeType||(k=g+d);3===u.nodeType&&(g+=u.nodeValue.length);if(null===(p=u.firstChild))break;r=u;u=p}for(;;){if(u===a)break b;r===c&&++m===e&&(h=g);r===f&&++t===d&&(k=g);if(null!==(p=u.nextSibling))break;u=r;r=u.parentNode}u=p}c=-1===h||-1===k?null:
{start:h,end:k}}else c=null}c=c||{start:0,end:0}}else c=null;Kf={focusedElem:a,selectionRange:c};Zc=!1;for(l=b;null!==l;)if(b=l,a=b.child,0!==(b.subtreeFlags&1028)&&null!==a)a.return=b,l=a;else for(;null!==l;){b=l;try{var x=b.alternate;if(0!==(b.flags&1024))switch(b.tag){case 0:case 11:case 15:break;case 1:if(null!==x){var w=x.memoizedProps,z=x.memoizedState,A=b.stateNode,v=A.getSnapshotBeforeUpdate(b.elementType===b.type?w:xa(b.type,w),z);A.__reactInternalSnapshotBeforeUpdate=v}break;case 3:var q=
b.stateNode.containerInfo;1===q.nodeType?q.textContent="":9===q.nodeType&&q.documentElement&&q.removeChild(q.documentElement);break;case 5:case 6:case 4:case 17:break;default:throw Error(n(163));}}catch(M){H(b,b.return,M)}a=b.sibling;if(null!==a){a.return=b.return;l=a;break}l=b.return}x=Ai;Ai=!1;return x}function Gc(a,b,c){var d=b.updateQueue;d=null!==d?d.lastEffect:null;if(null!==d){var e=d=d.next;do{if((e.tag&a)===a){var f=e.destroy;e.destroy=void 0;void 0!==f&&If(b,c,f)}e=e.next}while(e!==d)}}
function Id(a,b){b=b.updateQueue;b=null!==b?b.lastEffect:null;if(null!==b){var c=b=b.next;do{if((c.tag&a)===a){var d=c.create;c.destroy=d()}c=c.next}while(c!==b)}}function Lf(a){var b=a.ref;if(null!==b){var c=a.stateNode;switch(a.tag){case 5:a=c;break;default:a=c}"function"===typeof b?b(a):b.current=a}}function Bi(a){var b=a.alternate;null!==b&&(a.alternate=null,Bi(b));a.child=null;a.deletions=null;a.sibling=null;5===a.tag&&(b=a.stateNode,null!==b&&(delete b[Da],delete b[uc],delete b[Me],delete b[Ek],
delete b[Fk]));a.stateNode=null;a.return=null;a.dependencies=null;a.memoizedProps=null;a.memoizedState=null;a.pendingProps=null;a.stateNode=null;a.updateQueue=null}function Ci(a){return 5===a.tag||3===a.tag||4===a.tag}function Di(a){a:for(;;){for(;null===a.sibling;){if(null===a.return||Ci(a.return))return null;a=a.return}a.sibling.return=a.return;for(a=a.sibling;5!==a.tag&&6!==a.tag&&18!==a.tag;){if(a.flags&2)continue a;if(null===a.child||4===a.tag)continue a;else a.child.return=a,a=a.child}if(!(a.flags&
2))return a.stateNode}}function Mf(a,b,c){var d=a.tag;if(5===d||6===d)a=a.stateNode,b?8===c.nodeType?c.parentNode.insertBefore(a,b):c.insertBefore(a,b):(8===c.nodeType?(b=c.parentNode,b.insertBefore(a,c)):(b=c,b.appendChild(a)),c=c._reactRootContainer,null!==c&&void 0!==c||null!==b.onclick||(b.onclick=kd));else if(4!==d&&(a=a.child,null!==a))for(Mf(a,b,c),a=a.sibling;null!==a;)Mf(a,b,c),a=a.sibling}function Nf(a,b,c){var d=a.tag;if(5===d||6===d)a=a.stateNode,b?c.insertBefore(a,b):c.appendChild(a);
else if(4!==d&&(a=a.child,null!==a))for(Nf(a,b,c),a=a.sibling;null!==a;)Nf(a,b,c),a=a.sibling}function jb(a,b,c){for(c=c.child;null!==c;)Ei(a,b,c),c=c.sibling}function Ei(a,b,c){if(Ca&&"function"===typeof Ca.onCommitFiberUnmount)try{Ca.onCommitFiberUnmount(Uc,c)}catch(h){}switch(c.tag){case 5:X||Wb(c,b);case 6:var d=T,e=za;T=null;jb(a,b,c);T=d;za=e;null!==T&&(za?(a=T,c=c.stateNode,8===a.nodeType?a.parentNode.removeChild(c):a.removeChild(c)):T.removeChild(c.stateNode));break;case 18:null!==T&&(za?
(a=T,c=c.stateNode,8===a.nodeType?Re(a.parentNode,c):1===a.nodeType&&Re(a,c),nc(a)):Re(T,c.stateNode));break;case 4:d=T;e=za;T=c.stateNode.containerInfo;za=!0;jb(a,b,c);T=d;za=e;break;case 0:case 11:case 14:case 15:if(!X&&(d=c.updateQueue,null!==d&&(d=d.lastEffect,null!==d))){e=d=d.next;do{var f=e,g=f.destroy;f=f.tag;void 0!==g&&(0!==(f&2)?If(c,b,g):0!==(f&4)&&If(c,b,g));e=e.next}while(e!==d)}jb(a,b,c);break;case 1:if(!X&&(Wb(c,b),d=c.stateNode,"function"===typeof d.componentWillUnmount))try{d.props=
c.memoizedProps,d.state=c.memoizedState,d.componentWillUnmount()}catch(h){H(c,b,h)}jb(a,b,c);break;case 21:jb(a,b,c);break;case 22:c.mode&1?(X=(d=X)||null!==c.memoizedState,jb(a,b,c),X=d):jb(a,b,c);break;default:jb(a,b,c)}}function Fi(a){var b=a.updateQueue;if(null!==b){a.updateQueue=null;var c=a.stateNode;null===c&&(c=a.stateNode=new Gk);b.forEach(function(b){var d=Hk.bind(null,a,b);c.has(b)||(c.add(b),b.then(d,d))})}}function Aa(a,b,c){c=b.deletions;if(null!==c)for(var d=0;d<c.length;d++){var e=
c[d];try{var f=a,g=b,h=g;a:for(;null!==h;){switch(h.tag){case 5:T=h.stateNode;za=!1;break a;case 3:T=h.stateNode.containerInfo;za=!0;break a;case 4:T=h.stateNode.containerInfo;za=!0;break a}h=h.return}if(null===T)throw Error(n(160));Ei(f,g,e);T=null;za=!1;var k=e.alternate;null!==k&&(k.return=null);e.return=null}catch(m){H(e,b,m)}}if(b.subtreeFlags&12854)for(b=b.child;null!==b;)Gi(b,a),b=b.sibling}function Gi(a,b,c){var d=a.alternate;c=a.flags;switch(a.tag){case 0:case 11:case 14:case 15:Aa(b,a);
Ha(a);if(c&4){try{Gc(3,a,a.return),Id(3,a)}catch(F){H(a,a.return,F)}try{Gc(5,a,a.return)}catch(F){H(a,a.return,F)}}break;case 1:Aa(b,a);Ha(a);c&512&&null!==d&&Wb(d,d.return);break;case 5:Aa(b,a);Ha(a);c&512&&null!==d&&Wb(d,d.return);if(a.flags&32){var e=a.stateNode;try{Fc(e,"")}catch(F){H(a,a.return,F)}}if(c&4&&(e=a.stateNode,null!=e)){var f=a.memoizedProps,g=null!==d?d.memoizedProps:f,h=a.type,k=a.updateQueue;a.updateQueue=null;if(null!==k)try{"input"===h&&"radio"===f.type&&null!=f.name&&lg(e,f);
qe(h,g);var m=qe(h,f);for(g=0;g<k.length;g+=2){var t=k[g],u=k[g+1];"style"===t?sg(e,u):"dangerouslySetInnerHTML"===t?zi(e,u):"children"===t?Fc(e,u):$d(e,t,u,m)}switch(h){case "input":le(e,f);break;case "textarea":og(e,f);break;case "select":var r=e._wrapperState.wasMultiple;e._wrapperState.wasMultiple=!!f.multiple;var p=f.value;null!=p?Db(e,!!f.multiple,p,!1):r!==!!f.multiple&&(null!=f.defaultValue?Db(e,!!f.multiple,f.defaultValue,!0):Db(e,!!f.multiple,f.multiple?[]:"",!1))}e[uc]=f}catch(F){H(a,a.return,
F)}}break;case 6:Aa(b,a);Ha(a);if(c&4){if(null===a.stateNode)throw Error(n(162));e=a.stateNode;f=a.memoizedProps;try{e.nodeValue=f}catch(F){H(a,a.return,F)}}break;case 3:Aa(b,a);Ha(a);if(c&4&&null!==d&&d.memoizedState.isDehydrated)try{nc(b.containerInfo)}catch(F){H(a,a.return,F)}break;case 4:Aa(b,a);Ha(a);break;case 13:Aa(b,a);Ha(a);e=a.child;e.flags&8192&&(f=null!==e.memoizedState,e.stateNode.isHidden=f,!f||null!==e.alternate&&null!==e.alternate.memoizedState||(Of=P()));c&4&&Fi(a);break;case 22:t=
null!==d&&null!==d.memoizedState;a.mode&1?(X=(m=X)||t,Aa(b,a),X=m):Aa(b,a);Ha(a);if(c&8192){m=null!==a.memoizedState;if((a.stateNode.isHidden=m)&&!t&&0!==(a.mode&1))for(l=a,t=a.child;null!==t;){for(u=l=t;null!==l;){r=l;p=r.child;switch(r.tag){case 0:case 11:case 14:case 15:Gc(4,r,r.return);break;case 1:Wb(r,r.return);var x=r.stateNode;if("function"===typeof x.componentWillUnmount){c=r;b=r.return;try{d=c,x.props=d.memoizedProps,x.state=d.memoizedState,x.componentWillUnmount()}catch(F){H(c,b,F)}}break;
case 5:Wb(r,r.return);break;case 22:if(null!==r.memoizedState){Hi(u);continue}}null!==p?(p.return=r,l=p):Hi(u)}t=t.sibling}a:for(t=null,u=a;;){if(5===u.tag){if(null===t){t=u;try{e=u.stateNode,m?(f=e.style,"function"===typeof f.setProperty?f.setProperty("display","none","important"):f.display="none"):(h=u.stateNode,k=u.memoizedProps.style,g=void 0!==k&&null!==k&&k.hasOwnProperty("display")?k.display:null,h.style.display=rg("display",g))}catch(F){H(a,a.return,F)}}}else if(6===u.tag){if(null===t)try{u.stateNode.nodeValue=
m?"":u.memoizedProps}catch(F){H(a,a.return,F)}}else if((22!==u.tag&&23!==u.tag||null===u.memoizedState||u===a)&&null!==u.child){u.child.return=u;u=u.child;continue}if(u===a)break a;for(;null===u.sibling;){if(null===u.return||u.return===a)break a;t===u&&(t=null);u=u.return}t===u&&(t=null);u.sibling.return=u.return;u=u.sibling}}break;case 19:Aa(b,a);Ha(a);c&4&&Fi(a);break;case 21:break;default:Aa(b,a),Ha(a)}}function Ha(a){var b=a.flags;if(b&2){try{a:{for(var c=a.return;null!==c;){if(Ci(c)){var d=c;
break a}c=c.return}throw Error(n(160));}switch(d.tag){case 5:var e=d.stateNode;d.flags&32&&(Fc(e,""),d.flags&=-33);var f=Di(a);Nf(a,f,e);break;case 3:case 4:var g=d.stateNode.containerInfo,h=Di(a);Mf(a,h,g);break;default:throw Error(n(161));}}catch(k){H(a,a.return,k)}a.flags&=-3}b&4096&&(a.flags&=-4097)}function Ik(a,b,c){l=a;Ii(a,b,c)}function Ii(a,b,c){for(var d=0!==(a.mode&1);null!==l;){var e=l,f=e.child;if(22===e.tag&&d){var g=null!==e.memoizedState||Jd;if(!g){var h=e.alternate,k=null!==h&&null!==
h.memoizedState||X;h=Jd;var m=X;Jd=g;if((X=k)&&!m)for(l=e;null!==l;)g=l,k=g.child,22===g.tag&&null!==g.memoizedState?Ji(e):null!==k?(k.return=g,l=k):Ji(e);for(;null!==f;)l=f,Ii(f,b,c),f=f.sibling;l=e;Jd=h;X=m}Ki(a,b,c)}else 0!==(e.subtreeFlags&8772)&&null!==f?(f.return=e,l=f):Ki(a,b,c)}}function Ki(a,b,c){for(;null!==l;){b=l;if(0!==(b.flags&8772)){c=b.alternate;try{if(0!==(b.flags&8772))switch(b.tag){case 0:case 11:case 15:X||Id(5,b);break;case 1:var d=b.stateNode;if(b.flags&4&&!X)if(null===c)d.componentDidMount();
else{var e=b.elementType===b.type?c.memoizedProps:xa(b.type,c.memoizedProps);d.componentDidUpdate(e,c.memoizedState,d.__reactInternalSnapshotBeforeUpdate)}var f=b.updateQueue;null!==f&&Fh(b,f,d);break;case 3:var g=b.updateQueue;if(null!==g){c=null;if(null!==b.child)switch(b.child.tag){case 5:c=b.child.stateNode;break;case 1:c=b.child.stateNode}Fh(b,g,c)}break;case 5:var h=b.stateNode;if(null===c&&b.flags&4){c=h;var k=b.memoizedProps;switch(b.type){case "button":case "input":case "select":case "textarea":k.autoFocus&&
c.focus();break;case "img":k.src&&(c.src=k.src)}}break;case 6:break;case 4:break;case 12:break;case 13:if(null===b.memoizedState){var m=b.alternate;if(null!==m){var t=m.memoizedState;if(null!==t){var p=t.dehydrated;null!==p&&nc(p)}}}break;case 19:case 17:case 21:case 22:case 23:case 25:break;default:throw Error(n(163));}X||b.flags&512&&Lf(b)}catch(r){H(b,b.return,r)}}if(b===a){l=null;break}c=b.sibling;if(null!==c){c.return=b.return;l=c;break}l=b.return}}function Hi(a){for(;null!==l;){var b=l;if(b===
a){l=null;break}var c=b.sibling;if(null!==c){c.return=b.return;l=c;break}l=b.return}}function Ji(a){for(;null!==l;){var b=l;try{switch(b.tag){case 0:case 11:case 15:var c=b.return;try{Id(4,b)}catch(k){H(b,c,k)}break;case 1:var d=b.stateNode;if("function"===typeof d.componentDidMount){var e=b.return;try{d.componentDidMount()}catch(k){H(b,e,k)}}var f=b.return;try{Lf(b)}catch(k){H(b,f,k)}break;case 5:var g=b.return;try{Lf(b)}catch(k){H(b,g,k)}}}catch(k){H(b,b.return,k)}if(b===a){l=null;break}var h=b.sibling;
if(null!==h){h.return=b.return;l=h;break}l=b.return}}function Hc(){Hf=P()+500}function Z(){return 0!==(p&6)?P():-1!==Kd?Kd:Kd=P()}function hb(a){if(0===(a.mode&1))return 1;if(0!==(p&2)&&0!==U)return U&-U;if(null!==Jk.transition)return 0===Ld&&(Ld=Dg()),Ld;a=z;if(0!==a)return a;a=window.event;a=void 0===a?16:Lg(a.type);return a}function ya(a,b,c,d){if(50<Ic)throw Ic=0,Pf=null,Error(n(185));ic(a,c,d);if(0===(p&2)||a!==O)a===O&&(0===(p&2)&&(Md|=c),4===L&&kb(a,U)),ia(a,d),1===c&&0===p&&0===(b.mode&1)&&
(Hc(),md&&db())}function ia(a,b){var c=a.callbackNode;uj(a,b);var d=Vc(a,a===O?U:0);if(0===d)null!==c&&Li(c),a.callbackNode=null,a.callbackPriority=0;else if(b=d&-d,a.callbackPriority!==b){null!=c&&Li(c);if(1===b)0===a.tag?kk(Mi.bind(null,a)):wh(Mi.bind(null,a)),Kk(function(){0===(p&6)&&db()}),c=null;else{switch(Eg(d)){case 1:c=De;break;case 4:c=Mg;break;case 16:c=ad;break;case 536870912:c=Ng;break;default:c=ad}c=Ni(c,Oi.bind(null,a))}a.callbackPriority=b;a.callbackNode=c}}function Oi(a,b){Kd=-1;
Ld=0;if(0!==(p&6))throw Error(n(327));var c=a.callbackNode;if(Xb()&&a.callbackNode!==c)return null;var d=Vc(a,a===O?U:0);if(0===d)return null;if(0!==(d&30)||0!==(d&a.expiredLanes)||b)b=Nd(a,d);else{b=d;var e=p;p|=2;var f=Pi();if(O!==a||U!==b)Ra=null,Hc(),xb(a,b);do try{Lk();break}catch(h){Qi(a,h)}while(1);Ze();Od.current=f;p=e;null!==I?b=0:(O=null,U=0,b=L)}if(0!==b){2===b&&(e=ve(a),0!==e&&(d=e,b=Qf(a,e)));if(1===b)throw c=Jc,xb(a,0),kb(a,d),ia(a,P()),c;if(6===b)kb(a,d);else{e=a.current.alternate;
if(0===(d&30)&&!Mk(e)&&(b=Nd(a,d),2===b&&(f=ve(a),0!==f&&(d=f,b=Qf(a,f))),1===b))throw c=Jc,xb(a,0),kb(a,d),ia(a,P()),c;a.finishedWork=e;a.finishedLanes=d;switch(b){case 0:case 1:throw Error(n(345));case 2:yb(a,ja,Ra);break;case 3:kb(a,d);if((d&130023424)===d&&(b=Of+500-P(),10<b)){if(0!==Vc(a,0))break;e=a.suspendedLanes;if((e&d)!==d){Z();a.pingedLanes|=a.suspendedLanes&e;break}a.timeoutHandle=Rf(yb.bind(null,a,ja,Ra),b);break}yb(a,ja,Ra);break;case 4:kb(a,d);if((d&4194240)===d)break;b=a.eventTimes;
for(e=-1;0<d;){var g=31-ta(d);f=1<<g;g=b[g];g>e&&(e=g);d&=~f}d=e;d=P()-d;d=(120>d?120:480>d?480:1080>d?1080:1920>d?1920:3E3>d?3E3:4320>d?4320:1960*Nk(d/1960))-d;if(10<d){a.timeoutHandle=Rf(yb.bind(null,a,ja,Ra),d);break}yb(a,ja,Ra);break;case 5:yb(a,ja,Ra);break;default:throw Error(n(329));}}}ia(a,P());return a.callbackNode===c?Oi.bind(null,a):null}function Qf(a,b){var c=Kc;a.current.memoizedState.isDehydrated&&(xb(a,b).flags|=256);a=Nd(a,b);2!==a&&(b=ja,ja=c,null!==b&&Gf(b));return a}function Gf(a){null===
ja?ja=a:ja.push.apply(ja,a)}function Mk(a){for(var b=a;;){if(b.flags&16384){var c=b.updateQueue;if(null!==c&&(c=c.stores,null!==c))for(var d=0;d<c.length;d++){var e=c[d],f=e.getSnapshot;e=e.value;try{if(!ua(f(),e))return!1}catch(g){return!1}}}c=b.child;if(b.subtreeFlags&16384&&null!==c)c.return=b,b=c;else{if(b===a)break;for(;null===b.sibling;){if(null===b.return||b.return===a)return!0;b=b.return}b.sibling.return=b.return;b=b.sibling}}return!0}function kb(a,b){b&=~Sf;b&=~Md;a.suspendedLanes|=b;a.pingedLanes&=
~b;for(a=a.expirationTimes;0<b;){var c=31-ta(b),d=1<<c;a[c]=-1;b&=~d}}function Mi(a){if(0!==(p&6))throw Error(n(327));Xb();var b=Vc(a,0);if(0===(b&1))return ia(a,P()),null;var c=Nd(a,b);if(0!==a.tag&&2===c){var d=ve(a);0!==d&&(b=d,c=Qf(a,d))}if(1===c)throw c=Jc,xb(a,0),kb(a,b),ia(a,P()),c;if(6===c)throw Error(n(345));a.finishedWork=a.current.alternate;a.finishedLanes=b;yb(a,ja,Ra);ia(a,P());return null}function Tf(a,b){var c=p;p|=1;try{return a(b)}finally{p=c,0===p&&(Hc(),md&&db())}}function zb(a){null!==
lb&&0===lb.tag&&0===(p&6)&&Xb();var b=p;p|=1;var c=ca.transition,d=z;try{if(ca.transition=null,z=1,a)return a()}finally{z=d,ca.transition=c,p=b,0===(p&6)&&db()}}function xb(a,b){a.finishedWork=null;a.finishedLanes=0;var c=a.timeoutHandle;-1!==c&&(a.timeoutHandle=-1,Ok(c));if(null!==I)for(c=I.return;null!==c;){var d=c;Ve(d);switch(d.tag){case 1:d=d.type.childContextTypes;null!==d&&void 0!==d&&(w(S),w(J));break;case 3:Tb();w(S);w(J);lf();break;case 5:kf(d);break;case 4:Tb();break;case 13:w(G);break;
case 19:w(G);break;case 10:af(d.type._context);break;case 22:case 23:ba=Ga.current,w(Ga)}c=c.return}O=a;I=a=gb(a.current,null);U=ba=b;L=0;Jc=null;Sf=Md=ra=0;ja=Kc=null;if(null!==tb){for(b=0;b<tb.length;b++)if(c=tb[b],d=c.interleaved,null!==d){c.interleaved=null;var e=d.next,f=c.pending;if(null!==f){var g=f.next;f.next=e;d.next=g}c.pending=d}tb=null}return a}function Qi(a,b){do{var c=I;try{Ze();zd.current=Ad;if(Bd){for(var d=C.memoizedState;null!==d;){var e=d.queue;null!==e&&(e.pending=null);d=d.next}Bd=
!1}wb=0;N=K=C=null;zc=!1;Ac=0;Uf.current=null;if(null===c||null===c.return){L=1;Jc=b;I=null;break}a:{var f=a,g=c.return,h=c,k=b;b=U;h.flags|=32768;if(null!==k&&"object"===typeof k&&"function"===typeof k.then){var m=k,l=h,p=l.tag;if(0===(l.mode&1)&&(0===p||11===p||15===p)){var r=l.alternate;r?(l.updateQueue=r.updateQueue,l.memoizedState=r.memoizedState,l.lanes=r.lanes):(l.updateQueue=null,l.memoizedState=null)}var w=ki(g);if(null!==w){w.flags&=-257;li(w,g,h,f,b);w.mode&1&&ji(f,m,b);b=w;k=m;var x=b.updateQueue;
if(null===x){var z=new Set;z.add(k);b.updateQueue=z}else x.add(k);break a}else{if(0===(b&1)){ji(f,m,b);Ef();break a}k=Error(n(426))}}else if(D&&h.mode&1){var y=ki(g);if(null!==y){0===(y.flags&65536)&&(y.flags|=256);li(y,g,h,f,b);Ye(Ub(k,h));break a}}f=k=Ub(k,h);4!==L&&(L=2);null===Kc?Kc=[f]:Kc.push(f);f=g;do{switch(f.tag){case 3:f.flags|=65536;b&=-b;f.lanes|=b;var A=hi(f,k,b);Eh(f,A);break a;case 1:h=k;var v=f.type,q=f.stateNode;if(0===(f.flags&128)&&("function"===typeof v.getDerivedStateFromError||
null!==q&&"function"===typeof q.componentDidCatch&&(null===ib||!ib.has(q)))){f.flags|=65536;b&=-b;f.lanes|=b;var B=ii(f,h,b);Eh(f,B);break a}}f=f.return}while(null!==f)}Ri(c)}catch(ma){b=ma;I===c&&null!==c&&(I=c=c.return);continue}break}while(1)}function Pi(){var a=Od.current;Od.current=Ad;return null===a?Ad:a}function Ef(){if(0===L||3===L||2===L)L=4;null===O||0===(ra&268435455)&&0===(Md&268435455)||kb(O,U)}function Nd(a,b){var c=p;p|=2;var d=Pi();if(O!==a||U!==b)Ra=null,xb(a,b);do try{Pk();break}catch(e){Qi(a,
e)}while(1);Ze();p=c;Od.current=d;if(null!==I)throw Error(n(261));O=null;U=0;return L}function Pk(){for(;null!==I;)Si(I)}function Lk(){for(;null!==I&&!Qk();)Si(I)}function Si(a){var b=Rk(a.alternate,a,ba);a.memoizedProps=a.pendingProps;null===b?Ri(a):I=b;Uf.current=null}function Ri(a){var b=a;do{var c=b.alternate;a=b.return;if(0===(b.flags&32768)){if(c=yk(c,b,ba),null!==c){I=c;return}}else{c=Ck(c,b);if(null!==c){c.flags&=32767;I=c;return}if(null!==a)a.flags|=32768,a.subtreeFlags=0,a.deletions=null;
else{L=6;I=null;return}}b=b.sibling;if(null!==b){I=b;return}I=b=a}while(null!==b);0===L&&(L=5)}function yb(a,b,c){var d=z,e=ca.transition;try{ca.transition=null,z=1,Sk(a,b,c,d)}finally{ca.transition=e,z=d}return null}function Sk(a,b,c,d){do Xb();while(null!==lb);if(0!==(p&6))throw Error(n(327));c=a.finishedWork;var e=a.finishedLanes;if(null===c)return null;a.finishedWork=null;a.finishedLanes=0;if(c===a.current)throw Error(n(177));a.callbackNode=null;a.callbackPriority=0;var f=c.lanes|c.childLanes;
vj(a,f);a===O&&(I=O=null,U=0);0===(c.subtreeFlags&2064)&&0===(c.flags&2064)||Pd||(Pd=!0,Ni(ad,function(){Xb();return null}));f=0!==(c.flags&15990);if(0!==(c.subtreeFlags&15990)||f){f=ca.transition;ca.transition=null;var g=z;z=1;var h=p;p|=4;Uf.current=null;Dk(a,c);Gi(c,a);Uj(Kf);Zc=!!Jf;Kf=Jf=null;a.current=c;Ik(c,a,e);Tk();p=h;z=g;ca.transition=f}else a.current=c;Pd&&(Pd=!1,lb=a,Qd=e);f=a.pendingLanes;0===f&&(ib=null);pj(c.stateNode,d);ia(a,P());if(null!==b)for(d=a.onRecoverableError,c=0;c<b.length;c++)e=
b[c],d(e.value,{componentStack:e.stack,digest:e.digest});if(Ed)throw Ed=!1,a=xf,xf=null,a;0!==(Qd&1)&&0!==a.tag&&Xb();f=a.pendingLanes;0!==(f&1)?a===Pf?Ic++:(Ic=0,Pf=a):Ic=0;db();return null}function Xb(){if(null!==lb){var a=Eg(Qd),b=ca.transition,c=z;try{ca.transition=null;z=16>a?16:a;if(null===lb)var d=!1;else{a=lb;lb=null;Qd=0;if(0!==(p&6))throw Error(n(331));var e=p;p|=4;for(l=a.current;null!==l;){var f=l,g=f.child;if(0!==(l.flags&16)){var h=f.deletions;if(null!==h){for(var k=0;k<h.length;k++){var m=
h[k];for(l=m;null!==l;){var t=l;switch(t.tag){case 0:case 11:case 15:Gc(8,t,f)}var u=t.child;if(null!==u)u.return=t,l=u;else for(;null!==l;){t=l;var r=t.sibling,w=t.return;Bi(t);if(t===m){l=null;break}if(null!==r){r.return=w;l=r;break}l=w}}}var x=f.alternate;if(null!==x){var y=x.child;if(null!==y){x.child=null;do{var C=y.sibling;y.sibling=null;y=C}while(null!==y)}}l=f}}if(0!==(f.subtreeFlags&2064)&&null!==g)g.return=f,l=g;else b:for(;null!==l;){f=l;if(0!==(f.flags&2048))switch(f.tag){case 0:case 11:case 15:Gc(9,
f,f.return)}var A=f.sibling;if(null!==A){A.return=f.return;l=A;break b}l=f.return}}var v=a.current;for(l=v;null!==l;){g=l;var q=g.child;if(0!==(g.subtreeFlags&2064)&&null!==q)q.return=g,l=q;else b:for(g=v;null!==l;){h=l;if(0!==(h.flags&2048))try{switch(h.tag){case 0:case 11:case 15:Id(9,h)}}catch(ma){H(h,h.return,ma)}if(h===g){l=null;break b}var B=h.sibling;if(null!==B){B.return=h.return;l=B;break b}l=h.return}}p=e;db();if(Ca&&"function"===typeof Ca.onPostCommitFiberRoot)try{Ca.onPostCommitFiberRoot(Uc,
a)}catch(ma){}d=!0}return d}finally{z=c,ca.transition=b}}return!1}function Ti(a,b,c){b=Ub(c,b);b=hi(a,b,1);a=eb(a,b,1);b=Z();null!==a&&(ic(a,1,b),ia(a,b))}function H(a,b,c){if(3===a.tag)Ti(a,a,c);else for(;null!==b;){if(3===b.tag){Ti(b,a,c);break}else if(1===b.tag){var d=b.stateNode;if("function"===typeof b.type.getDerivedStateFromError||"function"===typeof d.componentDidCatch&&(null===ib||!ib.has(d))){a=Ub(c,a);a=ii(b,a,1);b=eb(b,a,1);a=Z();null!==b&&(ic(b,1,a),ia(b,a));break}}b=b.return}}function tk(a,
b,c){var d=a.pingCache;null!==d&&d.delete(b);b=Z();a.pingedLanes|=a.suspendedLanes&c;O===a&&(U&c)===c&&(4===L||3===L&&(U&130023424)===U&&500>P()-Of?xb(a,0):Sf|=c);ia(a,b)}function Ui(a,b){0===b&&(0===(a.mode&1)?b=1:(b=Rd,Rd<<=1,0===(Rd&130023424)&&(Rd=4194304)));var c=Z();a=Oa(a,b);null!==a&&(ic(a,b,c),ia(a,c))}function wk(a){var b=a.memoizedState,c=0;null!==b&&(c=b.retryLane);Ui(a,c)}function Hk(a,b){var c=0;switch(a.tag){case 13:var d=a.stateNode;var e=a.memoizedState;null!==e&&(c=e.retryLane);
break;case 19:d=a.stateNode;break;default:throw Error(n(314));}null!==d&&d.delete(b);Ui(a,c)}function Ni(a,b){return xh(a,b)}function Uk(a,b,c,d){this.tag=a;this.key=c;this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null;this.index=0;this.ref=null;this.pendingProps=b;this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null;this.mode=d;this.subtreeFlags=this.flags=0;this.deletions=null;this.childLanes=this.lanes=0;this.alternate=null}function yf(a){a=
a.prototype;return!(!a||!a.isReactComponent)}function Vk(a){if("function"===typeof a)return yf(a)?1:0;if(void 0!==a&&null!==a){a=a.$$typeof;if(a===ie)return 11;if(a===je)return 14}return 2}function gb(a,b){var c=a.alternate;null===c?(c=pa(a.tag,b,a.key,a.mode),c.elementType=a.elementType,c.type=a.type,c.stateNode=a.stateNode,c.alternate=a,a.alternate=c):(c.pendingProps=b,c.type=a.type,c.flags=0,c.subtreeFlags=0,c.deletions=null);c.flags=a.flags&14680064;c.childLanes=a.childLanes;c.lanes=a.lanes;c.child=
a.child;c.memoizedProps=a.memoizedProps;c.memoizedState=a.memoizedState;c.updateQueue=a.updateQueue;b=a.dependencies;c.dependencies=null===b?null:{lanes:b.lanes,firstContext:b.firstContext};c.sibling=a.sibling;c.index=a.index;c.ref=a.ref;return c}function wd(a,b,c,d,e,f){var g=2;d=a;if("function"===typeof a)yf(a)&&(g=1);else if("string"===typeof a)g=5;else a:switch(a){case Bb:return ub(c.children,e,f,b);case fe:g=8;e|=8;break;case ee:return a=pa(12,c,b,e|2),a.elementType=ee,a.lanes=f,a;case ge:return a=
pa(13,c,b,e),a.elementType=ge,a.lanes=f,a;case he:return a=pa(19,c,b,e),a.elementType=he,a.lanes=f,a;case Vi:return Gd(c,e,f,b);default:if("object"===typeof a&&null!==a)switch(a.$$typeof){case hg:g=10;break a;case gg:g=9;break a;case ie:g=11;break a;case je:g=14;break a;case Ta:g=16;d=null;break a}throw Error(n(130,null==a?a:typeof a,""));}b=pa(g,c,b,e);b.elementType=a;b.type=d;b.lanes=f;return b}function ub(a,b,c,d){a=pa(7,a,d,b);a.lanes=c;return a}function Gd(a,b,c,d){a=pa(22,a,d,b);a.elementType=
Vi;a.lanes=c;a.stateNode={isHidden:!1};return a}function gf(a,b,c){a=pa(6,a,null,b);a.lanes=c;return a}function hf(a,b,c){b=pa(4,null!==a.children?a.children:[],a.key,b);b.lanes=c;b.stateNode={containerInfo:a.containerInfo,pendingChildren:null,implementation:a.implementation};return b}function Wk(a,b,c,d,e){this.tag=b;this.containerInfo=a;this.finishedWork=this.pingCache=this.current=this.pendingChildren=null;this.timeoutHandle=-1;this.callbackNode=this.pendingContext=this.context=null;this.callbackPriority=
0;this.eventTimes=we(0);this.expirationTimes=we(-1);this.entangledLanes=this.finishedLanes=this.mutableReadLanes=this.expiredLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0;this.entanglements=we(0);this.identifierPrefix=d;this.onRecoverableError=e;this.mutableSourceEagerHydrationData=null}function Vf(a,b,c,d,e,f,g,h,k,m){a=new Wk(a,b,c,h,k);1===b?(b=1,!0===f&&(b|=8)):b=0;f=pa(3,null,null,b);a.current=f;f.stateNode=a;f.memoizedState={element:d,isDehydrated:c,cache:null,transitions:null,
pendingSuspenseBoundaries:null};df(f);return a}function Xk(a,b,c){var d=3<arguments.length&&void 0!==arguments[3]?arguments[3]:null;return{$$typeof:Cb,key:null==d?null:""+d,children:a,containerInfo:b,implementation:c}}function Wi(a){if(!a)return cb;a=a._reactInternals;a:{if(ob(a)!==a||1!==a.tag)throw Error(n(170));var b=a;do{switch(b.tag){case 3:b=b.stateNode.context;break a;case 1:if(ea(b.type)){b=b.stateNode.__reactInternalMemoizedMergedChildContext;break a}}b=b.return}while(null!==b);throw Error(n(171));
}if(1===a.tag){var c=a.type;if(ea(c))return uh(a,c,b)}return b}function Xi(a,b,c,d,e,f,g,h,k,m){a=Vf(c,d,!0,a,e,f,g,h,k);a.context=Wi(null);c=a.current;d=Z();e=hb(c);f=Pa(d,e);f.callback=void 0!==b&&null!==b?b:null;eb(c,f,e);a.current.lanes=e;ic(a,e,d);ia(a,d);return a}function Sd(a,b,c,d){var e=b.current,f=Z(),g=hb(e);c=Wi(c);null===b.context?b.context=c:b.pendingContext=c;b=Pa(f,g);b.payload={element:a};d=void 0===d?null:d;null!==d&&(b.callback=d);a=eb(e,b,g);null!==a&&(ya(a,e,g,f),sd(a,e,g));return g}
function Td(a){a=a.current;if(!a.child)return null;switch(a.child.tag){case 5:return a.child.stateNode;default:return a.child.stateNode}}function Yi(a,b){a=a.memoizedState;if(null!==a&&null!==a.dehydrated){var c=a.retryLane;a.retryLane=0!==c&&c<b?c:b}}function Wf(a,b){Yi(a,b);(a=a.alternate)&&Yi(a,b)}function Yk(a){a=Bg(a);return null===a?null:a.stateNode}function Zk(a){return null}function Xf(a){this._internalRoot=a}function Ud(a){this._internalRoot=a}function Yf(a){return!(!a||1!==a.nodeType&&9!==
a.nodeType&&11!==a.nodeType)}function Vd(a){return!(!a||1!==a.nodeType&&9!==a.nodeType&&11!==a.nodeType&&(8!==a.nodeType||" react-mount-point-unstable "!==a.nodeValue))}function Zi(){}function $k(a,b,c,d,e){if(e){if("function"===typeof d){var f=d;d=function(){var a=Td(g);f.call(a)}}var g=Xi(b,d,a,0,null,!1,!1,"",Zi);a._reactRootContainer=g;a[Ja]=g.current;sc(8===a.nodeType?a.parentNode:a);zb();return g}for(;e=a.lastChild;)a.removeChild(e);if("function"===typeof d){var h=d;d=function(){var a=Td(k);
h.call(a)}}var k=Vf(a,0,!1,null,null,!1,!1,"",Zi);a._reactRootContainer=k;a[Ja]=k.current;sc(8===a.nodeType?a.parentNode:a);zb(function(){Sd(b,k,c,d)});return k}function Wd(a,b,c,d,e){var f=c._reactRootContainer;if(f){var g=f;if("function"===typeof e){var h=e;e=function(){var a=Td(g);h.call(a)}}Sd(b,g,a,e)}else g=$k(c,b,a,e,d);return Td(g)}var cg=new Set,$b={},Ia=!("undefined"===typeof window||"undefined"===typeof window.document||"undefined"===typeof window.document.createElement),Zd=Object.prototype.hasOwnProperty,
dj=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,eg={},dg={},R={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(a){R[a]=
new Y(a,0,!1,a,null,!1,!1)});[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(a){var b=a[0];R[b]=new Y(b,1,!1,a[1],null,!1,!1)});["contentEditable","draggable","spellCheck","value"].forEach(function(a){R[a]=new Y(a,2,!1,a.toLowerCase(),null,!1,!1)});["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(a){R[a]=new Y(a,2,!1,a,null,!1,!1)});"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach(function(a){R[a]=
new Y(a,3,!1,a.toLowerCase(),null,!1,!1)});["checked","multiple","muted","selected"].forEach(function(a){R[a]=new Y(a,3,!0,a,null,!1,!1)});["capture","download"].forEach(function(a){R[a]=new Y(a,4,!1,a,null,!1,!1)});["cols","rows","size","span"].forEach(function(a){R[a]=new Y(a,6,!1,a,null,!1,!1)});["rowSpan","start"].forEach(function(a){R[a]=new Y(a,5,!1,a.toLowerCase(),null,!1,!1)});var Zf=/[\-:]([a-z])/g,$f=function(a){return a[1].toUpperCase()};"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach(function(a){var b=
a.replace(Zf,$f);R[b]=new Y(b,1,!1,a,null,!1,!1)});"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach(function(a){var b=a.replace(Zf,$f);R[b]=new Y(b,1,!1,a,"http://www.w3.org/1999/xlink",!1,!1)});["xml:base","xml:lang","xml:space"].forEach(function(a){var b=a.replace(Zf,$f);R[b]=new Y(b,1,!1,a,"http://www.w3.org/XML/1998/namespace",!1,!1)});["tabIndex","crossOrigin"].forEach(function(a){R[a]=new Y(a,1,!1,a.toLowerCase(),null,!1,!1)});R.xlinkHref=new Y("xlinkHref",
1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1);["src","href","action","formAction"].forEach(function(a){R[a]=new Y(a,1,!1,a.toLowerCase(),null,!0,!0)});var Sa=mb.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED,xd=Symbol.for("react.element"),Cb=Symbol.for("react.portal"),Bb=Symbol.for("react.fragment"),fe=Symbol.for("react.strict_mode"),ee=Symbol.for("react.profiler"),hg=Symbol.for("react.provider"),gg=Symbol.for("react.context"),ie=Symbol.for("react.forward_ref"),ge=Symbol.for("react.suspense"),
he=Symbol.for("react.suspense_list"),je=Symbol.for("react.memo"),Ta=Symbol.for("react.lazy");Symbol.for("react.scope");Symbol.for("react.debug_trace_mode");var Vi=Symbol.for("react.offscreen");Symbol.for("react.legacy_hidden");Symbol.for("react.cache");Symbol.for("react.tracing_marker");var fg=Symbol.iterator,E=Object.assign,ae,ce=!1,cc=Array.isArray,Xd,zi=function(a){return"undefined"!==typeof MSApp&&MSApp.execUnsafeLocalFunction?function(b,c,d,e){MSApp.execUnsafeLocalFunction(function(){return a(b,
c,d,e)})}:a}(function(a,b){if("http://www.w3.org/2000/svg"!==a.namespaceURI||"innerHTML"in a)a.innerHTML=b;else{Xd=Xd||document.createElement("div");Xd.innerHTML="<svg>"+b.valueOf().toString()+"</svg>";for(b=Xd.firstChild;a.firstChild;)a.removeChild(a.firstChild);for(;b.firstChild;)a.appendChild(b.firstChild)}}),Fc=function(a,b){if(b){var c=a.firstChild;if(c&&c===a.lastChild&&3===c.nodeType){c.nodeValue=b;return}}a.textContent=b},dc={animationIterationCount:!0,aspectRatio:!0,borderImageOutset:!0,
borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridArea:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,
strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},al=["Webkit","ms","Moz","O"];Object.keys(dc).forEach(function(a){al.forEach(function(b){b=b+a.charAt(0).toUpperCase()+a.substring(1);dc[b]=dc[a]})});var jj=E({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0}),ze=null,se=null,Eb=null,Fb=null,xg=function(a,b){return a(b)},yg=function(){},te=!1,Oe=!1;if(Ia)try{var Lc={};Object.defineProperty(Lc,
"passive",{get:function(){Oe=!0}});window.addEventListener("test",Lc,Lc);window.removeEventListener("test",Lc,Lc)}catch(a){Oe=!1}var lj=function(a,b,c,d,e,f,g,h,k){var m=Array.prototype.slice.call(arguments,3);try{b.apply(c,m)}catch(t){this.onError(t)}},gc=!1,Sc=null,Tc=!1,ue=null,mj={onError:function(a){gc=!0;Sc=a}},Ba=mb.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.Scheduler,Jg=Ba.unstable_scheduleCallback,Kg=Ba.unstable_NormalPriority,xh=Jg,Li=Ba.unstable_cancelCallback,Qk=Ba.unstable_shouldYield,
Tk=Ba.unstable_requestPaint,P=Ba.unstable_now,Ej=Ba.unstable_getCurrentPriorityLevel,De=Ba.unstable_ImmediatePriority,Mg=Ba.unstable_UserBlockingPriority,ad=Kg,Fj=Ba.unstable_LowPriority,Ng=Ba.unstable_IdlePriority,Uc=null,Ca=null,ta=Math.clz32?Math.clz32:qj,rj=Math.log,sj=Math.LN2,Wc=64,Rd=4194304,z=0,Ae=!1,Yc=[],Va=null,Wa=null,Xa=null,jc=new Map,kc=new Map,Ya=[],Cj="mousedown mouseup touchcancel touchend touchstart auxclick dblclick pointercancel pointerdown pointerup dragend dragstart drop compositionend compositionstart keydown keypress keyup input textInput copy cut paste click change contextmenu reset submit".split(" "),
Gb=Sa.ReactCurrentBatchConfig,Zc=!0,$c=null,Za=null,Ee=null,bd=null,Yb={eventPhase:0,bubbles:0,cancelable:0,timeStamp:function(a){return a.timeStamp||Date.now()},defaultPrevented:0,isTrusted:0},He=ka(Yb),Mc=E({},Yb,{view:0,detail:0}),bk=ka(Mc),ag,bg,Nc,Yd=E({},Mc,{screenX:0,screenY:0,clientX:0,clientY:0,pageX:0,pageY:0,ctrlKey:0,shiftKey:0,altKey:0,metaKey:0,getModifierState:Fe,button:0,buttons:0,relatedTarget:function(a){return void 0===a.relatedTarget?a.fromElement===a.srcElement?a.toElement:a.fromElement:
a.relatedTarget},movementX:function(a){if("movementX"in a)return a.movementX;a!==Nc&&(Nc&&"mousemove"===a.type?(ag=a.screenX-Nc.screenX,bg=a.screenY-Nc.screenY):bg=ag=0,Nc=a);return ag},movementY:function(a){return"movementY"in a?a.movementY:bg}}),ih=ka(Yd),bl=E({},Yd,{dataTransfer:0}),Xj=ka(bl),cl=E({},Mc,{relatedTarget:0}),Pe=ka(cl),dl=E({},Yb,{animationName:0,elapsedTime:0,pseudoElement:0}),Zj=ka(dl),el=E({},Yb,{clipboardData:function(a){return"clipboardData"in a?a.clipboardData:window.clipboardData}}),
dk=ka(el),fl=E({},Yb,{data:0}),qh=ka(fl),gk=qh,gl={Esc:"Escape",Spacebar:" ",Left:"ArrowLeft",Up:"ArrowUp",Right:"ArrowRight",Down:"ArrowDown",Del:"Delete",Win:"OS",Menu:"ContextMenu",Apps:"ContextMenu",Scroll:"ScrollLock",MozPrintableKey:"Unidentified"},hl={8:"Backspace",9:"Tab",12:"Clear",13:"Enter",16:"Shift",17:"Control",18:"Alt",19:"Pause",20:"CapsLock",27:"Escape",32:" ",33:"PageUp",34:"PageDown",35:"End",36:"Home",37:"ArrowLeft",38:"ArrowUp",39:"ArrowRight",40:"ArrowDown",45:"Insert",46:"Delete",
112:"F1",113:"F2",114:"F3",115:"F4",116:"F5",117:"F6",118:"F7",119:"F8",120:"F9",121:"F10",122:"F11",123:"F12",144:"NumLock",145:"ScrollLock",224:"Meta"},Hj={Alt:"altKey",Control:"ctrlKey",Meta:"metaKey",Shift:"shiftKey"},il=E({},Mc,{key:function(a){if(a.key){var b=gl[a.key]||a.key;if("Unidentified"!==b)return b}return"keypress"===a.type?(a=cd(a),13===a?"Enter":String.fromCharCode(a)):"keydown"===a.type||"keyup"===a.type?hl[a.keyCode]||"Unidentified":""},code:0,location:0,ctrlKey:0,shiftKey:0,altKey:0,
metaKey:0,repeat:0,locale:0,getModifierState:Fe,charCode:function(a){return"keypress"===a.type?cd(a):0},keyCode:function(a){return"keydown"===a.type||"keyup"===a.type?a.keyCode:0},which:function(a){return"keypress"===a.type?cd(a):"keydown"===a.type||"keyup"===a.type?a.keyCode:0}}),Wj=ka(il),jl=E({},Yd,{pointerId:0,width:0,height:0,pressure:0,tangentialPressure:0,tiltX:0,tiltY:0,twist:0,pointerType:0,isPrimary:0}),nh=ka(jl),kl=E({},Mc,{touches:0,targetTouches:0,changedTouches:0,altKey:0,metaKey:0,
ctrlKey:0,shiftKey:0,getModifierState:Fe}),Yj=ka(kl),ll=E({},Yb,{propertyName:0,elapsedTime:0,pseudoElement:0}),ak=ka(ll),ml=E({},Yd,{deltaX:function(a){return"deltaX"in a?a.deltaX:"wheelDeltaX"in a?-a.wheelDeltaX:0},deltaY:function(a){return"deltaY"in a?a.deltaY:"wheelDeltaY"in a?-a.wheelDeltaY:"wheelDelta"in a?-a.wheelDelta:0},deltaZ:0,deltaMode:0}),ck=ka(ml),Ij=[9,13,27,32],Ge=Ia&&"CompositionEvent"in window,Oc=null;Ia&&"documentMode"in document&&(Oc=document.documentMode);var fk=Ia&&"TextEvent"in
window&&!Oc,Ug=Ia&&(!Ge||Oc&&8<Oc&&11>=Oc),Tg=String.fromCharCode(32),Sg=!1,Hb=!1,Lj={color:!0,date:!0,datetime:!0,"datetime-local":!0,email:!0,month:!0,number:!0,password:!0,range:!0,search:!0,tel:!0,text:!0,time:!0,url:!0,week:!0},oc=null,pc=null,ph=!1;Ia&&(ph=Mj("input")&&(!document.documentMode||9<document.documentMode));var ua="function"===typeof Object.is?Object.is:Tj,ek=Ia&&"documentMode"in document&&11>=document.documentMode,Jb=null,Ke=null,rc=null,Je=!1,Kb={animationend:gd("Animation","AnimationEnd"),
animationiteration:gd("Animation","AnimationIteration"),animationstart:gd("Animation","AnimationStart"),transitionend:gd("Transition","TransitionEnd")},Le={},eh={};Ia&&(eh=document.createElement("div").style,"AnimationEvent"in window||(delete Kb.animationend.animation,delete Kb.animationiteration.animation,delete Kb.animationstart.animation),"TransitionEvent"in window||delete Kb.transitionend.transition);var jh=hd("animationend"),kh=hd("animationiteration"),lh=hd("animationstart"),mh=hd("transitionend"),
fh=new Map,$i="abort auxClick cancel canPlay canPlayThrough click close contextMenu copy cut drag dragEnd dragEnter dragExit dragLeave dragOver dragStart drop durationChange emptied encrypted ended error gotPointerCapture input invalid keyDown keyPress keyUp load loadedData loadedMetadata loadStart lostPointerCapture mouseDown mouseMove mouseOut mouseOver mouseUp paste pause play playing pointerCancel pointerDown pointerMove pointerOut pointerOver pointerUp progress rateChange reset resize seeked seeking stalled submit suspend timeUpdate touchCancel touchEnd touchStart volumeChange scroll toggle touchMove waiting wheel".split(" ");
(function(){for(var a=0;a<$i.length;a++){var b=$i[a],c=b.toLowerCase();b=b[0].toUpperCase()+b.slice(1);$a(c,"on"+b)}$a(jh,"onAnimationEnd");$a(kh,"onAnimationIteration");$a(lh,"onAnimationStart");$a("dblclick","onDoubleClick");$a("focusin","onFocus");$a("focusout","onBlur");$a(mh,"onTransitionEnd")})();Ab("onMouseEnter",["mouseout","mouseover"]);Ab("onMouseLeave",["mouseout","mouseover"]);Ab("onPointerEnter",["pointerout","pointerover"]);Ab("onPointerLeave",["pointerout","pointerover"]);nb("onChange",
"change click focusin focusout input keydown keyup selectionchange".split(" "));nb("onSelect","focusout contextmenu dragend focusin keydown keyup mousedown mouseup selectionchange".split(" "));nb("onBeforeInput",["compositionend","keypress","textInput","paste"]);nb("onCompositionEnd","compositionend focusout keydown keypress keyup mousedown".split(" "));nb("onCompositionStart","compositionstart focusout keydown keypress keyup mousedown".split(" "));nb("onCompositionUpdate","compositionupdate focusout keydown keypress keyup mousedown".split(" "));
var Ec="abort canplay canplaythrough durationchange emptied encrypted ended error loadeddata loadedmetadata loadstart pause play playing progress ratechange resize seeked seeking stalled suspend timeupdate volumechange waiting".split(" "),Vj=new Set("cancel close invalid load scroll toggle".split(" ").concat(Ec)),id="_reactListening"+Math.random().toString(36).slice(2),hk=/\r\n?/g,ik=/\u0000|\uFFFD/g,Jf=null,Kf=null,Rf="function"===typeof setTimeout?setTimeout:void 0,Ok="function"===typeof clearTimeout?
clearTimeout:void 0,aj="function"===typeof Promise?Promise:void 0,Kk="function"===typeof queueMicrotask?queueMicrotask:"undefined"!==typeof aj?function(a){return aj.resolve(null).then(a).catch(jk)}:Rf,Zb=Math.random().toString(36).slice(2),Da="__reactFiber$"+Zb,uc="__reactProps$"+Zb,Ja="__reactContainer$"+Zb,Me="__reactEvents$"+Zb,Ek="__reactListeners$"+Zb,Fk="__reactHandles$"+Zb,Se=[],Mb=-1,cb={},J=bb(cb),S=bb(!1),qb=cb,La=null,md=!1,Te=!1,Ob=[],Pb=0,od=null,nd=0,na=[],oa=0,sb=null,Ma=1,Na="",la=
null,fa=null,D=!1,wa=null,Jk=Sa.ReactCurrentBatchConfig,rd=bb(null),qd=null,Rb=null,$e=null,tb=null,lk=Oa,fb=!1,Jh=(new mb.Component).refs,ud={isMounted:function(a){return(a=a._reactInternals)?ob(a)===a:!1},enqueueSetState:function(a,b,c){a=a._reactInternals;var d=Z(),e=hb(a),f=Pa(d,e);f.payload=b;void 0!==c&&null!==c&&(f.callback=c);b=eb(a,f,e);null!==b&&(ya(b,a,e,d),sd(b,a,e))},enqueueReplaceState:function(a,b,c){a=a._reactInternals;var d=Z(),e=hb(a),f=Pa(d,e);f.tag=1;f.payload=b;void 0!==c&&null!==
c&&(f.callback=c);b=eb(a,f,e);null!==b&&(ya(b,a,e,d),sd(b,a,e))},enqueueForceUpdate:function(a,b){a=a._reactInternals;var c=Z(),d=hb(a),e=Pa(c,d);e.tag=2;void 0!==b&&null!==b&&(e.callback=b);b=eb(a,e,d);null!==b&&(ya(b,a,d,c),sd(b,a,d))}},Vb=Lh(!0),mi=Lh(!1),wc={},Ea=bb(wc),yc=bb(wc),xc=bb(wc),G=bb(0),mf=[],zd=Sa.ReactCurrentDispatcher,uf=Sa.ReactCurrentBatchConfig,wb=0,C=null,K=null,N=null,Bd=!1,zc=!1,Ac=0,nl=0,Ad={readContext:qa,useCallback:V,useContext:V,useEffect:V,useImperativeHandle:V,useInsertionEffect:V,
useLayoutEffect:V,useMemo:V,useReducer:V,useRef:V,useState:V,useDebugValue:V,useDeferredValue:V,useTransition:V,useMutableSource:V,useSyncExternalStore:V,useId:V,unstable_isNewReconciler:!1},mk={readContext:qa,useCallback:function(a,b){Fa().memoizedState=[a,void 0===b?null:b];return a},useContext:qa,useEffect:Wh,useImperativeHandle:function(a,b,c){c=null!==c&&void 0!==c?c.concat([a]):null;return Cd(4194308,4,Zh.bind(null,b,a),c)},useLayoutEffect:function(a,b){return Cd(4194308,4,a,b)},useInsertionEffect:function(a,
b){return Cd(4,2,a,b)},useMemo:function(a,b){var c=Fa();b=void 0===b?null:b;a=a();c.memoizedState=[a,b];return a},useReducer:function(a,b,c){var d=Fa();b=void 0!==c?c(b):b;d.memoizedState=d.baseState=b;a={pending:null,interleaved:null,lanes:0,dispatch:null,lastRenderedReducer:a,lastRenderedState:b};d.queue=a;a=a.dispatch=rk.bind(null,C,a);return[d.memoizedState,a]},useRef:function(a){var b=Fa();a={current:a};return b.memoizedState=a},useState:Uh,useDebugValue:tf,useDeferredValue:function(a){return Fa().memoizedState=
a},useTransition:function(){var a=Uh(!1),b=a[0];a=qk.bind(null,a[1]);Fa().memoizedState=a;return[b,a]},useMutableSource:function(a,b,c){},useSyncExternalStore:function(a,b,c){var d=C,e=Fa();if(D){if(void 0===c)throw Error(n(407));c=c()}else{c=b();if(null===O)throw Error(n(349));0!==(wb&30)||Rh(d,b,c)}e.memoizedState=c;var f={value:c,getSnapshot:b};e.queue=f;Wh(Ph.bind(null,d,f,a),[a]);d.flags|=2048;Cc(9,Qh.bind(null,d,f,c,b),void 0,null);return c},useId:function(){var a=Fa(),b=O.identifierPrefix;
if(D){var c=Na;var d=Ma;c=(d&~(1<<32-ta(d)-1)).toString(32)+c;b=":"+b+"R"+c;c=Ac++;0<c&&(b+="H"+c.toString(32));b+=":"}else c=nl++,b=":"+b+"r"+c.toString(32)+":";return a.memoizedState=b},unstable_isNewReconciler:!1},nk={readContext:qa,useCallback:ai,useContext:qa,useEffect:sf,useImperativeHandle:$h,useInsertionEffect:Xh,useLayoutEffect:Yh,useMemo:bi,useReducer:qf,useRef:Vh,useState:function(a){return qf(Bc)},useDebugValue:tf,useDeferredValue:function(a){var b=sa();return ci(b,K.memoizedState,a)},
useTransition:function(){var a=qf(Bc)[0],b=sa().memoizedState;return[a,b]},useMutableSource:Nh,useSyncExternalStore:Oh,useId:di,unstable_isNewReconciler:!1},ok={readContext:qa,useCallback:ai,useContext:qa,useEffect:sf,useImperativeHandle:$h,useInsertionEffect:Xh,useLayoutEffect:Yh,useMemo:bi,useReducer:rf,useRef:Vh,useState:function(a){return rf(Bc)},useDebugValue:tf,useDeferredValue:function(a){var b=sa();return null===K?b.memoizedState=a:ci(b,K.memoizedState,a)},useTransition:function(){var a=rf(Bc)[0],
b=sa().memoizedState;return[a,b]},useMutableSource:Nh,useSyncExternalStore:Oh,useId:di,unstable_isNewReconciler:!1},sk="function"===typeof WeakMap?WeakMap:Map,uk=Sa.ReactCurrentOwner,ha=!1,Cf={dehydrated:null,treeContext:null,retryLane:0};var Ak=function(a,b,c,d){for(c=b.child;null!==c;){if(5===c.tag||6===c.tag)a.appendChild(c.stateNode);else if(4!==c.tag&&null!==c.child){c.child.return=c;c=c.child;continue}if(c===b)break;for(;null===c.sibling;){if(null===c.return||c.return===b)return;c=c.return}c.sibling.return=
c.return;c=c.sibling}};var yi=function(a,b){};var zk=function(a,b,c,d,e){var f=a.memoizedProps;if(f!==d){a=b.stateNode;vb(Ea.current);e=null;switch(c){case "input":f=ke(a,f);d=ke(a,d);e=[];break;case "select":f=E({},f,{value:void 0});d=E({},d,{value:void 0});e=[];break;case "textarea":f=ne(a,f);d=ne(a,d);e=[];break;default:"function"!==typeof f.onClick&&"function"===typeof d.onClick&&(a.onclick=kd)}pe(c,d);var g;c=null;for(m in f)if(!d.hasOwnProperty(m)&&f.hasOwnProperty(m)&&null!=f[m])if("style"===
m){var h=f[m];for(g in h)h.hasOwnProperty(g)&&(c||(c={}),c[g]="")}else"dangerouslySetInnerHTML"!==m&&"children"!==m&&"suppressContentEditableWarning"!==m&&"suppressHydrationWarning"!==m&&"autoFocus"!==m&&($b.hasOwnProperty(m)?e||(e=[]):(e=e||[]).push(m,null));for(m in d){var k=d[m];h=null!=f?f[m]:void 0;if(d.hasOwnProperty(m)&&k!==h&&(null!=k||null!=h))if("style"===m)if(h){for(g in h)!h.hasOwnProperty(g)||k&&k.hasOwnProperty(g)||(c||(c={}),c[g]="");for(g in k)k.hasOwnProperty(g)&&h[g]!==k[g]&&(c||
(c={}),c[g]=k[g])}else c||(e||(e=[]),e.push(m,c)),c=k;else"dangerouslySetInnerHTML"===m?(k=k?k.__html:void 0,h=h?h.__html:void 0,null!=k&&h!==k&&(e=e||[]).push(m,k)):"children"===m?"string"!==typeof k&&"number"!==typeof k||(e=e||[]).push(m,""+k):"suppressContentEditableWarning"!==m&&"suppressHydrationWarning"!==m&&($b.hasOwnProperty(m)?(null!=k&&"onScroll"===m&&B("scroll",a),e||h===k||(e=[])):(e=e||[]).push(m,k))}c&&(e=e||[]).push("style",c);var m=e;if(b.updateQueue=m)b.flags|=4}};var Bk=function(a,
b,c,d){c!==d&&(b.flags|=4)};var Jd=!1,X=!1,Gk="function"===typeof WeakSet?WeakSet:Set,l=null,Ai=!1,T=null,za=!1,Nk=Math.ceil,Od=Sa.ReactCurrentDispatcher,Uf=Sa.ReactCurrentOwner,ca=Sa.ReactCurrentBatchConfig,p=0,O=null,I=null,U=0,ba=0,Ga=bb(0),L=0,Jc=null,ra=0,Md=0,Sf=0,Kc=null,ja=null,Of=0,Hf=Infinity,Ra=null,Ed=!1,xf=null,ib=null,Pd=!1,lb=null,Qd=0,Ic=0,Pf=null,Kd=-1,Ld=0;var Rk=function(a,b,c){if(null!==a)if(a.memoizedProps!==b.pendingProps||S.current)ha=!0;else{if(0===(a.lanes&c)&&0===(b.flags&
128))return ha=!1,xk(a,b,c);ha=0!==(a.flags&131072)?!0:!1}else ha=!1,D&&0!==(b.flags&1048576)&&yh(b,nd,b.index);b.lanes=0;switch(b.tag){case 2:var d=b.type;Fd(a,b);a=b.pendingProps;var e=Nb(b,J.current);Sb(b,c);e=of(null,b,d,a,e,c);var f=pf();b.flags|=1;"object"===typeof e&&null!==e&&"function"===typeof e.render&&void 0===e.$$typeof?(b.tag=1,b.memoizedState=null,b.updateQueue=null,ea(d)?(f=!0,ld(b)):f=!1,b.memoizedState=null!==e.state&&void 0!==e.state?e.state:null,df(b),e.updater=ud,b.stateNode=
e,e._reactInternals=b,ff(b,d,a,c),b=Af(null,b,d,!0,f,c)):(b.tag=0,D&&f&&Ue(b),aa(null,b,e,c),b=b.child);return b;case 16:d=b.elementType;a:{Fd(a,b);a=b.pendingProps;e=d._init;d=e(d._payload);b.type=d;e=b.tag=Vk(d);a=xa(d,a);switch(e){case 0:b=zf(null,b,d,a,c);break a;case 1:b=si(null,b,d,a,c);break a;case 11:b=ni(null,b,d,a,c);break a;case 14:b=oi(null,b,d,xa(d.type,a),c);break a}throw Error(n(306,d,""));}return b;case 0:return d=b.type,e=b.pendingProps,e=b.elementType===d?e:xa(d,e),zf(a,b,d,e,c);
case 1:return d=b.type,e=b.pendingProps,e=b.elementType===d?e:xa(d,e),si(a,b,d,e,c);case 3:a:{ti(b);if(null===a)throw Error(n(387));d=b.pendingProps;f=b.memoizedState;e=f.element;Dh(a,b);td(b,d,null,c);var g=b.memoizedState;d=g.element;if(f.isDehydrated)if(f={element:d,isDehydrated:!1,cache:g.cache,pendingSuspenseBoundaries:g.pendingSuspenseBoundaries,transitions:g.transitions},b.updateQueue.baseState=f,b.memoizedState=f,b.flags&256){e=Ub(Error(n(423)),b);b=ui(a,b,d,c,e);break a}else if(d!==e){e=
Ub(Error(n(424)),b);b=ui(a,b,d,c,e);break a}else for(fa=Ka(b.stateNode.containerInfo.firstChild),la=b,D=!0,wa=null,c=mi(b,null,d,c),b.child=c;c;)c.flags=c.flags&-3|4096,c=c.sibling;else{Qb();if(d===e){b=Qa(a,b,c);break a}aa(a,b,d,c)}b=b.child}return b;case 5:return Mh(b),null===a&&Xe(b),d=b.type,e=b.pendingProps,f=null!==a?a.memoizedProps:null,g=e.children,Qe(d,e)?g=null:null!==f&&Qe(d,f)&&(b.flags|=32),ri(a,b),aa(a,b,g,c),b.child;case 6:return null===a&&Xe(b),null;case 13:return vi(a,b,c);case 4:return jf(b,
b.stateNode.containerInfo),d=b.pendingProps,null===a?b.child=Vb(b,null,d,c):aa(a,b,d,c),b.child;case 11:return d=b.type,e=b.pendingProps,e=b.elementType===d?e:xa(d,e),ni(a,b,d,e,c);case 7:return aa(a,b,b.pendingProps,c),b.child;case 8:return aa(a,b,b.pendingProps.children,c),b.child;case 12:return aa(a,b,b.pendingProps.children,c),b.child;case 10:a:{d=b.type._context;e=b.pendingProps;f=b.memoizedProps;g=e.value;y(rd,d._currentValue);d._currentValue=g;if(null!==f)if(ua(f.value,g)){if(f.children===
e.children&&!S.current){b=Qa(a,b,c);break a}}else for(f=b.child,null!==f&&(f.return=b);null!==f;){var h=f.dependencies;if(null!==h){g=f.child;for(var k=h.firstContext;null!==k;){if(k.context===d){if(1===f.tag){k=Pa(-1,c&-c);k.tag=2;var m=f.updateQueue;if(null!==m){m=m.shared;var l=m.pending;null===l?k.next=k:(k.next=l.next,l.next=k);m.pending=k}}f.lanes|=c;k=f.alternate;null!==k&&(k.lanes|=c);bf(f.return,c,b);h.lanes|=c;break}k=k.next}}else if(10===f.tag)g=f.type===b.type?null:f.child;else if(18===
f.tag){g=f.return;if(null===g)throw Error(n(341));g.lanes|=c;h=g.alternate;null!==h&&(h.lanes|=c);bf(g,c,b);g=f.sibling}else g=f.child;if(null!==g)g.return=f;else for(g=f;null!==g;){if(g===b){g=null;break}f=g.sibling;if(null!==f){f.return=g.return;g=f;break}g=g.return}f=g}aa(a,b,e.children,c);b=b.child}return b;case 9:return e=b.type,d=b.pendingProps.children,Sb(b,c),e=qa(e),d=d(e),b.flags|=1,aa(a,b,d,c),b.child;case 14:return d=b.type,e=xa(d,b.pendingProps),e=xa(d.type,e),oi(a,b,d,e,c);case 15:return pi(a,
b,b.type,b.pendingProps,c);case 17:return d=b.type,e=b.pendingProps,e=b.elementType===d?e:xa(d,e),Fd(a,b),b.tag=1,ea(d)?(a=!0,ld(b)):a=!1,Sb(b,c),Hh(b,d,e),ff(b,d,e,c),Af(null,b,d,!0,a,c);case 19:return xi(a,b,c);case 22:return qi(a,b,c)}throw Error(n(156,b.tag));};var pa=function(a,b,c,d){return new Uk(a,b,c,d)},bj="function"===typeof reportError?reportError:function(a){console.error(a)};Ud.prototype.render=Xf.prototype.render=function(a){var b=this._internalRoot;if(null===b)throw Error(n(409));
Sd(a,b,null,null)};Ud.prototype.unmount=Xf.prototype.unmount=function(){var a=this._internalRoot;if(null!==a){this._internalRoot=null;var b=a.containerInfo;zb(function(){Sd(null,a,null,null)});b[Ja]=null}};Ud.prototype.unstable_scheduleHydration=function(a){if(a){var b=ol();a={blockedOn:null,target:a,priority:b};for(var c=0;c<Ya.length&&0!==b&&b<Ya[c].priority;c++);Ya.splice(c,0,a);0===c&&Hg(a)}};var Dj=function(a){switch(a.tag){case 3:var b=a.stateNode;if(b.current.memoizedState.isDehydrated){var c=
hc(b.pendingLanes);0!==c&&(xe(b,c|1),ia(b,P()),0===(p&6)&&(Hc(),db()))}break;case 13:zb(function(){var b=Oa(a,1);if(null!==b){var c=Z();ya(b,a,1,c)}}),Wf(a,1)}};var Gg=function(a){if(13===a.tag){var b=Oa(a,134217728);if(null!==b){var c=Z();ya(b,a,134217728,c)}Wf(a,134217728)}};var yj=function(a){if(13===a.tag){var b=hb(a),c=Oa(a,b);if(null!==c){var d=Z();ya(c,a,b,d)}Wf(a,b)}};var ol=function(){return z};var xj=function(a,b){var c=z;try{return z=a,b()}finally{z=c}};se=function(a,b,c){switch(b){case "input":le(a,
c);b=c.name;if("radio"===c.type&&null!=b){for(c=a;c.parentNode;)c=c.parentNode;c=c.querySelectorAll("input[name="+JSON.stringify(""+b)+'][type="radio"]');for(b=0;b<c.length;b++){var d=c[b];if(d!==a&&d.form===a.form){var e=Rc(d);if(!e)throw Error(n(90));jg(d);le(d,e)}}}break;case "textarea":og(a,c);break;case "select":b=c.value,null!=b&&Db(a,!!c.multiple,b,!1)}};(function(a,b,c){xg=a;yg=c})(Tf,function(a,b,c,d,e){var f=z,g=ca.transition;try{return ca.transition=null,z=1,a(b,c,d,e)}finally{z=f,ca.transition=
g,0===p&&Hc()}},zb);var pl={usingClientEntryPoint:!1,Events:[ec,Ib,Rc,ug,vg,Tf]};(function(a){a={bundleType:a.bundleType,version:a.version,rendererPackageName:a.rendererPackageName,rendererConfig:a.rendererConfig,overrideHookState:null,overrideHookStateDeletePath:null,overrideHookStateRenamePath:null,overrideProps:null,overridePropsDeletePath:null,overridePropsRenamePath:null,setErrorHandler:null,setSuspenseHandler:null,scheduleUpdate:null,currentDispatcherRef:Sa.ReactCurrentDispatcher,findHostInstanceByFiber:Yk,
findFiberByHostInstance:a.findFiberByHostInstance||Zk,findHostInstancesForRefresh:null,scheduleRefresh:null,scheduleRoot:null,setRefreshHandler:null,getCurrentFiber:null,reconcilerVersion:"18.2.0"};if("undefined"===typeof __REACT_DEVTOOLS_GLOBAL_HOOK__)a=!1;else{var b=__REACT_DEVTOOLS_GLOBAL_HOOK__;if(b.isDisabled||!b.supportsFiber)a=!0;else{try{Uc=b.inject(a),Ca=b}catch(c){}a=b.checkDCE?!0:!1}}return a})({findFiberByHostInstance:pb,bundleType:0,version:"18.2.0-next-9e3b772b8-20220608",
rendererPackageName:"react-dom"});Q.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED=pl;Q.createPortal=function(a,b){var c=2<arguments.length&&void 0!==arguments[2]?arguments[2]:null;if(!Yf(b))throw Error(n(200));return Xk(a,b,null,c)};Q.createRoot=function(a,b){if(!Yf(a))throw Error(n(299));var c=!1,d="",e=bj;null!==b&&void 0!==b&&(!0===b.unstable_strictMode&&(c=!0),void 0!==b.identifierPrefix&&(d=b.identifierPrefix),void 0!==b.onRecoverableError&&(e=b.onRecoverableError));b=Vf(a,1,!1,null,null,
c,!1,d,e);a[Ja]=b.current;sc(8===a.nodeType?a.parentNode:a);return new Xf(b)};Q.findDOMNode=function(a){if(null==a)return null;if(1===a.nodeType)return a;var b=a._reactInternals;if(void 0===b){if("function"===typeof a.render)throw Error(n(188));a=Object.keys(a).join(",");throw Error(n(268,a));}a=Bg(b);a=null===a?null:a.stateNode;return a};Q.flushSync=function(a){return zb(a)};Q.hydrate=function(a,b,c){if(!Vd(b))throw Error(n(200));return Wd(null,a,b,!0,c)};Q.hydrateRoot=function(a,b,c){if(!Yf(a))throw Error(n(405));
var d=null!=c&&c.hydratedSources||null,e=!1,f="",g=bj;null!==c&&void 0!==c&&(!0===c.unstable_strictMode&&(e=!0),void 0!==c.identifierPrefix&&(f=c.identifierPrefix),void 0!==c.onRecoverableError&&(g=c.onRecoverableError));b=Xi(b,null,a,1,null!=c?c:null,e,!1,f,g);a[Ja]=b.current;sc(a);if(d)for(a=0;a<d.length;a++)c=d[a],e=c._getVersion,e=e(c._source),null==b.mutableSourceEagerHydrationData?b.mutableSourceEagerHydrationData=[c,e]:b.mutableSourceEagerHydrationData.push(c,e);return new Ud(b)};Q.render=
function(a,b,c){if(!Vd(b))throw Error(n(200));return Wd(null,a,b,!1,c)};Q.unmountComponentAtNode=function(a){if(!Vd(a))throw Error(n(40));return a._reactRootContainer?(zb(function(){Wd(null,null,a,!1,function(){a._reactRootContainer=null;a[Ja]=null})}),!0):!1};Q.unstable_batchedUpdates=Tf;Q.unstable_renderSubtreeIntoContainer=function(a,b,c,d){if(!Vd(c))throw Error(n(200));if(null==a||void 0===a._reactInternals)throw Error(n(38));return Wd(a,b,c,!1,d)};Q.version="18.2.0-next-9e3b772b8-20220608"});
})();

View File

@ -0,0 +1,31 @@
/**
* @license React
* react.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
(function(){'use strict';(function(c,x){"object"===typeof exports&&"undefined"!==typeof module?x(exports):"function"===typeof define&&define.amd?define(["exports"],x):(c=c||self,x(c.React={}))})(this,function(c){function x(a){if(null===a||"object"!==typeof a)return null;a=V&&a[V]||a["@@iterator"];return"function"===typeof a?a:null}function w(a,b,e){this.props=a;this.context=b;this.refs=W;this.updater=e||X}function Y(){}function K(a,b,e){this.props=a;this.context=b;this.refs=W;this.updater=e||X}function Z(a,b,
e){var m,d={},c=null,h=null;if(null!=b)for(m in void 0!==b.ref&&(h=b.ref),void 0!==b.key&&(c=""+b.key),b)aa.call(b,m)&&!ba.hasOwnProperty(m)&&(d[m]=b[m]);var l=arguments.length-2;if(1===l)d.children=e;else if(1<l){for(var f=Array(l),k=0;k<l;k++)f[k]=arguments[k+2];d.children=f}if(a&&a.defaultProps)for(m in l=a.defaultProps,l)void 0===d[m]&&(d[m]=l[m]);return{$$typeof:y,type:a,key:c,ref:h,props:d,_owner:L.current}}function na(a,b){return{$$typeof:y,type:a.type,key:b,ref:a.ref,props:a.props,_owner:a._owner}}
function M(a){return"object"===typeof a&&null!==a&&a.$$typeof===y}function oa(a){var b={"=":"=0",":":"=2"};return"$"+a.replace(/[=:]/g,function(a){return b[a]})}function N(a,b){return"object"===typeof a&&null!==a&&null!=a.key?oa(""+a.key):b.toString(36)}function B(a,b,e,m,d){var c=typeof a;if("undefined"===c||"boolean"===c)a=null;var h=!1;if(null===a)h=!0;else switch(c){case "string":case "number":h=!0;break;case "object":switch(a.$$typeof){case y:case pa:h=!0}}if(h)return h=a,d=d(h),a=""===m?"."+
N(h,0):m,ca(d)?(e="",null!=a&&(e=a.replace(da,"$&/")+"/"),B(d,b,e,"",function(a){return a})):null!=d&&(M(d)&&(d=na(d,e+(!d.key||h&&h.key===d.key?"":(""+d.key).replace(da,"$&/")+"/")+a)),b.push(d)),1;h=0;m=""===m?".":m+":";if(ca(a))for(var l=0;l<a.length;l++){c=a[l];var f=m+N(c,l);h+=B(c,b,e,f,d)}else if(f=x(a),"function"===typeof f)for(a=f.call(a),l=0;!(c=a.next()).done;)c=c.value,f=m+N(c,l++),h+=B(c,b,e,f,d);else if("object"===c)throw b=String(a),Error("Objects are not valid as a React child (found: "+
("[object Object]"===b?"object with keys {"+Object.keys(a).join(", ")+"}":b)+"). If you meant to render a collection of children, use an array instead.");return h}function C(a,b,e){if(null==a)return a;var c=[],d=0;B(a,c,"","",function(a){return b.call(e,a,d++)});return c}function qa(a){if(-1===a._status){var b=a._result;b=b();b.then(function(b){if(0===a._status||-1===a._status)a._status=1,a._result=b},function(b){if(0===a._status||-1===a._status)a._status=2,a._result=b});-1===a._status&&(a._status=
0,a._result=b)}if(1===a._status)return a._result.default;throw a._result;}function O(a,b){var e=a.length;a.push(b);a:for(;0<e;){var c=e-1>>>1,d=a[c];if(0<D(d,b))a[c]=b,a[e]=d,e=c;else break a}}function p(a){return 0===a.length?null:a[0]}function E(a){if(0===a.length)return null;var b=a[0],e=a.pop();if(e!==b){a[0]=e;a:for(var c=0,d=a.length,k=d>>>1;c<k;){var h=2*(c+1)-1,l=a[h],f=h+1,g=a[f];if(0>D(l,e))f<d&&0>D(g,l)?(a[c]=g,a[f]=e,c=f):(a[c]=l,a[h]=e,c=h);else if(f<d&&0>D(g,e))a[c]=g,a[f]=e,c=f;else break a}}return b}
function D(a,b){var c=a.sortIndex-b.sortIndex;return 0!==c?c:a.id-b.id}function P(a){for(var b=p(r);null!==b;){if(null===b.callback)E(r);else if(b.startTime<=a)E(r),b.sortIndex=b.expirationTime,O(q,b);else break;b=p(r)}}function Q(a){z=!1;P(a);if(!u)if(null!==p(q))u=!0,R(S);else{var b=p(r);null!==b&&T(Q,b.startTime-a)}}function S(a,b){u=!1;z&&(z=!1,ea(A),A=-1);F=!0;var c=k;try{P(b);for(n=p(q);null!==n&&(!(n.expirationTime>b)||a&&!fa());){var m=n.callback;if("function"===typeof m){n.callback=null;
k=n.priorityLevel;var d=m(n.expirationTime<=b);b=v();"function"===typeof d?n.callback=d:n===p(q)&&E(q);P(b)}else E(q);n=p(q)}if(null!==n)var g=!0;else{var h=p(r);null!==h&&T(Q,h.startTime-b);g=!1}return g}finally{n=null,k=c,F=!1}}function fa(){return v()-ha<ia?!1:!0}function R(a){G=a;H||(H=!0,I())}function T(a,b){A=ja(function(){a(v())},b)}var y=Symbol.for("react.element"),pa=Symbol.for("react.portal"),ra=Symbol.for("react.fragment"),sa=Symbol.for("react.strict_mode"),ta=Symbol.for("react.profiler"),
ua=Symbol.for("react.provider"),va=Symbol.for("react.context"),wa=Symbol.for("react.forward_ref"),xa=Symbol.for("react.suspense"),ya=Symbol.for("react.memo"),za=Symbol.for("react.lazy"),V=Symbol.iterator,X={isMounted:function(a){return!1},enqueueForceUpdate:function(a,b,c){},enqueueReplaceState:function(a,b,c,m){},enqueueSetState:function(a,b,c,m){}},ka=Object.assign,W={};w.prototype.isReactComponent={};w.prototype.setState=function(a,b){if("object"!==typeof a&&"function"!==typeof a&&null!=a)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");
this.updater.enqueueSetState(this,a,b,"setState")};w.prototype.forceUpdate=function(a){this.updater.enqueueForceUpdate(this,a,"forceUpdate")};Y.prototype=w.prototype;var t=K.prototype=new Y;t.constructor=K;ka(t,w.prototype);t.isPureReactComponent=!0;var ca=Array.isArray,aa=Object.prototype.hasOwnProperty,L={current:null},ba={key:!0,ref:!0,__self:!0,__source:!0},da=/\/+/g,g={current:null},J={transition:null};if("object"===typeof performance&&"function"===typeof performance.now){var Aa=performance;
var v=function(){return Aa.now()}}else{var la=Date,Ba=la.now();v=function(){return la.now()-Ba}}var q=[],r=[],Ca=1,n=null,k=3,F=!1,u=!1,z=!1,ja="function"===typeof setTimeout?setTimeout:null,ea="function"===typeof clearTimeout?clearTimeout:null,ma="undefined"!==typeof setImmediate?setImmediate:null;"undefined"!==typeof navigator&&void 0!==navigator.scheduling&&void 0!==navigator.scheduling.isInputPending&&navigator.scheduling.isInputPending.bind(navigator.scheduling);var H=!1,G=null,A=-1,ia=5,ha=
-1,U=function(){if(null!==G){var a=v();ha=a;var b=!0;try{b=G(!0,a)}finally{b?I():(H=!1,G=null)}}else H=!1};if("function"===typeof ma)var I=function(){ma(U)};else if("undefined"!==typeof MessageChannel){t=new MessageChannel;var Da=t.port2;t.port1.onmessage=U;I=function(){Da.postMessage(null)}}else I=function(){ja(U,0)};t={ReactCurrentDispatcher:g,ReactCurrentOwner:L,ReactCurrentBatchConfig:J,Scheduler:{__proto__:null,unstable_ImmediatePriority:1,unstable_UserBlockingPriority:2,unstable_NormalPriority:3,
unstable_IdlePriority:5,unstable_LowPriority:4,unstable_runWithPriority:function(a,b){switch(a){case 1:case 2:case 3:case 4:case 5:break;default:a=3}var c=k;k=a;try{return b()}finally{k=c}},unstable_next:function(a){switch(k){case 1:case 2:case 3:var b=3;break;default:b=k}var c=k;k=b;try{return a()}finally{k=c}},unstable_scheduleCallback:function(a,b,c){var e=v();"object"===typeof c&&null!==c?(c=c.delay,c="number"===typeof c&&0<c?e+c:e):c=e;switch(a){case 1:var d=-1;break;case 2:d=250;break;case 5:d=
1073741823;break;case 4:d=1E4;break;default:d=5E3}d=c+d;a={id:Ca++,callback:b,priorityLevel:a,startTime:c,expirationTime:d,sortIndex:-1};c>e?(a.sortIndex=c,O(r,a),null===p(q)&&a===p(r)&&(z?(ea(A),A=-1):z=!0,T(Q,c-e))):(a.sortIndex=d,O(q,a),u||F||(u=!0,R(S)));return a},unstable_cancelCallback:function(a){a.callback=null},unstable_wrapCallback:function(a){var b=k;return function(){var c=k;k=b;try{return a.apply(this,arguments)}finally{k=c}}},unstable_getCurrentPriorityLevel:function(){return k},unstable_shouldYield:fa,
unstable_requestPaint:function(){},unstable_continueExecution:function(){u||F||(u=!0,R(S))},unstable_pauseExecution:function(){},unstable_getFirstCallbackNode:function(){return p(q)},get unstable_now(){return v},unstable_forceFrameRate:function(a){0>a||125<a?console.error("forceFrameRate takes a positive int between 0 and 125, forcing frame rates higher than 125 fps is not supported"):ia=0<a?Math.floor(1E3/a):5},unstable_Profiling:null}};c.Children={map:C,forEach:function(a,b,c){C(a,function(){b.apply(this,
arguments)},c)},count:function(a){var b=0;C(a,function(){b++});return b},toArray:function(a){return C(a,function(a){return a})||[]},only:function(a){if(!M(a))throw Error("React.Children.only expected to receive a single React element child.");return a}};c.Component=w;c.Fragment=ra;c.Profiler=ta;c.PureComponent=K;c.StrictMode=sa;c.Suspense=xa;c.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED=t;c.cloneElement=function(a,b,c){if(null===a||void 0===a)throw Error("React.cloneElement(...): The argument must be a React element, but you passed "+
a+".");var e=ka({},a.props),d=a.key,k=a.ref,h=a._owner;if(null!=b){void 0!==b.ref&&(k=b.ref,h=L.current);void 0!==b.key&&(d=""+b.key);if(a.type&&a.type.defaultProps)var l=a.type.defaultProps;for(f in b)aa.call(b,f)&&!ba.hasOwnProperty(f)&&(e[f]=void 0===b[f]&&void 0!==l?l[f]:b[f])}var f=arguments.length-2;if(1===f)e.children=c;else if(1<f){l=Array(f);for(var g=0;g<f;g++)l[g]=arguments[g+2];e.children=l}return{$$typeof:y,type:a.type,key:d,ref:k,props:e,_owner:h}};c.createContext=function(a){a={$$typeof:va,
_currentValue:a,_currentValue2:a,_threadCount:0,Provider:null,Consumer:null,_defaultValue:null,_globalName:null};a.Provider={$$typeof:ua,_context:a};return a.Consumer=a};c.createElement=Z;c.createFactory=function(a){var b=Z.bind(null,a);b.type=a;return b};c.createRef=function(){return{current:null}};c.forwardRef=function(a){return{$$typeof:wa,render:a}};c.isValidElement=M;c.lazy=function(a){return{$$typeof:za,_payload:{_status:-1,_result:a},_init:qa}};c.memo=function(a,b){return{$$typeof:ya,type:a,
compare:void 0===b?null:b}};c.startTransition=function(a,b){b=J.transition;J.transition={};try{a()}finally{J.transition=b}};c.unstable_act=function(a){throw Error("act(...) is not supported in production builds of React.");};c.useCallback=function(a,b){return g.current.useCallback(a,b)};c.useContext=function(a){return g.current.useContext(a)};c.useDebugValue=function(a,b){};c.useDeferredValue=function(a){return g.current.useDeferredValue(a)};c.useEffect=function(a,b){return g.current.useEffect(a,
b)};c.useId=function(){return g.current.useId()};c.useImperativeHandle=function(a,b,c){return g.current.useImperativeHandle(a,b,c)};c.useInsertionEffect=function(a,b){return g.current.useInsertionEffect(a,b)};c.useLayoutEffect=function(a,b){return g.current.useLayoutEffect(a,b)};c.useMemo=function(a,b){return g.current.useMemo(a,b)};c.useReducer=function(a,b,c){return g.current.useReducer(a,b,c)};c.useRef=function(a){return g.current.useRef(a)};c.useState=function(a){return g.current.useState(a)};
c.useSyncExternalStore=function(a,b,c){return g.current.useSyncExternalStore(a,b,c)};c.useTransition=function(){return g.current.useTransition()};c.version="18.2.0"});
})();

BIN
netbox/project-static/dist/index.umd.js vendored Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,3 +0,0 @@
// Rather than use CDNs to include GraphiQL dependencies, import and bundle the dependencies so
// they can be locally served.
@import '../node_modules/graphiql/graphiql.css';

View File

@ -1,17 +0,0 @@
/**
* Rather than use CDNs to include GraphiQL dependencies, import and bundle the dependencies so
* they can be locally served.
*/
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import 'graphql';
import GraphiQL from 'graphiql';
import SubscriptionsTransportWs from 'subscriptions-transport-ws';
window.React = React;
window.ReactDOM = ReactDOM;
// @ts-expect-error Assigning to window is required for graphene-django
window.SubscriptionsTransportWs = SubscriptionsTransportWs;
// @ts-expect-error Assigning to window is required for graphene-django
window.GraphiQL = GraphiQL;

View File

@ -1,16 +1,17 @@
{ {
"name": "netbox-graphiql", "name": "netbox-graphiql",
"version": "0.1.0", "version": "0.2.0",
"description": "NetBox GraphiQL Custom Front End", "description": "NetBox GraphiQL Custom Front End",
"main": "dist/graphiql.js", "main": "dist/graphiql.js",
"license": "Apache-2.0", "license": "Apache-2.0",
"private": true, "private": true,
"dependencies": { "dependencies": {
"graphiql": "1.8.9", "graphiql": "3.0.9",
"graphql": ">= v14.5.0 <= 15.5.0", "graphql": "16.8.1",
"react": "17.0.2", "react": "18.2.0",
"react-dom": "17.0.2", "react-dom": "18.2.0",
"subscriptions-transport-ws": "0.9.18", "react-scripts": "5.0.1",
"whatwg-fetch": "3.6.2" "js-cookie": "3.0.5",
"@graphiql/plugin-explorer": "1.0.2"
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{% comment %} {% comment %}
This template derives from the graphene-django project: This template derives from the strawberry-graphql project:
https://github.com/graphql-python/graphene-django/blob/main/graphene_django/templates/graphene/graphiql.html https://github.com/strawberry-graphql/strawberry/blob/main/strawberry/static/graphiql.html
{% endcomment %} {% endcomment %}
<!-- <!--
The request to this GraphQL server provided the header "Accept: text/html" The request to this GraphQL server provided the header "Accept: text/html"
@ -11,36 +11,130 @@ add "&raw" to the end of the URL within a browser.
--> -->
{% load static %} {% load static %}
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html>
<head> <head>
<title>GraphiQL | NetBox</title>
<link
rel="icon"
href="data:image/svg+xml,
<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22>
<!-- Strawberry Emoji as a HTML Entity (hex) -->
<text y=%22.9em%22 font-size=%2280%22>&#x1f353;</text>
</svg>"
/>
<style> <style>
html, body, #editor { body {
height: 100%; height: 100%;
margin: 0; margin: 0;
overflow: hidden;
width: 100%; width: 100%;
overflow: hidden;
}
#graphiql {
height: 100vh;
display: flex;
}
.docExplorerHide {
display: none;
}
.doc-explorer-contents {
overflow-y: hidden !important;
}
.docExplorerWrap {
width: unset !important;
min-width: unset !important;
}
.graphiql-explorer-actions select {
margin-left: 4px;
} }
</style> </style>
<link href="{% static 'graphiql.css'%}" rel="stylesheet" />
<link rel="icon" type="image/png" href="{% static 'graphql.ico' %}" /> <script src="{% static 'graphiql/react.production.min.js' %}"></script>
<title>GraphiQL | NetBox</title> <script src="{% static 'graphiql/react-dom.production.min.js' %}"></script>
<script src="{% static 'graphiql/js.cookie.min.js' %}"></script>
<link rel="stylesheet" href="{% static 'graphiql/graphiql.min.css' %}"/>
<link rel="stylesheet" href="{% static 'graphiql/plugin-explorer-style.css' %}"/>
</head> </head>
<body> <body>
<div id="editor"></div> <div id="graphiql" class="graphiql-container">Loading...</div>
{% csrf_token %} <script src="{% static 'graphiql/graphiql.min.js' %}"></script>
<script type="application/javascript"> <script src="{% static 'graphiql/index.umd.js' %}"></script>
window.GRAPHENE_SETTINGS = {
{% if subscription_path %} <script>
subscriptionPath: "{{subscription_path}}", const EXAMPLE_QUERY = `# Welcome to GraphiQL 🍓
{% endif %} #
graphiqlHeaderEditorEnabled: {{ graphiql_header_editor_enabled|yesno:"true,false" }}, # GraphiQL is an in-browser tool for writing, validating, and
}; # testing GraphQL queries.
#
# Type queries into this side of the screen, and you will see intelligent
# typeaheads aware of the current GraphQL type schema and live syntax and
# validation errors highlighted within the text.
#
# GraphQL queries typically start with a "{" character. Lines that starts
# with a # are ignored.
#
# An example GraphQL query might look like:
#
# {
# field(arg: "value") {
# subField
# }
# }
#
# Keyboard shortcuts:
#
# Run Query: Ctrl-Enter (or press the play button above)
#
# Auto Complete: Ctrl-Space (or just start typing)
#
`;
const fetchURL = window.location.href;
function httpUrlToWebSockeUrl(url) {
const parsedURL = new URL(url);
const protocol = parsedURL.protocol === "http:" ? "ws:" : "wss:";
parsedURL.protocol = protocol;
parsedURL.hash = "";
return parsedURL.toString();
}
const headers = {};
const csrfToken = Cookies.get("csrftoken");
if (csrfToken) {
headers["x-csrftoken"] = csrfToken;
}
const subscriptionsEnabled = JSON.parse("{{ SUBSCRIPTION_ENABLED }}");
const subscriptionUrl = subscriptionsEnabled
? httpUrlToWebSockeUrl(fetchURL)
: null;
const fetcher = GraphiQL.createFetcher({
url: fetchURL,
headers: headers,
subscriptionUrl,
});
const explorerPlugin = GraphiQLPluginExplorer.explorerPlugin();
const root = ReactDOM.createRoot(document.getElementById("graphiql"));
root.render(
React.createElement(GraphiQL, {
fetcher: fetcher,
defaultEditorToolsVisibility: true,
plugins: [explorerPlugin],
inputValueDeprecation: true,
}),
);
</script> </script>
<script
type="text/javascript"
src="{% static 'graphiql.js' %}"
onerror="window.location='{% url 'media_failure' %}?filename=graphiql.js'">
</script>
<script src="{% static 'graphene_django/graphiql.js' %}"></script>
</body> </body>
</html> </html>

View File

@ -127,11 +127,10 @@ class ContactAssignmentFilterSet(NetBoxModelFilterSet):
to_field_name='slug', to_field_name='slug',
label=_('Contact role (slug)'), label=_('Contact role (slug)'),
) )
tag = TagFilter()
class Meta: class Meta:
model = ContactAssignment model = ContactAssignment
fields = ('id', 'object_type_id', 'object_id', 'priority', 'tag') fields = ('id', 'object_type_id', 'object_id', 'priority')
def search(self, queryset, name, value): def search(self, queryset, name, value):
if not value.strip(): if not value.strip():

View File

@ -0,0 +1,49 @@
import strawberry_django
from netbox.graphql.filter_mixins import autotype_decorator, BaseFilterMixin
from tenancy import filtersets, models
__all__ = (
'TenantFilter',
'TenantGroupFilter',
'ContactFilter',
'ContactRoleFilter',
'ContactGroupFilter',
'ContactAssignmentFilter',
)
@strawberry_django.filter(models.Tenant, lookups=True)
@autotype_decorator(filtersets.TenantFilterSet)
class TenantFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.TenantGroup, lookups=True)
@autotype_decorator(filtersets.TenantGroupFilterSet)
class TenantGroupFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.Contact, lookups=True)
@autotype_decorator(filtersets.ContactFilterSet)
class ContactFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.ContactRole, lookups=True)
@autotype_decorator(filtersets.ContactRoleFilterSet)
class ContactRoleFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.ContactGroup, lookups=True)
@autotype_decorator(filtersets.ContactGroupFilterSet)
class ContactGroupFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.ContactAssignment, lookups=True)
@autotype_decorator(filtersets.ContactAssignmentFilterSet)
class ContactAssignmentFilter(BaseFilterMixin):
pass

View File

@ -0,0 +1,17 @@
from typing import Annotated, List
import strawberry
import strawberry_django
__all__ = (
'ContactAssignmentsMixin',
)
@strawberry.type
class ContactAssignmentsMixin:
@strawberry_django.field
def assignments(self) -> List[Annotated["ContactAssignmentType", strawberry.lazy('tenancy.graphql.types')]]:
return self.assignments.all()

View File

@ -1,44 +1,40 @@
import graphene from typing import List
import strawberry
import strawberry_django
from netbox.graphql.fields import ObjectField, ObjectListField
from tenancy import models from tenancy import models
from .types import * from .types import *
from utilities.graphql_optimizer import gql_query_optimizer
class TenancyQuery(graphene.ObjectType): @strawberry.type
tenant = ObjectField(TenantType) class TenancyQuery:
tenant_list = ObjectListField(TenantType) @strawberry.field
def tenant(self, id: int) -> TenantType:
return models.Tenant.objects.get(pk=id)
tenant_list: List[TenantType] = strawberry_django.field()
def resolve_tenant_list(root, info, **kwargs): @strawberry.field
return gql_query_optimizer(models.Tenant.objects.all(), info) def tenant_group(self, id: int) -> TenantGroupType:
return models.TenantGroup.objects.get(pk=id)
tenant_group_list: List[TenantGroupType] = strawberry_django.field()
tenant_group = ObjectField(TenantGroupType) @strawberry.field
tenant_group_list = ObjectListField(TenantGroupType) def contact(self, id: int) -> ContactType:
return models.Contact.objects.get(pk=id)
contact_list: List[ContactType] = strawberry_django.field()
def resolve_tenant_group_list(root, info, **kwargs): @strawberry.field
return gql_query_optimizer(models.TenantGroup.objects.all(), info) def contact_role(self, id: int) -> ContactRoleType:
return models.ContactRole.objects.get(pk=id)
contact_role_list: List[ContactRoleType] = strawberry_django.field()
contact = ObjectField(ContactType) @strawberry.field
contact_list = ObjectListField(ContactType) def contact_group(self, id: int) -> ContactGroupType:
return models.ContactGroup.objects.get(pk=id)
contact_group_list: List[ContactGroupType] = strawberry_django.field()
def resolve_contact_list(root, info, **kwargs): @strawberry.field
return gql_query_optimizer(models.Contact.objects.all(), info) def contact_assignment(self, id: int) -> ContactAssignmentType:
return models.ContactAssignment.objects.get(pk=id)
contact_role = ObjectField(ContactRoleType) contact_assignment_list: List[ContactAssignmentType] = strawberry_django.field()
contact_role_list = ObjectListField(ContactRoleType)
def resolve_contact_role_list(root, info, **kwargs):
return gql_query_optimizer(models.ContactRole.objects.all(), info)
contact_group = ObjectField(ContactGroupType)
contact_group_list = ObjectListField(ContactGroupType)
def resolve_contact_group_list(root, info, **kwargs):
return gql_query_optimizer(models.ContactGroup.objects.all(), info)
contact_assignment = ObjectField(ContactAssignmentType)
contact_assignment_list = ObjectListField(ContactAssignmentType)
def resolve_contact_assignment_list(root, info, **kwargs):
return gql_query_optimizer(models.ContactAssignment.objects.all(), info)

View File

@ -1,8 +1,13 @@
import graphene from typing import Annotated, List
import strawberry
import strawberry_django
from extras.graphql.mixins import CustomFieldsMixin, TagsMixin from extras.graphql.mixins import CustomFieldsMixin, TagsMixin
from tenancy import filtersets, models
from netbox.graphql.types import BaseObjectType, OrganizationalObjectType, NetBoxObjectType from netbox.graphql.types import BaseObjectType, OrganizationalObjectType, NetBoxObjectType
from tenancy import models
from .mixins import ContactAssignmentsMixin
from .filters import *
__all__ = ( __all__ = (
'ContactAssignmentType', 'ContactAssignmentType',
@ -14,64 +19,169 @@ __all__ = (
) )
class ContactAssignmentsMixin:
assignments = graphene.List('tenancy.graphql.types.ContactAssignmentType')
def resolve_assignments(self, info):
return self.assignments.restrict(info.context.user, 'view')
# #
# Tenants # Tenants
# #
@strawberry_django.type(
models.Tenant,
fields='__all__',
filters=TenantFilter
)
class TenantType(NetBoxObjectType): class TenantType(NetBoxObjectType):
group: Annotated["TenantGroupType", strawberry.lazy('tenancy.graphql.types')] | None
class Meta: @strawberry_django.field
model = models.Tenant def asns(self) -> List[Annotated["ASNType", strawberry.lazy('ipam.graphql.types')]]:
fields = '__all__' return self.asns.all()
filterset_class = filtersets.TenantFilterSet
@strawberry_django.field
def circuits(self) -> List[Annotated["CircuitType", strawberry.lazy('circuits.graphql.types')]]:
return self.circuits.all()
@strawberry_django.field
def sites(self) -> List[Annotated["SiteType", strawberry.lazy('dcim.graphql.types')]]:
return self.sites.all()
@strawberry_django.field
def vlans(self) -> List[Annotated["VLANType", strawberry.lazy('ipam.graphql.types')]]:
return self.vlans.all()
@strawberry_django.field
def wireless_lans(self) -> List[Annotated["WirelessLANType", strawberry.lazy('wireless.graphql.types')]]:
return self.wireless_lans.all()
@strawberry_django.field
def route_targets(self) -> List[Annotated["RouteTargetType", strawberry.lazy('ipam.graphql.types')]]:
return self.route_targets.all()
@strawberry_django.field
def locations(self) -> List[Annotated["LocationType", strawberry.lazy('dcim.graphql.types')]]:
return self.locations.all()
@strawberry_django.field
def ip_ranges(self) -> List[Annotated["IPRangeType", strawberry.lazy('ipam.graphql.types')]]:
return self.ip_ranges.all()
@strawberry_django.field
def rackreservations(self) -> List[Annotated["RackReservationType", strawberry.lazy('dcim.graphql.types')]]:
return self.rackreservations.all()
@strawberry_django.field
def racks(self) -> List[Annotated["RackType", strawberry.lazy('dcim.graphql.types')]]:
return self.racks.all()
@strawberry_django.field
def vdcs(self) -> List[Annotated["VirtualDeviceContextType", strawberry.lazy('dcim.graphql.types')]]:
return self.vdcs.all()
@strawberry_django.field
def prefixes(self) -> List[Annotated["PrefixType", strawberry.lazy('ipam.graphql.types')]]:
return self.prefixes.all()
@strawberry_django.field
def cables(self) -> List[Annotated["CableType", strawberry.lazy('dcim.graphql.types')]]:
return self.cables.all()
@strawberry_django.field
def virtual_machines(self) -> List[Annotated["VirtualMachineType", strawberry.lazy('virtualization.graphql.types')]]:
return self.virtual_machines.all()
@strawberry_django.field
def vrfs(self) -> List[Annotated["VRFType", strawberry.lazy('ipam.graphql.types')]]:
return self.vrfs.all()
@strawberry_django.field
def asn_ranges(self) -> List[Annotated["ASNRangeType", strawberry.lazy('ipam.graphql.types')]]:
return self.asn_ranges.all()
@strawberry_django.field
def wireless_links(self) -> List[Annotated["WirelessLinkType", strawberry.lazy('wireless.graphql.types')]]:
return self.wireless_links.all()
@strawberry_django.field
def aggregates(self) -> List[Annotated["AggregateType", strawberry.lazy('ipam.graphql.types')]]:
return self.aggregates.all()
@strawberry_django.field
def power_feeds(self) -> List[Annotated["PowerFeedType", strawberry.lazy('dcim.graphql.types')]]:
return self.power_feeds.all()
@strawberry_django.field
def devices(self) -> List[Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')]]:
return self.devices.all()
@strawberry_django.field
def tunnels(self) -> List[Annotated["TunnelType", strawberry.lazy('vpn.graphql.types')]]:
return self.tunnels.all()
@strawberry_django.field
def ip_addresses(self) -> List[Annotated["IPAddressType", strawberry.lazy('ipam.graphql.types')]]:
return self.ip_addresses.all()
@strawberry_django.field
def clusters(self) -> List[Annotated["ClusterType", strawberry.lazy('virtualization.graphql.types')]]:
return self.clusters.all()
@strawberry_django.field
def l2vpns(self) -> List[Annotated["L2VPNType", strawberry.lazy('vpn.graphql.types')]]:
return self.l2vpns.all()
@strawberry_django.type(
models.TenantGroup,
fields='__all__',
filters=TenantGroupFilter
)
class TenantGroupType(OrganizationalObjectType): class TenantGroupType(OrganizationalObjectType):
parent: Annotated["TenantGroupType", strawberry.lazy('tenancy.graphql.types')] | None
class Meta: @strawberry_django.field
model = models.TenantGroup def tenants(self) -> List[TenantType]:
fields = '__all__' return self.tenants.all()
filterset_class = filtersets.TenantGroupFilterSet
# #
# Contacts # Contacts
# #
@strawberry_django.type(
models.Contact,
fields='__all__',
filters=ContactFilter
)
class ContactType(ContactAssignmentsMixin, NetBoxObjectType): class ContactType(ContactAssignmentsMixin, NetBoxObjectType):
group: Annotated["ContactGroupType", strawberry.lazy('tenancy.graphql.types')] | None
class Meta:
model = models.Contact
fields = '__all__'
filterset_class = filtersets.ContactFilterSet
@strawberry_django.type(
models.ContactRole,
fields='__all__',
filters=ContactRoleFilter
)
class ContactRoleType(ContactAssignmentsMixin, OrganizationalObjectType): class ContactRoleType(ContactAssignmentsMixin, OrganizationalObjectType):
pass
class Meta:
model = models.ContactRole
fields = '__all__'
filterset_class = filtersets.ContactRoleFilterSet
@strawberry_django.type(
models.ContactGroup,
fields='__all__',
filters=ContactGroupFilter
)
class ContactGroupType(OrganizationalObjectType): class ContactGroupType(OrganizationalObjectType):
parent: Annotated["ContactGroupType", strawberry.lazy('tenancy.graphql.types')] | None
class Meta: @strawberry_django.field
model = models.ContactGroup def contacts(self) -> List[ContactType]:
fields = '__all__' return self.contacts.all()
filterset_class = filtersets.ContactGroupFilterSet
@strawberry_django.type(
models.ContactAssignment,
fields='__all__',
filters=ContactAssignmentFilter
)
class ContactAssignmentType(CustomFieldsMixin, TagsMixin, BaseObjectType): class ContactAssignmentType(CustomFieldsMixin, TagsMixin, BaseObjectType):
object_type: Annotated["ContentTypeType", strawberry.lazy('netbox.graphql.types')] | None
class Meta: contact: Annotated["ContactType", strawberry.lazy('tenancy.graphql.types')] | None
model = models.ContactAssignment role: Annotated["ContactRoleType", strawberry.lazy('tenancy.graphql.types')] | None
fields = '__all__'
filterset_class = filtersets.ContactAssignmentFilterSet

View File

@ -1,4 +1,5 @@
import django_filters import django_filters
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.db.models import Q from django.db.models import Q
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _

View File

@ -0,0 +1,23 @@
import strawberry
import strawberry_django
from django.contrib.auth import get_user_model
from users import filtersets, models
from netbox.graphql.filter_mixins import autotype_decorator, BaseFilterMixin
__all__ = (
'GroupFilter',
'UserFilter',
)
@strawberry_django.filter(models.Group, lookups=True)
@autotype_decorator(filtersets.GroupFilterSet)
class GroupFilter(BaseFilterMixin):
pass
@strawberry_django.filter(get_user_model(), lookups=True)
@autotype_decorator(filtersets.UserFilterSet)
class UserFilter(BaseFilterMixin):
pass

View File

@ -1,21 +1,21 @@
import graphene from typing import List
from django.contrib.auth import get_user_model import strawberry
import strawberry_django
from netbox.graphql.fields import ObjectField, ObjectListField from django.contrib.auth import get_user_model
from users.models import Group from django.contrib.auth.models import Group
from utilities.graphql_optimizer import gql_query_optimizer from users import models
from .types import * from .types import *
class UsersQuery(graphene.ObjectType): @strawberry.type
group = ObjectField(GroupType) class UsersQuery:
group_list = ObjectListField(GroupType) @strawberry.field
def group(self, id: int) -> GroupType:
return models.Group.objects.get(pk=id)
group_list: List[GroupType] = strawberry_django.field()
def resolve_group_list(root, info, **kwargs): @strawberry.field
return gql_query_optimizer(Group.objects.all(), info) def user(self, id: int) -> UserType:
return models.User.objects.get(pk=id)
user = ObjectField(UserType) user_list: List[UserType] = strawberry_django.field()
user_list = ObjectListField(UserType)
def resolve_user_list(root, info, **kwargs):
return gql_query_optimizer(get_user_model().objects.all(), info)

View File

@ -1,9 +1,14 @@
from django.contrib.auth import get_user_model from typing import List
from graphene_django import DjangoObjectType
import strawberry
import strawberry_django
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Group
from strawberry import auto
from users import filtersets from users import filtersets
from users.models import Group from users.models import Group
from utilities.querysets import RestrictedQuerySet from utilities.querysets import RestrictedQuerySet
from .filters import *
__all__ = ( __all__ = (
'GroupType', 'GroupType',
@ -11,28 +16,24 @@ __all__ = (
) )
class GroupType(DjangoObjectType): @strawberry_django.type(
Group,
class Meta: fields=['id', 'name'],
model = Group filters=GroupFilter
fields = ('id', 'name')
filterset_class = filtersets.GroupFilterSet
@classmethod
def get_queryset(cls, queryset, info):
return RestrictedQuerySet(model=Group).restrict(info.context.user, 'view')
class UserType(DjangoObjectType):
class Meta:
model = get_user_model()
fields = (
'id', 'username', 'password', 'first_name', 'last_name', 'email', 'is_staff', 'is_active', 'date_joined',
'groups',
) )
filterset_class = filtersets.UserFilterSet class GroupType:
pass
@classmethod
def get_queryset(cls, queryset, info): @strawberry_django.type(
return RestrictedQuerySet(model=get_user_model()).restrict(info.context.user, 'view') get_user_model(),
fields=[
'id', 'username', 'password', 'first_name', 'last_name', 'email', 'is_staff',
'is_active', 'date_joined', 'groups',
],
filters=UserFilter
)
class UserType:
@strawberry_django.field
def groups(self) -> List[GroupType]:
return self.groups.all()

View File

@ -1,252 +0,0 @@
import functools
from django.core.exceptions import FieldDoesNotExist
from django.db.models import ForeignKey
from django.db.models.constants import LOOKUP_SEP
from django.db.models.fields.reverse_related import ManyToOneRel
from graphene import InputObjectType
from graphene.types.generic import GenericScalar
from graphene.types.resolver import default_resolver
from graphene_django import DjangoObjectType
from graphql import GraphQLResolveInfo, GraphQLSchema
from graphql.execution.execute import get_field_def
from graphql.language.ast import FragmentSpreadNode, InlineFragmentNode, VariableNode
from graphql.pyutils import Path
from graphql.type.definition import GraphQLInterfaceType, GraphQLUnionType
__all__ = (
'gql_query_optimizer',
)
def gql_query_optimizer(queryset, info, **options):
return QueryOptimizer(info).optimize(queryset)
class QueryOptimizer(object):
def __init__(self, info, **options):
self.root_info = info
def optimize(self, queryset):
info = self.root_info
field_def = get_field_def(info.schema, info.parent_type, info.field_nodes[0])
field_names = self._optimize_gql_selections(
self._get_type(field_def),
info.field_nodes[0],
)
qs = queryset.prefetch_related(*field_names)
return qs
def _get_type(self, field_def):
a_type = field_def.type
while hasattr(a_type, "of_type"):
a_type = a_type.of_type
return a_type
def _get_graphql_schema(self, schema):
if isinstance(schema, GraphQLSchema):
return schema
else:
return schema.graphql_schema
def _get_possible_types(self, graphql_type):
if isinstance(graphql_type, (GraphQLInterfaceType, GraphQLUnionType)):
graphql_schema = self._get_graphql_schema(self.root_info.schema)
return graphql_schema.get_possible_types(graphql_type)
else:
return (graphql_type,)
def _get_base_model(self, graphql_types):
models = tuple(t.graphene_type._meta.model for t in graphql_types)
for model in models:
if all(issubclass(m, model) for m in models):
return model
return None
def handle_inline_fragment(self, selection, schema, possible_types, field_names):
fragment_type_name = selection.type_condition.name.value
graphql_schema = self._get_graphql_schema(schema)
fragment_type = graphql_schema.get_type(fragment_type_name)
fragment_possible_types = self._get_possible_types(fragment_type)
for fragment_possible_type in fragment_possible_types:
fragment_model = fragment_possible_type.graphene_type._meta.model
parent_model = self._get_base_model(possible_types)
if not parent_model:
continue
path_from_parent = fragment_model._meta.get_path_from_parent(parent_model)
select_related_name = LOOKUP_SEP.join(p.join_field.name for p in path_from_parent)
if not select_related_name:
continue
sub_field_names = self._optimize_gql_selections(
fragment_possible_type,
selection,
)
field_names.append(select_related_name)
return
def handle_fragment_spread(self, field_names, name, field_type):
fragment = self.root_info.fragments[name]
sub_field_names = self._optimize_gql_selections(
field_type,
fragment,
)
def _optimize_gql_selections(self, field_type, field_ast):
field_names = []
selection_set = field_ast.selection_set
if not selection_set:
return field_names
optimized_fields_by_model = {}
schema = self.root_info.schema
graphql_schema = self._get_graphql_schema(schema)
graphql_type = graphql_schema.get_type(field_type.name)
possible_types = self._get_possible_types(graphql_type)
for selection in selection_set.selections:
if isinstance(selection, InlineFragmentNode):
self.handle_inline_fragment(selection, schema, possible_types, field_names)
else:
name = selection.name.value
if isinstance(selection, FragmentSpreadNode):
self.handle_fragment_spread(field_names, name, field_type)
else:
for possible_type in possible_types:
selection_field_def = possible_type.fields.get(name)
if not selection_field_def:
continue
graphene_type = possible_type.graphene_type
model = getattr(graphene_type._meta, "model", None)
if model and name not in optimized_fields_by_model:
field_model = optimized_fields_by_model[name] = model
if field_model == model:
self._optimize_field(
field_names,
model,
selection,
selection_field_def,
possible_type,
)
return field_names
def _get_field_info(self, field_names, model, selection, field_def):
name = None
model_field = None
name = self._get_name_from_resolver(field_def.resolve)
if not name and callable(field_def.resolve) and not isinstance(field_def.resolve, functools.partial):
name = selection.name.value
if name:
model_field = self._get_model_field_from_name(model, name)
return (name, model_field)
def _optimize_field(self, field_names, model, selection, field_def, parent_type):
name, model_field = self._get_field_info(field_names, model, selection, field_def)
if model_field:
self._optimize_field_by_name(field_names, model, selection, field_def, name, model_field)
return
def _optimize_field_by_name(self, field_names, model, selection, field_def, name, model_field):
if model_field.many_to_one or model_field.one_to_one:
sub_field_names = self._optimize_gql_selections(
self._get_type(field_def),
selection,
)
if name not in field_names:
field_names.append(name)
for field in sub_field_names:
prefetch_key = f"{name}__{field}"
if prefetch_key not in field_names:
field_names.append(prefetch_key)
if model_field.one_to_many or model_field.many_to_many:
sub_field_names = self._optimize_gql_selections(
self._get_type(field_def),
selection,
)
if isinstance(model_field, ManyToOneRel):
sub_field_names.append(model_field.field.name)
field_names.append(name)
for field in sub_field_names:
prefetch_key = f"{name}__{field}"
if prefetch_key not in field_names:
field_names.append(prefetch_key)
return
def _get_optimization_hints(self, resolver):
return getattr(resolver, "optimization_hints", None)
def _get_value(self, info, value):
if isinstance(value, VariableNode):
var_name = value.name.value
value = info.variable_values.get(var_name)
return value
elif isinstance(value, InputObjectType):
return value.__dict__
else:
return GenericScalar.parse_literal(value)
def _get_name_from_resolver(self, resolver):
optimization_hints = self._get_optimization_hints(resolver)
if optimization_hints:
name_fn = optimization_hints.model_field
if name_fn:
return name_fn()
if self._is_resolver_for_id_field(resolver):
return "id"
elif isinstance(resolver, functools.partial):
resolver_fn = resolver
if resolver_fn.func != default_resolver:
# Some resolvers have the partial function as the second
# argument.
for arg in resolver_fn.args:
if isinstance(arg, (str, functools.partial)):
break
else:
# No suitable instances found, default to first arg
arg = resolver_fn.args[0]
resolver_fn = arg
if isinstance(resolver_fn, functools.partial) and resolver_fn.func == default_resolver:
return resolver_fn.args[0]
if self._is_resolver_for_id_field(resolver_fn):
return "id"
return resolver_fn
def _is_resolver_for_id_field(self, resolver):
resolve_id = DjangoObjectType.resolve_id
return resolver == resolve_id
def _get_model_field_from_name(self, model, name):
try:
return model._meta.get_field(name)
except FieldDoesNotExist:
descriptor = model.__dict__.get(name)
if not descriptor:
return None
return getattr(descriptor, "rel", None) or getattr(descriptor, "related", None) # Django < 1.9
def _is_foreign_key_id(self, model_field, name):
return isinstance(model_field, ForeignKey) and model_field.name != name and model_field.get_attname() == name
def _create_resolve_info(self, field_name, field_asts, return_type, parent_type):
return GraphQLResolveInfo(
field_name,
field_asts,
return_type,
parent_type,
Path(None, 0, None),
schema=self.root_info.schema,
fragments=self.root_info.fragments,
root_value=self.root_info.root_value,
operation=self.root_info.operation,
variable_values=self.root_info.variable_values,
context=self.root_info.context,
is_awaitable=self.root_info.is_awaitable,
)

View File

@ -1,5 +1,6 @@
__all__ = ( __all__ = (
'title', 'title',
'trailing_slash',
) )
@ -8,3 +9,10 @@ def title(value):
Improved implementation of str.title(); retains all existing uppercase letters. Improved implementation of str.title(); retains all existing uppercase letters.
""" """
return ' '.join([w[0].upper() + w[1:] for w in str(value).split()]) return ' '.join([w[0].upper() + w[1:] for w in str(value).split()])
def trailing_slash(value):
"""
Remove a leading slash (if any) and include a trailing slash, except for empty strings.
"""
return f'{value.strip("/")}/' if value else ''

View File

@ -1,12 +1,12 @@
import inspect import inspect
import json import json
import strawberry_django
from django.conf import settings from django.conf import settings
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.urls import reverse from django.urls import reverse
from django.test import override_settings from django.test import override_settings
from graphene.types import Dynamic as GQLDynamic, List as GQLList, Union as GQLUnion, String as GQLString, NonNull as GQLNonNull
from rest_framework import status from rest_framework import status
from rest_framework.test import APIClient from rest_framework.test import APIClient
@ -19,7 +19,10 @@ from .base import ModelTestCase
from .utils import disable_warnings from .utils import disable_warnings
from ipam.graphql.types import IPAddressFamilyType from ipam.graphql.types import IPAddressFamilyType
from strawberry.field import StrawberryField
from strawberry.lazy_type import LazyType
from strawberry.type import StrawberryList, StrawberryOptional
from strawberry.union import StrawberryUnion
__all__ = ( __all__ = (
'APITestCase', 'APITestCase',
@ -447,34 +450,34 @@ class APIViewTestCases:
# Compile list of fields to include # Compile list of fields to include
fields_string = '' fields_string = ''
for field_name, field in type_class._meta.fields.items():
is_string_array = False
if type(field.type) is GQLList:
if field.type.of_type is GQLString:
is_string_array = True
elif type(field.type.of_type) is GQLNonNull and field.type.of_type.of_type is GQLString:
is_string_array = True
if type(field) is GQLDynamic: file_fields = (strawberry_django.fields.types.DjangoFileType, strawberry_django.fields.types.DjangoImageType)
# Dynamic fields must specify a subselection for field in type_class.__strawberry_definition__.fields:
fields_string += f'{field_name} {{ id }}\n' if (
# TODO: Improve field detection logic to avoid nested ArrayFields field.type in file_fields or (
elif field_name == 'extra_choices': type(field.type) is StrawberryOptional and field.type.of_type in file_fields
)
):
# image / file fields nullable or not...
fields_string += f'{field.name} {{ name }}\n'
elif type(field.type) is StrawberryList and type(field.type.of_type) is LazyType:
# List of related objects (queryset)
fields_string += f'{field.name} {{ id }}\n'
elif type(field.type) is StrawberryList and type(field.type.of_type) is StrawberryUnion:
# this would require a fragment query
continue continue
elif inspect.isclass(field.type) and issubclass(field.type, GQLUnion): elif type(field.type) is StrawberryUnion:
# Union types dont' have an id or consistent values # this would require a fragment query
continue continue
elif type(field.type) is GQLList and inspect.isclass(field.type.of_type) and issubclass(field.type.of_type, GQLUnion): elif type(field.type) is StrawberryOptional and type(field.type.of_type) is LazyType:
# Union types dont' have an id or consistent values fields_string += f'{field.name} {{ id }}\n'
continue elif hasattr(field, 'is_relation') and field.is_relation:
elif type(field.type) is GQLList and not is_string_array: # Note: StrawberryField types do not have is_relation
# TODO: Come up with something more elegant fields_string += f'{field.name} {{ id }}\n'
# Temporary hack to support automated testing of reverse generic relations
fields_string += f'{field_name} {{ id }}\n'
elif inspect.isclass(field.type) and issubclass(field.type, IPAddressFamilyType): elif inspect.isclass(field.type) and issubclass(field.type, IPAddressFamilyType):
fields_string += f'{field_name} {{ value, label }}\n' fields_string += f'{field.name} {{ value, label }}\n'
else: else:
fields_string += f'{field_name}\n' fields_string += f'{field.name}\n'
query = f""" query = f"""
{{ {{
@ -496,7 +499,10 @@ class APIViewTestCases:
# Non-authenticated requests should fail # Non-authenticated requests should fail
with disable_warnings('django.request'): with disable_warnings('django.request'):
self.assertHttpStatus(self.client.post(url, data={'query': query}), status.HTTP_403_FORBIDDEN) header = {
'HTTP_ACCEPT': 'application/json',
}
self.assertHttpStatus(self.client.post(url, data={'query': query}, format="json", **header), status.HTTP_403_FORBIDDEN)
# Add object-level permission # Add object-level permission
obj_perm = ObjectPermission( obj_perm = ObjectPermission(
@ -507,7 +513,7 @@ class APIViewTestCases:
obj_perm.users.add(self.user) obj_perm.users.add(self.user)
obj_perm.object_types.add(ObjectType.objects.get_for_model(self.model)) obj_perm.object_types.add(ObjectType.objects.get_for_model(self.model))
response = self.client.post(url, data={'query': query}, **self.header) response = self.client.post(url, data={'query': query}, format="json", **self.header)
self.assertHttpStatus(response, status.HTTP_200_OK) self.assertHttpStatus(response, status.HTTP_200_OK)
data = json.loads(response.content) data = json.loads(response.content)
self.assertNotIn('errors', data) self.assertNotIn('errors', data)
@ -521,7 +527,10 @@ class APIViewTestCases:
# Non-authenticated requests should fail # Non-authenticated requests should fail
with disable_warnings('django.request'): with disable_warnings('django.request'):
self.assertHttpStatus(self.client.post(url, data={'query': query}), status.HTTP_403_FORBIDDEN) header = {
'HTTP_ACCEPT': 'application/json',
}
self.assertHttpStatus(self.client.post(url, data={'query': query}, format="json", **header), status.HTTP_403_FORBIDDEN)
# Add object-level permission # Add object-level permission
obj_perm = ObjectPermission( obj_perm = ObjectPermission(
@ -532,7 +541,7 @@ class APIViewTestCases:
obj_perm.users.add(self.user) obj_perm.users.add(self.user)
obj_perm.object_types.add(ObjectType.objects.get_for_model(self.model)) obj_perm.object_types.add(ObjectType.objects.get_for_model(self.model))
response = self.client.post(url, data={'query': query}, **self.header) response = self.client.post(url, data={'query': query}, format="json", **self.header)
self.assertHttpStatus(response, status.HTTP_200_OK) self.assertHttpStatus(response, status.HTTP_200_OK)
data = json.loads(response.content) data = json.loads(response.content)
self.assertNotIn('errors', data) self.assertNotIn('errors', data)

View File

@ -0,0 +1,49 @@
import strawberry_django
from netbox.graphql.filter_mixins import autotype_decorator, BaseFilterMixin
from virtualization import filtersets, models
__all__ = (
'ClusterFilter',
'ClusterGroupFilter',
'ClusterTypeFilter',
'VirtualMachineFilter',
'VMInterfaceFilter',
'VirtualDiskFilter',
)
@strawberry_django.filter(models.Cluster, lookups=True)
@autotype_decorator(filtersets.ClusterFilterSet)
class ClusterFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.ClusterGroup, lookups=True)
@autotype_decorator(filtersets.ClusterGroupFilterSet)
class ClusterGroupFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.ClusterType, lookups=True)
@autotype_decorator(filtersets.ClusterTypeFilterSet)
class ClusterTypeFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.VirtualMachine, lookups=True)
@autotype_decorator(filtersets.VirtualMachineFilterSet)
class VirtualMachineFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.VMInterface, lookups=True)
@autotype_decorator(filtersets.VMInterfaceFilterSet)
class VMInterfaceFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.VirtualDisk, lookups=True)
@autotype_decorator(filtersets.VirtualDiskFilterSet)
class VirtualDiskFilter(BaseFilterMixin):
pass

View File

@ -1,44 +1,40 @@
import graphene from typing import List
import strawberry
import strawberry_django
from netbox.graphql.fields import ObjectField, ObjectListField
from .types import *
from utilities.graphql_optimizer import gql_query_optimizer
from virtualization import models from virtualization import models
from .types import *
class VirtualizationQuery(graphene.ObjectType): @strawberry.type
cluster = ObjectField(ClusterType) class VirtualizationQuery:
cluster_list = ObjectListField(ClusterType) @strawberry.field
def cluster(self, id: int) -> ClusterType:
return models.Cluster.objects.get(pk=id)
cluster_list: List[ClusterType] = strawberry_django.field()
def resolve_cluster_list(root, info, **kwargs): @strawberry.field
return gql_query_optimizer(models.Cluster.objects.all(), info) def cluster_group(self, id: int) -> ClusterGroupType:
return models.ClusterGroup.objects.get(pk=id)
cluster_group_list: List[ClusterGroupType] = strawberry_django.field()
cluster_group = ObjectField(ClusterGroupType) @strawberry.field
cluster_group_list = ObjectListField(ClusterGroupType) def cluster_type(self, id: int) -> ClusterTypeType:
return models.ClusterType.objects.get(pk=id)
cluster_type_list: List[ClusterTypeType] = strawberry_django.field()
def resolve_cluster_group_list(root, info, **kwargs): @strawberry.field
return gql_query_optimizer(models.ClusterGroup.objects.all(), info) def virtual_machine(self, id: int) -> VirtualMachineType:
return models.VirtualMachine.objects.get(pk=id)
virtual_machine_list: List[VirtualMachineType] = strawberry_django.field()
cluster_type = ObjectField(ClusterTypeType) @strawberry.field
cluster_type_list = ObjectListField(ClusterTypeType) def vm_interface(self, id: int) -> VMInterfaceType:
return models.VMInterface.objects.get(pk=id)
vm_interface_list: List[VMInterfaceType] = strawberry_django.field()
def resolve_cluster_type_list(root, info, **kwargs): @strawberry.field
return gql_query_optimizer(models.ClusterType.objects.all(), info) def virtual_disk(self, id: int) -> VirtualDiskType:
return models.VirtualDisk.objects.get(pk=id)
virtual_machine = ObjectField(VirtualMachineType) virtual_disk_list: List[VirtualDiskType] = strawberry_django.field()
virtual_machine_list = ObjectListField(VirtualMachineType)
def resolve_virtual_machine_list(root, info, **kwargs):
return gql_query_optimizer(models.VirtualMachine.objects.all(), info)
vm_interface = ObjectField(VMInterfaceType)
vm_interface_list = ObjectListField(VMInterfaceType)
def resolve_vm_interface_list(root, info, **kwargs):
return gql_query_optimizer(models.VMInterface.objects.all(), info)
virtual_disk = ObjectField(VirtualDiskType)
virtual_disk_list = ObjectListField(VirtualDiskType)
def resolve_virtual_disk_list(root, info, **kwargs):
return gql_query_optimizer(models.VirtualDisk.objects.all(), info)

View File

@ -1,8 +1,14 @@
from dcim.graphql.types import ComponentObjectType from typing import Annotated, List
import strawberry
import strawberry_django
from extras.graphql.mixins import ConfigContextMixin, ContactsMixin from extras.graphql.mixins import ConfigContextMixin, ContactsMixin
from ipam.graphql.mixins import IPAddressesMixin, VLANGroupsMixin 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 OrganizationalObjectType, NetBoxObjectType
from virtualization import filtersets, models from virtualization import models
from .filters import *
__all__ = ( __all__ = (
'ClusterType', 'ClusterType',
@ -14,55 +20,121 @@ __all__ = (
) )
@strawberry.type
class ComponentType(NetBoxObjectType):
"""
Base type for device/VM components
"""
_name: str
virtual_machine: Annotated["VirtualMachineType", strawberry.lazy('virtualization.graphql.types')]
@strawberry_django.type(
models.Cluster,
fields='__all__',
filters=ClusterFilter
)
class ClusterType(VLANGroupsMixin, NetBoxObjectType): class ClusterType(VLANGroupsMixin, NetBoxObjectType):
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
site: Annotated["SiteType", strawberry.lazy('dcim.graphql.types')] | None
class Meta: @strawberry_django.field
model = models.Cluster def virtual_machines(self) -> List[Annotated["VirtualMachineType", strawberry.lazy('virtualization.graphql.types')]]:
fields = '__all__' return self.virtual_machines.all()
filterset_class = filtersets.ClusterFilterSet
@strawberry_django.field
def devices(self) -> List[Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')]]:
return self.devices.all()
@strawberry_django.type(
models.ClusterGroup,
fields='__all__',
filters=ClusterGroupFilter
)
class ClusterGroupType(VLANGroupsMixin, OrganizationalObjectType): class ClusterGroupType(VLANGroupsMixin, OrganizationalObjectType):
class Meta: @strawberry_django.field
model = models.ClusterGroup def clusters(self) -> List[Annotated["ClusterType", strawberry.lazy('virtualization.graphql.types')]]:
fields = '__all__' return self.clusters.all()
filterset_class = filtersets.ClusterGroupFilterSet
@strawberry_django.type(
models.ClusterType,
fields='__all__',
filters=ClusterTypeFilter
)
class ClusterTypeType(OrganizationalObjectType): class ClusterTypeType(OrganizationalObjectType):
class Meta: @strawberry_django.field
model = models.ClusterType def clusters(self) -> List[ClusterType]:
fields = '__all__' return self.clusters.all()
filterset_class = filtersets.ClusterTypeFilterSet
@strawberry_django.type(
models.VirtualMachine,
fields='__all__',
filters=VirtualMachineFilter
)
class VirtualMachineType(ConfigContextMixin, ContactsMixin, NetBoxObjectType): class VirtualMachineType(ConfigContextMixin, ContactsMixin, NetBoxObjectType):
_name: str
interface_count: BigInt
virtual_disk_count: BigInt
interface_count: BigInt
config_template: Annotated["ConfigTemplateType", strawberry.lazy('extras.graphql.types')] | None
site: Annotated["SiteType", strawberry.lazy('dcim.graphql.types')] | None
cluster: Annotated["ClusterType", strawberry.lazy('virtualization.graphql.types')] | None
device: Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')] | None
tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None
platform: Annotated["PlatformType", strawberry.lazy('dcim.graphql.types')] | None
role: Annotated["DeviceRoleType", 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
class Meta: @strawberry_django.field
model = models.VirtualMachine def interfaces(self) -> List[Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')]]:
fields = '__all__' return self.interfaces.all()
filterset_class = filtersets.VirtualMachineFilterSet
@strawberry_django.field
def services(self) -> List[Annotated["ServiceType", strawberry.lazy('ipam.graphql.types')]]:
return self.services.all()
@strawberry_django.field
def virtualdisks(self) -> List[Annotated["VirtualDiskType", strawberry.lazy('virtualization.graphql.types')]]:
return self.virtualdisks.all()
class VMInterfaceType(IPAddressesMixin, ComponentObjectType): @strawberry_django.type(
models.VMInterface,
fields='__all__',
filters=VMInterfaceFilter
)
class VMInterfaceType(IPAddressesMixin, ComponentType):
mac_address: str | None
parent: Annotated["VMInterfaceType", strawberry.lazy('virtualization.graphql.types')] | None
bridge: Annotated["VMInterfaceType", strawberry.lazy('virtualization.graphql.types')] | None
untagged_vlan: Annotated["VLANType", strawberry.lazy('ipam.graphql.types')] | None
vrf: Annotated["VRFType", strawberry.lazy('ipam.graphql.types')] | None
class Meta: @strawberry_django.field
model = models.VMInterface def tagged_vlans(self) -> List[Annotated["VLANType", strawberry.lazy('ipam.graphql.types')]]:
fields = '__all__' return self.tagged_vlans.all()
filterset_class = filtersets.VMInterfaceFilterSet
def resolve_mode(self, info): @strawberry_django.field
return self.mode or None def bridge_interfaces(self) -> List[Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')]]:
return self.bridge_interfaces.all()
@strawberry_django.field
def child_interfaces(self) -> List[Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')]]:
return self.child_interfaces.all()
class VirtualDiskType(ComponentObjectType): @strawberry_django.type(
models.VirtualDisk,
class Meta: fields='__all__',
model = models.VirtualDisk filters=VirtualDiskFilter
fields = '__all__' )
filterset_class = filtersets.VirtualDiskFilterSet class VirtualDiskType(ComponentType):
pass
def resolve_mode(self, info):
return self.mode or None

View File

@ -0,0 +1,77 @@
import strawberry_django
from netbox.graphql.filter_mixins import autotype_decorator, BaseFilterMixin
from vpn import filtersets, models
__all__ = (
'TunnelGroupFilter',
'TunnelTerminationFilter',
'TunnelFilter',
'IKEProposalFilter',
'IKEPolicyFilter',
'IPSecProposalFilter',
'IPSecPolicyFilter',
'IPSecProfileFilter',
'L2VPNFilter',
'L2VPNTerminationFilter',
)
@strawberry_django.filter(models.TunnelGroup, lookups=True)
@autotype_decorator(filtersets.TunnelGroupFilterSet)
class TunnelGroupFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.TunnelTermination, lookups=True)
@autotype_decorator(filtersets.TunnelTerminationFilterSet)
class TunnelTerminationFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.Tunnel, lookups=True)
@autotype_decorator(filtersets.TunnelFilterSet)
class TunnelFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.IKEProposal, lookups=True)
@autotype_decorator(filtersets.IKEProposalFilterSet)
class IKEProposalFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.IKEPolicy, lookups=True)
@autotype_decorator(filtersets.IKEPolicyFilterSet)
class IKEPolicyFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.IPSecProposal, lookups=True)
@autotype_decorator(filtersets.IPSecProposalFilterSet)
class IPSecProposalFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.IPSecPolicy, lookups=True)
@autotype_decorator(filtersets.IPSecPolicyFilterSet)
class IPSecPolicyFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.IPSecProfile, lookups=True)
@autotype_decorator(filtersets.IPSecProfileFilterSet)
class IPSecProfileFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.L2VPN, lookups=True)
@autotype_decorator(filtersets.L2VPNFilterSet)
class L2VPNFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.L2VPNTermination, lookups=True)
@autotype_decorator(filtersets.L2VPNTerminationFilterSet)
class L2VPNTerminationFilter(BaseFilterMixin):
pass

View File

@ -1,30 +0,0 @@
import graphene
from dcim.graphql.types import InterfaceType
from dcim.models import Interface
from ipam.graphql.types import VLANType
from ipam.models import VLAN
from virtualization.graphql.types import VMInterfaceType
from virtualization.models import VMInterface
__all__ = (
'L2VPNAssignmentType',
)
class L2VPNAssignmentType(graphene.Union):
class Meta:
types = (
InterfaceType,
VLANType,
VMInterfaceType,
)
@classmethod
def resolve_type(cls, instance, info):
if type(instance) is Interface:
return InterfaceType
if type(instance) is VLAN:
return VLANType
if type(instance) is VMInterface:
return VMInterfaceType

View File

@ -1,69 +1,60 @@
import graphene from typing import List
import strawberry
import strawberry_django
from netbox.graphql.fields import ObjectField, ObjectListField
from utilities.graphql_optimizer import gql_query_optimizer
from vpn import models from vpn import models
from .types import * from .types import *
class VPNQuery(graphene.ObjectType): @strawberry.type
class VPNQuery:
@strawberry.field
def ike_policy(self, id: int) -> IKEPolicyType:
return models.IKEPolicy.objects.get(pk=id)
ike_policy_list: List[IKEPolicyType] = strawberry_django.field()
ike_policy = ObjectField(IKEPolicyType) @strawberry.field
ike_policy_list = ObjectListField(IKEPolicyType) def ike_proposal(self, id: int) -> IKEProposalType:
return models.IKEProposal.objects.get(pk=id)
ike_proposal_list: List[IKEProposalType] = strawberry_django.field()
def resolve_ike_policy_list(root, info, **kwargs): @strawberry.field
return gql_query_optimizer(models.IKEPolicy.objects.all(), info) def ipsec_policy(self, id: int) -> IPSecPolicyType:
return models.IPSecPolicy.objects.get(pk=id)
ipsec_policy_list: List[IPSecPolicyType] = strawberry_django.field()
ike_proposal = ObjectField(IKEProposalType) @strawberry.field
ike_proposal_list = ObjectListField(IKEProposalType) def ipsec_profile(self, id: int) -> IPSecProfileType:
return models.IPSecProfile.objects.get(pk=id)
ipsec_profile_list: List[IPSecProfileType] = strawberry_django.field()
def resolve_ike_proposal_list(root, info, **kwargs): @strawberry.field
return gql_query_optimizer(models.IKEProposal.objects.all(), info) def ipsec_proposal(self, id: int) -> IPSecProposalType:
return models.IPSecProposal.objects.get(pk=id)
ipsec_proposal_list: List[IPSecProposalType] = strawberry_django.field()
ipsec_policy = ObjectField(IPSecPolicyType) @strawberry.field
ipsec_policy_list = ObjectListField(IPSecPolicyType) def l2vpn(self, id: int) -> L2VPNType:
return models.L2VPN.objects.get(pk=id)
l2vpn_list: List[L2VPNType] = strawberry_django.field()
def resolve_ipsec_policy_list(root, info, **kwargs): @strawberry.field
return gql_query_optimizer(models.IPSecPolicy.objects.all(), info) def l2vpn_termination(self, id: int) -> L2VPNTerminationType:
return models.L2VPNTermination.objects.get(pk=id)
l2vpn_termination_list: List[L2VPNTerminationType] = strawberry_django.field()
ipsec_profile = ObjectField(IPSecProfileType) @strawberry.field
ipsec_profile_list = ObjectListField(IPSecProfileType) def tunnel(self, id: int) -> TunnelType:
return models.Tunnel.objects.get(pk=id)
tunnel_list: List[TunnelType] = strawberry_django.field()
def resolve_ipsec_profile_list(root, info, **kwargs): @strawberry.field
return gql_query_optimizer(models.IPSecProfile.objects.all(), info) def tunnel_group(self, id: int) -> TunnelGroupType:
return models.TunnelGroup.objects.get(pk=id)
tunnel_group_list: List[TunnelGroupType] = strawberry_django.field()
ipsec_proposal = ObjectField(IPSecProposalType) @strawberry.field
ipsec_proposal_list = ObjectListField(IPSecProposalType) def tunnel_termination(self, id: int) -> TunnelTerminationType:
return models.TunnelTermination.objects.get(pk=id)
def resolve_ipsec_proposal_list(root, info, **kwargs): tunnel_termination_list: List[TunnelTerminationType] = strawberry_django.field()
return gql_query_optimizer(models.IPSecProposal.objects.all(), info)
l2vpn = ObjectField(L2VPNType)
l2vpn_list = ObjectListField(L2VPNType)
def resolve_l2vpn_list(root, info, **kwargs):
return gql_query_optimizer(models.L2VPN.objects.all(), info)
l2vpn_termination = ObjectField(L2VPNTerminationType)
l2vpn_termination_list = ObjectListField(L2VPNTerminationType)
def resolve_l2vpn_termination_list(root, info, **kwargs):
return gql_query_optimizer(models.L2VPNTermination.objects.all(), info)
tunnel = ObjectField(TunnelType)
tunnel_list = ObjectListField(TunnelType)
def resolve_tunnel_list(root, info, **kwargs):
return gql_query_optimizer(models.Tunnel.objects.all(), info)
tunnel_group = ObjectField(TunnelGroupType)
tunnel_group_list = ObjectListField(TunnelGroupType)
def resolve_tunnel_group_list(root, info, **kwargs):
return gql_query_optimizer(models.TunnelGroup.objects.all(), info)
tunnel_termination = ObjectField(TunnelTerminationType)
tunnel_termination_list = ObjectListField(TunnelTerminationType)
def resolve_tunnel_termination_list(root, info, **kwargs):
return gql_query_optimizer(models.TunnelTermination.objects.all(), info)

View File

@ -1,8 +1,12 @@
import graphene from typing import Annotated, List, Union
import strawberry
import strawberry_django
from extras.graphql.mixins import ContactsMixin, CustomFieldsMixin, TagsMixin from extras.graphql.mixins import ContactsMixin, CustomFieldsMixin, TagsMixin
from netbox.graphql.types import ObjectType, OrganizationalObjectType, NetBoxObjectType from netbox.graphql.types import ObjectType, OrganizationalObjectType, NetBoxObjectType
from vpn import filtersets, models from vpn import models
from .filters import *
__all__ = ( __all__ = (
'IKEPolicyType', 'IKEPolicyType',
@ -18,81 +22,147 @@ __all__ = (
) )
@strawberry_django.type(
models.TunnelGroup,
fields='__all__',
filters=TunnelGroupFilter
)
class TunnelGroupType(OrganizationalObjectType): class TunnelGroupType(OrganizationalObjectType):
class Meta: @strawberry_django.field
model = models.TunnelGroup def tunnels(self) -> List[Annotated["TunnelType", strawberry.lazy('vpn.graphql.types')]]:
fields = '__all__' return self.tunnels.all()
filterset_class = filtersets.TunnelGroupFilterSet
@strawberry_django.type(
models.TunnelTermination,
fields='__all__',
filters=TunnelTerminationFilter
)
class TunnelTerminationType(CustomFieldsMixin, TagsMixin, ObjectType): class TunnelTerminationType(CustomFieldsMixin, TagsMixin, ObjectType):
tunnel: Annotated["TunnelType", strawberry.lazy('vpn.graphql.types')]
class Meta: termination_type: Annotated["ContentTypeType", strawberry.lazy('netbox.graphql.types')] | None
model = models.TunnelTermination outside_ip: Annotated["IPAddressType", strawberry.lazy('ipam.graphql.types')] | None
fields = '__all__'
filterset_class = filtersets.TunnelTerminationFilterSet
@strawberry_django.type(
models.Tunnel,
fields='__all__',
filters=TunnelFilter
)
class TunnelType(NetBoxObjectType): class TunnelType(NetBoxObjectType):
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
class Meta: @strawberry_django.field
model = models.Tunnel def terminations(self) -> List[Annotated["TunnelTerminationType", strawberry.lazy('vpn.graphql.types')]]:
fields = '__all__' return self.terminations.all()
filterset_class = filtersets.TunnelFilterSet
@strawberry_django.type(
models.IKEProposal,
fields='__all__',
filters=IKEProposalFilter
)
class IKEProposalType(OrganizationalObjectType): class IKEProposalType(OrganizationalObjectType):
class Meta: @strawberry_django.field
model = models.IKEProposal def ike_policies(self) -> List[Annotated["IKEPolicyType", strawberry.lazy('vpn.graphql.types')]]:
fields = '__all__' return self.ike_policies.all()
filterset_class = filtersets.IKEProposalFilterSet
@strawberry_django.type(
models.IKEPolicy,
fields='__all__',
filters=IKEPolicyFilter
)
class IKEPolicyType(OrganizationalObjectType): class IKEPolicyType(OrganizationalObjectType):
class Meta: @strawberry_django.field
model = models.IKEPolicy def proposals(self) -> List[Annotated["IKEProposalType", strawberry.lazy('vpn.graphql.types')]]:
fields = '__all__' return self.proposals.all()
filterset_class = filtersets.IKEPolicyFilterSet
@strawberry_django.field
def ipsec_profiles(self) -> List[Annotated["IPSecProposalType", strawberry.lazy('vpn.graphql.types')]]:
return self.ipsec_profiles.all()
@strawberry_django.type(
models.IPSecProposal,
fields='__all__',
filters=IPSecProposalFilter
)
class IPSecProposalType(OrganizationalObjectType): class IPSecProposalType(OrganizationalObjectType):
class Meta: @strawberry_django.field
model = models.IPSecProposal def ipsec_policies(self) -> List[Annotated["IPSecPolicyType", strawberry.lazy('vpn.graphql.types')]]:
fields = '__all__' return self.ipsec_policies.all()
filterset_class = filtersets.IPSecProposalFilterSet
@strawberry_django.type(
models.IPSecPolicy,
fields='__all__',
filters=IPSecPolicyFilter
)
class IPSecPolicyType(OrganizationalObjectType): class IPSecPolicyType(OrganizationalObjectType):
class Meta: @strawberry_django.field
model = models.IPSecPolicy def proposals(self) -> List[Annotated["IPSecProposalType", strawberry.lazy('vpn.graphql.types')]]:
fields = '__all__' return self.proposals.all()
filterset_class = filtersets.IPSecPolicyFilterSet
@strawberry_django.field
def ipsec_profiles(self) -> List[Annotated["IPSecProfileType", strawberry.lazy('vpn.graphql.types')]]:
return self.ipsec_profiles.all()
@strawberry_django.type(
models.IPSecProfile,
fields='__all__',
filters=IPSecProfileFilter
)
class IPSecProfileType(OrganizationalObjectType): class IPSecProfileType(OrganizationalObjectType):
ike_policy: Annotated["IKEPolicyType", strawberry.lazy('vpn.graphql.types')]
ipsec_policy: Annotated["IPSecPolicyType", strawberry.lazy('vpn.graphql.types')]
class Meta: @strawberry_django.field
model = models.IPSecProfile def tunnels(self) -> List[Annotated["TunnelType", strawberry.lazy('vpn.graphql.types')]]:
fields = '__all__' return self.tunnels.all()
filterset_class = filtersets.IPSecProfileFilterSet
@strawberry_django.type(
models.L2VPN,
fields='__all__',
filters=L2VPNFilter
)
class L2VPNType(ContactsMixin, NetBoxObjectType): class L2VPNType(ContactsMixin, NetBoxObjectType):
class Meta: tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None
model = models.L2VPN
fields = '__all__' @strawberry_django.field
filtersets_class = filtersets.L2VPNFilterSet def export_targets(self) -> List[Annotated["RouteTargetType", strawberry.lazy('ipam.graphql.types')]]:
return self.export_targets.all()
@strawberry_django.field
def terminations(self) -> List[Annotated["L2VPNTerminationType", strawberry.lazy('vpn.graphql.types')]]:
return self.terminations.all()
@strawberry_django.field
def import_targets(self) -> List[Annotated["RouteTargetType", strawberry.lazy('ipam.graphql.types')]]:
return self.import_targets.all()
@strawberry_django.type(
models.L2VPNTermination,
exclude=('assigned_object_type', 'assigned_object_id'),
filters=L2VPNTerminationFilter
)
class L2VPNTerminationType(NetBoxObjectType): class L2VPNTerminationType(NetBoxObjectType):
assigned_object = graphene.Field('vpn.graphql.gfk_mixins.L2VPNAssignmentType') l2vpn: Annotated["L2VPNType", strawberry.lazy('vpn.graphql.types')]
class Meta: @strawberry_django.field
model = models.L2VPNTermination def assigned_object(self) -> Annotated[Union[
exclude = ('assigned_object_type', 'assigned_object_id') Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')],
filtersets_class = filtersets.L2VPNTerminationFilterSet Annotated["VLANType", strawberry.lazy('ipam.graphql.types')],
Annotated["VMInterfaceType", strawberry.lazy('virtualization.graphql.types')],
], strawberry.union("L2VPNAssignmentType")]:
return self.assigned_object

View File

@ -0,0 +1,28 @@
import strawberry_django
from netbox.graphql.filter_mixins import autotype_decorator, BaseFilterMixin
from wireless import filtersets, models
__all__ = (
'WirelessLANGroupFilter',
'WirelessLANFilter',
'WirelessLinkFilter',
)
@strawberry_django.filter(models.WirelessLANGroup, lookups=True)
@autotype_decorator(filtersets.WirelessLANGroupFilterSet)
class WirelessLANGroupFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.WirelessLAN, lookups=True)
@autotype_decorator(filtersets.WirelessLANFilterSet)
class WirelessLANFilter(BaseFilterMixin):
pass
@strawberry_django.filter(models.WirelessLink, lookups=True)
@autotype_decorator(filtersets.WirelessLinkFilterSet)
class WirelessLinkFilter(BaseFilterMixin):
pass

View File

@ -1,26 +1,25 @@
import graphene from typing import List
import strawberry
import strawberry_django
from netbox.graphql.fields import ObjectField, ObjectListField
from .types import *
from utilities.graphql_optimizer import gql_query_optimizer
from wireless import models from wireless import models
from .types import *
class WirelessQuery(graphene.ObjectType): @strawberry.type
wireless_lan = ObjectField(WirelessLANType) class WirelessQuery:
wireless_lan_list = ObjectListField(WirelessLANType) @strawberry.field
def wireless_lan(self, id: int) -> WirelessLANType:
return models.WirelessLAN.objects.get(pk=id)
wireless_lan_list: List[WirelessLANType] = strawberry_django.field()
def resolve_wireless_lan_list(root, info, **kwargs): @strawberry.field
return gql_query_optimizer(models.WirelessLAN.objects.all(), info) def wireless_lan_group(self, id: int) -> WirelessLANGroupType:
return models.WirelessLANGroup.objects.get(pk=id)
wireless_lan_group_list: List[WirelessLANGroupType] = strawberry_django.field()
wireless_lan_group = ObjectField(WirelessLANGroupType) @strawberry.field
wireless_lan_group_list = ObjectListField(WirelessLANGroupType) def wireless_link(self, id: int) -> WirelessLinkType:
return models.WirelessLink.objects.get(pk=id)
def resolve_wireless_lan_group_list(root, info, **kwargs): wireless_link_list: List[WirelessLinkType] = strawberry_django.field()
return gql_query_optimizer(models.WirelessLANGroup.objects.all(), info)
wireless_link = ObjectField(WirelessLinkType)
wireless_link_list = ObjectListField(WirelessLinkType)
def resolve_wireless_link_list(root, info, **kwargs):
return gql_query_optimizer(models.WirelessLink.objects.all(), info)

View File

@ -1,5 +1,11 @@
from wireless import filtersets, models from typing import Annotated, List
import strawberry
import strawberry_django
from netbox.graphql.types import OrganizationalObjectType, NetBoxObjectType from netbox.graphql.types import OrganizationalObjectType, NetBoxObjectType
from wireless import models
from .filters import *
__all__ = ( __all__ = (
'WirelessLANType', 'WirelessLANType',
@ -8,37 +14,42 @@ __all__ = (
) )
@strawberry_django.type(
models.WirelessLANGroup,
fields='__all__',
filters=WirelessLANGroupFilter
)
class WirelessLANGroupType(OrganizationalObjectType): class WirelessLANGroupType(OrganizationalObjectType):
parent: Annotated["WirelessLANGroupType", strawberry.lazy('wireless.graphql.types')] | None
class Meta: @strawberry_django.field
model = models.WirelessLANGroup def wireless_lans(self) -> List[Annotated["WirelessLANType", strawberry.lazy('wireless.graphql.types')]]:
fields = '__all__' return self.wireless_lans.all()
filterset_class = filtersets.WirelessLANGroupFilterSet
@strawberry_django.type(
models.WirelessLAN,
fields='__all__',
filters=WirelessLANFilter
)
class WirelessLANType(NetBoxObjectType): class WirelessLANType(NetBoxObjectType):
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
class Meta: @strawberry_django.field
model = models.WirelessLAN def interfaces(self) -> List[Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')]]:
fields = '__all__' return self.interfaces.all()
filterset_class = filtersets.WirelessLANFilterSet
def resolve_auth_type(self, info):
return self.auth_type or None
def resolve_auth_cipher(self, info):
return self.auth_cipher or None
@strawberry_django.type(
models.WirelessLink,
fields='__all__',
filters=WirelessLinkFilter
)
class WirelessLinkType(NetBoxObjectType): class WirelessLinkType(NetBoxObjectType):
interface_a: Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')]
class Meta: interface_b: Annotated["InterfaceType", strawberry.lazy('dcim.graphql.types')]
model = models.WirelessLink tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None
fields = '__all__' _interface_a_device: Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')] | None
filterset_class = filtersets.WirelessLinkFilterSet _interface_b_device: Annotated["DeviceType", strawberry.lazy('dcim.graphql.types')] | None
def resolve_auth_type(self, info):
return self.auth_type or None
def resolve_auth_cipher(self, info):
return self.auth_cipher or None

View File

@ -2,7 +2,6 @@ Django==5.0.3
django-cors-headers==4.3.1 django-cors-headers==4.3.1
django-debug-toolbar==4.3.0 django-debug-toolbar==4.3.0
django-filter==24.1 django-filter==24.1
django-graphiql-debug-toolbar==0.2.0
django-htmx==1.17.3 django-htmx==1.17.3
django-mptt==0.14.0 django-mptt==0.14.0
django-pglocks==1.0.4 django-pglocks==1.0.4
@ -17,7 +16,6 @@ djangorestframework==3.14.0
drf-spectacular==0.27.1 drf-spectacular==0.27.1
drf-spectacular-sidecar==2024.3.4 drf-spectacular-sidecar==2024.3.4
feedparser==6.0.11 feedparser==6.0.11
graphene-django==3.0.0
Jinja2==3.1.3 Jinja2==3.1.3
Markdown==3.5.2 Markdown==3.5.2
mkdocs-material==9.5.13 mkdocs-material==9.5.13
@ -31,6 +29,8 @@ PyYAML==6.0.1
requests==2.31.0 requests==2.31.0
social-auth-app-django==5.4.0 social-auth-app-django==5.4.0
social-auth-core[openidconnect]==4.5.3 social-auth-core[openidconnect]==4.5.3
strawberry-graphql==0.221.1
strawberry-graphql-django==0.35.1
svgwrite==1.4.3 svgwrite==1.4.3
tablib==3.5.0 tablib==3.5.0
tzdata==2024.1 tzdata==2024.1