✨(service_providers) add API endpoints
This allow to display service providers in the frontend. Not used yet, but will allow to manage organization and teams related service providers.
This commit is contained in:
@@ -285,3 +285,12 @@ class InvitationSerializer(serializers.ModelSerializer):
|
||||
attrs["team_id"] = team_id
|
||||
attrs["issuer"] = user
|
||||
return attrs
|
||||
|
||||
|
||||
class ServiceProviderSerializer(serializers.ModelSerializer):
|
||||
"""Serialize service providers."""
|
||||
|
||||
class Meta:
|
||||
model = models.ServiceProvider
|
||||
fields = ["id", "audience_id", "name"]
|
||||
read_only_fields = ["id", "audience_id"]
|
||||
|
||||
@@ -494,3 +494,50 @@ class ConfigView(views.APIView):
|
||||
dict_settings[setting] = getattr(settings, setting)
|
||||
|
||||
return response.Response(dict_settings)
|
||||
|
||||
|
||||
class ServiceProviderFilter(filters.BaseFilterBackend):
|
||||
"""
|
||||
Filter service providers.
|
||||
"""
|
||||
|
||||
def filter_queryset(self, request, queryset, view):
|
||||
"""
|
||||
Filter service providers by audience or name.
|
||||
"""
|
||||
if name := request.GET.get("name"):
|
||||
queryset = queryset.filter(name__icontains=name)
|
||||
if audience_id := request.GET.get("audience_id"):
|
||||
queryset = queryset.filter(audience_id=audience_id)
|
||||
return queryset
|
||||
|
||||
|
||||
class ServiceProviderViewSet(
|
||||
mixins.ListModelMixin,
|
||||
mixins.RetrieveModelMixin,
|
||||
viewsets.GenericViewSet,
|
||||
):
|
||||
"""
|
||||
API ViewSet for all interactions with service providers.
|
||||
|
||||
GET /api/v1.0/service-providers/
|
||||
Return a list of service providers.
|
||||
|
||||
GET /api/v1.0/service-providers/<service_provider_id>/
|
||||
Return a service provider.
|
||||
"""
|
||||
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
queryset = models.ServiceProvider.objects.all()
|
||||
serializer_class = serializers.ServiceProviderSerializer
|
||||
throttle_classes = [BurstRateThrottle, SustainedRateThrottle]
|
||||
pagination_class = Pagination
|
||||
filter_backends = [filters.OrderingFilter, ServiceProviderFilter]
|
||||
ordering = ["name"]
|
||||
ordering_fields = ["name", "created_at"]
|
||||
|
||||
def get_queryset(self):
|
||||
"""Filter the queryset to limit results to user's organization."""
|
||||
queryset = super().get_queryset()
|
||||
queryset = queryset.filter(organizations__id=self.request.user.organization_id)
|
||||
return queryset
|
||||
|
||||
@@ -226,5 +226,20 @@ class ServiceProviderFactory(factory.django.DjangoModelFactory):
|
||||
|
||||
class Meta:
|
||||
model = models.ServiceProvider
|
||||
skip_postgeneration_save = True
|
||||
|
||||
audience_id = factory.Faker("uuid4")
|
||||
|
||||
@factory.post_generation
|
||||
def teams(self, create, extracted, **kwargs):
|
||||
"""Add teams to service provider from a given list."""
|
||||
if not create or not extracted:
|
||||
return
|
||||
self.teams.set(extracted)
|
||||
|
||||
@factory.post_generation
|
||||
def organizations(self, create, extracted, **kwargs):
|
||||
"""Add organization to service provider from a given list."""
|
||||
if not create or not extracted:
|
||||
return
|
||||
self.organizations.set(extracted)
|
||||
|
||||
3
src/backend/core/tests/service_providers/__init__.py
Normal file
3
src/backend/core/tests/service_providers/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Test for the service providers viewset.
|
||||
"""
|
||||
@@ -0,0 +1,91 @@
|
||||
"""
|
||||
Tests for Service Provider API endpoint in People's core app: list
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from rest_framework.status import HTTP_200_OK, HTTP_401_UNAUTHORIZED
|
||||
|
||||
from core import factories
|
||||
|
||||
pytestmark = pytest.mark.django_db
|
||||
|
||||
|
||||
def test_api_service_providers_list_anonymous(client):
|
||||
"""Anonymous users should not be allowed to list service providers."""
|
||||
factories.ServiceProviderFactory.create_batch(2)
|
||||
|
||||
response = client.get("/api/v1.0/service-providers/")
|
||||
|
||||
assert response.status_code == HTTP_401_UNAUTHORIZED
|
||||
assert response.json() == {
|
||||
"detail": "Authentication credentials were not provided."
|
||||
}
|
||||
|
||||
|
||||
def test_api_service_providers_list_authenticated(client):
|
||||
"""
|
||||
Authenticated users should be able to list service providers
|
||||
of their organization.
|
||||
"""
|
||||
user = factories.UserFactory(with_organization=True)
|
||||
client.force_login(user)
|
||||
|
||||
service_provider_1 = factories.ServiceProviderFactory(
|
||||
name="A", organizations=[user.organization]
|
||||
)
|
||||
service_provider_2 = factories.ServiceProviderFactory(
|
||||
name="B", organizations=[user.organization]
|
||||
)
|
||||
|
||||
# Generate some not fetched data
|
||||
factories.ServiceProviderFactory.create_batch(
|
||||
2, organizations=[factories.OrganizationFactory(with_registration_id=True)]
|
||||
) # Other service providers
|
||||
factories.TeamFactory(
|
||||
users=[user], service_providers=[factories.ServiceProviderFactory()]
|
||||
)
|
||||
|
||||
response = client.get(
|
||||
"/api/v1.0/service-providers/",
|
||||
)
|
||||
|
||||
assert response.status_code == HTTP_200_OK
|
||||
assert response.json() == {
|
||||
"count": 2,
|
||||
"next": None,
|
||||
"previous": None,
|
||||
"results": [
|
||||
{
|
||||
"audience_id": str(service_provider_1.audience_id),
|
||||
"id": str(service_provider_1.pk),
|
||||
"name": "A",
|
||||
},
|
||||
{
|
||||
"audience_id": str(service_provider_2.audience_id),
|
||||
"id": str(service_provider_2.pk),
|
||||
"name": "B",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def test_api_service_providers_order(client):
|
||||
"""Test that the service providers are sorted as requested."""
|
||||
user = factories.UserFactory(with_organization=True)
|
||||
factories.ServiceProviderFactory(name="A", organizations=[user.organization])
|
||||
factories.ServiceProviderFactory(name="B", organizations=[user.organization])
|
||||
|
||||
client.force_login(user)
|
||||
|
||||
# Test ordering by name descending
|
||||
response = client.get("/api/v1.0/service-providers/?ordering=-name")
|
||||
assert response.status_code == 200
|
||||
response_data = response.json()["results"]
|
||||
assert response_data[0]["name"] == "B"
|
||||
assert response_data[1]["name"] == "A"
|
||||
|
||||
# Test ordering by creation date ascending
|
||||
response = client.get("/api/v1.0/service-providers/?ordering=created_at")
|
||||
response_data = response.json()["results"]
|
||||
assert response_data[0]["name"] == "A"
|
||||
assert response_data[1]["name"] == "B"
|
||||
@@ -0,0 +1,85 @@
|
||||
"""
|
||||
Tests for Service Provider API endpoint in People's core app: retrieve
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from rest_framework.status import HTTP_200_OK, HTTP_401_UNAUTHORIZED, HTTP_404_NOT_FOUND
|
||||
|
||||
from core import factories
|
||||
|
||||
pytestmark = pytest.mark.django_db
|
||||
|
||||
|
||||
def test_api_service_providers_retrieve_anonymous(client):
|
||||
"""Anonymous users should not be allowed to retrieve service providers."""
|
||||
service_provider = factories.ServiceProviderFactory()
|
||||
|
||||
response = client.get(f"/api/v1.0/service-providers/{service_provider.pk}/")
|
||||
|
||||
assert response.status_code == HTTP_401_UNAUTHORIZED
|
||||
assert response.json() == {
|
||||
"detail": "Authentication credentials were not provided."
|
||||
}
|
||||
|
||||
|
||||
def test_api_service_providers_retrieve_authenticated_allowed(client):
|
||||
"""
|
||||
Authenticated users should be able to retrieve service providers
|
||||
of their organization.
|
||||
"""
|
||||
user = factories.UserFactory(with_organization=True)
|
||||
client.force_login(user)
|
||||
|
||||
service_provider = factories.ServiceProviderFactory(
|
||||
organizations=[user.organization]
|
||||
)
|
||||
|
||||
response = client.get(f"/api/v1.0/service-providers/{service_provider.pk}/")
|
||||
|
||||
assert response.status_code == HTTP_200_OK
|
||||
assert response.json() == {
|
||||
"audience_id": str(service_provider.audience_id),
|
||||
"id": str(service_provider.pk),
|
||||
"name": service_provider.name,
|
||||
}
|
||||
|
||||
|
||||
def test_api_service_providers_retrieve_authenticated_other_organization(client):
|
||||
"""
|
||||
Authenticated users should not be able to retrieve service providers
|
||||
of other organization.
|
||||
"""
|
||||
user = factories.UserFactory(with_organization=True)
|
||||
client.force_login(user)
|
||||
|
||||
service_provider = factories.ServiceProviderFactory(
|
||||
organizations=[factories.OrganizationFactory(with_registration_id=True)]
|
||||
)
|
||||
|
||||
response = client.get(f"/api/v1.0/service-providers/{service_provider.pk}/")
|
||||
|
||||
assert response.status_code == HTTP_404_NOT_FOUND
|
||||
assert response.json() == {"detail": "No ServiceProvider matches the given query."}
|
||||
|
||||
|
||||
def test_api_service_providers_retrieve_authenticated_on_teams(client):
|
||||
"""
|
||||
Authenticated users should not be able to retrieve service providers
|
||||
just because it is related to one of their teams if it is not related
|
||||
to their organization (might change later if needed).
|
||||
"""
|
||||
user = factories.UserFactory(with_organization=True)
|
||||
client.force_login(user)
|
||||
|
||||
other_organization = factories.OrganizationFactory(with_registration_id=True)
|
||||
service_provider = factories.ServiceProviderFactory()
|
||||
factories.TeamFactory(
|
||||
users=[user],
|
||||
organization=other_organization,
|
||||
service_providers=[service_provider],
|
||||
)
|
||||
|
||||
response = client.get(f"/api/v1.0/service-providers/{service_provider.pk}/")
|
||||
|
||||
assert response.status_code == HTTP_404_NOT_FOUND
|
||||
assert response.json() == {"detail": "No ServiceProvider matches the given query."}
|
||||
@@ -14,6 +14,9 @@ router = DefaultRouter()
|
||||
router.register("contacts", viewsets.ContactViewSet, basename="contacts")
|
||||
router.register("teams", viewsets.TeamViewSet, basename="teams")
|
||||
router.register("users", viewsets.UserViewSet, basename="users")
|
||||
router.register(
|
||||
"service-providers", viewsets.ServiceProviderViewSet, basename="service-providers"
|
||||
)
|
||||
|
||||
# - Routes nested under a team
|
||||
team_related_router = DefaultRouter()
|
||||
|
||||
Reference in New Issue
Block a user