diff --git a/reana-ui/src/actions.js b/reana-ui/src/actions.js index 14af4e6..33e2112 100644 --- a/reana-ui/src/actions.js +++ b/reana-ui/src/actions.js @@ -16,25 +16,28 @@ import client, { INTERACTIVE_SESSIONS_OPEN_URL, USER_INFO_URL, USER_SIGNOUT_URL, + USERS_SHARED_WITH_YOU_URL, + USERS_YOU_SHARED_WITH_URL, WORKFLOW_FILES_URL, WORKFLOW_LOGS_URL, WORKFLOW_RETENTION_RULES_URL, WORKFLOW_SET_STATUS_URL, + WORKFLOW_SHARE_STATUS_URL, WORKFLOW_SPECIFICATION_URL, } from "~/client"; -import { - parseWorkflows, - parseWorkflowRetentionRules, - parseLogs, - parseFiles, - formatSearch, -} from "~/util"; import { getConfig, getWorkflow, getWorkflowLogs, getWorkflowSpecification, } from "~/selectors"; +import { + formatSearch, + parseFiles, + parseLogs, + parseWorkflowRetentionRules, + parseWorkflows, +} from "~/util"; export const ERROR = "Error"; export const NOTIFICATION = "Notification"; @@ -96,26 +99,11 @@ export const WORKFLOW_SHARE_STATUS_FETCH = "Fetch workflow share status"; export const WORKFLOW_SHARE_STATUS_RECEIVED = "Workflow share status received"; export const WORKFLOW_SHARE_STATUS_FETCH_ERROR = "Fetch workflow share status error"; -export const WORKFLOW_SHARE_INIT = "Initialise workflow sharing"; -export const WORKFLOW_SHARED_SUCCESSFULLY = "Workflow shared successfully"; -export const WORKFLOW_SHARED_ERROR = "Workflow shared error"; -export const WORKFLOW_SHARE_FINISH = "Finish workflow sharing"; -export const WORKFLOW_UNSHARE_INIT = "Initialise workflow unsharing"; -export const WORKFLOW_UNSHARED = "Workflow unshared"; -export const WORKFLOW_UNSHARE_ERROR = "Workflow unshare error"; - -export const USERS_SHARED_WITH_YOU_FETCH = - "Fetch users who shared workflows with you"; + export const USERS_SHARED_WITH_YOU_RECEIVED = "Users who shared workflows with you received"; -export const USERS_SHARED_WITH_YOU_FETCH_ERROR = - "Fetch users who shared workflows with you error"; -export const USERS_YOU_SHARED_WITH_FETCH = - "Fetch users you shared workflows with"; export const USERS_YOU_SHARED_WITH_RECEIVED = "Users you shared workflows with received"; -export const USERS_YOU_SHARED_WITH_FETCH_ERROR = - "Fetch users you shared workflows with error"; export function errorActionCreator(error, name) { const { status, data } = error?.response; @@ -288,12 +276,12 @@ export function fetchWorkflows({ pagination, search, status, - ownedBy, + sharedBy, sharedWith, sort, showLoader = true, workflowIdOrName, - includeShared = false, + shared = false, }) { return async (dispatch) => { if (showLoader) { @@ -305,20 +293,20 @@ export function fetchWorkflows({ pagination, search: formatSearch(search), status, - ownedBy, + sharedBy, sharedWith, sort, workflowIdOrName, - includeShared, + shared, }) - .then((resp) => { + .then((resp) => dispatch({ type: WORKFLOWS_RECEIVED, workflows: parseWorkflows(resp.data.items), total: resp.data.total, userHasWorkflows: resp.data.user_has_workflows, - }); - }) + }), + ) .catch((err) => { dispatch(errorActionCreator(err, USER_INFO_URL)); dispatch({ type: WORKFLOWS_FETCH_ERROR }); @@ -338,7 +326,7 @@ export function fetchWorkflow(id, { refetch = false, showLoader = true } = {}) { fetchWorkflows({ workflowIdOrName: id, showLoader, - includeShared: true, + shared: true, }), ); } @@ -567,38 +555,34 @@ export function closeShareWorkflowModal() { export function fetchUsersSharedWithYou() { return async (dispatch) => { - dispatch({ type: USERS_SHARED_WITH_YOU_FETCH }); - return await client .getUsersSharedWithYou() .then((resp) => { dispatch({ type: USERS_SHARED_WITH_YOU_RECEIVED, - users_shared_with_you: resp.data.users_shared_with_you, + usersSharedYouWith: resp.data.users_shared_with_you, }); return resp; }) .catch((err) => { - dispatch(errorActionCreator(err, USERS_SHARED_WITH_YOU_FETCH_ERROR)); + dispatch(errorActionCreator(err, USERS_SHARED_WITH_YOU_URL)); }); }; } export function fetchUsersYouSharedWith() { return async (dispatch) => { - dispatch({ type: USERS_YOU_SHARED_WITH_FETCH }); - return await client .getUsersYouSharedWith() .then((resp) => { dispatch({ type: USERS_YOU_SHARED_WITH_RECEIVED, - users_you_shared_with: resp.data.users_you_shared_with, + usersYouSharedWith: resp.data.users_you_shared_with, }); return resp; }) .catch((err) => { - dispatch(errorActionCreator(err, USERS_YOU_SHARED_WITH_FETCH_ERROR)); + dispatch(errorActionCreator(err, USERS_YOU_SHARED_WITH_URL)); }); }; } @@ -609,83 +593,24 @@ export function fetchWorkflowShareStatus(id) { return await client .getWorkflowShareStatus(id) .then((resp) => { + // convert to camel-case + const sharedWith = []; + for (const share of resp.data.shared_with) { + sharedWith.push({ + userEmail: share.user_email, + validUntil: share.valid_until, + }); + } dispatch({ type: WORKFLOW_SHARE_STATUS_RECEIVED, id, - workflow_share_status: resp.data.shared_with, + sharedWith, }); return resp; }) .catch((err) => { - dispatch(errorActionCreator(err, WORKFLOW_SHARE_STATUS_FETCH_ERROR)); - }); - }; -} - -export function shareWorkflow( - id, - user_id, - user_emails_to_share_with, - valid_until, -) { - return async (dispatch) => { - dispatch({ type: WORKFLOW_SHARE_INIT }); - - const users_shared_with = []; - const users_not_shared_with = []; - - for (const user_email_to_share_with of user_emails_to_share_with) { - await client - .shareWorkflow(id, { - user_id, - user_email_to_share_with, - valid_until, - }) - .then(() => { - users_shared_with.push(user_email_to_share_with); - }) - .catch((err) => { - const error_message = err.response.data.message; - users_not_shared_with.push({ - user_email_to_share_with, - error_message, - }); - }); - - if (users_shared_with.length > 0) { - dispatch({ - type: WORKFLOW_SHARED_SUCCESSFULLY, - users_shared_with, - }); - } - - if (users_not_shared_with.length > 0) { - dispatch({ - type: WORKFLOW_SHARED_ERROR, - users_not_shared_with, - }); - } - } - - dispatch({ type: WORKFLOW_SHARE_FINISH }); - }; -} - -export function unshareWorkflow(id, user_id, user_email_to_unshare_with) { - return async (dispatch) => { - dispatch({ type: WORKFLOW_UNSHARE_INIT }); - - return await client - .unshareWorkflow(id, { - user_id, - user_email_to_unshare_with, - }) - .then(() => { - dispatch({ type: WORKFLOW_UNSHARED, user_email_to_unshare_with }); - }) - .catch((err) => { - const error_message = err.response.data.message; - dispatch({ type: WORKFLOW_UNSHARE_ERROR, error_message }); + dispatch({ type: WORKFLOW_SHARE_STATUS_FETCH_ERROR }); + dispatch(errorActionCreator(err, WORKFLOW_SHARE_STATUS_URL(id))); }); }; } diff --git a/reana-ui/src/client.js b/reana-ui/src/client.js index f7f42e2..b9a20e2 100644 --- a/reana-ui/src/client.js +++ b/reana-ui/src/client.js @@ -127,20 +127,12 @@ class Client { pagination, search, status, - ownedBy, + sharedBy, sharedWith, sort, workflowIdOrName, - includeShared = false, + shared, } = {}) { - let shared = false; - if (ownedBy === "anybody" || includeShared) { - ownedBy = undefined; - shared = true; - } else if (ownedBy === "you") { - ownedBy = undefined; - } - return this._request( WORKFLOWS_URL({ ...(pagination ?? {}), @@ -148,7 +140,7 @@ class Client { search, status, shared, - shared_by: ownedBy, + shared_by: sharedBy, shared_with: sharedWith, sort, }), @@ -238,16 +230,19 @@ class Client { return this._request(WORKFLOW_SHARE_STATUS_URL(id)); } - shareWorkflow(id, data) { + shareWorkflow(id, { userEmailToShareWith, validUntil }) { return this._request(WORKFLOW_SHARE_URL(id), { - data, + data: { + user_email_to_share_with: userEmailToShareWith, + valid_until: validUntil, + }, method: "post", }); } - unshareWorkflow(id, data) { + unshareWorkflow(id, { userEmailToUnshareWith }) { return this._request(WORKFLOW_UNSHARE_URL(id), { - data, + data: { user_email_to_unshare_with: userEmailToUnshareWith }, method: "post", }); } diff --git a/reana-ui/src/components/WorkflowActionsPopup.js b/reana-ui/src/components/WorkflowActionsPopup.js index d0d551e..25b6252 100644 --- a/reana-ui/src/components/WorkflowActionsPopup.js +++ b/reana-ui/src/components/WorkflowActionsPopup.js @@ -8,20 +8,21 @@ under the terms of the MIT License; see LICENSE file for more details. */ -import { useState } from "react"; import PropTypes from "prop-types"; +import { useState } from "react"; +import { useDispatch, useSelector } from "react-redux"; import { Icon, Menu, Popup } from "semantic-ui-react"; -import { useDispatch } from "react-redux"; -import { workflowShape } from "~/props"; import { - deleteWorkflow, closeInteractiveSession, + deleteWorkflow, openDeleteWorkflowModal, - openStopWorkflowModal, openInteractiveSessionModal, openShareWorkflowModal, + openStopWorkflowModal, } from "~/actions"; +import { workflowShape } from "~/props"; +import { getUserEmail } from "~/selectors"; import { JupyterNotebookIcon } from "~/components"; @@ -29,13 +30,10 @@ import styles from "./WorkflowActionsPopup.module.scss"; const JupyterIcon = ; -export default function WorkflowActionsPopup({ - workflow, - className, - insideClickableElement, -}) { +export default function WorkflowActionsPopup({ workflow, className }) { const dispatch = useDispatch(); const [open, setOpen] = useState(false); + const userEmail = useSelector(getUserEmail); const { id, size, status, session_status: sessionStatus } = workflow; const isDeleted = status === "deleted"; const isDeletedUsingWorkspace = isDeleted && size.raw > 0; @@ -63,7 +61,6 @@ export default function WorkflowActionsPopup({ onClick: (e) => { dispatch(openShareWorkflowModal(workflow)); setOpen(false); - e.stopPropagation(); }, }); @@ -115,20 +112,9 @@ export default function WorkflowActionsPopup({ }); } - if (workflow.owner_email !== "-") { - return ( -
- ); - } - return (
- {menuItems.length > 0 && ( + {workflow.ownerEmail === userEmail && menuItems.length > 0 && ( - {workflow.owner_email === "-" ? ( + {workflow.ownerEmail === userEmail && ( <> {workflow.duration && (