Skip to content
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
14 changes: 7 additions & 7 deletions public/asset-manifest.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"core.min.js": "core.c5bd53363a.min.js",
"vendor.min.js": "vendor.7f9da8be8e.min.js",
"core.min.js": "core.3b07189932.min.js",
"vendor.min.js": "vendor.3036803e29.min.js",
"mermaid.min.js": "mermaid.f848a72d16.min.js",
"all.min.css": "all.5fbafcda4e.min.css",
"markdown.min.js": "markdown.ad7b1d71c3.min.js",
"pdf.min.js": "pdf.eaa7573247.min.js",
"notebook.min.js": "notebook.8844e2735f.min.js",
"org.min.js": "org.f4e2a3f59f.min.js",
"editor.min.js": "editor.e243722d87.min.js"
"markdown.min.js": "markdown.44f2e93820.min.js",
"pdf.min.js": "pdf.0175d53d2f.min.js",
"notebook.min.js": "notebook.c3144c18ae.min.js",
"org.min.js": "org.f61f262915.min.js",
"editor.min.js": "editor.4f142d3adc.min.js"
}
26 changes: 16 additions & 10 deletions public/script/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -649,11 +649,12 @@ export const unifiedDashboardController = function (state, http, location, promi
});
};

function waitRepoToBeReady(repoId, callback) {
function waitRepoToBeReady(repoId, callback, onError) {
http.get("/api/repo/" + repoId).then((res) => {
for (const item of state.items) {
if (item._type === "repo" && item.repoId == repoId) {
item.status = res.data.status;
item.statusMessage = res.data.statusMessage;
break;
}
}
Expand All @@ -666,8 +667,8 @@ export const unifiedDashboardController = function (state, http, location, promi
callback(res.data);
return;
}
timers.timeout(() => waitRepoToBeReady(repoId, callback), 2500);
});
timers.timeout(() => waitRepoToBeReady(repoId, callback, onError), 2500);
}, onError);
Comment thread
tdurieux marked this conversation as resolved.
}

const labelOf = (t) =>
Expand Down Expand Up @@ -716,26 +717,31 @@ export const unifiedDashboardController = function (state, http, location, promi
body: `The ${label} ${item._id} is going to be refreshed.`,
});
state.addToast(toast);
const onError = (error) => {
toast.title = `Error during the refresh of ${item._id}.`;
toast.body = error.data?.error || error.body || "The refresh could not be completed. Please try again.";
Comment thread
tdurieux marked this conversation as resolved.
loadAll();
};
const endpoint = `${apiBaseOf(item._type)}/${item._id}/refresh`;
http.post(endpoint).then(
() => {
if (item._type === "repo") {
waitRepoToBeReady(item._id, () => {
waitRepoToBeReady(item._id, (repo) => {
if (repo.status !== "ready") {
onError({ body: repo.statusMessage || `The repository is ${repo.status}.` });
return;
}
toast.title = `${item._id} is refreshed.`;
toast.body = `The ${label} ${item._id} is refreshed.`;

});
}, onError);
} else {
toast.title = `${item._id} is refreshed.`;
toast.body = `The ${label} ${item._id} is refreshed.`;
loadAll();
}
},
(error) => {
toast.title = `Error during the refresh of ${item._id}.`;
toast.body = error.body;
loadAll();
}
onError
);
};

Expand Down
4 changes: 2 additions & 2 deletions public/script/core.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion public/script/editor.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion public/script/markdown.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion public/script/notebook.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion public/script/org.min.js

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions public/script/pdf.min.js

Large diffs are not rendered by default.

36 changes: 18 additions & 18 deletions public/script/vendor.min.js

Large diffs are not rendered by default.

18 changes: 11 additions & 7 deletions src/server/routes/repository-private.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,15 +126,19 @@ router.post(
});
if (!repo) return;

if (
repo.status == "preparing" ||
repo.status == "removing" ||
repo.status == "expiring"
)
return;

const user = await getUser(req);
isOwnerCoauthorOrAdmin(repo, user);

if (
repo.status == RepositoryStatus.PREPARING ||
repo.status == RepositoryStatus.QUEUE ||
repo.status == RepositoryStatus.DOWNLOAD ||
Comment thread
tdurieux marked this conversation as resolved.
repo.status == RepositoryStatus.REMOVING ||
Comment thread
tdurieux marked this conversation as resolved.
repo.status == RepositoryStatus.EXPIRING
) {
throw new AnonymousError("invalid_status", { httpStatus: 409 });
}

await repo.updateIfNeeded({ force: true });
res.json({ status: repo.status });
} catch (error) {
Expand Down
64 changes: 64 additions & 0 deletions test/dashboard-refresh.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
const { expect } = require("chai");
const fs = require("fs");
const vm = require("vm");
const { setImmediate } = require("timers");
const source = fs.readFileSync(require("path").join(__dirname, "../public/script/app.js"), "utf8");
const actions = source.slice(source.indexOf(" function waitRepoToBeReady("), source.indexOf(" state.itemFilter ="));

describe("dashboard refresh feedback", () => {
async function run({ statuses = [], postError, getError } = {}) {
let toast;
const item = { _type: "repo", _id: "restore-me", repoId: "restore-me", status: "removed" };
const state = { items: [item], addToast: value => { toast = value; } };
const pending = [];
let reloads = 0;
vm.runInNewContext(actions, {
state, reactive: value => value,
http: {
post: async () => { if (postError) throw postError; return {}; },
get: async () => { if (getError) throw getError; return { data: statuses.shift() }; },
},
timers: { timeout: callback => pending.push(callback) },
loadAll: () => { reloads++; },
});
state.refreshItem(item);
await new Promise(resolve => setImmediate(resolve));
while (pending.length) {
pending.shift()();
await new Promise(resolve => setImmediate(resolve));
}
return { toast, item, reloads };
}

it("reports success after preparation reaches ready", async () => {
const result = await run({ statuses: [{ status: "preparing" }, { status: "ready" }] });
expect(result.toast.title).to.equal("restore-me is refreshed.");
expect(result.item.status).to.equal("ready");
expect(result.reloads).to.equal(0);
});
for (const status of ["error", "removed", "expired"]) {
it(`reports ${status} as a failed refresh`, async () => {
const result = await run({ statuses: [{ status, statusMessage: "token_expired" }] });
expect(result.toast.title).to.equal("Error during the refresh of restore-me.");
expect(result.toast.body).to.equal("token_expired");
expect(result.item.statusMessage).to.equal("token_expired");
expect(result.reloads).to.equal(1);
});
}
it("explains a terminal status without a status message", async () => {
const result = await run({ statuses: [{ status: "expired" }] });
expect(result.toast.body).to.equal("The repository is expired.");
});
for (const stage of ["postError", "getError"]) {
it(`shows the API error from ${stage}`, async () => {
const result = await run({ [stage]: { data: { error: "not_connected" } } });
expect(result.toast.title).to.equal("Error during the refresh of restore-me.");
expect(result.toast.body).to.equal("not_connected");
});
}
it("reports a polling network failure", async () => {
const result = await run({ getError: new Error("Network failure") });
expect(result.toast.title).to.equal("Error during the refresh of restore-me.");
expect(result.toast.body).to.include("Please try again");
});
});
Loading
Loading