class AreaViewSet(utils.MultiSerializerViewSetMixin, viewsets.ModelViewSet): permission_classes = (require_scopes(SCOPE_PRV_API), ) queryset = models.Area.objects.prefetch_related("polygons").all() filter_backends = (filters.OrderingFilter, ) ordering_fields = ("label", ) ordering = "label" lookup_field = "id" serializer_class = AreaResponseSerializer serializers_mapping = { "list": { "response": AreaResponseSerializer }, "retrieve": { "response": AreaResponseSerializer }, "create": { "request": AreaRequestSerializer, "response": utils.EmptyResponseSerializer, }, "update": { "request": AreaRequestSerializer, "response": utils.EmptyResponseSerializer, }, } def create(self, *args, **kwargs): return super()._create(*args, **kwargs) def update(self, *args, **kwargs): return super()._update(*args, **kwargs)
class AreaViewSet(viewsets.ReadOnlyModelViewSet): permission_classes = (require_scopes(SCOPE_AGENCY_API), ) queryset = models.Area.objects.prefetch_related("polygons").all() lookup_field = "id" serializer_class = AreaSerializer def get_queryset(self): queryset = super().get_queryset() provider_id = getattr(self.request.user, "provider_id", None) if provider_id: queryset = queryset.filter(providers__id=provider_id) else: queryset = queryset.none() return queryset
class DeviceViewSet(utils.MultiSerializerViewSetMixin, viewsets.ReadOnlyModelViewSet): permission_classes = (require_scopes(SCOPE_PRV_API), ) lookup_field = "id" serializer_class = DeviceSerializer pagination_class = utils.LimitOffsetPagination filter_backends = (filters.DjangoFilterBackend, ) filterset_class = DeviceFilter queryset = models.Device.objects.select_related("provider").all() serializers_mapping = { "list": { "response": DeviceSerializer }, "retrieve": { "response": RetrieveDeviceSerializer }, }
class ProviderApiViewSet(viewsets.ViewSet): permission_classes = [require_scopes(SCOPE_PRV_API)] @decorators.action(detail=False, methods=["get"]) def status_changes(self, request, *args, **kwargs): start_time = request.query_params.get("start_time") end_time = request.query_params.get("end_time") # Only forward events that were first retrieved though providers' # `status_changes' endpoint event_types = enums.PROVIDER_REASON_TO_AGENCY_EVENT.values() events = models.EventRecord.objects.select_related( "device__provider").filter(event_type__in=event_types) if start_time: start_time = utils.from_mds_timestamp(int(start_time)) events = events.filter(timestamp__gte=start_time) if end_time: end_time = utils.from_mds_timestamp(int(end_time)) events = events.filter(timestamp__lte=end_time) paginator = CustomPagination() page = paginator.paginate_queryset(events.order_by("timestamp"), request) data = DeviceStatusChangesSerializer(page, many=True).data return paginator.get_paginated_response(data)
class PolygonViewSet(utils.MultiSerializerViewSetMixin, viewsets.ModelViewSet): permission_classes = (require_scopes(SCOPE_PRV_API), ) queryset = models.Polygon.objects.prefetch_related("areas").all() filter_backends = (filters.OrderingFilter, ) ordering_fields = ("label", ) ordering = "label" lookup_field = "id" serializer_class = PolygonResponseSerializer serializers_mapping = { "list": { "response": PolygonResponseSerializer }, "retrieve": { "response": PolygonResponseSerializer }, "create": { "request": PolygonRequestSerializer, "response": utils.EmptyResponseSerializer, }, "update": { "request": PolygonRequestSerializer, "response": utils.EmptyResponseSerializer, }, "import_polygons": { "request": PolygonsImportRequestSerializer, "response": utils.EmptyResponseSerializer, }, } def create(self, *args, **kwargs): return super()._create(*args, **kwargs) def update(self, *args, **kwargs): return super()._update(*args, **kwargs) @action(methods=["post"], url_path="import", detail=False) def import_polygons(self, request, pk=None): polygons = request.data.get("polygons", None) if not isinstance(polygons, list): return Response(status=400) try: polygons_to_create = [] for polygon in polygons: geom = polygon.get("geom", None) if geom and geom["type"] == "Polygon": areas = [] for area_label in polygon.get("areas", []): defaults = { "color": "#%06x" % random.randint(0, 0xFFFFFF) } # Create new Area if doesn't exist (based on label) area = models.Area.objects.get_or_create( label=area_label, defaults=defaults)[0] areas.append(area) poly = models.Polygon(label=polygon.get("label", ""), geom=str(geom)) poly.areas.set([a.id for a in areas]) polygons_to_create.append(poly) models.Polygon.objects.bulk_create(polygons_to_create) except IntegrityError as ex: return Response(exception=ex, status=500) return Response({"message": "ok"})
class DeviceViewSet( apis_utils.MultiSerializerViewSetMixin, mixins.ListModelMixin, mixins.RetrieveModelMixin, mixins.CreateModelMixin, viewsets.GenericViewSet, ): queryset = models.Device.objects.with_latest_events() permission_classes = (require_scopes(SCOPE_AGENCY_API), ) lookup_field = "id" serializer_class = DeviceSerializer serializers_mapping = { "list": { "response": DeviceSerializer }, "retrieve": { "response": DeviceSerializer }, "create": { "request": DeviceRegisterSerializer, "response": apis_utils.EmptyResponseSerializer, }, "event": { "request": DeviceEventSerializer, "response": DeviceEventResponseSerializer, }, "telemetry": { "request": DeviceTelemetryInputSerializer, "response": apis_utils.EmptyResponseSerializer, }, } def list(self, *args, **kwargs): return super().list(*args, **kwargs) def retrieve(self, *args, **kwargs): return super().retrieve(*args, **kwargs) def create(self, *args, **kwargs): return self._create(*args, **kwargs) @action(detail=True, methods=["post", "options"]) def event(self, request, id): """Endpoint to receive an event from a provider.""" provider_id = request.user.provider_id device = models.Device.objects.filter(provider_id=provider_id, id=id).last() if not device: return Response(data={}, status=404) provider = models.Provider.objects.get(pk=provider_id) request_serializer = self.get_serializer( data=request.data, context={ "device": device, "request_or_response": "request", "provider": provider, }, ) request_serializer.is_valid(raise_exception=True) instance = request_serializer.save() response_serializer = self.get_serializer( instance=instance, context={"request_or_response": "response"}) return Response(response_serializer.data, status=status.HTTP_201_CREATED) @action(detail=False, methods=["post", "options"]) def telemetry(self, request): """Endpoint to receive a telemetry from a provider.""" context = self.get_serializer_context( ) # adds the request to the context context["request_or_response"] = "request" provider_id = request.user.provider_id context["provider"] = models.Provider.objects.get(pk=provider_id) serializer = self.get_serializer(data=request.data, context=context) serializer.is_valid(raise_exception=True) instance = serializer.save() response_serializer = self.get_serializer( instance=instance, context={"request_or_response": "response"}) return Response(response_serializer.data, status=status.HTTP_201_CREATED) def get_queryset(self): queryset = super().get_queryset() provider_id = getattr(self.request.user, "provider_id", None) if provider_id: queryset = queryset.filter(provider_id=provider_id) else: queryset = queryset.none() return queryset
class LongLivedTokenView(GenericAPIView): """Implements an endpoint to generate long lived (JWT) tokens. This is an alternate authentication method to Oauth2. An authorized user can generate such tokens and then transmit it to the token owner for future use with our API. """ permission_classes = (require_scopes(SCOPE_PRV_API),) class RequestSerializer(serializers.Serializer): app_owner = serializers.UUIDField( help_text="The owner for which the token is generated." ) token_duration = serializers.IntegerField( help_text="Token duration in seconds." ) class RevokeRequestSerializer(serializers.Serializer): access_token = serializers.CharField() class RevokeResponseSerializer(utils.EmptyResponseSerializer): revocation_date = serializers.DateTimeField() class ResponseSerializer(serializers.Serializer): access_token = serializers.CharField() token_type = serializers.ChoiceField(choices=["bearer"]) expires_in = serializers.IntegerField() def post(self, request, *args, **kwargs): serializer = self.RequestSerializer(data=request.data) serializer.is_valid(raise_exception=True) validated_data = serializer.validated_data try: token = public_api.get_long_lived_token( validated_data["app_owner"], validated_data["token_duration"] ) except public_api.NoApplicationForOwner: raise exceptions.ValidationError( { "app_owner": _("No application known for owner %s") % validated_data["app_owner"] } ) serializer = self.ResponseSerializer( instance={ "access_token": token, "token_type": "bearer", "expires_in": validated_data["token_duration"], } ) return Response(serializer.data, status=200) def delete(self, request, *args, **kwargs): serializer = self.RevokeRequestSerializer(data=request.data) serializer.is_valid(raise_exception=True) validated_data = serializer.validated_data try: revocation_date = public_api.revoke_long_lived_token( serializer.validated_data["access_token"] ) except public_api.UnknownToken: raise exceptions.ValidationError( { "token": _("No access token known for %s") % validated_data["access_token"] } ) return Response( self.RevokeResponseSerializer({"revocation_date": revocation_date}).data, status=200, )
class AppCreationView(GenericAPIView): """ Implements an endpoint to create Oauth2 application for providers """ permission_classes = (require_scopes(SCOPE_PRV_API),) class CreationRequestSerializer(serializers.Serializer): app_name = serializers.CharField() scopes = serializers.ListField( child=serializers.CharField( help_text="Scope of the application separated by a comma." ) ) app_owner = serializers.UUIDField( help_text="The owner for which the application is generated." ) class CreationResponseSerializer(serializers.Serializer): client_id = serializers.CharField(help_text="Newly created Oauth app Client ID") client_secret = serializers.CharField( help_text="Newly created Oauth app Client Secret" ) class RevocationRequestSerializer(serializers.Serializer): app_owner = serializers.UUIDField( help_text="The owner for which the application will be revoked." ) delete = serializers.BooleanField( required=False, help_text="Indicate if the application should be removed or just revoked", default=False, ) class RevokationResponseSerializer(utils.EmptyResponseSerializer): pass def post(self, request, *args, **kwargs): serializer = self.CreationRequestSerializer(data=request.data) serializer.is_valid(raise_exception=True) validated_data = serializer.validated_data application = public_api.create_application( name=validated_data["app_name"], scopes=validated_data["scopes"], owner=validated_data["app_owner"], ) serializer = self.CreationResponseSerializer( instance={ "client_id": application["client_id"], "client_secret": application["client_secret"], } ) return Response(serializer.data, status=200) def delete(self, request, *args, **kwargs): serializer = self.RevocationRequestSerializer(data=request.data) serializer.is_valid(raise_exception=True) validated_data = serializer.validated_data try: if validated_data["delete"]: public_api.delete_application(validated_data["app_owner"]) else: public_api.revoke_application(validated_data["app_owner"]) except public_api.NoApplicationForOwner: raise exceptions.ValidationError( { "app_owner": _("No application known for owner %s") % validated_data["app_owner"] } ) return Response(self.RevokationResponseSerializer().data, status=200)
class ProviderViewSet(viewsets.ModelViewSet): permission_classes = (require_scopes(SCOPE_PRV_API), ) queryset = models.Provider.objects.with_device_categories() lookup_field = "id" serializer_class = ProviderSerializer