@@ -69,9 +73,10 @@ class OAuth extends React.Component { centered size="sm" aria-labelledby="modal__oauth-modal" + role="main" > - Welcome To Cumulus Dashboard +

Welcome To Cumulus Dashboard

{ api.inflight ?

Authenticating ...

: null } { api.error ? : null } diff --git a/app/src/js/config/index.js b/app/src/js/config/index.js index e109ed356..b1ac6ec89 100644 --- a/app/src/js/config/index.js +++ b/app/src/js/config/index.js @@ -8,7 +8,7 @@ const deploymentConfig = require('./config'); const baseConfig = { environment: 'development', requireEarthdataLogin: false, - minCompatibleApiVersion: 'v11.1.0', + minCompatibleApiVersion: 'v14.0.0', oauthMethod: 'earthdata', graphicsPath: '/src/assets/images/', diff --git a/app/src/js/main.js b/app/src/js/main.js index a9a4758a8..35b49a5d7 100644 --- a/app/src/js/main.js +++ b/app/src/js/main.js @@ -33,7 +33,7 @@ const Main = ({ return (
{target !== 'cumulus' && ( -
+

{displayCase(target)} ({displayCase(environment)})

)} diff --git a/app/src/js/reducers/execution-status.js b/app/src/js/reducers/execution-status.js index ee99a1243..7fdc46280 100644 --- a/app/src/js/reducers/execution-status.js +++ b/app/src/js/reducers/execution-status.js @@ -1,4 +1,5 @@ import { createReducer } from '@reduxjs/toolkit'; +import get from 'lodash/get'; import { EXECUTION_STATUS, EXECUTION_STATUS_INFLIGHT, @@ -8,9 +9,7 @@ import { } from '../actions/types'; export const initialState = { - execution: null, - executionHistory: null, - stateMachine: null, + data: {}, searchString: null, inflight: false, error: false, @@ -25,13 +24,10 @@ export default createReducer(initialState, { const { data } = action; state.inflight = false; state.error = false; - state.execution = data.execution; - state.executionHistory = data.executionHistory; - state.stateMachine = data.stateMachine; - state.warning = data.warning; + state.data = data; - if (state.searchString) { - state.executionHistory.events = data.executionHistory.events.filter( + if (state.searchString && get(state, 'data.data.executionHistory.events')) { + state.data.data.executionHistory.events = data.data.executionHistory.events.filter( typeContains(state.searchString) ); } diff --git a/app/src/js/reducers/index.js b/app/src/js/reducers/index.js index 464c799d5..962ea05f9 100644 --- a/app/src/js/reducers/index.js +++ b/app/src/js/reducers/index.js @@ -22,6 +22,7 @@ import executionLogs from './execution-logs'; import operations from './operations'; import rules from './rules'; import reconciliationReports from './reconciliation-reports'; +import recoveryStatus from './recovery-status'; import cumulusInstance from './cumulus-instance'; import sidebar from './sidebar'; import sorts from './sort-persist'; @@ -54,6 +55,7 @@ export const reducers = { operations, rules, reconciliationReports, + recoveryStatus, sorts, locationQueryParams, }; diff --git a/app/src/js/reducers/recovery-status.js b/app/src/js/reducers/recovery-status.js new file mode 100644 index 000000000..8c0a5ec28 --- /dev/null +++ b/app/src/js/reducers/recovery-status.js @@ -0,0 +1,45 @@ +import { createReducer } from '@reduxjs/toolkit'; +import assignDate from './utils/assign-date'; + +import { + RECOVERY_GRANULE, + RECOVERY_GRANULE_INFLIGHT, + RECOVERY_GRANULE_ERROR, +} from '../actions/types'; + +export const initialState = { + list: { + data: [], + meta: {}, + params: {}, + }, + map: {}, +}; + +export default createReducer(initialState, { + [RECOVERY_GRANULE]: (state, action) => { + state.map[action.id] = { + data: assignDate(action.data), + error: false, + inflight: false, + }; + }, + [RECOVERY_GRANULE_INFLIGHT]: (state, action) => { + state.map[action.id] = { inflight: true }; + }, + [RECOVERY_GRANULE_ERROR]: (state, action) => { + let actionError = {}; + try { + actionError = JSON.parse(action.error); + } catch (_) { + // empty + } + + if (actionError.errorType !== 'NotFound' && actionError.httpStatus !== 404) { + state.map[action.id] = { + error: action.error, + inflight: false, + }; + } + }, +}); diff --git a/app/src/js/utils/recovery-status.js b/app/src/js/utils/recovery-status.js new file mode 100644 index 000000000..d55eb2c48 --- /dev/null +++ b/app/src/js/utils/recovery-status.js @@ -0,0 +1,16 @@ +export const getGranuleRecoveryJobStatusFromRecord = (record) => { + if (!record) return undefined; + const jobStatuses = (record.files || []).map((file) => file.status); + // file status may be 'pending', 'staged', 'success', or 'failed' + let recoveryStatus = 'failed'; + if (jobStatuses.length === 0) { + recoveryStatus = undefined; + } else if (jobStatuses.filter((jobStatus) => jobStatus !== 'success').length === 0) { + recoveryStatus = 'completed'; + } else if (jobStatuses.filter((jobStatus) => !['success', 'failed'].includes(jobStatus)).length > 0) { + recoveryStatus = 'running'; + } + return recoveryStatus; +}; + +export default getGranuleRecoveryJobStatusFromRecord; diff --git a/app/src/js/utils/table-config/executions.js b/app/src/js/utils/table-config/executions.js index fdbd33434..993cd4767 100644 --- a/app/src/js/utils/table-config/executions.js +++ b/app/src/js/utils/table-config/executions.js @@ -3,6 +3,7 @@ import { Link } from 'react-router-dom'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faCheckCircle, faTimesCircle } from '@fortawesome/free-solid-svg-icons'; import cloneDeep from 'lodash/cloneDeep'; +import get from 'lodash/get'; import { displayCase, seconds, @@ -14,6 +15,7 @@ import { strings } from '../../components/locale'; import { getPersistentQueryParams } from '../url-helper'; import { getEventDetails } from '../../components/Executions/execution-graph-utils'; import Tooltip from '../../components/Tooltip/tooltip'; +import { getExecutionStatus } from '../../actions'; export const formatEvents = (events) => { const mutableEvents = cloneDeep(events); @@ -70,7 +72,7 @@ export const subColumns = [ }, ]; -export const tableColumns = [ +export const tableColumns = ({ dispatch }) => ([ { Header: 'Name', accessor: 'name', @@ -116,7 +118,28 @@ export const tableColumns = [ accessor: 'collectionId', width: 200, Cell: ({ cell: { value } }) => formatCollectionId(value) + }, + { + Header: 'Download Execution', + id: 'download', + accessor: 'name', + Cell: ({ row: { original: { arn } } }) => (// eslint-disable-line react/prop-types +