Skip to content

Commit

Permalink
[Ref] Move file download to AsyncResourceActions.ts
Browse files Browse the repository at this point in the history
  • Loading branch information
ledsoft committed Oct 14, 2024
1 parent c01fa90 commit bedcc29
Show file tree
Hide file tree
Showing 6 changed files with 128 additions and 99 deletions.
40 changes: 0 additions & 40 deletions src/action/AsyncActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1130,46 +1130,6 @@ export function loadLatestTextAnalysisRecord(resourceIri: IRI) {
};
}

/**
* Downloads the content of a file with the specified IRI (assuming it is stored on the server).
* @param fileIri File identifier
* @param options File export options
*/
export function exportFileContent(
fileIri: IRI,
options: {
at?: string;
withoutUnconfirmedOccurrences?: boolean;
} = {}
) {
const action = {
type: ActionType.EXPORT_FILE_CONTENT,
};
return (dispatch: ThunkDispatch) => {
dispatch(asyncActionRequest(action));
const url =
Constants.API_PREFIX + "/resources/" + fileIri.fragment + "/content";
return Ajax.getRaw(
url,
param("namespace", fileIri.namespace)
.param("attachment", "true")
.param("at", options.at)
.param(
"withoutUnconfirmedOccurrences",
options.withoutUnconfirmedOccurrences?.toString()
)
.responseType("arraybuffer")
)
.then((resp: AxiosResponse) => {
const fileName = fileIri.fragment;
const mimeType = resp.headers["content-type"];
Utils.fileDownload(resp.data, fileName, mimeType);
return dispatch(asyncActionSuccess(action));
})
.catch((error: ErrorData) => dispatch(asyncActionFailure(action, error)));
};
}

export function loadHistory(asset: Asset) {
const assetIri = VocabularyUtils.create(asset.iri);
const historyConf = resolveHistoryLoadingParams(asset, assetIri);
Expand Down
42 changes: 42 additions & 0 deletions src/action/AsyncResourceActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import JsonLdUtils from "../util/JsonLdUtils";
import File, { FileData } from "../model/File";
import { CONTEXT as DOCUMENT_CONTEXT } from "../model/Document";
import { ErrorData } from "../model/ErrorInfo";
import { AxiosResponse } from "axios";
import Utils from "../util/Utils";

export function loadFileMetadata(fileIri: IRI) {
const action = { type: ActionType.LOAD_FILE_METADATA };
Expand All @@ -37,3 +39,43 @@ export function loadFileMetadata(fileIri: IRI) {
});
};
}

/**
* Downloads the content of a file with the specified IRI (assuming it is stored on the server).
* @param fileIri File identifier
* @param options File export options
*/
export function exportFileContent(
fileIri: IRI,
options: {
at?: string;
withoutUnconfirmedOccurrences?: boolean;
} = {}
) {
const action = {
type: ActionType.EXPORT_FILE_CONTENT,
};
return (dispatch: ThunkDispatch) => {
dispatch(asyncActionRequest(action));
const url =
Constants.API_PREFIX + "/resources/" + fileIri.fragment + "/content";
return Ajax.getRaw(
url,
param("namespace", fileIri.namespace)
.param("attachment", "true")
.param("at", options.at)
.param(
"withoutUnconfirmedOccurrences",
options.withoutUnconfirmedOccurrences?.toString()
)
.responseType("arraybuffer")
)
.then((resp: AxiosResponse) => {
const fileName = fileIri.fragment;
const mimeType = resp.headers["content-type"];
Utils.fileDownload(resp.data, fileName, mimeType);
return dispatch(asyncActionSuccess(action));
})
.catch((error: ErrorData) => dispatch(asyncActionFailure(action, error)));
};
}
57 changes: 0 additions & 57 deletions src/action/__tests__/AsyncActions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
createProperty,
createVocabulary,
executeFileTextAnalysis,
exportFileContent,
getContentType,
getLabel,
getProperties,
Expand Down Expand Up @@ -56,7 +55,6 @@ import RdfsResource, {
} from "../../model/RdfsResource";
import TermItState from "../../model/TermItState";
import Resource from "../../model/Resource";
import Utils from "../../util/Utils";
import AsyncActionStatus from "../AsyncActionStatus";
import fileContent from "../../rest-mock/file";
import TermItFile from "../../model/File";
Expand Down Expand Up @@ -1556,61 +1554,6 @@ describe("Async actions", () => {
});
});

