From 293eb7a59a31118ceac713111ce1492520f2039d Mon Sep 17 00:00:00 2001 From: rw-bsi Date: Fri, 6 Dec 2024 15:17:26 +0000 Subject: [PATCH 1/6] fix for DRF API access --- backend/apps/ifc_validation/serializers.py | 5 +++-- backend/core/settings.py | 6 +++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/backend/apps/ifc_validation/serializers.py b/backend/apps/ifc_validation/serializers.py index 19c135c..b76547d 100644 --- a/backend/apps/ifc_validation/serializers.py +++ b/backend/apps/ifc_validation/serializers.py @@ -5,7 +5,7 @@ from apps.ifc_validation_models.models import ValidationOutcome -class BaseSerializer(serializers.HyperlinkedModelSerializer): +class BaseSerializer(serializers.ModelSerializer): def get_field_names(self, declared_fields, info): @@ -28,7 +28,8 @@ class Meta: model = ValidationRequest fields = '__all__' show = ["public_id", "model_public_id"] - hide = ["id", "model"] + hide = ["id"] + read_only_fields = ['size', 'created_by'] class ValidationTaskSerializer(BaseSerializer): diff --git a/backend/core/settings.py b/backend/core/settings.py index ce1d9af..8ad5714 100644 --- a/backend/core/settings.py +++ b/backend/core/settings.py @@ -134,7 +134,11 @@ 'DEFAULT_AUTHENTICATION_CLASSES': [ 'rest_framework.authentication.BasicAuthentication', 'rest_framework.authentication.SessionAuthentication', - ] + 'rest_framework.authentication.TokenAuthentication', + ], + 'DEFAULT_PERMISSION_CLASSES':( + 'rest_framework.permissions.IsAuthenticated', + ), } SPECTACULAR_SETTINGS = { From d606475185023b4c896f6a4394987dd641c3bec0 Mon Sep 17 00:00:00 2001 From: rw-bsi Date: Fri, 6 Dec 2024 15:38:01 +0000 Subject: [PATCH 2/6] update API - fetch by public_id --- backend/apps/ifc_validation/views.py | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/backend/apps/ifc_validation/views.py b/backend/apps/ifc_validation/views.py index b4ca4fb..627ed28 100644 --- a/backend/apps/ifc_validation/views.py +++ b/backend/apps/ifc_validation/views.py @@ -34,40 +34,46 @@ class ValidationRequestDetailAPIView(APIView): parser_classes = (MultiPartParser, FormParser) serializer_class = ValidationRequestSerializer + def get_for_user_by_public_id(self, user_id, public_id): + + user_requests = ValidationRequest.objects.filter(created_by__id=user_id, deleted=False) + instance = [r for r in user_requests if r.public_id == public_id] + + return instance[0] if instance else None + @extend_schema(operation_id='validationrequest_get') def get(self, request, id, *args, **kwargs): """ - Retrieves a single Validation Request by Id. + Retrieves a single Validation Request by id. """ logger.info('API request - User IP: %s Request Method: %s Request URL: %s Content-Length: %s' % (get_client_ip_address(request), request.method, request.path, request.META.get('CONTENT_LENGTH'))) - instance = ValidationRequest.objects.filter(created_by__id=request.user.id, deleted=False, id=id).first() - + instance = self.get_for_user_by_public_id(user_id=request.user.id, public_id=id) if instance: serializer = ValidationRequestSerializer(instance) return Response(serializer.data, status=status.HTTP_200_OK) else: - data = {'message': f"Validation Request with id='{id}' does not exist for user with id='{request.user.id}'."} + data = {'message': f"Validation Request with public_id={id} does not exist for user with id={request.user.id}."} return Response(data, status=status.HTTP_404_NOT_FOUND) @extend_schema(operation_id='validationrequest_delete') def delete(self, request, id, *args, **kwargs): """ - Deletes an IFC Validation Request instance. + Deletes an IFC Validation Request instance by id. """ logger.info('API request - User IP: %s Request Method: %s Request URL: %s Content-Length: %s' % (get_client_ip_address(request), request.method, request.path, request.META.get('CONTENT_LENGTH'))) - instance = ValidationRequest.objects.filter(created_by__id=request.user.id, deleted=False).filter(id=id).first() + instance = self.get_for_user_by_public_id(user_id=request.user.id, public_id=id) if instance: instance.delete() - data = {'message': f"Validation Request with id='{id}' was deleted successfully."} + data = {'message': f"Validation Request with public_id={id} was deleted successfully."} return Response(data, status=status.HTTP_204_NO_CONTENT) else: - data = {'message': f"Validation Request with id='{id}' does not exist."} + data = {'message': f"Validation Request with public_id={id} does not exist."} return Response(data, status=status.HTTP_404_NOT_FOUND) @@ -88,8 +94,8 @@ def get(self, request, *args, **kwargs): logger.info('API request - User IP: %s Request Method: %s Request URL: %s Content-Length: %s' % (get_client_ip_address(request), request.method, request.path, request.META.get('CONTENT_LENGTH'))) - all_user_instances = ValidationRequest.objects.filter(created_by__id=request.user.id, deleted=False) - serializer = self.serializer_class(all_user_instances, many=True) + user_requests = ValidationRequest.objects.filter(created_by__id=request.user.id, deleted=False) + serializer = self.serializer_class(user_requests, many=True) return Response(serializer.data, status=status.HTTP_200_OK) @extend_schema(operation_id='validationrequest_create') From a3785ea59dd0f57791b153f1b593e3e7bdaa83e1 Mon Sep 17 00:00:00 2001 From: rw-bsi Date: Fri, 6 Dec 2024 16:19:55 +0000 Subject: [PATCH 3/6] support filtering Validation Tasks and Validation Outcomes by request/task id(s) --- backend/apps/ifc_validation/views.py | 34 +++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/backend/apps/ifc_validation/views.py b/backend/apps/ifc_validation/views.py index 627ed28..85d55f1 100644 --- a/backend/apps/ifc_validation/views.py +++ b/backend/apps/ifc_validation/views.py @@ -192,13 +192,22 @@ class ValidationTaskListAPIView(APIView): def get(self, request, *args, **kwargs): """ - Returns a list of all Validation Tasks. + Returns a list of all Validation Tasks, optionally filtered by request_public_id. """ logger.info('API request - User IP: %s Request Method: %s Request URL: %s Content-Length: %s' % (get_client_ip_address(request), request.method, request.path, request.META.get('CONTENT_LENGTH'))) - all_user_instances = ValidationTask.objects.filter(request__created_by__id=request.user.id, request__deleted=False) - serializer = self.serializer_class(all_user_instances, many=True) + user_tasks = ValidationTask.objects.filter(request__created_by__id=request.user.id, request__deleted=False) + + # parse query arguments + request_public_id = self.request.query_params.get('request_public_id', '').lower() + request_public_ids = [id for id in (request_public_id.split(',') if request_public_id else [])] + + # apply filter(s) + if request_public_ids: + user_tasks = [t for t in user_tasks if t.request_public_id in request_public_ids] + + serializer = self.serializer_class(user_tasks, many=True) return Response(serializer.data, status=status.HTTP_200_OK) @@ -238,12 +247,25 @@ class ValidationOutcomeListAPIView(APIView): def get(self, request, *args, **kwargs): """ - Returns a list of all Validation Outcomes. + Returns a list of all Validation Outcomes, optionally filtered by request_public_id or validation_task_public_id. """ logger.info('API request - User IP: %s Request Method: %s Request URL: %s Content-Length: %s' % (get_client_ip_address(request), request.method, request.path, request.META.get('CONTENT_LENGTH'))) - all_user_instances = ValidationOutcome.objects.filter(validation_task__request__created_by__id=request.user.id, validation_task__request__deleted=False) - serializer = self.serializer_class(all_user_instances, many=True) + user_outcomes = ValidationOutcome.objects.filter(validation_task__request__created_by__id=request.user.id, validation_task__request__deleted=False) + + # parse query arguments + request_public_id = self.request.query_params.get('request_public_id', '').lower() + task_public_id = self.request.query_params.get('validation_task_public_id', '').lower() + request_public_ids = [id for id in (request_public_id.split(',') if request_public_id else [])] + task_public_ids = [id for id in (task_public_id.split(',') if task_public_id else [])] + + # apply filter(s) + if request_public_ids: + user_outcomes = [o for o in user_outcomes if o.validation_task.request_public_id in request_public_ids] + if task_public_ids: + user_outcomes = [o for o in user_outcomes if o.validation_task_public_id in task_public_ids] + + serializer = self.serializer_class(user_outcomes, many=True) return Response(serializer.data, status=status.HTTP_200_OK) From 4793677e428940038427f4baf4c1c9b6fd63bb2a Mon Sep 17 00:00:00 2001 From: rw-bsi Date: Fri, 6 Dec 2024 16:39:17 +0000 Subject: [PATCH 4/6] support fetching outcomes and tasks by public_id --- backend/apps/ifc_validation/views.py | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/backend/apps/ifc_validation/views.py b/backend/apps/ifc_validation/views.py index 85d55f1..bfbd9b9 100644 --- a/backend/apps/ifc_validation/views.py +++ b/backend/apps/ifc_validation/views.py @@ -163,6 +163,13 @@ class ValidationTaskDetailAPIView(APIView): permission_classes = [IsAuthenticated] serializer_class = ValidationTaskSerializer + def get_for_user_by_public_id(self, user_id, public_id): + + user_tasks = ValidationTask.objects.filter(request__created_by__id=user_id, request__deleted=False) + instance = [t for t in user_tasks if t.public_id == public_id] + + return instance[0] if instance else None + @extend_schema(operation_id='validationtask_get') def get(self, request, id, *args, **kwargs): @@ -172,12 +179,12 @@ def get(self, request, id, *args, **kwargs): logger.info('API request - User IP: %s Request Method: %s Request URL: %s Content-Length: %s' % (get_client_ip_address(request), request.method, request.path, request.META.get('CONTENT_LENGTH'))) - instance = ValidationTask.objects.filter(request__created_by__id=request.user.id, request__deleted=False, id=id).first() + instance = self.get_for_user_by_public_id(request.user.id, id) if instance: serializer = ValidationTaskSerializer(instance) return Response(serializer.data, status=status.HTTP_200_OK) else: - data = {'message': f"Validation Task with id='{id}' does not exist for user with id='{request.user.id}'."} + data = {'message': f"Validation Task with public_id={id} does not exist for user with id={request.user.id}."} return Response(data, status=status.HTTP_404_NOT_FOUND) @@ -218,6 +225,13 @@ class ValidationOutcomeDetailAPIView(APIView): permission_classes = [IsAuthenticated] serializer_class = ValidationOutcomeSerializer + def get_for_user_by_public_id(self, user_id, public_id): + + user_outcomes = ValidationOutcome.objects.filter(validation_task__request__created_by__id=user_id, validation_task__request__deleted=False) + instance = [o for o in user_outcomes if o.public_id == public_id] + + return instance[0] if instance else None + @extend_schema(operation_id='validationoutcome_get') def get(self, request, id, *args, **kwargs): @@ -227,12 +241,12 @@ def get(self, request, id, *args, **kwargs): logger.info('API request - User IP: %s Request Method: %s Request URL: %s Content-Length: %s' % (get_client_ip_address(request), request.method, request.path, request.META.get('CONTENT_LENGTH'))) - instance = ValidationOutcome.objects.filter(validation_task__request__created_by__id=request.user.id, validation_task__request__deleted=False, id=id).first() + instance = self.get_for_user_by_public_id(request.user.id, id) if instance: serializer = ValidationOutcomeSerializer(instance) return Response(serializer.data, status=status.HTTP_200_OK) else: - data = {'message': f"Validation Outcome with id='{id}' does not exist for user with id='{request.user.id}'."} + data = {'message': f"Validation Outcome with public_id={id} does not exist for user with id={request.user.id}."} return Response(data, status=status.HTTP_404_NOT_FOUND) From cab9ca9e4fca229ee975f1f2329623a430dc5bbc Mon Sep 17 00:00:00 2001 From: rw-bsi Date: Fri, 6 Dec 2024 16:58:12 +0000 Subject: [PATCH 5/6] Mark VS API as in Preview mode --- backend/core/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/core/settings.py b/backend/core/settings.py index 8ad5714..aeb4423 100644 --- a/backend/core/settings.py +++ b/backend/core/settings.py @@ -142,7 +142,7 @@ } SPECTACULAR_SETTINGS = { - 'TITLE': 'IFC Validation Service API', + 'TITLE': 'IFC Validation Service API (PREVIEW)', 'DESCRIPTION': 'API for the buildingSMART Validation Service', 'VERSION': os.environ.get("VERSION", "UNDEFINED"), 'SERVE_INCLUDE_SCHEMA': False, From 5c06f07d87c7909c1c8635e7229f5085f2f50b78 Mon Sep 17 00:00:00 2001 From: rw-bsi Date: Sat, 7 Dec 2024 14:53:42 +0000 Subject: [PATCH 6/6] map public_id to private id --- backend/apps/ifc_validation/views.py | 35 ++++++---------------------- 1 file changed, 7 insertions(+), 28 deletions(-) diff --git a/backend/apps/ifc_validation/views.py b/backend/apps/ifc_validation/views.py index bfbd9b9..f4f29f7 100644 --- a/backend/apps/ifc_validation/views.py +++ b/backend/apps/ifc_validation/views.py @@ -34,23 +34,16 @@ class ValidationRequestDetailAPIView(APIView): parser_classes = (MultiPartParser, FormParser) serializer_class = ValidationRequestSerializer - def get_for_user_by_public_id(self, user_id, public_id): - - user_requests = ValidationRequest.objects.filter(created_by__id=user_id, deleted=False) - instance = [r for r in user_requests if r.public_id == public_id] - - return instance[0] if instance else None - @extend_schema(operation_id='validationrequest_get') def get(self, request, id, *args, **kwargs): """ - Retrieves a single Validation Request by id. + Retrieves a single Validation Request by public_id. """ logger.info('API request - User IP: %s Request Method: %s Request URL: %s Content-Length: %s' % (get_client_ip_address(request), request.method, request.path, request.META.get('CONTENT_LENGTH'))) - instance = self.get_for_user_by_public_id(user_id=request.user.id, public_id=id) + instance = ValidationRequest.objects.filter(created_by__id=request.user.id, deleted=False, id=ValidationRequest.to_private_id(id)).first() if instance: serializer = ValidationRequestSerializer(instance) return Response(serializer.data, status=status.HTTP_200_OK) @@ -67,7 +60,7 @@ def delete(self, request, id, *args, **kwargs): logger.info('API request - User IP: %s Request Method: %s Request URL: %s Content-Length: %s' % (get_client_ip_address(request), request.method, request.path, request.META.get('CONTENT_LENGTH'))) - instance = self.get_for_user_by_public_id(user_id=request.user.id, public_id=id) + instance = ValidationRequest.objects.filter(created_by__id=request.user.id, deleted=False, id=ValidationRequest.to_private_id(id)).first() if instance: instance.delete() data = {'message': f"Validation Request with public_id={id} was deleted successfully."} @@ -163,23 +156,16 @@ class ValidationTaskDetailAPIView(APIView): permission_classes = [IsAuthenticated] serializer_class = ValidationTaskSerializer - def get_for_user_by_public_id(self, user_id, public_id): - - user_tasks = ValidationTask.objects.filter(request__created_by__id=user_id, request__deleted=False) - instance = [t for t in user_tasks if t.public_id == public_id] - - return instance[0] if instance else None - @extend_schema(operation_id='validationtask_get') def get(self, request, id, *args, **kwargs): """ - Retrieves a single Validation Task by Id. + Retrieves a single Validation Task by public_id. """ logger.info('API request - User IP: %s Request Method: %s Request URL: %s Content-Length: %s' % (get_client_ip_address(request), request.method, request.path, request.META.get('CONTENT_LENGTH'))) - instance = self.get_for_user_by_public_id(request.user.id, id) + instance = ValidationTask.objects.filter(request__created_by__id=request.user.id, request__deleted=False, id=ValidationTask.to_private_id(id)).first() if instance: serializer = ValidationTaskSerializer(instance) return Response(serializer.data, status=status.HTTP_200_OK) @@ -225,23 +211,16 @@ class ValidationOutcomeDetailAPIView(APIView): permission_classes = [IsAuthenticated] serializer_class = ValidationOutcomeSerializer - def get_for_user_by_public_id(self, user_id, public_id): - - user_outcomes = ValidationOutcome.objects.filter(validation_task__request__created_by__id=user_id, validation_task__request__deleted=False) - instance = [o for o in user_outcomes if o.public_id == public_id] - - return instance[0] if instance else None - @extend_schema(operation_id='validationoutcome_get') def get(self, request, id, *args, **kwargs): """ - Retrieves a single Validation Outcome by Id. + Retrieves a single Validation Outcome by public_id. """ logger.info('API request - User IP: %s Request Method: %s Request URL: %s Content-Length: %s' % (get_client_ip_address(request), request.method, request.path, request.META.get('CONTENT_LENGTH'))) - instance = self.get_for_user_by_public_id(request.user.id, id) + instance = ValidationOutcome.objects.filter(validation_task__request__created_by__id=request.user.id, validation_task__request__deleted=False, id=ValidationOutcome.to_private_id(id)).first() if instance: serializer = ValidationOutcomeSerializer(instance) return Response(serializer.data, status=status.HTTP_200_OK)