From a323f49e54e3fc943274fa978677203c7e2c73d6 Mon Sep 17 00:00:00 2001 From: DonHaul Date: Mon, 18 Nov 2024 13:55:21 +0100 Subject: [PATCH] workflows: fixed accesses to authors api * ref: cern-sis/issues-inspire/issues/614 --- .pre-commit-config.yaml | 2 +- .../author_create/author_create_approved.py | 12 +- .../author_create/author_create_init.py | 11 +- .../author_create/author_create_rejected.py | 10 +- .../author/author_update/author_update.py | 14 +- .../backoffice/workflow_management_hook.py | 20 +- ...wManagementHook.test_get_workflow_url.yaml | 442 ++++++++++++++++++ workflows/tests/conftest.py | 11 + workflows/tests/test_author_create_tasks.py | 2 +- .../tests/test_workflow_management_hook.py | 16 + 10 files changed, 494 insertions(+), 46 deletions(-) create mode 100644 workflows/tests/cassettes/TestWorkflowManagementHook.test_get_workflow_url.yaml create mode 100644 workflows/tests/conftest.py create mode 100644 workflows/tests/test_workflow_management_hook.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8f8c84f3e2..a49937fa66 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,4 +1,4 @@ -files: ^(backend|backoffice|workflow)/ +files: ^(backend|backoffice|workflows)/ repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.6.0 diff --git a/workflows/dags/author/author_create/author_create_approved.py b/workflows/dags/author/author_create/author_create_approved.py index 6509e654a7..4f31edbdbb 100644 --- a/workflows/dags/author/author_create/author_create_approved.py +++ b/workflows/dags/author/author_create/author_create_approved.py @@ -48,16 +48,14 @@ def author_create_approved_dag(): """ inspire_http_hook = InspireHttpHook() inspire_http_record_management_hook = InspireHTTPRecordManagementHook() - workflow_management_hook = WorkflowManagementHook() + workflow_management_hook = WorkflowManagementHook(AUTHORS) workflow_ticket_management_hook = AuthorWorkflowTicketManagementHook() @task() def set_workflow_status_to_running(**context): status_name = "running" workflow_management_hook.set_workflow_status( - status_name=status_name, - workflow_id=context["params"]["workflow_id"], - collection=AUTHORS, + status_name=status_name, workflow_id=context["params"]["workflow_id"] ) @task.branch() @@ -107,7 +105,6 @@ def create_author_on_inspire(**context: dict) -> str: workflow_management_hook.partial_update_workflow( workflow_id=context["params"]["workflow_id"], workflow_partial_update_data={"data": workflow_data["data"]}, - collection=AUTHORS, ) return status @@ -124,9 +121,7 @@ def author_create_success_branch(**context: dict) -> str: def set_author_create_workflow_status_to_completed(**context: dict) -> None: status_name = "completed" workflow_management_hook.set_workflow_status( - status_name=status_name, - workflow_id=context["params"]["workflow_id"], - collection=AUTHORS, + status_name=status_name, workflow_id=context["params"]["workflow_id"] ) @task @@ -142,7 +137,6 @@ def set_author_create_workflow_status_to_error(**context: dict) -> None: workflow_management_hook.set_workflow_status( status_name=status_name, workflow_id=context["params"]["workflow_id"], - collection=AUTHORS, ) # task definitions diff --git a/workflows/dags/author/author_create/author_create_init.py b/workflows/dags/author/author_create/author_create_init.py index effa88f927..16c62bd33f 100644 --- a/workflows/dags/author/author_create/author_create_init.py +++ b/workflows/dags/author/author_create/author_create_init.py @@ -37,16 +37,14 @@ def author_create_initialization_dag(): """ inspire_http_hook = InspireHttpHook() - workflow_management_hook = WorkflowManagementHook() + workflow_management_hook = WorkflowManagementHook(AUTHORS) workflow_ticket_management_hook = AuthorWorkflowTicketManagementHook() @task() def set_workflow_status_to_running(**context): status_name = "running" workflow_management_hook.set_workflow_status( - status_name=status_name, - workflow_id=context["params"]["workflow_id"], - collection=AUTHORS, + status_name=status_name, workflow_id=context["params"]["workflow_id"] ) @task() @@ -57,7 +55,6 @@ def set_schema(**context): workflow_partial_update_data={ "data": {**context["params"]["data"], "$schema": schema} }, - collection=AUTHORS, ) @task() @@ -86,9 +83,7 @@ def create_author_create_user_ticket(**context: dict) -> None: def set_author_create_workflow_status_to_approval(**context: dict) -> None: status_name = "approval" workflow_management_hook.set_workflow_status( - status_name=status_name, - workflow_id=context["params"]["workflow_id"], - collection=AUTHORS, + status_name=status_name, workflow_id=context["params"]["workflow_id"] ) # task dependencies diff --git a/workflows/dags/author/author_create/author_create_rejected.py b/workflows/dags/author/author_create/author_create_rejected.py index 9de52a1b8b..5caae48629 100644 --- a/workflows/dags/author/author_create/author_create_rejected.py +++ b/workflows/dags/author/author_create/author_create_rejected.py @@ -28,24 +28,20 @@ def author_create_rejected_dag() -> None: 2. set_author_create_workflow_status_to_completed: Sets the status of the author creation workflow to 'completed'. """ - workflow_management_hook = WorkflowManagementHook() + workflow_management_hook = WorkflowManagementHook(AUTHORS) @task() def set_author_create_workflow_status_to_completed(**context: dict) -> None: status_name = "completed" workflow_management_hook.set_workflow_status( - status_name=status_name, - workflow_id=context["params"]["workflow_id"], - collection=AUTHORS, + status_name=status_name, workflow_id=context["params"]["workflow_id"] ) @task() def set_workflow_status_to_running(**context): status_name = "running" workflow_management_hook.set_workflow_status( - status_name=status_name, - workflow_id=context["params"]["workflow_id"], - collection=AUTHORS, + status_name=status_name, workflow_id=context["params"]["workflow_id"] ) # task definitions diff --git a/workflows/dags/author/author_update/author_update.py b/workflows/dags/author/author_update/author_update.py index 6576af941c..66ccdac6ef 100644 --- a/workflows/dags/author/author_update/author_update.py +++ b/workflows/dags/author/author_update/author_update.py @@ -39,16 +39,14 @@ def author_update_dag(): """ inspire_http_hook = InspireHttpHook() inspire_http_record_management_hook = InspireHTTPRecordManagementHook() - workflow_management_hook = WorkflowManagementHook() + workflow_management_hook = WorkflowManagementHook(AUTHORS) workflow_ticket_management_hook = AuthorWorkflowTicketManagementHook() @task() def set_author_update_workflow_status_to_running(**context): status_name = "running" workflow_management_hook.set_workflow_status( - status_name=status_name, - workflow_id=context["params"]["workflow_id"], - collection=AUTHORS, + status_name=status_name, workflow_id=context["params"]["workflow_id"] ) @task() @@ -94,9 +92,7 @@ def update_author_on_inspire(**context): def set_author_update_workflow_status_to_completed(**context): status_name = "completed" workflow_management_hook.set_workflow_status( - status_name=status_name, - workflow_id=context["params"]["workflow_id"], - collection=AUTHORS, + status_name=status_name, workflow_id=context["params"]["workflow_id"] ) @task.branch() @@ -114,9 +110,7 @@ def set_author_update_workflow_status_to_error(**context): ti = context["ti"] status_name = ti.xcom_pull(task_ids="update_author_on_inspire") workflow_management_hook.set_workflow_status( - status_name=status_name, - workflow_id=context["params"]["workflow_id"], - collection=AUTHORS, + status_name=status_name, workflow_id=context["params"]["workflow_id"] ) # task definitions diff --git a/workflows/plugins/hooks/backoffice/workflow_management_hook.py b/workflows/plugins/hooks/backoffice/workflow_management_hook.py index 7df0c72099..91e2395a51 100644 --- a/workflows/plugins/hooks/backoffice/workflow_management_hook.py +++ b/workflows/plugins/hooks/backoffice/workflow_management_hook.py @@ -16,9 +16,11 @@ class WorkflowManagementHook(BackofficeHook): :type http_conn_id: str """ - def set_workflow_status( - self, status_name: str, workflow_id: str, collection: str - ) -> Response: + def __init__(self, collection): + super().__init__() + self.endpoint = f"api/workflows/{collection}" + + def set_workflow_status(self, status_name: str, workflow_id: str) -> Response: """ Updates the status of a workflow in the backoffice system. @@ -32,13 +34,11 @@ def set_workflow_status( "status": status_name, } return self.partial_update_workflow( - workflow_partial_update_data=request_data, - workflow_id=workflow_id, - collection=collection, + workflow_partial_update_data=request_data, workflow_id=workflow_id ) def get_workflow(self, workflow_id: str) -> dict: - endpoint = f"api/workflows/{workflow_id}" + endpoint = f"{self.endpoint}/{workflow_id}" response = self.run_with_advanced_retry( _retry_args=self.tenacity_retry_kwargs, method="GET", endpoint=endpoint ) @@ -46,7 +46,7 @@ def get_workflow(self, workflow_id: str) -> dict: return response.json() def update_workflow(self, workflow_id: str, workflow_data: dict) -> Response: - endpoint = f"api/workflows/{workflow_id}/" + endpoint = f"{self.endpoint}/{workflow_id}/" return self.run_with_advanced_retry( _retry_args=self.tenacity_retry_kwargs, method="PUT", @@ -55,9 +55,9 @@ def update_workflow(self, workflow_id: str, workflow_data: dict) -> Response: ) def partial_update_workflow( - self, workflow_id: str, workflow_partial_update_data: dict, collection: str + self, workflow_id: str, workflow_partial_update_data: dict ) -> Response: - endpoint = f"api/workflows/{collection}/{workflow_id}/" + endpoint = f"{self.endpoint}/{workflow_id}/" return self.run_with_advanced_retry( _retry_args=self.tenacity_retry_kwargs, method="PATCH", diff --git a/workflows/tests/cassettes/TestWorkflowManagementHook.test_get_workflow_url.yaml b/workflows/tests/cassettes/TestWorkflowManagementHook.test_get_workflow_url.yaml new file mode 100644 index 0000000000..2fe00df6c8 --- /dev/null +++ b/workflows/tests/cassettes/TestWorkflowManagementHook.test_get_workflow_url.yaml @@ -0,0 +1,442 @@ +interactions: +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Authorization: + - Token 2e04111a61e8f5ba6ecec52af21bbb9e81732085 + Connection: + - keep-alive + Content-Type: + - application/json + User-Agent: + - python-requests/2.31.0 + method: GET + uri: http://host.docker.internal:8001/api/workflows/authors/invalid_workflow_id + response: + body: + string: '' + headers: + Connection: + - close + Content-Language: + - en + Content-Type: + - text/html; charset=utf-8 + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Wed, 20 Nov 2024 10:33:51 GMT + Location: + - /api/workflows/authors/invalid_workflow_id/ + Referrer-Policy: + - same-origin + Server: + - WSGIServer/0.2 CPython/3.11.6 + Vary: + - Accept-Language, origin + X-Content-Type-Options: + - nosniff + status: + code: 301 + message: Moved Permanently +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Authorization: + - Token 2e04111a61e8f5ba6ecec52af21bbb9e81732085 + Connection: + - keep-alive + User-Agent: + - python-requests/2.31.0 + method: GET + uri: http://host.docker.internal:8001/api/workflows/authors/invalid_workflow_id/ + response: + body: + string: '{"detail":"Not found."}' + headers: + Allow: + - GET, PUT, PATCH, DELETE, HEAD, OPTIONS + Content-Language: + - en + Content-Length: + - '23' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Wed, 20 Nov 2024 10:33:51 GMT + Referrer-Policy: + - same-origin + Server: + - WSGIServer/0.2 CPython/3.11.6 + Vary: + - Accept, Accept-Language, Cookie, origin + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + status: + code: 404 + message: Not Found +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Authorization: + - Token 2e04111a61e8f5ba6ecec52af21bbb9e81732085 + Connection: + - keep-alive + Content-Type: + - application/json + User-Agent: + - python-requests/2.31.0 + method: GET + uri: http://host.docker.internal:8001/api/workflows/authors/invalid_workflow_id + response: + body: + string: '' + headers: + Connection: + - close + Content-Language: + - en + Content-Type: + - text/html; charset=utf-8 + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Wed, 20 Nov 2024 10:33:52 GMT + Location: + - /api/workflows/authors/invalid_workflow_id/ + Referrer-Policy: + - same-origin + Server: + - WSGIServer/0.2 CPython/3.11.6 + Vary: + - Accept-Language, origin + X-Content-Type-Options: + - nosniff + status: + code: 301 + message: Moved Permanently +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Authorization: + - Token 2e04111a61e8f5ba6ecec52af21bbb9e81732085 + Connection: + - keep-alive + User-Agent: + - python-requests/2.31.0 + method: GET + uri: http://host.docker.internal:8001/api/workflows/authors/invalid_workflow_id/ + response: + body: + string: '{"detail":"Not found."}' + headers: + Allow: + - GET, PUT, PATCH, DELETE, HEAD, OPTIONS + Content-Language: + - en + Content-Length: + - '23' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Wed, 20 Nov 2024 10:33:52 GMT + Referrer-Policy: + - same-origin + Server: + - WSGIServer/0.2 CPython/3.11.6 + Vary: + - Accept, Accept-Language, Cookie, origin + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + status: + code: 404 + message: Not Found +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Authorization: + - Token 2e04111a61e8f5ba6ecec52af21bbb9e81732085 + Connection: + - keep-alive + Content-Type: + - application/json + User-Agent: + - python-requests/2.31.0 + method: GET + uri: http://host.docker.internal:8001/api/workflows/authors/invalid_workflow_id + response: + body: + string: '' + headers: + Connection: + - close + Content-Language: + - en + Content-Type: + - text/html; charset=utf-8 + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Wed, 20 Nov 2024 10:33:54 GMT + Location: + - /api/workflows/authors/invalid_workflow_id/ + Referrer-Policy: + - same-origin + Server: + - WSGIServer/0.2 CPython/3.11.6 + Vary: + - Accept-Language, origin + X-Content-Type-Options: + - nosniff + status: + code: 301 + message: Moved Permanently +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Authorization: + - Token 2e04111a61e8f5ba6ecec52af21bbb9e81732085 + Connection: + - keep-alive + User-Agent: + - python-requests/2.31.0 + method: GET + uri: http://host.docker.internal:8001/api/workflows/authors/invalid_workflow_id/ + response: + body: + string: '{"detail":"Not found."}' + headers: + Allow: + - GET, PUT, PATCH, DELETE, HEAD, OPTIONS + Content-Language: + - en + Content-Length: + - '23' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Wed, 20 Nov 2024 10:33:54 GMT + Referrer-Policy: + - same-origin + Server: + - WSGIServer/0.2 CPython/3.11.6 + Vary: + - Accept, Accept-Language, Cookie, origin + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + status: + code: 404 + message: Not Found +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Authorization: + - Token 2e04111a61e8f5ba6ecec52af21bbb9e81732085 + Connection: + - keep-alive + Content-Type: + - application/json + User-Agent: + - python-requests/2.31.0 + method: GET + uri: http://host.docker.internal:8001/api/workflows/authors/invalid_workflow_id + response: + body: + string: '' + headers: + Connection: + - close + Content-Language: + - en + Content-Type: + - text/html; charset=utf-8 + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Wed, 20 Nov 2024 10:33:58 GMT + Location: + - /api/workflows/authors/invalid_workflow_id/ + Referrer-Policy: + - same-origin + Server: + - WSGIServer/0.2 CPython/3.11.6 + Vary: + - Accept-Language, origin + X-Content-Type-Options: + - nosniff + status: + code: 301 + message: Moved Permanently +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Authorization: + - Token 2e04111a61e8f5ba6ecec52af21bbb9e81732085 + Connection: + - keep-alive + User-Agent: + - python-requests/2.31.0 + method: GET + uri: http://host.docker.internal:8001/api/workflows/authors/invalid_workflow_id/ + response: + body: + string: '{"detail":"Not found."}' + headers: + Allow: + - GET, PUT, PATCH, DELETE, HEAD, OPTIONS + Content-Language: + - en + Content-Length: + - '23' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Wed, 20 Nov 2024 10:33:58 GMT + Referrer-Policy: + - same-origin + Server: + - WSGIServer/0.2 CPython/3.11.6 + Vary: + - Accept, Accept-Language, Cookie, origin + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + status: + code: 404 + message: Not Found +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Authorization: + - Token 2e04111a61e8f5ba6ecec52af21bbb9e81732085 + Connection: + - keep-alive + Content-Type: + - application/json + User-Agent: + - python-requests/2.31.0 + method: GET + uri: http://host.docker.internal:8001/api/workflows/authors/invalid_workflow_id + response: + body: + string: '' + headers: + Connection: + - close + Content-Language: + - en + Content-Type: + - text/html; charset=utf-8 + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Wed, 20 Nov 2024 10:34:06 GMT + Location: + - /api/workflows/authors/invalid_workflow_id/ + Referrer-Policy: + - same-origin + Server: + - WSGIServer/0.2 CPython/3.11.6 + Vary: + - Accept-Language, origin + X-Content-Type-Options: + - nosniff + status: + code: 301 + message: Moved Permanently +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Authorization: + - Token 2e04111a61e8f5ba6ecec52af21bbb9e81732085 + Connection: + - keep-alive + User-Agent: + - python-requests/2.31.0 + method: GET + uri: http://host.docker.internal:8001/api/workflows/authors/invalid_workflow_id/ + response: + body: + string: '{"detail":"Not found."}' + headers: + Allow: + - GET, PUT, PATCH, DELETE, HEAD, OPTIONS + Content-Language: + - en + Content-Length: + - '23' + Content-Type: + - application/json + Cross-Origin-Opener-Policy: + - same-origin + Date: + - Wed, 20 Nov 2024 10:34:06 GMT + Referrer-Policy: + - same-origin + Server: + - WSGIServer/0.2 CPython/3.11.6 + Vary: + - Accept, Accept-Language, Cookie, origin + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - DENY + status: + code: 404 + message: Not Found +version: 1 diff --git a/workflows/tests/conftest.py b/workflows/tests/conftest.py new file mode 100644 index 0000000000..dd35659784 --- /dev/null +++ b/workflows/tests/conftest.py @@ -0,0 +1,11 @@ +import pytest + + +@pytest.fixture(scope="session") +def vcr_config(): + return { + "ignore_localhost": True, + "decode_compressed_response": True, + "filter_headers": ("Authorization", "User-Agent"), + "record_mode": "once", + } diff --git a/workflows/tests/test_author_create_tasks.py b/workflows/tests/test_author_create_tasks.py index 1958145a7a..7a38d86bab 100644 --- a/workflows/tests/test_author_create_tasks.py +++ b/workflows/tests/test_author_create_tasks.py @@ -12,6 +12,6 @@ class TestAuthorCreate: } } - @pytest.mark.vcr() + @pytest.mark.vcr def test_close_author_create_user_ticket(self): close_author_create_user_ticket.function(**self.context) diff --git a/workflows/tests/test_workflow_management_hook.py b/workflows/tests/test_workflow_management_hook.py new file mode 100644 index 0000000000..f972f01081 --- /dev/null +++ b/workflows/tests/test_workflow_management_hook.py @@ -0,0 +1,16 @@ +import pytest +from hooks.backoffice.workflow_management_hook import AUTHORS, WorkflowManagementHook +from tenacity import RetryError + + +class TestWorkflowManagementHook: + workflow_management_hook = WorkflowManagementHook(AUTHORS) + + def test_collection(self): + assert self.workflow_management_hook.endpoint == f"api/workflows/{AUTHORS}" + + @pytest.mark.vcr + def test_get_workflow_url(self): + with pytest.raises(RetryError) as excinfo: + self.workflow_management_hook.get_workflow("invalid_workflow_id") + assert str(excinfo.value.__cause__) == "404:Not Found"