mirror of
https://github.com/netbox-community/netbox.git
synced 2025-12-11 19:09:36 -06:00
Some checks are pending
CI / build (20.x, 3.10) (push) Waiting to run
CI / build (20.x, 3.11) (push) Waiting to run
CI / build (20.x, 3.12) (push) Waiting to run
CodeQL / Analyze (${{ matrix.language }}) (none, python) (push) Waiting to run
CodeQL / Analyze (${{ matrix.language }}) (none, actions) (push) Waiting to run
CodeQL / Analyze (${{ matrix.language }}) (none, javascript-typescript) (push) Waiting to run
* Fixes #20638: Document bulk create support in OpenAPI schema POST operations on NetBoxModelViewSet endpoints accept both single objects and arrays, but the schema only documented single objects. This prevented API client generators from producing correct code. Add explicit bulk_create_enabled flag to NetBoxModelViewSet and update schema generation to emit oneOf for these endpoints. * Address PR feedback - Removed brittle serializer marking mechanism in favor of direct checks on behavior. - Attempted to introduce a bulk_create action and then route to it on POST in NetBoxRouter, but ran in to several obstacles including breaking HTTP status code reporting in the schema. Opted to simply * Remove unused bulk_create_enabled attr Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com> --------- Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
109 lines
4.0 KiB
Python
109 lines
4.0 KiB
Python
"""
|
|
Unit tests for OpenAPI schema generation.
|
|
|
|
Refs: #20638
|
|
"""
|
|
import json
|
|
from django.test import TestCase
|
|
|
|
|
|
class OpenAPISchemaTestCase(TestCase):
|
|
"""Tests for OpenAPI schema generation."""
|
|
|
|
def setUp(self):
|
|
"""Fetch schema via API endpoint."""
|
|
response = self.client.get('/api/schema/', {'format': 'json'})
|
|
self.assertEqual(response.status_code, 200)
|
|
self.schema = json.loads(response.content)
|
|
|
|
def test_post_operation_documents_single_or_array(self):
|
|
"""
|
|
POST operations on NetBoxModelViewSet endpoints should document
|
|
support for both single objects and arrays via oneOf.
|
|
|
|
Refs: #20638
|
|
"""
|
|
# Test representative endpoints across different apps
|
|
test_paths = [
|
|
'/api/core/data-sources/',
|
|
'/api/dcim/sites/',
|
|
'/api/users/users/',
|
|
'/api/ipam/ip-addresses/',
|
|
]
|
|
|
|
for path in test_paths:
|
|
with self.subTest(path=path):
|
|
operation = self.schema['paths'][path]['post']
|
|
|
|
# Get the request body schema
|
|
request_schema = operation['requestBody']['content']['application/json']['schema']
|
|
|
|
# Should have oneOf with two options
|
|
self.assertIn('oneOf', request_schema, f"POST {path} should have oneOf schema")
|
|
self.assertEqual(
|
|
len(request_schema['oneOf']), 2,
|
|
f"POST {path} oneOf should have exactly 2 options"
|
|
)
|
|
|
|
# First option: single object (has $ref or properties)
|
|
single_schema = request_schema['oneOf'][0]
|
|
self.assertTrue(
|
|
'$ref' in single_schema or 'properties' in single_schema,
|
|
f"POST {path} first oneOf option should be single object"
|
|
)
|
|
|
|
# Second option: array of objects
|
|
array_schema = request_schema['oneOf'][1]
|
|
self.assertEqual(
|
|
array_schema['type'], 'array',
|
|
f"POST {path} second oneOf option should be array"
|
|
)
|
|
self.assertIn('items', array_schema, f"POST {path} array should have items")
|
|
|
|
def test_bulk_update_operations_require_array_only(self):
|
|
"""
|
|
Bulk update/patch operations should require arrays only, not oneOf.
|
|
They don't support single object input.
|
|
|
|
Refs: #20638
|
|
"""
|
|
test_paths = [
|
|
'/api/dcim/sites/',
|
|
'/api/users/users/',
|
|
]
|
|
|
|
for path in test_paths:
|
|
for method in ['put', 'patch']:
|
|
with self.subTest(path=path, method=method):
|
|
operation = self.schema['paths'][path][method]
|
|
request_schema = operation['requestBody']['content']['application/json']['schema']
|
|
|
|
# Should be array-only, not oneOf
|
|
self.assertNotIn(
|
|
'oneOf', request_schema,
|
|
f"{method.upper()} {path} should NOT have oneOf (array-only)"
|
|
)
|
|
self.assertEqual(
|
|
request_schema['type'], 'array',
|
|
f"{method.upper()} {path} should require array"
|
|
)
|
|
self.assertIn(
|
|
'items', request_schema,
|
|
f"{method.upper()} {path} array should have items"
|
|
)
|
|
|
|
def test_bulk_delete_requires_array(self):
|
|
"""
|
|
Bulk delete operations should require arrays.
|
|
|
|
Refs: #20638
|
|
"""
|
|
path = '/api/dcim/sites/'
|
|
operation = self.schema['paths'][path]['delete']
|
|
request_schema = operation['requestBody']['content']['application/json']['schema']
|
|
|
|
# Should be array-only
|
|
self.assertNotIn('oneOf', request_schema, "DELETE should NOT have oneOf")
|
|
self.assertEqual(request_schema['type'], 'array', "DELETE should require array")
|
|
self.assertIn('items', request_schema, "DELETE array should have items")
|