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

UIBULKED-570 Downloading files from Logs tab #676

Merged
merged 8 commits into from
Jan 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
* [UIBULKED-599](https://folio-org.atlassian.net/browse/UIBULKED-599) Change Administrative note type is not supported for MARC instances.
* [UIBULKED-589](https://folio-org.atlassian.net/browse/UIBULKED-589) Make options in the "Actions" dropdown in "Bulk edits" in alphabetical order.
* [UIBULKED-588](https://folio-org.atlassian.net/browse/IBULKED-588) Displaying errors and warnings.
* [UIBULKED-570](https://folio-org.atlassian.net/browse/UIBULKED-570) Downloading files from Logs tab

## [4.2.2](https://github.com/folio-org/ui-bulk-edit/tree/v4.2.2) (2024-11-15)

Expand Down
8 changes: 3 additions & 5 deletions src/components/BulkEditActionMenu/BulkEditActionMenu.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { useOkapiKy } from '@folio/stripes/core';

import '../../../test/jest/__mock__';
import { runAxeTest } from '@folio/stripes-testing';
import { omit } from 'lodash';
import { bulkEditLogsData } from '../../../test/jest/__mock__/fakeData';
import { queryClient } from '../../../test/jest/utils/queryClient';

Expand All @@ -19,8 +20,8 @@ import {
CRITERIA,
EDITING_STEPS,
JOB_STATUSES,
FILE_KEYS,
FILE_SEARCH_PARAMS,
LINK_KEYS,
} from '../../constants';
import { useBulkOperationDetails } from '../../hooks/api';

Expand All @@ -36,10 +37,7 @@ const onToggle = jest.fn();
const bulkOperation = {
...bulkEditLogsData[0],
status: JOB_STATUSES.DATA_MODIFICATION,
[FILE_KEYS.MATCHING_RECORDS_LINK]: FILE_KEYS.MATCHING_RECORDS_LINK,
[FILE_KEYS.UPDATED_RECORDS_LINK]: FILE_KEYS.UPDATED_RECORDS_LINK,
[FILE_KEYS.MATCHING_ERRORS_LINK]: FILE_KEYS.MATCHING_ERRORS_LINK,
[FILE_KEYS.UPDATED_ERRORS_LINK]: FILE_KEYS.UPDATED_ERRORS_LINK,
...omit(LINK_KEYS, ['expired']),
};
const defaultProviderState = {
visibleColumns: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import React, {
useEffect,
} from 'react';
import PropTypes from 'prop-types';
import { saveAs } from 'file-saver';
import {
IconButton,
DropdownMenu,
Expand All @@ -16,12 +15,12 @@ import {
} from '@folio/stripes/components';
import { FormattedMessage } from 'react-intl';
import { QUERY_KEY_DOWNLOAD_LOGS, useFileDownload } from '../../../hooks/api';
import { APPROACHES, CAPABILITIES, linkNamesMap } from '../../../constants';
import { APPROACHES, CAPABILITIES, LINK_KEYS } from '../../../constants';
import { useBulkPermissions } from '../../../hooks';
import { getFileName } from '../../../utils/files';
import { savePreviewFile } from '../../../utils/files';

const BulkEditLogsActions = ({ item }) => {
const fileNamePostfix = item.fqlQueryId ? `.${APPROACHES.QUERY}` : '';
const fileNamePostfix = item?.fqlQueryId ? `.${APPROACHES.QUERY}` : '';

const {
hasUsersViewPerms,
Expand All @@ -33,9 +32,13 @@ const BulkEditLogsActions = ({ item }) => {
queryKey: QUERY_KEY_DOWNLOAD_LOGS,
enabled: false,
id: item.id,
fileContentType: linkNamesMap[triggeredFile],
onSuccess: data => {
saveAs(new Blob([data]), getFileName(item, triggeredFile));
fileContentType: LINK_KEYS[triggeredFile],
onSuccess: fileData => {
savePreviewFile({
fileName: item?.[triggeredFile],
fileData,
});

setTriggeredFile(null);
},
});
Expand All @@ -50,7 +53,7 @@ const BulkEditLogsActions = ({ item }) => {
setTriggeredFile(file);
};

const availableFiles = Object.keys(linkNamesMap).filter(linkName => item[linkName]);
const availableFiles = Object.keys(LINK_KEYS).filter(linkName => item[linkName]);

const renderTrigger = useCallback(({ triggerRef, onToggle, ariaProps, keyHandler }) => (
<IconButton
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,45 +3,43 @@ import { render, screen } from '@testing-library/react';
import { QueryClientProvider } from 'react-query';
import { useOkapiKy } from '@folio/stripes/core';
import '../../../../test/jest/__mock__';
import { omit } from 'lodash';
import {
bulkEditLogsData,
bulkEditLogsDataWithExpiredFlag,
} from '../../../../test/jest/__mock__/fakeData';
import { queryClient } from '../../../../test/jest/utils/queryClient';
import {
JOB_STATUSES,
FILE_KEYS, CAPABILITIES,
FILE_KEYS,
CAPABILITIES,
LINK_KEYS,
} from '../../../constants';
import BulkEditLogsActions from './BulkEditLogsActions';
import { useBulkPermissions } from '../../../hooks';
import { useFileDownload } from '../../../hooks/api';

const linksWithoutExpired = omit(LINK_KEYS, ['expired']);

const bulkOperation = {
...bulkEditLogsData[0],
status: JOB_STATUSES.DATA_MODIFICATION,
[FILE_KEYS.MATCHING_RECORDS_LINK]: FILE_KEYS.MATCHING_RECORDS_LINK,
[FILE_KEYS.UPDATED_RECORDS_LINK]: FILE_KEYS.UPDATED_RECORDS_LINK,
[FILE_KEYS.MATCHING_ERRORS_LINK]: FILE_KEYS.MATCHING_ERRORS_LINK,
[FILE_KEYS.UPDATED_ERRORS_LINK]: FILE_KEYS.UPDATED_ERRORS_LINK,
[FILE_KEYS.PROPOSED_CHANGES_LINK]: FILE_KEYS.PROPOSED_CHANGES_LINK,
[FILE_KEYS.TRIGGERING_FILE]: FILE_KEYS.TRIGGERING_FILE,
[FILE_KEYS.PROPOSED_CHANGES_LINK_MARC]: FILE_KEYS.PROPOSED_CHANGES_LINK_MARC,
[FILE_KEYS.UPDATE_CHANGES_LINK_MARC]: FILE_KEYS.UPDATE_CHANGES_LINK_MARC,
...linksWithoutExpired,
};

const bulkOperationWithExpired = {
...bulkEditLogsDataWithExpiredFlag[0],
status: JOB_STATUSES.DATA_MODIFICATION,
[FILE_KEYS.MATCHING_RECORDS_LINK]: FILE_KEYS.MATCHING_RECORDS_LINK,
[FILE_KEYS.UPDATED_RECORDS_LINK]: FILE_KEYS.UPDATED_RECORDS_LINK,
[FILE_KEYS.MATCHING_ERRORS_LINK]: FILE_KEYS.MATCHING_ERRORS_LINK,
[FILE_KEYS.UPDATED_ERRORS_LINK]: FILE_KEYS.UPDATED_ERRORS_LINK,
[FILE_KEYS.PROPOSED_CHANGES_LINK]: FILE_KEYS.PROPOSED_CHANGES_LINK,
[FILE_KEYS.PROPOSED_CHANGES_LINK_MARC]: FILE_KEYS.PROPOSED_CHANGES_LINK_MARC,
[FILE_KEYS.TRIGGERING_FILE]: FILE_KEYS.TRIGGERING_FILE,
...linksWithoutExpired,
};
jest.mock('../../../hooks', () => ({
...jest.requireActual('../../../hooks'),
useBulkPermissions: jest.fn(),
useFileDownload: jest.fn(),
}));

jest.mock('../../../hooks/api', () => ({
useFileDownload: jest.fn(),
}));

jest.mock('../../../hooks/useSearchParams', () => ({
Expand All @@ -57,6 +55,19 @@ const renderBulkEditLogsActions = ({ item = bulkOperation } = {}) => {
};

describe('BulkEditLogsActions', () => {
const mockPermissions = {
hasUsersViewPerms: true,
hasInventoryInstanceViewPerms: true,
};
const mockRefetch = jest.fn();

beforeEach(() => {
useBulkPermissions.mockReturnValue(mockPermissions);
useFileDownload.mockReturnValue({
refetch: mockRefetch,
});
});

beforeEach(() => {
useOkapiKy.mockClear().mockReturnValue({});
});
Expand Down
14 changes: 8 additions & 6 deletions src/components/BulkEditPane/BulkEditPane.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React, { useCallback, useMemo, useState } from 'react';
import { FormattedMessage } from 'react-intl';
import { saveAs } from 'file-saver';

import {
Pane,
Expand All @@ -22,7 +21,7 @@ import {
CRITERIA,
APPROACHES,
FILE_SEARCH_PARAMS,
FILE_TO_LINK,
FILE_KEYS,
} from '../../constants';
import { RootContext } from '../../context/RootContext';
import { BulkEditLogs } from '../BulkEditLogs/BulkEditLogs';
Expand All @@ -38,6 +37,7 @@ import { useResetFilters } from '../../hooks/useResetFilters';

import { BulkEditInAppLayer } from './BulkEditInAppLayer/BulkEditInAppLayer';
import { BulkEditMarcLayer } from './BulkEditMarcLayer/BulkEditMarcLayer';
import { savePreviewFile } from '../../utils/files';

export const BulkEditPane = () => {
const [isFileUploaded, setIsFileUploaded] = useState(false);
Expand Down Expand Up @@ -103,10 +103,12 @@ export const BulkEditPane = () => {
queryKey: QUERY_KEY_DOWNLOAD_ACTION_MENU,
enabled: !!fileInfo,
id: bulkOperationId,
fileContentType: FILE_SEARCH_PARAMS[fileInfo?.param]?.replace('_MARC', ''),
onSuccess: data => {
/* istanbul ignore next */
saveAs(new Blob([data]), fileInfo?.bulkDetails[FILE_TO_LINK[fileInfo?.param]].split('/')[1]);
fileContentType: FILE_SEARCH_PARAMS[fileInfo?.param],
onSuccess: fileData => {
savePreviewFile({
fileData,
fileName: fileInfo?.bulkDetails[FILE_KEYS[fileInfo?.param]],
});
},
onSettled: () => {
/* istanbul ignore next */
Expand Down
48 changes: 26 additions & 22 deletions src/constants/files.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,65 +2,69 @@ import { FormattedMessage } from 'react-intl';
import React from 'react';
import { EDITING_STEPS } from './core';

// use as marks that getFileName are ready
export const FILE_KEYS = {
MATCHING_RECORDS_LINK: 'linkToMatchedRecordsCsvFile',
MATCHING_ERRORS_LINK: 'linkToMatchedRecordsErrorsCsvFile',
PROPOSED_CHANGES_LINK: 'linkToModifiedRecordsCsvFile',
PROPOSED_CHANGES_LINK_MARC: 'linkToModifiedRecordsMarcFile',
UPDATE_CHANGES_LINK_MARC: 'linkToCommittedRecordsMarcFile',
UPDATED_RECORDS_LINK: 'linkToCommittedRecordsCsvFile',
UPDATED_ERRORS_LINK: 'linkToCommittedRecordsErrorsCsvFile',
TRIGGERING_FILE: 'linkToTriggeringCsvFile',
};

// use as API key for /download
export const FILE_SEARCH_PARAMS = {
TRIGGERING_FILE: 'TRIGGERING_FILE',
MATCHED_RECORDS_FILE: 'MATCHED_RECORDS_FILE',
RECORD_MATCHING_ERROR_FILE: 'RECORD_MATCHING_ERROR_FILE',
COMMITTED_RECORDS_FILE: 'COMMITTED_RECORDS_FILE',
COMMITTED_RECORDS_MARC_FILE: 'COMMITTED_RECORDS_MARC_FILE',
COMMITTING_CHANGES_ERROR_FILE: 'COMMITTING_CHANGES_ERROR_FILE',
PROPOSED_CHANGES_FILE: 'PROPOSED_CHANGES_FILE',
PROPOSED_CHANGES_MARC_FILE: 'PROPOSED_CHANGES_MARC_FILE',
COMMITTED_RECORDS_FILE_MARC: 'COMMITTED_RECORDS_FILE_MARC',
};

export const FILE_TO_LINK = {
// use as marks that getFileName are ready
export const FILE_KEYS = {
TRIGGERING_FILE: 'linkToTriggeringCsvFile',
MATCHED_RECORDS_FILE: 'linkToMatchedRecordsCsvFile',
RECORD_MATCHING_ERROR_FILE: 'linkToMatchedRecordsErrorsCsvFile',
COMMITTED_RECORDS_FILE: 'linkToCommittedRecordsCsvFile',
COMMITTED_RECORDS_MARC_FILE: 'linkToCommittedRecordsMarcFile',
COMMITTING_CHANGES_ERROR_FILE: 'linkToCommittedRecordsErrorsCsvFile',
PROPOSED_CHANGES_FILE: 'linkToModifiedRecordsCsvFile',
COMMITTED_RECORDS_FILE_MARC: 'linkToCommittedRecordsMarcFile'
PROPOSED_CHANGES_MARC_FILE: 'linkToModifiedRecordsMarcFile',
};

export const LINK_KEYS = {
linkToTriggeringCsvFile: FILE_SEARCH_PARAMS.TRIGGERING_FILE,
linkToMatchedRecordsCsvFile: FILE_SEARCH_PARAMS.MATCHED_RECORDS_FILE,
linkToMatchedRecordsErrorsCsvFile: FILE_SEARCH_PARAMS.RECORD_MATCHING_ERROR_FILE,
linkToModifiedRecordsCsvFile: FILE_SEARCH_PARAMS.PROPOSED_CHANGES_FILE,
linkToModifiedRecordsMarcFile: FILE_SEARCH_PARAMS.PROPOSED_CHANGES_MARC_FILE,
linkToCommittedRecordsCsvFile: FILE_SEARCH_PARAMS.COMMITTED_RECORDS_FILE,
linkToCommittedRecordsMarcFile: FILE_SEARCH_PARAMS.COMMITTED_RECORDS_MARC_FILE,
linkToCommittedRecordsErrorsCsvFile: FILE_SEARCH_PARAMS.COMMITTING_CHANGES_ERROR_FILE,
expired: 'expired',
};

export const getDownloadLinks = ({ perms, step }) => [
{
KEY: FILE_KEYS.MATCHING_RECORDS_LINK,
KEY: FILE_KEYS.MATCHED_RECORDS_FILE,
SEARCH_PARAM: FILE_SEARCH_PARAMS.MATCHED_RECORDS_FILE,
LINK_NAME: <FormattedMessage id="ui-bulk-edit.start.downloadMathcedRecords" />,
LINK_NAME: <FormattedMessage id="ui-bulk-edit.start.downloadMatchedRecords" />,
IS_VISIBLE: perms.hasAnyEditPermissions && step === EDITING_STEPS.UPLOAD,
},
{
KEY: FILE_KEYS.UPDATED_RECORDS_LINK,
KEY: FILE_KEYS.COMMITTED_RECORDS_FILE,
SEARCH_PARAM: FILE_SEARCH_PARAMS.COMMITTED_RECORDS_FILE,
LINK_NAME: <FormattedMessage id="ui-bulk-edit.start.downloadChangedRecords" />,
IS_VISIBLE: perms.hasAnyEditPermissions,
},
{
KEY: FILE_KEYS.UPDATE_CHANGES_LINK_MARC,
SEARCH_PARAM: FILE_SEARCH_PARAMS.COMMITTED_RECORDS_FILE_MARC,
KEY: FILE_KEYS.COMMITTED_RECORDS_MARC_FILE,
SEARCH_PARAM: FILE_SEARCH_PARAMS.COMMITTED_RECORDS_MARC_FILE,
LINK_NAME: <FormattedMessage id="ui-bulk-edit.start.downloadChangedRecords.marc" />,
IS_VISIBLE: perms.hasAnyEditPermissions,
},
{
KEY: FILE_KEYS.MATCHING_ERRORS_LINK,
KEY: FILE_KEYS.RECORD_MATCHING_ERROR_FILE,
SEARCH_PARAM: FILE_SEARCH_PARAMS.RECORD_MATCHING_ERROR_FILE,
LINK_NAME: <FormattedMessage id="ui-bulk-edit.start.downloadErrors" />,
IS_VISIBLE: perms.hasAnyEditPermissions && step === EDITING_STEPS.UPLOAD,
},
{
KEY: FILE_KEYS.UPDATED_ERRORS_LINK,
KEY: FILE_KEYS.COMMITTING_CHANGES_ERROR_FILE,
SEARCH_PARAM: FILE_SEARCH_PARAMS.COMMITTING_CHANGES_ERROR_FILE,
LINK_NAME: <FormattedMessage id="ui-bulk-edit.start.downloadErrors" />,
IS_VISIBLE: perms.hasAnyEditPermissions && step === EDITING_STEPS.COMMIT,
Expand Down
1 change: 0 additions & 1 deletion src/constants/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,4 @@ export * from './columns';
export * from './inAppActions';
export * from '../utils/date';
export * from './files';
export * from './logsActions';
export * from './moduleNames';
11 changes: 0 additions & 11 deletions src/constants/logsActions.js

This file was deleted.

20 changes: 0 additions & 20 deletions src/utils/files.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,5 @@
import { saveAs } from 'file-saver';

import { getFormattedFilePrefixDate } from './date';


export const getFileName = (item, triggeredFile) => {
if (item.fqlQueryId) {
return {
linkToTriggeringCsvFile: `Query-${item.id}.csv`,
linkToMatchedRecordsCsvFile: `${getFormattedFilePrefixDate()}-Matched-Records-Query-${item.id}.csv`,
linkToModifiedRecordsCsvFile: `${getFormattedFilePrefixDate()}-Updates-Preview-Query-${item.id}.csv`,
linkToModifiedRecordsMarcFile: `${getFormattedFilePrefixDate()}-Updates-Preview-Query-${item.id}.mrc`,
linkToCommittedRecordsCsvFile: `${getFormattedFilePrefixDate()}-Changed-Records-Query-${item.id}.csv`,
linkToCommittedRecordsMarcFile: `${getFormattedFilePrefixDate()}-Changed-Records-Query-${item.id}.mrc`,
linkToCommittedRecordsErrorsCsvFile: `${getFormattedFilePrefixDate()}-Committing-changes-Errors-Query-${item.id}.csv`,
linkToMatchedRecordsErrorsCsvFile:`${getFormattedFilePrefixDate()}-Matching-Records-Errors-Query-${item.id}.csv`,
}[triggeredFile];
}

return item[triggeredFile].split('/')[1];
};

export const changeExtension = (fileName, extension) => {
if (!fileName) return fileName;

Expand Down
25 changes: 1 addition & 24 deletions src/utils/files.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { saveAs } from 'file-saver';

import { getFileName, changeExtension, savePreviewFile } from './files';
import { changeExtension, savePreviewFile } from './files';
import { getFormattedFilePrefixDate } from './date';


Expand All @@ -14,29 +14,6 @@ jest.mock('file-saver', () => ({


describe('files', () => {
describe('getFileName', () => {
it('should return the correct file name for Query approach - linkToTriggeringCsvFile', () => {
const item = { fqlQueryId: '111', id: 123 };
const triggeredFile = 'linkToTriggeringCsvFile';
const result = getFileName(item, triggeredFile);
expect(result).toBe('Query-123.csv');
});

it('should return the correct file name for Query approach - linkToMatchedRecordsCsvFile', () => {
const item = { fqlQueryId: '111', id: 123 };
const triggeredFile = 'linkToMatchedRecordsCsvFile';
const result = getFileName(item, triggeredFile);
expect(result).toBe('mockedDate-Matched-Records-Query-123.csv');
});

it('should return the correct file name for non-Query approach', () => {
const item = { fqlQueryId: null, linkToTriggeringCsvFile: 'somePath/someFile.csv' };
const triggeredFile = 'linkToTriggeringCsvFile';
const result = getFileName(item, triggeredFile);
expect(result).toBe('someFile.csv');
});
});

describe('changeExtension', () => {
it('should change the extension of a file', () => {
expect(changeExtension('abc.csv', 'mrc')).toBe('abc.mrc');
Expand Down
4 changes: 2 additions & 2 deletions src/utils/formatters.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import { NoValue } from '@folio/stripes/components';
import { FolioFormattedTime } from '@folio/stripes-acq-components';

import BulkEditLogsActions from '../components/BulkEditLogs/BulkEditLogsActions/BulkEditLogsActions';
import { linkNamesMap } from '../constants';
import { LINK_KEYS } from '../constants';

const isActionsRendered = (item) => Object.keys(item).some(key => Object.keys(linkNamesMap).includes(key));
const isActionsRendered = (item) => Object.keys(item).some(key => Object.keys(LINK_KEYS).includes(key));

export const getLogsResultsFormatter = () => ({
id: item => item.id,
Expand Down
Loading
Loading