From 3c9d5d32b0c211e68d3a93235083eeb804e5e682 Mon Sep 17 00:00:00 2001 From: DonHaul Date: Thu, 24 Oct 2024 18:40:21 +0200 Subject: [PATCH] backoffice & ui: standardize author routes *ref: cern-sis/issues-inspire/issues/55 --- backoffice/backoffice/authors/api/views.py | 13 ++++ .../backoffice/authors/tests/test_views.py | 77 ++++++++----------- backoffice/backoffice/authors/urls.py | 18 ----- backoffice/config/api_router.py | 16 ++-- backoffice/config/search_router.py | 4 +- backoffice/config/settings/local.py | 2 + docker-compose.services.yml | 5 +- ui/src/actions/backoffice.ts | 6 +- ui/src/common/routes.ts | 7 +- .../scripts/connections/connections.json | 2 +- ....test_close_author_create_user_ticket.yaml | 2 +- 11 files changed, 69 insertions(+), 83 deletions(-) delete mode 100644 backoffice/backoffice/authors/urls.py diff --git a/backoffice/backoffice/authors/api/views.py b/backoffice/backoffice/authors/api/views.py index 9bf64693c..56c7846de 100644 --- a/backoffice/backoffice/authors/api/views.py +++ b/backoffice/backoffice/authors/api/views.py @@ -106,6 +106,19 @@ def get_queryset(self): return self.queryset.filter(status__status=status) return self.queryset + def retrieve(self, request, *args, **kwargs): + instance = self.get_object() + serializer = self.get_serializer(instance) + validation_errors = list(get_validation_errors(instance.data)) + validation_errors_msg = utils.render_validation_error_response( + validation_errors + ) + response_data = { + "data": serializer.data, + "validation_errors": validation_errors_msg, + } + return Response(response_data) + def perform_destroy(self, instance): airflow_utils.delete_workflow_dag_runs(instance.id, instance.workflow_type) super().perform_destroy(instance) diff --git a/backoffice/backoffice/authors/tests/test_views.py b/backoffice/backoffice/authors/tests/test_views.py index 2b535731b..71522292f 100644 --- a/backoffice/backoffice/authors/tests/test_views.py +++ b/backoffice/backoffice/authors/tests/test_views.py @@ -56,7 +56,7 @@ def setUp(self): class TestWorkflowViewSet(BaseTransactionTestCase): - endpoint = "/api/workflows/" + endpoint = reverse("api:authors-list") reset_sequences = True fixtures = ["backoffice/fixtures/groups.json"] @@ -118,7 +118,7 @@ def test_delete(self): self.workflow.id, self.workflow.workflow_type ) - url = reverse("api:workflows-detail", kwargs={"pk": self.workflow.id}) + url = reverse("api:authors-detail", kwargs={"pk": self.workflow.id}) response = self.api_client.delete(url) self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) @@ -130,8 +130,8 @@ def test_delete(self): ) -class TestWorkflowSearchViewSet(BaseTransactionTestCase): - endpoint = reverse("search:workflow-list") +class TestAuthorWorkflowSearchViewSet(BaseTransactionTestCase): + endpoint = reverse("search:authors-list") reset_sequences = True fixtures = ["backoffice/fixtures/groups.json"] @@ -175,7 +175,6 @@ def test_contains_decisions(self): class TestAuthorWorkflowPartialUpdateViewSet(BaseTransactionTestCase): - endpoint_base_url = "/api/workflow-update" reset_sequences = True fixtures = ["backoffice/fixtures/groups.json"] @@ -188,7 +187,7 @@ def setUp(self): @property def endpoint(self): return reverse( - "api:workflows-authors-detail", + "api:authors-detail", kwargs={"pk": self.workflow.id}, ) @@ -236,7 +235,7 @@ def test_patch_anonymous(self): class TestAuthorWorkflowTicketViewSet(BaseTransactionTestCase): - endpoint = "/api/workflow-ticket" + endpoint = reverse("api:authors-tickets-list") reset_sequences = True fixtures = ["backoffice/fixtures/groups.json"] @@ -250,7 +249,7 @@ def setUp(self): def test_get_missing_params(self): self.api_client.force_authenticate(user=self.curator) response = self.api_client.get( - f"{TestAuthorWorkflowTicketViewSet.endpoint}/{self.workflow.id}/", + f"{self.endpoint}{self.workflow.id}/", format="json", data={}, ) @@ -262,7 +261,7 @@ def test_get_ticket_not_found(self): query_params = {"ticket_type": "test"} self.api_client.force_authenticate(user=self.curator) response = self.api_client.get( - f"{TestAuthorWorkflowTicketViewSet.endpoint}/{self.workflow.id}/", + f"{self.endpoint}{self.workflow.id}/", format="json", data=query_params, ) @@ -275,7 +274,7 @@ def test_get_ticket_happy_flow(self): query_params = {"ticket_type": self.workflow_ticket.ticket_type} response = self.api_client.get( - f"{TestAuthorWorkflowTicketViewSet.endpoint}/{self.workflow.id}/", + f"{self.endpoint}{self.workflow.id}/", format="json", data=query_params, ) @@ -290,9 +289,7 @@ def test_ticket_url(self): def test_create_missing_params(self): self.api_client.force_authenticate(user=self.curator) - response = self.api_client.post( - f"{TestAuthorWorkflowTicketViewSet.endpoint}/", format="json", data={} - ) + response = self.api_client.post(self.endpoint, format="json", data={}) assert response.status_code == 400 assert response.json() == { @@ -308,9 +305,8 @@ def test_create_happy_flow(self): "ticket_id": "dc94caad1b4f71502d06117a3b4bcb25", "ticket_type": "author_create_user", } - response = self.api_client.post( - f"{TestAuthorWorkflowTicketViewSet.endpoint}/", format="json", data=data - ) + # import ipdb; ipdb.set_trace() + response = self.api_client.post(self.endpoint, format="json", data=data) assert response.status_code == 201 @@ -325,7 +321,7 @@ def test_create_happy_flow(self): class TestAuthorWorkflowViewSet(BaseTransactionTestCase): - endpoint = "/api/authors/" + endpoint = reverse("api:authors-list") reset_sequences = True fixtures = ["backoffice/fixtures/groups.json"] @@ -366,7 +362,7 @@ def test_create_author(self): }, } - url = reverse("api:workflows-authors-list") + url = reverse("api:authors-list") response = self.api_client.post(url, format="json", data=data) self.assertEqual(response.status_code, 201) self.assertEqual(response.json(), data) @@ -378,7 +374,7 @@ def test_accept_author(self): data = {"create_ticket": True, "value": action} response = self.api_client.post( - reverse("api:workflows-authors-resolve", kwargs={"pk": self.workflow.id}), + reverse("api:authors-resolve", kwargs={"pk": self.workflow.id}), format="json", data=data, ) @@ -400,7 +396,7 @@ def test_reject_author(self): data = {"create_ticket": True, "value": action} response = self.api_client.post( - reverse("api:workflows-authors-resolve", kwargs={"pk": self.workflow.id}), + reverse("api:authors-resolve", kwargs={"pk": self.workflow.id}), format="json", data=data, ) @@ -420,7 +416,7 @@ def test_reject_author(self): def test_restart_full_dagrun(self): self.api_client.force_authenticate(user=self.curator) url = reverse( - "api:workflows-authors-restart", + "api:authors-restart", kwargs={"pk": self.workflow.id}, ) response = self.api_client.post(url) @@ -431,7 +427,7 @@ def test_restart_full_dagrun(self): def test_restart_a_task(self): self.api_client.force_authenticate(user=self.curator) url = reverse( - "api:workflows-authors-restart", + "api:authors-restart", kwargs={"pk": self.workflow.id}, ) response = self.api_client.post( @@ -443,7 +439,7 @@ def test_restart_a_task(self): def test_restart_with_params(self): self.api_client.force_authenticate(user=self.curator) url = reverse( - "api:workflows-authors-restart", + "api:authors-restart", kwargs={"pk": self.workflow.id}, ) response = self.api_client.post( @@ -462,7 +458,7 @@ def test_validate_valid_record(self): "$schema": "https://inspirehep.net/schemas/records/authors.json", } url = reverse( - "api:workflows-authors-validate", + "api:authors-validate", ) response = self.api_client.post(url, format="json", data=data) self.assertContains(response, "Record is valid.", status_code=200) @@ -479,7 +475,7 @@ def test_validate_not_valid_record(self): "_collections": ["Authors"], } url = reverse( - "api:workflows-authors-validate", + "api:authors-validate", ) response = self.api_client.post(url, format="json", data=data) expected_response = { @@ -500,7 +496,7 @@ def test_validate_not_valid_record(self): def test_validate_no_schema_record(self): self.api_client.force_authenticate(user=self.curator) url = reverse( - "api:workflows-authors-validate", + "api:authors-validate", ) response = self.api_client.post(url, format="json", data={}) self.assertContains( @@ -516,7 +512,7 @@ def test_validate_invalid_schema_record(self): "$schema": "https://inspirehep.net/schemas/records/notajsonschema.json", } url = reverse( - "api:workflows-authors-validate", + "api:authors-validate", ) response = self.api_client.post(url, format="json", data=data) self.assertContains( @@ -526,8 +522,8 @@ def test_validate_invalid_schema_record(self): ) -class TestWorkflowSearchFilterViewSet(BaseTransactionTestCase): - endpoint = "/api/workflows/search/" +class TestAuthorWorkflowSearchFilterViewSet(BaseTransactionTestCase): + endpoint = reverse("search:authors-list") reset_sequences = True fixtures = ["backoffice/fixtures/groups.json"] @@ -572,7 +568,7 @@ def setUpClass(cls): def test_facets(self): self.api_client.force_authenticate(user=self.admin) - response = self.api_client.get(reverse("search:workflow-list")) + response = self.api_client.get(self.endpoint) assert "_filter_status" in response.json()["facets"] assert "_filter_workflow_type" in response.json()["facets"] @@ -580,7 +576,7 @@ def test_facets(self): def test_search_data_name(self): self.api_client.force_authenticate(user=self.admin) - url = reverse("search:workflow-list") + "?search=John" + url = self.endpoint + "?search=John" response = self.api_client.get(url) results = response.json()["results"] @@ -593,7 +589,7 @@ def test_search_data_email(self, query_params): email = "john.smith@something.ch" - url = reverse("search:workflow-list") + f"{query_params}{email}" + url = self.endpoint + f"{query_params}{email}" response = self.api_client.get(url) results = response.json()["results"] @@ -603,7 +599,7 @@ def test_search_data_email(self, query_params): def test_filter_status(self): self.api_client.force_authenticate(user=self.admin) - url = reverse("search:workflow-list") + f"?status={StatusChoices.RUNNING}" + url = self.endpoint + f"?status={StatusChoices.RUNNING}" response = self.api_client.get(url) @@ -613,10 +609,7 @@ def test_filter_status(self): def test_filter_workflow_type(self): self.api_client.force_authenticate(user=self.admin) - url = ( - reverse("search:workflow-list") - + f'?workflow_type="={WorkflowType.AUTHOR_CREATE}' - ) + url = self.endpoint + f'?workflow_type="={WorkflowType.AUTHOR_CREATE}' response = self.api_client.get(url) @@ -626,9 +619,7 @@ def test_filter_workflow_type(self): def test_ordering_updated_at(self): self.api_client.force_authenticate(user=self.admin) - base_url = reverse("search:workflow-list") - - urls = [base_url, base_url + "?ordering=-_updated_at"] + urls = [self.endpoint, self.endpoint + "?ordering=-_updated_at"] for url in urls: response = self.api_client.get(url) @@ -645,14 +636,14 @@ def test_ordering_score(self): search_str = "search=Frank Castle^10 OR John^6" - url = reverse("search:workflow-list") + f"?ordering=_score&{search_str}" + url = self.endpoint + f"?ordering=_score&{search_str}" response = self.api_client.get(url) self.assertEqual( response.json()["results"][0]["data"]["name"]["preferred_name"], "John Smith", ) - url = reverse("search:workflow-list") + f"?ordering=-_score&{search_str}" + url = self.endpoint + f"?ordering=-_score&{search_str}" response = self.api_client.get(url) self.assertEqual( response.json()["results"][0]["data"]["name"]["preferred_name"], @@ -676,6 +667,6 @@ def test_create_decision(self): "action": "accept", } - url = reverse("api:decisions-list") + url = reverse("api:authors-decisions-list") response = self.api_client.post(url, format="json", data=data) self.assertEqual(response.status_code, status.HTTP_201_CREATED) diff --git a/backoffice/backoffice/authors/urls.py b/backoffice/backoffice/authors/urls.py deleted file mode 100644 index 6b429b9df..000000000 --- a/backoffice/backoffice/authors/urls.py +++ /dev/null @@ -1,18 +0,0 @@ -from django.conf import settings -from rest_framework.routers import DefaultRouter, SimpleRouter - -from backoffice.authors.api.views import ( - AuthorDecisionViewSet, - AuthorWorkflowTicketViewSet, - AuthorWorkflowViewSet, -) - -router = DefaultRouter() if settings.DEBUG else SimpleRouter() - -# Workflows -router.register("authors", AuthorWorkflowViewSet, basename="authors") -router.register( - "authors/tickets", AuthorWorkflowTicketViewSet, basename="author-tickets" -) -router.register("authors/decisions", AuthorDecisionViewSet, basename="author-decisions") -app_name = "authors" diff --git a/backoffice/config/api_router.py b/backoffice/config/api_router.py index fd25d873c..bde8e1ac3 100644 --- a/backoffice/config/api_router.py +++ b/backoffice/config/api_router.py @@ -12,17 +12,13 @@ router.register("users", UserViewSet) # Workflows -( - router.register( - "workflows/authors", AuthorWorkflowViewSet, basename="workflows-authors" - ), +router.register( + "workflows/authors/tickets", AuthorWorkflowTicketViewSet, basename="authors-tickets" ) -router.register("workflows", AuthorWorkflowViewSet, basename="workflows") -( - router.register( - "workflow-ticket", AuthorWorkflowTicketViewSet, basename="workflow-ticket" - ), +router.register( + "workflows/authors/decisions", AuthorDecisionViewSet, basename="authors-decisions" ) -router.register("decisions", AuthorDecisionViewSet, basename="decisions") +router.register("workflows/authors", AuthorWorkflowViewSet, basename="authors") + app_name = "api" urlpatterns = router.urls diff --git a/backoffice/config/search_router.py b/backoffice/config/search_router.py index b7b40f4a3..67f330f69 100644 --- a/backoffice/config/search_router.py +++ b/backoffice/config/search_router.py @@ -6,7 +6,9 @@ # Workflow -router.register("workflows/search", AuthorWorkflowDocumentView, basename="workflow") +router.register( + "workflows/authors/search", AuthorWorkflowDocumentView, basename="authors" +) app_name = "search" urlpatterns = router.urls diff --git a/backoffice/config/settings/local.py b/backoffice/config/settings/local.py index 3f93cf218..8bcf41837 100644 --- a/backoffice/config/settings/local.py +++ b/backoffice/config/settings/local.py @@ -85,3 +85,5 @@ "BASE_DOMAIN": "sandbox.orcid.org", } } + +CORS_ALLOW_ALL_ORIGINS = True diff --git a/docker-compose.services.yml b/docker-compose.services.yml index f1827b9bf..695eb71e1 100644 --- a/docker-compose.services.yml +++ b/docker-compose.services.yml @@ -64,9 +64,10 @@ services: - cluster.name=opensearch-cluster - node.name=opensearch-node1 - discovery.seed_hosts=opensearch-node1 - - bootstrap.memory_lock=true + - bootstrap.memory_lock=false + - cluster.routing.allocation.disk.threshold_enabled=false - discovery.type=single-node - - 'ES_JAVA_OPTS=-Xms1024m -Xms1024m' + - 'OPENSEARCH_JAVA_OPTS=-Xms1g -Xmx1g' ulimits: memlock: soft: -1 diff --git a/ui/src/actions/backoffice.ts b/ui/src/actions/backoffice.ts index c2ada5588..ceae34ee9 100644 --- a/ui/src/actions/backoffice.ts +++ b/ui/src/actions/backoffice.ts @@ -262,7 +262,7 @@ export function fetchAuthor( ): (dispatch: ActionCreator) => Promise { return async (dispatch) => { dispatch(fetchingAuthor()); - const resolveQuery = `${BACKOFFICE_API}/${id}`; + const resolveQuery = `${BACKOFFICE_API}/workflows/authors/${id}`; try { const response = await httpClient.get(`${resolveQuery}`); @@ -304,7 +304,7 @@ export function resolveAction( dispatch(resolvingAction(action)); try { const response = await httpClient.post( - `${BACKOFFICE_API}/authors/${id}/${action}/`, + `${BACKOFFICE_API}/workflows/authors/${id}/${action}/`, payload ); @@ -350,7 +350,7 @@ export function deleteWorkflow( return async (dispatch) => { dispatch(deletingWorkflow()); try { - await httpClient.delete(`${BACKOFFICE_API}/${id}/`); + await httpClient.delete(`${BACKOFFICE_API}/workflows/authors/${id}/`); dispatch(deleteWorkflowSuccess()); notifyDeleteSuccess(); diff --git a/ui/src/common/routes.ts b/ui/src/common/routes.ts index 5a512d442..c9120fac3 100644 --- a/ui/src/common/routes.ts +++ b/ui/src/common/routes.ts @@ -31,10 +31,9 @@ export const BACKOFFICE = '/backoffice'; export const BACKOFFICE_LOGIN = `${BACKOFFICE}/login`; export const BACKOFFICE_SEARCH = `${BACKOFFICE}/search`; export const BACKOFFICE_API = - 'https://backoffice.dev.inspirebeta.net/api/workflows'; -export const BACKOFFICE_LOGIN_API = - 'https://backoffice.dev.inspirebeta.net/api/token/'; -export const BACKOFFICE_SEARCH_API = `${BACKOFFICE_API}/search`; + 'backoffice.dev.inspirebeta.net/api'; +export const BACKOFFICE_LOGIN_API = `${BACKOFFICE_API}/token/`; +export const BACKOFFICE_SEARCH_API = `${BACKOFFICE_API}/workflows/authors/search`; export const ERRORS = '/errors'; export const ERROR_401 = `${ERRORS}/401`; diff --git a/workflows/scripts/connections/connections.json b/workflows/scripts/connections/connections.json index 15551c5dc..c50099f54 100644 --- a/workflows/scripts/connections/connections.json +++ b/workflows/scripts/connections/connections.json @@ -24,7 +24,7 @@ "description": "", "login": "", "password": null, - "host": "http://host.docker.internal:8000", + "host": "http://host.docker.internal:8001", "port": null, "schema": "", "extra": "" diff --git a/workflows/tests/cassettes/TestAuthorCreate.test_close_author_create_user_ticket.yaml b/workflows/tests/cassettes/TestAuthorCreate.test_close_author_create_user_ticket.yaml index d7371e1c4..208431431 100644 --- a/workflows/tests/cassettes/TestAuthorCreate.test_close_author_create_user_ticket.yaml +++ b/workflows/tests/cassettes/TestAuthorCreate.test_close_author_create_user_ticket.yaml @@ -15,7 +15,7 @@ interactions: User-Agent: - python-requests/2.31.0 method: GET - uri: http://host.docker.internal:8000/api/workflow-ticket/f8301c06-8fa1-4124-845e-c270b910af5f/?ticket_type=author_create_user + uri: http://host.docker.internal:8001/api/workflow-ticket/f8301c06-8fa1-4124-845e-c270b910af5f/?ticket_type=author_create_user response: body: string: '{"id":1,"ticket_url":"https://cerntraining.service-now.com/nav_to.do?uri=/u_request_fulfillment.do?sys_id=656f2d17878c929095f833340cbb3531","ticket_id":"656f2d17878c929095f833340cbb3531","ticket_type":"author_create_user","workflow_id":"f8301c06-8fa1-4124-845e-c270b910af5f"}'