Skip to content
Snippets Groups Projects
Commit 64b3608d authored by Nathan Hertz's avatar Nathan Hertz Committed by Nathan Hertz
Browse files

Added new REST endpoint for deleting capability requests; requests with

existing executions cannot be deleted (they must be disabled instead,
which will have to be implemented)
parent 2163e382
No related branches found
No related tags found
1 merge request!105WS-85: Delete capability request REST endpoint
Pipeline #745 passed
......@@ -4,13 +4,12 @@
File containing definitions for the other half of the capability side of the Workspaces REST API,
concerning capability requests
"""
from pyramid.httpexceptions import HTTPBadRequest, HTTPNotFound, HTTPPreconditionFailed
from pyramid.request import Request
from pyramid.response import Response
from pyramid.view import view_config
from workspaces.capability.schema import CapabilityRequest
@view_config(route_name="view_capability_request", renderer="json")
def view_capability_request(request: Request) -> Response:
......@@ -19,7 +18,7 @@ def view_capability_request(request: Request) -> Response:
URL: capability/{capability_name}/request/{request_id}
:param request: GET request
:return: Response with JSON-formatted capability request info if found
:return: 200 OK response with JSON-formatted capability request info if found
or 404 response (HTTPNotFound)
"""
capability_request = request.capability_info.lookup_capability_request(
......@@ -42,7 +41,7 @@ def create_capability_request(request: Request) -> Response:
URL: capability/{capability_name}/request/create
:param request: POST request, expecting JSON parameter "parameters"
:return: Response with JSON-formatted info of newly created capability request
:return: 200 OK response with JSON-formatted info of newly created capability request
or 400 response (HTTPBadRequest) if expected parameters not given
or 412 response (HTTPPreconditionFailed) if capability with given name does not exist and thus cannot be
requested
......@@ -92,7 +91,7 @@ def submit_capability_request(request: Request) -> Response:
URL: capability/{capability_name}/request/{request_id}/submit
:param request: POST request
:return: Response with
:return: 200 OK response
or 412 response (HTTPPreconditionFailed) if capability request with given ID does not exist and thus cannot be
submitted
"""
......@@ -102,8 +101,41 @@ def submit_capability_request(request: Request) -> Response:
if not capability_request:
# Capability request not found
does_not_exist_msg = f"Capability request for {capability_name} with ID {request_id} does not exist. Cannot submit request."
does_not_exist_msg = f"Capability request for {capability_name} with ID {request_id} does not exist. Cannot submit."
return HTTPPreconditionFailed(detail=does_not_exist_msg)
else:
execution = request.capability_service.run_capability(capability_request)
return Response(json_body=execution.__json__())
@view_config(route_name="delete_capability_request", renderer="json")
def delete_capability_request(request: Request) -> Response:
"""
Pyramid view that accepts a request to delete a capability request from the face of the archive
URL: capability/{capability_name}/request/{request_id}
:param request: DELETE request
:return: 200 OK response
or 412 response (HTTPPreconditionFailed) if capability request with given ID does not exist and thus cannot be
deleted
or 400 response (HTTPBadRequest) if capability request with given ID cannot be deleted
"""
capability_name = request.matchdict["capability_name"]
request_id = request.matchdict["request_id"]
capability_request = request.capability_info.lookup_capability_request(request_id)
if not capability_request:
# Capability request not found
does_not_exist_msg = f"Capability request for {capability_name} with ID {request_id} does not exist. Cannot delete."
return HTTPPreconditionFailed(detail=does_not_exist_msg)
else:
rows_deleted = request.capability_info.delete_capability_request(request_id)
if rows_deleted == -1:
bad_request_msg = (
f"Capability request for {capability_name} with ID {request_id} cannot be deleted;",
f"it has existing executions.",
)
return HTTPBadRequest(detail=bad_request_msg)
return Response(
body=f"Capability request for {capability_name} with ID {request_id} successfully deleted."
)
......@@ -90,6 +90,24 @@ class MockCapabilityInfo(MagicMock):
return capability_request
return None
def delete_capability_request(self, request_id: int) -> int:
for capability_request in self.capability_requests:
if request_id == capability_request.id:
if self.can_delete_capability_request(request_id):
self.capability_requests.remove(capability_request)
return 1
else:
return -1
return 0
def can_delete_capability_request(self, request_id) -> bool:
for execution in self.capability_executions:
if execution.capability_request_id == request_id:
print(execution)
print(execution.capability_request_id)
return False
return True
def save_entity(self, entity: Any):
if type(entity) is Capability:
self.capabilities.append(entity)
......@@ -150,6 +168,7 @@ class MockExecutionManager:
"""
execution = CapabilityExecution(
state=ExecutionState.Ready.name,
capability_request_id=capability_request.id,
version=CapabilityVersion(
capability_request_id=capability_request.id,
version_number=1,
......
......@@ -135,3 +135,52 @@ def test_submit_capability_request_error(
response = submit_capability_request(request_null_capability)
assert response.status_code == 412
assert type(response) is HTTPPreconditionFailed
def test_delete_capability_request(
test_config: Configurator, request_null_capability: DummyRequest
):
"""
Tests that the delete_capability_request view correctly executes its logic
:param test_config: Dummy Pyramid Configurator object set up for testing
:param request_null_capability: Dummy Pyramid request object set up with mocked DB access
supporting the null capability
"""
from capability.views.capability_request import delete_capability_request
request_null_capability.capability_info.capability_executions = []
request_null_capability.matchdict["capability_name"] = "null"
request_null_capability.matchdict["request_id"] = 0
response = delete_capability_request(request_null_capability)
assert response.status_code == 200
def test_delete_capability_request_error(
test_config: Configurator, request_null_capability: DummyRequest
):
"""
Tests that the delete_capability_request view correctly executes its logic
:param test_config: Dummy Pyramid Configurator object set up for testing
:param request_null_capability: Dummy Pyramid request object set up with mocked DB access
supporting the null capability
"""
from capability.views.capability_request import delete_capability_request
# Request cannot be deleted
request_id = request_null_capability.matchdict["request_id"] = 1
capability_request = request_null_capability.capability_info.lookup_capability_request(
request_id
)
request_null_capability.matchdict["capability_name"] = "null"
request_null_capability.capability_service.run_capability(capability_request)
response = delete_capability_request(request_null_capability)
assert response.status_code == 400
assert type(response) is HTTPBadRequest
# Request does not exist
request_null_capability.matchdict["request_id"] = -1
response = delete_capability_request(request_null_capability)
assert response.status_code == 412
assert type(response) is HTTPPreconditionFailed
from workspaces.capability.schema import CapabilityVersion
from workspaces.capability.services.capability_info import CapabilityInfo
pytest_plugins = ["testing.utils.conftest"]
......@@ -24,3 +25,24 @@ def test_edit_capability(mock_capability_info: CapabilityInfo):
mock_capability_info.session.update.return_value = 0
assert not mock_capability_info.edit_capability("null", "test", 2)
assert mock_capability_info.session.flush.call_count == 4
# def test_delete_request_versions(mock_capability_info: CapabilityInfo):
# """
# Tests that delete_request_versions correctly deletes capability versions associated with a capability request
# TODO: Implement once mocked capability_info is complete with mocked values for capability requests
# """
# pass
def test_can_delete_capability_request(mock_capability_info: CapabilityInfo):
"""
Tests that can_delete_capability_request correctly determines if a capability request is safe to delete
"""
request = mock_capability_info.create_capability_request("null")
request.capability = mock_capability_info.lookup_capability("null")
# Request has no executions; can be deleted
assert mock_capability_info.can_delete_capability_request(request.id) is True
mock_capability_info.create_execution(request)
# Request not has associated execution; can no longer be deleted
assert mock_capability_info.can_delete_capability_request(request.id) is False
......@@ -177,3 +177,49 @@ class CapabilityInfo(CapabilityInfoIF):
def save_execution(self, execution: CapabilityExecutionIF):
self.session.add(execution)
transaction.commit()
def delete_request_versions(self, request_id: int) -> int:
"""
Deleting all versions of a capability request. This is executed as preparation for deleting the request itself
:param request_id: ID of request that's versions will be deleted
:return: Number of rows deleted
"""
rows_changed = (
self.session.query(CapabilityVersion)
.filter_by(capability_request_id=request_id)
.delete()
)
self.session.flush()
return rows_changed
def delete_capability_request(self, request_id: int) -> int:
"""
Delete a specified capability request
:param request_id: ID of capability request to delete
:return: Number of rows deleted
"""
if self.can_delete_capability_request(request_id):
self.delete_request_versions(request_id)
rows_changed = self.session.query(CapabilityRequest).filter_by(id=request_id).delete()
self.session.flush()
return rows_changed
else:
return -1
def can_delete_capability_request(self, request_id: int) -> bool:
"""
Method that checks if a capability request has executions associated with it. If it does, it cannot be deleted.
:param request_id: ID of capability request to be checked
:return: True if there are no executions associated with request; else False
"""
if (
self.session.query(CapabilityExecution)
.filter_by(capability_request_id=request_id)
.all()
):
return False
else:
return True
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment