Closes #20295: Make cable terminations REST API endpoint read-only (#20394)
Some checks failed
CI / build (20.x, 3.12) (push) Has been cancelled
CI / build (20.x, 3.13) (push) Has been cancelled
CodeQL / Analyze (${{ matrix.language }}) (none, actions) (push) Has been cancelled
CodeQL / Analyze (${{ matrix.language }}) (none, javascript-typescript) (push) Has been cancelled
CodeQL / Analyze (${{ matrix.language }}) (none, python) (push) Has been cancelled

This commit is contained in:
Jeremy Stretch 2025-09-19 13:54:51 -04:00 committed by GitHub
parent f0ae0da1c7
commit 12818f1786
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 41 additions and 394 deletions

View File

@ -21698,191 +21698,6 @@
"description": ""
}
}
},
"post": {
"operationId": "dcim_cable_terminations_create",
"description": "Post a list of cable termination objects.",
"tags": [
"dcim"
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CableTerminationRequest"
}
},
"multipart/form-data": {
"schema": {
"$ref": "#/components/schemas/CableTerminationRequest"
}
}
},
"required": true
},
"security": [
{
"cookieAuth": []
},
{
"tokenAuth": []
}
],
"responses": {
"201": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CableTermination"
}
}
},
"description": ""
}
}
},
"put": {
"operationId": "dcim_cable_terminations_bulk_update",
"description": "Put a list of cable termination objects.",
"tags": [
"dcim"
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/CableTerminationRequest"
}
}
},
"multipart/form-data": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/CableTerminationRequest"
}
}
}
},
"required": true
},
"security": [
{
"cookieAuth": []
},
{
"tokenAuth": []
}
],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/CableTermination"
}
}
}
},
"description": ""
}
}
},
"patch": {
"operationId": "dcim_cable_terminations_bulk_partial_update",
"description": "Patch a list of cable termination objects.",
"tags": [
"dcim"
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/CableTerminationRequest"
}
}
},
"multipart/form-data": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/CableTerminationRequest"
}
}
}
},
"required": true
},
"security": [
{
"cookieAuth": []
},
{
"tokenAuth": []
}
],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/CableTermination"
}
}
}
},
"description": ""
}
}
},
"delete": {
"operationId": "dcim_cable_terminations_bulk_destroy",
"description": "Delete a list of cable termination objects.",
"tags": [
"dcim"
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/CableTerminationRequest"
}
}
},
"multipart/form-data": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/CableTerminationRequest"
}
}
}
},
"required": true
},
"security": [
{
"cookieAuth": []
},
{
"tokenAuth": []
}
],
"responses": {
"204": {
"description": "No response body"
}
}
}
},
"/api/dcim/cable-terminations/{id}/": {
@ -21923,142 +21738,6 @@
"description": ""
}
}
},
"put": {
"operationId": "dcim_cable_terminations_update",
"description": "Put a cable termination object.",
"parameters": [
{
"in": "path",
"name": "id",
"schema": {
"type": "integer"
},
"description": "A unique integer value identifying this cable termination.",
"required": true
}
],
"tags": [
"dcim"
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CableTerminationRequest"
}
},
"multipart/form-data": {
"schema": {
"$ref": "#/components/schemas/CableTerminationRequest"
}
}
},
"required": true
},
"security": [
{
"cookieAuth": []
},
{
"tokenAuth": []
}
],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CableTermination"
}
}
},
"description": ""
}
}
},
"patch": {
"operationId": "dcim_cable_terminations_partial_update",
"description": "Patch a cable termination object.",
"parameters": [
{
"in": "path",
"name": "id",
"schema": {
"type": "integer"
},
"description": "A unique integer value identifying this cable termination.",
"required": true
}
],
"tags": [
"dcim"
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PatchedCableTerminationRequest"
}
},
"multipart/form-data": {
"schema": {
"$ref": "#/components/schemas/PatchedCableTerminationRequest"
}
}
}
},
"security": [
{
"cookieAuth": []
},
{
"tokenAuth": []
}
],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CableTermination"
}
}
},
"description": ""
}
}
},
"delete": {
"operationId": "dcim_cable_terminations_destroy",
"description": "Delete a cable termination object.",
"parameters": [
{
"in": "path",
"name": "id",
"schema": {
"type": "integer"
},
"description": "A unique integer value identifying this cable termination.",
"required": true
}
],
"tags": [
"dcim"
],
"security": [
{
"cookieAuth": []
},
{
"tokenAuth": []
}
],
"responses": {
"204": {
"description": "No response body"
}
}
}
},
"/api/dcim/cables/": {
@ -204463,7 +204142,8 @@
"readOnly": true
},
"cable": {
"type": "integer"
"type": "integer",
"readOnly": true
},
"cable_end": {
"enum": [
@ -204473,16 +204153,16 @@
"type": "string",
"description": "* `A` - A\n* `B` - B",
"x-spec-enum-id": "1db84f9b93b261c8",
"readOnly": true,
"title": "End"
},
"termination_type": {
"type": "string"
"type": "string",
"readOnly": true
},
"termination_id": {
"type": "integer",
"maximum": 9223372036854775807,
"minimum": 0,
"format": "int64"
"readOnly": true
},
"termination": {
"nullable": true,
@ -204514,40 +204194,6 @@
"url"
]
},
"CableTerminationRequest": {
"type": "object",
"description": "Adds support for custom fields and tags.",
"properties": {
"cable": {
"type": "integer"
},
"cable_end": {
"enum": [
"A",
"B"
],
"type": "string",
"description": "* `A` - A\n* `B` - B",
"x-spec-enum-id": "1db84f9b93b261c8",
"title": "End"
},
"termination_type": {
"type": "string"
},
"termination_id": {
"type": "integer",
"maximum": 9223372036854775807,
"minimum": 0,
"format": "int64"
}
},
"required": [
"cable",
"cable_end",
"termination_id",
"termination_type"
]
},
"Circuit": {
"type": "object",
"description": "Adds support for custom fields and tags.",
@ -226099,34 +225745,6 @@
}
}
},
"PatchedCableTerminationRequest": {
"type": "object",
"description": "Adds support for custom fields and tags.",
"properties": {
"cable": {
"type": "integer"
},
"cable_end": {
"enum": [
"A",
"B"
],
"type": "string",
"description": "* `A` - A\n* `B` - B",
"x-spec-enum-id": "1db84f9b93b261c8",
"title": "End"
},
"termination_type": {
"type": "string"
},
"termination_id": {
"type": "integer",
"maximum": 9223372036854775807,
"minimum": 0,
"format": "int64"
}
}
},
"PatchedCircuitGroupRequest": {
"type": "object",
"description": "Adds support for custom fields and tags.",

View File

@ -1,10 +1,8 @@
from django.contrib.contenttypes.models import ContentType
from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import extend_schema_field
from rest_framework import serializers
from dcim.choices import *
from dcim.constants import *
from dcim.models import Cable, CablePath, CableTermination
from netbox.api.fields import ChoiceField, ContentTypeField
from netbox.api.serializers import BaseModelSerializer, GenericObjectSerializer, NetBoxModelSerializer
@ -51,9 +49,11 @@ class TracedCableSerializer(BaseModelSerializer):
class CableTerminationSerializer(NetBoxModelSerializer):
termination_type = ContentTypeField(
queryset=ContentType.objects.filter(CABLE_TERMINATION_MODELS)
read_only=True,
)
termination = serializers.SerializerMethodField(
read_only=True,
)
termination = serializers.SerializerMethodField(read_only=True)
class Meta:
model = CableTermination
@ -61,6 +61,8 @@ class CableTerminationSerializer(NetBoxModelSerializer):
'id', 'url', 'display', 'cable', 'cable_end', 'termination_type', 'termination_id',
'termination', 'created', 'last_updated',
]
read_only_fields = fields
brief_fields = ('id', 'url', 'display', 'cable', 'cable_end', 'termination_type', 'termination_id')
@extend_schema_field(serializers.JSONField(allow_null=True))
def get_termination(self, obj):

