feat: When attempting to create and objetc must be unique and alrready exists, dont return error return existing object

ref: #855
This commit is contained in:
2025-07-06 22:15:47 +09:30
parent 22ad79386e
commit 45abdc2e00
39 changed files with 299 additions and 38 deletions

View File

@ -18,7 +18,10 @@ from api.viewsets.common import ModelViewSet
summary = 'Create an orgnaization',
description='',
responses = {
# 200: OpenApiResponse(description='Allready exists', response=OrganizationViewSerializer),
200: OpenApiResponse(
description='Already exists',
response = TenantViewSerializer
),
201: OpenApiResponse(description='Created', response=TenantViewSerializer),
# 400: OpenApiResponse(description='Validation failed.'),
403: OpenApiResponse(description='User is missing add permissions'),

View File

@ -17,6 +17,10 @@ from api.viewsets.common import ModelViewSet
summary = 'Create a Role',
description='',
responses = {
200: OpenApiResponse(
description='Already exists',
response = ViewSerializer
),
201: OpenApiResponse(description='Created', response=ViewSerializer),
403: OpenApiResponse(description='User is missing add permissions'),
}

View File

@ -25,9 +25,12 @@ from api.viewsets.common import ModelViewSet
location = 'path',
type = int
),
],
],
responses = {
200: OpenApiResponse(description='Allready exists', response=TeamViewSerializer),
200: OpenApiResponse(
description='Already exists',
response = TeamViewSerializer
),
201: OpenApiResponse(description='Created', response=TeamViewSerializer),
# 400: OpenApiResponse(description='Validation failed.'),
403: OpenApiResponse(description='User is missing add permissions'),

View File

@ -29,7 +29,10 @@ from api.viewsets.common import ModelViewSet
),
],
responses = {
# 200: OpenApiResponse(description='Allready exists', response=TeamUserViewSerializer),
200: OpenApiResponse(
description='Already exists',
response = TeamUserViewSerializer
),
201: OpenApiResponse(description='Created', response=TeamUserViewSerializer),
# 400: OpenApiResponse(description='Validation failed.'),
403: OpenApiResponse(description='User is missing add permissions'),

View File

@ -109,7 +109,8 @@ class APIPermissionAddInheritedCases:
response = client.post(
path = url,
data = kwargs_api_create
data = kwargs_api_create,
content_type = 'application/json'
)
except NoReverseMatch:

View File

@ -41,6 +41,7 @@ class Create(
"""
response = None
instance = None
try:
@ -49,42 +50,77 @@ class Create(
self.model.context['user'] = self.request.user
self.model.context['logging'] = self.get_log()
response = super().create(request = request, *args, **kwargs)
try:
if str(response.status_code).startswith('2'):
response = super().create(request = request, *args, **kwargs)
# Always return using the ViewSerializer
serializer_module = importlib.import_module(self.get_serializer_class().__module__)
except Exception as e:
view_serializer = getattr(serializer_module, self.get_view_serializer_name())
if not isinstance(e, APIException):
if response.data['id'] is not None:
e = self._django_to_api_exception(e)
serializer = view_serializer(
response.data.serializer.instance,
context = {
'request': request,
'view': self,
},
)
if not isinstance(e, rest_framework.exceptions.ValidationError):
serializer_data = serializer.data
raise e
else:
is_unique = False
for field, code in e.get_codes().items():
if 'unique' in code[0]:
is_unique = True
serializer_data = {}
if not is_unique:
raise e
# Mimic ALL details from DRF response except serializer
response = Response(
data = serializer_data,
status = response.status_code,
template_name = response.template_name,
headers = response.headers,
exception = response.exception,
content_type = response.content_type,
)
instance = self.model.objects.get( organization = request.data['organization'])
# Always return using the ViewSerializer
serializer_module = importlib.import_module(self.get_serializer_class().__module__)
view_serializer = getattr(serializer_module, self.get_view_serializer_name())
if(
# response.data['id'] is not None
response is not None
and instance is None
):
instance = response.data.serializer.instance
serializer = view_serializer(
instance,
context = {
'request': request,
'view': self,
},
)
serializer_data = serializer.data
if response is None:
headers = self.get_success_headers(serializer.data)
status_code = rest_framework.status.HTTP_200_OK
else:
headers = response.headers
status_code = response.status_code
# Mimic ALL details from DRF response except serializer
response = Response(
data = serializer_data,
status = status_code,
# template_name = response.template_name,
headers = headers,
# exception = response.exception,
# content_type = response.content_type,
)
except Exception as e:

View File

@ -15,7 +15,10 @@ from assistance.serializers.knowledge_base import ( # pylint: disable=W0611:u
summary = 'Create a knowledge base article',
description='',
responses = {
# 200: OpenApiResponse(description='Allready exists', response=KnowledgeBaseViewSerializer),
200: OpenApiResponse(
description='Already exists',
response = KnowledgeBaseViewSerializer
),
201: OpenApiResponse(description='Created', response=KnowledgeBaseViewSerializer),
# 400: OpenApiResponse(description='Validation failed.'),
403: OpenApiResponse(description='User is missing add permissions'),

View File

@ -15,7 +15,10 @@ from assistance.serializers.knowledge_base_category import ( # pylint: disabl
summary = 'Create a knowledge base article',
description='',
responses = {
# 200: OpenApiResponse(description='Allready exists', response=KnowledgeBaseCategoryViewSerializer),
200: OpenApiResponse(
description='Already exists',
response = KnowledgeBaseCategoryViewSerializer
),
201: OpenApiResponse(description='Created', response=KnowledgeBaseCategoryViewSerializer),
# 400: OpenApiResponse(description='Validation failed.'),
403: OpenApiResponse(description='User is missing add permissions'),

View File

@ -20,6 +20,10 @@ from django.apps import apps
summary = 'Create a knowledge base article',
description='',
responses = {
200: OpenApiResponse(
description='Already exists',
response = ModelKnowledgeBaseArticleViewSerializer
),
201: OpenApiResponse(description='Created', response=ModelKnowledgeBaseArticleViewSerializer),
403: OpenApiResponse(description='User is missing add permissions'),
}

View File

@ -155,7 +155,7 @@ class ConfigGroupSoftwareModelSerializer(
detail = {
'software': 'This software is already assigned to this group'
},
code = 'software_exists'
code = 'unique_software_exists'
)
except self.Meta.model.DoesNotExist as exc:

View File

@ -0,0 +1,37 @@
import pytest
from django.test import Client
class AdditionalTestCases:
def test_permission_add(self, model_instance, api_request_permissions,
model_kwargs, kwargs_api_create
):
""" Check correct permission for add
Attempt to add as user with permission
"""
client = Client()
client.force_login( api_request_permissions['user']['add'] )
the_model = model_instance( kwargs_create = model_kwargs )
url = the_model.get_url( many = True )
response = client.post(
path = url,
data = kwargs_api_create,
content_type = 'application/json'
)
if response.status_code == 405:
pytest.xfail( reason = 'ViewSet does not have this request method.' )
assert response.status_code == 200, response.content

View File

@ -118,7 +118,7 @@ class ConfigGroupSoftwareValidationAPI(
serializer.is_valid(raise_exception = True)
assert err.value.get_codes()['software'][0] == 'software_exists'
assert err.value.get_codes()['software'][0] == 'unique_software_exists'

View File

@ -15,7 +15,10 @@ from config_management.serializers.config_group import ( # pylint: disable=W0
summary = 'Create a config group',
description='',
responses = {
# 200: OpenApiResponse(description='Allready exists', response=ConfigGroupViewSerializer),
200: OpenApiResponse(
description='Already exists',
response = ConfigGroupViewSerializer
),
201: OpenApiResponse(description='Created', response=ConfigGroupViewSerializer),
# 400: OpenApiResponse(description='Validation failed.'),
403: OpenApiResponse(description='User is missing add permissions'),

View File

@ -15,7 +15,10 @@ from config_management.serializers.config_group_software import ( # pylint: d
summary = 'Create a config group software',
description='',
responses = {
# 200: OpenApiResponse(description='Allready exists', response=ConfigGroupSoftwareViewSerializer),
200: OpenApiResponse(
description='Already exists',
response = ConfigGroupSoftwareViewSerializer
),
201: OpenApiResponse(description='Created', response=ConfigGroupSoftwareViewSerializer),
# 400: OpenApiResponse(description='Validation failed.'),
403: OpenApiResponse(description='User is missing add permissions'),

View File

@ -18,7 +18,10 @@ from api.viewsets.common import ModelViewSet
summary = 'Create a manufacturer',
description='',
responses = {
# 200: OpenApiResponse(description='Allready exists', response=ConfigGroupViewSerializer),
200: OpenApiResponse(
description='Already exists',
response = ManufacturerViewSerializer
),
201: OpenApiResponse(description='Created', response=ManufacturerViewSerializer),
# 400: OpenApiResponse(description='Validation failed.'),
403: OpenApiResponse(description='User is missing add permissions'),

View File

@ -17,6 +17,10 @@ from core.serializers.ticket_category import ( # pylint: disable=W0611:unused
summary = 'Create a ticket category',
description='',
responses = {
200: OpenApiResponse(
description='Already exists',
response = TicketCategoryViewSerializer
),
201: OpenApiResponse(description='Created', response=TicketCategoryViewSerializer),
403: OpenApiResponse(description='User is missing add permissions'),
}

View File

@ -18,6 +18,10 @@ from api.viewsets.common import ModelViewSet
summary = 'Create a ticket comment category',
description='',
responses = {
200: OpenApiResponse(
description='Already exists',
response = TicketCommentCategoryViewSerializer
),
201: OpenApiResponse(description='Created', response=TicketCommentCategoryViewSerializer),
403: OpenApiResponse(description='User is missing add permissions'),
}

View File

@ -1,10 +1,41 @@
import pytest
from django.test import Client
class AdditionalTestCases:
def test_permission_add(self, model_instance, api_request_permissions,
model_kwargs, kwargs_api_create
):
""" Check correct permission for add
Attempt to add as user with permission
"""
client = Client()
client.force_login( api_request_permissions['user']['add'] )
the_model = model_instance( kwargs_create = model_kwargs )
url = the_model.get_url( many = True )
response = client.post(
path = url,
data = kwargs_api_create,
content_type = 'application/json'
)
if response.status_code == 405:
pytest.xfail( reason = 'ViewSet does not have this request method.' )
assert response.status_code == 200, response.content
def test_returned_data_from_user_and_global_organizations_only(
self
):

View File

@ -18,6 +18,10 @@ from api.viewsets.common import ModelViewSet
summary = 'Create a Feature Flag',
description='',
responses = {
200: OpenApiResponse(
description='Already exists',
response = ViewSerializer
),
201: OpenApiResponse(description='Created', response=ViewSerializer),
403: OpenApiResponse(description='User is missing add permissions'),
}

View File

@ -22,6 +22,10 @@ from api.viewsets.common import ModelViewSet
summary = 'Create a GIT Group',
description='Create',
responses = {
200: OpenApiResponse(
description='Already exists',
response = ViewSerializer
),
201: OpenApiResponse(
description='Created. Will be serialized with the serializer matching the provider.',
response = ViewSerializer

View File

@ -60,6 +60,18 @@ from api.viewsets.common import (
many = False,
),
responses = {
200: OpenApiResponse(
description='Already exists',
response=PolymorphicProxySerializer(
component_name = 'Git Provider',
serializers=[
GitHubViewSerializer,
GitLabViewSerializer,
],
resource_type_field_name=None,
many = False,
),
),
201: OpenApiResponse(
description='Created. Will be serialized with the serializer matching the provider.',
response=PolymorphicProxySerializer(

View File

@ -17,6 +17,10 @@ from itam.models.software import Software
summary = 'Enable Feature Flagging for Software',
description='',
responses = {
200: OpenApiResponse(
description='Already exists',
response = ViewSerializer
),
201: OpenApiResponse(description='Created', response=ViewSerializer),
403: OpenApiResponse(description='User is missing add permissions'),
}

View File

@ -1,10 +1,42 @@
import pytest
from django.test import Client
class AdditionalTestCases:
def test_permission_add(self, model_instance, api_request_permissions,
model_kwargs, kwargs_api_create
):
""" Check correct permission for add
Attempt to add as user with permission
"""
client = Client()
client.force_login( api_request_permissions['user']['add'] )
the_model = model_instance( kwargs_create = model_kwargs )
url = the_model.get_url( many = True )
response = client.post(
path = url,
data = kwargs_api_create,
content_type = 'application/json'
)
if response.status_code == 405:
pytest.xfail( reason = 'ViewSet does not have this request method.' )
assert response.status_code == 200, response.content
def test_returned_results_only_user_orgs(self):
"""Returned results check

View File

@ -18,7 +18,7 @@ from itam.serializers.device import ( # pylint: disable=W0611:unused-import
is found within the database, it will not re-create it. The device will be returned within the message body.
""",
responses = {
200: OpenApiResponse(description='Device allready exists', response=DeviceViewSerializer),
200: OpenApiResponse(description='Device already exists', response=DeviceViewSerializer),
201: OpenApiResponse(description='Device created', response=DeviceViewSerializer),
400: OpenApiResponse(description='Validation failed.'),
403: OpenApiResponse(description='User is missing create permissions'),

View File

@ -17,6 +17,10 @@ from itam.serializers.device_model import ( # pylint: disable=W0611:unused-im
summary = 'Create a device model',
description='',
responses = {
200: OpenApiResponse(
description='Already exists',
response = DeviceModelViewSerializer
),
201: OpenApiResponse(description='Device created', response=DeviceModelViewSerializer),
400: OpenApiResponse(description='Validation failed.'),
403: OpenApiResponse(description='User is missing create permissions'),

View File

@ -27,6 +27,10 @@ from itam.serializers.device_operating_system import ( # pylint: disable=W061
),
],
responses = {
200: OpenApiResponse(
description='Already exists',
response = DeviceOperatingSystemViewSerializer
),
201: OpenApiResponse(description='Device created', response=DeviceOperatingSystemModelSerializer),
400: OpenApiResponse(description='Validation failed.'),
403: OpenApiResponse(description='User is missing create permissions'),

View File

@ -26,6 +26,10 @@ from itam.serializers.device_software import ( # pylint: disable=W0611:unused
),
],
responses = {
200: OpenApiResponse(
description='Already exists',
response = DeviceSoftwareViewSerializer
),
201: OpenApiResponse(description='Device created', response=DeviceSoftwareModelSerializer),
400: OpenApiResponse(description='Validation failed.'),
403: OpenApiResponse(description='User is missing create permissions'),

View File

@ -17,6 +17,10 @@ from itam.serializers.device_type import ( # pylint: disable=W0611:unused-imp
summary = 'Create a device type',
description='',
responses = {
200: OpenApiResponse(
description='Already exists',
response = DeviceTypeViewSerializer
),
201: OpenApiResponse(description='Device created', response=DeviceTypeViewSerializer),
400: OpenApiResponse(description='Validation failed.'),
403: OpenApiResponse(description='User is missing create permissions'),

View File

@ -16,6 +16,10 @@ from api.viewsets.common import ModelViewSet
summary = 'Create a software category',
description='',
responses = {
200: OpenApiResponse(
description='Already exists',
response = SoftwareCategoryViewSerializer
),
201: OpenApiResponse(description='Software created', response=SoftwareCategoryViewSerializer),
400: OpenApiResponse(description='Validation failed.'),
403: OpenApiResponse(description='User is missing create permissions'),

View File

@ -26,6 +26,10 @@ from api.viewsets.common import ModelViewSet
),
],
responses = {
200: OpenApiResponse(
description='Already exists',
response = SoftwareVersionViewSerializer
),
201: OpenApiResponse(description='Software created', response=SoftwareVersionViewSerializer),
400: OpenApiResponse(description='Validation failed.'),
403: OpenApiResponse(description='User is missing create permissions'),

View File

@ -17,6 +17,10 @@ from itim.serializers.cluster import ( # pylint: disable=W0611:unused-import
summary = 'Create a cluster',
description='',
responses = {
200: OpenApiResponse(
description='Already exists',
response = ClusterViewSerializer
),
201: OpenApiResponse(description='Device created', response=ClusterViewSerializer),
400: OpenApiResponse(description='Validation failed.'),
403: OpenApiResponse(description='User is missing create permissions'),

View File

@ -17,6 +17,10 @@ from itim.serializers.cluster_type import ( # pylint: disable=W0611:unused-im
summary = 'Create a cluster type',
description='',
responses = {
200: OpenApiResponse(
description='Already exists',
response = ClusterTypeViewSerializer
),
201: OpenApiResponse(description='Device created', response=ClusterTypeViewSerializer),
400: OpenApiResponse(description='Validation failed.'),
403: OpenApiResponse(description='User is missing create permissions'),

View File

@ -17,6 +17,10 @@ from itim.serializers.port import ( # pylint: disable=W0611:unused-import
summary = 'Create a port',
description='',
responses = {
200: OpenApiResponse(
description='Already exists',
response = PortViewSerializer
),
201: OpenApiResponse(description='Device created', response=PortViewSerializer),
400: OpenApiResponse(description='Validation failed.'),
403: OpenApiResponse(description='User is missing create permissions'),

View File

@ -20,6 +20,10 @@ from itim.serializers.service import ( # pylint: disable=W0611:unused-import
is found within the database, it will not re-create it. The device will be returned within the message body.
""",
responses = {
200: OpenApiResponse(
description='Already exists',
response = ServiceViewSerializer
),
201: OpenApiResponse(description='Device created', response=ServiceViewSerializer),
400: OpenApiResponse(description='Validation failed.'),
403: OpenApiResponse(description='User is missing create permissions'),

View File

@ -19,6 +19,10 @@ from project_management.serializers.project import ( # pylint: disable=W0611:
summary = 'Create a cluster',
description='',
responses = {
200: OpenApiResponse(
description='Already exists',
response = ProjectViewSerializer
),
201: OpenApiResponse(description='Device created', response=ProjectViewSerializer),
400: OpenApiResponse(description='Validation failed.'),
403: OpenApiResponse(description='User is missing create permissions'),

View File

@ -24,6 +24,10 @@ from project_management.serializers.project_milestone import ( # pylint: disa
),
],
responses = {
200: OpenApiResponse(
description='Already exists',
response = ProjectMilestoneViewSerializer
),
201: OpenApiResponse(description='Device created', response=ProjectMilestoneViewSerializer),
400: OpenApiResponse(description='Validation failed.'),
403: OpenApiResponse(description='User is missing create permissions'),

View File

@ -17,6 +17,10 @@ from project_management.serializers.project_states import ( # pylint: disable
summary = 'Create a project state',
description='',
responses = {
200: OpenApiResponse(
description='Already exists',
response = ProjectStateViewSerializer
),
201: OpenApiResponse(description='Device created', response=ProjectStateViewSerializer),
400: OpenApiResponse(description='Validation failed.'),
403: OpenApiResponse(description='User is missing create permissions'),

View File

@ -17,6 +17,10 @@ from project_management.serializers.project_type import ( # pylint: disable=W
summary = 'Create a project type',
description='',
responses = {
200: OpenApiResponse(
description='Already exists',
response = ProjectTypeViewSerializer
),
201: OpenApiResponse(description='Device created', response=ProjectTypeViewSerializer),
400: OpenApiResponse(description='Validation failed.'),
403: OpenApiResponse(description='User is missing create permissions'),

View File

@ -20,6 +20,10 @@ from settings.serializers.external_links import ( # pylint: disable=W0611:unu
is found within the database, it will not re-create it. The device will be returned within the message body.
""",
responses = {
200: OpenApiResponse(
description='Already exists',
response = ExternalLinkViewSerializer
),
201: OpenApiResponse(description='Device created', response=ExternalLinkViewSerializer),
400: OpenApiResponse(description='Validation failed.'),
403: OpenApiResponse(description='User is missing create permissions'),