diff --git a/contrib/openapi.json b/contrib/openapi.json index 2e0cbaa07..33b5092af 100644 --- a/contrib/openapi.json +++ b/contrib/openapi.json @@ -215569,24 +215569,26 @@ "IntegerRange": { "type": "array", "items": { - "type": "array", - "items": { - "type": "integer" - }, - "minItems": 2, - "maxItems": 2 - } + "type": "integer" + }, + "minItems": 2, + "maxItems": 2, + "example": [ + 10, + 20 + ] }, "IntegerRangeRequest": { "type": "array", "items": { - "type": "array", - "items": { - "type": "integer" - }, - "minItems": 2, - "maxItems": 2 - } + "type": "integer" + }, + "minItems": 2, + "maxItems": 2, + "example": [ + 10, + 20 + ] }, "Interface": { "type": "object", @@ -228986,7 +228988,6 @@ }, "key": { "type": "string", - "writeOnly": true, "maxLength": 40, "minLength": 40 }, @@ -245221,6 +245222,11 @@ "format": "date-time", "nullable": true }, + "key": { + "type": "string", + "maxLength": 40, + "minLength": 40 + }, "write_enabled": { "type": "boolean", "description": "Permit create/update/delete operations using this key" @@ -245367,7 +245373,6 @@ }, "key": { "type": "string", - "writeOnly": true, "maxLength": 40, "minLength": 40 }, diff --git a/docs/plugins/development/filtersets.md b/docs/plugins/development/filtersets.md index e19b3a733..a4b9e1375 100644 --- a/docs/plugins/development/filtersets.md +++ b/docs/plugins/development/filtersets.md @@ -1,6 +1,6 @@ # Filters & Filter Sets -Filter sets define the mechanisms available for filtering or searching through a set of objects in NetBox. For instance, sites can be filtered by their parent region or group, status, facility ID, and so on. The same filter set is used consistently for a model whether the request is made via the UI or REST API. (Note that the GraphQL API uses a separate filter class.) NetBox employs the [django-filters2](https://django-tables2.readthedocs.io/en/latest/) library to define filter sets. +Filter sets define the mechanisms available for filtering or searching through a set of objects in NetBox. For instance, sites can be filtered by their parent region or group, status, facility ID, and so on. The same filter set is used consistently for a model whether the request is made via the UI or REST API. (Note that the GraphQL API uses a separate filter class.) NetBox employs the [django-filter](https://django-filter.readthedocs.io/en/stable/) library to define filter sets. ## FilterSet Classes diff --git a/netbox/circuits/apps.py b/netbox/circuits/apps.py index 4d5f177e2..7b29a1509 100644 --- a/netbox/circuits/apps.py +++ b/netbox/circuits/apps.py @@ -1,5 +1,7 @@ from django.apps import AppConfig +from netbox import denormalized + class CircuitsConfig(AppConfig): name = "circuits" @@ -8,6 +10,16 @@ class CircuitsConfig(AppConfig): def ready(self): from netbox.models.features import register_models from . import signals, search # noqa: F401 + from .models import CircuitTermination # Register models register_models(*self.get_models()) + + denormalized.register(CircuitTermination, '_site', { + '_region': 'region', + '_site_group': 'group', + }) + + denormalized.register(CircuitTermination, '_location', { + '_site': 'site', + }) diff --git a/netbox/core/api/schema.py b/netbox/core/api/schema.py index 0c59da5a1..c61675cf3 100644 --- a/netbox/core/api/schema.py +++ b/netbox/core/api/schema.py @@ -282,18 +282,18 @@ class FixSerializedPKRelatedField(OpenApiSerializerFieldExtension): class FixIntegerRangeSerializerSchema(OpenApiSerializerExtension): target_class = 'netbox.api.fields.IntegerRangeSerializer' + match_subclasses = True def map_serializer(self, auto_schema: 'AutoSchema', direction: Direction) -> _SchemaType: + # One range = two integers; many=True will wrap this in an outer array return { 'type': 'array', 'items': { - 'type': 'array', - 'items': { - 'type': 'integer', - }, - 'minItems': 2, - 'maxItems': 2, + 'type': 'integer', }, + 'minItems': 2, + 'maxItems': 2, + 'example': [10, 20], } diff --git a/netbox/ipam/graphql/types.py b/netbox/ipam/graphql/types.py index e8f98eabe..a07316fe4 100644 --- a/netbox/ipam/graphql/types.py +++ b/netbox/ipam/graphql/types.py @@ -74,7 +74,7 @@ class BaseIPAddressFamilyType: filters=ASNFilter, pagination=True ) -class ASNType(NetBoxObjectType): +class ASNType(NetBoxObjectType, ContactsMixin): asn: BigInt rir: Annotated["RIRType", strawberry.lazy('ipam.graphql.types')] | None tenant: Annotated["TenantType", strawberry.lazy('tenancy.graphql.types')] | None diff --git a/netbox/netbox/api/fields.py b/netbox/netbox/api/fields.py index db5ec184d..7dfd7d7eb 100644 --- a/netbox/netbox/api/fields.py +++ b/netbox/netbox/api/fields.py @@ -169,7 +169,7 @@ class IntegerRangeSerializer(serializers.Serializer): if type(data[0]) is not int or type(data[1]) is not int: raise ValidationError(_("Range boundaries must be defined as integers.")) - return NumericRange(data[0], data[1], bounds='[]') + return NumericRange(data[0], data[1] + 1, bounds='[)') def to_representation(self, instance): return instance.lower, instance.upper - 1 diff --git a/netbox/templates/extras/htmx/script_result.html b/netbox/templates/extras/htmx/script_result.html index ac5d87ef2..3844c7857 100644 --- a/netbox/templates/extras/htmx/script_result.html +++ b/netbox/templates/extras/htmx/script_result.html @@ -44,8 +44,8 @@
+ hx-trigger="load" hx-select=".htmx-container" hx-swap="outerHTML"> + {% endif %} @@ -60,11 +60,12 @@ {% trans "Download" %} + {% copy_content "job_data_output" %} {% endif %} {% if job.data.output %} -{{ job.data.output }}
+ {{ job.data.output }}
{% else %}