Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

task/WP-163 Compress Archive Path Fix #846

Merged
merged 9 commits into from
Sep 13, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ const DataFilesDownloadMessageModal = () => {
payload: {
filename: filenameDisplay,
files: selected,
scheme: params.scheme,
compressionType,
onSuccess: {
type: 'DATA_FILES_TOGGLE_MODAL',
Expand All @@ -74,6 +75,7 @@ const DataFilesDownloadMessageModal = () => {
: `Archive_${new Date().toISOString().split('.')[0]}`,
compressionType: 'zip',
};

const validationSchema = yup.object().shape({
filenameDisplay: yup
.string()
Expand Down Expand Up @@ -110,7 +112,8 @@ const DataFilesDownloadMessageModal = () => {
const handleSelectChange = (e) => {
setFieldValue('compressionType', e.target.value);
};
const formDisabled = status === 'RUNNING' || status === 'SUCCESS';
const formDisabled =
status.type === 'RUNNING' || status.type === 'SUCCESS';
const buttonDisabled =
formDisabled || !isValid || values.filenameDisplay === '';
return (
Expand Down Expand Up @@ -153,15 +156,21 @@ const DataFilesDownloadMessageModal = () => {
</p>
</ModalBody>
<ModalFooter>
<InlineMessage isVisible={status === 'SUCCESS'} type="success">
<InlineMessage
isVisible={status.type === 'SUCCESS'}
type="success"
>
Successfully started compress job
</InlineMessage>
{status.type === 'ERROR' && status.message && (
<InlineMessage type="error">{status.message}</InlineMessage>
)}
<Button
disabled={buttonDisabled}
type="primary"
size={status === 'ERROR' ? 'long' : 'medium'}
isLoading={status === 'RUNNING'}
iconNameBefore={status === 'ERROR' ? 'alert' : null}
size={status.type === 'ERROR' ? 'long' : 'medium'}
isLoading={status.type === 'RUNNING'}
iconNameBefore={status.type === 'ERROR' ? 'alert' : null}
attr="submit"
>
Compress
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ const systemsFixture = {
icon: null,
hidden: true,
homeDir: '/home/username',
default: true,
keyservice: true,
},
{
name: 'My Data (Frontera)',
Expand Down
2 changes: 1 addition & 1 deletion client/src/redux/reducers/datafiles.reducers.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ export const initialFilesState = {
upload: {},
trash: {},
empty: {},
compress: null,
compress: {},
extract: null,
link: {
method: null,
Expand Down
59 changes: 49 additions & 10 deletions client/src/redux/sagas/datafiles.sagas.js
Original file line number Diff line number Diff line change
Expand Up @@ -995,22 +995,31 @@ const getCompressParams = (
files,
archiveFileName,
compressionType,
defaultPrivateSystem,
latestCompress,
defaultAllocation
) => {
const fileInputs = files.map((file) => ({
sourceUrl: encodeURI(`tapis://${file.system}/${file.path}`),
}));

const archivePath = `${files[0].path.slice(0, -files[0].name.length)}`;
let archivePath, archiveSystem;

if (defaultPrivateSystem) {
archivePath = defaultPrivateSystem.homeDir;
archiveSystem = defaultPrivateSystem.system;
} else {
archivePath = `${files[0].path.slice(0, -files[0].name.length)}`;
archiveSystem = files[0].system;
}

return JSON.stringify({
job: {
fileInputs: fileInputs,
name: `${latestCompress.definition.id}-${
latestCompress.definition.version
}_${new Date().toISOString().split('.')[0]}`,
archiveSystemId: files[0].system,
archiveSystemId: archiveSystem,
archiveSystemDir: archivePath,
archiveOnAppError: false,
appId: latestCompress.definition.id,
Expand Down Expand Up @@ -1044,26 +1053,53 @@ const getCompressParams = (
export const compressAppSelector = (state) =>
state.workbench.config.compressApp;

export const systemsSelector = (state) => state.systems.storage.configuration;

export function* compressFiles(action) {
const compressErrorAction = {
type: 'DATA_FILES_SET_OPERATION_STATUS',
payload: { status: 'ERROR', operation: 'compress' },
const compressErrorAction = (errorMessage) => {
return {
type: 'DATA_FILES_SET_OPERATION_STATUS',
payload: {
status: { type: 'ERROR', message: errorMessage },
operation: 'compress',
},
};
};

try {
yield put({
type: 'DATA_FILES_SET_OPERATION_STATUS',
payload: { status: 'RUNNING', operation: 'compress' },
payload: { status: { type: 'RUNNING' }, operation: 'compress' },
});
const compressApp = yield select(compressAppSelector);
const defaultAllocation = yield select(defaultAllocationSelector);
const latestCompress = yield call(fetchAppDefinitionUtil, compressApp);
const systems = yield select(systemsSelector);

let defaultPrivateSystem;

if (
action.payload.scheme !== 'private' &&
shayanaijaz marked this conversation as resolved.
Show resolved Hide resolved
action.payload.scheme !== 'projects'
) {
defaultPrivateSystem = systems.find((s) => s.default);

if (!defaultPrivateSystem) {
throw new Error('Folder downloads are unavailable in this portal', {
cause: 'compressError',
});
}
}

const params = getCompressParams(
action.payload.files,
action.payload.filename,
action.payload.compressionType,
defaultPrivateSystem,
latestCompress,
defaultAllocation
);

const res = yield call(jobHelper, params);
// If the execution system requires pushing keys, then
// bring up the modal and retry the compress action
Expand All @@ -1075,23 +1111,26 @@ export function* compressFiles(action) {
props: {
onSuccess: action,
system: res.execSys,
onCancel: compressErrorAction,
onCancel: compressErrorAction('An error has occurred'),
},
},
});
} else if (res.status === 'PENDING') {
yield put({
type: 'DATA_FILES_SET_OPERATION_STATUS',
payload: { status: 'SUCCESS', operation: 'compress' },
payload: { status: { type: 'SUCCESS' }, operation: 'compress' },
});
} else {
throw new Error('Unable to compress files');
throw new Error('Unable to compress files', { cause: 'compressError' });
}
if (action.payload.onSuccess) {
yield put(action.payload.onSuccess);
}
} catch (error) {
yield put(compressErrorAction);
const errorMessage =
error.cause === 'compressError' ? error.message : 'An error has occurred';

yield put(compressErrorAction(errorMessage));
}
}
export async function jobHelper(body) {
Expand Down
98 changes: 67 additions & 31 deletions client/src/redux/sagas/datafiles.sagas.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
makePublicUtil,
doMakePublic,
defaultAllocationSelector,
systemsSelector,
} from './datafiles.sagas';
import { select } from 'redux-saga/effects';
import { expectSaga } from 'redux-saga-test-plan';
Expand All @@ -26,6 +27,7 @@ import * as matchers from 'redux-saga-test-plan/matchers';
import { fetchAppDefinitionUtil } from './apps.sagas';
import compressApp from './fixtures/compress.fixture';
import extractApp from './fixtures/extract.fixture';
import systemsFixture from 'components/DataFiles/fixtures/DataFiles.systems.fixture';

jest.mock('cross-fetch');

Expand Down Expand Up @@ -406,28 +408,31 @@ describe('extractFiles', () => {
});

describe('compressFiles', () => {
const action = {
type: 'DATA_FILES_COMPRESS',
payload: {
filename: 'test',
files: [
{
system: 'test.system',
path: 'test1.txt',
name: 'test1.txt',
},
{
system: 'test.system',
path: 'test2.txt',
name: 'test2.txt',
},
],
compressionType: 'zip',
},
const createAction = (scheme) => {
return {
type: 'DATA_FILES_COMPRESS',
payload: {
filename: 'test',
files: [
{
system: 'test.system',
path: 'test1.txt',
name: 'test1.txt',
},
{
system: 'test.system',
path: 'test2.txt',
name: 'test2.txt',
},
],
compressionType: 'zip',
scheme: scheme,
},
};
};

const jobHelperExpected = () =>
JSON.stringify({
const createJobHelperExpected = (archiveSystemId, archiveSystemDir) => {
return JSON.stringify({
job: {
fileInputs: [
{
Expand All @@ -438,8 +443,8 @@ describe('compressFiles', () => {
},
],
name: `compress-0.0.1_${new Date().toISOString().split('.')[0]}`,
archiveSystemId: 'test.system',
archiveSystemDir: '',
archiveSystemId: archiveSystemId,
archiveSystemDir: archiveSystemDir,
archiveOnAppError: false,
appId: 'compress',
appVersion: '0.0.1',
Expand Down Expand Up @@ -467,58 +472,89 @@ describe('compressFiles', () => {
execSystemId: 'frontera',
},
});
};

it('runs compressFiles saga with success', () => {
return expectSaga(compressFiles, action)
return expectSaga(compressFiles, createAction('private'))
.provide([
[select(compressAppSelector), 'compress'],
[select(defaultAllocationSelector), 'TACC-ACI'],
[select(systemsSelector), []],
[matchers.call.fn(fetchAppDefinitionUtil), compressApp],
[matchers.call.fn(jobHelper), { status: 'PENDING' }],
])
.call(fetchAppDefinitionUtil, 'compress')
.put({
type: 'DATA_FILES_SET_OPERATION_STATUS',
payload: { status: 'RUNNING', operation: 'compress' },
payload: { status: { type: 'RUNNING' }, operation: 'compress' },
})
.call(jobHelper, jobHelperExpected())
.call(jobHelper, createJobHelperExpected('test.system', ''))
.put({
type: 'DATA_FILES_SET_OPERATION_STATUS',
payload: { status: 'SUCCESS', operation: 'compress' },
payload: { status: { type: 'SUCCESS' }, operation: 'compress' },
})
.run();
});

it('runs compressFiles saga with push keys modal', () => {
return expectSaga(compressFiles, action)
return expectSaga(compressFiles, createAction('private'))
.provide([
[select(compressAppSelector), 'compress'],
[select(defaultAllocationSelector), 'TACC-ACI'],
[select(systemsSelector), []],
[matchers.call.fn(fetchAppDefinitionUtil), compressApp],
[matchers.call.fn(jobHelper), { execSys: 'test.cli.system' }],
])
.call(fetchAppDefinitionUtil, 'compress')
.put({
type: 'DATA_FILES_SET_OPERATION_STATUS',
payload: { status: 'RUNNING', operation: 'compress' },
payload: { status: { type: 'RUNNING' }, operation: 'compress' },
})
.call(jobHelper, jobHelperExpected())
.call(jobHelper, createJobHelperExpected('test.system', ''))
.put({
type: 'SYSTEMS_TOGGLE_MODAL',
payload: {
operation: 'pushKeys',
props: {
onSuccess: action,
onSuccess: createAction('private'),
system: 'test.cli.system',
onCancel: {
type: 'DATA_FILES_SET_OPERATION_STATUS',
payload: { status: 'ERROR', operation: 'compress' },
payload: {
status: { type: 'ERROR', message: 'An error has occurred' },
operation: 'compress',
},
},
},
},
})
.run();
});

it('runs compressFiles saga with success for file in a public system', () => {
return expectSaga(compressFiles, createAction('public'))
.provide([
[select(compressAppSelector), 'compress'],
[select(defaultAllocationSelector), 'TACC-ACI'],
[select(systemsSelector), systemsFixture.storage.configuration],
[matchers.call.fn(fetchAppDefinitionUtil), compressApp],
[matchers.call.fn(jobHelper), { status: 'PENDING' }],
])
.call(fetchAppDefinitionUtil, 'compress')
.put({
type: 'DATA_FILES_SET_OPERATION_STATUS',
payload: { status: { type: 'RUNNING' }, operation: 'compress' },
})
.call(
jobHelper,
createJobHelperExpected('corral.home.username', '/home/username')
)
.put({
type: 'DATA_FILES_SET_OPERATION_STATUS',
payload: { status: { type: 'SUCCESS' }, operation: 'compress' },
})
.run();
});
});

describe('copyFiles', () => {
Expand Down