From 174b161b0265e3bb89165ee114798ae5e64723f5 Mon Sep 17 00:00:00 2001 From: David Paul Graham <43794491+dpgraham4401@users.noreply.github.com> Date: Fri, 14 Jun 2024 17:36:35 -0400 Subject: [PATCH] Use django URL names in test suite (#730) * namespaces urls by app * refactor urls patterns and test for wasteline views * refactor core app tests to use named urls * refactor rcrasite urls to use named urls remove 'rcra' from our REST API endpoint URLs * refactor profile app endpoints to use namespaces and named endpoints * refactor manifest app to use namespaces and named endpoints * refactor org app to use namespaces and named endpoints * refactor site app to use namespaces and named endpoints --- .../GeneralInfo/ManifestStatusSelect.spec.tsx | 6 +- .../Handler/Search/HandlerSearchForm.spec.tsx | 11 ++-- .../SyncManifestBtn/SyncManifestBtn.spec.tsx | 2 +- .../features/NewManifest/NewManifest.spec.tsx | 2 +- .../useUserSiteIds/useUserSiteIds.spec.tsx | 2 +- client/src/store/htApi.slice.ts | 26 ++++---- client/src/store/userSlice/user.slice.ts | 8 +-- .../test-utils/mock/mockManifestEndpoints.ts | 8 +-- .../src/test-utils/mock/mockUserEndpoints.ts | 4 +- .../src/test-utils/mock/mockWasteEndpoints.ts | 4 +- server/apps/core/exceptions.py | 6 +- server/apps/core/tests/test_views.py | 64 +++++++++++++++++++ server/apps/core/urls.py | 35 +++++----- server/apps/core/views.py | 43 ++++--------- server/apps/manifest/tests/test_views.py | 41 +++++++++--- server/apps/manifest/urls.py | 34 +++++++--- server/apps/manifest/views.py | 6 +- server/apps/org/tests/test_views.py | 30 +++++++-- server/apps/org/urls.py | 3 +- server/apps/profile/tests/test_views.py | 40 ++++++++++-- server/apps/profile/urls.py | 27 ++++---- server/apps/profile/views.py | 2 +- server/apps/rcrasite/tests/test_views.py | 24 ++----- server/apps/rcrasite/urls.py | 20 +++--- server/apps/site/tests/test_views.py | 21 +++--- server/apps/site/urls.py | 7 +- .../apps/wasteline/tests/test_waste_views.py | 21 +++--- server/apps/wasteline/urls.py | 26 ++++++-- server/haztrak/urls.py | 21 ++---- 29 files changed, 334 insertions(+), 210 deletions(-) create mode 100644 server/apps/core/tests/test_views.py diff --git a/client/src/components/Manifest/GeneralInfo/ManifestStatusSelect.spec.tsx b/client/src/components/Manifest/GeneralInfo/ManifestStatusSelect.spec.tsx index ed39406f..4c2bb8be 100644 --- a/client/src/components/Manifest/GeneralInfo/ManifestStatusSelect.spec.tsx +++ b/client/src/components/Manifest/GeneralInfo/ManifestStatusSelect.spec.tsx @@ -59,7 +59,7 @@ describe('Manifest Status Field', () => { }), }); server.use( - http.get(`${API_BASE_URL}/api/user/profile`, () => { + http.get(`${API_BASE_URL}/api/profile`, () => { return HttpResponse.json( { ...createMockProfileResponse({ @@ -97,7 +97,7 @@ describe('Manifest Status Field', () => { }), }); server.use( - http.get(`${API_BASE_URL}/api/user/profile`, () => { + http.get(`${API_BASE_URL}/api/profile`, () => { return HttpResponse.json( { ...createMockProfileResponse({ @@ -131,7 +131,7 @@ describe('Manifest Status Field', () => { }), }); server.use( - http.get(`${API_BASE_URL}/api/user/profile`, () => { + http.get(`${API_BASE_URL}/api/profile`, () => { return HttpResponse.json( { ...createMockProfileResponse({ diff --git a/client/src/components/Manifest/Handler/Search/HandlerSearchForm.spec.tsx b/client/src/components/Manifest/Handler/Search/HandlerSearchForm.spec.tsx index 558f125e..85f5704a 100644 --- a/client/src/components/Manifest/Handler/Search/HandlerSearchForm.spec.tsx +++ b/client/src/components/Manifest/Handler/Search/HandlerSearchForm.spec.tsx @@ -31,16 +31,17 @@ const mockRcrainfoSite2 = createMockRcrainfoSite({ epaSiteId: mockRcrainfoSite2I const mockRcraSite1 = createMockRcrainfoSite({ epaSiteId: mockRcraSite1Id }); const mockRcraSite2 = createMockRcrainfoSite({ epaSiteId: mockRcraSite2Id }); export const mockHandlerSearches = [ - http.get(`${API_BASE_URL}/api/rcra/site/search`, () => { + http.get(`${API_BASE_URL}/api/rcrasite/search`, () => { return HttpResponse.json([mockRcraSite1, mockRcraSite2], { status: 200 }); }), - http.get(`${API_BASE_URL}/api/rcra/handler/search`, () => { + // ToDo: remove this + http.get(`${API_BASE_URL}/api/rcrainfo/rcrasite/search`, () => { return HttpResponse.json([mockRcrainfoSite1, mockRcrainfoSite2], { status: 200 }); }), - http.get(`${API_BASE_URL}/api/user/profile`, () => { + http.get(`${API_BASE_URL}/api/profile`, () => { return HttpResponse.json({ ...mockProfile }, { status: 200 }); }), - http.post(`${API_BASE_URL}/api/rcra/handler/search`, () => { + http.post(`${API_BASE_URL}/api/rcrainfo/rcrasite/search`, () => { return HttpResponse.json([mockRcrainfoSite1, mockRcrainfoSite2], { status: 200 }); }), ]; @@ -72,7 +73,7 @@ describe('HandlerSearchForm', () => { }); test('retrieves rcra sites from haztrak if org not rcrainfo integrated', async () => { server.use( - http.get(`${API_BASE_URL}/api/user/profile`, () => { + http.get(`${API_BASE_URL}/api/profile`, () => { return HttpResponse.json( { ...mockProfile, diff --git a/client/src/components/Rcrainfo/buttons/SyncManifestBtn/SyncManifestBtn.spec.tsx b/client/src/components/Rcrainfo/buttons/SyncManifestBtn/SyncManifestBtn.spec.tsx index 1f2092f6..8b501990 100644 --- a/client/src/components/Rcrainfo/buttons/SyncManifestBtn/SyncManifestBtn.spec.tsx +++ b/client/src/components/Rcrainfo/buttons/SyncManifestBtn/SyncManifestBtn.spec.tsx @@ -12,7 +12,7 @@ const testTaskID = 'testTaskId'; const server = setupServer(...mockUserEndpoints); server.use( - http.post(`${API_BASE_URL}rcra/manifest/emanifest/sync`, () => { + http.post(`${API_BASE_URL}manifest/emanifest/sync`, () => { // Mock Sync Site Manifests response return HttpResponse.json( { diff --git a/client/src/features/NewManifest/NewManifest.spec.tsx b/client/src/features/NewManifest/NewManifest.spec.tsx index 4a39fd57..515af5a0 100644 --- a/client/src/features/NewManifest/NewManifest.spec.tsx +++ b/client/src/features/NewManifest/NewManifest.spec.tsx @@ -31,7 +31,7 @@ const mockProfile: HaztrakProfileResponse = { const server = setupServer(...mockUserEndpoints); server.use( - http.get(`${API_BASE_URL}/api/user/profile`, () => { + http.get(`${API_BASE_URL}/api/profile`, () => { return HttpResponse.json({ ...mockProfile }, { status: 200 }); }) ); diff --git a/client/src/hooks/useUserSiteIds/useUserSiteIds.spec.tsx b/client/src/hooks/useUserSiteIds/useUserSiteIds.spec.tsx index 0cd79046..6717d0d4 100644 --- a/client/src/hooks/useUserSiteIds/useUserSiteIds.spec.tsx +++ b/client/src/hooks/useUserSiteIds/useUserSiteIds.spec.tsx @@ -45,7 +45,7 @@ describe('useUserSiteId hook', () => { }), }); server.use( - http.get(`${API_BASE_URL}/api/user/profile`, () => { + http.get(`${API_BASE_URL}/api/profile`, () => { return HttpResponse.json( { ...createMockProfileResponse({ diff --git a/client/src/store/htApi.slice.ts b/client/src/store/htApi.slice.ts index b86b1d3e..9f75ea6d 100644 --- a/client/src/store/htApi.slice.ts +++ b/client/src/store/htApi.slice.ts @@ -81,21 +81,21 @@ export const haztrakApi = createApi({ // Note: build.query searchRcrainfoSites: build.query, RcrainfoSiteSearch>({ query: (data: RcrainfoSiteSearch) => ({ - url: 'rcra/handler/search', + url: 'rcrainfo/rcrasite/search', method: 'post', data: data, }), }), searchRcraSites: build.query, RcrainfoSiteSearch>({ query: (data: RcrainfoSiteSearch) => ({ - url: 'rcra/site/search', + url: 'rcrasite/search', method: 'get', params: { epaId: data.siteId, siteType: data.siteType }, }), }), getRcrainfoSite: build.query({ query: (epaSiteId) => ({ - url: `rcra/handler/${epaSiteId}`, + url: `rcrasite/${epaSiteId}`, method: 'get', }), }), @@ -103,15 +103,15 @@ export const haztrakApi = createApi({ query: (taskId) => ({ url: `task/${taskId}`, method: 'get' }), }), getFedWasteCodes: build.query, void>({ - query: () => ({ url: 'rcra/waste/code/federal', method: 'get' }), + query: () => ({ url: 'waste/code/federal', method: 'get' }), providesTags: ['code'], }), getStateWasteCodes: build.query, string>({ - query: (state) => ({ url: `rcra/waste/code/state/${state}`, method: 'get' }), + query: (state) => ({ url: `waste/code/state/${state}`, method: 'get' }), providesTags: ['code'], }), getDotIdNumbers: build.query, string>({ - query: (id) => ({ url: 'rcra/waste/dot/id', method: 'get', params: { q: id } }), + query: (id) => ({ url: 'waste/dot/id', method: 'get', params: { q: id } }), providesTags: ['code'], }), getOrgSites: build.query, string>({ @@ -128,18 +128,18 @@ export const haztrakApi = createApi({ }), getMTN: build.query, string | undefined>({ query: (siteId) => ({ - url: siteId ? `rcra/manifest/mtn/${siteId}` : 'rcra/manifest/mtn', + url: siteId ? `manifest/mtn/${siteId}` : 'manifest/mtn', method: 'get', }), providesTags: ['manifest'], }), getManifest: build.query({ - query: (mtn) => ({ url: `rcra/manifest/${mtn}`, method: 'get' }), + query: (mtn) => ({ url: `manifest/${mtn}`, method: 'get' }), providesTags: ['manifest'], }), createManifest: build.mutation({ query: (data) => ({ - url: 'rcra/manifest', + url: 'manifest', method: 'POST', data, }), @@ -147,7 +147,7 @@ export const haztrakApi = createApi({ }), updateManifest: build.mutation({ query: ({ mtn, manifest }) => ({ - url: `rcra/manifest/${mtn}`, + url: `manifest/${mtn}`, method: 'PUT', data: manifest, }), @@ -155,7 +155,7 @@ export const haztrakApi = createApi({ }), saveEManifest: build.mutation({ query: (data) => ({ - url: 'rcra/manifest/emanifest', + url: 'manifest/emanifest', method: 'POST', data, }), @@ -163,7 +163,7 @@ export const haztrakApi = createApi({ }), syncEManifest: build.mutation({ query: (siteId) => ({ - url: 'rcra/manifest/emanifest/sync', + url: 'manifest/emanifest/sync', method: 'POST', data: { siteId: siteId }, }), @@ -171,7 +171,7 @@ export const haztrakApi = createApi({ }), signEManifest: build.mutation({ query: (signature) => ({ - url: 'rcra/manifest/emanifest/sign', + url: 'manifest/emanifest/sign', method: 'POST', data: signature, }), diff --git a/client/src/store/userSlice/user.slice.ts b/client/src/store/userSlice/user.slice.ts index 28973b47..c2dc02e4 100644 --- a/client/src/store/userSlice/user.slice.ts +++ b/client/src/store/userSlice/user.slice.ts @@ -112,7 +112,7 @@ export const userApi = haztrakApi.injectEndpoints({ }), getProfile: build.query({ query: () => ({ - url: 'user/profile', + url: 'profile', method: 'GET', }), providesTags: ['profile'], @@ -135,7 +135,7 @@ export const userApi = haztrakApi.injectEndpoints({ }), getRcrainfoProfile: build.query({ query: (username) => ({ - url: `user/rcrainfo-profile/${username}`, + url: `rcrainfo-profile/${username}`, method: 'GET', }), providesTags: ['rcrainfoProfile'], @@ -154,7 +154,7 @@ export const userApi = haztrakApi.injectEndpoints({ }), updateRcrainfoProfile: build.mutation({ query: (data) => ({ - url: `user/rcrainfo-profile/${data.username}`, + url: `rcrainfo-profile/${data.username}`, method: 'PUT', data: data.data, }), @@ -162,7 +162,7 @@ export const userApi = haztrakApi.injectEndpoints({ }), syncRcrainfoProfile: build.mutation({ query: () => ({ - url: `user/rcrainfo-profile/sync`, + url: `rcrainfo-profile/sync`, method: 'POST', }), invalidatesTags: ['rcrainfoProfile'], diff --git a/client/src/test-utils/mock/mockManifestEndpoints.ts b/client/src/test-utils/mock/mockManifestEndpoints.ts index a51ae61d..97545f55 100644 --- a/client/src/test-utils/mock/mockManifestEndpoints.ts +++ b/client/src/test-utils/mock/mockManifestEndpoints.ts @@ -11,18 +11,18 @@ const generateRandomMTN = (): string => { export const mockManifestEndpoints = [ /** mock GET Manifest*/ - http.get(`${API_BASE_URL}/api/rcra/manifest/${mockMTN}`, (info) => { + http.get(`${API_BASE_URL}/api/manifest/${mockMTN}`, (info) => { return HttpResponse.json(createMockManifest(), { status: 200 }); }), /** Mock create local Manifests*/ - http.post(`${API_BASE_URL}/api/rcra/manifest`, async (info) => { + http.post(`${API_BASE_URL}/api/manifest`, async (info) => { let bodyManifest = (await info.request.json()) as Manifest; if (!bodyManifest.manifestTrackingNumber) bodyManifest.manifestTrackingNumber = `${generateRandomMTN()}DFT`.padEnd(9, '0'); return HttpResponse.json(bodyManifest, { status: 200 }); }), /** Mock update local Manifests*/ - http.put(`${API_BASE_URL}/api/rcra/manifest/:mtn`, async (info) => { + http.put(`${API_BASE_URL}/api/manifest/:mtn`, async (info) => { const { mtn } = info.params; let bodyManifest = (await info.request.json()) as Manifest; if (bodyManifest.manifestTrackingNumber !== mtn) @@ -30,7 +30,7 @@ export const mockManifestEndpoints = [ return HttpResponse.json(bodyManifest, { status: 200 }); }), /** list of manifests ('My Manifests' feature and a site's manifests)*/ - http.get(`${API_BASE_URL}/api/rcra/mtn*`, (info) => { + http.get(`${API_BASE_URL}/api/mtn*`, (info) => { const mockManifestArray = [ createMockManifest(), createMockManifest({ manifestTrackingNumber: '987654321ELC', status: 'Pending' }), diff --git a/client/src/test-utils/mock/mockUserEndpoints.ts b/client/src/test-utils/mock/mockUserEndpoints.ts index 564af424..50365f87 100644 --- a/client/src/test-utils/mock/mockUserEndpoints.ts +++ b/client/src/test-utils/mock/mockUserEndpoints.ts @@ -20,7 +20,7 @@ export const mockUserEndpoints = [ return HttpResponse.json({ ...user, ...info.request.body }, { status: 200 }); }), /** GET Profile */ - http.get(`${API_BASE_URL}/api/user/profile`, () => { + http.get(`${API_BASE_URL}/api/profile`, () => { return HttpResponse.json({ ...createMockProfileResponse() }, { status: 200 }); }), /** Login */ @@ -34,7 +34,7 @@ export const mockUserEndpoints = [ ); }), /** GET RCRAInfo profile */ - http.get(`${API_BASE_URL}/api/user/rcrainfo-profile/:username`, (info) => { + http.get(`${API_BASE_URL}/api/rcrainfo-profile/:username`, (info) => { const { username } = info.params; // @ts-ignore const rcrainfoProfile = createMockRcrainfoProfileResponse({ user: username ?? '' }); diff --git a/client/src/test-utils/mock/mockWasteEndpoints.ts b/client/src/test-utils/mock/mockWasteEndpoints.ts index 4235f59f..493bc2e4 100644 --- a/client/src/test-utils/mock/mockWasteEndpoints.ts +++ b/client/src/test-utils/mock/mockWasteEndpoints.ts @@ -5,10 +5,10 @@ import { mockDotIdNumbers, mockFederalWasteCodes } from 'test-utils/fixtures/moc const API_BASE_URL = import.meta.env.VITE_HT_API_URL; export const mockWasteEndpoints = [ /** GET User */ - http.get(`${API_BASE_URL}/api/rcra/waste/code/federal`, (info) => { + http.get(`${API_BASE_URL}/api/waste/code/federal`, (info) => { return HttpResponse.json(mockFederalWasteCodes, { status: 200 }); }), - http.get(`${API_BASE_URL}/api/rcra/waste/dot/id`, (info) => { + http.get(`${API_BASE_URL}/api/waste/dot/id`, (info) => { const url = new URL(info.request.url); const query = url.searchParams.get('q'); let filteredIds: Array = []; diff --git a/server/apps/core/exceptions.py b/server/apps/core/exceptions.py index 7c64f52c..5ff5af00 100644 --- a/server/apps/core/exceptions.py +++ b/server/apps/core/exceptions.py @@ -1,8 +1,8 @@ -from django.core.exceptions import PermissionDenied +from django.core.exceptions import ObjectDoesNotExist, PermissionDenied from django.core.exceptions import ValidationError as DjangoValidationError from django.http import Http404 from rest_framework import exceptions -from rest_framework.exceptions import APIException +from rest_framework.exceptions import APIException, NotFound from rest_framework.serializers import as_serializer_error from rest_framework.views import exception_handler @@ -32,6 +32,8 @@ def haztrak_exception_handler(exc, context): exc = exceptions.PermissionDenied() case Http404(): exc = exceptions.NotFound() + case ObjectDoesNotExist(): + exc = NotFound() case KeyError(): exc = exceptions.ParseError() case ValueError(): diff --git a/server/apps/core/tests/test_views.py b/server/apps/core/tests/test_views.py new file mode 100644 index 00000000..1ac0e3db --- /dev/null +++ b/server/apps/core/tests/test_views.py @@ -0,0 +1,64 @@ +from unittest import mock + +import pytest +from django.test.client import Client +from rest_framework import status +from rest_framework.reverse import reverse +from rest_framework.test import APIRequestFactory, force_authenticate + +from apps.core.views import LaunchExampleTaskView, TaskStatusView + + +class TestUserViews: + @pytest.fixture + def factory(self): + return APIRequestFactory() + + @pytest.fixture + def user(self, user_factory): + return user_factory() + + def test_returns_user_details(self, factory, user): + c = Client() + c.force_login(user) + response = c.get(reverse("core:user:details")) + data: dict = response.json() + assert response.status_code == 200 + assert user.username in data.values() + assert user.first_name in data.values() + + +class TestLaunchExampleTaskView: + @pytest.fixture + def factory(self): + return APIRequestFactory() + + def test_successful_launch(self, factory): + with mock.patch("apps.core.views.launch_example_task") as mock_task: + request = factory.get(reverse("core:task:example")) + mock_task.return_value = "123" + response = LaunchExampleTaskView.as_view()(request) + assert response.status_code == status.HTTP_200_OK + assert response.data == {"taskId": "123"} + + +class TestTaskStatusView: + @pytest.fixture + def factory(self): + return APIRequestFactory() + + def test_successful_get_status(self, factory, user_factory): + user = user_factory() + task_id = "123" + request = factory.get(reverse("core:task:status", args=[task_id])) + force_authenticate(request, user) + with mock.patch("apps.core.views.get_task_status") as mock_task: + mock_task.return_value = { + "status": "PENDING", + "name": "task_name", + "result": None, + "taskId": task_id, + } + response = TaskStatusView.as_view()(request, task_id=task_id) + assert response.status_code == status.HTTP_200_OK + assert "PENDING" in response.data.values() diff --git a/server/apps/core/urls.py b/server/apps/core/urls.py index e4fb349c..9ad4ca3a 100644 --- a/server/apps/core/urls.py +++ b/server/apps/core/urls.py @@ -1,22 +1,27 @@ from dj_rest_auth.views import LoginView, LogoutView, UserDetailsView from django.urls import include, path -from .views import ( - LaunchExampleTaskView, - TaskStatusView, +from .views import LaunchExampleTaskView, TaskStatusView + +user_patterns = ( + [ + path("", UserDetailsView.as_view(), name="details"), + path("/login", LoginView.as_view(), name="login"), + path("/logout", LogoutView.as_view(), name="logout"), + ], + "user", +) + +task_patterns = ( + [ + path("/example", LaunchExampleTaskView.as_view(), name="example"), + path("/", TaskStatusView.as_view(), name="status"), + ], + "task", ) +app_name = "core" urlpatterns = [ - path("task/example", LaunchExampleTaskView.as_view()), - path("task/", TaskStatusView.as_view()), - path( - "user", - include( - [ - path("", UserDetailsView.as_view(), name="rest_user_details"), - path("/login", LoginView.as_view(), name="rest_login"), - path("/logout", LogoutView.as_view(), name="rest_logout"), - ] - ), - ), + path("task", include(task_patterns)), + path("user", include(user_patterns)), ] diff --git a/server/apps/core/views.py b/server/apps/core/views.py index 90f45988..b180f8a0 100644 --- a/server/apps/core/views.py +++ b/server/apps/core/views.py @@ -1,36 +1,15 @@ from django_celery_results.models import TaskResult -from rest_framework import permissions, serializers, status +from rest_framework import permissions, status from rest_framework.exceptions import ValidationError from rest_framework.request import Request from rest_framework.response import Response from rest_framework.views import APIView -from apps.core.services.task_service import ( - get_task_status, - launch_example_task, -) - - -class CeleryTaskResultSerializer(serializers.ModelSerializer): - """ - Serializer for results of long-running celery tasks - from django-celery-results - """ - - taskId = serializers.CharField(source="task_id", read_only=True) - taskName = serializers.CharField(source="task_name", read_only=True) - createdDate = serializers.DateTimeField(source="date_created", read_only=True) - doneDate = serializers.DateTimeField(source="date_done", read_only=True) - - class Meta: - model = TaskResult - fields = ["taskId", "taskName", "status", "createdDate", "doneDate"] +from apps.core.services.task_service import get_task_status, launch_example_task class LaunchExampleTaskView(APIView): - """ - Launches an example long-running background task - """ + """Launches an example long-running background task""" permission_classes = [permissions.AllowAny] @@ -45,10 +24,7 @@ def get(self, request, *args, **kwargs): class TaskStatusView(APIView): - """ - Endpoint for retrieving the status of long-running celery tasks - Uses django-celery-results pacakge which stores the task results in our DB - """ + """retrieve the status of long-running tasks""" queryset = TaskResult.objects.all() @@ -56,11 +32,14 @@ def get(self, request: Request, task_id): try: data = get_task_status(task_id) return Response(data=data, status=status.HTTP_200_OK) - except KeyError as exc: - return Response(data={"error": str(exc)}, status=status.HTTP_400_BAD_REQUEST) - except ValidationError as exc: + except KeyError: + return Response( + data={"error": "malformed request"}, status=status.HTTP_400_BAD_REQUEST + ) + except ValidationError: return Response( - data={"error": exc.detail}, status=status.HTTP_500_INTERNAL_SERVER_ERROR + data={"error": "problem validating request"}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR, ) except TaskResult.DoesNotExist: return Response(data={"taskId": "unknown"}, status=status.HTTP_200_OK) diff --git a/server/apps/manifest/tests/test_views.py b/server/apps/manifest/tests/test_views.py index 6a9ce90f..8166778e 100644 --- a/server/apps/manifest/tests/test_views.py +++ b/server/apps/manifest/tests/test_views.py @@ -1,16 +1,17 @@ +from unittest.mock import patch + import pytest from celery.result import AsyncResult from rest_framework import status +from rest_framework.reverse import reverse from rest_framework.test import APIRequestFactory, force_authenticate -from apps.manifest.views import ElectronicManifestSignView, ManifestViewSet +from apps.manifest.views import ElectronicManifestSignView, ManifestViewSet, MtnListView class TestManifestCRUD: """Tests the for the Manifest ModelViewSet""" - base_url = "/api/rcra/manifest" - @pytest.fixture def factory(self): return APIRequestFactory() @@ -29,19 +30,18 @@ def manifest(self, manifest_factory): def test_returns_manifest_by_mtn(self, factory, manifest, manifest_json, user): # Arrange - request = factory.get(f"{self.base_url}/{manifest.mtn}") + request = factory.get(reverse("manifest:manifest-detail", args=[manifest.mtn])) force_authenticate(request, user) # Act response = ManifestViewSet.as_view({"get": "retrieve"})(request, mtn=manifest.mtn) # Assert assert response.status_code == status.HTTP_200_OK - assert response.data["manifestTrackingNumber"] == manifest.mtn + assert manifest.mtn in response.data.values() class TestSignManifestVIew: """Quicker Sign endpoint test suite""" - base_url = "/api/manifest/emanifest/sign" factory = APIRequestFactory() mtn = ["123456789ELC", "987654321ELC"] @@ -63,10 +63,10 @@ def test_returns_celery_task_id( profile_factory(user=user) org_access_factory(user=user, org=org) request = self.factory.post( - f"{self.base_url}", + reverse("manifest:emanifest:sign"), data={ "manifestTrackingNumbers": self.mtn, - "printedSignatureName": "Joe Blow", + "printedSignatureName": "John Doe", "siteType": "Generator", "siteId": "VATESTGEN001", }, @@ -75,3 +75,28 @@ def test_returns_celery_task_id( force_authenticate(request, user) response = ElectronicManifestSignView.as_view()(request) assert response.data["taskId"] == self.mock_task_id + + +class TestMtnListView: + @pytest.fixture + def factory(self): + return APIRequestFactory() + + @pytest.fixture + def user(self, user_factory): + return user_factory() + + def test_returns_empty_list_if_no_manifests(self, factory, user): + with patch("apps.manifest.views.get_manifests") as mock_get_manifests: + mock_get_manifests.return_value = [] + request = factory.get(reverse("manifest:mtn:list")) + force_authenticate(request, user) + response = MtnListView.as_view()(request) + assert isinstance(response.data, list) + + def test_401_if_not_authorized(self, factory, user): + with patch("apps.manifest.views.get_manifests") as mock_get_manifests: + mock_get_manifests.return_value = [] + request = factory.get(reverse("manifest:mtn:list")) + response = MtnListView.as_view()(request) + assert response.status_code == status.HTTP_401_UNAUTHORIZED diff --git a/server/apps/manifest/urls.py b/server/apps/manifest/urls.py index d859d4b7..ae336967 100644 --- a/server/apps/manifest/urls.py +++ b/server/apps/manifest/urls.py @@ -12,19 +12,35 @@ manifest_router = SimpleRouter(trailing_slash=False) manifest_router.register("", ManifestViewSet, basename="manifest") +emanifest_patterns = ( + [ + path("", ElectronicManifestSaveView.as_view(), name="save"), + path("/sign", ElectronicManifestSignView.as_view(), name="sign"), + path("/sync", SiteManifestSyncView.as_view(), name="sync"), + ], + "emanifest", +) + +mtn_patterns = ( + [ + path("", MtnListView.as_view(), name="list"), + path("/", MtnListView.as_view(), name="site-list"), + path("//", MtnListView.as_view(), name="site-type-list"), + ], + "mtn", +) + +app_name = "manifest" urlpatterns = [ path( - "manifest/", + "manifest", include( [ - # Manifest - path("emanifest", ElectronicManifestSaveView.as_view()), - path("emanifest/sign", ElectronicManifestSignView.as_view()), - path("emanifest/sync", SiteManifestSyncView.as_view()), - # MT - path("mtn", MtnListView.as_view()), - path("mtn/", MtnListView.as_view()), - path("mtn//", MtnListView.as_view()), + # e-Manifest + path("/emanifest", include(emanifest_patterns)), + # Manifest Tracking Numbers + path("/mtn", include(mtn_patterns)), + # Manifests path("", include(manifest_router.urls)), ] ), diff --git a/server/apps/manifest/views.py b/server/apps/manifest/views.py index 22aeb9c8..ee329a0b 100644 --- a/server/apps/manifest/views.py +++ b/server/apps/manifest/views.py @@ -82,15 +82,11 @@ def post(self, request: Request) -> Response: class MtnListView(ListAPIView): - """List of manifest tracking numbers and select details.""" + """List of manifest tracking numbers and select details. Filter by EPA ID and site type.""" serializer_class = MtnSerializer queryset = Manifest.objects.all() - def get(self, request, *args, **kwargs): - print(f" MTN List View {request.user}") - return super().get(request, *args, **kwargs) - def get_queryset(self): return get_manifests( username=str(self.request.user), diff --git a/server/apps/org/tests/test_views.py b/server/apps/org/tests/test_views.py index e314ccef..e4f49879 100644 --- a/server/apps/org/tests/test_views.py +++ b/server/apps/org/tests/test_views.py @@ -1,21 +1,37 @@ +from unittest.mock import patch + +import pytest from rest_framework import status +from rest_framework.reverse import reverse from rest_framework.test import APIRequestFactory, force_authenticate +from apps.org.models import Org from apps.org.views import OrgDetailsView class TestOrgDetailsView: - base_url = "/api/org" factory = APIRequestFactory() - def test_get_returns_org_details(self, org_factory, org_access_factory, user_factory): - org = org_factory() - user = user_factory() + @pytest.fixture + def user(self, user_factory): + return user_factory() + + @pytest.fixture + def org(self, org_factory): + return org_factory() + + def test_get_returns_org_details(self, org, org_access_factory, user): org_access_factory(org=org, user=user) - request = self.factory.get( - f"{self.base_url}/{org.id}", - ) + request = self.factory.get(reverse("org:details", args=[org.id])) force_authenticate(request, user) response = OrgDetailsView.as_view()(request, org_id=org.id) assert response.status_code == status.HTTP_200_OK assert response.data["id"] == org.id + + def test_404_when_org_id_not_defined(self, org, org_access_factory, user): + request = self.factory.get(reverse("org:details", args=["foo"])) + force_authenticate(request, user) + with patch("apps.org.views.get_org_by_id") as mock_org: + mock_org.side_effect = Org.DoesNotExist + response = OrgDetailsView.as_view()(request, org_id="foo") + assert response.status_code == status.HTTP_404_NOT_FOUND diff --git a/server/apps/org/urls.py b/server/apps/org/urls.py index 491a39db..e794468c 100644 --- a/server/apps/org/urls.py +++ b/server/apps/org/urls.py @@ -2,6 +2,7 @@ from apps.org.views import OrgDetailsView +app_name = "org" urlpatterns = [ - path("org/", OrgDetailsView.as_view()), + path("org/", OrgDetailsView.as_view(), name="details"), ] diff --git a/server/apps/profile/tests/test_views.py b/server/apps/profile/tests/test_views.py index a3e51c5a..7558fa94 100644 --- a/server/apps/profile/tests/test_views.py +++ b/server/apps/profile/tests/test_views.py @@ -1,11 +1,12 @@ +import pytest from rest_framework import status +from rest_framework.reverse import reverse from rest_framework.test import APIRequestFactory, force_authenticate -from apps.profile.views import RcrainfoProfileRetrieveUpdateView +from apps.profile.views import ProfileDetailsView, RcrainfoProfileRetrieveUpdateView class TestRcrainfoProfileRetrieveUpdateView: - base_url = "/api/user/rcrainfo-profile" factory = APIRequestFactory() id_field = "rcraAPIID" @@ -19,7 +20,7 @@ def test_returns_rcrainfo_profile_details( rcrainfo_profile = rcrainfo_profile_factory() profile_factory(user=user, rcrainfo_profile=rcrainfo_profile) request = self.factory.get( - f"{self.base_url}/{user.username}", + reverse("profile:rcrainfo:retrieve-update", args=[user.username]) ) force_authenticate(request, user) response = RcrainfoProfileRetrieveUpdateView.as_view()(request, username=user.username) @@ -35,7 +36,7 @@ def test_does_not_return_the_api_key_but_does_return_api_id( rcrainfo_profile = rcrainfo_profile_factory(rcra_api_key=my_key, rcra_api_id=my_id) profile_factory(user=user, rcrainfo_profile=rcrainfo_profile) request = self.factory.get( - f"{self.base_url}/{user.username}", + reverse("profile:rcrainfo:retrieve-update", args=[user.username]) ) force_authenticate(request, user) response = RcrainfoProfileRetrieveUpdateView.as_view()(request, username=user.username) @@ -49,7 +50,7 @@ def test_returns_a_user_profile_in_json_representation( client = api_client_factory(user=user) rcra_profile = rcrainfo_profile_factory() profile_factory(user=user, rcrainfo_profile=rcra_profile) - response = client.get(f"{self.base_url}/{user.username}") + response = client.get(reverse("profile:rcrainfo:retrieve-update", args=[user.username])) assert response.headers["Content-Type"] == "application/json" assert response.status_code == status.HTTP_200_OK @@ -62,7 +63,7 @@ def test_rcrainfo_profile_updates( profile_factory(user=user, rcrainfo_profile=rcra_profile) factory = APIRequestFactory() request = factory.put( - f"{self.base_url}/{user.username}", + reverse("profile:rcrainfo:retrieve-update", args=[user.username]), { self.id_field: rcra_profile.rcra_api_id, self.username_field: user.username, @@ -85,7 +86,7 @@ def test_update_does_not_return_api_key( profile = profile_factory(user=user, rcrainfo_profile=rcra_profile) factory = APIRequestFactory() request = factory.put( - f"{self.base_url}/{user.username}", + reverse("profile:rcrainfo:retrieve-update", args=[user.username]), { self.id_field: rcra_profile.rcra_api_id, self.username_field: user.username, @@ -101,3 +102,28 @@ def test_update_does_not_return_api_key( # Assert assert self.key_field not in response.data assert self.id_field in response.data + + +class TestProfileDetailsView: + @pytest.fixture + def request_factory(self, user_factory): + user = user_factory() + factory = APIRequestFactory() + request = factory.get(reverse("profile:details")) + force_authenticate(request, user) + return request, user + + def test_returns_profile(self, request_factory, profile_factory): + request, user = request_factory + profile_factory(user=user) + response = ProfileDetailsView.as_view()(request) + assert response.status_code == status.HTTP_200_OK + assert "user" in response.data.keys() + + def test_returns_401_when_unauthenticated(self, profile_factory, user_factory): + user = user_factory() + factory = APIRequestFactory() + request = factory.get(reverse("profile:details")) + profile_factory(user=user) + response = ProfileDetailsView.as_view()(request) + assert response.status_code == status.HTTP_401_UNAUTHORIZED diff --git a/server/apps/profile/urls.py b/server/apps/profile/urls.py index 13ebca8c..c7f8661d 100644 --- a/server/apps/profile/urls.py +++ b/server/apps/profile/urls.py @@ -1,22 +1,23 @@ from django.urls import include, path from .views import ( + ProfileDetailsView, RcrainfoProfileRetrieveUpdateView, RcrainfoProfileSyncView, - TrakProfileDetailsView, ) -urlpatterns = [ - path( - "user", - include( - [ - path("/profile", TrakProfileDetailsView.as_view()), - path("/rcrainfo-profile/sync", RcrainfoProfileSyncView.as_view()), - path( - "/rcrainfo-profile/", RcrainfoProfileRetrieveUpdateView.as_view() - ), - ] +rcrainfo_profile_patterns = ( + [ + path("/sync", RcrainfoProfileSyncView.as_view(), name="sync"), + path( + "/", RcrainfoProfileRetrieveUpdateView.as_view(), name="retrieve-update" ), - ), + ], + "rcrainfo", +) + +app_name = "profile" +urlpatterns = [ + path("profile", ProfileDetailsView.as_view(), name="details"), + path("rcrainfo-profile", include(rcrainfo_profile_patterns)), ] diff --git a/server/apps/profile/views.py b/server/apps/profile/views.py index 8cbe7296..4d014d11 100644 --- a/server/apps/profile/views.py +++ b/server/apps/profile/views.py @@ -11,7 +11,7 @@ from apps.rcrasite.tasks import sync_user_rcrainfo_sites -class TrakProfileDetailsView(RetrieveAPIView): +class ProfileDetailsView(RetrieveAPIView): """Displays a user's HaztrakProfile""" queryset = Profile.objects.all() diff --git a/server/apps/rcrasite/tests/test_views.py b/server/apps/rcrasite/tests/test_views.py index 98f7da5c..8d7cdaac 100644 --- a/server/apps/rcrasite/tests/test_views.py +++ b/server/apps/rcrasite/tests/test_views.py @@ -3,6 +3,7 @@ import pytest from rest_framework import status from rest_framework.response import Response +from rest_framework.reverse import reverse from rest_framework.test import APIClient, APIRequestFactory, force_authenticate from apps.rcrasite.models import RcraSiteType # type: ignore @@ -12,8 +13,6 @@ class TestRcraSiteView: """Handler endpoints test suite""" - URL = "/api/rcra/handler" - @pytest.fixture def client(self, api_client_factory): return api_client_factory() @@ -23,7 +22,7 @@ def generator(self, rcra_site_factory): return rcra_site_factory() def test_endpoint_returns_json_with_rcra_site(self, client, generator): - response: Response = client.get(f"{self.URL}/{generator.epa_id}") + response: Response = client.get(reverse("rcrasite:details", args=[generator.epa_id])) assert response.headers["Content-Type"] == "application/json" assert response.status_code == status.HTTP_200_OK assert response.data["epaSiteId"] == generator.epa_id @@ -34,7 +33,7 @@ class TestRcraSiteSearchView: Tests for the RcraSite Search endpoint """ - URL = "/api/rcra/site/search" + URL = reverse("rcrasite:search") def test_view_returns_array_of_handlers(self, user_factory, rcra_site_factory): # Arrange @@ -106,20 +105,9 @@ def test_endpoint_returns_200_if_bad_query_params( assert response.headers["Content-Type"] == "application/json" -# class TestHandlerSearchView: -# -# @patch("apps.rcrasite.services.RcraSiteService.search_rcrainfo_handlers") -# def test_endpoint_returns_404_if_no_epa_id(self, client): -# response: Response = client.get("/api/rcra/handler/") -# - - class TestHandlerSearchView: - base_url = "/api/site/search" - @pytest.fixture(autouse=True) def set_up(self): - self.view = HandlerSearchView() self.request_factory = APIRequestFactory() def test_valid_search_returns_200(self, user_factory): @@ -129,7 +117,7 @@ def test_valid_search_returns_200(self, user_factory): user = user_factory() data = {"siteId": "VAT000000000", "siteType": "designatedFacility"} request = self.request_factory.post( - f"{self.base_url}", + reverse("rcrasite:rcrainfo:search"), data=data, format="json", ) @@ -141,7 +129,7 @@ def test_short_epa_id_returns_400(self, user_factory): user = user_factory() data = {"siteId": "V", "siteType": "designatedFacility"} request = self.request_factory.post( - f"{self.base_url}", + reverse("rcrasite:rcrainfo:search"), data=data, format="json", ) @@ -153,7 +141,7 @@ def test_null_epa_id_returns_400(self, user_factory): user = user_factory() data = {"siteType": "designatedFacility"} request = self.request_factory.post( - f"{self.base_url}", + reverse("rcrasite:rcrainfo:search"), data=data, format="json", ) diff --git a/server/apps/rcrasite/urls.py b/server/apps/rcrasite/urls.py index 9c45bbe2..f3e81df8 100644 --- a/server/apps/rcrasite/urls.py +++ b/server/apps/rcrasite/urls.py @@ -1,13 +1,17 @@ -from django.urls import path +from django.urls import include, path -from apps.rcrasite.views import ( # type: ignore - HandlerSearchView, - RcraSiteDetailsView, - RcraSiteSearchView, +from apps.rcrasite.views import HandlerSearchView, RcraSiteDetailsView, RcraSiteSearchView + +rcrainfo_rcrasite_patterns = ( + [ + path("/rcrasite/search", HandlerSearchView.as_view(), name="search"), + ], + "rcrainfo", ) +app_name = "rcrasite" urlpatterns = [ - path("handler/search", HandlerSearchView.as_view()), - path("handler/", RcraSiteDetailsView.as_view()), - path("site/search", RcraSiteSearchView.as_view()), + path("rcrainfo", include(rcrainfo_rcrasite_patterns)), + path("rcrasite/search", RcraSiteSearchView.as_view(), name="search"), + path("rcrasite/", RcraSiteDetailsView.as_view(), name="details"), ] diff --git a/server/apps/site/tests/test_views.py b/server/apps/site/tests/test_views.py index 5adea443..812e8955 100644 --- a/server/apps/site/tests/test_views.py +++ b/server/apps/site/tests/test_views.py @@ -1,5 +1,6 @@ import pytest from rest_framework import status +from rest_framework.reverse import reverse from rest_framework.test import APIClient, APIRequestFactory, force_authenticate from apps.site.views import SiteDetailsView @@ -21,20 +22,18 @@ def api_client( self.site_permissions = site_access_factory(user=self.user, site=self.user_site) self.other_site = site_factory(rcra_site=rcra_site_factory(epa_id="VA12345678")) - base_url = "/api/site" - def test_responds_with_site_in_json_format(self, api_client): - response = self.client.get(f"{self.base_url}") + response = self.client.get(reverse("site:list")) assert response.status_code == status.HTTP_200_OK def test_other_sites_not_included(self, api_client): - response = self.client.get(f"{self.base_url}") + response = self.client.get(reverse("site:list")) response_site_id = [i["handler"]["epaSiteId"] for i in response.data] assert self.other_site.rcra_site.epa_id not in response_site_id def test_unauthenticated_returns_401(self, api_client): self.client.logout() - response = self.client.get(f"{self.base_url}") + response = self.client.get(reverse("site:list")) assert response.status_code == status.HTTP_401_UNAUTHORIZED @@ -43,8 +42,6 @@ class TestSiteDetailsApi: Tests the site details endpoint """ - url = "/api/site" - def test_returns_site_by_id( self, user_factory, @@ -56,7 +53,7 @@ def test_returns_site_by_id( site = site_factory() site_access_factory(user=user, site=site) request = APIRequestFactory() - request = request.get(f"{self.url}/{site.rcra_site.epa_id}") + request = request.get(reverse("site:details", args=[site.rcra_site.epa_id])) force_authenticate(request, user) # Act response = SiteDetailsView.as_view()(request, epa_id=site.rcra_site.epa_id) @@ -79,7 +76,7 @@ def test_non_user_sites_not_returned( other_org = org_factory() other_site = site_factory(org=other_org) request = APIRequestFactory() - request = request.get(f"{self.url}/{other_site.rcra_site.epa_id}") + request = request.get(reverse("site:details", args=[site.rcra_site.epa_id])) force_authenticate(request, user) # Act response = SiteDetailsView.as_view()(request, epa_id=other_site.rcra_site.epa_id) @@ -101,15 +98,13 @@ def test_returns_formatted_http_response( client = APIClient() client.force_authenticate(user=user) # Act - response = client.get(f"{self.url}/{site.rcra_site.epa_id}") + response = client.get(reverse("site:details", args=[site.rcra_site.epa_id])) # Assert assert response.headers["Content-Type"] == "application/json" assert response.status_code == status.HTTP_200_OK class TestOrgSitesListView: - URL = "/api/org" - def test_returns_list_of_organizations_sites( self, user_factory, @@ -124,7 +119,7 @@ def test_returns_list_of_organizations_sites( client = APIClient() client.force_authenticate(user=user) # Act - response = client.get(f"{self.URL}/{org.id}/sites") + response = client.get(reverse("site:org-sites", args=[org.id])) # Assert assert response.headers["Content-Type"] == "application/json" assert response.status_code == status.HTTP_200_OK diff --git a/server/apps/site/urls.py b/server/apps/site/urls.py index 456642e8..15f7be8d 100644 --- a/server/apps/site/urls.py +++ b/server/apps/site/urls.py @@ -6,8 +6,9 @@ SiteListView, ) +app_name = "site" urlpatterns = [ - path("site", SiteListView.as_view()), - path("site/", SiteDetailsView.as_view()), - path("org//sites", OrgSitesListView.as_view()), + path("site", SiteListView.as_view(), name="list"), + path("site/", SiteDetailsView.as_view(), name="details"), + path("org//sites", OrgSitesListView.as_view(), name="org-sites"), ] diff --git a/server/apps/wasteline/tests/test_waste_views.py b/server/apps/wasteline/tests/test_waste_views.py index 1414281f..f2243bfc 100644 --- a/server/apps/wasteline/tests/test_waste_views.py +++ b/server/apps/wasteline/tests/test_waste_views.py @@ -1,6 +1,7 @@ import pytest from rest_framework import status from rest_framework.response import Response +from rest_framework.reverse import reverse from rest_framework.test import APIRequestFactory, force_authenticate from apps.wasteline.models import DotLookupType, WasteCode @@ -16,8 +17,6 @@ class TestWasteCodeLookupViews: """Tests the for the Waste Code views""" - base_url = "/api/waste/code" - @pytest.fixture def factory(self): return APIRequestFactory() @@ -27,14 +26,14 @@ def user(self, user_factory): return user_factory() def test_federal_returns_200(self, factory, user): - request = factory.get(f"{self.base_url}/federal") + request = factory.get(reverse("wasteline:code:federal")) force_authenticate(request, user) response: Response = FederalWasteCodesView.as_view()(request) assert response.status_code == status.HTTP_200_OK def test_federal_returns_list_of_all_federal_codes(self, factory, user): number_federal_codes = WasteCode.federal.all().count() - request = factory.get(f"{self.base_url}/federal") + request = factory.get(reverse("wasteline:code:federal")) force_authenticate(request, user) response: Response = FederalWasteCodesView.as_view()(request) @@ -43,7 +42,7 @@ def test_federal_returns_list_of_all_federal_codes(self, factory, user): def test_state_returns_200(self, factory, user): state_id = "VA" - request = factory.get(f"{self.base_url}/state/{state_id}") + request = factory.get(f"{reverse("wasteline:code:state", args=[state_id])}") force_authenticate(request, user) response: Response = StateWasteCodesView.as_view()(request, state_id=state_id) assert response.status_code == status.HTTP_200_OK @@ -70,7 +69,7 @@ def test_state_waste_codes_returns_list_codes(self, factory, user, waste_code_fa ) number_state_codes = WasteCode.state.filter(state_id=WasteCode.VA).count() state_id = "VA" - request = factory.get(f"{self.base_url}/state/{state_id}") + request = factory.get(f"{reverse("wasteline:code:state", args=[state_id])}") force_authenticate(request, user) # Act response: Response = StateWasteCodesView.as_view()(request, state_id=state_id) @@ -82,8 +81,6 @@ def test_state_waste_codes_returns_list_codes(self, factory, user, waste_code_fa class TestDOTLookupViews: """Tests the for the Waste Code views""" - base_url = "/api/waste/dot" - @pytest.fixture def factory(self): return APIRequestFactory() @@ -98,7 +95,7 @@ def dot_lookup(self, dot_lookup_factory): def test_dot_id_view_returns_json_with_list_of_strings(self, factory, user): # Arrange - request = factory.get(f"{self.base_url}/id") + request = factory.get(reverse("wasteline:dot:id-numbers")) force_authenticate(request, user) # Act response: Response = DotIdNumberView.as_view()(request) @@ -110,7 +107,7 @@ def test_dot_identifiers_can_be_queried(self, factory, user, dot_lookup_factory) # Arrange dot_id = "mock_id" dot_lookup_factory(value=dot_id, value_type=DotLookupType.ID) - request = factory.get(f"{self.base_url}/id", {"q": dot_id}) + request = factory.get(reverse("wasteline:dot:id-numbers"), {"q": dot_id}) force_authenticate(request, user) # Act response: Response = DotIdNumberView.as_view()(request) @@ -121,7 +118,7 @@ def test_dot_identifiers_can_be_queried(self, factory, user, dot_lookup_factory) def test_dot_shipping_name_view_returns_json_with_list_of_strings(self, factory, user): # Arrange - request = factory.get(f"{self.base_url}/id") + request = factory.get(reverse("wasteline:dot:id-numbers")) force_authenticate(request, user) # Act response: Response = DotShippingNameView.as_view()(request) @@ -131,7 +128,7 @@ def test_dot_shipping_name_view_returns_json_with_list_of_strings(self, factory, def test_dot_hazard_class_view_returns_a_list_of_strings(self, factory, user): # Arrange - request = factory.get(f"{self.base_url}/id") + request = factory.get(reverse("wasteline:dot:id-numbers")) force_authenticate(request, user) # Act response: Response = DotHazardClassView.as_view()(request) diff --git a/server/apps/wasteline/urls.py b/server/apps/wasteline/urls.py index 29ae5f9b..eb21a220 100644 --- a/server/apps/wasteline/urls.py +++ b/server/apps/wasteline/urls.py @@ -8,17 +8,31 @@ StateWasteCodesView, ) +dot_patterns = ( + [ + path("id", DotIdNumberView.as_view(), name="id-numbers"), + path("class", DotHazardClassView.as_view(), name="hazard-classes"), + path("name", DotShippingNameView.as_view(), name="shipping-names"), + ], + "dot", +) + +code_patterns = ( + [ + path("federal", FederalWasteCodesView.as_view(), name="federal"), + path("state/", StateWasteCodesView.as_view(), name="state"), + ], + "code", +) + +app_name = "wasteline" urlpatterns = [ - # waste info path( "waste/", include( [ - path("code/federal", FederalWasteCodesView.as_view()), - path("code/state/", StateWasteCodesView.as_view()), - path("dot/id", DotIdNumberView.as_view()), - path("dot/class", DotHazardClassView.as_view()), - path("dot/name", DotShippingNameView.as_view()), + path("code/", include(code_patterns)), + path("dot/", include(dot_patterns)), ] ), ), diff --git a/server/haztrak/urls.py b/server/haztrak/urls.py index 5d176992..0f6f5df3 100644 --- a/server/haztrak/urls.py +++ b/server/haztrak/urls.py @@ -28,20 +28,13 @@ "api/", include( [ - path( - "rcra/", - include( - [ - path("", include("apps.manifest.urls")), - path("", include("apps.rcrasite.urls")), - path("", include("apps.wasteline.urls")), - ] - ), - ), - path("", include("apps.core.urls")), - path("", include("apps.org.urls")), - path("", include("apps.site.urls")), - path("", include("apps.profile.urls")), + path("", include("apps.manifest.urls", namespace="manifest")), + path("", include("apps.wasteline.urls", namespace="wasteline")), + path("", include("apps.rcrasite.urls", namespace="rcrasite")), + path("", include("apps.core.urls", namespace="core")), + path("", include("apps.org.urls", namespace="org")), + path("", include("apps.site.urls", namespace="site")), + path("", include("apps.profile.urls", namespace="profile")), path("schema/", SpectacularAPIView.as_view(), name="schema"), path( "schema/swagger-ui",