Skip to content

Commit

Permalink
Merge branch 'main' into task/WP-505--APCD--File-Upload-Failures
Browse files Browse the repository at this point in the history
  • Loading branch information
jalowe13 authored Dec 6, 2024
2 parents a877589 + 498d801 commit 4921686
Show file tree
Hide file tree
Showing 5 changed files with 162 additions and 43 deletions.
1 change: 1 addition & 0 deletions client/src/components/PublicData/PublicData.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@
/* FAQ: Public pages, like `PublicData` and `SiteSearch`, have no sidebar */
/* padding-left: 1.5em; /* ~24px (20px * design * 1.2 design-to-app ratio) */
}

35 changes: 0 additions & 35 deletions client/src/hooks/datafiles/mutations/useMove.js

This file was deleted.

127 changes: 127 additions & 0 deletions client/src/hooks/datafiles/mutations/useMove.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import { useDispatch, useSelector, shallowEqual } from 'react-redux';
import { useSelectedFiles } from 'hooks/datafiles';
import { useMutation } from '@tanstack/react-query';
import { apiClient } from 'utils/apiClient';
import truncateMiddle from 'utils/truncateMiddle';

export async function moveFileUtil({
api,
scheme,
system,
path,
destSystem,
destPath,
}: {
api: string;
scheme: string;
system: string;
path: string;
destSystem: string;
destPath: string;
}): Promise<{ name: string; path: string }> {
const body = {
dest_system: destSystem,
dest_path: destPath,
};
const url = `/api/datafiles/${api}/move/${scheme}/${system}/${path}/`;
const request = await apiClient.put(url, body);
return request.data;
}

function useMove() {
const dispatch = useDispatch();
const { selectedFiles: selected } = useSelectedFiles();
const status = useSelector(
(state: any) => state.files.operationStatus.move,
shallowEqual
);

const { api, scheme } = useSelector(
(state: any) => state.files.params.FilesListing
);
const setStatus = () => {
dispatch({
type: 'DATA_FILES_SET_OPERATION_STATUS',
payload: { operation: 'move', status: 'RUNNING' },
});
};

const { mutateAsync } = useMutation({
mutationFn: moveFileUtil,
});

const move = ({
destSystem,
destPath,
callback,
}: {
destSystem: string;
destPath: string;
callback: () => void;
}) => {
const filteredSelected = selected.filter(
(f: any) => status[f.id] !== 'SUCCESS'
);
const moveCalls: Promise<any>[] = filteredSelected.forEach((file: any) => {
dispatch({
type: 'DATA_FILES_SET_OPERATION_STATUS_BY_KEY',
payload: {
status: 'RUNNING',
key: file.id,
operation: 'move',
},
});
return mutateAsync(
{
api: api,
scheme: scheme,
system: file.system,
path: file.path,
destSystem: destSystem,
destPath: destPath,
},
{
onSuccess: () => {
dispatch({
type: 'DATA_FILES_SET_OPERATION_STATUS_BY_KEY',
payload: {
status: 'SUCCESS',
key: (index: string) => index,
operation: 'move',
},
});
dispatch({
type: 'DATA_FILES_TOGGLE_MODAL',
payload: { operation: 'move', props: {} },
});
dispatch({
type: 'ADD_TOAST',
payload: {
message: `${
filteredSelected.length > 1
? `${filteredSelected.length} files`
: 'File'
} moved to ${truncateMiddle(destPath, 20) || '/'}`,
},
});
callback();
},
onError: () => {
dispatch({
type: 'DATA_FILES_SET_OPERATION_STATUS_BY_KEY',
payload: {
status: 'ERROR',
key: (index: string) => index,
operation: 'move',
},
});
},
}
);
});
};

return { move, status, setStatus };
}

export default useMove;
12 changes: 10 additions & 2 deletions server/portal/apps/site_search/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,18 @@ def get(self, request, *args, **kwargs):
return JsonResponse(response)

def _handle_tapis_ssh_exception(self, e):
if 'SSH_POOL_MISSING_CREDENTIALS' in str(e) or 'SSH_FX_PERMISSION_DENIED' in str(e):
if (
"SSH_POOL_MISSING_CREDENTIALS" in str(e)
or "SSH_FX_PERMISSION_DENIED" in str(e)
or "FILES_CLIENT_SSH_OP_ERR1" in str(e)
):
# in case of these error types, user is not authenticated
# or does not have access do not fail the entire search
# request, log the issue.
logger.exception("Error retrieving search results due to TAPIS SSH related error: {}".format(str(e)))
logger.exception(
"Error retrieving search results due to TAPIS SSH related error: {}".format(
str(e)
)
)
else:
raise
30 changes: 24 additions & 6 deletions server/portal/apps/site_search/views_unit_test.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,36 @@
from unittest.mock import patch

from tapipy.errors import BaseTapyException

from portal.apps.site_search.api.views import SiteSearchApiView


def test_search_unauthenticated(client, regular_user):
response = client.get('/search/')
response = client.get("/search/")
assert response.status_code == 200
assert response.context['setup_complete'] is False
assert response.context["setup_complete"] is False


def test_search_authenticated_without_setup_complete(client, authenticated_user):
response = client.get('/search/')
response = client.get("/search/")
assert response.status_code == 200
assert response.context['setup_complete'] is False
assert response.context["setup_complete"] is False


def test_search_authenticated_with_setup_complete(client, authenticated_user):
authenticated_user.profile.setup_complete = True
authenticated_user.profile.save()
response = client.get('/search/')
response = client.get("/search/")
assert response.status_code == 200
assert response.context['setup_complete']
assert response.context["setup_complete"]


@patch("portal.apps.site_search.api.views.logger")
def test_handle_tapis_ssh_exception_files_client_ssh_op_err1(mock_logger):
view = SiteSearchApiView()
message = "FILES_CLIENT_SSH_OP_ERR1"
exception = BaseTapyException(message)
view._handle_tapis_ssh_exception(exception)
mock_logger.exception.assert_called_once_with(
f"Error retrieving search results due to TAPIS SSH related error: message: {message}"
)

0 comments on commit 4921686

Please sign in to comment.