diff --git a/api/v1/models/server.py b/api/v1/models/server.py index 54f5462..a15e9aa 100644 --- a/api/v1/models/server.py +++ b/api/v1/models/server.py @@ -35,6 +35,11 @@ class OptionalSchemeURLValidator(URLValidator): super(OptionalSchemeURLValidator, self).__call__(value) +# TODO: Validation should be probably be moved to `clean` method instead of `save`. +# Currently the serializer handles ValidationError during input, and the only +# other thing calling `save` should be the management tasks which should already +# be validated so should be safe? +# https://docs.djangoproject.com/en/5.0/ref/models/instances/#validating-objects class Server(models.Model): name = models.CharField(max_length=255, null=True, editable=False) url = models.CharField( @@ -74,6 +79,11 @@ class Server(models.Model): parsed_url = urlparse(self.url) self.url = parsed_url.netloc.lower() + # Check if formatted server URL is unique + existing_server = Server.objects.filter(url=self.url).first() + if existing_server: + raise ValidationError("A server with this URL already exists.") + # Validate the server API if not self.parse_server_api(): if not self.created: diff --git a/api/v1/views/server.py b/api/v1/views/server.py index 35ad485..dbfc880 100644 --- a/api/v1/views/server.py +++ b/api/v1/views/server.py @@ -58,7 +58,8 @@ class ServerDetailsViewSet( def get_object(self): queryset = self.filter_queryset(self.get_queryset()) url_param = self.request.query_params.get("url") - if url_param is not None: + if url_param: + url_param = url_param.lower() try: return queryset.get(url=url_param) except Server.DoesNotExist: @@ -66,34 +67,43 @@ class ServerDetailsViewSet( return super().get_object() def create_server(self, url_param): - try: - serializer = self.get_serializer(data={"url": url_param}) - serializer.is_valid(raise_exception=True) - self.perform_create(serializer) - return serializer.instance - except ValidationError as e: - raise Http404(e.message) + serializer = self.get_serializer(data={"url": url_param}) + serializer.is_valid(raise_exception=True) + self.perform_create(serializer) + return serializer.instance def create(self, request, *args, **kwargs): url_param = request.data.get("url") if url_param: + url_param = url_param.lower() try: server = Server.objects.get(url=url_param) serializer = self.get_serializer(server) return Response(serializer.data, status=status.HTTP_200_OK) except Server.DoesNotExist: - instance = self.create_server(url_param) - serializer = self.get_serializer(instance) - return Response(serializer.data, status=status.HTTP_201_CREATED) + try: + instance = self.create_server(url_param) + serializer = self.get_serializer(instance) + return Response(serializer.data, status=status.HTTP_201_CREATED) + except ValidationError as e: + return Response( + {"url": e.messages}, status=status.HTTP_400_BAD_REQUEST + ) def list(self, request, *args, **kwargs): queryset = self.filter_queryset(self.get_queryset()) url_param = self.request.query_params.get("url") - if url_param is not None: + if url_param: + url_param = url_param.lower() queryset = queryset.filter(url=url_param) if not queryset.exists(): - instance = self.create_server(url_param) - serializer = self.get_serializer(instance) - return Response(serializer.data, status=status.HTTP_201_CREATED) + try: + instance = self.create_server(url_param) + serializer = self.get_serializer(instance) + return Response(serializer.data, status=status.HTTP_201_CREATED) + except ValidationError as e: + return Response( + {"url": e.messages}, status=status.HTTP_400_BAD_REQUEST + ) return self.retrieve(request, *args, **kwargs) raise Http404("No URL provided.") diff --git a/client/src/apiUtil.tsx b/client/src/apiUtil.tsx index 077c407..b8ca4e1 100644 --- a/client/src/apiUtil.tsx +++ b/client/src/apiUtil.tsx @@ -76,7 +76,7 @@ export const postData = async (inputText: string) => { // bad url or duplicate if (response.status === 400) { const responseData = await response.json(); - message = responseData.url; + message = responseData.url || responseData.detail; } return { message,