describe("exportFileContent", () => {
const fileName = "test-file.html";
const fileIri = VocabularyUtils.create(
"http://onto.fel.cvut.cz/ontologies/termit/resources/" + fileName
);

it("sends request asking for content as attachment", () => {
Ajax.getRaw = jest.fn().mockImplementation(() =>
Promise.resolve({
data: "test",
headers: {
"content-type": "text/html",
"content-disposition": 'attachment; filename="' + fileName + '"',
},
})
);
Utils.fileDownload = jest.fn();
return Promise.resolve(
(store.dispatch as ThunkDispatch)(exportFileContent(fileIri))
).then(() => {
expect(Ajax.getRaw).toHaveBeenCalled();
const url = (Ajax.getRaw as jest.Mock).mock.calls[0][0];
expect(url).toEqual(
Constants.API_PREFIX + "/resources/" + fileName + "/content"
);
const config = (Ajax.getRaw as jest.Mock).mock.calls[0][1];
expect(config.getParams().attachment).toEqual("true");
expect(config.getParams().namespace).toEqual(fileIri.namespace);
});
});

it("stores response attachment", () => {
const data = '<html lang="en">test</html>';
Ajax.getRaw = jest.fn().mockImplementation(() =>
Promise.resolve({
data,
headers: {
"content-type": "text/html",
"content-disposition": 'attachment; filename="' + fileName + '"',
},
})
);
Utils.fileDownload = jest.fn();
return Promise.resolve(
(store.dispatch as ThunkDispatch)(exportFileContent(fileIri))
).then(() => {
expect(Utils.fileDownload).toHaveBeenCalled();
const args = (Utils.fileDownload as jest.Mock).mock.calls[0];
expect(args[0]).toEqual(data);
expect(args[1]).toEqual(fileName);
expect(args[2]).toEqual("text/html");
});
});
});

describe("loadImportedVocabularies", () => {
it("loads imported vocabularies for the specified vocabulary IRI", () => {
const imports = [Generator.generateUri(), Generator.generateUri()];
Expand Down
84 changes: 84 additions & 0 deletions src/action/__tests__/AsyncResourceActions.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import configureMockStore, { MockStoreEnhanced } from "redux-mock-store";
import TermItState from "../../model/TermItState";
import thunk from "redux-thunk";
import VocabularyUtils from "../../util/VocabularyUtils";
import Utils from "../../util/Utils";
import { ThunkDispatch } from "../../util/Types";
import Constants from "../../util/Constants";
import Ajax from "../../util/Ajax";
import { exportFileContent } from "../AsyncResourceActions";

jest.mock("../../util/Routing");
jest.mock("../../util/Ajax", () => {
const originalModule = jest.requireActual("../../util/Ajax");
return {
...originalModule,
default: jest.fn(),
};
});

const mockStore = configureMockStore<TermItState>([thunk]);

describe("AsyncResourceActions", () => {
let store: MockStoreEnhanced<TermItState>;

beforeEach(() => {
jest.clearAllMocks();
store = mockStore(new TermItState());
});

describe("exportFileContent", () => {
const fileName = "test-file.html";
const fileIri = VocabularyUtils.create(
"http://onto.fel.cvut.cz/ontologies/termit/resources/" + fileName
);

it("sends request asking for content as attachment", () => {
Ajax.getRaw = jest.fn().mockImplementation(() =>
Promise.resolve({
data: "test",
headers: {
"content-type": "text/html",
"content-disposition": 'attachment; filename="' + fileName + '"',
},
})
);
Utils.fileDownload = jest.fn();
return Promise.resolve(
(store.dispatch as ThunkDispatch)(exportFileContent(fileIri))
).then(() => {
expect(Ajax.getRaw).toHaveBeenCalled();
const url = (Ajax.getRaw as jest.Mock).mock.calls[0][0];
expect(url).toEqual(
Constants.API_PREFIX + "/resources/" + fileName + "/content"
);
const config = (Ajax.getRaw as jest.Mock).mock.calls[0][1];
expect(config.getParams().attachment).toEqual("true");
expect(config.getParams().namespace).toEqual(fileIri.namespace);
});
});

it("stores response attachment", () => {
const data = '<html lang="en">test</html>';
Ajax.getRaw = jest.fn().mockImplementation(() =>
Promise.resolve({
data,
headers: {
"content-type": "text/html",
"content-disposition": 'attachment; filename="' + fileName + '"',
},
})
);
Utils.fileDownload = jest.fn();
return Promise.resolve(
(store.dispatch as ThunkDispatch)(exportFileContent(fileIri))
).then(() => {
expect(Utils.fileDownload).toHaveBeenCalled();
const args = (Utils.fileDownload as jest.Mock).mock.calls[0];
expect(args[0]).toEqual(data);
expect(args[1]).toEqual(fileName);
expect(args[2]).toEqual("text/html");
});
});
});
});
2 changes: 1 addition & 1 deletion src/component/annotator/AnnotatorDownloadActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ import {
import { FaCloudDownloadAlt } from "react-icons/fa";
import { useDispatch } from "react-redux";
import { ThunkDispatch } from "../../util/Types";
import { exportFileContent } from "../../action/AsyncActions";
import { trackPromise } from "react-promise-tracker";
import { DateTime } from "luxon";
import Constants from "../../util/Constants";
import { exportFileContent } from "../../action/AsyncResourceActions";

const AnnotatorDownloadActions: React.FC<{ fileIri: IRI }> = ({ fileIri }) => {
const { i18n } = useI18n();
Expand Down
2 changes: 1 addition & 1 deletion src/component/resource/document/DocumentFiles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import { ThunkDispatch } from "../../../util/Types";
import { useDispatch } from "react-redux";
import {
createFileInDocument,
exportFileContent,
removeFileFromDocument,
updateResource,
uploadFileContent,
} from "../../../action/AsyncActions";
import { exportFileContent } from "../../../action/AsyncResourceActions";
import VocabularyUtils from "../../../util/VocabularyUtils";
import Files from "./Files";
import NotificationType from "../../../model/NotificationType";
Expand Down

0 comments on commit bedcc29

Please sign in to comment.