Skip to content

Commit dc8a90f

Browse files
committed
NXT-4436: Add error handling to exportSpaceItem
NXT-4436 (Workflow export fails silently if LOCAL mountpoint is deleted or deactivated)
1 parent 84514ec commit dc8a90f

File tree

5 files changed

+50
-11
lines changed

5 files changed

+50
-11
lines changed

org.knime.ui.java/src/eclipse/org/knime/ui/java/api/ExportAPI.java

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,17 @@
4848
*/
4949
package org.knime.ui.java.api;
5050

51+
import java.util.Objects;
52+
53+
import org.apache.commons.lang3.exception.ExceptionUtils;
5154
import org.eclipse.jface.viewers.StructuredSelection;
5255
import org.eclipse.jface.window.Window;
5356
import org.eclipse.jface.wizard.WizardDialog;
5457
import org.eclipse.ui.PlatformUI;
5558
import org.knime.core.node.workflow.NodeTimer;
59+
import org.knime.core.workbench.preferences.MountPointsPreferencesUtil;
5660
import org.knime.gateway.api.service.GatewayException;
61+
import org.knime.gateway.api.webui.service.util.ServiceExceptions.ServiceCallException;
5762
import org.knime.gateway.impl.webui.spaces.Space;
5863
import org.knime.workbench.explorer.localworkspace.LocalWorkspaceFileStore;
5964
import org.knime.workbench.explorer.view.ContentObject;
@@ -92,15 +97,31 @@ static boolean exportSpaceItem(final String spaceProviderId, final String spaceI
9297
* @param itemId id of the item to export
9398
*
9499
* @return true if the legacy workbench dialog has not been exited via "Cancel"
100+
* @throws ServiceCallException if the export fails
95101
*/
96-
static boolean openExportWizard(final Space space, final String itemId) {
102+
static boolean openExportWizard(final Space space, final String itemId) throws ServiceCallException {
97103
final var workbench = PlatformUI.getWorkbench();
98104
final var shell = workbench.getModalDialogShellProvider().getShell();
99105
final var itemUri = space.toKnimeUrl(itemId);
100106
return workbench.getDisplay().syncCall(() -> {
101107
final var exportWizard = new WorkflowExportWizard();
102-
final var fileStore = new LocalWorkspaceFileStore(itemUri.getHost(), itemUri.getPath());
108+
final var mountId = itemUri.getHost();
109+
final var fileStore = new LocalWorkspaceFileStore(mountId, itemUri.getPath());
103110
final var item = ContentObject.forFile(fileStore);
111+
if (item == null) {
112+
final var mountPointPrefs = MountPointsPreferencesUtil.loadSortedMountSettingsFromPreferences(false);
113+
final var isDeactivated =
114+
mountPointPrefs.stream().anyMatch(p -> Objects.equals(mountId, p.mountID()) && !p.isActive());
115+
final var serviceCallEx = ServiceCallException.builder() //
116+
.withTitle("The LOCAL mountpoint has been " + (isDeactivated ? "deactivated" : "deleted")
117+
+ ", disabling the export.") //
118+
.withDetails( //
119+
(isDeactivated ? "Reactivate it" : "Readd the 'Local Workspace'") + " in the preferences.", //
120+
"The setting can be found under Preferences → KNIME → KNIME Explorer.") //
121+
.canCopy(false) //
122+
.build();
123+
throw ExceptionUtils.asRuntimeException(serviceCallEx); // thrown out of the `syncCall()` invocation
124+
}
104125
exportWizard.init(workbench, new StructuredSelection(item));
105126
final var dialog = new WizardDialog(shell, exportWizard);
106127
dialog.create();

org.knime.ui.js/src/composables/useSpaceExplorerActions/useSpaceExplorerActions.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -192,11 +192,15 @@ export const useSpaceExplorerActions = (
192192
disabled: doesSelectionContainFile.value || isSelectionMultiple.value,
193193
metadata: {
194194
id: "export",
195-
handler: () => {
196-
exportSpaceItem({
197-
projectId: projectId.value,
198-
itemId: selectedItemIds.value[0],
199-
});
195+
handler: async () => {
196+
try {
197+
await exportSpaceItem({
198+
projectId: projectId.value,
199+
itemId: selectedItemIds.value[0],
200+
});
201+
} catch (error) {
202+
toastPresets.spaces.crud.exportItemFailed({ error });
203+
}
200204
},
201205
},
202206
}) satisfies MenuItemWithHandler,

org.knime.ui.js/src/shortcuts/__tests__/workflowShortcuts.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ describe("workflowShortcuts", () => {
163163
it("executes export action", () => {
164164
const { spaceOperationsStore, applicationStore } = createStore();
165165

166+
spaceOperationsStore.exportSpaceItem.mockResolvedValue(undefined);
166167
workflowShortcuts.export.execute(mockShortcutContext());
167168
expect(spaceOperationsStore.exportSpaceItem).toHaveBeenCalledWith({
168169
projectId: applicationStore.activeProjectId,

org.knime.ui.js/src/shortcuts/workflowShortcuts/generalShortcuts.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { useClipboardInteractionsStore } from "@/store/workflow/clipboardInterac
2020
import { useDesktopInteractionsStore } from "@/store/workflow/desktopInteractions";
2121
import { useMovingStore } from "@/store/workflow/moving";
2222
import { useWorkflowStore } from "@/store/workflow/workflow";
23+
import { getToastPresets } from "@/toastPresets";
2324
import { getKanvasDomElement } from "@/util/getKanvasDomElement";
2425
import type { UnionToShortcutRegistry } from "../types";
2526

@@ -244,10 +245,15 @@ const generalWorkflowShortcuts: GeneralNodeWorkflowShortcuts = {
244245
execute: () => {
245246
const { activeProjectId, activeProjectOrigin } = useApplicationStore();
246247

247-
useSpaceOperationsStore().exportSpaceItem({
248-
projectId: activeProjectId!,
249-
itemId: activeProjectOrigin!.itemId,
250-
});
248+
useSpaceOperationsStore()
249+
.exportSpaceItem({
250+
projectId: activeProjectId!,
251+
itemId: activeProjectOrigin!.itemId,
252+
})
253+
.catch((error) => {
254+
const { toastPresets } = getToastPresets();
255+
toastPresets.spaces.crud.exportItemFailed({ error });
256+
});
251257
},
252258
condition: () => {
253259
const { activeProjectId, activeProjectOrigin } = useApplicationStore();

org.knime.ui.js/src/toastPresets/spaces.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ type SpacesCrudToastPresets = {
3737
renameItemFailed: ToastPresetErrorHandler<{ newName: string }>;
3838

3939
createFolderFailed: ToastPresetErrorHandler;
40+
exportItemFailed: ToastPresetErrorHandler;
4041
};
4142

4243
type SpacesRevealToastPresets = {
@@ -125,6 +126,12 @@ export const getPresets = (
125126
headline: "Create folder failed",
126127
message: "Error while creating folder.",
127128
}),
129+
exportItemFailed: ({ error }) =>
130+
defaultAPIErrorHandler($toast, error, {
131+
type: "error",
132+
headline: "Export failed",
133+
message: "Error while exporting item.",
134+
}),
128135
moveItemsFailed: ({ error }) =>
129136
defaultAPIErrorHandler($toast, error, {
130137
type: "error",

0 commit comments

Comments
 (0)