From 09aa8b699b6b25488dfb4abf739cb29b4744f590 Mon Sep 17 00:00:00 2001 From: Christofer Date: Fri, 20 Oct 2023 18:22:10 +0000 Subject: [PATCH] feat: Use windows.location.href to call the export endpoint --- src/taxonomy/api/hooks/api.js | 37 ++------------ src/taxonomy/api/hooks/api.test.js | 54 +++++++++------------ src/taxonomy/api/hooks/selectors.js | 6 +-- src/taxonomy/api/hooks/selectors.test.js | 14 +++--- src/taxonomy/modals/ExportModal.jsx | 11 +---- src/taxonomy/taxonomy-card/TaxonomyCard.jsx | 1 - src/utils.js | 9 ---- 7 files changed, 39 insertions(+), 93 deletions(-) diff --git a/src/taxonomy/api/hooks/api.js b/src/taxonomy/api/hooks/api.js index 3efaea019a..b524bd197f 100644 --- a/src/taxonomy/api/hooks/api.js +++ b/src/taxonomy/api/hooks/api.js @@ -1,13 +1,12 @@ // @ts-check -import { useQuery, useMutation } from '@tanstack/react-query'; +import { useQuery } from '@tanstack/react-query'; import { camelCaseObject, getConfig } from '@edx/frontend-platform'; import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth'; -import { downloadDataAsFile } from '../../../utils'; const getApiBaseUrl = () => getConfig().STUDIO_BASE_URL; const getTaxonomyListApiUrl = () => new URL('api/content_tagging/v1/taxonomies/?enabled=true', getApiBaseUrl()).href; -const getExportTaxonomyApiUrl = (pk, format) => new URL( - `api/content_tagging/v1/taxonomies/${pk}/export/?output_format=${format}`, +export const getExportTaxonomyApiUrl = (pk, format) => new URL( + `api/content_tagging/v1/taxonomies/${pk}/export/?output_format=${format}&download=1`, getApiBaseUrl(), ).href; @@ -22,32 +21,6 @@ export const useTaxonomyListData = () => ( }) ); -export const useExportTaxonomy = () => { - /** - * Calls the export request and downloads the file. - * - * Extra logic is needed to download the exported file, - * because it is not possible to download the file using the Content-Disposition header - * Ref: https://medium.com/@drevets/you-cant-prompt-a-file-download-with-the-content-disposition-header-using-axios-xhr-sorry-56577aa706d6 - * - * @param {import("../types.mjs").ExportRequestParams} params - * @returns {Promise} - */ - const exportTaxonomy = async (params) => { - const { pk, format, name } = params; - const response = await getAuthenticatedHttpClient().get(getExportTaxonomyApiUrl(pk, format)); - const contentType = response.headers['content-type']; - let fileExtension = ''; - let data; - if (contentType === 'application/json') { - fileExtension = 'json'; - data = JSON.stringify(response.data, null, 2); - } else { - fileExtension = 'csv'; - data = response.data; - } - downloadDataAsFile(data, contentType, `${name}.${fileExtension}`); - }; - - return useMutation(exportTaxonomy); +export const exportTaxonomy = (pk, format) => { + window.location.href = getExportTaxonomyApiUrl(pk, format); }; diff --git a/src/taxonomy/api/hooks/api.test.js b/src/taxonomy/api/hooks/api.test.js index a34ede3da6..b712f05869 100644 --- a/src/taxonomy/api/hooks/api.test.js +++ b/src/taxonomy/api/hooks/api.test.js @@ -1,6 +1,5 @@ -import { useQuery, useMutation } from '@tanstack/react-query'; -import { useTaxonomyListData, useExportTaxonomy } from './api'; -import { downloadDataAsFile } from '../../../utils'; +import { useQuery } from '@tanstack/react-query'; +import { useTaxonomyListData, exportTaxonomy } from './api'; const mockHttpClient = { get: jest.fn(), @@ -19,12 +18,12 @@ jest.mock('../../../utils', () => ({ downloadDataAsFile: jest.fn(), })); -describe('taxonomy API', () => { +describe('useTaxonomyListData', () => { afterEach(() => { jest.clearAllMocks(); }); - it('useTaxonomyListData should call useQuery with the correct parameters', () => { + it('should call useQuery with the correct parameters', () => { useTaxonomyListData(); expect(useQuery).toHaveBeenCalledWith({ @@ -32,40 +31,31 @@ describe('taxonomy API', () => { queryFn: expect.any(Function), }); }); +}); - it('useExportTaxonomy should export data correctly', async () => { - useMutation.mockImplementation((exportFunc) => exportFunc); +describe('exportTaxonomy', () => { + const { location } = window; - const mockResponseJson = { - headers: { - 'content-type': 'application/json', - }, - data: { tags: 'tags' }, - }; - const mockResponseCsv = { - headers: { - 'content-type': 'text', - }, - data: 'This is a CSV', + beforeAll(() => { + delete window.location; + window.location = { + href: '', }; + }); - const exportTaxonomy = useExportTaxonomy(); + afterAll(() => { + window.location = location; + }); - mockHttpClient.get.mockResolvedValue(mockResponseJson); - await exportTaxonomy({ pk: 1, format: 'json', name: 'testFile' }); + it('should set window.location.href correctly', () => { + const pk = 1; + const format = 'json'; - expect(downloadDataAsFile).toHaveBeenCalledWith( - JSON.stringify(mockResponseJson.data, null, 2), - 'application/json', - 'testFile.json', - ); + exportTaxonomy(pk, format); - mockHttpClient.get.mockResolvedValue(mockResponseCsv); - await exportTaxonomy({ pk: 1, format: 'csv', name: 'testFile' }); - expect(downloadDataAsFile).toHaveBeenCalledWith( - mockResponseCsv.data, - 'text', - 'testFile.csv', + expect(window.location.href).toEqual( + 'http://localhost:18010/api/content_tagging/' + + 'v1/taxonomies/1/export/?output_format=json&download=1', ); }); }); diff --git a/src/taxonomy/api/hooks/selectors.js b/src/taxonomy/api/hooks/selectors.js index 970ae49392..b2c678be78 100644 --- a/src/taxonomy/api/hooks/selectors.js +++ b/src/taxonomy/api/hooks/selectors.js @@ -1,7 +1,7 @@ // @ts-check import { useTaxonomyListData, - useExportTaxonomy, + exportTaxonomy, } from './api'; /** @@ -22,6 +22,6 @@ export const useIsTaxonomyListDataLoaded = () => ( useTaxonomyListData().status === 'success' ); -export const useExportTaxonomyMutation = () => ( - useExportTaxonomy() +export const callExportTaxonomy = (pk, format) => ( + exportTaxonomy(pk, format) ); diff --git a/src/taxonomy/api/hooks/selectors.test.js b/src/taxonomy/api/hooks/selectors.test.js index 7654006922..772b39c4d8 100644 --- a/src/taxonomy/api/hooks/selectors.test.js +++ b/src/taxonomy/api/hooks/selectors.test.js @@ -1,14 +1,14 @@ import { useTaxonomyListDataResponse, useIsTaxonomyListDataLoaded, - useExportTaxonomyMutation, + callExportTaxonomy, } from './selectors'; -import { useTaxonomyListData, useExportTaxonomy } from './api'; +import { useTaxonomyListData, exportTaxonomy } from './api'; jest.mock('./api', () => ({ __esModule: true, useTaxonomyListData: jest.fn(), - useExportTaxonomy: jest.fn(), + exportTaxonomy: jest.fn(), })); describe('useTaxonomyListDataResponse', () => { @@ -47,10 +47,10 @@ describe('useIsTaxonomyListDataLoaded', () => { }); }); -describe('useExportTaxonomyMutation', () => { - it('should trigger useExportTaxonomy', () => { - useExportTaxonomyMutation(); +describe('callExportTaxonomy', () => { + it('should trigger exportTaxonomy', () => { + callExportTaxonomy(1, 'csv'); - expect(useExportTaxonomy).toHaveBeenCalled(); + expect(exportTaxonomy).toHaveBeenCalled(); }); }); diff --git a/src/taxonomy/modals/ExportModal.jsx b/src/taxonomy/modals/ExportModal.jsx index 3627648c9b..31dfd16449 100644 --- a/src/taxonomy/modals/ExportModal.jsx +++ b/src/taxonomy/modals/ExportModal.jsx @@ -8,25 +8,19 @@ import { import PropTypes from 'prop-types'; import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import messages from '../messages'; -import { useExportTaxonomyMutation } from '../api/hooks/selectors'; +import { callExportTaxonomy } from '../api/hooks/selectors'; const ExportModal = ({ taxonomyId, - taxonomyName, isOpen, onClose, intl, }) => { const [outputFormat, setOutputFormat] = useState('csv'); - const exportMutation = useExportTaxonomyMutation(); const onClickExport = () => { onClose(); - exportMutation.mutate({ - pk: taxonomyId, - format: outputFormat, - name: taxonomyName, - }); + callExportTaxonomy(taxonomyId, outputFormat); }; return ( @@ -84,7 +78,6 @@ const ExportModal = ({ ExportModal.propTypes = { taxonomyId: PropTypes.number.isRequired, - taxonomyName: PropTypes.string.isRequired, isOpen: PropTypes.bool.isRequired, onClose: PropTypes.func.isRequired, intl: intlShape.isRequired, diff --git a/src/taxonomy/taxonomy-card/TaxonomyCard.jsx b/src/taxonomy/taxonomy-card/TaxonomyCard.jsx index 48e6688d91..73a6145c7e 100644 --- a/src/taxonomy/taxonomy-card/TaxonomyCard.jsx +++ b/src/taxonomy/taxonomy-card/TaxonomyCard.jsx @@ -93,7 +93,6 @@ const TaxonomyCard = ({ className, original, intl }) => { isOpen={isExportModalOpen} onClose={() => setIsExportModalOpen(false)} taxonomyId={id} - taxonomyName={name} /> )} diff --git a/src/utils.js b/src/utils.js index d1dc1bfc5f..67a37e6db7 100644 --- a/src/utils.js +++ b/src/utils.js @@ -256,12 +256,3 @@ export const isValidDate = (date) => { return Boolean(formattedValue.length <= 10); }; - -export const downloadDataAsFile = (data, contentType, fileName) => { - const url = window.URL.createObjectURL(new Blob([data], { type: contentType })); - const link = document.createElement('a'); - link.href = url; - link.setAttribute('download', fileName); - document.body.appendChild(link); - link.click(); -};