diff --git a/reana-ui/src/actions.js b/reana-ui/src/actions.js index 9881adda..a0cefac1 100644 --- a/reana-ui/src/actions.js +++ b/reana-ui/src/actions.js @@ -16,17 +16,25 @@ import client, { USER_SIGNOUT_URL, WORKFLOW_LOGS_URL, WORKFLOW_SPECIFICATION_URL, + WORKFLOW_RETENTION_RULES_URL, WORKFLOW_FILES_URL, WORKFLOW_SET_STATUS_URL, INTERACTIVE_SESSIONS_OPEN_URL, INTERACTIVE_SESSIONS_CLOSE_URL, } from "~/client"; -import { parseWorkflows, parseLogs, parseFiles, formatSearch } from "~/util"; +import { + parseWorkflows, + parseWorkflowRetentionRules, + parseLogs, + parseFiles, + formatSearch, +} from "~/util"; import { getConfig, getWorkflow, getWorkflowLogs, getWorkflowSpecification, + getWorkflowRetentionRules, } from "~/selectors"; export const ERROR = "Error"; @@ -68,6 +76,8 @@ export const WORKFLOW_FILES_RECEIVED = "Workflow files received"; export const WORKFLOW_SPECIFICATION_FETCH = "Fetch workflow specification"; export const WORKFLOW_SPECIFICATION_RECEIVED = "Workflow specification received"; +export const WORKFLOW_RETENTION_RULES_RECEIVED = + "Workflow workflow retention received"; export const WORKFLOW_DELETE_INIT = "Initialize workflow deletion"; export const WORKFLOW_DELETED = "Workflow deleted"; export const OPEN_DELETE_WORKFLOW_MODAL = "Open delete workflow modal"; @@ -365,6 +375,31 @@ export function fetchWorkflowSpecification(id) { }; } +export function fetchWorkflowRetentionRules(id) { + return async (dispatch, getStore) => { + const state = getStore(); + const retentionRules = getWorkflowRetentionRules(id)(state); + // Only fetch if needed + if (!isEmpty(retentionRules)) { + return retentionRules; + } + return await client + .getWorkflowRetentionRules(id) + .then((resp) => + dispatch({ + type: WORKFLOW_RETENTION_RULES_RECEIVED, + id, + retentionRules: parseWorkflowRetentionRules( + resp.data.retention_rules + ), + }) + ) + .catch((err) => { + dispatch(errorActionCreator(err, WORKFLOW_RETENTION_RULES_URL(id))); + }); + }; +} + export function deleteWorkflow(id, workspace = true) { return async (dispatch) => { dispatch({ type: WORKFLOW_DELETE_INIT }); diff --git a/reana-ui/src/client.js b/reana-ui/src/client.js index cc4b3cba..b4b8df3f 100644 --- a/reana-ui/src/client.js +++ b/reana-ui/src/client.js @@ -32,6 +32,8 @@ export const WORKFLOWS_URL = (params) => export const WORKFLOW_LOGS_URL = (id) => `${api}/api/workflows/${id}/logs`; export const WORKFLOW_SPECIFICATION_URL = (id) => `${api}/api/workflows/${id}/specification`; +export const WORKFLOW_RETENTION_RULES_URL = (id) => + `${api}/api/workflows/${id}/retention_rules`; export const WORKFLOW_FILES_URL = (id, params) => `${api}/api/workflows/${id}/workspace?${stringifyQueryParams(params)}`; export const WORKFLOW_FILE_URL = (id, filename, preview = true) => @@ -141,6 +143,10 @@ class Client { return this._request(WORKFLOW_SPECIFICATION_URL(id)); } + getWorkflowRetentionRules(id) { + return this._request(WORKFLOW_RETENTION_RULES_URL(id)); + } + deleteWorkflow(id, data) { return this._request(WORKFLOW_SET_STATUS_URL(id, { status: "deleted" }), { data, diff --git a/reana-ui/src/pages/workflowDetails/components/WorkflowRetentionRules.js b/reana-ui/src/pages/workflowDetails/components/WorkflowRetentionRules.js index fe369c75..77f150a0 100644 --- a/reana-ui/src/pages/workflowDetails/components/WorkflowRetentionRules.js +++ b/reana-ui/src/pages/workflowDetails/components/WorkflowRetentionRules.js @@ -13,8 +13,8 @@ import { useEffect, useState } from "react"; import { useDispatch, useSelector } from "react-redux"; import { Accordion, Icon, Label, Table } from "semantic-ui-react"; -import { fetchWorkflow } from "~/actions"; -import { getWorkflow } from "~/selectors"; +import { fetchWorkflowRetentionRules } from "~/actions"; +import { getWorkflowRetentionRules } from "~/selectors"; import styles from "./WorkflowRetentionRules.module.scss"; @@ -24,22 +24,18 @@ function LabelRule({ workspaceFiles }) { export default function WorkflowRetentionRules({ id }) { const dispatch = useDispatch(); - const workflow = useSelector(getWorkflow(id)); - const [showRules, setShowRules] = useState(false); useEffect(() => { - dispatch(fetchWorkflow(id)); + dispatch(fetchWorkflowRetentionRules(id)); }, [dispatch, id]); const handleClick = () => { setShowRules((showRules) => !showRules); }; - const rules = workflow.retentionRules || []; - if (rules.length === 0) { - return null; - } + const rules = useSelector(getWorkflowRetentionRules(id)) || []; + if (!rules.length) return null; // First rule that still needs to be applied const nextRule = rules.find(({ active, created }) => active || created); diff --git a/reana-ui/src/reducers.js b/reana-ui/src/reducers.js index c320d52a..30f9ee83 100644 --- a/reana-ui/src/reducers.js +++ b/reana-ui/src/reducers.js @@ -37,6 +37,7 @@ import { WORKFLOW_SPECIFICATION_RECEIVED, WORKFLOW_FILES_FETCH, WORKFLOW_FILES_RECEIVED, + WORKFLOW_RETENTION_RULES_RECEIVED, OPEN_DELETE_WORKFLOW_MODAL, CLOSE_DELETE_WORKFLOW_MODAL, } from "~/actions"; @@ -276,6 +277,17 @@ const details = (state = detailsInitialState, action) => { }, loadingDetails: false, }; + case WORKFLOW_RETENTION_RULES_RECEIVED: + return { + ...state, + details: { + ...state.details, + [action.id]: { + ...state.details[action.id], + retentionRules: action.retentionRules, + }, + }, + }; default: return state; } diff --git a/reana-ui/src/selectors.js b/reana-ui/src/selectors.js index f190e01d..62b37846 100644 --- a/reana-ui/src/selectors.js +++ b/reana-ui/src/selectors.js @@ -64,3 +64,5 @@ export const getWorkflowSpecification = (id) => (state) => id in state.details.details && state.details.details[id].specification; export const getWorkflowParameters = (id) => (state) => id in state.details.details && state.details.details[id].parameters; +export const getWorkflowRetentionRules = (id) => (state) => + id in state.details.details && state.details.details[id].retentionRules; diff --git a/reana-ui/src/util.js b/reana-ui/src/util.js index a42037b0..404e13b3 100644 --- a/reana-ui/src/util.js +++ b/reana-ui/src/util.js @@ -55,7 +55,6 @@ export function parseWorkflows(workflows) { workflow.total = total.total; workflow.launcherURL = workflow.launcher_url; workflow = parseWorkflowDates(workflow); - workflow = parseWorkflowRetentionRules(workflow); obj[workflow.id] = workflow; return obj; @@ -67,7 +66,7 @@ export function parseWorkflows(workflows) { /** * Parses workflow's retention rules. */ -function parseWorkflowRetentionRules(workflow) { +export function parseWorkflowRetentionRules(retentionRules) { const getTimeBeforeExecution = (applyOn, currentTime) => { const diff = moment.duration(applyOn.diff(currentTime)); if (diff.asDays() < 1) { @@ -84,13 +83,9 @@ function parseWorkflowRetentionRules(workflow) { return timeBeforeExecution; }; - const retentionRules = workflow.retention_rules; - if (!retentionRules) { - return workflow; - } - delete workflow.retention_rules; + if (!Array.isArray(retentionRules)) return []; const currentTime = moment.now(); - workflow.retentionRules = sortBy( + return sortBy( retentionRules.map( ({ apply_on, retention_days, status, workspace_files }) => { const applyOn = apply_on ? moment(apply_on) : null; @@ -113,7 +108,6 @@ function parseWorkflowRetentionRules(workflow) { ), [({ retentionDays }) => retentionDays] ); - return workflow; } /**