View File

@ -16,7 +16,7 @@ from extras.api.mixins import ConfigContextQuerySetMixin, RenderConfigMixin
from netbox.api.authentication import IsAuthenticatedOrLoginNotRequired
from netbox.api.metadata import ContentTypeMetadata
from netbox.api.pagination import StripCountAnnotationsPaginator
from netbox.api.viewsets import NetBoxModelViewSet, MPTTLockedMixin
from netbox.api.viewsets import NetBoxModelViewSet, MPTTLockedMixin, NetBoxReadOnlyModelViewSet
from netbox.api.viewsets.mixins import SequentialBulkCreatesMixin
from utilities.api import get_serializer_for_model
from utilities.query_functions import CollateAsChar
@ -563,7 +563,7 @@ class CableViewSet(NetBoxModelViewSet):
filterset_class = filtersets.CableFilterSet
class CableTerminationViewSet(NetBoxModelViewSet):
class CableTerminationViewSet(NetBoxReadOnlyModelViewSet):
metadata_class = ContentTypeMetadata
queryset = CableTermination.objects.all()
serializer_class = serializers.CableTerminationSerializer

View File

@ -2376,6 +2376,33 @@ class CableTest(APIViewTestCases.APIViewTestCase):
]
class CableTerminationTest(
APIViewTestCases.GetObjectViewTestCase,
APIViewTestCases.ListObjectsViewTestCase,
):
model = CableTermination
brief_fields = ['cable', 'cable_end', 'display', 'id', 'termination_id', 'termination_type', 'url']
@classmethod
def setUpTestData(cls):
device1 = create_test_device('Device 1')
device2 = create_test_device('Device 2')
interfaces = []
for device in (device1, device2):
for i in range(0, 10):
interfaces.append(Interface(device=device, type=InterfaceTypeChoices.TYPE_1GE_FIXED, name=f'eth{i}'))
Interface.objects.bulk_create(interfaces)
cables = (
Cable(a_terminations=[interfaces[0]], b_terminations=[interfaces[10]], label='Cable 1'),
Cable(a_terminations=[interfaces[1]], b_terminations=[interfaces[11]], label='Cable 2'),
Cable(a_terminations=[interfaces[2]], b_terminations=[interfaces[12]], label='Cable 3'),
)
for cable in cables:
cable.save()
class ConnectedDeviceTest(APITestCase):
@classmethod