Skip to content

Commit 83eee02

Browse files
mdonadonitiborsimko
authored andcommitted
fix(ui): fix workflow sharing interface after UI changes (reanahub#375)
1 parent 089853f commit 83eee02

13 files changed

+441
-682
lines changed

reana-ui/src/actions.js

+34-109
Original file line numberDiff line numberDiff line change
@@ -16,25 +16,28 @@ import client, {
1616
INTERACTIVE_SESSIONS_OPEN_URL,
1717
USER_INFO_URL,
1818
USER_SIGNOUT_URL,
19+
USERS_SHARED_WITH_YOU_URL,
20+
USERS_YOU_SHARED_WITH_URL,
1921
WORKFLOW_FILES_URL,
2022
WORKFLOW_LOGS_URL,
2123
WORKFLOW_RETENTION_RULES_URL,
2224
WORKFLOW_SET_STATUS_URL,
25+
WORKFLOW_SHARE_STATUS_URL,
2326
WORKFLOW_SPECIFICATION_URL,
2427
} from "~/client";
25-
import {
26-
parseWorkflows,
27-
parseWorkflowRetentionRules,
28-
parseLogs,
29-
parseFiles,
30-
formatSearch,
31-
} from "~/util";
3228
import {
3329
getConfig,
3430
getWorkflow,
3531
getWorkflowLogs,
3632
getWorkflowSpecification,
3733
} from "~/selectors";
34+
import {
35+
formatSearch,
36+
parseFiles,
37+
parseLogs,
38+
parseWorkflowRetentionRules,
39+
parseWorkflows,
40+
} from "~/util";
3841

3942
export const ERROR = "Error";
4043
export const NOTIFICATION = "Notification";
@@ -96,26 +99,11 @@ export const WORKFLOW_SHARE_STATUS_FETCH = "Fetch workflow share status";
9699
export const WORKFLOW_SHARE_STATUS_RECEIVED = "Workflow share status received";
97100
export const WORKFLOW_SHARE_STATUS_FETCH_ERROR =
98101
"Fetch workflow share status error";
99-
export const WORKFLOW_SHARE_INIT = "Initialise workflow sharing";
100-
export const WORKFLOW_SHARED_SUCCESSFULLY = "Workflow shared successfully";
101-
export const WORKFLOW_SHARED_ERROR = "Workflow shared error";
102-
export const WORKFLOW_SHARE_FINISH = "Finish workflow sharing";
103-
export const WORKFLOW_UNSHARE_INIT = "Initialise workflow unsharing";
104-
export const WORKFLOW_UNSHARED = "Workflow unshared";
105-
export const WORKFLOW_UNSHARE_ERROR = "Workflow unshare error";
106-
107-
export const USERS_SHARED_WITH_YOU_FETCH =
108-
"Fetch users who shared workflows with you";
102+
109103
export const USERS_SHARED_WITH_YOU_RECEIVED =
110104
"Users who shared workflows with you received";
111-
export const USERS_SHARED_WITH_YOU_FETCH_ERROR =
112-
"Fetch users who shared workflows with you error";
113-
export const USERS_YOU_SHARED_WITH_FETCH =
114-
"Fetch users you shared workflows with";
115105
export const USERS_YOU_SHARED_WITH_RECEIVED =
116106
"Users you shared workflows with received";
117-
export const USERS_YOU_SHARED_WITH_FETCH_ERROR =
118-
"Fetch users you shared workflows with error";
119107

120108
export function errorActionCreator(error, name) {
121109
const { status, data } = error?.response;
@@ -288,12 +276,12 @@ export function fetchWorkflows({
288276
pagination,
289277
search,
290278
status,
291-
ownedBy,
279+
sharedBy,
292280
sharedWith,
293281
sort,
294282
showLoader = true,
295283
workflowIdOrName,
296-
includeShared = false,
284+
shared = false,
297285
}) {
298286
return async (dispatch) => {
299287
if (showLoader) {
@@ -305,20 +293,20 @@ export function fetchWorkflows({
305293
pagination,
306294
search: formatSearch(search),
307295
status,
308-
ownedBy,
296+
sharedBy,
309297
sharedWith,
310298
sort,
311299
workflowIdOrName,
312-
includeShared,
300+
shared,
313301
})
314-
.then((resp) => {
302+
.then((resp) =>
315303
dispatch({
316304
type: WORKFLOWS_RECEIVED,
317305
workflows: parseWorkflows(resp.data.items),
318306
total: resp.data.total,
319307
userHasWorkflows: resp.data.user_has_workflows,
320-
});
321-
})
308+
}),
309+
)
322310
.catch((err) => {
323311
dispatch(errorActionCreator(err, USER_INFO_URL));
324312
dispatch({ type: WORKFLOWS_FETCH_ERROR });
@@ -338,7 +326,7 @@ export function fetchWorkflow(id, { refetch = false, showLoader = true } = {}) {
338326
fetchWorkflows({
339327
workflowIdOrName: id,
340328
showLoader,
341-
includeShared: true,
329+
shared: true,
342330
}),
343331
);
344332
}
@@ -567,38 +555,34 @@ export function closeShareWorkflowModal() {
567555

568556
export function fetchUsersSharedWithYou() {
569557
return async (dispatch) => {
570-
dispatch({ type: USERS_SHARED_WITH_YOU_FETCH });
571-
572558
return await client
573559
.getUsersSharedWithYou()
574560
.then((resp) => {
575561
dispatch({
576562
type: USERS_SHARED_WITH_YOU_RECEIVED,
577-
users_shared_with_you: resp.data.users_shared_with_you,
563+
usersSharedYouWith: resp.data.users_shared_with_you,
578564
});
579565
return resp;
580566
})
581567
.catch((err) => {
582-
dispatch(errorActionCreator(err, USERS_SHARED_WITH_YOU_FETCH_ERROR));
568+
dispatch(errorActionCreator(err, USERS_SHARED_WITH_YOU_URL));
583569
});
584570
};
585571
}
586572

587573
export function fetchUsersYouSharedWith() {
588574
return async (dispatch) => {
589-
dispatch({ type: USERS_YOU_SHARED_WITH_FETCH });
590-
591575
return await client
592576
.getUsersYouSharedWith()
593577
.then((resp) => {
594578
dispatch({
595579
type: USERS_YOU_SHARED_WITH_RECEIVED,
596-
users_you_shared_with: resp.data.users_you_shared_with,
580+
usersYouSharedWith: resp.data.users_you_shared_with,
597581
});
598582
return resp;
599583
})
600584
.catch((err) => {
601-
dispatch(errorActionCreator(err, USERS_YOU_SHARED_WITH_FETCH_ERROR));
585+
dispatch(errorActionCreator(err, USERS_YOU_SHARED_WITH_URL));
602586
});
603587
};
604588
}
@@ -609,83 +593,24 @@ export function fetchWorkflowShareStatus(id) {
609593
return await client
610594
.getWorkflowShareStatus(id)
611595
.then((resp) => {
596+
// convert to camel-case
597+
const sharedWith = [];
598+
for (const share of resp.data.shared_with) {
599+
sharedWith.push({
600+
userEmail: share.user_email,
601+
validUntil: share.valid_until,
602+
});
603+
}
612604
dispatch({
613605
type: WORKFLOW_SHARE_STATUS_RECEIVED,
614606
id,
615-
workflow_share_status: resp.data.shared_with,
607+
sharedWith,
616608
});
617609
return resp;
618610
})
619611
.catch((err) => {
620-
dispatch(errorActionCreator(err, WORKFLOW_SHARE_STATUS_FETCH_ERROR));
621-
});
622-
};
623-
}
624-
625-
export function shareWorkflow(
626-
id,
627-
user_id,
628-
user_emails_to_share_with,
629-
valid_until,
630-
) {
631-
return async (dispatch) => {
632-
dispatch({ type: WORKFLOW_SHARE_INIT });
633-
634-
const users_shared_with = [];
635-
const users_not_shared_with = [];
636-
637-
for (const user_email_to_share_with of user_emails_to_share_with) {
638-
await client
639-
.shareWorkflow(id, {
640-
user_id,
641-
user_email_to_share_with,
642-
valid_until,
643-
})
644-
.then(() => {
645-
users_shared_with.push(user_email_to_share_with);
646-
})
647-
.catch((err) => {
648-
const error_message = err.response.data.message;
649-
users_not_shared_with.push({
650-
user_email_to_share_with,
651-
error_message,
652-
});
653-
});
654-
655-
if (users_shared_with.length > 0) {
656-
dispatch({
657-
type: WORKFLOW_SHARED_SUCCESSFULLY,
658-
users_shared_with,
659-
});
660-
}
661-
662-
if (users_not_shared_with.length > 0) {
663-
dispatch({
664-
type: WORKFLOW_SHARED_ERROR,
665-
users_not_shared_with,
666-
});
667-
}
668-
}
669-
670-
dispatch({ type: WORKFLOW_SHARE_FINISH });
671-
};
672-
}
673-
674-
export function unshareWorkflow(id, user_id, user_email_to_unshare_with) {
675-
return async (dispatch) => {
676-
dispatch({ type: WORKFLOW_UNSHARE_INIT });
677-
678-
return await client
679-
.unshareWorkflow(id, {
680-
user_id,
681-
user_email_to_unshare_with,
682-
})
683-
.then(() => {
684-
dispatch({ type: WORKFLOW_UNSHARED, user_email_to_unshare_with });
685-
})
686-
.catch((err) => {
687-
const error_message = err.response.data.message;
688-
dispatch({ type: WORKFLOW_UNSHARE_ERROR, error_message });
612+
dispatch({ type: WORKFLOW_SHARE_STATUS_FETCH_ERROR });
613+
dispatch(errorActionCreator(err, WORKFLOW_SHARE_STATUS_URL(id)));
689614
});
690615
};
691616
}

reana-ui/src/client.js

+10-15
Original file line numberDiff line numberDiff line change
@@ -127,28 +127,20 @@ class Client {
127127
pagination,
128128
search,
129129
status,
130-
ownedBy,
130+
sharedBy,
131131
sharedWith,
132132
sort,
133133
workflowIdOrName,
134-
includeShared = false,
134+
shared,
135135
} = {}) {
136-
let shared = false;
137-
if (ownedBy === "anybody" || includeShared) {
138-
ownedBy = undefined;
139-
shared = true;
140-
} else if (ownedBy === "you") {
141-
ownedBy = undefined;
142-
}
143-
144136
return this._request(
145137
WORKFLOWS_URL({
146138
...(pagination ?? {}),
147139
workflow_id_or_name: workflowIdOrName,
148140
search,
149141
status,
150142
shared,
151-
shared_by: ownedBy,
143+
shared_by: sharedBy,
152144
shared_with: sharedWith,
153145
sort,
154146
}),
@@ -238,16 +230,19 @@ class Client {
238230
return this._request(WORKFLOW_SHARE_STATUS_URL(id));
239231
}
240232

241-
shareWorkflow(id, data) {
233+
shareWorkflow(id, { userEmailToShareWith, validUntil }) {
242234
return this._request(WORKFLOW_SHARE_URL(id), {
243-
data,
235+
data: {
236+
user_email_to_share_with: userEmailToShareWith,
237+
valid_until: validUntil,
238+
},
244239
method: "post",
245240
});
246241
}
247242

248-
unshareWorkflow(id, data) {
243+
unshareWorkflow(id, { userEmailToUnshareWith }) {
249244
return this._request(WORKFLOW_UNSHARE_URL(id), {
250-
data,
245+
data: { user_email_to_unshare_with: userEmailToUnshareWith },
251246
method: "post",
252247
});
253248
}

reana-ui/src/components/WorkflowActionsPopup.js

+9-24
Original file line numberDiff line numberDiff line change
@@ -8,34 +8,32 @@
88
under the terms of the MIT License; see LICENSE file for more details.
99
*/
1010

11-
import { useState } from "react";
1211
import PropTypes from "prop-types";
12+
import { useState } from "react";
13+
import { useDispatch, useSelector } from "react-redux";
1314
import { Icon, Menu, Popup } from "semantic-ui-react";
14-
import { useDispatch } from "react-redux";
1515

16-
import { workflowShape } from "~/props";
1716
import {
18-
deleteWorkflow,
1917
closeInteractiveSession,
18+
deleteWorkflow,
2019
openDeleteWorkflowModal,
21-
openStopWorkflowModal,
2220
openInteractiveSessionModal,
2321
openShareWorkflowModal,
22+
openStopWorkflowModal,
2423
} from "~/actions";
24+
import { workflowShape } from "~/props";
25+
import { getUserEmail } from "~/selectors";
2526

2627
import { JupyterNotebookIcon } from "~/components";
2728

2829
import styles from "./WorkflowActionsPopup.module.scss";
2930

3031
const JupyterIcon = <JupyterNotebookIcon className={styles["jupyter-icon"]} />;
3132

32-
export default function WorkflowActionsPopup({
33-
workflow,
34-
className,
35-
insideClickableElement,
36-
}) {
33+
export default function WorkflowActionsPopup({ workflow, className }) {
3734
const dispatch = useDispatch();
3835
const [open, setOpen] = useState(false);
36+
const userEmail = useSelector(getUserEmail);
3937
const { id, size, status, session_status: sessionStatus } = workflow;
4038
const isDeleted = status === "deleted";
4139
const isDeletedUsingWorkspace = isDeleted && size.raw > 0;
@@ -63,7 +61,6 @@ export default function WorkflowActionsPopup({
6361
onClick: (e) => {
6462
dispatch(openShareWorkflowModal(workflow));
6563
setOpen(false);
66-
e.stopPropagation();
6764
},
6865
});
6966

@@ -115,20 +112,9 @@ export default function WorkflowActionsPopup({
115112
});
116113
}
117114

118-
if (workflow.owner_email !== "-") {
119-
return (
120-
<div
121-
className={className || styles.container}
122-
style={
123-
insideClickableElement ? { cursor: "pointer" } : { cursor: "default" }
124-
}
125-
/>
126-
);
127-
}
128-
129115
return (
130116
<div className={className}>
131-
{menuItems.length > 0 && (
117+
{workflow.ownerEmail === userEmail && menuItems.length > 0 && (
132118
<Popup
133119
basic
134120
trigger={
@@ -160,5 +146,4 @@ WorkflowActionsPopup.defaultProps = {
160146
WorkflowActionsPopup.propTypes = {
161147
workflow: workflowShape.isRequired,
162148
className: PropTypes.string,
163-
insideClickableElement: PropTypes.bool,
164149
};

0 commit comments

Comments
 (0)