Skip to content
Snippets Groups Projects
Unverified Commit 9e4dedcd authored by Ryan Laurie's avatar Ryan Laurie Committed by GitHub
Browse files

Make CSV "Start Exploring" Link Actually Start Exploring (#30421)


* Return the created model ID when uploading a CSV

* fix upload complete link

* update e2e test

* update unit tests

---------

Co-authored-by: default avatarTim Macdonald <tim@tsmacdonald.com>
parent f20fab5b
No related branches found
No related tags found
No related merge requests found
...@@ -67,6 +67,13 @@ describe("CSV Uploading", { tags: ["@external", "@actions"] }, () => { ...@@ -67,6 +67,13 @@ describe("CSV Uploading", { tags: ["@external", "@actions"] }, () => {
cy.findByText(testFile.tableName); // TODO: we should humanize model names cy.findByText(testFile.tableName); // TODO: we should humanize model names
}); });
cy.findByRole("status").within(() => {
cy.findByText("Start exploring").click();
});
cy.url().should("include", `/model/4`);
cy.findByTestId("TableInteractive-root");
const tableQuery = `SELECT * FROM information_schema.tables WHERE table_name LIKE 'upload_${testFile.tableName}_%' ORDER BY table_name DESC LIMIT 1;`; const tableQuery = `SELECT * FROM information_schema.tables WHERE table_name LIKE 'upload_${testFile.tableName}_%' ORDER BY table_name DESC LIMIT 1;`;
queryWritableDB(tableQuery, dialect).then(result => { queryWritableDB(tableQuery, dialect).then(result => {
......
...@@ -27,7 +27,7 @@ export const UPLOAD_FILE_TO_COLLECTION_CLEAR = ...@@ -27,7 +27,7 @@ export const UPLOAD_FILE_TO_COLLECTION_CLEAR =
const MAX_UPLOAD_SIZE = 200 * 1024 * 1024; // 200MB const MAX_UPLOAD_SIZE = 200 * 1024 * 1024; // 200MB
const MAX_UPLOAD_STRING = "200MB"; const MAX_UPLOAD_STRING = "200MB";
const CLEAR_AFTER_MS = 5000; const CLEAR_AFTER_MS = 8000;
const uploadStart = createAction(UPLOAD_FILE_TO_COLLECTION_START); const uploadStart = createAction(UPLOAD_FILE_TO_COLLECTION_START);
const uploadEnd = createAction(UPLOAD_FILE_TO_COLLECTION_END); const uploadEnd = createAction(UPLOAD_FILE_TO_COLLECTION_END);
...@@ -76,7 +76,7 @@ export const uploadFile = createThunkAction( ...@@ -76,7 +76,7 @@ export const uploadFile = createThunkAction(
dispatch( dispatch(
uploadEnd({ uploadEnd({
id, id,
modelId: response.model_id, modelId: response,
}), }),
); );
......
...@@ -10,13 +10,13 @@ import { ...@@ -10,13 +10,13 @@ import {
const now = Date.now(); const now = Date.now();
const NOTIFICATION_DELAY = 9000;
const mockUploadCSV = (valid = true) => { const mockUploadCSV = (valid = true) => {
fetchMock.post( fetchMock.post(
"path:/api/card/from-csv", "path:/api/card/from-csv",
valid valid
? { ? "3"
model_id: 3,
}
: { : {
throws: { data: { message: "It's dead Jim" } }, throws: { data: { message: "It's dead Jim" } },
}, },
...@@ -47,7 +47,7 @@ describe("csv uploads", () => { ...@@ -47,7 +47,7 @@ describe("csv uploads", () => {
mockUploadCSV(); mockUploadCSV();
await uploadFile(file, "root")(dispatch); await uploadFile(file, "root")(dispatch);
jest.advanceTimersByTime(6000); jest.advanceTimersByTime(NOTIFICATION_DELAY);
expect(dispatch).toHaveBeenCalledWith({ expect(dispatch).toHaveBeenCalledWith({
type: UPLOAD_FILE_TO_COLLECTION_START, type: UPLOAD_FILE_TO_COLLECTION_START,
...@@ -78,7 +78,7 @@ describe("csv uploads", () => { ...@@ -78,7 +78,7 @@ describe("csv uploads", () => {
mockUploadCSV(false); mockUploadCSV(false);
await uploadFile(file, "root")(dispatch); await uploadFile(file, "root")(dispatch);
jest.advanceTimersByTime(6000); jest.advanceTimersByTime(NOTIFICATION_DELAY);
expect(dispatch).toHaveBeenCalledWith({ expect(dispatch).toHaveBeenCalledWith({
type: UPLOAD_FILE_TO_COLLECTION_START, type: UPLOAD_FILE_TO_COLLECTION_START,
...@@ -109,7 +109,7 @@ describe("csv uploads", () => { ...@@ -109,7 +109,7 @@ describe("csv uploads", () => {
const bigFile = new File([""], "test.csv"); const bigFile = new File([""], "test.csv");
Object.defineProperty(bigFile, "size", { value: 200 * 1024 * 1024 + 1 }); Object.defineProperty(bigFile, "size", { value: 200 * 1024 * 1024 + 1 });
await uploadFile(bigFile, "root")(dispatch); await uploadFile(bigFile, "root")(dispatch);
jest.advanceTimersByTime(6000); jest.advanceTimersByTime(NOTIFICATION_DELAY);
expect(dispatch).toHaveBeenCalledWith({ expect(dispatch).toHaveBeenCalledWith({
type: UPLOAD_FILE_TO_COLLECTION_START, type: UPLOAD_FILE_TO_COLLECTION_START,
......
...@@ -70,7 +70,7 @@ describe("FileUploadStatus", () => { ...@@ -70,7 +70,7 @@ describe("FileUploadStatus", () => {
}); });
it("Should show a start exploring link on completion", async () => { it("Should show a start exploring link on completion", async () => {
fetchMock.post("path:/api/card/from-csv", { model_id: 3 }, { delay: 1000 }); fetchMock.post("path:/api/card/from-csv", "3", { delay: 1000 });
renderWithProviders( renderWithProviders(
<Route <Route
......
...@@ -27,7 +27,7 @@ const FileUploadLarge = ({ ...@@ -27,7 +27,7 @@ const FileUploadLarge = ({
title: getTitle(uploads, collection), title: getTitle(uploads, collection),
items: uploads.map(upload => ({ items: uploads.map(upload => ({
id: upload.id, id: upload.id,
title: upload.name, title: getName(upload),
icon: "model", icon: "model",
description: getDescription(upload), description: getDescription(upload),
isInProgress: isUploadInProgress(upload), isInProgress: isUploadInProgress(upload),
...@@ -39,6 +39,13 @@ const FileUploadLarge = ({ ...@@ -39,6 +39,13 @@ const FileUploadLarge = ({
return <StatusLarge status={status} isActive={isActive} />; return <StatusLarge status={status} isActive={isActive} />;
}; };
const getName = (upload: FileUpload) => {
if (upload.status === "complete") {
return <Link to={`/model/${upload.modelId}`}>{upload.name}</Link>;
}
return upload.name;
};
const getTitle = (uploads: FileUpload[], collection: Collection) => { const getTitle = (uploads: FileUpload[], collection: Collection) => {
const isDone = uploads.every(isUploadCompleted); const isDone = uploads.every(isUploadCompleted);
const isError = uploads.some(isUploadAborted); const isError = uploads.some(isUploadAborted);
......
...@@ -24,7 +24,7 @@ type Status = { ...@@ -24,7 +24,7 @@ type Status = {
type StatusItem = { type StatusItem = {
id?: number; id?: number;
title: string; title: string | JSX.Element;
icon: string; icon: string;
description?: string | JSX.Element; description?: string | JSX.Element;
isInProgress: boolean; isInProgress: boolean;
......
...@@ -978,7 +978,8 @@ saved later when it is ready." ...@@ -978,7 +978,8 @@ saved later when it is ready."
(defn upload-csv! (defn upload-csv!
"Main entry point for CSV uploading. Coordinates detecting the schema, inserting it into an appropriate database, "Main entry point for CSV uploading. Coordinates detecting the schema, inserting it into an appropriate database,
syncing and scanning the new data, and creating an appropriate model. May throw validation or DB errors." syncing and scanning the new data, and creating an appropriate model which is then returned. May throw validation or
DB errors."
[collection-id filename csv-file] [collection-id filename csv-file]
(when (not (public-settings/uploads-enabled)) (when (not (public-settings/uploads-enabled))
(throw (Exception. "Uploads are not enabled."))) (throw (Exception. "Uploads are not enabled.")))
...@@ -1012,12 +1013,13 @@ saved later when it is ready." ...@@ -1012,12 +1013,13 @@ saved later when it is ready."
:visualization_settings {}}))) :visualization_settings {}})))
(api/defendpoint ^:multipart POST "/from-csv" (api/defendpoint ^:multipart POST "/from-csv"
"Create a table and model populated with the values from the attached CSV." "Create a table and model populated with the values from the attached CSV. Returns the model ID if successful."
[:as {raw-params :params}] [:as {raw-params :params}]
;; parse-long returns nil with "root", which is what we want anyway ;; parse-long returns nil with "root", which is what we want anyway
(upload-csv! (parse-long (get raw-params "collection_id")) (let [model-id (:id (upload-csv! (parse-long (get raw-params "collection_id"))
(get-in raw-params ["file" :filename]) (get-in raw-params ["file" :filename])
(get-in raw-params ["file" :tempfile])) (get-in raw-params ["file" :tempfile])))]
{:status 200}) {:status 200
:body model-id}))
(api/define-routes) (api/define-routes)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment