diff --git a/.eslintrc b/.eslintrc index a9725f25d..b286e49bd 100644 --- a/.eslintrc +++ b/.eslintrc @@ -17,6 +17,7 @@ }, "rules": { "comma-dangle": ["error", "only-multiline"], + "space-before-function-paren": ["warn", {"anonymous": "ignore", "named": "ignore", "asyncArrow": "ignore"}], "semi": [2, "always"], "no-extra-semi": 2, "semi-spacing": [2, { "before": false, "after": true }], diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000..067baad37 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,15 @@ +**Summary:** Summary of changes + +Addresses [CUMULUS-XX: Develop amazing new feature](https://bugs.earthdata.nasa.gov/browse/CUMULUS-XXX) + +## Changes + +* Detailed list or prose of changes +* ... + +## PR Checklist + +- [ ] Update CHANGELOG +- [ ] Unit tests +- [ ] Adhoc testing +- [ ] Integration tests \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 666f4ee5b..93067f5e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,59 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] +## [v1.9.0] + +### BREAKING CHANGES + +- This dashboard version requires Cumulus API version >= v1.23.0 + +### Changed + +- **CUMULUS-1888** + - On the Granules page, CSV data was being refreshed in the background alog with the rest + of the data based on the timer. This could take a long time, depending on the number of granules. + This has been changed so that the data is only fetched when the user clicks the "Download CSV" button. + +- **CUMULUS-1913** + - Add datepicker to reconcilation-reports page + +- **CUMULUS-1916** + - reconcilation-reports page now requires Cumulus API version >= v1.23.0 + +## [v1.8.1] + +### Changed + +- **CUMULUS-1816** + - Change Datepicker behavior on login. The default to "Recent" start/end dates + now only occurs on first login on the hompage. + - URL is updated on login to reflect Datepicker params + +- **CUMULUS-1903** + - Replace individual tables in reconciliation report with tabs that change which table is displayed on click + +- **CUMULUS-1920** + - Add additional columns to reconciliation report list + +- **CUMULUS-1920** + - Updated styles for granule reingest modal + +- **CUMULUS-1948** + - Add provider to Granules tables + +### Fixed + +- **CUMULUS-1881** + - Fix Elasticsearch query bug for Gateway Access Metrics + +- **CUMULUS-1984** + - Fix bug where Distribution metrics were showing on the homepage even when + Elasaticsearch/Kibana not set up + +- **CUMULUS-1988** + - Fix bugs in reducer-creators + + ## [v1.8.0] ### Added @@ -66,12 +119,16 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - **CUMULUS-1787** - Changes `listCollections` action to hit `/collections/active` endpoint when timefilters are present (requires Cumulus API v1.22.1) +- **CUMULUS-1790** + - Changes default values and visuals for home page's datepicker. When the page loads, it defauls to display "Recent" data, which is the previous 24 hours with no end time. + - **CUMULUS-1798** - Change the 12HR/24HR Format selector from radio to dropdown - Hide clock component in react-datetime-picker -- **CUMULUS-1790** - - Changes default values and visuals for home page's datepicker. When the page loads, it defauls to display "Recent" data, which is the previous 24 hours with no end time. +- **CUMULUS-1810** + - Unified the coding pattern used for creating Redux reducers to avoid + unnecessary object creation and reduce unnecessary UI component refreshes ### Fixed @@ -352,7 +409,9 @@ Fix for serving the dashboard through the Cumulus API. - Versioning and changelog [CUMULUS-197] by @kkelly51 -[Unreleased]: https://github.com/nasa/cumulus-dashboard/compare/v1.8.0...HEAD +[Unreleased]: https://github.com/nasa/cumulus-dashboard/compare/v1.9.0...HEAD +[v1.9.0]: https://github.com/nasa/cumulus-dashboard/compare/v1.8.1...v1.9.0 +[v1.8.1]: https://github.com/nasa/cumulus-dashboard/compare/v1.8.0...v1.8.1 [v1.8.0]: https://github.com/nasa/cumulus-dashboard/compare/v1.7.2...v1.8.0 [v1.7.2]: https://github.com/nasa/cumulus-dashboard/compare/v1.7.1...v1.7.2 [v1.7.1]: https://github.com/nasa/cumulus-dashboard/compare/v1.7.0...v1.7.1 diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 6b82096fe..c8a107a0e 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -91,20 +91,70 @@ This sends the granule data to the store. We need to specify the primary key so export const SET_GRANULE = 'SET_GRANULE'; ``` -Now in `reducers/api.js` we import the primary key and export a reducer function, which receives the current state, and the reducer in question. We use a primary key, because every action is sent to every reducer. The reducer doesn't manipulate the current state, but rather returns a new state object that includes the new data. +Now in `reducers/api.js` we import the primary key and export a reducer +function, which receives the current state, and the reducer in question. We +use a primary key, because every action is sent to every reducer. The reducer +doesn't manipulate the current state, but rather returns a new state object +that includes the new data. + +Since it is critical to avoid directly mutating the current state in a reducer +function, we use the `createReducer` function from the +[Redux Toolkit](https://redux-toolkit.js.org/). This not only allows us to +avoid the more verbose `switch` statement syntax that is normally used without +the use of a convenience function such as `createReducer`, but also guarantees +that we never mutate the current state by providing a proxy state object +instead. + +We can then conveniently mutate the proxy state object _as if_ we were mutating +the actual state object, and the underlying functionality takes care of +producing a new state object for us, with the fewest possible changes. The +general pattern for each reducer within the `app/src/js/reducers` directory is +as follows: ```javascript -import { SET_GRANULE } from '../actions'; -export function reducer (currentState, action) { - const newState = Object.assign({}, currentState); - if (action.type === SET_GRANULE) { - newState.granuleDetail[action.id] = action.data; - } - return newState; +import { createReducer } from '@reduxjs/toolkit'; +import { + ACTION_TYPE_1, + ACTION_TYPE_2, + ... + ACTION_TYPE_N +} from '../actions/types'; + +export const initialState = { + // Some initial state object appropriate for the reducer + ... }; + +export default createReducer(initialState, { + [ACTION_TYPE_1]: (state, action) => { + state.path.to.prop1 = action.newValue1; + state.path.to.prop2 = action.newValue2; + }, + [ACTION_TYPE_2]: (state, action) => { + ... + }, + ... + [ACTION_TYPE_N]: (state, action) => { + ... + } +}); ``` -Finally, this allows us to access the data from a component, where component state is passed as a `prop`: +Again, note that the `state` parameter for each of the case reducers above is +a proxy object, not the actual state object, but it can be mutated just as if +it were. Further, you will likely never return a completely new state object, +because that is the purpose of the proxy object instead. The library manages +the creation of a new object on your behalf. Of course, if it is absolutely +necessary for some reason to return a new object, you may, but do _not_ mutate +the proxy _and also_ return a new object. For more details, see +[createReducer](https://redux-toolkit.js.org/api/createReducer). + +To reduce some common boilerplate code in the case reducers, there are a few +convenience reducer creators in `app/src/js/reducers/utils/reducer-creators.js`. +See the documentation in that file for more information. + +Finally, this allows us to access the data from a component, where component +state is passed as a `prop`: ```javascript // import the action so we can call it diff --git a/TABLES.md b/TABLES.md index 9678ef3db..832a8d753 100644 --- a/TABLES.md +++ b/TABLES.md @@ -21,13 +21,13 @@ A basic table component that supports row selection and dumb sorting (see below) * Additional options can be found [here](https://github.com/tannerlinsley/react-table/blob/master/docs/api/useTable.md#column-options) or in the documation for a specific plugin hook - **data**: Array of data items. Items can be any format. -- **sortIdx**: The id of the column to sort on. -- **changeSortProps**: Callback when a new sort order is defined, passed an object with the properties `{ sortIdx, order }`. +- **sortId**: The id of the column to sort on. +- **changeSortProps**: Callback when a new sort order is defined, passed an object with the properties `{ sortId, order }`. - **onSelect**: Callback when a row is selected (or unselected), passed an array containing the ids of all selected rows. - **canSelect**: Boolean value defining whether 1. rows can be selected and 2. to render check marks. - **rowId**: String or function that defines a particular row's id. Passed to `useTable` options via `getRowId`. -Note, `sortIdx` and `changeSortProps` only apply to components that implement smart searching, such as `list-view`. This base component does internal prop checking to determine whether it uses smart or dumb sorting, based on whether the above props are defined. +Note, `sortId` and `changeSortProps` only apply to components that implement smart searching, such as `list-view`. This base component does internal prop checking to determine whether it uses smart or dumb sorting, based on whether the above props are defined. ## `list-view` @@ -38,7 +38,7 @@ Wraps `sortable-table` and implements auto-update and smart sort. When a new sor - **list**: Parent data structure, ie `state.granules.list` or `state.collections.list`. Expected to contain `{ data, inflight, error, meta }` properties corresponding to all `list` state objects. - **dispatch**: Redux dispatch function. - **action**: Redux-style action to send, ie `listCollections`. -- **sortIdx**: Corresponds to `sortableTable#sortIdx`. +- **sortId**: Corresponds to `sortableTable#sortId`. - **query**: Array of configuration objects passed to `batch-async-command`. - **rowId** Corresponds to `sortableTable#rowId`. diff --git a/app/src/css/_base.scss b/app/src/css/_base.scss index ad29753f1..4451441cb 100644 --- a/app/src/css/_base.scss +++ b/app/src/css/_base.scss @@ -596,6 +596,27 @@ a:active { } } +/************************************************** + Status Indicator +**************************************************/ + +.status-indicator { + display: inline-block; + width: 0.5rem; + height: 0.5rem; + border-radius: 50%; + background-color: $grey; + margin-right: 0.5em; + + &--success { + background-color: $light-green; + } + + &--failed { + background-color: $error-red; + } +} + /************************************************** Table **************************************************/ diff --git a/app/src/css/modules/_modals.scss b/app/src/css/modules/_modals.scss index cc28d344d..4ba39400e 100644 --- a/app/src/css/modules/_modals.scss +++ b/app/src/css/modules/_modals.scss @@ -108,6 +108,12 @@ li { font-weight: bold; + + .Collapsible__contentInner { + font-weight: normal; + max-width: none; + color: $error-red; + } } } diff --git a/app/src/css/vendor/bootstrap/_bootstrap.scss b/app/src/css/vendor/bootstrap/_bootstrap.scss index 06198e561..de6e8c984 100644 --- a/app/src/css/vendor/bootstrap/_bootstrap.scss +++ b/app/src/css/vendor/bootstrap/_bootstrap.scss @@ -17,6 +17,7 @@ and behaviors for our Cumulus Dashboard components /* Bootstrap Overrides */ @import "overrides/breadcrumb"; +@import "overrides/card"; @import "overrides/pagination"; @import "overrides/modal"; diff --git a/app/src/css/vendor/bootstrap/overrides/_card.scss b/app/src/css/vendor/bootstrap/overrides/_card.scss new file mode 100644 index 000000000..be1322ff2 --- /dev/null +++ b/app/src/css/vendor/bootstrap/overrides/_card.scss @@ -0,0 +1,28 @@ +.card-wrapper { + display: flex; + justify-content: space-between; +} + +.card { + width: 10em; + cursor: pointer; + + .card-header { + color: $white; + background-color: $grey; + } + + .h5 { + font-size: 1.25rem; + } + + &:hover { + box-shadow: $shadow__hover; + } + + &.active { + .card-header { + background-color: $theme--blue; + } + } +} diff --git a/app/src/js/actions/action-config/apiGatewaySearch.js b/app/src/js/actions/action-config/apiGatewaySearch.js index d97203d48..c02734306 100644 --- a/app/src/js/actions/action-config/apiGatewaySearch.js +++ b/app/src/js/actions/action-config/apiGatewaySearch.js @@ -21,14 +21,14 @@ export const apiGatewaySearchTemplate = (prefix, startTimeEpochMilli, endTimeEpo }, "ApiAccessErrors": { "query_string": { - "query": "status:[400 TO 599]", + "query": "statusCode:[400 TO 599]", "analyze_wildcard": true, "default_field": "*" } }, "ApiAccessSuccesses": { "query_string": { - "query": "status:[200 TO 399]", + "query": "statusCode:[200 TO 399]", "analyze_wildcard": true, "default_field": "*" } diff --git a/app/src/js/actions/index.js b/app/src/js/actions/index.js index 8b107e054..e0468eaf3 100644 --- a/app/src/js/actions/index.js +++ b/app/src/js/actions/index.js @@ -74,19 +74,27 @@ export const refreshAccessToken = (token) => { export const setTokenState = (token) => ({ type: types.SET_TOKEN, token }); export const interval = function (action, wait, immediate) { - if (immediate) { action(); } + if (immediate) { + action(); + } const intervalId = setInterval(action, wait); return () => clearInterval(intervalId); }; -export const getCollection = (name, version) => ({ - [CALL_API]: { - type: types.COLLECTION, - method: 'GET', - id: getCollectionId({ name, version }), - path: `collections?name=${name}&version=${version}` - } -}); +export const getCollection = (name, version) => { + return (dispatch, getState) => { + const timeFilters = fetchCurrentTimeFilters(getState().datepicker); + return dispatch({ + [CALL_API]: { + type: types.COLLECTION, + method: 'GET', + id: getCollectionId({ name, version }), + path: `collections?name=${name}&version=${version}`, + qs: timeFilters, + }, + }); + }; +}; export const getApiVersion = () => { return (dispatch) => { @@ -915,14 +923,19 @@ export const clearRulesSearch = () => ({ type: types.CLEAR_RULES_SEARCH }); export const filterRules = (param) => ({ type: types.FILTER_RULES, param: param }); export const clearRulesFilter = (paramKey) => ({ type: types.CLEAR_RULES_FILTER, paramKey: paramKey }); -export const listReconciliationReports = (options) => ({ - [CALL_API]: { - type: types.RECONCILIATIONS, - method: 'GET', - url: new URL('reconciliationReports', root).href, - qs: Object.assign({ limit: defaultPageLimit }, options) - } -}); +export const listReconciliationReports = (options) => { + return (dispatch, getState) => { + const timeFilters = fetchCurrentTimeFilters(getState().datepicker); + return dispatch({ + [CALL_API]: { + type: types.RECONCILIATIONS, + method: 'GET', + url: new URL('reconciliationReports', root).href, + qs: Object.assign({ limit: defaultPageLimit }, options, timeFilters) + } + }); + }; +}; export const getReconciliationReport = (reconciliationName) => ({ [CALL_API]: { diff --git a/app/src/js/actions/types.js b/app/src/js/actions/types.js index 54397590e..32699ebf9 100644 --- a/app/src/js/actions/types.js +++ b/app/src/js/actions/types.js @@ -245,3 +245,7 @@ export const PROCESSING_MODAL = 'PROCESSING_MODAL'; export const DATEPICKER_DROPDOWN_FILTER = 'DATEPICKER_DROPDOWN_FILTER'; export const DATEPICKER_DATECHANGE = 'DATEPICKER_DATECHANGE'; export const DATEPICKER_HOUR_FORMAT = 'DATEPICKER_HOUR_FORMAT'; +// timer state +export const TIMER_STOP = 'TIMER_STOP'; +export const TIMER_START = 'TIMER_START'; +export const TIMER_SET_COUNTDOWN = 'TIMER_SET_COUNTDOWN'; diff --git a/app/src/js/components/AsyncCommands/AsyncCommands.js b/app/src/js/components/AsyncCommands/AsyncCommands.js index e9835b982..b97bf363a 100644 --- a/app/src/js/components/AsyncCommands/AsyncCommands.js +++ b/app/src/js/components/AsyncCommands/AsyncCommands.js @@ -7,14 +7,14 @@ import PropTypes from 'prop-types'; import Ellipsis from '../LoadingEllipsis/loading-ellipsis'; import DefaultModal from '../Modal/modal'; import { preventDefault } from '../../utils/noop'; -import _config from '../../config'; - -const { updateDelay } = _config; class AsyncCommand extends React.Component { constructor () { super(); - this.state = { confirmModal: false }; + this.state = { + confirmModal: false, + successModal: false + }; this.buttonClass = this.buttonClass.bind(this); this.elementClass = this.elementClass.bind(this); this.handleClick = this.handleClick.bind(this); @@ -23,18 +23,20 @@ class AsyncCommand extends React.Component { } componentDidUpdate (prevProps) { + const { status, showSuccessModal } = this.props; if ( prevProps.status === 'inflight' && - this.props.status === 'success' && - typeof prevProps.success === 'function' + status === 'success' ) { - const timeout = isNaN(prevProps.successTimeout) ? updateDelay : prevProps.successTimeout; - setTimeout(prevProps.success, timeout); - this.setState({ successModal: true }); // eslint-disable-line react/no-did-update-set-state + if (showSuccessModal) { + this.setState({ successModal: true }); // eslint-disable-line react/no-did-update-set-state + } else if (typeof prevProps.success === 'function') { + prevProps.success(); + } } else if ( prevProps.status === 'inflight' && - this.props.status === 'error' && - typeof prevProps.error === 'function' + status === 'error' && + typeof prevProps.error === 'function' ) { prevProps.error(); } @@ -79,7 +81,7 @@ class AsyncCommand extends React.Component { } render () { - const { status, text, confirmText, confirmOptions, showSuccessModal, postActionText } = this.props; + const { status, text, confirmText, confirmOptions, postActionText, success } = this.props; const { confirmModal, successModal } = this.state; const inflight = status === 'inflight'; const element = this.props.element || 'button'; @@ -90,11 +92,10 @@ class AsyncCommand extends React.Component { if (element === 'a') props.href = '#'; const children = ( - {text}{inflight ? : ''} + {text}{inflight && } ); const button = React.createElement(element, props, children); - return (
{ button } @@ -104,34 +105,33 @@ class AsyncCommand extends React.Component { 'modal__container--onscreen': confirmModal })}> - { confirmOptions ? (confirmOptions).map(option => -
- {option} -
-
- ) : null } -

{confirmText}

-
- )} showModal={confirmModal} - /> + > +
+ { confirmOptions && (confirmOptions).map(option => +
+ {option} +
+
+ )} +

{confirmText}

+
+ @@ -147,14 +147,12 @@ AsyncCommand.propTypes = { text: PropTypes.string, className: PropTypes.string, disabled: PropTypes.bool, - successTimeout: PropTypes.number, element: PropTypes.string, confirmAction: PropTypes.bool, confirmText: PropTypes.string, confirmOptions: PropTypes.array, showSuccessModal: PropTypes.bool, - postActionText: PropTypes.string, - href: PropTypes.string + postActionText: PropTypes.string }; export default AsyncCommand; diff --git a/app/src/js/components/BatchAsyncCommands/BatchAsyncCommands.js b/app/src/js/components/BatchAsyncCommands/BatchAsyncCommands.js index c75988db3..0a49619db 100644 --- a/app/src/js/components/BatchAsyncCommands/BatchAsyncCommands.js +++ b/app/src/js/components/BatchAsyncCommands/BatchAsyncCommands.js @@ -120,10 +120,10 @@ export class BatchCommand extends React.Component { onComplete (errors, results) { const delay = this.props.updateDelay ? this.props.updateDelay : updateDelay; // turn array of errors from queue into single error for ui - const error = this.createErrorMessage(errors); - this.setState({ status: (error ? 'error' : 'success') }); + const errorMessage = this.createErrorMessage(errors); + this.setState({ status: (errorMessage ? 'error' : 'success') }); setTimeout(() => { - this.cleanup(error, results); + this.cleanup(errorMessage, errors, results); }, delay); } @@ -134,7 +134,7 @@ export class BatchCommand extends React.Component { } // call onSuccess and onError functions as needed - cleanup (error, results) { + cleanup (errorMessage, errors, results) { const { onSuccess, onError, getModalOptions, selected, history } = this.props; this.setState({ completed: 0, status: null }); if (typeof getModalOptions === 'function') { @@ -142,14 +142,15 @@ export class BatchCommand extends React.Component { history, selected, results, - error, + errors, + errorMessage, isOnModalComplete: true, closeModal: this.closeModal }); this.setState({ modalOptions }); } - if (error && typeof onError === 'function') onError(error); - if (results && results.length && typeof onSuccess === 'function') onSuccess(results, error); + if (errorMessage && typeof onError === 'function') onError(errorMessage); + if (results && results.length && typeof onSuccess === 'function') onSuccess(results, errorMessage); } isInflight () { @@ -196,7 +197,6 @@ export class BatchCommand extends React.Component { text={text} className={className} disabled={!activeModal && (!todo || !!inflight)} - successTimeout={0} status={!activeModal && inflight ? 'inflight' : null} /> { activeModal &&
} diff --git a/app/src/js/components/Collections/add.js b/app/src/js/components/Collections/add.js index 21cb71d78..78070e684 100644 --- a/app/src/js/components/Collections/add.js +++ b/app/src/js/components/Collections/add.js @@ -32,7 +32,7 @@ const AddCollection = ({ location = {}, collections, dispatch, schema }) => { }, [collectionSchema, collectionId, collectionsMap, isCopy]); const getBaseRoute = (pk = collectionId) => { - return (pk && pk !== 'unknown') ? collectionHref(pk) : '/collections'; + return pk && pk !== 'unknown' ? collectionHref(pk) : '/collections'; }; return ( @@ -56,7 +56,9 @@ AddCollection.propTypes = { schema: PropTypes.object, }; -export default withRouter(connect(state => ({ - collections: state.collections, - schema: state.schema -}))(AddCollection)); +export default withRouter( + connect((state) => ({ + collections: state.collections, + schema: state.schema, + }))(AddCollection) +); diff --git a/app/src/js/components/Collections/edit.js b/app/src/js/components/Collections/edit.js index 92bd4eda7..f4a55a6dd 100644 --- a/app/src/js/components/Collections/edit.js +++ b/app/src/js/components/Collections/edit.js @@ -6,7 +6,7 @@ import { withRouter } from 'react-router-dom'; import { getCollection, updateCollection, - clearUpdateCollection + clearUpdateCollection, } from '../../actions'; import { getCollectionId } from '../../utils/format'; import EditRaw from '../EditRaw/edit-raw'; @@ -14,17 +14,19 @@ import EditRaw from '../EditRaw/edit-raw'; const SCHEMA_KEY = 'collection'; const EditCollection = ({ match, collections }) => { - const { params: { name, version } } = match; + const { + params: { name, version }, + } = match; const collectionId = getCollectionId({ name, version }); return ( getCollection(name, version)} - updateRecord={payload => updateCollection(payload, name, version)} + updateRecord={(payload) => updateCollection(payload, name, version)} backRoute={`/collections/collection/${name}/${version}`} clearRecordUpdate={clearUpdateCollection} hasModal={true} @@ -34,9 +36,11 @@ const EditCollection = ({ match, collections }) => { EditCollection.propTypes = { match: PropTypes.object, - collections: PropTypes.object + collections: PropTypes.object, }; -export default withRouter(connect(state => ({ - collections: state.collections -}))(EditCollection)); +export default withRouter( + connect((state) => ({ + collections: state.collections, + }))(EditCollection) +); diff --git a/app/src/js/components/Collections/granules.js b/app/src/js/components/Collections/granules.js index b7aa2cf02..0b90f3848 100644 --- a/app/src/js/components/Collections/granules.js +++ b/app/src/js/components/Collections/granules.js @@ -10,12 +10,12 @@ import { clearGranulesFilter, applyWorkflowToGranule, searchGranules, - clearGranulesSearch + clearGranulesSearch, } from '../../actions'; import { simpleDropdownOption, bulkActions, - tableColumns + tableColumns, } from '../../utils/table-config/granules'; import List from '../Table/Table'; import Dropdown from '../DropDown/dropdown'; @@ -32,7 +32,7 @@ const CollectionGranules = ({ match, location, granules, - workflowOptions + workflowOptions, }) => { const { params } = match; const { name: collectionName, version: collectionVersion } = params; @@ -48,76 +48,76 @@ const CollectionGranules = ({ const breadcrumbConfig = [ { label: 'Dashboard Home', - href: '/' + href: '/', }, { label: 'Collections', - href: '/collections' + href: '/collections', }, { label: 'Collection Overview', - href: `/collections/collection/${collectionName}/${collectionVersion}` + href: `/collections/collection/${collectionName}/${collectionVersion}`, }, { label: 'Collection Granules', href: `/collections/collection/${collectionName}/${collectionVersion}/granules`, - active: view === 'all' - } + active: view === 'all', + }, ]; if (view !== 'all') { breadcrumbConfig.push({ label: displayCase(view), - active: true + active: true, }); } - function generateQuery () { + function generateQuery() { const options = { collectionId }; if (view && view !== 'all') options.status = view; return options; } - function getView () { + function getView() { if (pathname.includes('/granules/completed')) return 'completed'; if (pathname.includes('/granules/processing')) return 'running'; if (pathname.includes('/granules/failed')) return 'failed'; return 'all'; } - function generateBulkActions () { + function generateBulkActions() { const actionConfig = { execute: { options: getExecuteOptions(), - action: applyWorkflow - } + action: applyWorkflow, + }, }; return bulkActions(granules, actionConfig); } - function selectWorkflow (selector, workflow) { + function selectWorkflow(selector, workflow) { setWorkflow(workflow); } - function applyWorkflow (granuleId) { + function applyWorkflow(granuleId) { return applyWorkflowToGranule(granuleId, workflow); } - function getExecuteOptions () { + function getExecuteOptions() { return [ simpleDropdownOption({ handler: selectWorkflow, label: 'workflow', value: workflow, - options: workflowOptions - }) + options: workflowOptions, + }), ]; } return (
-
+
@@ -149,8 +149,8 @@ const CollectionGranules = ({ action={listGranules} query={query} bulkActions={generateBulkActions()} - rowId='granuleId' - sortIdx='timestamp' + rowId="granuleId" + sortId="timestamp" tableColumns={tableColumns} > @@ -158,18 +158,18 @@ const CollectionGranules = ({ dispatch={dispatch} action={searchGranules} clear={clearGranulesSearch} - label='Search' - placeholder='Granule ID' + label="Search" + placeholder="Granule ID" /> {view === 'all' && ( )} @@ -177,10 +177,10 @@ const CollectionGranules = ({ options={pageSizeOptions} action={filterGranules} clear={clearGranulesFilter} - label='Results Per Page' - paramKey='limit' + label="Results Per Page" + paramKey="limit" inputProps={{ - placeholder: 'Results Per Page' + placeholder: 'Results Per Page', }} /> @@ -194,14 +194,13 @@ CollectionGranules.propTypes = { granules: PropTypes.object, dispatch: PropTypes.func, location: PropTypes.object, - config: PropTypes.object, workflowOptions: PropTypes.array, - params: PropTypes.object, - match: PropTypes.object + match: PropTypes.object, }; -export default withRouter(connect(state => ({ - workflowOptions: workflowOptionNames(state), - granules: state.granules, - config: state.config -}))(CollectionGranules)); +export default withRouter( + connect((state) => ({ + workflowOptions: workflowOptionNames(state), + granules: state.granules, + }))(CollectionGranules) +); diff --git a/app/src/js/components/Collections/index.js b/app/src/js/components/Collections/index.js index f9fa1eb5c..f76c0a13b 100644 --- a/app/src/js/components/Collections/index.js +++ b/app/src/js/components/Collections/index.js @@ -3,7 +3,6 @@ import React from 'react'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import { withRouter, Redirect, Route, Switch } from 'react-router-dom'; -import withQueryParams from 'react-router-query-params'; import Sidebar from '../Sidebar/sidebar'; import { strings } from '../locale'; import CollectionList from '../../components/Collections/list'; @@ -17,42 +16,91 @@ import DatePickerHeader from '../../components/DatePickerHeader/DatePickerHeader import { listCollections } from '../../actions'; class Collections extends React.Component { - constructor () { - super(); - this.displayName = strings.collection; - } - - query () { + query() { this.props.dispatch(listCollections()); } - render () { + render() { const { pathname } = this.props.location; const existingCollection = pathname !== '/collections/add'; return ( -
- -
-
- - - -
+
+ +
+
+ + + +
- - - - - - - - - - - - + + + + + + + + + + + +
@@ -62,11 +110,12 @@ class Collections extends React.Component { } } +Collections.displayName = strings.collection; + Collections.propTypes = { children: PropTypes.object, dispatch: PropTypes.func, location: PropTypes.object, - queryParams: PropTypes.object }; -export default withRouter(withQueryParams()(connect(state => state)(Collections))); +export default withRouter(connect()(Collections)); diff --git a/app/src/js/components/Collections/ingest.js b/app/src/js/components/Collections/ingest.js index 4b33e7d84..278eba725 100644 --- a/app/src/js/components/Collections/ingest.js +++ b/app/src/js/components/Collections/ingest.js @@ -11,11 +11,11 @@ import config from '../../config'; import Loading from '../LoadingIndicator/loading-indicator'; class CollectionIngest extends React.Component { - constructor () { + constructor() { super(); this.displayName = 'CollectionIngest'; this.state = { - view: 'json' + view: 'json', }; this.get = this.get.bind(this); this.renderReadOnlyJson = this.renderReadOnlyJson.bind(this); @@ -23,7 +23,7 @@ class CollectionIngest extends React.Component { this.renderJson = this.renderJson.bind(this); } - componentDidUpdate (prevProps) { + componentDidUpdate(prevProps) { const { name, version } = prevProps.match.params; const collectionId = getCollectionId({ name, version }); const record = prevProps.collections.map[collectionId]; @@ -32,25 +32,24 @@ class CollectionIngest extends React.Component { } } - componentDidMount () { + componentDidMount() { const { name, version } = this.props.match.params; this.get(name, version); } - get (name, version) { + get(name, version) { this.props.dispatch(getCollection(name, version)); } - renderReadOnlyJson (name, data) { + renderReadOnlyJson(name, data) { return ( -
-

{name}

- Edit +
+
+

+ {name} +

+ + Edit + {lastUpdated(data.queriedAt)}
-
-
- +
+
+
- {this.state.view === 'list' ? this.renderList(data) : this.renderJson(data)} + {this.state.view === 'list' + ? this.renderList(data) + : this.renderJson(data)}
); } - renderList (data) { + renderList(data) { const ingest = get(data, 'ingest', {}); const recipe = get(data, 'recipe', {}); return ( -
-
-

Ingest

+
+
+

Ingest

Type: {ingest.type}

Configuration
Concurrency: {get(ingest, 'config.concurrency', nullValue)}
Endpoint: {get(ingest, 'config.endpoint', nullValue)}
-
-

Recipe

+
+

Recipe

Order
- {get(recipe, 'order', []).map((step, i) =>
{step}
)} + {get(recipe, 'order', []).map((step, i) => ( +
{step}
+ ))}
Process step
Description: {get(recipe, 'processStep.description', '--')}
Input files
- {get(recipe, 'processStep.config.inputFiles', []).map((file, i) =>
{file}
)} + {get(recipe, 'processStep.config.inputFiles', []).map((file, i) => ( +
{file}
+ ))}
Output files
- {get(recipe, 'processStep.config.outputFiles', []).map((file, i) =>
{file}
)} + {get(recipe, 'processStep.config.outputFiles', []).map((file, i) => ( +
{file}
+ ))}
); } - renderJson (data) { + renderJson(data) { return (
  • @@ -136,7 +159,9 @@ class CollectionIngest extends React.Component { CollectionIngest.propTypes = { match: PropTypes.object, collections: PropTypes.object, - dispatch: PropTypes.func + dispatch: PropTypes.func, }; -export default withRouter(connect(state => state)(CollectionIngest)); +export default withRouter( + connect((state) => ({ collections: state.collections }))(CollectionIngest) +); diff --git a/app/src/js/components/Collections/list.js b/app/src/js/components/Collections/list.js index 11a33817c..b8839e29f 100644 --- a/app/src/js/components/Collections/list.js +++ b/app/src/js/components/Collections/list.js @@ -11,18 +11,18 @@ import { listCollections, searchCollections, filterCollections, - clearCollectionsFilter + clearCollectionsFilter, } from '../../actions'; import { collectionSearchResult, lastUpdated, tally, - getCollectionId + getCollectionId, } from '../../utils/format'; import { bulkActions, recoverAction, - tableColumns + tableColumns, } from '../../utils/table-config/collections'; import Search from '../Search/search'; import List from '../Table/Table'; @@ -35,43 +35,45 @@ import pageSizeOptions from '../../utils/page-size'; const breadcrumbConfig = [ { label: 'Dashboard Home', - href: '/' + href: '/', }, { label: 'Collections', - active: true - } + active: true, + }, ]; class CollectionList extends React.Component { - constructor () { + constructor() { super(); this.generateQuery = this.generateQuery.bind(this); this.generateBulkActions = this.generateBulkActions.bind(this); } - componentDidMount () { + componentDidMount() { const { dispatch } = this.props; dispatch(getCumulusInstanceMetadata()); } - generateQuery () { + generateQuery() { return {}; } - generateBulkActions () { + generateBulkActions() { const actionConfig = { recover: { - action: applyRecoveryWorkflowToCollection - } + action: applyRecoveryWorkflowToCollection, + }, }; const { collections, config } = this.props; let actions = bulkActions(collections); - if (config.enableRecovery) actions = actions.concat(recoverAction(collections, actionConfig)); + if (config.enableRecovery) { + actions = actions.concat(recoverAction(collections, actionConfig)); + } return actions; } - render () { + render() { const { collections, mmtLinks, datepicker } = this.props; const { list } = collections; const { startDateTime, endDateTime } = datepicker || {}; @@ -81,26 +83,30 @@ class CollectionList extends React.Component { const data = list.data.map((collection) => { return { ...collection, - mmtLink: mmtLinks[getCollectionId(collection)] + mmtLink: mmtLinks[getCollectionId(collection)], }; }); const { count, queriedAt } = list.meta; return ( -
    -
    -
    +
    +
    +
    -
    -

    {strings.collection_overview}

    +
    +

    + {strings.collection_overview} +

    {lastUpdated(queriedAt)}
    -
    -
    -

    - {hasTimeFilter ? strings.active_collections : strings.all_collections} - {count ? tally(count) : 0} +
    +
    +

    + {hasTimeFilter + ? strings.active_collections + : strings.all_collections} + {count ? tally(count) : 0}

    @@ -113,7 +119,7 @@ class CollectionList extends React.Component { query={this.generateQuery()} bulkActions={this.generateBulkActions()} rowId={getCollectionId} - sortIdx='duration' + sortId="duration" > @@ -148,10 +154,17 @@ CollectionList.propTypes = { mmtLinks: PropTypes.object, dispatch: PropTypes.func, config: PropTypes.object, - datepicker: PropTypes.object + datepicker: PropTypes.object, }; CollectionList.displayName = 'CollectionList'; export { CollectionList }; -export default withRouter(connect(state => state)(CollectionList)); +export default withRouter( + connect((state) => ({ + config: state.config, + collections: state.collections, + datepicker: state.datepicker, + mmtLinks: state.mmtLinks, + }))(CollectionList) +); diff --git a/app/src/js/components/Collections/logs.js b/app/src/js/components/Collections/logs.js index 0e12f7b4a..9088032ab 100644 --- a/app/src/js/components/Collections/logs.js +++ b/app/src/js/components/Collections/logs.js @@ -8,24 +8,35 @@ import LogViewer from '../Logs/viewer'; import { strings } from '../locale'; class CollectionLogs extends React.Component { - constructor () { + constructor() { super(); this.displayName = strings.collection_logs; } - render () { + render() { const collectionName = this.props.match.params.name; const { queriedAt } = this.props.logs; return ( -
    -
    +
    +
    -

    {collectionName}

    +

    + {collectionName} +

    - Edit + + Edit + {lastUpdated(queriedAt)}
    - +
    ); } @@ -34,9 +45,11 @@ class CollectionLogs extends React.Component { CollectionLogs.propTypes = { dispatch: PropTypes.func, match: PropTypes.object, - logs: PropTypes.object + logs: PropTypes.object, }; -export default withRouter(connect(state => ({ - logs: state.logs -}))(CollectionLogs)); +export default withRouter( + connect((state) => ({ + logs: state.logs, + }))(CollectionLogs) +); diff --git a/app/src/js/components/Collections/overview.js b/app/src/js/components/Collections/overview.js index a52fdb603..0f1164f39 100644 --- a/app/src/js/components/Collections/overview.js +++ b/app/src/js/components/Collections/overview.js @@ -1,8 +1,9 @@ 'use strict'; +import { get } from 'object-path'; +import PropTypes from 'prop-types'; import React from 'react'; import { connect } from 'react-redux'; -import { withRouter, Link } from 'react-router-dom'; -import PropTypes from 'prop-types'; +import { Link, withRouter } from 'react-router-dom'; import { clearGranulesFilter, clearGranulesSearch, @@ -11,51 +12,51 @@ import { getCollection, getCumulusInstanceMetadata, listGranules, - searchGranules + searchGranules, } from '../../actions'; -import { get } from 'object-path'; import { collectionName as collectionLabelForId, - tally, - lastUpdated, + collectionNameVersion, getCollectionId, - collectionNameVersion + lastUpdated, + tally, } from '../../utils/format'; +import pageSizeOptions from '../../utils/page-size'; +import statusOptions from '../../utils/status'; +import isEqual from 'lodash.isequal'; +import { + reingestAction, + tableColumns, +} from '../../utils/table-config/granules'; +import Breadcrumbs from '../Breadcrumbs/Breadcrumbs'; +import DeleteCollection from '../DeleteCollection/DeleteCollection'; import Dropdown from '../DropDown/dropdown'; import SimpleDropdown from '../DropDown/simple-dropdown'; -import Search from '../Search/search'; -import statusOptions from '../../utils/status'; -import pageSizeOptions from '../../utils/page-size'; -import List from '../Table/Table'; import Bulk from '../Granules/bulk'; -import Overview from '../Overview/overview'; -import { tableColumns, reingestAction } from '../../utils/table-config/granules'; -import { strings } from '../locale'; -import DeleteCollection from '../DeleteCollection/DeleteCollection'; -import Breadcrumbs from '../Breadcrumbs/Breadcrumbs'; import ListFilters from '../ListActions/ListFilters'; +import { strings } from '../locale'; +import Overview from '../Overview/overview'; +import Search from '../Search/search'; +import List from '../Table/Table'; const breadcrumbConfig = [ { label: 'Dashboard Home', - href: '/' + href: '/', }, { label: 'Collections', - href: '/collections' + href: '/collections', }, { label: 'Collection Overview', - active: true - } + active: true, + }, ]; class CollectionOverview extends React.Component { - constructor (props) { + constructor(props) { super(props); - - this.displayName = 'CollectionOverview'; - [ this.changeCollection, this.deleteMe, @@ -64,94 +65,102 @@ class CollectionOverview extends React.Component { this.generateBulkActions, this.gotoGranules, this.load, - this.navigateBack + this.navigateBack, ].forEach((fn) => (this[fn.name] = fn.bind(this))); } - componentDidMount () { + componentDidMount() { this.load(); } - componentDidUpdate (prevProps) { + componentDidUpdate(prevProps) { const { name, version } = this.props.match.params; const { name: prevName, version: prevVersion } = prevProps.match.params; - if (name !== prevName || version !== prevVersion) { + if ( + name !== prevName || + version !== prevVersion || + !isEqual(this.props.datepicker, prevProps.datepicker) + ) { this.load(); } } - load () { + load() { const { name, version } = this.props.match.params; this.props.dispatch(getCumulusInstanceMetadata()); this.props.dispatch(getCollection(name, version)); } - changeCollection (_, collectionId) { + changeCollection(_, collectionId) { const { name, version } = collectionNameVersion(collectionId); this.props.history.push(`/collections/collection/${name}/${version}`); } - generateBulkActions () { + generateBulkActions() { const { granules } = this.props; return [ reingestAction(granules), { - Component: - - } + Component: ( + + ), + }, ]; } - generateQuery () { + generateQuery() { return { - collectionId: getCollectionId(this.props.match.params) + collectionId: getCollectionId(this.props.match.params), }; } - deleteMe () { + deleteMe() { const { name, version } = this.props.match.params; this.props.dispatch(deleteCollection(name, version)); } - navigateBack () { + navigateBack() { this.props.history.push('/collections/all'); } - gotoGranules () { + gotoGranules() { this.props.history.push('/granules'); } - errors () { + errors() { const { name, version } = this.props.match.params; const collectionId = getCollectionId({ name, version }); return [ get(this.props.collections.map, [collectionId, 'error']), - get(this.props.collections.deleted, [collectionId, 'error']) + get(this.props.collections.deleted, [collectionId, 'error']), ].filter(Boolean); } - renderOverview (record) { + renderOverview(record) { const data = get(record, 'data', {}); const stats = get(data, 'stats', {}); const overview = [ [tally(stats.completed), strings.granules_completed], [tally(stats.failed), strings.granules_failed], - [tally(stats.running), strings.granules_running] + [tally(stats.running), strings.granules_running], ]; return ; } - renderDeleteButton () { - const { match: { params }, collections } = this.props; + renderDeleteButton() { + const { + match: { params }, + collections, + } = this.props; const collectionId = getCollectionId(params); const deleteStatus = get(collections.deleted, [collectionId, 'status']); - const hasGranules = get( - collections.map[collectionId], 'data.stats.total', 0) > 0; + const hasGranules = + get(collections.map[collectionId], 'data.stats.total', 0) > 0; return ( id1.localeCompare(id2, 'en', { sensitivity: 'base' })); + (id1, id2) => id1.localeCompare(id2, 'en', { sensitivity: 'base' }) + ); const record = collections.map[collectionId]; // create the overview boxes const overview = record ? this.renderOverview(record) :
    ; return ( -
    -
    +
    +
    • -
      +
    -
    -
    -
      +
      +
      +
      • -

        +

        {collectionLabelForId(collectionId)}

      • Copy @@ -235,35 +242,37 @@ class CollectionOverview extends React.Component {
      • Edit
      • -
      • - {this.renderDeleteButton()} -
      • +
      • {this.renderDeleteButton()}
      - {lastUpdated(get(record, 'data.timestamp'))} + + {lastUpdated(get(record, 'data.timestamp'))} +
      -
      -
      -

      Granule Metrics

      +
      +
      +

      + Granule Metrics +

      {overview}
      -
      -
      -

      +
      +
      +

      {strings.total_granules} - - {meta.count ? ` ${meta.count}` : 0} + + {list.meta.count ? ` ${list.meta.count}` : 0}

      {strings.view_all_granules} @@ -276,35 +285,35 @@ class CollectionOverview extends React.Component { tableColumns={tableColumns} query={this.generateQuery()} bulkActions={this.generateBulkActions()} - rowId='granuleId' - sortIdx='timestamp' + rowId="granuleId" + sortId="timestamp" > @@ -315,16 +324,22 @@ class CollectionOverview extends React.Component { } } +CollectionOverview.displayName = 'CollectionOverview'; + CollectionOverview.propTypes = { - match: PropTypes.object, - history: PropTypes.object, + collections: PropTypes.object, + datepicker: PropTypes.object, dispatch: PropTypes.func, granules: PropTypes.object, - collections: PropTypes.object, - router: PropTypes.object + history: PropTypes.object, + match: PropTypes.object, + router: PropTypes.object, }; -export default withRouter(connect(state => ({ - collections: state.collections, - granules: state.granules -}))(CollectionOverview)); +export default withRouter( + connect((state) => ({ + collections: state.collections, + datepicker: state.datepicker, + granules: state.granules, + }))(CollectionOverview) +); diff --git a/app/src/js/components/Datepicker/Datepicker.js b/app/src/js/components/Datepicker/Datepicker.js index 738c01433..c7900b13f 100644 --- a/app/src/js/components/Datepicker/Datepicker.js +++ b/app/src/js/components/Datepicker/Datepicker.js @@ -40,7 +40,7 @@ const updateDatepickerStateFromQueryParams = (props) => { values.dateRange = dropdownValue(values); props.dispatch({ - type: 'DATEPICKER_DATECHANGE', + type: DATEPICKER_DATECHANGE, data: { ...props.datepicker, ...values } }); } @@ -63,6 +63,7 @@ class Datepicker extends React.PureComponent { componentDidMount () { updateDatepickerStateFromQueryParams(this.props); + this.homePageInitialDateTime(); } refresh (e) { @@ -77,6 +78,16 @@ class Datepicker extends React.PureComponent { this.props.dispatch(this.dispatchDropdownUpdate(value, label)); } + homePageInitialDateTime () { + const { location, queryParams } = this.props; + if (location.pathname === '/' && queryParams.new_session === 'true') { + queryParams.new_session = undefined; + this.props.setQueryParams(queryParams); + const { value, label } = allDateRanges.find((a) => a.label === 'Recent'); + this.props.dispatch(this.dispatchDropdownUpdate(value, label)); + } + } + dispatchDropdownUpdate (value, label) { return (dispatch, getState) => { dispatch({ @@ -124,11 +135,7 @@ class Datepicker extends React.PureComponent { const updatedQueryParams = { ...this.props.queryParams }; urlDateProps.map((time) => { let urlValue; - // If user selects 'Recent', drop the start and end date/time query - // parameters, otherwise on the next navigation, the dropdown will switch - // back to 'Custom'. Excluding these query params ensures that 'Recent' - // remains selected until the user selects otherwise. - if (newProps.dateRange.value !== 'Recent' && newProps[time] !== null) { + if (newProps[time] !== null) { urlValue = moment.utc(newProps[time]).format(urlDateFormat); } updatedQueryParams[time] = urlValue; @@ -281,7 +288,8 @@ Datepicker.propTypes = { setQueryParams: PropTypes.func, onChange: PropTypes.func, dispatch: PropTypes.func, - hideWrapper: PropTypes.bool + hideWrapper: PropTypes.bool, + location: PropTypes.object }; export default withRouter( diff --git a/app/src/js/components/DropDown/dropdown-async-command.js b/app/src/js/components/DropDown/dropdown-async-command.js index ebe267db9..137393b50 100644 --- a/app/src/js/components/DropDown/dropdown-async-command.js +++ b/app/src/js/components/DropDown/dropdown-async-command.js @@ -12,7 +12,8 @@ class DropdownAsync extends React.Component { this.onOutsideClick = this.onOutsideClick.bind(this); this.toggleActions = this.toggleActions.bind(this); this.close = this.close.bind(this); - this.onSuccess = this.onSuccess.bind(this); + this.handleSuccess = this.handleSuccess.bind(this); + this.handleError = this.handleError.bind(this); } componentDidMount () { @@ -24,22 +25,31 @@ class DropdownAsync extends React.Component { } onOutsideClick (e) { - if (!findDOMNode(this).contains(e.target)) this.setState({ showActions: false }); + if (!findDOMNode(this).contains(e.target)) this.close(); } toggleActions (e) { e.preventDefault(); e.stopPropagation(); - this.setState({ showActions: !this.state.showActions }); + this.setState((prevState) => { + return { + showActions: !prevState.showActions + }; + }); } close () { this.setState({ showActions: false }); } - onSuccess (success) { - this.setState({ showActions: false }); - if (typeof success === 'function') success(); + handleSuccess (onSuccess) { + this.close(); + if (typeof onSuccess === 'function') onSuccess(); + } + + handleError (onError) { + this.close(); + if (typeof onError === 'function') onError(); } render () { @@ -53,8 +63,8 @@ class DropdownAsync extends React.Component { })}> {config.map(d =>
    • this.onSuccess(d.success)} - error={this.close} + success={() => this.handleSuccess(d.success)} + error={() => this.handleError(d.error)} status={d.status} disabled={d.disabled} confirmAction={d.confirmAction} diff --git a/app/src/js/components/Executions/execution-events.js b/app/src/js/components/Executions/execution-events.js index 42fdca712..d71632b8b 100644 --- a/app/src/js/components/Executions/execution-events.js +++ b/app/src/js/components/Executions/execution-events.js @@ -59,7 +59,7 @@ class ExecutionEvents extends React.Component { dispatch={this.props.dispatch} tableColumns={tableColumns} rowId='id' - sortIdx='id' + sortId='id' props={[]} order='asc' /> diff --git a/app/src/js/components/Executions/overview.js b/app/src/js/components/Executions/overview.js index 1ecbb5baa..089364e73 100644 --- a/app/src/js/components/Executions/overview.js +++ b/app/src/js/components/Executions/overview.js @@ -107,7 +107,7 @@ class ExecutionOverview extends React.Component { tableColumns={tableColumns} query={{}} rowId='name' - sortIdx='createdAt' + sortId='createdAt' > { - draftState.inputs[inputId].value = value; - draftState.dirty[inputId] = true; + this.setState(createNextState((state) => { + state.inputs[inputId].value = value; + state.dirty[inputId] = true; // validate the field for live changes this.validateField({ field: this.state.inputs[inputId], inputId, - draftState + state }); })); } @@ -122,9 +122,9 @@ export class Form extends React.Component { validateField ({ field, inputId, - draftState + state }) { - const { dirty, inputs, errors } = draftState; + const { dirty, inputs, errors } = state; let { value } = inputs[inputId]; // don't set a value for values that haven't changed and aren't required @@ -152,7 +152,7 @@ export class Form extends React.Component { const error = field.error || field.validationError || t.errors.generic; inputs[inputId].error = error; } else if (inputs[inputId].error) { - draftState.errors = errors.filter(item => item !== field.labelText); + state.errors = errors.filter(item => item !== field.labelText); delete inputs[inputId].error; } } @@ -171,18 +171,18 @@ export class Form extends React.Component { // validate input values in the store - this.setState(createNextState((draftState) => { + this.setState(createNextState((state) => { this.props.inputMeta.forEach(field => { const inputId = generateComponentId(field.schemaProperty, this.id); this.validateField({ field, inputId, - draftState + state }); }); - draftState.submitted = true; + state.submitted = true; })); } diff --git a/app/src/js/components/Form/_form.scss b/app/src/js/components/Form/_form.scss index 9eec3ce51..fa9964a2a 100644 --- a/app/src/js/components/Form/_form.scss +++ b/app/src/js/components/Form/_form.scss @@ -227,7 +227,7 @@ select option{ display: block; } .metadata__updated { - padding-top: 18px; + margin-top: 18px; padding-right: 4px; &:hover { text-decoration: underline; diff --git a/app/src/js/components/Granules/granule.js b/app/src/js/components/Granules/granule.js index e4c6074f6..ff1c4c684 100644 --- a/app/src/js/components/Granules/granule.js +++ b/app/src/js/components/Granules/granule.js @@ -30,7 +30,7 @@ import Loading from '../LoadingIndicator/loading-indicator'; import LogViewer from '../Logs/viewer'; import ErrorReport from '../Errors/report'; import Metadata from '../Table/Metadata'; -import AsyncCommands from '../DropDown/dropdown-async-command'; +import DropdownAsync from '../DropDown/dropdown-async-command'; import _config from '../../config'; import { strings } from '../locale'; import { workflowOptionNames } from '../../selectors'; @@ -81,7 +81,7 @@ const metaAccessors = [ { label: `${strings.cmr} Link`, property: 'cmrLink', - accesssor: (d) => d ? Link : nullValue + accessor: (d) => d ? link : nullValue }, { label: 'Execution', @@ -119,7 +119,6 @@ class GranuleOverview extends React.Component { this.errors = this.errors.bind(this); this.selectWorkflow = this.selectWorkflow.bind(this); this.getExecuteOptions = this.getExecuteOptions.bind(this); - this.displayName = strings.granule; this.state = {}; } @@ -274,7 +273,7 @@ class GranuleOverview extends React.Component {
    • {granuleId}

      - + {lastUpdated(granule.createdAt, 'Created')}
      @@ -328,6 +327,8 @@ GranuleOverview.defaultProps = { skipReloadOnMount: false }; +GranuleOverview.displayName = strings.granule; + export { GranuleOverview }; export default withRouter(connect(state => ({ diff --git a/app/src/js/components/Granules/list.js b/app/src/js/components/Granules/list.js index 72e0b96df..bd404ebdf 100644 --- a/app/src/js/components/Granules/list.js +++ b/app/src/js/components/Granules/list.js @@ -118,7 +118,7 @@ class AllGranules extends React.Component { const view = this.getView(); const displayCaseView = displayCase(view); const statusOpts = (view === 'all') ? statusOptions : null; - const tableSortIdx = view === 'failed' ? 'granuleId' : 'timestamp'; + const tablesortId = view === 'failed' ? 'granuleId' : 'timestamp'; const breadcrumbConfig = [ { label: 'Dashboard Home', @@ -154,7 +154,7 @@ class AllGranules extends React.Component { query={query} bulkActions={this.generateBulkActions()} rowId='granuleId' - sortIdx={tableSortIdx} + sortId={tablesortId} > { + const { granuleCSV } = this.props; + const { data } = granuleCSV; + const csvData = new Blob([data], { type: 'text/csv' }); + + const link = document.createElement('a'); + link.setAttribute('download', 'granules.csv'); + const url = window.URL.createObjectURL(csvData); + link.href = url; + document.body.appendChild(link); + link.click(); + link.parentNode.removeChild(link); + }); + } + render () { - const { stats, granules, granuleCSV, dispatch } = this.props; + const { stats, granules, dispatch } = this.props; const { list, dropdowns } = granules; const { count, queriedAt } = list.meta; - const { data } = granuleCSV; - const csvData = data ? new Blob([data], { type: 'text/csv' }) : null; const statsCount = get(stats, 'count.data.granules.count', []); const overviewItems = statsCount.map(d => [tally(d.count), displayCase(d.key)]); return ( @@ -151,12 +166,10 @@ class GranulesOverview extends React.Component {

      {strings.granules} {count ? ` ${tally(count)}` : 0}

      - {csvData && - Download Granule List} + Download Granule List
      true; class GranuleRecipe extends React.Component { constructor () { super(); - this.displayName = 'GranuleRecipe'; this.navigateBack = this.navigateBack.bind(this); this.reprocess = this.reprocess.bind(this); this.reingest = this.reingest.bind(this); @@ -140,7 +139,7 @@ class GranuleRecipe extends React.Component {

      {granuleId}

      - + {lastUpdated(granule.queriedAt)}
      diff --git a/app/src/js/components/Header/header.js b/app/src/js/components/Header/header.js index 2356212d8..ba95fdb3b 100644 --- a/app/src/js/components/Header/header.js +++ b/app/src/js/components/Header/header.js @@ -33,8 +33,10 @@ class Header extends React.Component { componentDidMount () { const { dispatch, api } = this.props; - if (api.authenticated) dispatch(getApiVersion()); - dispatch(getCumulusInstanceMetadata()); + if (api.authenticated) { + dispatch(getApiVersion()); + dispatch(getCumulusInstanceMetadata()); + } } logout () { diff --git a/app/src/js/components/Operations/overview.js b/app/src/js/components/Operations/overview.js index 50ef5b8aa..1bd5cc473 100644 --- a/app/src/js/components/Operations/overview.js +++ b/app/src/js/components/Operations/overview.js @@ -118,7 +118,7 @@ class OperationOverview extends React.Component { tableColumns={tableColumns} query={this.generateQuery()} rowId='id' - sortIdx='createdAt' + sortId='createdAt' > diff --git a/app/src/js/components/Overview/overview.js b/app/src/js/components/Overview/overview.js index 9b79b05ca..7bf54dee8 100644 --- a/app/src/js/components/Overview/overview.js +++ b/app/src/js/components/Overview/overview.js @@ -4,21 +4,22 @@ import PropTypes from 'prop-types'; import Loading from '../LoadingIndicator/loading-indicator'; class Overview extends React.Component { - constructor () { + constructor() { super(); this.displayName = 'Overview'; } - render () { + render() { const { inflight, items } = this.props; return ( -
      +
      {inflight ? : null}
        - {items.map(d => ( + {items.map((d) => (
      • - - {d[0]}{d[1]} + + {d[0]} + {d[1]}
      • ))} @@ -30,7 +31,7 @@ class Overview extends React.Component { Overview.propTypes = { items: PropTypes.array, - inflight: PropTypes.bool + inflight: PropTypes.bool, }; export default Overview; diff --git a/app/src/js/components/Pagination/pagination.js b/app/src/js/components/Pagination/pagination.js index 5cf135787..07b019780 100644 --- a/app/src/js/components/Pagination/pagination.js +++ b/app/src/js/components/Pagination/pagination.js @@ -9,7 +9,6 @@ const disabled = ' pagination__link--disabled'; class Pagination extends React.Component { constructor () { super(); - this.displayName = 'Pagination'; this.onPageClick = this.onPageClick.bind(this); this.setPage = this.setPage.bind(this); } diff --git a/app/src/js/components/Pagination/simple-pagniation.js b/app/src/js/components/Pagination/simple-pagniation.js new file mode 100644 index 000000000..3589dad35 --- /dev/null +++ b/app/src/js/components/Pagination/simple-pagniation.js @@ -0,0 +1,61 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +/** + * SimplePagination + * @description Component for rendering pagination for tables using the react-table usePagination hook + */ + +const disabled = ' pagination__link--disabled'; + +const SimplePagination = ({ + canPreviousPage, + canNextPage, + pageCount, + gotoPage, + nextPage, + previousPage, + pageOptions, + pageIndex +}) => { + const currentPage = pageIndex + 1; + return ( +
        +
          +
        1. + previousPage()} + >Previous +
        2. + {pageOptions.map(page => { + const pageNumber = page + 1; + return ( +
        3. + gotoPage(page)}>{pageNumber} +
        4. + ); + })} +
        5. + nextPage()} + >Next +
        6. +
        +
        + ); +}; + +SimplePagination.propTypes = { + canPreviousPage: PropTypes.bool, + canNextPage: PropTypes.bool, + pageCount: PropTypes.number, + gotoPage: PropTypes.func, + nextPage: PropTypes.func, + previousPage: PropTypes.func, + pageOptions: PropTypes.array, + pageIndex: PropTypes.number +}; + +export default SimplePagination; diff --git a/app/src/js/components/Pdr/overview.js b/app/src/js/components/Pdr/overview.js index eb89eea76..2373d8e14 100644 --- a/app/src/js/components/Pdr/overview.js +++ b/app/src/js/components/Pdr/overview.js @@ -84,7 +84,7 @@ class PdrOverview extends React.Component { dispatch={this.props.dispatch} action={listPdrs} tableColumns={tableColumns} - sortIdx='timestamp' + sortId='timestamp' query={this.generateQuery()} bulkActions={this.generateBulkActions()} rowId='pdrName' diff --git a/app/src/js/components/Providers/overview.js b/app/src/js/components/Providers/overview.js index b66463a24..45822bdc9 100644 --- a/app/src/js/components/Providers/overview.js +++ b/app/src/js/components/Providers/overview.js @@ -96,7 +96,7 @@ class ProvidersOverview extends React.Component { query={this.generateQuery()} bulkActions={[]} rowId='name' - sortIdx='timestamp' + sortId='timestamp' >

        {providerId}

        - + Edit diff --git a/app/src/js/components/ReconciliationReports/index.js b/app/src/js/components/ReconciliationReports/index.js index 999d5832f..26c2759a5 100644 --- a/app/src/js/components/ReconciliationReports/index.js +++ b/app/src/js/components/ReconciliationReports/index.js @@ -4,30 +4,15 @@ import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import { withRouter, Route, Switch } from 'react-router-dom'; import Sidebar from '../Sidebar/sidebar'; -import { interval, getCount } from '../../actions'; -import _config from '../../config'; +import { strings } from '../locale'; +import { getCount, listReconciliationReports } from '../../actions'; import ReconciliationReportList from './list'; import ReconciliationReport from './reconciliation-report'; -import withQueryParams from 'react-router-query-params'; - -const { updateInterval } = _config; +import DatePickerHeader from '../../components/DatePickerHeader/DatePickerHeader'; class ReconciliationReports extends React.Component { - constructor () { - super(); - this.displayName = 'Reconciliation Reports'; - this.queryParams = this.queryParams.bind(this); - } - - componentDidMount () { - this.cancelInterval = interval(() => this.queryParams(), updateInterval, true); - } - - componentWillUnmount () { - if (this.cancelInterval) { this.cancelInterval(); } - } - - queryParams () { + query() { + this.props.dispatch(listReconciliationReports()); this.props.dispatch(getCount({ type: 'reconciliationReports', field: 'status' @@ -37,11 +22,7 @@ class ReconciliationReports extends React.Component { render () { return (
        -
        -
        -

        Reconciliation Reports

        -
        -
        +
        ({ - stats: state.stats, - reconciliationReports: state.reconciliationReports -}))(ReconciliationReports))); +ReconciliationReports.displayName = 'Reconciliation Reports'; + +export default withRouter(connect()(ReconciliationReports)); diff --git a/app/src/js/components/ReconciliationReports/list.js b/app/src/js/components/ReconciliationReports/list.js index 3728b31d5..6de7c6313 100644 --- a/app/src/js/components/ReconciliationReports/list.js +++ b/app/src/js/components/ReconciliationReports/list.js @@ -80,7 +80,8 @@ class ReconciliationReportList extends React.Component { tableColumns={tableColumns} query={this.generateQuery()} bulkActions={this.generateBulkActions()} - rowId='reconciliationReportName' + rowId='name' + sortId='createdAt' > ({ diff --git a/app/src/js/components/ReconciliationReports/reconciliation-report.js b/app/src/js/components/ReconciliationReports/reconciliation-report.js index f0515c927..6c7714fd0 100644 --- a/app/src/js/components/ReconciliationReports/reconciliation-report.js +++ b/app/src/js/components/ReconciliationReports/reconciliation-report.js @@ -8,10 +8,8 @@ import { connect } from 'react-redux'; import { withRouter } from 'react-router-dom'; import { - interval, getReconciliationReport } from '../../actions'; -import _config from '../../config'; import { tableColumnsS3Files, tableColumnsFiles, @@ -19,22 +17,11 @@ import { tableColumnsGranules } from '../../utils/table-config/reconciliation-reports'; -import Metadata from '../Table/Metadata'; import Loading from '../LoadingIndicator/loading-indicator'; import ErrorReport from '../Errors/report'; -import ReportTable from './report-table'; - -const { updateInterval } = _config; - -const reportMetaAccessors = [ - { label: 'Created', property: 'reportStartTime' }, - { label: 'Status', property: 'status' }, - { label: 'Files in DynamoDB and S3', property: 'filesInCumulus.okCount' }, - { label: 'Collections in Cumulus and CMR', property: 'collectionsInCumulusCmr.okCount' }, - { label: 'Granules in Cumulus and CMR', property: 'granulesInCumulusCmr.okCount' }, - { label: 'Granule files in Cumulus and CMR', property: 'filesInCumulusCmr.okCount' } -]; +import TableCards from './table-cards'; +import SortableTable from '../SortableTable/SortableTable'; const parseFileObject = (d) => { const parsed = url.parse(d.uri); @@ -49,25 +36,19 @@ const parseFileObject = (d) => { class ReconciliationReport extends React.Component { constructor () { super(); - this.reload = this.reload.bind(this); this.navigateBack = this.navigateBack.bind(this); + this.handleCardClick = this.handleCardClick.bind(this); + this.state = { + activeIdx: 0 + }; } componentDidMount () { - const { reconciliationReportName } = this.props.match.params; - const immediate = !this.props.reconciliationReports.map[reconciliationReportName]; - this.reload(immediate); - } - - componentWillUnmount () { - if (this.cancelInterval) { this.cancelInterval(); } - } - - reload (immediate) { - const { reconciliationReportName } = this.props.match.params; - const { dispatch } = this.props; - if (this.cancelInterval) { this.cancelInterval(); } - this.cancelInterval = interval(() => dispatch(getReconciliationReport(reconciliationReportName)), updateInterval, immediate); + const { dispatch, match, reconciliationReports } = this.props; + const { reconciliationReportName } = match.params; + if (!reconciliationReports.map[reconciliationReportName]) { + dispatch(getReconciliationReport(reconciliationReportName)); + } } navigateBack () { @@ -131,9 +112,15 @@ class ReconciliationReport extends React.Component { return { granuleFilesOnlyInCumulus, granuleFilesOnlyInCmr }; } + handleCardClick (e, index) { + e.preventDefault(); + this.setState({ activeIdx: index }); + } + render () { const { reconciliationReports } = this.props; const { reconciliationReportName } = this.props.match.params; + const { activeIdx } = this.state; const record = reconciliationReports.map[reconciliationReportName]; @@ -154,8 +141,9 @@ class ReconciliationReport extends React.Component { let granulesInCmr = []; let report; + let error; - if (record && record.data) { + if (record.data) { report = record.data; const { @@ -184,13 +172,61 @@ class ReconciliationReport extends React.Component { granuleFilesOnlyInCumulus, granuleFilesOnlyInCmr } = this.getGranuleFilesSummary(filesInCumulusCmr)); - } - let error; - if (record && record.data) { error = record.data.error; } + const cardConfig = [ + { + id: 'dynamo', + name: 'DynamoDB', + data: filesInDynamoDb, + columns: tableColumnsFiles, + }, + { + id: 's3', + name: 'S3', + data: filesInS3, + columns: tableColumnsS3Files + }, + { + id: 'cumulusCollections', + name: 'Cumulus Collections', + data: collectionsInCumulus, + columns: tableColumnsCollections + }, + { + id: 'cmrCollections', + name: 'CMR Collections', + data: collectionsInCmr, + columns: tableColumnsCollections + }, + { + id: 'cumulusGranules', + name: 'Cumulus Granules', + data: granulesInCumulus, + columns: tableColumnsGranules + }, + { + id: 'cmrGranules', + name: 'CMR Granules', + data: granulesInCmr, + columns: tableColumnsGranules + }, + { + id: 'cumulusGranules', + name: 'Cumulus Only Granules', + data: granuleFilesOnlyInCumulus, + columns: tableColumnsFiles + }, + { + id: 'cmrGranules', + name: 'CMR Only Granules', + data: granuleFilesOnlyInCmr, + columns: tableColumnsFiles + } + ]; + return (
        @@ -200,60 +236,16 @@ class ReconciliationReport extends React.Component {
        -
        -
        -
        -

        Reconciliation report

        -
        - -
        - - - - - - - - - - - - +
        + +
        - +
        -
      diff --git a/app/src/js/components/ReconciliationReports/report-table.js b/app/src/js/components/ReconciliationReports/report-table.js deleted file mode 100644 index ea4d90302..000000000 --- a/app/src/js/components/ReconciliationReports/report-table.js +++ /dev/null @@ -1,63 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import Collapsible from 'react-collapsible'; - -import SortableTable from '../SortableTable/SortableTable'; - -const ReportTable = ({ - collapsible, - collapseThreshold, - data, - title, - tableColumns -}) => { - if (!data || !data.length) { - return null; - } - - const shouldCollapse = collapsible && data.length > collapseThreshold; - - let reportTable = ( - - ); - - if (shouldCollapse) { - reportTable = ( - - { reportTable } - - ); - } - - return ( -
      -

      - {title} ({data.length}) -

      - { reportTable } -
      - ); -}; - -ReportTable.propTypes = { - collapsible: PropTypes.bool, - collapseThreshold: PropTypes.number, - data: PropTypes.array, - title: PropTypes.string, - tableColumns: PropTypes.array -}; - -ReportTable.defaultProps = { - collapsible: true, - collapseThreshold: 10 -}; - -export default ReportTable; diff --git a/app/src/js/components/ReconciliationReports/table-cards.js b/app/src/js/components/ReconciliationReports/table-cards.js new file mode 100644 index 000000000..069b96982 --- /dev/null +++ b/app/src/js/components/ReconciliationReports/table-cards.js @@ -0,0 +1,52 @@ +'use strict'; + +import React from 'react'; +import PropTypes from 'prop-types'; + +import { Card } from 'react-bootstrap'; + +const TableCards = ({ + config, + onClick, + activeCard +}) => { + function handleCardClick (e, index) { + e.preventDefault(); + if (typeof onClick === 'function') { + onClick(e, index); + } + } + + return ( +
      + {config.map((item, index) => { + // TODO: once API is updated with status indicator, remove the default + const { name, data, status = 'success' } = item; + const count = data.length; + if (!data || !count) { + return null; + } + return ( + handleCardClick(e, index)}> + {name} + + {count} + + + {status} + + + + ); + })} +
      + ); +}; + +TableCards.propTypes = { + config: PropTypes.array, + onClick: PropTypes.func, + activeCard: PropTypes.number +}; + +export default TableCards; diff --git a/app/src/js/components/ReingestGranules/BatchReingestCompleteContent.js b/app/src/js/components/ReingestGranules/BatchReingestCompleteContent.js index 7dd128a3c..6ae64cce4 100644 --- a/app/src/js/components/ReingestGranules/BatchReingestCompleteContent.js +++ b/app/src/js/components/ReingestGranules/BatchReingestCompleteContent.js @@ -1,42 +1,71 @@ import React from 'react'; import PropTypes from 'prop-types'; +import { Alert } from 'react-bootstrap'; +import Collapsible from 'react-collapsible'; export const maxDisplayed = 10; const BatchReingestCompleteContent = ({ results, - error + errors }) => { const confirmation = () => `successfully reingested ${results.length > 1 ? 'these' : 'this'} granule${results.length > 1 ? 's' : ''}`; - const displayedItems = () => { + + const displayedItems = (array, renderContent) => { const items = []; - for (let i = 0; i < Math.min(results.length, maxDisplayed); i++) { - items.push(
    • {results[i]}
    • ); + for (let i = 0; i < Math.min(array.length, maxDisplayed); i++) { + items.push(renderContent(i, array[i])); } - if (results.length > maxDisplayed) { - items.push(
    • and {results.length - maxDisplayed} more.
    • ); + if (array.length > maxDisplayed) { + items.push(
    • and {array.length - maxDisplayed} more.
    • ); } return items; }; + const renderResults = (index, result) => { + return
    • {result}
    • ; + }; + + const renderErrors = (index, error) => { + const { id, error: errorMessage } = error; + return ( +
    • + {id} + + {errorMessage.toString()} + +
    • + ); + }; + return ( <> {(results && results.length > 0) && - <> -

      {confirmation()}

      -
        - {displayedItems()} -
      - + <> + Success: Your request has been processed. +

      {confirmation()}

      +
        + {displayedItems(results, renderResults)} +
      +

      To quickly view the status, click "View Granules".

      + + } + {(errors && errors.length > 0) && + <> + Error: There is an issue with the request. +
        + {displayedItems(errors, renderErrors)} +
      +

      To return to granules page, click "Close".

      + } - {error && {error}} ); }; BatchReingestCompleteContent.propTypes = { results: PropTypes.array, - error: PropTypes.string + errors: PropTypes.array }; export default BatchReingestCompleteContent; diff --git a/app/src/js/components/ReingestGranules/BatchReingestConfirmContent.js b/app/src/js/components/ReingestGranules/BatchReingestConfirmContent.js index 4eeccf545..52dfbfd8a 100644 --- a/app/src/js/components/ReingestGranules/BatchReingestConfirmContent.js +++ b/app/src/js/components/ReingestGranules/BatchReingestConfirmContent.js @@ -1,5 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; +import { Alert } from 'react-bootstrap'; export const maxDisplayed = 10; @@ -8,7 +9,7 @@ const BatchReingestConfirmContent = ({ selected = [] }) => { const these = isMultiple ? 'These' : 'This'; const s = isMultiple ? 's' : ''; const requestText = `You have submitted a request to reingest the following granule${s}.`; - const confirmText = `Are you sure that you want to reingest ${these.toLowerCase()} granule${s}?\nNote: ${these} granule file${s} will be overwritten.`; + const confirmText = `Are you sure that you want to reingest ${these.toLowerCase()} granule${s}?`; const displayedItems = () => { const items = []; @@ -23,6 +24,7 @@ const BatchReingestConfirmContent = ({ selected = [] }) => { return ( <> + Attention: {`The selected granule${s} will be overwritten`} {requestText}
        {displayedItems()} diff --git a/app/src/js/components/Rules/overview.js b/app/src/js/components/Rules/overview.js index 9d30d0f24..d0aaeb6f4 100644 --- a/app/src/js/components/Rules/overview.js +++ b/app/src/js/components/Rules/overview.js @@ -70,7 +70,7 @@ class RulesOverview extends React.Component { action={listRules} tableColumns={tableColumns} query={{}} - sortIdx='timestamp' + sortId='timestamp' bulkActions={this.generateBulkActions()} rowId='name' > diff --git a/app/src/js/components/Rules/rule.js b/app/src/js/components/Rules/rule.js index 250e46b9b..a5e68d856 100644 --- a/app/src/js/components/Rules/rule.js +++ b/app/src/js/components/Rules/rule.js @@ -25,7 +25,7 @@ import { } from '../../actions'; import Loading from '../LoadingIndicator/loading-indicator'; import Metadata from '../Table/Metadata'; -import AsyncCommands from '../DropDown/dropdown-async-command'; +import DropdownAsync from '../DropDown/dropdown-async-command'; import ErrorReport from '../Errors/report'; import Breadcrumbs from '../Breadcrumbs/Breadcrumbs'; @@ -117,7 +117,8 @@ class Rule extends React.Component { get(rules.map, [ruleName, 'error']), get(rules.deleted, [ruleName, 'error']), get(rules.enabled, [ruleName, 'error']), - get(rules.disabled, [ruleName, 'error']) + get(rules.disabled, [ruleName, 'error']), + get(rules.rerun, [ruleName, 'error']) ].filter(Boolean); } @@ -181,7 +182,7 @@ class Rule extends React.Component {

        {ruleName}

        - + Edit Rule - {lastUpdated(data.timestamp)} + {lastUpdated(data.timestamp || data.updatedAt)}
        - {errors.length ? : null} + {errors.length > 0 && }

        Rule Overview

        - {data.state ? ( + {data.state && (
        State:
        {displayCase(data.state)}
        - ) : null} + )}
        diff --git a/app/src/js/components/SortableTable/SortableTable.js b/app/src/js/components/SortableTable/SortableTable.js index 26563660c..033397cd4 100644 --- a/app/src/js/components/SortableTable/SortableTable.js +++ b/app/src/js/components/SortableTable/SortableTable.js @@ -6,7 +6,8 @@ import React, { useRef } from 'react'; import PropTypes from 'prop-types'; -import { useTable, useResizeColumns, useFlexLayout, useSortBy, useRowSelect } from 'react-table'; +import { useTable, useResizeColumns, useFlexLayout, useSortBy, useRowSelect, usePagination } from 'react-table'; +import SimplePagination from '../Pagination/simple-pagniation'; /** * IndeterminateCheckbox @@ -34,7 +35,8 @@ IndeterminateCheckbox.propTypes = { }; const SortableTable = ({ - sortIdx, + sortId, + initialSortId, rowId, order = 'desc', canSelect, @@ -42,7 +44,8 @@ const SortableTable = ({ tableColumns = [], data = [], onSelect, - clearSelected + clearSelected, + shouldUsePagination = false }) => { const defaultColumn = useMemo( () => ({ @@ -54,7 +57,7 @@ const SortableTable = ({ [] ); - const shouldManualSort = !!sortIdx; + const shouldManualSort = !!initialSortId; const { getTableProps, @@ -63,9 +66,18 @@ const SortableTable = ({ headerGroups, state: { selectedRowIds, - sortBy + sortBy, + pageIndex }, - toggleAllRowsSelected + toggleAllRowsSelected, + page, + canPreviousPage, + canNextPage, + pageCount, + pageOptions, + gotoPage, + nextPage, + previousPage } = useTable( { data, @@ -74,12 +86,14 @@ const SortableTable = ({ getRowId: (row, relativeIndex) => typeof rowId === 'function' ? rowId(row) : row[rowId] || relativeIndex, autoResetSelectedRows: false, autoResetSortBy: false, - manualSortBy: shouldManualSort + manualSortBy: shouldManualSort, + manualPagination: !shouldUsePagination, // if we want to use the pagination hook, then pagination should not be manual }, useFlexLayout, // this allows table to have dynamic layouts outside of standard table markup useResizeColumns, // this allows for resizing columns useSortBy, // this allows for sorting useRowSelect, // this allows for checkbox in table + usePagination, hooks => { if (canSelect) { hooks.visibleColumns.push(columns => [ @@ -88,8 +102,8 @@ const SortableTable = ({ Header: ({ getToggleAllRowsSelectedProps }) => ( // eslint-disable-line react/prop-types ), - Cell: ({ row }) => ( - + Cell: ({ row }) => ( // eslint-disable-line react/prop-types + // eslint-disable-line react/prop-types ), minWidth: 61, width: 61, @@ -101,6 +115,8 @@ const SortableTable = ({ } ); + const tableRows = page || rows; + useEffect(() => { if (clearSelected) { toggleAllRowsSelected(false); @@ -127,11 +143,11 @@ const SortableTable = ({ if (typeof desc !== 'undefined') { sortOrder = desc ? 'desc' : 'asc'; } - const sortFieldId = id || sortIdx; + const sortFieldId = id || sortId; if (typeof changeSortProps === 'function') { - changeSortProps({ sortIdx: sortFieldId, order: sortOrder || order }); + changeSortProps({ sortId: sortFieldId, order: sortOrder || order }); } - }, [changeSortProps, sortBy, sortIdx, order]); + }, [changeSortProps, sortBy, sortId, order]); return (
        @@ -142,9 +158,13 @@ const SortableTable = ({ {headerGroups.map(headerGroup => (
        {headerGroup.headers.map(column => { + let columnClassName = ''; + if (column.canSort) { + columnClassName = `table__sort${column.isSortedDesc === true ? '--desc' : (column.isSortedDesc === false ? '--asc' : '')}`; + } return (
        -
        +
        {column.render('Header')}
        - {rows.map((row, i) => { + {tableRows.map((row, i) => { prepareRow(row); return (
        @@ -182,25 +202,34 @@ const SortableTable = ({ })}
        + {shouldUsePagination && + }
        ); }; SortableTable.propTypes = { - primaryIdx: PropTypes.number, data: PropTypes.array, - header: PropTypes.array, order: PropTypes.string, - row: PropTypes.array, - sortIdx: PropTypes.string, + sortId: PropTypes.string, + initialSortId: PropTypes.string, changeSortProps: PropTypes.func, onSelect: PropTypes.func, canSelect: PropTypes.bool, - collapsible: PropTypes.bool, rowId: PropTypes.any, tableColumns: PropTypes.array, - clearSelected: PropTypes.bool + clearSelected: PropTypes.bool, + shouldUsePagination: PropTypes.bool }; export default SortableTable; diff --git a/app/src/js/components/Table/Table.js b/app/src/js/components/Table/Table.js index c4a9a1945..d735ec8c7 100644 --- a/app/src/js/components/Table/Table.js +++ b/app/src/js/components/Table/Table.js @@ -16,7 +16,6 @@ import ListActions from '../ListActions/ListActions'; class List extends React.Component { constructor (props) { super(props); - this.displayName = 'List'; this.queryNewPage = this.queryNewPage.bind(this); this.queryNewSort = this.queryNewSort.bind(this); this.updateSelection = this.updateSelection.bind(this); @@ -25,12 +24,12 @@ class List extends React.Component { this.getQueryConfig = this.getQueryConfig.bind(this); const initialPage = 1; - const initialSortIdx = props.sortIdx || 0; + const initialSortId = props.sortId; const initialOrder = 'desc'; this.state = { page: initialPage, - sortIdx: initialSortIdx, + sortId: initialSortId, order: initialOrder, selected: [], clearSelected: false, @@ -38,7 +37,7 @@ class List extends React.Component { queryConfig: { page: initialPage, order: initialOrder, - sort_by: initialSortIdx, + sort_by: initialSortId, ...(props.query || {}) }, params: {}, @@ -79,7 +78,7 @@ class List extends React.Component { ...sortProps, queryConfig: this.getQueryConfig({ order: sortProps.order, - sort_by: sortProps.sortIdx + sort_by: sortProps.sortId }), clearSelected: true }); @@ -117,7 +116,7 @@ class List extends React.Component { return omitBy({ page: this.state.page, order: this.state.order, - sort_by: this.state.sortIdx, + sort_by: this.state.sortId, ...this.state.params, ...config, ...query @@ -131,6 +130,7 @@ class List extends React.Component { children, bulkActions, rowId, + sortId: initialSortId, list, tableColumns, data @@ -140,7 +140,7 @@ class List extends React.Component { const tableData = data || listData; const { page, - sortIdx, + sortId, order, selected, clearSelected, @@ -182,7 +182,8 @@ class List extends React.Component { canSelect={hasActions} rowId={rowId} onSelect={this.updateSelection} - sortIdx={sortIdx} + initialSortId={initialSortId} + sortId={sortId} changeSortProps={this.queryNewSort} order={order} clearSelected={clearSelected} @@ -206,7 +207,7 @@ List.propTypes = { dispatch: PropTypes.func, action: PropTypes.func, children: PropTypes.node, - sortIdx: PropTypes.string, + sortId: PropTypes.string, query: PropTypes.object, bulkActions: PropTypes.array, rowId: PropTypes.any, diff --git a/app/src/js/components/Timer/timer.js b/app/src/js/components/Timer/timer.js index 7fa282fa7..8a8adda55 100644 --- a/app/src/js/components/Timer/timer.js +++ b/app/src/js/components/Timer/timer.js @@ -1,92 +1,122 @@ 'use strict'; import React from 'react'; +import { connect } from 'react-redux'; import PropTypes from 'prop-types'; import _config from '../../config'; +import { + TIMER_START, + TIMER_STOP, + TIMER_SET_COUNTDOWN, +} from '../../actions/types'; +import isEqual from 'lodash.isequal'; const { updateInterval } = _config; -const delay = updateInterval / 1000; +const oneSecondTick = 1000; +const secondsToRefresh = updateInterval / 1000; class Timer extends React.Component { - constructor () { - super(); - this.state = { - running: true, - seconds: delay - }; + constructor(props) { + super(props); this.stop = this.stop.bind(this); this.start = this.start.bind(this); this.toggle = this.toggle.bind(this); - this.createTimer = this.createTimer.bind(this); + this.refreshTimer = this.refreshTimer.bind(this); this.interval = this.interval.bind(this); this.parentClass = this.parentClass.bind(this); } - componentDidMount () { - this.createTimer(this.props.config); + componentDidMount() { + this.refreshTimer(this.props.config); } - componentDidUpdate (prevProps) { - if (JSON.stringify(prevProps.config) !== JSON.stringify(this.props.config) || - (this.props.reload && prevProps.reload !== this.props.reload)) { - this.createTimer(this.props.config); + componentDidUpdate(prevProps) { + if ( + JSON.stringify(prevProps.config) !== JSON.stringify(this.props.config) || + (this.props.reload && prevProps.reload !== this.props.reload) || + !isEqual(prevProps.datepicker, this.props.datepicker) + ) { + this.refreshTimer(this.props.config); } } - componentWillUnmount () { - if (this.cancelInterval) { this.cancelInterval(); } + componentWillUnmount() { + if (this.cancelInterval) { + this.cancelInterval(); + } } - stop () { - if (this.cancelInterval) { this.cancelInterval(); } - this.setState({ seconds: -1, running: false }); + stop() { + if (this.cancelInterval) { + this.cancelInterval(); + } + this.props.dispatch({ type: TIMER_STOP }); } - start () { - this.setState({ seconds: 0, running: true }); - this.createTimer(this.props.config); + start() { + this.props.dispatch({ type: TIMER_START, secondsToRefresh }); + this.refreshTimer(this.props.config, true); } - toggle () { - if (this.state.running) this.stop(); - else this.start(); + toggle() { + this.props.timer.running ? this.stop() : this.start(); } - createTimer (config) { - if (this.cancelInterval) { this.cancelInterval(); } + refreshTimer(config, startCountdown) { + if (this.cancelInterval) { + this.cancelInterval(); + } const { dispatch, action } = this.props; - this.cancelInterval = this.interval(() => dispatch(action(config)), delay); + dispatch(action(config)); + if (this.props.timer.running || startCountdown) { + this.cancelInterval = this.interval( + () => dispatch(action(config)), + secondsToRefresh + ); + } } - interval (action, seconds) { - action(); + interval(action, seconds) { const intervalId = setInterval(() => { - this.setState({ seconds: seconds }); if (seconds === 0) { - seconds = delay; + seconds = secondsToRefresh; action(); } else { seconds -= 1; } - }, 1000); + this.props.dispatch({ + type: TIMER_SET_COUNTDOWN, + secondsToRefresh: seconds, + }); + }, oneSecondTick); return () => clearInterval(intervalId); } - parentClass () { + parentClass() { const className = 'form__element__updateToggle'; - return this.props.noheader ? className + ' form__element__updateToggle-noHeader' : className; + return this.props.noheader + ? className + ' form__element__updateToggle-noHeader' + : className; } - render () { - const { seconds } = this.state; + render() { + const { seconds, running } = this.props.timer; return (
        - this.createTimer(this.props.config)}> - - Next update in: { seconds === -1 ? '-' : seconds } + this.refreshTimer(this.props.config)} + > + + {running ? `Next update in: ${seconds}` : 'Update'} - - {seconds === -1 ? 'Start automatic updates' : 'Stop automatic updates'} + + {running ? 'Stop automatic updates' : 'Start automatic updates'}
        ); @@ -94,11 +124,16 @@ class Timer extends React.Component { } Timer.propTypes = { - noheader: PropTypes.bool, - dispatch: PropTypes.func, action: PropTypes.func, config: PropTypes.object, - reload: PropTypes.any + datepicker: PropTypes.object, + dispatch: PropTypes.func, + noheader: PropTypes.bool, + reload: PropTypes.any, + timer: PropTypes.object, }; -export default Timer; +export default connect((state) => ({ + datepicker: state.datepicker, + timer: state.timer, +}))(Timer); diff --git a/app/src/js/components/Workflows/overview.js b/app/src/js/components/Workflows/overview.js index 7fd44502a..04ed43572 100644 --- a/app/src/js/components/Workflows/overview.js +++ b/app/src/js/components/Workflows/overview.js @@ -46,7 +46,7 @@ const WorkflowOverview = ({ workflows }) => { action={listWorkflows} tableColumns={tableColumns} query={{}} - sortIdx='name' + sortId='name' rowId='name' />
        diff --git a/app/src/js/components/home.js b/app/src/js/components/home.js index fef87ed79..d0d73f7a4 100644 --- a/app/src/js/components/home.js +++ b/app/src/js/components/home.js @@ -55,7 +55,7 @@ class Home extends React.Component { componentDidMount () { const { dispatch } = this.props; - this.cancelInterval = interval(this.query, updateInterval, true); + this.refreshQuery(); dispatch(getCumulusInstanceMetadata()) .then(() => { dispatch(getDistApiGatewayMetrics(this.props.cumulusInstance)); @@ -222,7 +222,7 @@ class Home extends React.Component { dispatch={this.props.dispatch} action={listGranules} tableColumns={errorTableColumns} - sortIdx='timestamp' + sortId='timestamp' query={this.generateQuery()} />
      diff --git a/app/src/js/components/locale.js b/app/src/js/components/locale.js index 74908dbd7..79a4c86a7 100644 --- a/app/src/js/components/locale.js +++ b/app/src/js/components/locale.js @@ -34,6 +34,7 @@ export const strings = new LocalizedStrings({ logo: 'cumulus-logo.png', operations: 'Operations', pdrs: 'Pdrs', + reconciliation_reports: 'Reconciliation Reports', remove_from_cmr: 'Remove from CMR', running_granules: 'Running Granules', total_granules: 'Total Granules', @@ -41,6 +42,7 @@ export const strings = new LocalizedStrings({ view_granules_overview: 'View Granule Overview' }, gitc: { + active_collections: 'Active Layers', add_collection: 'Add Layer', all_collections: 'All Layers', all_granules: 'All Products', @@ -68,8 +70,10 @@ export const strings = new LocalizedStrings({ logo: 'gitc-logo.png', operations: 'Operations', pdrs: 'Pdrs', + reconciliation_reports: 'Reconciliation Reports', remove_from_cmr: 'Remove from OnEarth', running_granules: 'Running Products', + total_granules: 'Total Products', view_all_granules: 'View All Products', view_granules_overview: 'View Product Overview' } diff --git a/app/src/js/components/oauth.js b/app/src/js/components/oauth.js index 6b3f13fe0..d95900966 100644 --- a/app/src/js/components/oauth.js +++ b/app/src/js/components/oauth.js @@ -33,7 +33,7 @@ class OAuth extends React.Component { if (pathname !== '/auth' && window.location && window.location.reload) { setTimeout(() => window.location.reload(), updateDelay); } else if (pathname === '/auth') { - setTimeout(() => this.props.history.push('/'), updateDelay); // react isn't seeing this a function + setTimeout(() => this.props.history.push('/?new_session=true'), updateDelay); // react isn't seeing this a function } } } diff --git a/app/src/js/config/index.js b/app/src/js/config/index.js index 1f37ac0df..917269347 100644 --- a/app/src/js/config/index.js +++ b/app/src/js/config/index.js @@ -10,7 +10,7 @@ const baseConfig = { environment: 'development', requireEarthdataLogin: false, apiRoot: 'https://wjdkfyb6t6.execute-api.us-east-1.amazonaws.com/dev/', - minCompatibleApiVersion: '1.22.1', + minCompatibleApiVersion: '1.23.2', oauthMethod: 'earthdata', graphicsPath: '/src/assets/images/', diff --git a/app/src/js/middleware/request.js b/app/src/js/middleware/request.js index c94b7e827..6083133ce 100644 --- a/app/src/js/middleware/request.js +++ b/app/src/js/middleware/request.js @@ -1,6 +1,3 @@ -import requestPromise from 'request-promise'; - -import { loginError } from '../actions'; import { CALL_API } from '../actions/types'; import { configureRequest, @@ -10,30 +7,28 @@ import { import log from '../utils/log'; import { isValidApiRequestAction } from './validate'; -const handleError = ({ id, type, error, requestAction }, next) => { +// Use require to allow for mocking +const requestPromise = require('request-promise'); +const { loginError } = require('../actions'); + +const handleError = ({ + id, + type, + error, + requestAction, + statusCode +}, next) => { console.groupCollapsed('handleError'); console.log(`id: ${id}`); console.log(`type: ${type}`); console.dir(error); console.dir(requestAction); console.groupEnd(); - if (error.message) { - // Temporary fix until the 'logs' endpoint is fixed - // TODO: is this still relevant? - if (error.message.includes('Invalid Authorization token') && - requestAction.url.includes('logs')) { - const data = { results: [] }; - return next({ id, type, data, config: requestAction }); - } - // Catch the session expired error - // Weirdly error.message shows up as " : Session expired" - // So it's using indexOf instead of a direct comparison - if (error.message.includes('Your session has expired. Please login again.') || - error.message.includes('Invalid Authorization token') || - error.message.includes('Access token has expired')) { - return next(loginError(error.message.replace('Bad Request: ', ''))); - } + // If the error response indicates lack or failure of request + // authorization, then the log user out + if ([401, 403].includes(+statusCode)) { + return next(loginError(error.message)); } const errorType = type + '_ERROR'; @@ -70,11 +65,19 @@ export const requestMiddleware = ({ dispatch, getState }) => next => action => { const start = new Date(); return requestPromise(requestAction) .then((response) => { - const { body } = response; - - if (+response.statusCode >= 400) { + const { body, statusCode } = response; + if (+statusCode >= 400) { const error = new Error(getErrorMessage(response)); - return handleError({ id, type, error, requestAction }, next); + return handleError( + { + id, + type, + error, + requestAction, + statusCode + }, + next + ); } const duration = new Date() - start; diff --git a/app/src/js/reducers/api-version.js b/app/src/js/reducers/api-version.js index 4e5ab11de..db3dc3c5e 100644 --- a/app/src/js/reducers/api-version.js +++ b/app/src/js/reducers/api-version.js @@ -1,10 +1,12 @@ +'use strict'; + +import { createReducer } from '@reduxjs/toolkit'; import { API_VERSION, - API_VERSION_ERROR, API_VERSION_COMPATIBLE, + API_VERSION_ERROR, API_VERSION_INCOMPATIBLE } from '../actions/types'; -import { createReducer } from '@reduxjs/toolkit'; export const initialState = { versionNumber: '', @@ -13,33 +15,20 @@ export const initialState = { }; export default createReducer(initialState, { - [API_VERSION]: (state, action) => { - return { - ...state, - versionNumber: action.payload.versionNumber, - warning: '' - }; + state.versionNumber = action.payload.versionNumber; + state.warning = ''; }, - [API_VERSION_ERROR]: (state, action) => { - return { - ...state, - apiVersion: action.payload.error.message, - warning: 'Failed to acquire Cumulus API Version' - }; + [API_VERSION_COMPATIBLE]: (state) => { + state.isCompatible = true; + state.warning = ''; }, - [API_VERSION_COMPATIBLE]: (state, action) => { - return { - ...state, - isCompatible: true, - warning: '' - }; + [API_VERSION_ERROR]: (state, action) => { + state.apiVersion = action.payload.error.message; + state.warning = 'Failed to acquire Cumulus API Version'; }, [API_VERSION_INCOMPATIBLE]: (state, action) => { - return { - ...state, - isCompatible: false, - warning: action.payload.warning - }; - } + state.isCompatible = false; + state.warning = action.payload.warning; + }, }); diff --git a/app/src/js/reducers/api.js b/app/src/js/reducers/api.js index bd583fe7f..cf7bab08b 100644 --- a/app/src/js/reducers/api.js +++ b/app/src/js/reducers/api.js @@ -1,66 +1,65 @@ 'use strict'; -import { set } from 'object-path'; -import { get as getToken, set as setToken } from '../utils/auth'; +import { get as getToken, set as setToken } from '../utils/auth'; +import { createReducer } from '@reduxjs/toolkit'; import { DELETE_TOKEN, LOGIN, - LOGIN_INFLIGHT, LOGIN_ERROR, + LOGIN_INFLIGHT, LOGOUT, REFRESH_TOKEN, REFRESH_TOKEN_ERROR, REFRESH_TOKEN_INFLIGHT, - SET_TOKEN + SET_TOKEN, } from '../actions/types'; -import { createReducer } from '@reduxjs/toolkit'; export const initialState = { - authenticated: !!getToken(), + authenticated: getToken() !== null, inflight: false, error: null, tokens: { error: null, inflight: false, - token: getToken() - } + token: getToken(), + }, }; export default createReducer(initialState, { [DELETE_TOKEN]: (state) => { - set(state, 'tokens.token', null); setToken(''); + state.tokens.token = null; }, [LOGIN]: (state) => { - set(state, 'authenticated', true); - set(state, 'inflight', false); - }, - [LOGIN_INFLIGHT]: (state) => { - set(state, 'inflight', true); + state.authenticated = true; + state.inflight = false; }, [LOGIN_ERROR]: (state, action) => { - set(state, 'error', action.error); - set(state, 'inflight', false); - set(state, 'authenticated', false); + state.error = action.error; + state.inflight = false; + state.authenticated = false; + }, + [LOGIN_INFLIGHT]: (state) => { + state.inflight = true; }, [LOGOUT]: (state) => { - set(state, 'authenticated', false); + state.authenticated = false; }, [REFRESH_TOKEN]: (state, action) => { - set(state, 'tokens.error', null); - set(state, 'tokens.inflight', false); - set(state, 'tokens.token', action.token); setToken(action.token); + state.tokens.token = action.token; + state.tokens.error = null; + state.tokens.inflight = false; }, [REFRESH_TOKEN_ERROR]: (state, action) => { - set(state, 'tokens.error', action.error); - set(state, 'tokens.inflight', false); + state.tokens.error = action.error; + state.tokens.inflight = false; }, [REFRESH_TOKEN_INFLIGHT]: (state) => { - set(state, 'tokens.inflight', true); + state.tokens.inflight = true; }, [SET_TOKEN]: (state, action) => { - set(state, 'tokens.token', action.token); setToken(action.token); - } + state.tokens.token = action.token; + }, }); diff --git a/app/src/js/reducers/collections.js b/app/src/js/reducers/collections.js index e039ac595..cfb770276 100644 --- a/app/src/js/reducers/collections.js +++ b/app/src/js/reducers/collections.js @@ -1,46 +1,51 @@ 'use strict'; -import { set, del } from 'object-path'; -import assignDate from './assign-date'; +import { createReducer } from '@reduxjs/toolkit'; +import { deconstructCollectionId } from '../utils/format'; +import assignDate from './utils/assign-date'; import { - COLLECTION, - COLLECTION_INFLIGHT, - COLLECTION_ERROR, - COLLECTION_APPLYWORKFLOW, - COLLECTION_APPLYWORKFLOW_INFLIGHT, + createClearItemReducer, + createErrorReducer, + createInflightReducer, + createSuccessReducer +} from './utils/reducer-creators'; +import { + CLEAR_COLLECTIONS_FILTER, + CLEAR_COLLECTIONS_SEARCH, COLLECTION_APPLYWORKFLOW_ERROR, - COLLECTIONS, - COLLECTIONS_INFLIGHT, + COLLECTION_APPLYWORKFLOW_INFLIGHT, + COLLECTION_APPLYWORKFLOW, + COLLECTION_DELETE_ERROR, + COLLECTION_DELETE_INFLIGHT, + COLLECTION_DELETE, + COLLECTION_ERROR, + COLLECTION_INFLIGHT, + COLLECTION, COLLECTIONS_ERROR, - NEW_COLLECTION, - NEW_COLLECTION_INFLIGHT, + COLLECTIONS_INFLIGHT, + COLLECTIONS, + FILTER_COLLECTIONS, NEW_COLLECTION_ERROR, - COLLECTION_DELETE, - COLLECTION_DELETE_INFLIGHT, - COLLECTION_DELETE_ERROR, - UPDATE_COLLECTION, - UPDATE_COLLECTION_INFLIGHT, - UPDATE_COLLECTION_ERROR, - UPDATE_COLLECTION_CLEAR, + NEW_COLLECTION_INFLIGHT, + NEW_COLLECTION, SEARCH_COLLECTIONS, - CLEAR_COLLECTIONS_SEARCH, - FILTER_COLLECTIONS, - CLEAR_COLLECTIONS_FILTER + UPDATE_COLLECTION_CLEAR, + UPDATE_COLLECTION_ERROR, + UPDATE_COLLECTION_INFLIGHT, + UPDATE_COLLECTION, } from '../actions/types'; -import { createReducer } from '@reduxjs/toolkit'; -import { deconstructCollectionId } from '../utils/format'; export const initialState = { list: { data: [], meta: {}, - params: {} + params: {}, }, created: {}, deleted: {}, executed: {}, map: {}, - updated: {} + updated: {}, }; export default createReducer(initialState, { @@ -48,109 +53,74 @@ export default createReducer(initialState, { const { id, data } = action; const { name } = deconstructCollectionId(id); const collection = data.results.find((element) => element.name === name); - set(state, ['map', id, 'inflight'], false); - set(state, ['map', id, 'data'], assignDate(collection)); - del(state, ['deleted', id]); + + state.map[id] = { + inflight: false, + data: assignDate(collection), + }; + delete state.deleted[id]; }, [COLLECTION_INFLIGHT]: (state, action) => { - const { id } = action; - set(state, ['map', id, 'inflight'], true); + state.map[action.id] = { inflight: true }; }, [COLLECTION_ERROR]: (state, action) => { - const { id } = action; - set(state, ['map', id, 'inflight'], false); - set(state, ['map', id, 'error'], action.error); + const { id, error } = action; + state.map[id] = { + inflight: false, + error, + }; }, - [COLLECTION_APPLYWORKFLOW]: (state, action) => { - const { id } = action; - set(state, ['executed', id, 'status'], 'success'); - set(state, ['executed', id, 'error'], null); - }, - [COLLECTION_APPLYWORKFLOW_INFLIGHT]: (state, action) => { - const { id } = action; - set(state, ['executed', id, 'status'], 'inflight'); - }, - [COLLECTION_APPLYWORKFLOW_ERROR]: (state, action) => { - const { id } = action; - set(state, ['executed', id, 'status'], 'error'); - set(state, ['executed', id, 'error'], action.error); + state.executed[action.id] = { + status: 'success', + error: null, + }; }, - + [COLLECTION_APPLYWORKFLOW_INFLIGHT]: createInflightReducer('executed'), + [COLLECTION_APPLYWORKFLOW_ERROR]: createErrorReducer('executed'), [COLLECTIONS]: (state, action) => { - const { data } = action; - set(state, ['list', 'data'], data.results); - set(state, ['list', 'meta'], assignDate(data.meta)); - set(state, ['list', 'inflight'], false); - set(state, ['list', 'error'], false); + state.list.data = action.data.results; + state.list.meta = assignDate(action.data.meta); + state.list.inflight = false; + state.list.error = false; }, - [COLLECTIONS_INFLIGHT]: (state, action) => { - set(state, ['list', 'inflight'], true); + [COLLECTIONS_INFLIGHT]: (state) => { + state.list.inflight = true; }, [COLLECTIONS_ERROR]: (state, action) => { - set(state, ['list', 'inflight'], false); - set(state, ['list', 'error'], action.error); - }, - - [NEW_COLLECTION]: (state, action) => { - const { id } = action; - set(state, ['created', id, 'status'], 'success'); - }, - [NEW_COLLECTION_INFLIGHT]: (state, action) => { - const { id } = action; - set(state, ['created', id, 'status'], 'inflight'); + state.list.inflight = false; + state.list.error = action.error; }, - [NEW_COLLECTION_ERROR]: (state, action) => { - const { id } = action; - set(state, ['created', id, 'status'], 'error'); - set(state, ['created', id, 'error'], action.error); - }, - + [NEW_COLLECTION]: createSuccessReducer('created'), + [NEW_COLLECTION_INFLIGHT]: createInflightReducer('created'), + [NEW_COLLECTION_ERROR]: createErrorReducer('created'), [UPDATE_COLLECTION]: (state, action) => { const { id, data } = action; - set(state, ['map', id, 'data'], data); - set(state, ['updated', id, 'status'], 'success'); - }, - [UPDATE_COLLECTION_INFLIGHT]: (state, action) => { - const { id } = action; - set(state, ['updated', id, 'status'], 'inflight'); - }, - [UPDATE_COLLECTION_ERROR]: (state, action) => { - const { id } = action; - set(state, ['updated', id, 'status'], 'error'); - set(state, ['updated', id, 'error'], action.error); + state.map[id] = { data }; + state.updated[id] = { status: 'success' }; }, - [UPDATE_COLLECTION_CLEAR]: (state, action) => { - const { id } = action; - del(state, ['updated', id]); - }, - + [UPDATE_COLLECTION_INFLIGHT]: createInflightReducer('updated'), + [UPDATE_COLLECTION_ERROR]: createErrorReducer('updated'), + [UPDATE_COLLECTION_CLEAR]: createClearItemReducer('updated'), [COLLECTION_DELETE]: (state, action) => { - const { id } = action; - set(state, ['deleted', id, 'status'], 'success'); - set(state, ['deleted', id, 'error'], null); - }, - [COLLECTION_DELETE_INFLIGHT]: (state, action) => { - const { id } = action; - set(state, ['deleted', id, 'status'], 'inflight'); - }, - [COLLECTION_DELETE_ERROR]: (state, action) => { - const { id } = action; - set(state, ['deleted', id, 'status'], 'error'); - set(state, ['deleted', id, 'error'], action.error); + state.deleted[action.id] = { + status: 'success', + error: null, + }; }, - + [COLLECTION_DELETE_INFLIGHT]: createInflightReducer('deleted'), + [COLLECTION_DELETE_ERROR]: createErrorReducer('deleted'), [SEARCH_COLLECTIONS]: (state, action) => { - set(state, ['list', 'params', 'prefix'], action.prefix); + state.list.params.prefix = action.prefix; }, - [CLEAR_COLLECTIONS_SEARCH]: (state, action) => { - set(state, ['list', 'params', 'prefix'], null); + [CLEAR_COLLECTIONS_SEARCH]: (state) => { + state.list.params.prefix = null; }, - [FILTER_COLLECTIONS]: (state, action) => { - set(state, ['list', 'params', action.param.key], action.param.value); + const { key, value } = action.param; + state.list.params[key] = value; }, [CLEAR_COLLECTIONS_FILTER]: (state, action) => { - set(state, ['list', 'params', action.paramKey], null); - } + state.list.params[action.paramKey] = null; + }, }); diff --git a/app/src/js/reducers/cumulus-instance.js b/app/src/js/reducers/cumulus-instance.js index a433669ec..cbdd5b60f 100644 --- a/app/src/js/reducers/cumulus-instance.js +++ b/app/src/js/reducers/cumulus-instance.js @@ -1,23 +1,15 @@ 'use strict'; -import { get } from 'object-path'; -import { ADD_INSTANCE_META } from '../actions/types'; -// In Node 12.0.0+, use built-in Object.fromEntries instead of fromPairs -import fromPairs from 'lodash.frompairs'; import { createReducer } from '@reduxjs/toolkit'; +import { ADD_INSTANCE_META } from '../actions/types'; export const initialState = {}; export default createReducer(initialState, { [ADD_INSTANCE_META]: (state, action) => { - const { data } = action; - return { - ...state, - ...fromPairs([ - ['cmrEnvironment', get(data, 'cmr.environment')], - ['cmrProvider', get(data, 'cmr.provider')], - ['stackName', get(data, 'cumulus.stackName')] - ].filter(([_, value]) => value)) - }; - } + const { cmr = {}, cumulus = {} } = action.data; + if (cmr.environment) state.cmrEnvironment = cmr.environment; + if (cmr.provider) state.cmrProvider = cmr.provider; + if (cumulus.stackName) state.stackName = cumulus.stackName; + }, }); diff --git a/app/src/js/reducers/datepicker.js b/app/src/js/reducers/datepicker.js index 76649c4a7..1f72f8c13 100644 --- a/app/src/js/reducers/datepicker.js +++ b/app/src/js/reducers/datepicker.js @@ -1,23 +1,20 @@ 'use strict'; +import { msPerDay, allDateRanges } from '../utils/datepicker'; +import { createReducer } from '@reduxjs/toolkit'; import { DATEPICKER_DATECHANGE, DATEPICKER_DROPDOWN_FILTER, - DATEPICKER_HOUR_FORMAT + DATEPICKER_HOUR_FORMAT, } from '../actions/types'; -import { msPerDay, allDateRanges } from '../utils/datepicker'; -import { createReducer } from '@reduxjs/toolkit'; // Also becomes default props for Datepicker -export const initialState = () => { - const now = Date.now(); - return { - startDateTime: now - msPerDay, - endDateTime: null, - dateRange: allDateRanges.find((a) => a.value === 'Recent'), - hourFormat: '12HR' - }; -}; +export const initialState = () => ({ + startDateTime: null, + endDateTime: null, + dateRange: allDateRanges.find((a) => a.value === 'Custom'), + hourFormat: '12HR' +}); /** * Computes the desired time range from present. @@ -34,40 +31,49 @@ const computeDateTimeDelta = (timeDeltaInDays) => { endDateTime = Date.now(); startDateTime = endDateTime - timeDeltaInDays * msPerDay; } + return { startDateTime, endDateTime }; }; /** -* Sets the state for recent data start time is 24 hours ago, end time is null -* -* @returns {Object} with startDateTime and dateRange set to "Recent" -*/ -const recentData = () => { - const endDateTime = null; - const startDateTime = Date.now() - msPerDay; - return { startDateTime, endDateTime, dateRange: allDateRanges.find((a) => a.value === 'Recent') }; -}; + * Sets the state for recent data start time is 24 hours ago, end time is null + * + * @returns {Object} with startDateTime and dateRange set to "Recent" + */ +const recentData = () => ({ + startDateTime: Date.now() - msPerDay, + endDateTime: null, + dateRange: allDateRanges.find((a) => a.value === 'Recent'), +}); export default createReducer(initialState(), { - [DATEPICKER_DROPDOWN_FILTER]: (state, action) => { const { data } = action; + switch (data.dateRange.label) { case 'Custom': case 'All': - return { ...state, ...data, startDateTime: null, endDateTime: null }; + Object.assign(state, data, { + startDateTime: null, + endDateTime: null, + }); + break; case 'Recent': - return { ...state, ...data, ...recentData() }; + Object.assign(state, data, recentData()); + break; default: - return { ...state, ...computeDateTimeDelta(data.dateRange.value), ...data }; + Object.assign( + state, + computeDateTimeDelta(data.dateRange.value), + data + ); + break; } }, [DATEPICKER_DATECHANGE]: (state, action) => { - const { data } = action; - return { ...state, ...data }; + Object.assign(state, action.data); }, [DATEPICKER_HOUR_FORMAT]: (state, action) => { - const { data } = action; - return { ...state, hourFormat: data }; - } + state.hourFormat = action.data; + }, }); diff --git a/app/src/js/reducers/dist.js b/app/src/js/reducers/dist.js index af03fcdb4..b3991235c 100644 --- a/app/src/js/reducers/dist.js +++ b/app/src/js/reducers/dist.js @@ -1,8 +1,7 @@ 'use strict'; import get from 'lodash.get'; -import { set } from 'object-path'; - +import { createReducer } from '@reduxjs/toolkit'; import { DIST_APIGATEWAY, DIST_APIGATEWAY_INFLIGHT, @@ -15,122 +14,80 @@ import { DIST_TEA_LAMBDA_ERROR, DIST_S3ACCESS, DIST_S3ACCESS_INFLIGHT, - DIST_S3ACCESS_ERROR + DIST_S3ACCESS_ERROR, } from '../actions/types'; -import { createReducer } from '@reduxjs/toolkit'; -const initialState = { +export const initialState = { apiGateway: { - execution: { errors: {}, successes: {} }, - access: { errors: {}, successes: {} } + execution: { errors: null, successes: null }, + access: { errors: null, successes: null }, }, - apiLambda: { errors: {}, successes: {} }, - teaLambda: { errors: {}, successes: {} }, - s3Access: { errors: {}, successes: {} } + apiLambda: { errors: null, successes: null }, + teaLambda: { errors: null, successes: null }, + s3Access: { errors: null, successes: null }, }; -const countsFromElasticSearchQuery = (data, name) => { - return get(data, `aggregations.2.buckets.${name}.doc_count`, null); -}; +const count = (data, name) => + get(data, `aggregations.2.buckets.${name}.doc_count`, 0); export default createReducer(initialState, { - [DIST_APIGATEWAY]: (state, action) => { - set(state, 'apiGateway.error', null); - set(state, 'apiGateway.inflight', false); - set(state, 'apiGateway.queriedAt', Date.now()); - set( - state, - 'apiGateway.access.errors', - countsFromElasticSearchQuery(action.data, 'ApiAccessErrors') - ); - set( - state, - 'apiGateway.access.successes', - countsFromElasticSearchQuery(action.data, 'ApiAccessSuccesses') - ); - set( - state, - 'apiGateway.execution.errors', - countsFromElasticSearchQuery(action.data, 'ApiExecutionErrors') - ); - set( - state, - 'apiGateway.execution.successes', - countsFromElasticSearchQuery(action.data, 'ApiExecutionSuccesses') - ); + const { data } = action; + state.apiGateway.error = null; + state.apiGateway.inflight = false; + state.apiGateway.queriedAt = Date.now(); + state.apiGateway.access.errors = count(data, 'ApiAccessErrors'); + state.apiGateway.access.successes = count(data, 'ApiAccessSuccesses'); + state.apiGateway.execution.errors = count(data, 'ApiExecutionErrors'); + state.apiGateway.execution.successes = count(data, 'ApiExecutionSuccesses'); }, - [DIST_APIGATEWAY_INFLIGHT]: (state, action) => { - set(state, 'apiGateway.inflight', true); + [DIST_APIGATEWAY_INFLIGHT]: (state) => { + state.apiGateway.inflight = true; }, [DIST_APIGATEWAY_ERROR]: (state, action) => { - set(state, 'apiGateway.inflight', false); - set(state, 'apiGateway.error', action.error); + state.apiGateway.inflight = false; + state.apiGateway.error = action.error; }, [DIST_API_LAMBDA]: (state, action) => { - set(state, 'apiLambda.error', null); - set(state, 'apiLambda.inflight', false); - set(state, 'apiLambda.queriedAt', Date.now()); - set( - state, - 'apiLambda.errors', - countsFromElasticSearchQuery(action.data, 'LambdaAPIErrors') - ); - set( - state, - 'apiLambda.successes', - countsFromElasticSearchQuery(action.data, 'LambdaAPISuccesses') - ); + state.apiLambda.error = null; + state.apiLambda.inflight = false; + state.apiLambda.queriedAt = Date.now(); + state.apiLambda.errors = count(action.data, 'LambdaAPIErrors'); + state.apiLambda.successes = count(action.data, 'LambdaAPISuccesses'); }, - [DIST_API_LAMBDA_INFLIGHT]: (state, action) => { - set(state, 'apiLambda.inflight', true); + [DIST_API_LAMBDA_INFLIGHT]: (state) => { + state.apiLambda.inflight = true; }, [DIST_API_LAMBDA_ERROR]: (state, action) => { - set(state, 'apiLambda.inflight', false); - set(state, 'apiLambda.error', action.error); + state.apiLambda.inflight = false; + state.apiLambda.error = action.error; }, [DIST_TEA_LAMBDA]: (state, action) => { - set(state, 'teaLambda.error', null); - set(state, 'teaLambda.inflight', false); - set(state, 'teaLambda.queriedAt', Date.now()); - set( - state, - 'teaLambda.errors', - countsFromElasticSearchQuery(action.data, 'TEALambdaErrors') - ); - set( - state, - 'teaLambda.successes', - countsFromElasticSearchQuery(action.data, 'TEALambdaSuccesses') - ); + state.teaLambda.error = null; + state.teaLambda.inflight = false; + state.teaLambda.queriedAt = Date.now(); + state.teaLambda.errors = count(action.data, 'TEALambdaErrors'); + state.teaLambda.successes = count(action.data, 'TEALambdaSuccesses'); }, - [DIST_TEA_LAMBDA_INFLIGHT]: (state, action) => { - set(state, 'teaLambda.inflight', true); + [DIST_TEA_LAMBDA_INFLIGHT]: (state) => { + state.teaLambda.inflight = true; }, [DIST_TEA_LAMBDA_ERROR]: (state, action) => { - set(state, 'teaLambda.inflight', false); - set(state, 'teaLambda.error', action.error); + state.teaLambda.inflight = false; + state.teaLambda.error = action.error; }, [DIST_S3ACCESS]: (state, action) => { - set(state, 's3Access.error', null); - set(state, 's3Access.inflight', false); - set(state, 's3Access.queriedAt', Date.now()); - set( - state, - 's3Access.errors', - countsFromElasticSearchQuery(action.data, 's3AccessFailures') - ); - set( - state, - 's3Access.successes', - countsFromElasticSearchQuery(action.data, 's3AccessSuccesses') - ); + state.s3Access.error = null; + state.s3Access.inflight = false; + state.s3Access.queriedAt = Date.now(); + state.s3Access.errors = count(action.data, 's3AccessFailures'); + state.s3Access.successes = count(action.data, 's3AccessSuccesses'); }, - [DIST_S3ACCESS_INFLIGHT]: (state, action) => { - set(state, 's3Access.inflight', true); + [DIST_S3ACCESS_INFLIGHT]: (state) => { + state.s3Access.inflight = true; }, [DIST_S3ACCESS_ERROR]: (state, action) => { - set(state, 's3Access.inflight', false); - set(state, 's3Access.error', action.error); - } + state.s3Access.inflight = false; + state.s3Access.error = action.error; + }, }); diff --git a/app/src/js/reducers/execution-logs.js b/app/src/js/reducers/execution-logs.js index 9970e9828..3ec73093f 100644 --- a/app/src/js/reducers/execution-logs.js +++ b/app/src/js/reducers/execution-logs.js @@ -1,35 +1,32 @@ 'use strict'; -import { set } from 'object-path'; +import { createReducer } from '@reduxjs/toolkit'; import { EXECUTION_LOGS, EXECUTION_LOGS_INFLIGHT, - EXECUTION_LOGS_ERROR + EXECUTION_LOGS_ERROR, } from '../actions/types'; -import { createReducer } from '@reduxjs/toolkit'; export const initialState = { results: null, details: null, inflight: false, error: false, - meta: {} + meta: {}, }; export default createReducer(initialState, { - [EXECUTION_LOGS]: (state, action) => { - const { data } = action; - set(state, ['inflight'], false); - set(state, ['error'], false); - set(state, ['details'], data.meta); - set(state, ['results'], data.results); + state.inflight = false; + state.error = false; + state.details = action.data.meta; + state.results = action.data.results; }, - [EXECUTION_LOGS_INFLIGHT]: (state, action) => { - set(state, ['inflight'], true); + [EXECUTION_LOGS_INFLIGHT]: (state) => { + state.inflight = true; }, [EXECUTION_LOGS_ERROR]: (state, action) => { - set(state, ['inflight'], false); - set(state, ['error'], action.error); - } + state.inflight = false; + state.error = action.error; + }, }); diff --git a/app/src/js/reducers/execution-status.js b/app/src/js/reducers/execution-status.js index 9333c1e57..ee87afbd2 100644 --- a/app/src/js/reducers/execution-status.js +++ b/app/src/js/reducers/execution-status.js @@ -1,14 +1,13 @@ 'use strict'; -import { set } from 'object-path'; +import { createReducer } from '@reduxjs/toolkit'; import { EXECUTION_STATUS, EXECUTION_STATUS_INFLIGHT, EXECUTION_STATUS_ERROR, SEARCH_EXECUTION_EVENTS, - CLEAR_EXECUTION_EVENTS_SEARCH + CLEAR_EXECUTION_EVENTS_SEARCH, } from '../actions/types'; -import { createReducer } from '@reduxjs/toolkit'; export const initialState = { execution: null, @@ -17,51 +16,38 @@ export const initialState = { searchString: null, inflight: false, error: false, - meta: {} + meta: {}, }; -/** - * Filters intput data by searchString on the data'a object's type. -* - * @param {Array} rawData - An array of objects with a type parameter. - * @param {string} searchString - a string to check if type includes. - * @returns {Array} Filtered Array of rawData's objects who's type include searchString. - */ -export const filterData = (rawData, searchString) => { - if (searchString !== null) { - const data = { - execution: rawData.execution, - stateMachine: rawData.stateMachine, - executionHistory: rawData.executionHistory - }; - const filteredEvents = rawData.executionHistory.events.filter(d => d.type.toLowerCase().includes(searchString.toLowerCase())); - data.executionHistory.events = filteredEvents; - return data; - } - return rawData; -}; +const typeContains = (string) => ({ type }) => + type.toLowerCase().includes(string.toLowerCase()); export default createReducer(initialState, { [EXECUTION_STATUS]: (state, action) => { - const { data: rawData } = action; - const data = filterData(rawData, state.searchString); - set(state, ['inflight'], false); - set(state, ['error'], false); - set(state, ['execution'], data.execution); - set(state, ['executionHistory'], data.executionHistory); - set(state, ['stateMachine'], data.stateMachine); + const { data } = action; + state.inflight = false; + state.error = false; + state.execution = data.execution; + state.executionHistory = data.executionHistory; + state.stateMachine = data.stateMachine; + + if (state.searchString) { + state.executionHistory.events = data.executionHistory.events.filter( + typeContains(state.searchString) + ); + } }, - [EXECUTION_STATUS_INFLIGHT]: (state, action) => { - set(state, ['inflight'], true); + [EXECUTION_STATUS_INFLIGHT]: (state) => { + state.inflight = true; }, [EXECUTION_STATUS_ERROR]: (state, action) => { - set(state, ['inflight'], false); - set(state, ['error'], action.error); + state.inflight = false; + state.error = action.error; }, [SEARCH_EXECUTION_EVENTS]: (state, action) => { - set(state, ['searchString'], action.searchString); + state.searchString = action.searchString; }, [CLEAR_EXECUTION_EVENTS_SEARCH]: (state) => { - set(state, ['searchString'], null); + state.searchString = null; }, }); diff --git a/app/src/js/reducers/executions.js b/app/src/js/reducers/executions.js index 96e059a25..b792f8960 100644 --- a/app/src/js/reducers/executions.js +++ b/app/src/js/reducers/executions.js @@ -1,18 +1,16 @@ 'use strict'; -import { set } from 'object-path'; -import assignDate from './assign-date'; + +import assignDate from './utils/assign-date'; +import { createReducer } from '@reduxjs/toolkit'; import { EXECUTIONS, EXECUTIONS_INFLIGHT, EXECUTIONS_ERROR, - FILTER_EXECUTIONS, CLEAR_EXECUTIONS_FILTER, - SEARCH_EXECUTIONS, - CLEAR_EXECUTIONS_SEARCH + CLEAR_EXECUTIONS_SEARCH, } from '../actions/types'; -import { createReducer } from '@reduxjs/toolkit'; export const initialState = { list: { @@ -20,37 +18,35 @@ export const initialState = { meta: {}, params: {}, inflight: false, - error: false + error: false, }, - map: {} + map: {}, }; export default createReducer(initialState, { [EXECUTIONS]: (state, action) => { - set(state, ['list', 'data'], action.data.results); - set(state, ['list', 'meta'], assignDate(action.data.meta)); - set(state, ['list', 'inflight'], false); - set(state, ['list', 'error'], false); + state.list.data = action.data.results; + state.list.meta = assignDate(action.data.meta); + state.list.inflight = false; + state.list.error = false; }, - [EXECUTIONS_INFLIGHT]: (state, action) => { - set(state, ['list', 'inflight'], true); + [EXECUTIONS_INFLIGHT]: (state) => { + state.list.inflight = true; }, [EXECUTIONS_ERROR]: (state, action) => { - set(state, ['list', 'inflight'], false); - set(state, ['list', 'error'], action.error); + state.list.inflight = false; + state.list.error = action.error; }, - [FILTER_EXECUTIONS]: (state, action) => { - set(state, ['list', 'params', action.param.key], action.param.value); + state.list.params[action.param.key] = action.param.value; }, [CLEAR_EXECUTIONS_FILTER]: (state, action) => { - set(state, ['list', 'params', action.paramKey], null); + state.list.params[action.paramKey] = null; }, - [SEARCH_EXECUTIONS]: (state, action) => { - set(state, ['list', 'prefix'], action.prefix); + state.list.prefix = action.prefix; + }, + [CLEAR_EXECUTIONS_SEARCH]: (state) => { + state.list.prefix = null; }, - [CLEAR_EXECUTIONS_SEARCH]: (state, action) => { - set(state, ['list', 'prefix'], null); - } }); diff --git a/app/src/js/reducers/granule-csv.js b/app/src/js/reducers/granule-csv.js index a8d8546e7..d36d02658 100644 --- a/app/src/js/reducers/granule-csv.js +++ b/app/src/js/reducers/granule-csv.js @@ -1,26 +1,29 @@ 'use strict'; +import { createReducer } from '@reduxjs/toolkit'; import { GRANULE_CSV, GRANULE_CSV_INFLIGHT, - GRANULE_CSV_ERROR + GRANULE_CSV_ERROR, } from '../actions/types'; -import { createReducer } from '@reduxjs/toolkit'; export const initialState = { data: null, inflight: false, - error: null + error: null, }; export default createReducer(initialState, { [GRANULE_CSV]: (state, action) => { - return { ...state, data: action.data, inflight: false, error: null }; + state.data = action.data; + state.inflight = false; + state.error = null; }, [GRANULE_CSV_INFLIGHT]: (state) => { - return { ...state, inflight: true, error: state.error }; + state.inflight = true; }, [GRANULE_CSV_ERROR]: (state, action) => { - return { ...state, inflight: false, error: action.error }; - } + state.inflight = false; + state.error = action.error; + }, }); diff --git a/app/src/js/reducers/granules.js b/app/src/js/reducers/granules.js index 601675671..84f5644a2 100644 --- a/app/src/js/reducers/granules.js +++ b/app/src/js/reducers/granules.js @@ -1,59 +1,55 @@ 'use strict'; -import { set, del } from 'object-path'; -import assignDate from './assign-date'; -import removeDeleted from './remove-deleted'; + +import { set } from 'object-path'; +import assignDate from './utils/assign-date'; +import removeDeleted from './utils/remove-deleted'; +import { createReducer } from '@reduxjs/toolkit'; +import { getCollectionId } from '../utils/format'; +import { + createErrorReducer, + createInflightReducer, + createSuccessReducer, +} from './utils/reducer-creators'; import { GRANULE, GRANULE_INFLIGHT, GRANULE_ERROR, - GRANULES, GRANULES_INFLIGHT, GRANULES_ERROR, - GRANULE_REPROCESS, GRANULE_REPROCESS_INFLIGHT, GRANULE_REPROCESS_ERROR, - GRANULE_REINGEST, GRANULE_REINGEST_INFLIGHT, GRANULE_REINGEST_ERROR, - GRANULE_APPLYWORKFLOW, GRANULE_APPLYWORKFLOW_INFLIGHT, GRANULE_APPLYWORKFLOW_ERROR, - GRANULE_REMOVE, GRANULE_REMOVE_INFLIGHT, GRANULE_REMOVE_ERROR, - BULK_GRANULE, BULK_GRANULE_INFLIGHT, BULK_GRANULE_ERROR, - GRANULE_DELETE, GRANULE_DELETE_INFLIGHT, GRANULE_DELETE_ERROR, - SEARCH_GRANULES, CLEAR_GRANULES_SEARCH, - FILTER_GRANULES, CLEAR_GRANULES_FILTER, - OPTIONS_COLLECTIONNAME, OPTIONS_COLLECTIONNAME_INFLIGHT, - OPTIONS_COLLECTIONNAME_ERROR + OPTIONS_COLLECTIONNAME_ERROR, } from '../actions/types'; -import { createReducer } from '@reduxjs/toolkit'; -import { getCollectionId } from '../utils/format'; export const initialState = { list: { data: [], meta: {}, - params: {} + params: {}, }, dropdowns: {}, map: {}, @@ -64,159 +60,90 @@ export const initialState = { recovered: {}, executed: {}, deleted: {}, - recent: {} + recent: {}, }; +const getConfigRequestId = ({ config: { requestId } }) => requestId; + export default createReducer(initialState, { [GRANULE]: (state, action) => { - const { id, data } = action; - set(state, ['map', id, 'inflight'], false); - set(state, ['map', id, 'data'], assignDate(data)); - del(state, ['deleted', id]); + state.map[action.id] = { + inflight: false, + data: assignDate(action.data), + }; + delete state.deleted[action.id]; }, [GRANULE_INFLIGHT]: (state, action) => { - const { id } = action; - set(state, ['map', id, 'inflight'], true); + state.map[action.id] = { inflight: true }; }, [GRANULE_ERROR]: (state, action) => { - const { id } = action; - set(state, ['map', id, 'inflight'], false); - set(state, ['map', id, 'error'], action.error); + state.map[action.id] = { + inflight: false, + error: action.error, + }; }, - [GRANULES]: (state, action) => { - const { data } = action; - set(state, ['list', 'data'], removeDeleted('granuleId', data.results, state.deleted)); - set(state, ['list', 'meta'], assignDate(data.meta)); - set(state, ['list', 'inflight'], false); - set(state, ['list', 'error'], false); + state.list = { + ...state.list, + data: removeDeleted('granuleId', action.data.results, state.deleted), + meta: assignDate(action.data.meta), + inflight: false, + error: false + }; }, - [GRANULES_INFLIGHT]: (state, action) => { - set(state, ['list', 'inflight'], true); + [GRANULES_INFLIGHT]: (state) => { + state.list.inflight = true; }, [GRANULES_ERROR]: (state, action) => { - set(state, ['list', 'inflight'], false); - set(state, ['list', 'error'], action.error); - }, - - [GRANULE_REPROCESS]: (state, action) => { - const { id } = action; - set(state, ['reprocessed', id, 'status'], 'success'); - set(state, ['reprocessed', id, 'error'], null); - }, - [GRANULE_REPROCESS_INFLIGHT]: (state, action) => { - const { id } = action; - set(state, ['reprocessed', id, 'status'], 'inflight'); - }, - [GRANULE_REPROCESS_ERROR]: (state, action) => { - const { id } = action; - set(state, ['reprocessed', id, 'status'], 'error'); - set(state, ['reprocessed', id, 'error'], action.error); - }, - - [GRANULE_REINGEST]: (state, action) => { - const { id } = action; - set(state, ['reingested', id, 'status'], 'success'); - set(state, ['reingested', id, 'error'], null); - }, - [GRANULE_REINGEST_INFLIGHT]: (state, action) => { - const { id } = action; - set(state, ['reingested', id, 'status'], 'inflight'); - }, - [GRANULE_REINGEST_ERROR]: (state, action) => { - const { id } = action; - set(state, ['reingested', id, 'status'], 'error'); - set(state, ['reingested', id, 'error'], action.error); - }, - - [GRANULE_APPLYWORKFLOW]: (state, action) => { - const { id } = action; - set(state, ['executed', id, 'status'], 'success'); - set(state, ['executed', id, 'error'], null); - }, - [GRANULE_APPLYWORKFLOW_INFLIGHT]: (state, action) => { - const { id } = action; - set(state, ['executed', id, 'status'], 'inflight'); - }, - [GRANULE_APPLYWORKFLOW_ERROR]: (state, action) => { - const { id } = action; - set(state, ['executed', id, 'status'], 'error'); - set(state, ['executed', id, 'error'], action.error); - }, - - [GRANULE_REMOVE]: (state, action) => { - const { id } = action; - set(state, ['removed', id, 'status'], 'success'); - set(state, ['removed', id, 'error'], null); - }, - [GRANULE_REMOVE_INFLIGHT]: (state, action) => { - const { id } = action; - set(state, ['removed', id, 'status'], 'inflight'); - }, - [GRANULE_REMOVE_ERROR]: (state, action) => { - const { id } = action; - set(state, ['removed', id, 'status'], 'error'); - set(state, ['removed', id, 'error'], action.error); - }, - - [BULK_GRANULE]: (state, action) => { - const { data, config } = action; - set(state, ['bulk', config.requestId, 'data'], data); - set(state, ['bulk', config.requestId, 'status'], 'success'); - set(state, ['bulk', config.requestId, 'error'], null); - }, - [BULK_GRANULE_INFLIGHT]: (state, action) => { - const { config } = action; - set(state, ['bulk', config.requestId, 'status'], 'inflight'); - }, - [BULK_GRANULE_ERROR]: (state, action) => { - const { config } = action; - set(state, ['bulk', config.requestId, 'status'], 'error'); - set(state, ['bulk', config.requestId, 'error'], action.error); - }, - - [GRANULE_DELETE]: (state, action) => { - const { id } = action; - set(state, ['deleted', id, 'status'], 'success'); - set(state, ['deleted', id, 'error'], null); - }, - [GRANULE_DELETE_INFLIGHT]: (state, action) => { - const { id } = action; - set(state, ['deleted', id, 'status'], 'inflight'); - }, - [GRANULE_DELETE_ERROR]: (state, action) => { - const { id } = action; - set(state, ['deleted', id, 'status'], 'error'); - set(state, ['deleted', id, 'error'], action.error); - }, + state.list.inflight = false; + state.list.error = action.error; + }, + + [GRANULE_REPROCESS]: createSuccessReducer('reprocessed'), + [GRANULE_REPROCESS_INFLIGHT]: createInflightReducer('reprocessed'), + [GRANULE_REPROCESS_ERROR]: createErrorReducer('reprocessed'), + [GRANULE_REINGEST]: createSuccessReducer('reingested'), + [GRANULE_REINGEST_INFLIGHT]: createInflightReducer('reingested'), + [GRANULE_REINGEST_ERROR]: createErrorReducer('reingested'), + [GRANULE_APPLYWORKFLOW]: createSuccessReducer('executed'), + [GRANULE_APPLYWORKFLOW_INFLIGHT]: createInflightReducer('executed'), + [GRANULE_APPLYWORKFLOW_ERROR]: createErrorReducer('executed'), + [GRANULE_REMOVE]: createSuccessReducer('removed'), + [GRANULE_REMOVE_INFLIGHT]: createInflightReducer('removed'), + [GRANULE_REMOVE_ERROR]: createErrorReducer('removed'), + [BULK_GRANULE]: createSuccessReducer('bulk', getConfigRequestId), + [BULK_GRANULE_INFLIGHT]: createInflightReducer('bulk', getConfigRequestId), + [BULK_GRANULE_ERROR]: createErrorReducer('bulk', getConfigRequestId), + [GRANULE_DELETE]: createSuccessReducer('deleted'), + [GRANULE_DELETE_INFLIGHT]: createInflightReducer('deleted'), + [GRANULE_DELETE_ERROR]: createErrorReducer('deleted'), [SEARCH_GRANULES]: (state, action) => { - set(state, ['list', 'params', 'prefix'], action.prefix); + state.list.params.prefix = action.prefix; }, - [CLEAR_GRANULES_SEARCH]: (state, action) => { - set(state, ['list', 'params', 'prefix'], null); + [CLEAR_GRANULES_SEARCH]: (state) => { + state.list.params.prefix = null; }, - [FILTER_GRANULES]: (state, action) => { - set(state, ['list', 'params', action.param.key], action.param.value); + state.list.params[action.param.key] = action.param.value; }, [CLEAR_GRANULES_FILTER]: (state, action) => { - set(state, ['list', 'params', action.paramKey], null); + state.list.params[action.paramKey] = null; }, - [OPTIONS_COLLECTIONNAME]: (state, action) => { const options = action.data.results.reduce( (obj, { name, version }) => Object.assign(obj, { - [`${name} ${version}`]: getCollectionId({ name, version }) + [`${name} ${version}`]: getCollectionId({ name, version }), }), {} ); - set(state, ['dropdowns', 'collectionName', 'options'], options); + + set(state.dropdowns, 'collectionName.options', options); }, [OPTIONS_COLLECTIONNAME_INFLIGHT]: () => {}, [OPTIONS_COLLECTIONNAME_ERROR]: (state, action) => { - set(state, ['dropdowns', 'collectionName', 'options'], []); - set(state, ['list', 'error'], action.error); - } + set(state.dropdowns, 'collectionName.options', []); + state.list.error = action.error; + }, }); diff --git a/app/src/js/reducers/index.js b/app/src/js/reducers/index.js index 07cf7eec7..366513d06 100644 --- a/app/src/js/reducers/index.js +++ b/app/src/js/reducers/index.js @@ -3,7 +3,7 @@ import { connectRouter } from 'connected-react-router'; import api from './api'; import apiVersion from './api-version'; import collections from './collections'; -import config from './config'; +import config from './utils/config'; import dist from './dist'; import datepicker from './datepicker'; import granules from './granules'; @@ -13,6 +13,7 @@ import pdrs from './pdrs'; import providers from './providers'; import logs from './logs'; import schema from './schema'; +import timer from './timer'; import workflows from './workflows'; import executions from './executions'; import executionStatus from './execution-status'; @@ -23,7 +24,7 @@ import reconciliationReports from './reconciliation-reports'; import mmtLinks from './mmtLinks'; import cumulusInstance from './cumulus-instance'; -const def = (state = {}, action) => state; +const def = (state, _action) => state || {}; export const reducers = { def, @@ -38,6 +39,7 @@ export const reducers = { granules, granuleCSV, stats, + timer, pdrs, providers, logs, @@ -48,10 +50,11 @@ export const reducers = { executionLogs, operations, rules, - reconciliationReports + reconciliationReports, }; -export const createRootReducer = (history) => combineReducers({ - router: connectRouter(history), - ...reducers -}); +export const createRootReducer = (history) => + combineReducers({ + router: connectRouter(history), + ...reducers, + }); diff --git a/app/src/js/reducers/logs.js b/app/src/js/reducers/logs.js index 8ba236a37..4f612942f 100644 --- a/app/src/js/reducers/logs.js +++ b/app/src/js/reducers/logs.js @@ -1,76 +1,61 @@ 'use strict'; -import { set, get } from 'object-path'; -import moment from 'moment'; -import { - LOGS, - LOGS_ERROR, - LOGS_INFLIGHT, - CLEAR_LOGS -} from '../actions/types'; +import moment from 'moment'; +import uniqBy from 'lodash/fp'; +import { get } from 'object-path'; import { createReducer } from '@reduxjs/toolkit'; - -export const initialState = { - items: [] -}; +import { LOGS, LOGS_ERROR, LOGS_INFLIGHT, CLEAR_LOGS } from '../actions/types'; // https://momentjs.com/docs/#/displaying/ const format = 'MM/DD/YY hh:mma ss:SSS[s]'; -export default createReducer(initialState, { +const processLog = (logEntry) => ({ + ...logEntry, + displayTime: moment(logEntry.timestamp).format(format), + displayText: logEntry.message || logEntry.msg, + key: `${logEntry.timestamp}-${logEntry.displayText}`, + searchKey: Object.values(logEntry).join(' '), +}); + +export const initialState = { + items: [], +}; +export default createReducer(initialState, { [LOGS]: (state, action) => { const { data } = action; + if (Array.isArray(data.results) && data.results.length) { - data.results.forEach(processLog); - let items = data.results.concat(state.items); - items = dedupe(items); - set(state, 'items', items); + state.items = uniqBy( + 'key', + data.results + .filter((result) => result.timestamp && result.displayText) + .map(processLog) + .concat(state.items) + ); } - set(state, 'inflight', false); - set(state, 'queriedAt', Date.now()); - set(state, 'error', false); + + state.inflight = false; + state.queriedAt = Date.now(); + state.error = false; }, [LOGS_INFLIGHT]: (state, action) => { const query = get(action.config, 'qs.q', ''); - const replace = state.query !== query; - if (replace) { - set(state, 'items', []); + + if (state.query !== query) { + state.query = query; + state.items = []; } - set(state, 'inflight', true); - set(state, 'query', query); + + state.inflight = true; }, [LOGS_ERROR]: (state, action) => { - set(state, 'inflight', false); - set(state, 'error', action.error); + state.inflight = false; + state.error = action.error; }, [CLEAR_LOGS]: (state, action) => { - set(state, 'inflight', false); - set(state, 'error', action.error); - set(state, 'items', []); - } - + state.inflight = false; + state.error = action.error; + state.items = []; + }, }); - -function processLog (d) { - d.displayTime = moment(d.timestamp).format(format); - d.displayText = d.message || d.msg; - d.key = d.timestamp + '-' + d.displayText; - let metafields = ''; - for (const key in d) { - metafields += ' ' + d[key]; - } - d.searchkey = metafields; -} - -function dedupe (items) { - const deduped = []; - const keymap = {}; - items.forEach(d => { - if (d.timestamp && d.displayText && !keymap[d.key]) { - keymap[d.key] = 1; - deduped.push(d); - } - }); - return deduped; -} diff --git a/app/src/js/reducers/mmtLinks.js b/app/src/js/reducers/mmtLinks.js index 25e3ef17f..701691cc2 100644 --- a/app/src/js/reducers/mmtLinks.js +++ b/app/src/js/reducers/mmtLinks.js @@ -1,18 +1,13 @@ 'use strict'; -import { set } from 'object-path'; -import { getCollectionId } from '../utils/format'; -import { - ADD_MMTLINK -} from '../actions/types'; +import { getCollectionId } from '../utils/format'; import { createReducer } from '@reduxjs/toolkit'; +import { ADD_MMTLINK } from '../actions/types'; const initialState = {}; export default createReducer(initialState, { [ADD_MMTLINK]: (state, action) => { - const { data } = action; - const collectionId = getCollectionId(data); - set(state, [collectionId], data.url); + state[getCollectionId(action.data)] = action.data.url; }, }); diff --git a/app/src/js/reducers/operations.js b/app/src/js/reducers/operations.js index 7ee61b2e3..1047bac62 100644 --- a/app/src/js/reducers/operations.js +++ b/app/src/js/reducers/operations.js @@ -1,21 +1,19 @@ 'use strict'; -import { set } from 'object-path'; -import assignDate from './assign-date'; + +import assignDate from './utils/assign-date'; +import { createReducer } from '@reduxjs/toolkit'; import { OPERATIONS, OPERATIONS_INFLIGHT, OPERATIONS_ERROR, - OPERATION, OPERATION_INFLIGHT, OPERATION_ERROR, - FILTER_OPERATIONS, CLEAR_OPERATIONS_FILTER, SEARCH_OPERATIONS, - CLEAR_OPERATIONS_SEARCH + CLEAR_OPERATIONS_SEARCH, } from '../actions/types'; -import { createReducer } from '@reduxjs/toolkit'; export const initialState = { list: { @@ -25,53 +23,48 @@ export const initialState = { params: {}, internal: {}, inflight: false, - error: false + error: false, }, - map: {} + map: {}, }; export default createReducer(initialState, { [OPERATIONS]: (state, action) => { - const { data } = action; - set(state, ['list', 'data'], data.results); - set(state, ['list', 'meta'], assignDate(data.meta)); - set(state, ['list', 'inflight'], false); - set(state, ['list', 'error'], false); + state.list.data = action.data.results; + state.list.meta = assignDate(action.data.meta); + state.list.inflight = false; + state.list.error = false; }, - [OPERATIONS_INFLIGHT]: (state, action) => { - set(state, ['list', 'inflight'], true); + [OPERATIONS_INFLIGHT]: (state) => { + state.list.inflight = true; }, [OPERATIONS_ERROR]: (state, action) => { - set(state, ['list', 'inflight'], false); - set(state, ['list', 'error'], action.error); + state.list.inflight = false; + state.list.error = action.error; }, - [OPERATION]: (state, action) => { - const { data } = action; - set(state, ['list', 'data'], data.results); - set(state, ['list', 'meta'], assignDate(data.meta)); - set(state, ['list', 'inflight'], false); - set(state, ['list', 'error'], false); + state.list.data = action.data.results; + state.list.meta = assignDate(action.data.meta); + state.list.inflight = false; + state.list.error = false; }, - [OPERATION_INFLIGHT]: (state, action) => { - set(state, ['list', 'inflight'], true); + [OPERATION_INFLIGHT]: (state) => { + state.list.inflight = true; }, [OPERATION_ERROR]: (state, action) => { - set(state, ['list', 'inflight'], false); - set(state, ['list', 'error'], action.error); + state.list.inflight = false; + state.list.error = action.error; }, - [FILTER_OPERATIONS]: (state, action) => { - set(state, ['list', 'params', action.param.key], action.param.value); + state.list.params[action.param.key] = action.param.value; }, [CLEAR_OPERATIONS_FILTER]: (state, action) => { - set(state, ['list', 'params', action.paramKey], null); + delete state.list.params[action.paramKey]; }, - [SEARCH_OPERATIONS]: (state, action) => { - set(state, ['list', 'internal', 'prefix'], action.prefix); + state.list.internal.prefix = action.prefix; + }, + [CLEAR_OPERATIONS_SEARCH]: (state) => { + delete state.list.internal.prefix; }, - [CLEAR_OPERATIONS_SEARCH]: (state, action) => { - set(state, ['list', 'internal', 'prefix'], null); - } }); diff --git a/app/src/js/reducers/pdrs.js b/app/src/js/reducers/pdrs.js index 0e8546ba1..9b08d674b 100644 --- a/app/src/js/reducers/pdrs.js +++ b/app/src/js/reducers/pdrs.js @@ -1,98 +1,83 @@ 'use strict'; -import { set, del } from 'object-path'; -import assignDate from './assign-date'; +import { createReducer } from '@reduxjs/toolkit'; +import assignDate from './utils/assign-date'; +import { + createErrorReducer, + createInflightReducer, + createSuccessReducer, +} from './utils/reducer-creators'; import { PDR, PDR_INFLIGHT, PDR_ERROR, - PDRS, PDRS_INFLIGHT, PDRS_ERROR, - SEARCH_PDRS, CLEAR_PDRS_SEARCH, - FILTER_PDRS, CLEAR_PDRS_FILTER, - PDR_DELETE, PDR_DELETE_INFLIGHT, - PDR_DELETE_ERROR + PDR_DELETE_ERROR, } from '../actions/types'; -import { createReducer } from '@reduxjs/toolkit'; export const initialState = { list: { data: [], meta: {}, - params: {} + params: {}, }, map: {}, search: {}, - deleted: {} + deleted: {}, }; export default createReducer(initialState, { [PDR]: (state, action) => { - const { id, data } = action; - set(state, ['map', id, 'inflight'], false); - set(state, ['map', id, 'data'], assignDate(data)); + state.map[action.id] = { + inflight: false, + data: assignDate(action.data), + }; // https://github.com/nasa/cumulus-dashboard/issues/284 - del(state, ['deleted', id]); + delete state.deleted[action.id]; }, [PDR_INFLIGHT]: (state, action) => { - const { id } = action; - set(state, ['map', id, 'inflight'], true); + state.map[action.id] = { inflight: true }; }, [PDR_ERROR]: (state, action) => { - const { id } = action; - set(state, ['map', id, 'inflight'], false); - set(state, ['map', id, 'error'], action.error); + state.map[action.id] = { + inflight: false, + error: action.error, + }; }, - [PDRS]: (state, action) => { - const { data } = action; - set(state, ['list', 'data'], data.results); - set(state, ['list', 'meta'], assignDate(data.meta)); - set(state, ['list', 'inflight'], false); - set(state, ['list', 'error'], false); + state.list.data = action.data.results; + state.list.meta = assignDate(action.data.meta); + state.list.inflight = false; + state.list.error = false; }, - [PDRS_INFLIGHT]: (state, action) => { - set(state, ['list', 'inflight'], true); + [PDRS_INFLIGHT]: (state) => { + state.list.inflight = true; }, [PDRS_ERROR]: (state, action) => { - set(state, ['list', 'inflight'], false); - set(state, ['list', 'error'], action.error); + state.list.inflight = false; + state.list.error = action.error; }, - [SEARCH_PDRS]: (state, action) => { - set(state, ['list', 'params', 'prefix'], action.prefix); + state.list.params.prefix = action.prefix; }, - [CLEAR_PDRS_SEARCH]: (state, action) => { - set(state, ['list', 'params', 'prefix'], null); + [CLEAR_PDRS_SEARCH]: (state) => { + delete state.list.params.prefix; }, - [FILTER_PDRS]: (state, action) => { - set(state, ['list', 'params', action.param.key], action.param.value); + state.list.params[action.param.key] = action.param.value; }, [CLEAR_PDRS_FILTER]: (state, action) => { - set(state, ['list', 'params', action.paramKey], null); - }, - - [PDR_DELETE]: (state, action) => { - const { id } = action; - set(state, ['deleted', id, 'status'], 'success'); - set(state, ['deleted', id, 'error'], null); - }, - [PDR_DELETE_INFLIGHT]: (state, action) => { - const { id } = action; - set(state, ['deleted', id, 'status'], 'inflight'); + delete state.list.params[action.paramKey]; }, - [PDR_DELETE_ERROR]: (state, action) => { - const { id } = action; - set(state, ['deleted', id, 'status'], 'error'); - set(state, ['deleted', id, 'error'], action.error); - } + [PDR_DELETE]: createSuccessReducer('deleted'), + [PDR_DELETE_INFLIGHT]: createInflightReducer('deleted'), + [PDR_DELETE_ERROR]: createErrorReducer('deleted'), }); diff --git a/app/src/js/reducers/providers.js b/app/src/js/reducers/providers.js index f883414c5..fb55adb43 100644 --- a/app/src/js/reducers/providers.js +++ b/app/src/js/reducers/providers.js @@ -1,50 +1,48 @@ 'use strict'; -import { get, set, del } from 'object-path'; -import assignDate from './assign-date'; +import { get, set } from 'object-path'; +import { createReducer } from '@reduxjs/toolkit'; +import assignDate from './utils/assign-date'; +import { + createClearItemReducer, + createErrorReducer, + createInflightReducer, + createSuccessReducer, +} from './utils/reducer-creators'; import { PROVIDER, PROVIDER_INFLIGHT, PROVIDER_ERROR, - NEW_PROVIDER, NEW_PROVIDER_INFLIGHT, NEW_PROVIDER_ERROR, - PROVIDER_COLLECTIONS, PROVIDER_COLLECTIONS_INFLIGHT, PROVIDER_COLLECTIONS_ERROR, - UPDATE_PROVIDER, UPDATE_PROVIDER_INFLIGHT, UPDATE_PROVIDER_ERROR, UPDATE_PROVIDER_CLEAR, - PROVIDERS, PROVIDERS_INFLIGHT, PROVIDERS_ERROR, - SEARCH_PROVIDERS, CLEAR_PROVIDERS_SEARCH, - FILTER_PROVIDERS, CLEAR_PROVIDERS_FILTER, - PROVIDER_DELETE, PROVIDER_DELETE_INFLIGHT, PROVIDER_DELETE_ERROR, - OPTIONS_PROVIDERGROUP, OPTIONS_PROVIDERGROUP_INFLIGHT, - OPTIONS_PROVIDERGROUP_ERROR + OPTIONS_PROVIDERGROUP_ERROR, } from '../actions/types'; -import { createReducer } from '@reduxjs/toolkit'; export const initialState = { list: { data: [], meta: {}, - params: {} + params: {}, }, dropdowns: {}, map: {}, @@ -54,137 +52,96 @@ export const initialState = { updated: {}, deleted: {}, restarted: {}, - stopped: {} + stopped: {}, }; export default createReducer(initialState, { - [PROVIDER]: (state, action) => { - const { data, id } = action; - set(state, ['map', id, 'inflight'], false); - set(state, ['map', id, 'data'], data); - set(state, ['map', id, 'error'], null); + const { id, data } = action; + + state.map[id] = { + inflight: false, + data, + error: null, + }; + if (get(state, ['deleted', id, 'status']) !== 'error') { - del(state, ['deleted', id]); + delete state.deleted[id]; } }, [PROVIDER_INFLIGHT]: (state, action) => { - const { id } = action; - set(state, ['map', id, 'inflight'], true); - }, - [PROVIDER_ERROR]: (state, action) => { - const { id } = action; - set(state, ['map', id, 'inflight'], false); - set(state, ['map', id, 'error'], action.error); - }, - - [NEW_PROVIDER]: (state, action) => { - const { id } = action; - set(state, ['created', id, 'status'], 'success'); + state.map[action.id] = { inflight: true }; }, - [NEW_PROVIDER_INFLIGHT]: (state, action) => { - const { id } = action; - set(state, ['created', id, 'status'], 'inflight'); - }, - [NEW_PROVIDER_ERROR]: (state, action) => { - const { id } = action; - set(state, ['created', id, 'status'], 'error'); - set(state, ['created', id, 'error'], action.error); - }, - - [PROVIDER_COLLECTIONS]: (state, action) => { - const { data, id } = action; - set(state, ['collections', id, 'inflight'], false); - set(state, ['collections', id, 'data'], data.results.map(c => c.collectionName)); + [PROVIDER_ERROR]: createErrorReducer('map'), + [NEW_PROVIDER]: createSuccessReducer('created'), + [NEW_PROVIDER_INFLIGHT]: createInflightReducer('created'), + [NEW_PROVIDER_ERROR]: createErrorReducer('created'), + [PROVIDER_COLLECTIONS]: (state, { id, data }) => { + state.collections[id] = { data: data.results.map((c) => c.collectionName) }; }, [PROVIDER_COLLECTIONS_INFLIGHT]: (state, action) => { - const { id } = action; - set(state, ['collections', id, 'inflight'], true); + state.collections[action.id] = { inflight: true }; }, [PROVIDER_COLLECTIONS_ERROR]: (state, action) => { - const { id } = action; - set(state, ['collections', id, 'inflight'], false); - set(state, ['collections', id, 'error'], action.error); + state.collections[action.id] = { + inflight: false, + error: action.error, + }; }, - [UPDATE_PROVIDER]: (state, action) => { - const { data, id } = action; - set(state, ['map', id, 'data'], data); - set(state, ['updated', id, 'status'], 'success'); + const { id, data } = action; + state.map[id] = { data }; + state.updated = { ...state.updated, [id]: { status: 'success' } }; }, - [UPDATE_PROVIDER_INFLIGHT]: (state, action) => { - const { id } = action; - set(state, ['updated', id, 'status'], 'inflight'); - }, - [UPDATE_PROVIDER_ERROR]: (state, action) => { - const { id } = action; - set(state, ['updated', id, 'status'], 'error'); - set(state, ['updated', id, 'error'], action.error); - }, - [UPDATE_PROVIDER_CLEAR]: (state, action) => { - const { id } = action; - del(state, ['updated', id]); - }, - + [UPDATE_PROVIDER_INFLIGHT]: createInflightReducer('updated'), + [UPDATE_PROVIDER_ERROR]: createErrorReducer('updated'), + [UPDATE_PROVIDER_CLEAR]: createClearItemReducer('updated'), [PROVIDERS]: (state, action) => { - const { data } = action; - set(state, ['list', 'data'], data.results); - set(state, ['list', 'meta'], assignDate(data.meta)); - set(state, ['list', 'inflight'], false); - set(state, ['list', 'error'], false); + state.list.data = action.data.results; + state.list.meta = assignDate(action.data.meta); + state.list.inflight = false; + state.list.error = false; }, - [PROVIDERS_INFLIGHT]: (state, action) => { - set(state, ['list', 'inflight'], true); + [PROVIDERS_INFLIGHT]: (state) => { + state.list.inflight = true; }, [PROVIDERS_ERROR]: (state, action) => { - set(state, ['list', 'inflight'], false); - set(state, ['list', 'error'], action.error); + state.list.inflight = false; + state.list.error = action.error; }, - [SEARCH_PROVIDERS]: (state, action) => { - set(state, ['list', 'params', 'prefix'], action.prefix); + state.list.params.prefix = action.prefix; }, - [CLEAR_PROVIDERS_SEARCH]: (state, action) => { - set(state, ['list', 'params', 'prefix'], null); + [CLEAR_PROVIDERS_SEARCH]: (state) => { + delete state.list.params.prefix; }, - [FILTER_PROVIDERS]: (state, action) => { - set(state, ['list', 'params', action.param.key], action.param.value); + state.list.params[action.param.key] = action.param.value; }, [CLEAR_PROVIDERS_FILTER]: (state, action) => { - set(state, ['list', 'params', action.paramKey], null); - }, - - [PROVIDER_DELETE]: (state, action) => { - const { id } = action; - set(state, ['deleted', id, 'status'], 'success'); - set(state, ['deleted', id, 'error'], null); + delete state.list.params[action.paramKey]; }, - [PROVIDER_DELETE_INFLIGHT]: (state, action) => { - const { id } = action; - set(state, ['deleted', id, 'status'], 'inflight'); - }, - [PROVIDER_DELETE_ERROR]: (state, action) => { - const { id } = action; - set(state, ['deleted', id, 'status'], 'error'); - set(state, ['deleted', id, 'error'], action.error); - }, - + [PROVIDER_DELETE]: createSuccessReducer('deleted'), + [PROVIDER_DELETE_INFLIGHT]: createInflightReducer('deleted'), + [PROVIDER_DELETE_ERROR]: createErrorReducer('deleted'), [OPTIONS_PROVIDERGROUP]: (state, action) => { - const { data } = action; // Map the list response to an object with key-value pairs like: // displayValue: optionElementValue - const options = data.results.reduce((obj, provider) => { - // Several `results` items can share a `providerName`, but - // these are de-duplciated by the key-value structure - obj[provider.providerName] = provider.providerName; - return obj; - }, { '': '' }); - set(state, ['dropdowns', 'group', 'options'], options); - }, - [OPTIONS_PROVIDERGROUP_INFLIGHT]: () => { }, + const options = action.data.results.reduce( + (obj, provider) => { + // Several `results` items can share a `providerName`, but + // these are de-duplciated by the key-value structure + obj[provider.providerName] = provider.providerName; + return obj; + }, + { '': '' } + ); + + set(state.dropdowns, 'group.options', options); + }, + [OPTIONS_PROVIDERGROUP_INFLIGHT]: () => {}, [OPTIONS_PROVIDERGROUP_ERROR]: (state, action) => { - set(state, ['dropdowns', 'group', 'options'], []); - set(state, ['list', 'error'], action.error); - } + set(state.dropdowns, 'group.options', []); + state.list.error = action.error; + }, }); diff --git a/app/src/js/reducers/reconciliation-reports.js b/app/src/js/reducers/reconciliation-reports.js index 855a99c69..f3d31b06a 100644 --- a/app/src/js/reducers/reconciliation-reports.js +++ b/app/src/js/reducers/reconciliation-reports.js @@ -1,82 +1,73 @@ 'use strict'; -import { set, del } from 'object-path'; -import assignDate from './assign-date'; +import assignDate from './utils/assign-date'; +import { createReducer } from '@reduxjs/toolkit'; import { RECONCILIATION, RECONCILIATION_INFLIGHT, RECONCILIATION_ERROR, - RECONCILIATIONS, RECONCILIATIONS_INFLIGHT, RECONCILIATIONS_ERROR, - SEARCH_RECONCILIATIONS, CLEAR_RECONCILIATIONS_SEARCH, - NEW_RECONCILIATION_INFLIGHT, - NEW_RECONCILIATION + NEW_RECONCILIATION, } from '../actions/types'; -import { createReducer } from '@reduxjs/toolkit'; export const initialState = { list: { data: [], meta: {}, - params: {} + params: {}, }, map: {}, createReportInflight: false, search: {}, - deleted: {} + deleted: {}, }; export default createReducer(initialState, { [RECONCILIATION]: (state, action) => { - const { id, data } = action; - set(state, ['map', id, 'inflight'], false); - set(state, ['map', id, 'data'], assignDate(data)); - del(state, ['deleted', id]); + state.map[action.id] = { + inflight: false, + data: assignDate(action.data), + }; + delete state.deleted[action.id]; }, [RECONCILIATION_INFLIGHT]: (state, action) => { - const { id } = action; - set(state, ['map', id, 'inflight'], true); + state.map[action.id] = { inflight: true }; }, [RECONCILIATION_ERROR]: (state, action) => { - const { id } = action; - set(state, ['map', id, 'inflight'], false); - set(state, ['map', id, 'error'], action.error); + state.map[action.id] = { + inflight: false, + error: action.error, + }; }, - [RECONCILIATIONS]: (state, action) => { - const { data } = action; - // response.results is a array of string filenames - const results = data.results.map((filename) => ({ reconciliationReportName: filename })); - set(state, ['list', 'data'], results); - set(state, ['list', 'meta'], assignDate(data.meta)); - set(state, ['list', 'inflight'], false); - set(state, ['list', 'error'], false); + const reports = action.data.results; + state.list.data = reports; + state.list.meta = assignDate(action.data.meta); + state.list.inflight = false; + state.list.error = false; }, - [RECONCILIATIONS_INFLIGHT]: (state, action) => { - set(state, ['list', 'inflight'], true); + [RECONCILIATIONS_INFLIGHT]: (state) => { + state.list.inflight = true; }, [RECONCILIATIONS_ERROR]: (state, action) => { - set(state, ['list', 'inflight'], false); - set(state, ['list', 'error'], action.error); + state.list.inflight = false; + state.list.error = action.error; }, - [SEARCH_RECONCILIATIONS]: (state, action) => { - set(state, ['list', 'params', 'prefix'], action.prefix); + state.list.params.prefix = action.prefix; }, - [CLEAR_RECONCILIATIONS_SEARCH]: (state, action) => { - set(state, ['list', 'params', 'prefix'], null); + [CLEAR_RECONCILIATIONS_SEARCH]: (state) => { + delete state.list.params.prefix; }, - - [NEW_RECONCILIATION_INFLIGHT]: (state, action) => { - set(state, 'createReportInflight', true); + [NEW_RECONCILIATION_INFLIGHT]: (state) => { + state.createReportInflight = true; + }, + [NEW_RECONCILIATION]: (state) => { + state.createReportInflight = false; }, - - [NEW_RECONCILIATION]: (state, action) => { - set(state, 'createReportInflight', false); - } }); diff --git a/app/src/js/reducers/rules.js b/app/src/js/reducers/rules.js index 14ee3e435..f4bf8ad31 100644 --- a/app/src/js/reducers/rules.js +++ b/app/src/js/reducers/rules.js @@ -1,48 +1,46 @@ 'use strict'; -import { set, del } from 'object-path'; -import assignDate from './assign-date'; -import removeDeleted from './remove-deleted'; +import { createReducer } from '@reduxjs/toolkit'; +import assignDate from './utils/assign-date'; +import removeDeleted from './utils/remove-deleted'; +import { + createClearItemReducer, + createErrorReducer, + createInflightReducer, + createSerialReducer, + createSuccessReducer, +} from './utils/reducer-creators'; import { RULES, RULES_INFLIGHT, RULES_ERROR, - RULE, RULE_INFLIGHT, RULE_ERROR, - UPDATE_RULE, UPDATE_RULE_INFLIGHT, UPDATE_RULE_ERROR, UPDATE_RULE_CLEAR, - NEW_RULE, NEW_RULE_INFLIGHT, NEW_RULE_ERROR, - RULE_DELETE, RULE_DELETE_INFLIGHT, RULE_DELETE_ERROR, - RULE_RERUN, RULE_RERUN_INFLIGHT, RULE_RERUN_ERROR, - RULE_ENABLE, RULE_ENABLE_INFLIGHT, RULE_ENABLE_ERROR, - RULE_DISABLE, RULE_DISABLE_INFLIGHT, RULE_DISABLE_ERROR, - SEARCH_RULES, CLEAR_RULES_SEARCH, FILTER_RULES, - CLEAR_RULES_FILTER + CLEAR_RULES_FILTER, } from '../actions/types'; -import { createReducer } from '@reduxjs/toolkit'; export const initialState = { list: { @@ -50,153 +48,84 @@ export const initialState = { meta: {}, params: {}, inflight: false, - error: false + error: false, }, map: {}, created: {}, updated: {}, deleted: {}, enabled: {}, - disabled: {} + disabled: {}, }; export default createReducer(initialState, { [RULE]: (state, action) => { - const { data, id } = action; - set(state, ['map', id, 'inflight'], false); - set(state, ['map', id, 'data'], data); - del(state, ['deleted', id]); + state.map[action.id] = { + inflight: false, + data: action.data, + }; + delete state.deleted[action.id]; }, [RULE_INFLIGHT]: (state, action) => { - const { id } = action; - set(state, ['map', id, 'inflight'], true); + state.map[action.id] = { inflight: true }; }, [RULE_ERROR]: (state, action) => { - const { id } = action; - set(state, ['map', id, 'inflight'], false); - set(state, ['map', id, 'error'], action.error); + state.map[action.id] = { + inflight: false, + error: action.error, + }; }, - [RULES]: (state, action) => { - const { data } = action; - set(state, ['list', 'data'], removeDeleted('name', data.results, state.deleted)); - set(state, ['list', 'meta'], assignDate(data.meta)); - set(state, ['list', 'inflight'], false); - set(state, ['list', 'error'], false); + state.list.data = removeDeleted('name', action.data.results, state.deleted); + state.list.meta = assignDate(action.data.meta); + state.list.inflight = false; + state.list.error = false; }, - [RULES_INFLIGHT]: (state, action) => { - set(state, ['list', 'inflight'], true); + [RULES_INFLIGHT]: (state) => { + state.list.inflight = true; }, [RULES_ERROR]: (state, action) => { - set(state, ['list', 'inflight'], false); - set(state, ['list', 'error'], action.error); + state.list.inflight = false; + state.list.error = action.error; }, - [UPDATE_RULE]: (state, action) => { - const { data, id } = action; - set(state, ['map', id, 'data'], data); - set(state, ['updated', id, 'status'], 'success'); - }, - [UPDATE_RULE_INFLIGHT]: (state, action) => { - const { id } = action; - set(state, ['updated', id, 'status'], 'inflight'); - }, - [UPDATE_RULE_ERROR]: (state, action) => { - const { id } = action; - set(state, ['updated', id, 'status'], 'error'); - set(state, ['updated', id, 'error'], action.error); - }, - [UPDATE_RULE_CLEAR]: (state, action) => { - const { id } = action; - del(state, ['updated', id]); - }, - - [NEW_RULE]: (state, action) => { - const { id } = action; - set(state, ['created', id, 'status'], 'success'); - }, - [NEW_RULE_INFLIGHT]: (state, action) => { - const { id } = action; - set(state, ['created', id, 'status'], 'inflight'); - }, - [NEW_RULE_ERROR]: (state, action) => { - const { id } = action; - set(state, ['created', id, 'status'], 'error'); - set(state, ['created', id, 'error'], action.error); - }, - - [RULE_DELETE]: (state, action) => { - const { id } = action; - set(state, ['deleted', id, 'status'], 'success'); - set(state, ['deleted', id, 'error'], null); - }, - [RULE_DELETE_INFLIGHT]: (state, action) => { - const { id } = action; - set(state, ['deleted', id, 'status'], 'inflight'); - }, - [RULE_DELETE_ERROR]: (state, action) => { - const { id } = action; - set(state, ['deleted', id, 'status'], 'error'); - set(state, ['deleted', id, 'error'], action.error); - }, - - [RULE_RERUN]: (state, action) => { - const { id } = action; - set(state, ['rerun', id, 'status'], 'success'); - set(state, ['rerun', id, 'error'], null); - }, - [RULE_RERUN_INFLIGHT]: (state, action) => { - const { id } = action; - set(state, ['rerun', id, 'status'], 'inflight'); - }, - [RULE_RERUN_ERROR]: (state, action) => { - const { id } = action; - set(state, ['rerun', id, 'status'], 'error'); - set(state, ['rerun', id, 'error'], action.error); - }, - - [RULE_ENABLE]: (state, action) => { - const { id } = action; - set(state, ['enabled', id, 'status'], 'success'); - set(state, ['enabled', id, 'error'], null); - del(state, ['disbled', id]); - }, - [RULE_ENABLE_INFLIGHT]: (state, action) => { - const { id } = action; - set(state, ['enabled', id, 'status'], 'inflight'); - }, - [RULE_ENABLE_ERROR]: (state, action) => { - const { id } = action; - set(state, ['enabled', id, 'status'], 'error'); - set(state, ['enabled', id, 'error'], action.error); - }, - - [RULE_DISABLE]: (state, action) => { - const { id } = action; - set(state, ['disabled', id, 'status'], 'success'); - set(state, ['disabled', id, 'error'], null); - del(state, ['enabled', id]); - }, - [RULE_DISABLE_INFLIGHT]: (state, action) => { - const { id } = action; - set(state, ['disabled', id, 'status'], 'inflight'); - }, - [RULE_DISABLE_ERROR]: (state, action) => { - const { id } = action; - set(state, ['disabled', id, 'status'], 'error'); - set(state, ['disabled', id, 'error'], action.error); - }, - + state.map[action.id] = { data: action.data }; + state.updated[action.id] = { status: 'success' }; + }, + [UPDATE_RULE_INFLIGHT]: createInflightReducer('updated'), + [UPDATE_RULE_ERROR]: createErrorReducer('updated'), + [UPDATE_RULE_CLEAR]: createClearItemReducer('updated'), + [NEW_RULE]: createSuccessReducer('created'), + [NEW_RULE_INFLIGHT]: createInflightReducer('created'), + [NEW_RULE_ERROR]: createErrorReducer('created'), + [RULE_DELETE]: createSuccessReducer('deleted'), + [RULE_DELETE_INFLIGHT]: createInflightReducer('deleted'), + [RULE_DELETE_ERROR]: createErrorReducer('deleted'), + [RULE_RERUN]: createSuccessReducer('rerun'), + [RULE_RERUN_INFLIGHT]: createInflightReducer('rerun'), + [RULE_RERUN_ERROR]: createErrorReducer('rerun'), + [RULE_ENABLE]: createSerialReducer( + createSuccessReducer('enabled'), + createClearItemReducer('disabled') + ), + [RULE_ENABLE_INFLIGHT]: createInflightReducer('enabled'), + [RULE_ENABLE_ERROR]: createErrorReducer('enabled'), + [RULE_DISABLE]: createSerialReducer( + createSuccessReducer('disabled'), + createClearItemReducer('enabled') + ), + [RULE_DISABLE_INFLIGHT]: createInflightReducer('disabled'), + [RULE_DISABLE_ERROR]: createErrorReducer('disabled'), [SEARCH_RULES]: (state, action) => { - set(state, ['list', 'params', 'prefix'], action.prefix); + state.list.params.prefix = action.prefix; }, - [CLEAR_RULES_SEARCH]: (state, action) => { - set(state, ['list', 'params', 'prefix'], null); + [CLEAR_RULES_SEARCH]: (state) => { + delete state.list.params.prefix; }, [FILTER_RULES]: (state, action) => { - set(state, ['list', 'params', action.param.key], action.param.value); + state.list.params[action.param.key] = action.param.value; }, [CLEAR_RULES_FILTER]: (state, action) => { - set(state, ['list', 'params', action.paramKey], null); - } + delete state.list.params[action.paramKey]; + }, }); diff --git a/app/src/js/reducers/schema.js b/app/src/js/reducers/schema.js index 8acf3b4f2..3e0fa8bf5 100644 --- a/app/src/js/reducers/schema.js +++ b/app/src/js/reducers/schema.js @@ -1,22 +1,15 @@ 'use strict'; -import { - SCHEMA -} from '../actions/types'; +import { SCHEMA } from '../actions/types'; import { createReducer } from '@reduxjs/toolkit'; export const initialState = {}; export default createReducer(initialState, { - [SCHEMA]: (state, action) => { - const { config } = action; - return { ...state, [key(config.url)]: action.data }; + state[key(action.config.url)] = action.data; } }); // sample config url is `{rootUrl}/schema/{type}` -function key (url) { - const parts = url.split('/'); - return parts[parts.length - 1]; -} +const key = (url) => url.substr(url.lastIndexOf('/') + 1); diff --git a/app/src/js/reducers/stats.js b/app/src/js/reducers/stats.js index d0042f6fb..87899bc22 100644 --- a/app/src/js/reducers/stats.js +++ b/app/src/js/reducers/stats.js @@ -1,81 +1,59 @@ 'use strict'; -import assignDate from './assign-date'; -import { set } from 'object-path'; -import { createReducer } from '@reduxjs/toolkit'; -import cloneDeep from 'lodash.clonedeep'; +import assignDate from './utils/assign-date'; +import { createReducer } from '@reduxjs/toolkit'; import { STATS, STATS_INFLIGHT, STATS_ERROR, - COUNT, COUNT_INFLIGHT, - COUNT_ERROR + COUNT_ERROR, } from '../actions/types'; export const initialState = { stats: { - data: { // overview statistics from `/stats` + data: { + // overview statistics from `/stats` collections: {}, errors: {}, granules: {}, processingTime: {}, }, inflight: false, - error: null + error: null, }, - count: { // aggregate stats from /stats/aggregate?type=&field=status + count: { + // aggregate stats from /stats/aggregate?type=&field=status data: {}, inflight: false, - error: null - } + error: null, + }, }; export default createReducer(initialState, { - // These actions' reducers are a bit strange because the createReducer - // function was not picking up the deep mutation, and because the wrapper - // thought the underlying state was unchanged (it wasn't) the wrapper - // returned the original object and the unit tests failed. cloning the state - // on entry was a hacky way to force the wrapper to recognize new state in - // each reducer. These could be cleaned up, but be sure the tests still pass - // as they are. [STATS]: (state, action) => { - let newState = cloneDeep(state); - const stats = { data: assignDate(action.data), inflight: false, error: null }; - newState = Object.assign({}, newState, { stats }); - return newState; + state.stats.data = assignDate(action.data); + state.stats.inflight = false; + state.stats.error = null; }, [STATS_INFLIGHT]: (state) => { - let newState = cloneDeep(state); - const stats = { data: newState.stats.data, inflight: true, error: state.stats.error }; - newState = Object.assign({}, newState, { stats }); - return newState; + state.stats.inflight = true; }, [STATS_ERROR]: (state, action) => { - let newState = cloneDeep(state); - const stats = { data: newState.stats.data, inflight: false, error: action.error }; - newState = Object.assign({}, newState, { stats }); - return newState; + state.stats.inflight = false; + state.stats.error = action.error; }, - [COUNT]: (state, action) => { - let newState = cloneDeep(state); - const count = Object.assign({}, newState.count, { inflight: false, error: null }); - set(count, ['data', action.config.qs.type], action.data); - newState = Object.assign({}, newState, { count }); - return newState; + state.count.inflight = false; + state.count.error = null; + state.count.data[action.config.qs.type] = action.data; }, [COUNT_INFLIGHT]: (state) => { - let newState = cloneDeep(state); - const count = { data: newState.count.data, inflight: true, error: newState.count.error }; - newState = Object.assign({}, newState, { count }); - return newState; + state.count.inflight = true; }, [COUNT_ERROR]: (state, action) => { - let newState = cloneDeep(state); - const count = { data: newState.count.data, inflight: false, error: action.error }; - newState = Object.assign({}, newState, { count }); - return newState; - } + state.count.inflight = false; + state.count.error = action.error; + }, }); diff --git a/app/src/js/reducers/timer.js b/app/src/js/reducers/timer.js new file mode 100644 index 000000000..63ec5f0c6 --- /dev/null +++ b/app/src/js/reducers/timer.js @@ -0,0 +1,22 @@ +'use strict'; +import { createReducer } from '@reduxjs/toolkit'; +import { TIMER_START, TIMER_STOP, TIMER_SET_COUNTDOWN } from '../actions/types'; + +export const initialState = { + running: false, + seconds: -1, +}; + +export default createReducer(initialState, { + [TIMER_STOP]: (state, action) => { + state.running = false; + state.seconds = -1; + }, + [TIMER_START]: (state, action) => { + state.running = true; + state.seconds = action.secondsToRefresh; + }, + [TIMER_SET_COUNTDOWN]: (state, action) => { + state.seconds = action.secondsToRefresh; + }, +}); diff --git a/app/src/js/reducers/assign-date.js b/app/src/js/reducers/utils/assign-date.js similarity index 100% rename from app/src/js/reducers/assign-date.js rename to app/src/js/reducers/utils/assign-date.js diff --git a/app/src/js/reducers/config.js b/app/src/js/reducers/utils/config.js similarity index 64% rename from app/src/js/reducers/config.js rename to app/src/js/reducers/utils/config.js index a9604e7a7..efe3924ae 100644 --- a/app/src/js/reducers/config.js +++ b/app/src/js/reducers/utils/config.js @@ -1,5 +1,5 @@ 'use strict'; -import _config from '../config'; +import _config from '../../config'; export default (state = { ..._config }) => state; diff --git a/app/src/js/reducers/utils/reducer-creators.js b/app/src/js/reducers/utils/reducer-creators.js new file mode 100644 index 000000000..bcd7a0e00 --- /dev/null +++ b/app/src/js/reducers/utils/reducer-creators.js @@ -0,0 +1,197 @@ +'use strict'; +import { del } from 'object-path'; + +/** + * Returns the value of the `id` property of the specified object. + * + * @param {object} - object to extract the `id` property from + * @returns {*} value of the `id` property of the specified object + */ +const idProp = ({ id }) => id; + +/** + * Returns a reducer function that delete's a nested object from the value + * of the specified state property, where the key of the nested object is + * obtained by applying the specified ID selector function to the action. + * + * @example + * createReducer(initialState, { + * [MY_OBJECT_CLEAR]: createClearItemReducer('map'), + * // The line above is equivalent to the following + * [MY_OBJECT_CLEAR]: (state, action) => { + * delete state.map[action.id]; + * }, + * ... + * } + * + * @param {string} stateProp - state property that contains nested objects + * @param {function} [idSelector] - function to apply to the action object to + * obtain an ID value for selecting a nested object from the value of the + * specified state property (defaults to selecting the `id` property of the + * action) + * @returns {function} reducer function that takes a state and an action and + * mutates the state by deleting the nested object of the specified state + * property where the key (property name) of the nested object is obtained + * by applying the ID selector function to the action + */ +export const createClearItemReducer = (stateProp, idSelector = idProp) => { + return (state, action) => { + const id = idSelector(action); + del(state, [stateProp, id]); + }; +}; + +/** + * Returns a reducer function that sets the status property of a nested state + * object to `'inflight'`. The "top level" state object is associated with the + * specified state property, and the key of the nested object within the top + * level object is obtained by applying the specified ID selector function to + * the action object (which defaults to getting the value of the action's `id` + * property). + * + * @example + * createReducer(initialState, { + * [NEW_PROVIDER_INFLIGHT]: createInflightReducer('created'), + * // The line above is equivalent to the following + * [NEW_PROVIDER_INFLIGHT]: (state, action) => { + * state.created[action.id] = { status: 'inflight' }; + * }, + * ... + * } + * + * @param {string} stateProp - state property that contains nested objects + * @param {function} [idSelector] - function to apply to the action object to + * obtain an ID value for selecting a nested object from the value of the + * specified state property (defaults to selecting the `id` property of the + * action) + * @returns {function} reducer function that takes a state and an action and + * mutates the state by setting the `status` of the nested object to + * `'inflight'` + */ +export const createInflightReducer = (stateProp, idSelector = idProp) => { + return (state, action) => { + const id = idSelector(action); + state[stateProp] = { ...state[stateProp], [id]: { status: 'inflight' } }; + }; +}; + +/** + * Returns a reducer function that sets the `status` property of a nested state + * object to `'success'`, and also sets the `data` property to data found in + * the action, if any. The "top level" state object is associated with the + * specified state property, and the key of the nested object within the top + * level object is obtained by applying the specified ID selector function to + * the action object (which defaults to getting the value of the action's `id` + * property). + * + * @example + * createReducer(initialState, { + * [NEW_PROVIDER]: createSuccessReducer('created'), + * // The line above is equivalent to the following + * [NEW_PROVIDER]: (state, action) => { + * state.created[action.id] = { + * status: 'success', + * data: action.data + * }; + * }, + * ... + * } + * + * @param {string} stateProp - state property that contains nested objects + * @param {function} [idSelector] - function to apply to the action object to + * obtain an ID value for selecting a nested object from the value of the + * specified state property (defaults to selecting the `id` property of the + * action) + * @returns {function} reducer function that takes a state and an action and + * mutates the state by setting the `status` of the nested object to + * `'success'`, and the `data` property to the value of the action's `data` + * property, if it has one + */ +export const createSuccessReducer = (stateProp, idSelector = idProp) => { + return (state, action) => { + const id = idSelector(action); + state[stateProp] = { + ...state[stateProp], + [id]: { + status: 'success', + error: null, + data: action.data, + } + }; + }; +}; + +/** + * Returns a reducer function that sets the `status` property of a nested state + * object to `'error'`, and also sets the `error` property to error found in + * the action, if any. The "top level" state object is associated with the + * specified state property, and the key of the nested object within the top + * level object is obtained by applying the specified ID selector function to + * the action object (which defaults to getting the value of the action's `id` + * property). + * + * @example + * createReducer(initialState, { + * [NEW_PROVIDER_ERROR]: createErrorReducer('created'), + * // The line above is equivalent to the following + * [NEW_PROVIDER_ERROR]: (state, action) => { + * state.created[action.id] = { + * status: 'error', + * error: action.error + * }; + * }, + * ... + * } + * + * @param {string} stateProp - state property that contains nested objects + * @param {function} [idSelector] - function to apply to the action object to + * obtain an ID value for selecting a nested object from the value of the + * specified state property (defaults to selecting the `id` property of the + * action) + * @returns {function} reducer function that takes a state and an action and + * mutates the state by setting the `status` of the nested object to + * `'error'`, and the `error` property to the value of the action's `error` + * property, if it has one + */ +export const createErrorReducer = (stateProp, idSelector = idProp) => { + return (state, action) => { + const id = idSelector(action); + state[stateProp] = { + ...state[stateProp], + [id]: { + status: 'error', + error: action.error, + } + }; + }; +}; + +/** + * Returns a reducer function that calls all of the specified reducers (given + * as separate arguments, _not_ as a single array argument) in the order they + * appear as arguments. + * + * @example + * createReducer(initialState, { + * [ACTION_NAME]: createSerialReducer( + * createSuccessReducer('enabled'), + * createClearItemReducer('disabled') + * ), + * // The lines above are equivalent to the following + * [ACTION_NAME]: (state, action) => { + * const successReducer = createSuccessReducer('enabled'); + * const clearReducer = createClearItemReducer('disabled'); + * successReducer(state, action); + * clearReducer(state, action); + * }, + * ... + * } + * + * @param {...any} reducers - reducer functions to be invoked in the order + * they are specified as individual arguments, each of which is passed a + * state and action, respectively + */ +export const createSerialReducer = (...reducers) => { + return (state, action) => + reducers.forEach((reducer) => reducer(state, action)); +}; diff --git a/app/src/js/reducers/remove-deleted.js b/app/src/js/reducers/utils/remove-deleted.js similarity index 100% rename from app/src/js/reducers/remove-deleted.js rename to app/src/js/reducers/utils/remove-deleted.js diff --git a/app/src/js/reducers/workflows.js b/app/src/js/reducers/workflows.js index 44a83128c..3545924b0 100644 --- a/app/src/js/reducers/workflows.js +++ b/app/src/js/reducers/workflows.js @@ -1,13 +1,13 @@ 'use strict'; -import { set } from 'object-path'; + +import { createReducer } from '@reduxjs/toolkit'; import { WORKFLOWS, WORKFLOWS_INFLIGHT, WORKFLOWS_ERROR, SEARCH_WORKFLOWS, - CLEAR_WORKFLOWS_SEARCH + CLEAR_WORKFLOWS_SEARCH, } from '../actions/types'; -import { createReducer } from '@reduxjs/toolkit'; export const initialState = { list: { @@ -20,49 +20,48 @@ export const initialState = { searchString: null, }; -function createMap (data) { - const map = {}; - data.forEach(d => { - map[d.name] = d; - }); - return map; -} +const mapByName = (data) => + data.reduce((map, datum) => ({ ...map, [datum.name]: datum }), {}); /** - * Filters intput data by filterstring on the data'a object's name. -* - * @param {Array} rawData - An array of objects with a name parameter. - * @param {string} filterString - a string to check if name includes. - * @returns {Array} Filtered Array of rawData's objects who's names include filterString. + * Returns all items from specified data array where each item's name property + * contains the specified filter string, or the original array if no filter + * string is specified. + * + * @param {Array} data - array of objects with a name parameter + * @param {string} filterString - string to check if name includes + * @returns {Array} array of data's objects with names that include filterString */ -export const filterData = (rawData, filterString) => { - if (filterString !== null) { - return rawData.filter(d => d.name && d.name.toLowerCase().includes(filterString.toLowerCase())); - } - return rawData; -}; +export const filterData = (data, filterString) => + !filterString + ? data + : data.filter( + (item) => + item.name && + item.name.toLowerCase().includes(filterString.toLowerCase()) + ); export default createReducer(initialState, { [WORKFLOWS]: (state, action) => { - const { data: rawData } = action; - const data = filterData(rawData, state.searchString); - set(state, 'map', createMap(data)); - set(state, ['list', 'data'], data); - set(state, ['list', 'meta'], { queriedAt: Date.now() }); - set(state, ['list', 'inflight'], false); - set(state, ['list', 'error'], false); + const filteredData = filterData(action.data, state.searchString); + + state.map = mapByName(filteredData); + state.list.data = filteredData; + state.list.meta = { queriedAt: Date.now() }; + state.list.inflight = false; + state.list.error = false; }, [WORKFLOWS_INFLIGHT]: (state) => { - set(state, ['list', 'inflight'], true); + state.list.inflight = true; }, [WORKFLOWS_ERROR]: (state, action) => { - set(state, ['list', 'inflight'], false); - set(state, ['list', 'error'], action.error); + state.list.inflight = false; + state.list.error = action.error; }, [SEARCH_WORKFLOWS]: (state, action) => { - set(state, ['searchString'], action.searchString); + state.searchString = action.searchString; }, [CLEAR_WORKFLOWS_SEARCH]: (state) => { - set(state, ['searchString'], null); + state.searchString = null; }, }); diff --git a/app/src/js/utils/format.js b/app/src/js/utils/format.js index b8fd95692..5d8351ea9 100644 --- a/app/src/js/utils/format.js +++ b/app/src/js/utils/format.js @@ -13,6 +13,11 @@ export const fullDate = function (datestring) { return moment(datestring).format('kk:mm:ss MM/DD/YY'); }; +export const dateOnly = function (datestring) { + if (!datestring) { return nullValue; } + return moment(datestring).format('MM/DD/YYYY'); +}; + export const parseJson = function (jsonString) { const parsed = JSON.parse(jsonString); return JSON.stringify(parsed, null, 2); diff --git a/app/src/js/utils/kibana.js b/app/src/js/utils/kibana.js index 51dd8cb88..8d6556a45 100644 --- a/app/src/js/utils/kibana.js +++ b/app/src/js/utils/kibana.js @@ -85,14 +85,14 @@ export const kibanaGatewayAccessErrorsLink = (cumulusInstanceMeta, datepicker) = if (!kibanaConfigured(cumulusInstanceMeta)) return ''; const stackName = cumulusInstanceMeta.stackName; const timeInterval = formatKibanaDate(datepicker); - return `${_config.kibanaRoot}/app/kibana#/discover?_g=(refreshInterval:(pause:!t,value:0),time:(${timeInterval}))&_a=(columns:!(message),filters:!(('$state':(store:appState),meta:(alias:!n,disabled:!f,index:${stackName},key:_index,negate:!f,params:(query:'${stackName}-cloudwatch*',type:phrase),type:phrase,value:'${stackName}-cloudwatch*'),query:(match:(_index:(query:'${stackName}-cloudwatch*',type:phrase)))),('$state':(store:appState),meta:(alias:!n,disabled:!f,index:${stackName},key:logGroup,negate:!f,params:(query:'%22API%5C-Gateway%5C-Execution*%22',type:phrase),type:phrase,value:'%22API%5C-Gateway%5C-Execution*%22'),query:(match:(logGroup:(query:'%22API%5C-Gateway%5C-Execution*%22',type:phrase))))),index:${stackName},interval:auto,query:(language:lucene,query:'status:%5B400%20TO%20599%5D'),sort:!('@timestamp',desc))`; + return `${_config.kibanaRoot}/app/kibana#/discover?_g=(refreshInterval:(pause:!t,value:0),time:(${timeInterval}))&_a=(columns:!(message),filters:!(('$state':(store:appState),meta:(alias:!n,disabled:!f,index:${stackName},key:_index,negate:!f,params:(query:'${stackName}-cloudwatch*',type:phrase),type:phrase,value:'${stackName}-cloudwatch*'),query:(match:(_index:(query:'${stackName}-cloudwatch*',type:phrase)))),('$state':(store:appState),meta:(alias:!n,disabled:!f,index:${stackName},key:logGroup,negate:!f,params:(query:'%22API%5C-Gateway%5C-Execution*%22',type:phrase),type:phrase,value:'%22API%5C-Gateway%5C-Execution*%22'),query:(match:(logGroup:(query:'%22API%5C-Gateway%5C-Execution*%22',type:phrase))))),index:${stackName},interval:auto,query:(language:lucene,query:'statusCode:%5B400%20TO%20599%5D'),sort:!('@timestamp',desc))`; }; export const kibanaGatewayAccessSuccessesLink = (cumulusInstanceMeta, datepicker) => { if (!kibanaConfigured(cumulusInstanceMeta)) return ''; const stackName = cumulusInstanceMeta.stackName; const timeInterval = formatKibanaDate(datepicker); - return `${_config.kibanaRoot}/app/kibana#/discover?_g=(refreshInterval:(pause:!t,value:0),time:(${timeInterval}))&_a=(columns:!(message),filters:!(('$state':(store:appState),meta:(alias:!n,disabled:!f,index:${stackName},key:_index,negate:!f,params:(query:'${stackName}-cloudwatch*',type:phrase),type:phrase,value:'${stackName}-cloudwatch*'),query:(match:(_index:(query:'${stackName}-cloudwatch*',type:phrase)))),('$state':(store:appState),meta:(alias:!n,disabled:!f,index:${stackName},key:logGroup,negate:!f,params:(query:'%22API%5C-Gateway%5C-Execution*%22',type:phrase),type:phrase,value:'%22API%5C-Gateway%5C-Execution*%22'),query:(match:(logGroup:(query:'%22API%5C-Gateway%5C-Execution*%22',type:phrase))))),index:${stackName},interval:auto,query:(language:lucene,query:'status:%5B200%20TO%20399%5D'),sort:!('@timestamp',desc))`; + return `${_config.kibanaRoot}/app/kibana#/discover?_g=(refreshInterval:(pause:!t,value:0),time:(${timeInterval}))&_a=(columns:!(message),filters:!(('$state':(store:appState),meta:(alias:!n,disabled:!f,index:${stackName},key:_index,negate:!f,params:(query:'${stackName}-cloudwatch*',type:phrase),type:phrase,value:'${stackName}-cloudwatch*'),query:(match:(_index:(query:'${stackName}-cloudwatch*',type:phrase)))),('$state':(store:appState),meta:(alias:!n,disabled:!f,index:${stackName},key:logGroup,negate:!f,params:(query:'%22API%5C-Gateway%5C-Execution*%22',type:phrase),type:phrase,value:'%22API%5C-Gateway%5C-Execution*%22'),query:(match:(logGroup:(query:'%22API%5C-Gateway%5C-Execution*%22',type:phrase))))),index:${stackName},interval:auto,query:(language:lucene,query:'statusCode:%5B200%20TO%20399%5D'),sort:!('@timestamp',desc))`; }; export const kibanaAllLogsLink = (cumulusInstanceMeta) => { diff --git a/app/src/js/utils/table-config/collections.js b/app/src/js/utils/table-config/collections.js index 6481c58f9..83dd0a747 100644 --- a/app/src/js/utils/table-config/collections.js +++ b/app/src/js/utils/table-config/collections.js @@ -85,7 +85,7 @@ export const bulkActions = function (collections) { history, isOnModalConfirm, isOnModalComplete, - error, + errorMessage, results, }) => { const modalOptions = {}; @@ -110,7 +110,7 @@ export const bulkActions = function (collections) { modalOptions.children = ; } } else if (isOnModalComplete) { - modalOptions.children = ; + modalOptions.children = ; // since cancel button closes the modal, let's use that. modalOptions.hasConfirmButton = false; modalOptions.cancelButtonClass = 'button--green'; diff --git a/app/src/js/utils/table-config/granules.js b/app/src/js/utils/table-config/granules.js index 369bf56be..280063f0d 100644 --- a/app/src/js/utils/table-config/granules.js +++ b/app/src/js/utils/table-config/granules.js @@ -10,7 +10,8 @@ import { nullValue, displayCase, collectionLink, - granuleLink + granuleLink, + providerLink } from '../format'; import { reingestGranule, @@ -47,6 +48,11 @@ export const tableColumns = [ accessor: row => collectionLink(row.collectionId), id: 'collectionId' }, + { + Header: 'Provider', + accessor: row => providerLink(row.provider), + id: 'provider' + }, { Header: 'Execution', accessor: row => link, @@ -122,7 +128,7 @@ export const recoverAction = (granules, config) => ({ confirm: confirmRecover }); -const confirmReingest = (d) => `Reingest ${d} granule${d > 1 ? 's' : ''}?`; +const confirmReingest = (d) => `Reingest ${d} Granule${d > 1 ? 's' : ''}?`; const confirmApply = (d) => `Run workflow on ${d} granules?`; const confirmRemove = (d) => `Remove ${d} granule(s) from ${strings.cmr}?`; const confirmDelete = (d) => `Delete ${d} granule(s)?`; @@ -176,7 +182,8 @@ const granuleModalJourney = ({ history, isOnModalConfirm, isOnModalComplete, - error, + errorMessage, + errors, results, closeModal }) => { @@ -186,15 +193,15 @@ const granuleModalJourney = ({ modalOptions.children = ; } if (isOnModalComplete) { - modalOptions.children = ; - modalOptions.hasConfirmButton = !error; - modalOptions.title = (error ? 'Error' : 'Complete'); + modalOptions.children = ; + modalOptions.hasConfirmButton = !errorMessage; + modalOptions.title = 'Reingest Granule(s)'; modalOptions.cancelButtonText = 'Close'; - if (!error) { + if (!errorMessage) { modalOptions.confirmButtonText = (selected.length > 1) ? 'View Running' : 'View Granule'; modalOptions.cancelButtonClass = 'button--green'; modalOptions.confirmButtonClass = 'button__goto'; - modalOptions.onConfirm = setOnConfirm({ history, selected, error, closeModal }); + modalOptions.onConfirm = setOnConfirm({ history, selected, errorMessage, closeModal }); } } return modalOptions; diff --git a/app/src/js/utils/table-config/reconciliation-reports.js b/app/src/js/utils/table-config/reconciliation-reports.js index 8e64b4b5b..6010786d9 100644 --- a/app/src/js/utils/table-config/reconciliation-reports.js +++ b/app/src/js/utils/table-config/reconciliation-reports.js @@ -2,12 +2,32 @@ import React from 'react'; import { Link } from 'react-router-dom'; -import { nullValue } from '../format'; +import { nullValue, dateOnly } from '../format'; export const tableColumns = [ { - Header: 'Report filename', - accessor: row => {row.reconciliationReportName} + Header: 'Name', + id: 'name', + accessor: row => {row.name} + }, + { + Header: 'Report Type', + accessor: 'type' + }, + { + Header: 'Status', + accessor: 'status' + }, + { + Header: 'Date Generated', + id: 'createdAt', + accessor: row => dateOnly(row.createdAt) + }, + { + Header: 'Download Report' + }, + { + Header: 'Delete Report' } ]; diff --git a/audit-ci.json b/audit-ci.json new file mode 100644 index 000000000..bebf3d721 --- /dev/null +++ b/audit-ci.json @@ -0,0 +1,6 @@ +{ + "high": true, + "pass-enoaudit": true, + "retry-count": 20, + "whitelist": [] +} diff --git a/cypress/fixtures/rule-error.json b/cypress/fixtures/rule-error.json new file mode 100644 index 000000000..aac674001 --- /dev/null +++ b/cypress/fixtures/rule-error.json @@ -0,0 +1,3 @@ +{ + "error": "Something bad totally happened." +} diff --git a/cypress/fixtures/rule-success.json b/cypress/fixtures/rule-success.json new file mode 100644 index 000000000..c24e3788a --- /dev/null +++ b/cypress/fixtures/rule-success.json @@ -0,0 +1,19 @@ +{ + "createdAt": 1589212222652, + "workflow": "HelloWorldWorkflow", + "provider": "PODAAC_SWOT", + "meta": { + "cnmResponseStream": "test-src-integration-cnmResponseStream" + }, + "name": "MOD09GK_TEST_kinesisRule", + "rule": { + "type": "onetime", + "value": "none" + }, + "collection": { + "name": "MOD09GK", + "version": "006" + }, + "state": "ENABLED", + "updatedAt": 1589212222701 +} diff --git a/cypress/fixtures/seeds/reconciliation-reports/inventoryReport-20200114T202529026.json b/cypress/fixtures/seeds/reconciliation-reports/inventoryReport-20200114T202529026.json new file mode 100644 index 000000000..63baa24a3 --- /dev/null +++ b/cypress/fixtures/seeds/reconciliation-reports/inventoryReport-20200114T202529026.json @@ -0,0 +1,2323 @@ +{ + "reportStartTime": "2020-01-14T20:25:29.026Z", + "reportEndTime": "2020-01-14T20:25:35.573Z", + "status": "SUCCESS", + "error": null, + "filesInCumulus": { + "okCount": 129, + "onlyInS3": [ + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1573838955288/MOD09GQ.A3119781.haeynr.006.4074740546315.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1574358566826/MOD09GQ.A8013695.K4N0ey.006.3127282204118.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1574359092072/MOD09GQ.A8702100.fZBWNI.006.3775135520157.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1574359380076/MOD09GQ.A9699053.vVDeTY.006.4873356130094.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1574377023900/MOD09GQ.A4940969.yRnTIO.006.6074235721878.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1574381176138/MOD09GQ.A5336057.nAG4KK.006.3221004503457.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1574712390341/MOD09GQ.A3493490.Xo2qAN.006.9380282039899.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1574716254780/MOD09GQ.A5754494.1nimTh.006.5626987564096.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1574717133091/MOD09GQ.A7767142.CYxJFA.006.2633356778897.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575912896952/MOD09GQ.A7399114.BmDCbe.006.9892042967204.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575923953772/MOD09GQ.A4805149.9oDhyh.006.9022510397676.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575925438077/MOD09GQ.A5542657.kyhjj1.006.5537130666716.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575926279043/MOD09GQ.A9109322.4iOCj1.006.8298263671181.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575927241619/MOD09GQ.A6015560.1qb9nY.006.2220280411055.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575992158393/MOD09GQ.A3341699.cCjc1D.006.4406545864663.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575992637033/MOD09GQ.A1138952.gpR2e1.006.4518044814416.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575993430080/MOD09GQ.A9946950.mAlY72.006.1085172309558.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575993900166/MOD09GQ.A3810090.EW85di.006.3466133695305.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575994615809/MOD09GQ.A7333345.RckwyJ.006.0332555268735.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575995102177/MOD09GQ.A9133222.xSOAC_.006.7143442454933.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575996898914/MOD09GQ.A4671964.mcRJii.006.3575074548289.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575999558578/MOD09GQ.A5908420.1mVEOo.006.0961024812410.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576000334935/MOD09GQ.A5395888.IfjqmW.006.6212087429175.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576005735223/MOD09GQ.A5675548.0G4ccz.006.6657554508083.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576006501720/MOD09GQ.A5778024.foNw7Q.006.6574105768023.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576007039732/MOD09GQ.A0573928.tG823W.006.8959114868194.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576007578106/MOD09GQ.A8200321.wvugBk.006.2104901543407.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576012842378/MOD09GQ.A0087634.eCMzPL.006.7194649784728.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576015468515/MOD09GQ.A1775543.QmnKWZ.006.6004195429297.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576088588648/MOD09GQ.A5486655.lMAgW3.006.1459975841058.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576102296412/MOD09GQ.A5622767.jVW893.006.2184575670628.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576105218713/MOD09GQ.A4452652.1ykqZ3.006.2451039858926.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576109440469/MOD09GQ.A2592006.XKcNf_.006.1485654940189.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576182120916/MOD09GQ.A5826214.xtwugO.006.6261686801168.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576249014314/MOD09GQ.A6210996.nHGu8O.006.3174297550568.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576249970860/MOD09GQ.A8421402.t6nFKh.006.3037894989051.hdf.met", + "s3://mhs3-protected/495c7321334171d867b0dcdf3ad5cc21e9cef365", + "s3://mhs3-protected/DEMO/sample-data-a.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1573838955288/MOD09GQ.A3119781.haeynr.006.4074740546315.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1573838955288/MOD09GQ.A3119781.haeynr.006.4074740546315.hdf.v20191115T173114000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1573838955288/MOD09GQ.A3119781.haeynr.006.4074740546315.hdf.v20191115T173129000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1574358566826/MOD09GQ.A8013695.K4N0ey.006.3127282204118.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1574358566826/MOD09GQ.A8013695.K4N0ey.006.3127282204118.hdf.v20191121T175043000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1574359092072/MOD09GQ.A8702100.fZBWNI.006.3775135520157.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1574359092072/MOD09GQ.A8702100.fZBWNI.006.3775135520157.hdf.v20191121T175828000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1574359092072/MOD09GQ.A8702100.fZBWNI.006.3775135520157.hdf.v20191121T175845000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1574359380076/MOD09GQ.A9699053.vVDeTY.006.4873356130094.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1574359380076/MOD09GQ.A9699053.vVDeTY.006.4873356130094.hdf.v20191121T180329000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1574359380076/MOD09GQ.A9699053.vVDeTY.006.4873356130094.hdf.v20191121T180346000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1574377023900/MOD09GQ.A4940969.yRnTIO.006.6074235721878.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1574377023900/MOD09GQ.A4940969.yRnTIO.006.6074235721878.hdf.v20191121T225740000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1574377023900/MOD09GQ.A4940969.yRnTIO.006.6074235721878.hdf.v20191121T225757000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1574381176138/MOD09GQ.A5336057.nAG4KK.006.3221004503457.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1574381176138/MOD09GQ.A5336057.nAG4KK.006.3221004503457.hdf.v20191122T000648000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1574381176138/MOD09GQ.A5336057.nAG4KK.006.3221004503457.hdf.v20191122T000705000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1574712390341/MOD09GQ.A3493490.Xo2qAN.006.9380282039899.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1574712390341/MOD09GQ.A3493490.Xo2qAN.006.9380282039899.hdf.v20191125T200714000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1574712390341/MOD09GQ.A3493490.Xo2qAN.006.9380282039899.hdf.v20191125T200732000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1574716254780/MOD09GQ.A5754494.1nimTh.006.5626987564096.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1574716254780/MOD09GQ.A5754494.1nimTh.006.5626987564096.hdf.v20191125T211128000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1574716254780/MOD09GQ.A5754494.1nimTh.006.5626987564096.hdf.v20191125T211146000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1574717133091/MOD09GQ.A7767142.CYxJFA.006.2633356778897.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1574717133091/MOD09GQ.A7767142.CYxJFA.006.2633356778897.hdf.v20191125T212558000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1574717133091/MOD09GQ.A7767142.CYxJFA.006.2633356778897.hdf.v20191125T212615000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575912896952/MOD09GQ.A7399114.BmDCbe.006.9892042967204.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575912896952/MOD09GQ.A7399114.BmDCbe.006.9892042967204.hdf.v20191209T173526000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575912896952/MOD09GQ.A7399114.BmDCbe.006.9892042967204.hdf.v20191209T173547000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575923953772/MOD09GQ.A4805149.9oDhyh.006.9022510397676.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575923953772/MOD09GQ.A4805149.9oDhyh.006.9022510397676.hdf.v20191209T203947000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575923953772/MOD09GQ.A4805149.9oDhyh.006.9022510397676.hdf.v20191209T204003000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575925438077/MOD09GQ.A5542657.kyhjj1.006.5537130666716.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575925438077/MOD09GQ.A5542657.kyhjj1.006.5537130666716.hdf.v20191209T210419000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575925438077/MOD09GQ.A5542657.kyhjj1.006.5537130666716.hdf.v20191209T210448000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575926279043/MOD09GQ.A9109322.4iOCj1.006.8298263671181.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575926279043/MOD09GQ.A9109322.4iOCj1.006.8298263671181.hdf.v20191209T211818000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575926279043/MOD09GQ.A9109322.4iOCj1.006.8298263671181.hdf.v20191209T211833000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575927241619/MOD09GQ.A6015560.1qb9nY.006.2220280411055.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575927241619/MOD09GQ.A6015560.1qb9nY.006.2220280411055.hdf.v20191209T213423000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575927241619/MOD09GQ.A6015560.1qb9nY.006.2220280411055.hdf.v20191209T213438000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575992158393/MOD09GQ.A3341699.cCjc1D.006.4406545864663.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575992158393/MOD09GQ.A3341699.cCjc1D.006.4406545864663.hdf.v20191210T153630000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575992158393/MOD09GQ.A3341699.cCjc1D.006.4406545864663.hdf.v20191210T153645000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575992637033/MOD09GQ.A1138952.gpR2e1.006.4518044814416.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575992637033/MOD09GQ.A1138952.gpR2e1.006.4518044814416.hdf.v20191210T154417000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575992637033/MOD09GQ.A1138952.gpR2e1.006.4518044814416.hdf.v20191210T154432000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575993430080/MOD09GQ.A9946950.mAlY72.006.1085172309558.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575993430080/MOD09GQ.A9946950.mAlY72.006.1085172309558.hdf.v20191210T155738000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575993430080/MOD09GQ.A9946950.mAlY72.006.1085172309558.hdf.v20191210T155751000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575993900166/MOD09GQ.A3810090.EW85di.006.3466133695305.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575993900166/MOD09GQ.A3810090.EW85di.006.3466133695305.hdf.v20191210T160519000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575993900166/MOD09GQ.A3810090.EW85di.006.3466133695305.hdf.v20191210T160533000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575994615809/MOD09GQ.A7333345.RckwyJ.006.0332555268735.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575994615809/MOD09GQ.A7333345.RckwyJ.006.0332555268735.hdf.v20191210T161727000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575994615809/MOD09GQ.A7333345.RckwyJ.006.0332555268735.hdf.v20191210T161743000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575995102177/MOD09GQ.A9133222.xSOAC_.006.7143442454933.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575995102177/MOD09GQ.A9133222.xSOAC_.006.7143442454933.hdf.v20191210T162527000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575995102177/MOD09GQ.A9133222.xSOAC_.006.7143442454933.hdf.v20191210T162603000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575996898914/MOD09GQ.A4671964.mcRJii.006.3575074548289.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575996898914/MOD09GQ.A4671964.mcRJii.006.3575074548289.hdf.v20191210T165533000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575996898914/MOD09GQ.A4671964.mcRJii.006.3575074548289.hdf.v20191210T165547000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575999558578/MOD09GQ.A5908420.1mVEOo.006.0961024812410.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575999558578/MOD09GQ.A5908420.1mVEOo.006.0961024812410.hdf.v20191210T173949000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575999558578/MOD09GQ.A5908420.1mVEOo.006.0961024812410.hdf.v20191210T174003000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576000334935/MOD09GQ.A5395888.IfjqmW.006.6212087429175.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576000334935/MOD09GQ.A5395888.IfjqmW.006.6212087429175.hdf.v20191210T175241000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576000334935/MOD09GQ.A5395888.IfjqmW.006.6212087429175.hdf.v20191210T175257000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576005735223/MOD09GQ.A5675548.0G4ccz.006.6657554508083.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576005735223/MOD09GQ.A5675548.0G4ccz.006.6657554508083.hdf.v20191210T192240000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576005735223/MOD09GQ.A5675548.0G4ccz.006.6657554508083.hdf.v20191210T192305000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576006501720/MOD09GQ.A5778024.foNw7Q.006.6574105768023.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576006501720/MOD09GQ.A5778024.foNw7Q.006.6574105768023.hdf.v20191210T193523000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576006501720/MOD09GQ.A5778024.foNw7Q.006.6574105768023.hdf.v20191210T193540000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576007039732/MOD09GQ.A0573928.tG823W.006.8959114868194.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576007039732/MOD09GQ.A0573928.tG823W.006.8959114868194.hdf.v20191210T194447000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576007039732/MOD09GQ.A0573928.tG823W.006.8959114868194.hdf.v20191210T194502000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576007578106/MOD09GQ.A8200321.wvugBk.006.2104901543407.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576007578106/MOD09GQ.A8200321.wvugBk.006.2104901543407.hdf.v20191210T195328000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576007578106/MOD09GQ.A8200321.wvugBk.006.2104901543407.hdf.v20191210T195343000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576012842378/MOD09GQ.A0087634.eCMzPL.006.7194649784728.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576012842378/MOD09GQ.A0087634.eCMzPL.006.7194649784728.hdf.v20191210T212114000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576012842378/MOD09GQ.A0087634.eCMzPL.006.7194649784728.hdf.v20191210T212129000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576015468515/MOD09GQ.A1775543.QmnKWZ.006.6004195429297.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576015468515/MOD09GQ.A1775543.QmnKWZ.006.6004195429297.hdf.v20191210T220459000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576015468515/MOD09GQ.A1775543.QmnKWZ.006.6004195429297.hdf.v20191210T220515000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576088588648/MOD09GQ.A5486655.lMAgW3.006.1459975841058.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576088588648/MOD09GQ.A5486655.lMAgW3.006.1459975841058.hdf.v20191211T182348000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576088588648/MOD09GQ.A5486655.lMAgW3.006.1459975841058.hdf.v20191211T182404000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576102296412/MOD09GQ.A5622767.jVW893.006.2184575670628.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576102296412/MOD09GQ.A5622767.jVW893.006.2184575670628.hdf.v20191211T221208000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576102296412/MOD09GQ.A5622767.jVW893.006.2184575670628.hdf.v20191211T221224000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576105218713/MOD09GQ.A4452652.1ykqZ3.006.2451039858926.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576105218713/MOD09GQ.A4452652.1ykqZ3.006.2451039858926.hdf.v20191211T230050000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576105218713/MOD09GQ.A4452652.1ykqZ3.006.2451039858926.hdf.v20191211T230108000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576109440469/MOD09GQ.A2592006.XKcNf_.006.1485654940189.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576109440469/MOD09GQ.A2592006.XKcNf_.006.1485654940189.hdf.v20191212T001124000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576182120916/MOD09GQ.A5826214.xtwugO.006.6261686801168.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576182120916/MOD09GQ.A5826214.xtwugO.006.6261686801168.hdf.v20191212T202225000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576182120916/MOD09GQ.A5826214.xtwugO.006.6261686801168.hdf.v20191212T202239000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576249014314/MOD09GQ.A6210996.nHGu8O.006.3174297550568.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576249014314/MOD09GQ.A6210996.nHGu8O.006.3174297550568.hdf.v20191213T145726000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576249014314/MOD09GQ.A6210996.nHGu8O.006.3174297550568.hdf.v20191213T145740000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576249970860/MOD09GQ.A8421402.t6nFKh.006.3037894989051.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576249970860/MOD09GQ.A8421402.t6nFKh.006.3037894989051.hdf.v20191213T151312000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576249970860/MOD09GQ.A8421402.t6nFKh.006.3037894989051.hdf.v20191213T151327000", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1573838955288/MOD09GQ.A3119781.haeynr.006.4074740546315.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1574358566826/MOD09GQ.A8013695.K4N0ey.006.3127282204118.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1574359092072/MOD09GQ.A8702100.fZBWNI.006.3775135520157.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1574359380076/MOD09GQ.A9699053.vVDeTY.006.4873356130094.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1574377023900/MOD09GQ.A4940969.yRnTIO.006.6074235721878.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1574381176138/MOD09GQ.A5336057.nAG4KK.006.3221004503457.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1574712390341/MOD09GQ.A3493490.Xo2qAN.006.9380282039899.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1574716254780/MOD09GQ.A5754494.1nimTh.006.5626987564096.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1574717133091/MOD09GQ.A7767142.CYxJFA.006.2633356778897.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575912896952/MOD09GQ.A7399114.BmDCbe.006.9892042967204.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575923953772/MOD09GQ.A4805149.9oDhyh.006.9022510397676.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575925438077/MOD09GQ.A5542657.kyhjj1.006.5537130666716.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575926279043/MOD09GQ.A9109322.4iOCj1.006.8298263671181.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575927241619/MOD09GQ.A6015560.1qb9nY.006.2220280411055.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575992158393/MOD09GQ.A3341699.cCjc1D.006.4406545864663.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575992637033/MOD09GQ.A1138952.gpR2e1.006.4518044814416.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575993430080/MOD09GQ.A9946950.mAlY72.006.1085172309558.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575993900166/MOD09GQ.A3810090.EW85di.006.3466133695305.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575994615809/MOD09GQ.A7333345.RckwyJ.006.0332555268735.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575995102177/MOD09GQ.A9133222.xSOAC_.006.7143442454933.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575996898914/MOD09GQ.A4671964.mcRJii.006.3575074548289.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575999558578/MOD09GQ.A5908420.1mVEOo.006.0961024812410.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576000334935/MOD09GQ.A5395888.IfjqmW.006.6212087429175.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576005735223/MOD09GQ.A5675548.0G4ccz.006.6657554508083.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576006501720/MOD09GQ.A5778024.foNw7Q.006.6574105768023.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576007039732/MOD09GQ.A0573928.tG823W.006.8959114868194.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576007578106/MOD09GQ.A8200321.wvugBk.006.2104901543407.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576012842378/MOD09GQ.A0087634.eCMzPL.006.7194649784728.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576015468515/MOD09GQ.A1775543.QmnKWZ.006.6004195429297.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576088588648/MOD09GQ.A5486655.lMAgW3.006.1459975841058.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576102296412/MOD09GQ.A5622767.jVW893.006.2184575670628.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576105218713/MOD09GQ.A4452652.1ykqZ3.006.2451039858926.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576109440469/MOD09GQ.A2592006.XKcNf_.006.1485654940189.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576182120916/MOD09GQ.A5826214.xtwugO.006.6261686801168.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576249014314/MOD09GQ.A6210996.nHGu8O.006.3174297550568.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576249970860/MOD09GQ.A8421402.t6nFKh.006.3037894989051.cmr.xml", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1573838955288/MOD09GQ.A3119781.haeynr.006.4074740546315_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1574358566826/MOD09GQ.A8013695.K4N0ey.006.3127282204118_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1574359092072/MOD09GQ.A8702100.fZBWNI.006.3775135520157_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1574359380076/MOD09GQ.A9699053.vVDeTY.006.4873356130094_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1574377023900/MOD09GQ.A4940969.yRnTIO.006.6074235721878_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1574381176138/MOD09GQ.A5336057.nAG4KK.006.3221004503457_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1574712390341/MOD09GQ.A3493490.Xo2qAN.006.9380282039899_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1574716254780/MOD09GQ.A5754494.1nimTh.006.5626987564096_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1574717133091/MOD09GQ.A7767142.CYxJFA.006.2633356778897_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575912896952/MOD09GQ.A7399114.BmDCbe.006.9892042967204_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575923953772/MOD09GQ.A4805149.9oDhyh.006.9022510397676_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575925438077/MOD09GQ.A5542657.kyhjj1.006.5537130666716_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575926279043/MOD09GQ.A9109322.4iOCj1.006.8298263671181_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575927241619/MOD09GQ.A6015560.1qb9nY.006.2220280411055_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575992158393/MOD09GQ.A3341699.cCjc1D.006.4406545864663_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575992637033/MOD09GQ.A1138952.gpR2e1.006.4518044814416_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575993430080/MOD09GQ.A9946950.mAlY72.006.1085172309558_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575993900166/MOD09GQ.A3810090.EW85di.006.3466133695305_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575994615809/MOD09GQ.A7333345.RckwyJ.006.0332555268735_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575995102177/MOD09GQ.A9133222.xSOAC_.006.7143442454933_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575996898914/MOD09GQ.A4671964.mcRJii.006.3575074548289_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575999558578/MOD09GQ.A5908420.1mVEOo.006.0961024812410_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576000334935/MOD09GQ.A5395888.IfjqmW.006.6212087429175_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576005735223/MOD09GQ.A5675548.0G4ccz.006.6657554508083_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576006501720/MOD09GQ.A5778024.foNw7Q.006.6574105768023_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576007039732/MOD09GQ.A0573928.tG823W.006.8959114868194_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576007578106/MOD09GQ.A8200321.wvugBk.006.2104901543407_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576012842378/MOD09GQ.A0087634.eCMzPL.006.7194649784728_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576015468515/MOD09GQ.A1775543.QmnKWZ.006.6004195429297_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576088588648/MOD09GQ.A5486655.lMAgW3.006.1459975841058_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576102296412/MOD09GQ.A5622767.jVW893.006.2184575670628_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576105218713/MOD09GQ.A4452652.1ykqZ3.006.2451039858926_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576109440469/MOD09GQ.A2592006.XKcNf_.006.1485654940189_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576182120916/MOD09GQ.A5826214.xtwugO.006.6261686801168_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576249014314/MOD09GQ.A6210996.nHGu8O.006.3174297550568_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576249970860/MOD09GQ.A8421402.t6nFKh.006.3037894989051_ndvi.jpg" + ], + "onlyInDynamoDb": [ + { + "uri": "s3://mhs3-private/MOD09GQ___006/MOD/MOD09GQ.A9218123.TJbx_C.006.7667598863143.hdf.met", + "granuleId": "MOD09GQ.A9218123.TJbx_C.006.7667598863143" + }, + { + "uri": "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestUMMGSuccess-1575927241338/MOD09GQ.A9544845.jLg7sy.006.6264785375800.hdf.met", + "granuleId": "MOD09GQ.A9544845.jLg7sy.006.6264785375800" + }, + { + "uri": "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestUMMGSuccess-1575993900243/MOD09GQ.A8473991.0Vg1BF.006.7743328283296.hdf.met", + "granuleId": "MOD09GQ.A8473991.0Vg1BF.006.7743328283296" + }, + { + "uri": "s3://mhs3-private/MOD14A1___006/MOD/MOD14A1.A6229684.liNodB.006.7559017774430.hdf.met", + "granuleId": "MOD14A1.A6229684.liNodB.006.7559017774430" + }, + { + "uri": "s3://mhs3-private/MOD14A1___006/MOD/MOD14A1.A7489357.WuQwlB.006.1633383724781.hdf.met", + "granuleId": "MOD14A1.A7489357.WuQwlB.006.1633383724781" + }, + { + "uri": "s3://mhs3-private/MOD14A1___006/MOD/MOD14A1.A9471544.8iLnaC.006.7851658874405.hdf.met", + "granuleId": "MOD14A1.A9471544.8iLnaC.006.7851658874405" + }, + { + "uri": "s3://mhs3-private/MYD13Q1___006/BRO/BROWSE.MYD13Q1.A8039859.cm_J1d.006.4049670693348.hdf", + "granuleId": "MYD13Q1.A8039859.cm_J1d.006.4049670693348" + }, + { + "uri": "s3://mhs3-private/MYD13Q1___006/BRO/BROWSE.MYD13Q1.A9323228.btdDqi.006.2091522185393.hdf", + "granuleId": "MYD13Q1.A9323228.btdDqi.006.2091522185393" + }, + { + "uri": "s3://mhs3-private/MYD13Q1___006/MYD/MYD13Q1.A8039859.cm_J1d.006.4049670693348.hdf.met", + "granuleId": "MYD13Q1.A8039859.cm_J1d.006.4049670693348" + }, + { + "uri": "s3://mhs3-private/MYD13Q1___006/MYD/MYD13Q1.A9323228.btdDqi.006.2091522185393.hdf.met", + "granuleId": "MYD13Q1.A9323228.btdDqi.006.2091522185393" + }, + { + "uri": "s3://mhs3-protected/MOD09GQ___006/2017/MOD/MOD09GQ.A9218123.TJbx_C.006.7667598863143.hdf", + "granuleId": "MOD09GQ.A9218123.TJbx_C.006.7667598863143" + }, + { + "uri": "s3://mhs3-protected/MOD14A1___006/2017/MOD/MOD14A1.A6229684.liNodB.006.7559017774430.hdf", + "granuleId": "MOD14A1.A6229684.liNodB.006.7559017774430" + }, + { + "uri": "s3://mhs3-protected/MOD14A1___006/2017/MOD/MOD14A1.A7489357.WuQwlB.006.1633383724781.hdf", + "granuleId": "MOD14A1.A7489357.WuQwlB.006.1633383724781" + }, + { + "uri": "s3://mhs3-protected/MOD14A1___006/2017/MOD/MOD14A1.A9471544.8iLnaC.006.7851658874405.hdf", + "granuleId": "MOD14A1.A9471544.8iLnaC.006.7851658874405" + }, + { + "uri": "s3://mhs3-protected/MYD13Q1___006/2017/MYD/MYD13Q1.A8039859.cm_J1d.006.4049670693348.hdf", + "granuleId": "MYD13Q1.A8039859.cm_J1d.006.4049670693348" + }, + { + "uri": "s3://mhs3-protected/MYD13Q1___006/2017/MYD/MYD13Q1.A9323228.btdDqi.006.2091522185393.hdf", + "granuleId": "MYD13Q1.A9323228.btdDqi.006.2091522185393" + }, + { + "uri": "s3://mhs3-protected/b793ea149845295b794101bd50fea09a24c8be28", + "granuleId": "6d949f7514da513414c5021ef194268589b65182" + }, + { + "uri": "s3://mhs3-protected/mhs3-IngestUMMGSuccess-1575927241338-test-data/files/MOD09GQ___006/2016/MOD/mhs3-IngestUMMGSuccess-1575927241338/MOD09GQ.A9544845.jLg7sy.006.6264785375800.hdf", + "granuleId": "MOD09GQ.A9544845.jLg7sy.006.6264785375800" + }, + { + "uri": "s3://mhs3-protected/mhs3-IngestUMMGSuccess-1575993900243-test-data/files/MOD09GQ___006/2016/MOD/mhs3-IngestUMMGSuccess-1575993900243/MOD09GQ.A8473991.0Vg1BF.006.7743328283296.hdf", + "granuleId": "MOD09GQ.A8473991.0Vg1BF.006.7743328283296" + }, + { + "uri": "s3://mhs3-protected-2/MOD09GQ___006/MOD/MOD09GQ.A9218123.TJbx_C.006.7667598863143.cmr.xml", + "granuleId": "MOD09GQ.A9218123.TJbx_C.006.7667598863143" + }, + { + "uri": "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestUMMGSuccess-1575927241338/MOD09GQ.A9544845.jLg7sy.006.6264785375800.cmr.json", + "granuleId": "MOD09GQ.A9544845.jLg7sy.006.6264785375800" + }, + { + "uri": "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestUMMGSuccess-1575993900243/MOD09GQ.A8473991.0Vg1BF.006.7743328283296.cmr.json", + "granuleId": "MOD09GQ.A8473991.0Vg1BF.006.7743328283296" + }, + { + "uri": "s3://mhs3-protected-2/MOD14A1___006/MOD/MOD14A1.A6229684.liNodB.006.7559017774430.cmr.xml", + "granuleId": "MOD14A1.A6229684.liNodB.006.7559017774430" + }, + { + "uri": "s3://mhs3-protected-2/MOD14A1___006/MOD/MOD14A1.A7489357.WuQwlB.006.1633383724781.cmr.xml", + "granuleId": "MOD14A1.A7489357.WuQwlB.006.1633383724781" + }, + { + "uri": "s3://mhs3-protected-2/MOD14A1___006/MOD/MOD14A1.A9471544.8iLnaC.006.7851658874405.cmr.xml", + "granuleId": "MOD14A1.A9471544.8iLnaC.006.7851658874405" + }, + { + "uri": "s3://mhs3-protected-2/MYD13Q1___006/MYD/MYD13Q1.A8039859.cm_J1d.006.4049670693348.cmr.xml", + "granuleId": "MYD13Q1.A8039859.cm_J1d.006.4049670693348" + }, + { + "uri": "s3://mhs3-protected-2/MYD13Q1___006/MYD/MYD13Q1.A9323228.btdDqi.006.2091522185393.cmr.xml", + "granuleId": "MYD13Q1.A9323228.btdDqi.006.2091522185393" + }, + { + "uri": "s3://mhs3-public/MOD09GQ___006/MOD/MOD09GQ.A9218123.TJbx_C.006.7667598863143_ndvi.jpg", + "granuleId": "MOD09GQ.A9218123.TJbx_C.006.7667598863143" + }, + { + "uri": "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestUMMGSuccess-1575927241338/MOD09GQ.A9544845.jLg7sy.006.6264785375800_ndvi.jpg", + "granuleId": "MOD09GQ.A9544845.jLg7sy.006.6264785375800" + }, + { + "uri": "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestUMMGSuccess-1575993900243/MOD09GQ.A8473991.0Vg1BF.006.7743328283296_ndvi.jpg", + "granuleId": "MOD09GQ.A8473991.0Vg1BF.006.7743328283296" + }, + { + "uri": "s3://mhs3-public/MOD14A1___006/BRO/BROWSE.MOD14A1.A6229684.liNodB.006.7559017774430.1.jpg", + "granuleId": "MOD14A1.A6229684.liNodB.006.7559017774430" + }, + { + "uri": "s3://mhs3-public/MOD14A1___006/BRO/BROWSE.MOD14A1.A7489357.WuQwlB.006.1633383724781.1.jpg", + "granuleId": "MOD14A1.A7489357.WuQwlB.006.1633383724781" + }, + { + "uri": "s3://mhs3-public/MOD14A1___006/BRO/BROWSE.MOD14A1.A9471544.8iLnaC.006.7851658874405.1.jpg", + "granuleId": "MOD14A1.A9471544.8iLnaC.006.7851658874405" + }, + { + "uri": "s3://mhs3-public/MYD13Q1___006/BRO/BROWSE.MYD13Q1.A8039859.cm_J1d.006.4049670693348.1.jpg", + "granuleId": "MYD13Q1.A8039859.cm_J1d.006.4049670693348" + }, + { + "uri": "s3://mhs3-public/MYD13Q1___006/BRO/BROWSE.MYD13Q1.A9323228.btdDqi.006.2091522185393.1.jpg", + "granuleId": "MYD13Q1.A9323228.btdDqi.006.2091522185393" + } + ] + }, + "collectionsInCumulusCmr": { + "okCount": 1, + "onlyInCumulus": [ + "L2_HR_PIXC_test-mhs3-KinesisTestError-1573767582442___000", + "L2_HR_PIXC_test-mhs3-KinesisTestError-1573771398711___000", + "L2_HR_PIXC_test-mhs3-KinesisTestError-1573838955147___000", + "L2_HR_PIXC_test-mhs3-KinesisTestError-1574358566311___000", + "L2_HR_PIXC_test-mhs3-KinesisTestError-1574359379551___000", + "L2_HR_PIXC_test-mhs3-KinesisTestError-1574377023979___000", + "L2_HR_PIXC_test-mhs3-KinesisTestError-1574381175998___000", + "L2_HR_PIXC_test-mhs3-KinesisTestError-1574712390523___000", + "L2_HR_PIXC_test-mhs3-KinesisTestError-1574716254980___000", + "L2_HR_PIXC_test-mhs3-KinesisTestError-1574717133091___000", + "L2_HR_PIXC_test-mhs3-KinesisTestError-1575912896780___000", + "L2_HR_PIXC_test-mhs3-KinesisTestError-1575922912270___000", + "L2_HR_PIXC_test-mhs3-KinesisTestError-1575923953715___000", + "L2_HR_PIXC_test-mhs3-KinesisTestError-1575925438162___000", + "L2_HR_PIXC_test-mhs3-KinesisTestError-1575926279085___000", + "L2_HR_PIXC_test-mhs3-KinesisTestError-1575927241491___000", + "L2_HR_PIXC_test-mhs3-KinesisTestError-1575992158042___000", + "L2_HR_PIXC_test-mhs3-KinesisTestError-1575992637028___000", + "L2_HR_PIXC_test-mhs3-KinesisTestError-1575993430247___000", + "L2_HR_PIXC_test-mhs3-KinesisTestError-1575993900229___000", + "L2_HR_PIXC_test-mhs3-KinesisTestError-1575994615798___000", + "L2_HR_PIXC_test-mhs3-KinesisTestError-1575995101984___000", + "L2_HR_PIXC_test-mhs3-KinesisTestError-1575996898917___000", + "L2_HR_PIXC_test-mhs3-KinesisTestError-1575999558588___000", + "L2_HR_PIXC_test-mhs3-KinesisTestError-1576000334594___000", + "L2_HR_PIXC_test-mhs3-KinesisTestError-1576005735112___000", + "L2_HR_PIXC_test-mhs3-KinesisTestError-1576006501741___000", + "L2_HR_PIXC_test-mhs3-KinesisTestError-1576007039306___000", + "L2_HR_PIXC_test-mhs3-KinesisTestError-1576007578006___000", + "L2_HR_PIXC_test-mhs3-KinesisTestError-1576012842312___000", + "L2_HR_PIXC_test-mhs3-KinesisTestError-1576015468425___000", + "L2_HR_PIXC_test-mhs3-KinesisTestError-1576088588601___000", + "L2_HR_PIXC_test-mhs3-KinesisTestError-1576102296392___000", + "L2_HR_PIXC_test-mhs3-KinesisTestError-1576105218731___000", + "L2_HR_PIXC_test-mhs3-KinesisTestError-1576109440367___000", + "L2_HR_PIXC_test-mhs3-KinesisTestError-1576182121139___000", + "L2_HR_PIXC_test-mhs3-KinesisTestError-1576249014350___000", + "L2_HR_PIXC_test-mhs3-KinesisTestError-1576249970768___000", + "L2_HR_PIXC_test-mhs3-LambdaEventSourceTest-1575912897241___000", + "L2_HR_PIXC_test-mhs3-LambdaEventSourceTest-1576007578110___000", + "MOD09GQ_test-mhs3-IngestGranuleDuplicateHandling-1573771398863___006", + "MOD09GQ_test-mhs3-IngestGranuleSuccess-1573767582932___006", + "MOD09GQ_test-mhs3-IngestGranuleSuccess-1573771398734___006", + "MOD09GQ_test-mhs3-IngestGranuleSuccess-1573777725229___006", + "MOD09GQ_test-mhs3-IngestGranuleSuccess-1573831122562___006", + "MOD09GQ_test-mhs3-IngestGranuleSuccess-1574183135222___006", + "MOD09GQ_test-mhs3-IngestGranuleSuccess-1574377024207___006", + "MOD09GQ_test-mhs3-IngestGranuleSuccess-1574381176165___006", + "MOD09GQ_test-mhs3-IngestGranuleSuccess-1576012842508___006", + "MOD09GQ_test-mhs3-IngestGranuleSuccess-1576108764706___006", + "MOD09GQ_test-mhs3-IngestGranuleSuccess-1576109066903___006", + "MOD09GQ_test-mhs3-IngestGranuleSuccess-1576109175453___006", + "MOD09GQ_test-mhs3-IngestUMMGSuccess-1573767582815___006", + "MOD09GQ_test-mhs3-IngestUMMGSuccess-1573771398848___006", + "MOD09GQ_test-mhs3-LambdaEventSourceTest-1575912897241___006", + "MOD09GQ_test-mhs3-LambdaEventSourceTest-1576007578110___006", + "MOD09GQ_test-mhs3-sqsRule-1573833523483___006", + "MOD09GQ_test-mhs3-sqsRule-1573834389690___006", + "f3c03a599cffafe65f73099b52742aa1b95628a4___b3abc31920fbca4d2762c5d6e917f5dda432a41a", + "http_testcollection_test-mhs3-DiscoverGranules-1574352719684___001", + "http_testcollection_test-mhs3-DiscoverGranules-1574710649779___001", + "http_testcollection_test-mhs3-DiscoverGranules-1575929640094___001" + ], + "onlyInCmr": [ + "A2_RainOcn_NRT___0", + "A2_SI12_NRT___0", + "A2_SI25_NRT___0", + "A2_SI6_NRT___0", + "AST_L1A___003", + "L2_HR_PIXC___000", + "L2_HR_PIXC___1", + "MCD43A1___006", + "MOD09GQ___006", + "MOD11A1___006", + "MUR-JPL-L4-GLOB-v4.1___1", + "MYD09A1___006", + "MYD13A1___006", + "MYD13Q1___006", + "hs3avaps___1", + "hs3cpl___1", + "hs3hamsr___1", + "hs3hirad___1", + "hs3hiwrap2___2", + "hs3hiwrap3___3", + "hs3hiwrap4___4", + "hs3hiwrap5___5", + "hs3hiwrap___1", + "hs3wwlln___1", + "tmt_test___001" + ] + }, + "granulesInCumulusCmr": { + "okCount": 7, + "onlyInCumulus": [ + { + "granuleId": "MOD14A1.A4135026.ekHe8x.006.3355759967228", + "collectionId": "MOD14A1___006" + }, + { + "granuleId": "MOD14A1.A4526233.492TT9.006.7426276787300", + "collectionId": "MOD14A1___006" + }, + { + "granuleId": "MOD14A1.A4562488.2ppgk0.006.3054981124199", + "collectionId": "MOD14A1___006" + }, + { + "granuleId": "MOD14A1.A5124023.qCpo9T.006.5379922199867", + "collectionId": "MOD14A1___006" + }, + { + "granuleId": "MOD14A1.A8512234.0CE5a0.006.8217730108870", + "collectionId": "MOD14A1___006" + }, + { + "granuleId": "MOD14A1.A8859169.oyt3ZQ.006.6651148631629", + "collectionId": "MOD14A1___006" + }, + { + "granuleId": "MOD14A1.A8959582.q932t8.006.4564896025574", + "collectionId": "MOD14A1___006" + } + ], + "onlyInCmr": [ + { + "GranuleUR": "MOD14A1.A0031922.9lenyG.006.4681412227733", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0161520.Icg3Hd.006.2590173029614", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0181238.I2lQMR.006.5213076331746", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0241275.PP6Bru.006.6690267738935", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0268709.XzCq6U.006.2062396767458", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0303340.ZOAcic.006.3126112323008", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0367178.OZLnKJ.006.1553933304160", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0385810.sfCLoU.006.4410204411125", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0408148.aVHJRj.006.6581128707331", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0426428.O6puS3.006.4037434378100", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0435030.F4TfO6.006.5475131012423", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0438468.A4UnhQ.006.0916618386198", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0439787.yYkYoF.006.9258816292523", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0469562.6buMGA.006.6641209270503", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0477077.ZwnsyD.006.2155034172666", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0477862.m5RyzJ.006.7749345641502", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0478724.X0E3aO.006.2167329718097", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0493385.ncUbST.006.8724510800351", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0496800.o1BZif.006.7997570788246", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0499025.Bl5XRj.006.7345541650084", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0530215.QTb6o8.006.3728095241030", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0543357.DeSiuz.006.9248161352838", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0559967.WaTfcs.006.7692038273164", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0573198.lNm8ej.006.4937226253472", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0579654.EfC7b5.006.8547135416723", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0594210.INgzWo.006.9610785542200", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0605025.sNQ6Xj.006.8017396449666", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0686414.KR4Yj9.006.1657242945365", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0708604.EOI65z.006.5281139092272", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0731080.kPnCI3.006.9645252704886", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0743873.dTU1LJ.006.2850872371107", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0829889.4LBq3W.006.5205877566239", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0830785.BzRLhh.006.2098466050997", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0851511.fpq1t8.006.8813309643418", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0854437.MAY0Q8.006.9959874308730", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0858047.xzmmnz.006.1278403771091", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0919553.8RKHu9.006.9149860071026", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0970863.XLNxT5.006.2798234861119", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0989760.1rAfoU.006.1423241066769", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1034119.yAxS0C.006.1060775566122", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1081653.E8hdrc.006.4410360914417", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1115000.4bDb_o.006.2278326105128", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1135111.j6ltZS.006.6490601038632", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1148386.BTNJja.006.1703429501514", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1178631.xM5T76.006.0022032029423", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1181278.bQPeTW.006.1783680708565", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1189394.88OTN4.006.7906263730471", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1218342.lSwbHi.006.9300255035117", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1320673.fbqmSu.006.2496971059489", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1324671.g1zQ18.006.6645018100409", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1331363.iuSTbG.006.8466737452095", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1386278.PER5I0.006.3128037156396", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1394645.jvLNB4.006.7932563501595", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1398327.OoEhNp.006.6140710403958", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1414599.Ir8MaJ.006.3472304331616", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1425794.dGVwQt.006.9221129681517", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1469997.3eC9_v.006.9974796300625", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1487775.Xh_YYy.006.3907075335867", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1540212.i4bO61.006.3792997119574", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1555091.ArCGtd.006.7836090584762", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1627106.yXnxzc.006.5122291281725", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1637985.L2C7jj.006.4081838044955", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1689913.IhNjOC.006.0805594965670", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1699875.I7fCuo.006.5825142358071", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1740324.k9dRfR.006.5120684081887", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1756305.44qrI7.006.6544298445142", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1773221.vL2xGp.006.1502509955110", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1792533._ROTa6.006.7574259958264", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1804251.OYGvNk.006.3189308757490", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1828394.mSRFgP.006.0394762493146", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1839190.6bZY0R.006.1308585329472", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1869818.paiptw.006.4469275989446", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1871604.ws5lvU.006.3880569542726", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1884556.YljVmJ.006.7073152912015", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1903936.gdxhTt.006.8859200024953", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1926989.sWkBkt.006.4161390607995", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1930398.32K2n6.006.3858864868017", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1960074.lVDIHZ.006.0029599573994", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1982683.VRNZ9e.006.8232263994655", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1988586.g5hg52.006.0093634085750", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2015525.gSlbDC.006.6162623767400", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2023218.gKxjhE.006.0679016601696", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2045155.IldGaW.006.5587732568060", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2047465.DIVVCq.006.3300117538068", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2082913.k2AH9d.006.2063774562440", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2107169.4Hx5QW.006.4489290579513", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2199026.BW6m07.006.7360388694859", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2249549.MIX7bE.006.4598121918597", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2326990.tOsSli.006.7888038073008", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2406099.Sqm_ya.006.3870376151154", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2434928.qQSDWp.006.0111410832633", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2436662.fEDb_J.006.9857059507872", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2467775.iFASnu.006.9373140163082", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2503671.SJJAxR.006.6004529904364", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2508345.rH7Jmx.006.9155872079831", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2530773.vZzH3t.006.3247672973972", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2543004.CN_Urw.006.9383083242890", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2579574.IPJ0_h.006.4470297178564", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2607276.kAHAKI.006.9719289832615", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2620427.S0GPId.006.6556845431909", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2640677.tJZhUP.006.3275708023016", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2659229.tFUmqE.006.2699754508939", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2768377.b65yZa.006.2465291425471", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2811951.KfhzNs.006.1071136264564", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2813635.PmJSgS.006.6559229596528", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2823097.rwEG6d.006.9559616817462", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2867966.O1iEvW.006.8400528388272", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2874085.m9ZBCj.006.6926008557571", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2935897.gCcyF0.006.2120404779982", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2980296.9bMG3X.006.1850666675406", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2981534.Qr3jFk.006.9466954357415", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3015108.zJBsS8.006.8064551695149", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3065415.tRf2a4.006.5375458618864", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3094861.BxVNFv.006.1428320778400", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3103291.oqjLLY.006.9681234364077", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3181089.c84dOC.006.6849102364166", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3192158.EXtdRS.006.5241419287772", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3221972.5LrA4V.006.6741514442929", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3236084.0KyoMb.006.6325930374418", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3237911.2wiWBj.006.3788637026706", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3242735.B590Id.006.0789662245995", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3243788.FaigZr.006.5070669011170", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3253459.Wg2qVw.006.8645624871878", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3261333.kmf3Da.006.5494327939194", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3384866.pnYPpq.006.3422769889410", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3393327.TLAHdj.006.7025810345883", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3474712.qiCipo.006.3574120304485", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3538628.1u8ie7.006.0861387413780", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3568827.aI6SVu.006.7927924616119", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3587507.oJpXUl.006.0050227223409", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3639028.D7TRcU.006.3524076198815", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3659941.U9_JFB.006.9640903408294", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3706999.olpdUX.006.9985944506720", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3730274._FMaX5.006.1488458332851", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3745297.7yqN7N.006.6203206230519", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3777110.2nI_z1.006.4458932161560", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3836139.rEwbJj.006.5244797579248", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3883637.Em4acn.006.7492507042664", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3899227.54rhqw.006.9198542516974", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3899600.C6Ahqo.006.2951490717460", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4036255.LSIcy8.006.4004574608122", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4054288.8xvHDB.006.6199273392157", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4068488.NS6rb0.006.9525121033929", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4083098.iB_17X.006.8170576880727", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4092129.ICW8_c.006.2835778900249", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4165221.i0AGqZ.006.0000114393839", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4211671.EzOor8.006.9264465388568", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4215712.IiJiBs.006.3078636075675", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4219006.Ok_npC.006.8717060833704", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4220400.faCGhR.006.8939271143193", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4250172.4KUesf.006.9271102774552", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4297834.DNrDFd.006.1911684462762", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4321780.obqoQT.006.2924612998174", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4346691._LlduX.006.5638546826700", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4352997.cycHF8.006.0390693353762", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4374447.3efcem.006.2904967410566", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4380588.pH6Fdy.006.4273334058926", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4439788.wiesOZ.006.5924892079401", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4450652.hScD6j.006.6991048602566", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4467658.RV0m12.006.4618584382673", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4493406.XGGHRX.006.3941985703164", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4510771.cYFwb3.006.6093583601899", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4540721.GWlZyE.006.3216801552592", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4600227.K2FpI2.006.0348804402510", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4601203.Mtnovz.006.1758782609202", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4632703.VZRyrL.006.1868762621072", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4666224.gbIm_n.006.3153915963290", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4742629.w1Q8sE.006.8413755571036", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4743195.XXuFep.006.9650249440254", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4743973.Ptu4QH.006.2859569760830", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4753915.XfIvAK.006.2647520684202", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4819663.hceLMY.006.4888474957881", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4848233.if6V2s.006.5228249902400", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4894103.gNQSfb.006.0498231218639", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4908086.VCZ_lp.006.2403345043184", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4910518.JVmHGD.006.6285896487087", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4978437.opBZwR.006.0038826280988", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4986395.CCcRHU.006.2754654735559", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4998771.mlwd2W.006.3091218809916", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5071312.QoQBhY.006.6641136667881", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5083285.2xdBAq.006.5528806886139", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5134375.IFJxMc.006.9089019094156", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5210697.Zj1X60.006.1671443614005", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5255259.6QB4P5.006.5861378094572", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5259810.IHt7wm.006.2042821443503", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5333141.ej2_dh.006.2075553701768", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5388005.PDcM2l.006.7499759541761", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5446137.R5VQju.006.0496023742915", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5470474.PYLkXK.006.2486305145959", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5536132.Sh9I1Q.006.7321416452215", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5578210.vYkHrP.006.3097372279359", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5581046.QznotV.006.2199781681621", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5613054.DSp_0v.006.7488042302935", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5613577.kr3VyR.006.5957040298705", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5615374.kqK1gv.006.9862819253374", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5645384.pwoYe4.006.1647551931437", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5674760.wwUR1r.006.1161313538574", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5779783.iAoyn9.006.0753207782283", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5794321.zBygQV.006.0606713843927", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5843576.ftYID0.006.3852722614544", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5879650.xvPlJj.006.0164200189000", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5895367.uNnp4k.006.6870373694725", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5906713.GCZmcq.006.0996141592045", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5915652.IFBD5L.006.8209803787553", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5916225.iHZvvk.006.1256889269179", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5924160.8v7G3a.006.8071081859957", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5957211.uwomBD.006.1874059172147", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5959582.Alz3YH.006.9784966634734", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6140781.dwVHRM.006.9795613581334", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6154811._K9e2H.006.0554685839175", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6166519.2Zezz5.006.7680191797368", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6169478.oVibQR.006.5307651972872", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6182322.vuyWZV.006.2878435205612", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6193133.00WOva.006.2742381792467", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6220907.ffSLhU.006.8444268919429", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6221592.0AvUpm.006.4972115341840", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6240892.J_ihzm.006.8232514440182", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6242594.PD8_UC.006.7945609781836", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6294412.iWI3ep.006.6795977534561", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6314532.78zfxS.006.6223531035200", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6321424._ZyOBe.006.5762837656120", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6345909.bstkMx.006.5525239283796", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6346827.RucmvD.006.0338928454861", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6350821.yoZG65.006.3318590275783", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6377697._KygO1.006.6790931369124", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6433508.Cn9VA_.006.4956661543835", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6470016.qcoFaa.006.5498889293816", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6498944.7N8pXl.006.1521246263458", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6504617.OhkNgA.006.8477604720347", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6546816.VEO92z.006.5480489177490", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6575512.mW2bSV.006.6185576608888", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6657595.psbb42.006.8233981132204", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6682927.EoMMj_.006.1855665560268", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6706112.TF1qq9.006.1771098824289", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6780016.8PSUed.006.6143912609815", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6780662.GFErXb.006.0683838822328", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6787983.6uEcJQ.006.0491441481084", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6801171.BhjLbF.006.9713529333826", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6813543.4KPfCP.006.0879840866356", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6829604.KjI8cW.006.9117191472337", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6854369.0mUX3W.006.6320693878688", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6876510.MUAcHG.006.7692816445490", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6879561.g2xHzm.006.5729596282955", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6885606.ZpeFgD.006.6814469969755", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6904037.33OVN7.006.3977486435483", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6954524.9_88bg.006.2010411596751", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6969850.Y9k4Oq.006.9751563750493", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6988892.FR1RiB.006.5977637509747", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7000581.IWRvrw.006.9637649469071", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7040298.wALe2l.006.8076432241643", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7079341.AFFouJ.006.4177191558605", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7116232.YPJAyy.006.9634780456925", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7121539.rRlj84.006.8568841150181", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7228927.7QRGz7.006.5737505467736", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7235010.9PdjHH.006.1891432762889", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7243264.Vt36ib.006.1578864318653", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7254030.b_TPqs.006.3035275090373", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7272337.DyV01y.006.4585183067998", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7357306.fu6Lfh.006.6722367866085", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7384707.0CQnZb.006.4490242707292", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7391492.pj93zG.006.7762172202424", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7442669.V6hzlo.006.8727283948669", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7511554.eOXCq5.006.1269728944670", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7571652.EXH_Bf.006.1980305546683", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7589953.mQ_rr6.006.5761289422251", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7662314.Q3HPTA.006.2925884807202", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7675910.VVEdpg.006.9881036819285", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7710418.8Y6DPu.006.1050339994866", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7717473.vdce9J.006.4764634793209", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7774307.1xfV3Q.006.2135217284739", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7774534.ospCfh.006.1382047512532", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7792926.uxBDKg.006.8459817991128", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7797808.bMQSOj.006.6390170359956", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7809989.0L9HHU.006.6459538384869", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7859847.8969jr.006.4573035209852", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7892638.q2HPu7.006.9408643265275", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7900349.UvVafB.006.5028127642428", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7910742.Mn5yt1.006.2396134919724", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7918975.P4XLCu.006.1743355786218", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7977416.pWD5iP.006.7074134724949", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8010774.dA2YUh.006.4342162115952", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8011056.BeHzAb.006.4490265608575", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8015009.E4P0ug.006.5741781701080", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8049294.vkawoy.006.0937382245643", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8052708.iCaVer.006.7787777713005", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8060469.ZPahDZ.006.8982046108230", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8073097.j6jSWQ.006.4083585468997", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8093883.QxXolH.006.4493515582587", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8106903.MZq9eP.006.5382401845931", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8124629.hY1dN5.006.3064417321277", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8161961.Kw_Zre.006.5456643911149", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8177133.oZYaja.006.0364568139575", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8195501.KU8yll.006.3296667507482", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8216766.7T40z1.006.4947562988284", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8221290.PIL5Hb.006.9130547287716", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8248086.elRIsi.006.0567672305391", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8250434.CAqXH9.006.5676147389429", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8261494.iS8TuO.006.2282913340704", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8269413.pKogkh.006.4412309077024", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8279197.hToD9P.006.6175739867970", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8281719.gum1ab.006.1459398984505", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8288987.uo_gYS.006.0250896887040", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8295706.kPc8E4.006.7759769767754", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8303794.PMYy_l.006.9511602329121", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8343082.ezfBlw.006.2682603002957", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8385273.QxjKuc.006.6198021802383", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8478053.KaIGMi.006.3840199854152", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8488534.6J0MbI.006.1265985239675", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8499672.ecbNK3.006.5953840974188", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8529721.9lhoO0.006.6507378736299", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8572944.gippDi.006.4878766702019", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8583690.eGuOFF.006.3178725084313", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8586334.HLzyH2.006.4849383317064", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8606985.so7CF1.006.9688821597814", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8641020.MeZI6O.006.9373642031107", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8647926.ihXCFx.006.4788920531385", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8685478.azlSgB.006.2688103987158", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8702349.ZWlsXG.006.4957379951587", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8770143.OGIqeK.006.5547498198971", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8789273.0XW8dF.006.7316776957736", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8802522.sFaJEM.006.2026321336527", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8837879.Bki1Yv.006.9338509978014", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8859597.OS7v_B.006.2492497371139", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8859711.11wb3X.006.7995412937159", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8881987.Vtk46X.006.2569820256169", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8882746.Lekpxo.006.9257961202638", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8895901.bOhVvA.006.7046394153913", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8909863.eydtSS.006.1149409844354", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8932526.XjjHVI.006.3394128582613", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8958133.2PbhKY.006.7237752355433", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8963220.DSYj8h.006.0512930156059", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8987608.lF3pIc.006.2893793704999", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9041180.MCpHp9.006.8191502640460", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9053660.qjpXaq.006.0165283377258", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9149355.aaHA7v.006.5099150808083", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9164803.RUftmG.006.1807010132692", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9166659.O9jTZH.006.4902641669957", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9211448.ILglHo.006.1801978246969", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9226530.v31XjU.006.2733149852665", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9309522.IdHGiP.006.1511064985561", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9311127.e928Zx.006.9503691370855", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9317680.A72Rxm.006.9227785953020", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9330328.YSRF8v.006.1819704284610", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9355035.W1gKQO.006.6409047050587", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9365735.GCMy2Y.006.2875216040398", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9391469.9XYoae.006.3013397272445", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9414005.cb12cM.006.7479280228146", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9513175._mfpmw.006.1963912704522", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9539025.XQy0KQ.006.3923459802201", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9541790.gDH7wC.006.9947767843106", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9567824.AkANSV.006.8470786106077", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9591289.FvUfbk.006.2520719079816", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9597426.D_ag6a.006.9140139663225", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9645467.OE9YwX.006.7200895500926", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9664423.aIUz39.006.3652981858375", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9677433.OeeR2S.006.9290490472475", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9684065.ecsNmm.006.9181724505840", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9814119.trqgNT.006.7636352907992", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9834656.SLmX8g.006.3493493596874", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9839020.nik1MP.006.2145484187039", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9845048.PcmJ_y.006.6013670131610", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9867427.9ciRAK.006.9977199496795", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9890507.RU4JUk.006.4082866676019", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9990408.N4T1Mn.006.9738328377056", + "ShortName": "MOD14A1", + "Version": "006" + } + ] + }, + "filesInCumulusCmr": { + "okCount": 28, + "onlyInCumulus": [], + "onlyInCmr": [] + } +} diff --git a/cypress/fixtures/seeds/reconciliation-reports/inventoryReport-20200114T205238781.json b/cypress/fixtures/seeds/reconciliation-reports/inventoryReport-20200114T205238781.json new file mode 100644 index 000000000..68e062727 --- /dev/null +++ b/cypress/fixtures/seeds/reconciliation-reports/inventoryReport-20200114T205238781.json @@ -0,0 +1,2291 @@ +{ + "reportStartTime": "2020-01-14T20:52:38.781Z", + "reportEndTime": "2020-01-14T20:52:45.789Z", + "status": "SUCCESS", + "error": null, + "filesInCumulus": { + "okCount": 129, + "onlyInS3": [ + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1573838955288/MOD09GQ.A3119781.haeynr.006.4074740546315.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1574358566826/MOD09GQ.A8013695.K4N0ey.006.3127282204118.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1574359092072/MOD09GQ.A8702100.fZBWNI.006.3775135520157.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1574359380076/MOD09GQ.A9699053.vVDeTY.006.4873356130094.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1574377023900/MOD09GQ.A4940969.yRnTIO.006.6074235721878.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1574381176138/MOD09GQ.A5336057.nAG4KK.006.3221004503457.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1574712390341/MOD09GQ.A3493490.Xo2qAN.006.9380282039899.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1574716254780/MOD09GQ.A5754494.1nimTh.006.5626987564096.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1574717133091/MOD09GQ.A7767142.CYxJFA.006.2633356778897.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575912896952/MOD09GQ.A7399114.BmDCbe.006.9892042967204.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575923953772/MOD09GQ.A4805149.9oDhyh.006.9022510397676.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575925438077/MOD09GQ.A5542657.kyhjj1.006.5537130666716.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575926279043/MOD09GQ.A9109322.4iOCj1.006.8298263671181.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575927241619/MOD09GQ.A6015560.1qb9nY.006.2220280411055.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575992158393/MOD09GQ.A3341699.cCjc1D.006.4406545864663.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575992637033/MOD09GQ.A1138952.gpR2e1.006.4518044814416.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575993430080/MOD09GQ.A9946950.mAlY72.006.1085172309558.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575993900166/MOD09GQ.A3810090.EW85di.006.3466133695305.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575994615809/MOD09GQ.A7333345.RckwyJ.006.0332555268735.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575995102177/MOD09GQ.A9133222.xSOAC_.006.7143442454933.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575996898914/MOD09GQ.A4671964.mcRJii.006.3575074548289.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575999558578/MOD09GQ.A5908420.1mVEOo.006.0961024812410.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576000334935/MOD09GQ.A5395888.IfjqmW.006.6212087429175.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576005735223/MOD09GQ.A5675548.0G4ccz.006.6657554508083.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576006501720/MOD09GQ.A5778024.foNw7Q.006.6574105768023.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576007039732/MOD09GQ.A0573928.tG823W.006.8959114868194.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576007578106/MOD09GQ.A8200321.wvugBk.006.2104901543407.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576012842378/MOD09GQ.A0087634.eCMzPL.006.7194649784728.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576015468515/MOD09GQ.A1775543.QmnKWZ.006.6004195429297.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576088588648/MOD09GQ.A5486655.lMAgW3.006.1459975841058.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576102296412/MOD09GQ.A5622767.jVW893.006.2184575670628.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576105218713/MOD09GQ.A4452652.1ykqZ3.006.2451039858926.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576109440469/MOD09GQ.A2592006.XKcNf_.006.1485654940189.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576182120916/MOD09GQ.A5826214.xtwugO.006.6261686801168.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576249014314/MOD09GQ.A6210996.nHGu8O.006.3174297550568.hdf.met", + "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576249970860/MOD09GQ.A8421402.t6nFKh.006.3037894989051.hdf.met", + "s3://mhs3-protected/495c7321334171d867b0dcdf3ad5cc21e9cef365", + "s3://mhs3-protected/DEMO/sample-data-a.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1573838955288/MOD09GQ.A3119781.haeynr.006.4074740546315.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1573838955288/MOD09GQ.A3119781.haeynr.006.4074740546315.hdf.v20191115T173114000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1573838955288/MOD09GQ.A3119781.haeynr.006.4074740546315.hdf.v20191115T173129000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1574358566826/MOD09GQ.A8013695.K4N0ey.006.3127282204118.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1574358566826/MOD09GQ.A8013695.K4N0ey.006.3127282204118.hdf.v20191121T175043000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1574359092072/MOD09GQ.A8702100.fZBWNI.006.3775135520157.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1574359092072/MOD09GQ.A8702100.fZBWNI.006.3775135520157.hdf.v20191121T175828000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1574359092072/MOD09GQ.A8702100.fZBWNI.006.3775135520157.hdf.v20191121T175845000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1574359380076/MOD09GQ.A9699053.vVDeTY.006.4873356130094.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1574359380076/MOD09GQ.A9699053.vVDeTY.006.4873356130094.hdf.v20191121T180329000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1574359380076/MOD09GQ.A9699053.vVDeTY.006.4873356130094.hdf.v20191121T180346000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1574377023900/MOD09GQ.A4940969.yRnTIO.006.6074235721878.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1574377023900/MOD09GQ.A4940969.yRnTIO.006.6074235721878.hdf.v20191121T225740000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1574377023900/MOD09GQ.A4940969.yRnTIO.006.6074235721878.hdf.v20191121T225757000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1574381176138/MOD09GQ.A5336057.nAG4KK.006.3221004503457.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1574381176138/MOD09GQ.A5336057.nAG4KK.006.3221004503457.hdf.v20191122T000648000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1574381176138/MOD09GQ.A5336057.nAG4KK.006.3221004503457.hdf.v20191122T000705000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1574712390341/MOD09GQ.A3493490.Xo2qAN.006.9380282039899.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1574712390341/MOD09GQ.A3493490.Xo2qAN.006.9380282039899.hdf.v20191125T200714000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1574712390341/MOD09GQ.A3493490.Xo2qAN.006.9380282039899.hdf.v20191125T200732000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1574716254780/MOD09GQ.A5754494.1nimTh.006.5626987564096.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1574716254780/MOD09GQ.A5754494.1nimTh.006.5626987564096.hdf.v20191125T211128000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1574716254780/MOD09GQ.A5754494.1nimTh.006.5626987564096.hdf.v20191125T211146000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1574717133091/MOD09GQ.A7767142.CYxJFA.006.2633356778897.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1574717133091/MOD09GQ.A7767142.CYxJFA.006.2633356778897.hdf.v20191125T212558000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1574717133091/MOD09GQ.A7767142.CYxJFA.006.2633356778897.hdf.v20191125T212615000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575912896952/MOD09GQ.A7399114.BmDCbe.006.9892042967204.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575912896952/MOD09GQ.A7399114.BmDCbe.006.9892042967204.hdf.v20191209T173526000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575912896952/MOD09GQ.A7399114.BmDCbe.006.9892042967204.hdf.v20191209T173547000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575923953772/MOD09GQ.A4805149.9oDhyh.006.9022510397676.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575923953772/MOD09GQ.A4805149.9oDhyh.006.9022510397676.hdf.v20191209T203947000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575923953772/MOD09GQ.A4805149.9oDhyh.006.9022510397676.hdf.v20191209T204003000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575925438077/MOD09GQ.A5542657.kyhjj1.006.5537130666716.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575925438077/MOD09GQ.A5542657.kyhjj1.006.5537130666716.hdf.v20191209T210419000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575925438077/MOD09GQ.A5542657.kyhjj1.006.5537130666716.hdf.v20191209T210448000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575926279043/MOD09GQ.A9109322.4iOCj1.006.8298263671181.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575926279043/MOD09GQ.A9109322.4iOCj1.006.8298263671181.hdf.v20191209T211818000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575926279043/MOD09GQ.A9109322.4iOCj1.006.8298263671181.hdf.v20191209T211833000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575927241619/MOD09GQ.A6015560.1qb9nY.006.2220280411055.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575927241619/MOD09GQ.A6015560.1qb9nY.006.2220280411055.hdf.v20191209T213423000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575927241619/MOD09GQ.A6015560.1qb9nY.006.2220280411055.hdf.v20191209T213438000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575992158393/MOD09GQ.A3341699.cCjc1D.006.4406545864663.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575992158393/MOD09GQ.A3341699.cCjc1D.006.4406545864663.hdf.v20191210T153630000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575992158393/MOD09GQ.A3341699.cCjc1D.006.4406545864663.hdf.v20191210T153645000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575992637033/MOD09GQ.A1138952.gpR2e1.006.4518044814416.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575992637033/MOD09GQ.A1138952.gpR2e1.006.4518044814416.hdf.v20191210T154417000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575992637033/MOD09GQ.A1138952.gpR2e1.006.4518044814416.hdf.v20191210T154432000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575993430080/MOD09GQ.A9946950.mAlY72.006.1085172309558.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575993430080/MOD09GQ.A9946950.mAlY72.006.1085172309558.hdf.v20191210T155738000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575993430080/MOD09GQ.A9946950.mAlY72.006.1085172309558.hdf.v20191210T155751000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575993900166/MOD09GQ.A3810090.EW85di.006.3466133695305.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575993900166/MOD09GQ.A3810090.EW85di.006.3466133695305.hdf.v20191210T160519000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575993900166/MOD09GQ.A3810090.EW85di.006.3466133695305.hdf.v20191210T160533000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575994615809/MOD09GQ.A7333345.RckwyJ.006.0332555268735.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575994615809/MOD09GQ.A7333345.RckwyJ.006.0332555268735.hdf.v20191210T161727000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575994615809/MOD09GQ.A7333345.RckwyJ.006.0332555268735.hdf.v20191210T161743000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575995102177/MOD09GQ.A9133222.xSOAC_.006.7143442454933.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575995102177/MOD09GQ.A9133222.xSOAC_.006.7143442454933.hdf.v20191210T162527000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575995102177/MOD09GQ.A9133222.xSOAC_.006.7143442454933.hdf.v20191210T162603000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575996898914/MOD09GQ.A4671964.mcRJii.006.3575074548289.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575996898914/MOD09GQ.A4671964.mcRJii.006.3575074548289.hdf.v20191210T165533000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575996898914/MOD09GQ.A4671964.mcRJii.006.3575074548289.hdf.v20191210T165547000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575999558578/MOD09GQ.A5908420.1mVEOo.006.0961024812410.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575999558578/MOD09GQ.A5908420.1mVEOo.006.0961024812410.hdf.v20191210T173949000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1575999558578/MOD09GQ.A5908420.1mVEOo.006.0961024812410.hdf.v20191210T174003000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576000334935/MOD09GQ.A5395888.IfjqmW.006.6212087429175.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576000334935/MOD09GQ.A5395888.IfjqmW.006.6212087429175.hdf.v20191210T175241000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576000334935/MOD09GQ.A5395888.IfjqmW.006.6212087429175.hdf.v20191210T175257000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576005735223/MOD09GQ.A5675548.0G4ccz.006.6657554508083.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576005735223/MOD09GQ.A5675548.0G4ccz.006.6657554508083.hdf.v20191210T192240000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576005735223/MOD09GQ.A5675548.0G4ccz.006.6657554508083.hdf.v20191210T192305000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576006501720/MOD09GQ.A5778024.foNw7Q.006.6574105768023.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576006501720/MOD09GQ.A5778024.foNw7Q.006.6574105768023.hdf.v20191210T193523000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576006501720/MOD09GQ.A5778024.foNw7Q.006.6574105768023.hdf.v20191210T193540000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576007039732/MOD09GQ.A0573928.tG823W.006.8959114868194.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576007039732/MOD09GQ.A0573928.tG823W.006.8959114868194.hdf.v20191210T194447000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576007039732/MOD09GQ.A0573928.tG823W.006.8959114868194.hdf.v20191210T194502000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576007578106/MOD09GQ.A8200321.wvugBk.006.2104901543407.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576007578106/MOD09GQ.A8200321.wvugBk.006.2104901543407.hdf.v20191210T195328000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576007578106/MOD09GQ.A8200321.wvugBk.006.2104901543407.hdf.v20191210T195343000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576012842378/MOD09GQ.A0087634.eCMzPL.006.7194649784728.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576012842378/MOD09GQ.A0087634.eCMzPL.006.7194649784728.hdf.v20191210T212114000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576012842378/MOD09GQ.A0087634.eCMzPL.006.7194649784728.hdf.v20191210T212129000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576015468515/MOD09GQ.A1775543.QmnKWZ.006.6004195429297.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576015468515/MOD09GQ.A1775543.QmnKWZ.006.6004195429297.hdf.v20191210T220459000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576015468515/MOD09GQ.A1775543.QmnKWZ.006.6004195429297.hdf.v20191210T220515000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576088588648/MOD09GQ.A5486655.lMAgW3.006.1459975841058.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576088588648/MOD09GQ.A5486655.lMAgW3.006.1459975841058.hdf.v20191211T182348000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576088588648/MOD09GQ.A5486655.lMAgW3.006.1459975841058.hdf.v20191211T182404000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576102296412/MOD09GQ.A5622767.jVW893.006.2184575670628.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576102296412/MOD09GQ.A5622767.jVW893.006.2184575670628.hdf.v20191211T221208000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576102296412/MOD09GQ.A5622767.jVW893.006.2184575670628.hdf.v20191211T221224000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576105218713/MOD09GQ.A4452652.1ykqZ3.006.2451039858926.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576105218713/MOD09GQ.A4452652.1ykqZ3.006.2451039858926.hdf.v20191211T230050000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576105218713/MOD09GQ.A4452652.1ykqZ3.006.2451039858926.hdf.v20191211T230108000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576109440469/MOD09GQ.A2592006.XKcNf_.006.1485654940189.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576109440469/MOD09GQ.A2592006.XKcNf_.006.1485654940189.hdf.v20191212T001124000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576182120916/MOD09GQ.A5826214.xtwugO.006.6261686801168.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576182120916/MOD09GQ.A5826214.xtwugO.006.6261686801168.hdf.v20191212T202225000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576182120916/MOD09GQ.A5826214.xtwugO.006.6261686801168.hdf.v20191212T202239000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576249014314/MOD09GQ.A6210996.nHGu8O.006.3174297550568.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576249014314/MOD09GQ.A6210996.nHGu8O.006.3174297550568.hdf.v20191213T145726000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576249014314/MOD09GQ.A6210996.nHGu8O.006.3174297550568.hdf.v20191213T145740000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576249970860/MOD09GQ.A8421402.t6nFKh.006.3037894989051.hdf", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576249970860/MOD09GQ.A8421402.t6nFKh.006.3037894989051.hdf.v20191213T151312000", + "s3://mhs3-protected/MOD09GQ___006/2017/MOD/mhs3-IngestGranuleDuplicateHandling-1576249970860/MOD09GQ.A8421402.t6nFKh.006.3037894989051.hdf.v20191213T151327000", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1573838955288/MOD09GQ.A3119781.haeynr.006.4074740546315.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1574358566826/MOD09GQ.A8013695.K4N0ey.006.3127282204118.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1574359092072/MOD09GQ.A8702100.fZBWNI.006.3775135520157.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1574359380076/MOD09GQ.A9699053.vVDeTY.006.4873356130094.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1574377023900/MOD09GQ.A4940969.yRnTIO.006.6074235721878.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1574381176138/MOD09GQ.A5336057.nAG4KK.006.3221004503457.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1574712390341/MOD09GQ.A3493490.Xo2qAN.006.9380282039899.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1574716254780/MOD09GQ.A5754494.1nimTh.006.5626987564096.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1574717133091/MOD09GQ.A7767142.CYxJFA.006.2633356778897.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575912896952/MOD09GQ.A7399114.BmDCbe.006.9892042967204.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575923953772/MOD09GQ.A4805149.9oDhyh.006.9022510397676.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575925438077/MOD09GQ.A5542657.kyhjj1.006.5537130666716.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575926279043/MOD09GQ.A9109322.4iOCj1.006.8298263671181.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575927241619/MOD09GQ.A6015560.1qb9nY.006.2220280411055.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575992158393/MOD09GQ.A3341699.cCjc1D.006.4406545864663.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575992637033/MOD09GQ.A1138952.gpR2e1.006.4518044814416.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575993430080/MOD09GQ.A9946950.mAlY72.006.1085172309558.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575993900166/MOD09GQ.A3810090.EW85di.006.3466133695305.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575994615809/MOD09GQ.A7333345.RckwyJ.006.0332555268735.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575995102177/MOD09GQ.A9133222.xSOAC_.006.7143442454933.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575996898914/MOD09GQ.A4671964.mcRJii.006.3575074548289.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575999558578/MOD09GQ.A5908420.1mVEOo.006.0961024812410.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576000334935/MOD09GQ.A5395888.IfjqmW.006.6212087429175.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576005735223/MOD09GQ.A5675548.0G4ccz.006.6657554508083.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576006501720/MOD09GQ.A5778024.foNw7Q.006.6574105768023.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576007039732/MOD09GQ.A0573928.tG823W.006.8959114868194.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576007578106/MOD09GQ.A8200321.wvugBk.006.2104901543407.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576012842378/MOD09GQ.A0087634.eCMzPL.006.7194649784728.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576015468515/MOD09GQ.A1775543.QmnKWZ.006.6004195429297.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576088588648/MOD09GQ.A5486655.lMAgW3.006.1459975841058.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576102296412/MOD09GQ.A5622767.jVW893.006.2184575670628.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576105218713/MOD09GQ.A4452652.1ykqZ3.006.2451039858926.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576109440469/MOD09GQ.A2592006.XKcNf_.006.1485654940189.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576182120916/MOD09GQ.A5826214.xtwugO.006.6261686801168.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576249014314/MOD09GQ.A6210996.nHGu8O.006.3174297550568.cmr.xml", + "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576249970860/MOD09GQ.A8421402.t6nFKh.006.3037894989051.cmr.xml", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1573838955288/MOD09GQ.A3119781.haeynr.006.4074740546315_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1574358566826/MOD09GQ.A8013695.K4N0ey.006.3127282204118_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1574359092072/MOD09GQ.A8702100.fZBWNI.006.3775135520157_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1574359380076/MOD09GQ.A9699053.vVDeTY.006.4873356130094_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1574377023900/MOD09GQ.A4940969.yRnTIO.006.6074235721878_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1574381176138/MOD09GQ.A5336057.nAG4KK.006.3221004503457_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1574712390341/MOD09GQ.A3493490.Xo2qAN.006.9380282039899_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1574716254780/MOD09GQ.A5754494.1nimTh.006.5626987564096_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1574717133091/MOD09GQ.A7767142.CYxJFA.006.2633356778897_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575912896952/MOD09GQ.A7399114.BmDCbe.006.9892042967204_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575923953772/MOD09GQ.A4805149.9oDhyh.006.9022510397676_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575925438077/MOD09GQ.A5542657.kyhjj1.006.5537130666716_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575926279043/MOD09GQ.A9109322.4iOCj1.006.8298263671181_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575927241619/MOD09GQ.A6015560.1qb9nY.006.2220280411055_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575992158393/MOD09GQ.A3341699.cCjc1D.006.4406545864663_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575992637033/MOD09GQ.A1138952.gpR2e1.006.4518044814416_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575993430080/MOD09GQ.A9946950.mAlY72.006.1085172309558_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575993900166/MOD09GQ.A3810090.EW85di.006.3466133695305_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575994615809/MOD09GQ.A7333345.RckwyJ.006.0332555268735_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575995102177/MOD09GQ.A9133222.xSOAC_.006.7143442454933_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575996898914/MOD09GQ.A4671964.mcRJii.006.3575074548289_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1575999558578/MOD09GQ.A5908420.1mVEOo.006.0961024812410_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576000334935/MOD09GQ.A5395888.IfjqmW.006.6212087429175_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576005735223/MOD09GQ.A5675548.0G4ccz.006.6657554508083_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576006501720/MOD09GQ.A5778024.foNw7Q.006.6574105768023_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576007039732/MOD09GQ.A0573928.tG823W.006.8959114868194_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576007578106/MOD09GQ.A8200321.wvugBk.006.2104901543407_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576012842378/MOD09GQ.A0087634.eCMzPL.006.7194649784728_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576015468515/MOD09GQ.A1775543.QmnKWZ.006.6004195429297_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576088588648/MOD09GQ.A5486655.lMAgW3.006.1459975841058_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576102296412/MOD09GQ.A5622767.jVW893.006.2184575670628_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576105218713/MOD09GQ.A4452652.1ykqZ3.006.2451039858926_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576109440469/MOD09GQ.A2592006.XKcNf_.006.1485654940189_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576182120916/MOD09GQ.A5826214.xtwugO.006.6261686801168_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576249014314/MOD09GQ.A6210996.nHGu8O.006.3174297550568_ndvi.jpg", + "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1576249970860/MOD09GQ.A8421402.t6nFKh.006.3037894989051_ndvi.jpg" + ], + "onlyInDynamoDb": [ + { + "uri": "s3://mhs3-private/MOD09GQ___006/MOD/MOD09GQ.A9218123.TJbx_C.006.7667598863143.hdf.met", + "granuleId": "MOD09GQ.A9218123.TJbx_C.006.7667598863143" + }, + { + "uri": "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestUMMGSuccess-1575927241338/MOD09GQ.A9544845.jLg7sy.006.6264785375800.hdf.met", + "granuleId": "MOD09GQ.A9544845.jLg7sy.006.6264785375800" + }, + { + "uri": "s3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestUMMGSuccess-1575993900243/MOD09GQ.A8473991.0Vg1BF.006.7743328283296.hdf.met", + "granuleId": "MOD09GQ.A8473991.0Vg1BF.006.7743328283296" + }, + { + "uri": "s3://mhs3-private/MOD14A1___006/MOD/MOD14A1.A6229684.liNodB.006.7559017774430.hdf.met", + "granuleId": "MOD14A1.A6229684.liNodB.006.7559017774430" + }, + { + "uri": "s3://mhs3-private/MOD14A1___006/MOD/MOD14A1.A7489357.WuQwlB.006.1633383724781.hdf.met", + "granuleId": "MOD14A1.A7489357.WuQwlB.006.1633383724781" + }, + { + "uri": "s3://mhs3-private/MOD14A1___006/MOD/MOD14A1.A9471544.8iLnaC.006.7851658874405.hdf.met", + "granuleId": "MOD14A1.A9471544.8iLnaC.006.7851658874405" + }, + { + "uri": "s3://mhs3-private/MYD13Q1___006/BRO/BROWSE.MYD13Q1.A8039859.cm_J1d.006.4049670693348.hdf", + "granuleId": "MYD13Q1.A8039859.cm_J1d.006.4049670693348" + }, + { + "uri": "s3://mhs3-private/MYD13Q1___006/BRO/BROWSE.MYD13Q1.A9323228.btdDqi.006.2091522185393.hdf", + "granuleId": "MYD13Q1.A9323228.btdDqi.006.2091522185393" + }, + { + "uri": "s3://mhs3-private/MYD13Q1___006/MYD/MYD13Q1.A8039859.cm_J1d.006.4049670693348.hdf.met", + "granuleId": "MYD13Q1.A8039859.cm_J1d.006.4049670693348" + }, + { + "uri": "s3://mhs3-private/MYD13Q1___006/MYD/MYD13Q1.A9323228.btdDqi.006.2091522185393.hdf.met", + "granuleId": "MYD13Q1.A9323228.btdDqi.006.2091522185393" + }, + { + "uri": "s3://mhs3-protected/MOD09GQ___006/2017/MOD/MOD09GQ.A9218123.TJbx_C.006.7667598863143.hdf", + "granuleId": "MOD09GQ.A9218123.TJbx_C.006.7667598863143" + }, + { + "uri": "s3://mhs3-protected/MOD14A1___006/2017/MOD/MOD14A1.A6229684.liNodB.006.7559017774430.hdf", + "granuleId": "MOD14A1.A6229684.liNodB.006.7559017774430" + }, + { + "uri": "s3://mhs3-protected/MOD14A1___006/2017/MOD/MOD14A1.A7489357.WuQwlB.006.1633383724781.hdf", + "granuleId": "MOD14A1.A7489357.WuQwlB.006.1633383724781" + }, + { + "uri": "s3://mhs3-protected/MOD14A1___006/2017/MOD/MOD14A1.A9471544.8iLnaC.006.7851658874405.hdf", + "granuleId": "MOD14A1.A9471544.8iLnaC.006.7851658874405" + }, + { + "uri": "s3://mhs3-protected/MYD13Q1___006/2017/MYD/MYD13Q1.A8039859.cm_J1d.006.4049670693348.hdf", + "granuleId": "MYD13Q1.A8039859.cm_J1d.006.4049670693348" + }, + { + "uri": "s3://mhs3-protected/MYD13Q1___006/2017/MYD/MYD13Q1.A9323228.btdDqi.006.2091522185393.hdf", + "granuleId": "MYD13Q1.A9323228.btdDqi.006.2091522185393" + }, + { + "uri": "s3://mhs3-protected/b793ea149845295b794101bd50fea09a24c8be28", + "granuleId": "6d949f7514da513414c5021ef194268589b65182" + }, + { + "uri": "s3://mhs3-protected/mhs3-IngestUMMGSuccess-1575927241338-test-data/files/MOD09GQ___006/2016/MOD/mhs3-IngestUMMGSuccess-1575927241338/MOD09GQ.A9544845.jLg7sy.006.6264785375800.hdf", + "granuleId": "MOD09GQ.A9544845.jLg7sy.006.6264785375800" + }, + { + "uri": "s3://mhs3-protected/mhs3-IngestUMMGSuccess-1575993900243-test-data/files/MOD09GQ___006/2016/MOD/mhs3-IngestUMMGSuccess-1575993900243/MOD09GQ.A8473991.0Vg1BF.006.7743328283296.hdf", + "granuleId": "MOD09GQ.A8473991.0Vg1BF.006.7743328283296" + }, + { + "uri": "s3://mhs3-protected-2/MOD09GQ___006/MOD/MOD09GQ.A9218123.TJbx_C.006.7667598863143.cmr.xml", + "granuleId": "MOD09GQ.A9218123.TJbx_C.006.7667598863143" + }, + { + "uri": "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestUMMGSuccess-1575927241338/MOD09GQ.A9544845.jLg7sy.006.6264785375800.cmr.json", + "granuleId": "MOD09GQ.A9544845.jLg7sy.006.6264785375800" + }, + { + "uri": "s3://mhs3-protected-2/MOD09GQ___006/MOD/mhs3-IngestUMMGSuccess-1575993900243/MOD09GQ.A8473991.0Vg1BF.006.7743328283296.cmr.json", + "granuleId": "MOD09GQ.A8473991.0Vg1BF.006.7743328283296" + }, + { + "uri": "s3://mhs3-protected-2/MOD14A1___006/MOD/MOD14A1.A6229684.liNodB.006.7559017774430.cmr.xml", + "granuleId": "MOD14A1.A6229684.liNodB.006.7559017774430" + }, + { + "uri": "s3://mhs3-protected-2/MOD14A1___006/MOD/MOD14A1.A7489357.WuQwlB.006.1633383724781.cmr.xml", + "granuleId": "MOD14A1.A7489357.WuQwlB.006.1633383724781" + }, + { + "uri": "s3://mhs3-protected-2/MOD14A1___006/MOD/MOD14A1.A9471544.8iLnaC.006.7851658874405.cmr.xml", + "granuleId": "MOD14A1.A9471544.8iLnaC.006.7851658874405" + }, + { + "uri": "s3://mhs3-protected-2/MYD13Q1___006/MYD/MYD13Q1.A8039859.cm_J1d.006.4049670693348.cmr.xml", + "granuleId": "MYD13Q1.A8039859.cm_J1d.006.4049670693348" + }, + { + "uri": "s3://mhs3-protected-2/MYD13Q1___006/MYD/MYD13Q1.A9323228.btdDqi.006.2091522185393.cmr.xml", + "granuleId": "MYD13Q1.A9323228.btdDqi.006.2091522185393" + }, + { + "uri": "s3://mhs3-public/MOD09GQ___006/MOD/MOD09GQ.A9218123.TJbx_C.006.7667598863143_ndvi.jpg", + "granuleId": "MOD09GQ.A9218123.TJbx_C.006.7667598863143" + }, + { + "uri": "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestUMMGSuccess-1575927241338/MOD09GQ.A9544845.jLg7sy.006.6264785375800_ndvi.jpg", + "granuleId": "MOD09GQ.A9544845.jLg7sy.006.6264785375800" + }, + { + "uri": "s3://mhs3-public/MOD09GQ___006/MOD/mhs3-IngestUMMGSuccess-1575993900243/MOD09GQ.A8473991.0Vg1BF.006.7743328283296_ndvi.jpg", + "granuleId": "MOD09GQ.A8473991.0Vg1BF.006.7743328283296" + }, + { + "uri": "s3://mhs3-public/MOD14A1___006/BRO/BROWSE.MOD14A1.A6229684.liNodB.006.7559017774430.1.jpg", + "granuleId": "MOD14A1.A6229684.liNodB.006.7559017774430" + }, + { + "uri": "s3://mhs3-public/MOD14A1___006/BRO/BROWSE.MOD14A1.A7489357.WuQwlB.006.1633383724781.1.jpg", + "granuleId": "MOD14A1.A7489357.WuQwlB.006.1633383724781" + }, + { + "uri": "s3://mhs3-public/MOD14A1___006/BRO/BROWSE.MOD14A1.A9471544.8iLnaC.006.7851658874405.1.jpg", + "granuleId": "MOD14A1.A9471544.8iLnaC.006.7851658874405" + }, + { + "uri": "s3://mhs3-public/MYD13Q1___006/BRO/BROWSE.MYD13Q1.A8039859.cm_J1d.006.4049670693348.1.jpg", + "granuleId": "MYD13Q1.A8039859.cm_J1d.006.4049670693348" + }, + { + "uri": "s3://mhs3-public/MYD13Q1___006/BRO/BROWSE.MYD13Q1.A9323228.btdDqi.006.2091522185393.1.jpg", + "granuleId": "MYD13Q1.A9323228.btdDqi.006.2091522185393" + } + ] + }, + "collectionsInCumulusCmr": { + "okCount": 1, + "onlyInCumulus": [ + "L2_HR_PIXC_test-mhs3-KinesisTestError-1574717133091___000", + "L2_HR_PIXC_test-mhs3-KinesisTestError-1575912896780___000", + "L2_HR_PIXC_test-mhs3-KinesisTestError-1575927241491___000", + "L2_HR_PIXC_test-mhs3-KinesisTestError-1575992637028___000", + "L2_HR_PIXC_test-mhs3-KinesisTestError-1575994615798___000", + "L2_HR_PIXC_test-mhs3-KinesisTestError-1576007039306___000", + "L2_HR_PIXC_test-mhs3-KinesisTestError-1576007578006___000", + "L2_HR_PIXC_test-mhs3-KinesisTestError-1576015468425___000", + "L2_HR_PIXC_test-mhs3-KinesisTestError-1576088588601___000", + "L2_HR_PIXC_test-mhs3-KinesisTestError-1576249014350___000", + "MOD09GQ_test-mhs3-IngestGranuleSuccess-1573771398734___006", + "MOD09GQ_test-mhs3-IngestGranuleSuccess-1576012842508___006", + "MOD09GQ_test-mhs3-IngestGranuleSuccess-1576108764706___006" + ], + "onlyInCmr": [ + "A2_RainOcn_NRT___0", + "A2_SI12_NRT___0", + "A2_SI25_NRT___0", + "A2_SI6_NRT___0", + "AST_L1A___003", + "L2_HR_PIXC___000", + "L2_HR_PIXC___1", + "MCD43A1___006", + "MOD09GQ___006", + "MOD11A1___006", + "MUR-JPL-L4-GLOB-v4.1___1", + "MYD09A1___006", + "MYD13A1___006", + "MYD13Q1___006", + "hs3avaps___1", + "hs3cpl___1", + "hs3hamsr___1", + "hs3hirad___1", + "hs3hiwrap2___2", + "hs3hiwrap3___3", + "hs3hiwrap4___4", + "hs3hiwrap5___5", + "hs3hiwrap___1", + "hs3wwlln___1", + "tmt_test___001" + ] + }, + "granulesInCumulusCmr": { + "okCount": 7, + "onlyInCumulus": [ + { + "granuleId": "MOD14A1.A4135026.ekHe8x.006.3355759967228", + "collectionId": "MOD14A1___006" + }, + { + "granuleId": "MOD14A1.A4526233.492TT9.006.7426276787300", + "collectionId": "MOD14A1___006" + }, + { + "granuleId": "MOD14A1.A4562488.2ppgk0.006.3054981124199", + "collectionId": "MOD14A1___006" + }, + { + "granuleId": "MOD14A1.A5124023.qCpo9T.006.5379922199867", + "collectionId": "MOD14A1___006" + }, + { + "granuleId": "MOD14A1.A8512234.0CE5a0.006.8217730108870", + "collectionId": "MOD14A1___006" + }, + { + "granuleId": "MOD14A1.A8859169.oyt3ZQ.006.6651148631629", + "collectionId": "MOD14A1___006" + }, + { + "granuleId": "MOD14A1.A8959582.q932t8.006.4564896025574", + "collectionId": "MOD14A1___006" + } + ], + "onlyInCmr": [ + { + "GranuleUR": "MOD14A1.A0031922.9lenyG.006.4681412227733", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0161520.Icg3Hd.006.2590173029614", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0181238.I2lQMR.006.5213076331746", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0241275.PP6Bru.006.6690267738935", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0268709.XzCq6U.006.2062396767458", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0303340.ZOAcic.006.3126112323008", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0367178.OZLnKJ.006.1553933304160", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0385810.sfCLoU.006.4410204411125", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0408148.aVHJRj.006.6581128707331", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0426428.O6puS3.006.4037434378100", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0435030.F4TfO6.006.5475131012423", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0438468.A4UnhQ.006.0916618386198", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0439787.yYkYoF.006.9258816292523", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0469562.6buMGA.006.6641209270503", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0477077.ZwnsyD.006.2155034172666", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0477862.m5RyzJ.006.7749345641502", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0478724.X0E3aO.006.2167329718097", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0493385.ncUbST.006.8724510800351", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0496800.o1BZif.006.7997570788246", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0499025.Bl5XRj.006.7345541650084", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0530215.QTb6o8.006.3728095241030", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0543357.DeSiuz.006.9248161352838", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0559967.WaTfcs.006.7692038273164", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0573198.lNm8ej.006.4937226253472", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0579654.EfC7b5.006.8547135416723", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0594210.INgzWo.006.9610785542200", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0605025.sNQ6Xj.006.8017396449666", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0686414.KR4Yj9.006.1657242945365", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0708604.EOI65z.006.5281139092272", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0731080.kPnCI3.006.9645252704886", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0743873.dTU1LJ.006.2850872371107", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0829889.4LBq3W.006.5205877566239", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0830785.BzRLhh.006.2098466050997", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0851511.fpq1t8.006.8813309643418", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0854437.MAY0Q8.006.9959874308730", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0858047.xzmmnz.006.1278403771091", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0919553.8RKHu9.006.9149860071026", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0970863.XLNxT5.006.2798234861119", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A0989760.1rAfoU.006.1423241066769", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1034119.yAxS0C.006.1060775566122", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1081653.E8hdrc.006.4410360914417", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1115000.4bDb_o.006.2278326105128", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1135111.j6ltZS.006.6490601038632", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1148386.BTNJja.006.1703429501514", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1178631.xM5T76.006.0022032029423", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1181278.bQPeTW.006.1783680708565", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1189394.88OTN4.006.7906263730471", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1218342.lSwbHi.006.9300255035117", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1320673.fbqmSu.006.2496971059489", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1324671.g1zQ18.006.6645018100409", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1331363.iuSTbG.006.8466737452095", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1386278.PER5I0.006.3128037156396", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1394645.jvLNB4.006.7932563501595", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1398327.OoEhNp.006.6140710403958", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1414599.Ir8MaJ.006.3472304331616", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1425794.dGVwQt.006.9221129681517", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1469997.3eC9_v.006.9974796300625", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1487775.Xh_YYy.006.3907075335867", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1540212.i4bO61.006.3792997119574", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1555091.ArCGtd.006.7836090584762", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1627106.yXnxzc.006.5122291281725", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1637985.L2C7jj.006.4081838044955", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1689913.IhNjOC.006.0805594965670", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1699875.I7fCuo.006.5825142358071", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1740324.k9dRfR.006.5120684081887", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1756305.44qrI7.006.6544298445142", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1773221.vL2xGp.006.1502509955110", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1792533._ROTa6.006.7574259958264", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1804251.OYGvNk.006.3189308757490", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1828394.mSRFgP.006.0394762493146", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1839190.6bZY0R.006.1308585329472", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1869818.paiptw.006.4469275989446", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1871604.ws5lvU.006.3880569542726", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1884556.YljVmJ.006.7073152912015", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1903936.gdxhTt.006.8859200024953", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1926989.sWkBkt.006.4161390607995", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1930398.32K2n6.006.3858864868017", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1960074.lVDIHZ.006.0029599573994", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1982683.VRNZ9e.006.8232263994655", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A1988586.g5hg52.006.0093634085750", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2015525.gSlbDC.006.6162623767400", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2023218.gKxjhE.006.0679016601696", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2045155.IldGaW.006.5587732568060", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2047465.DIVVCq.006.3300117538068", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2082913.k2AH9d.006.2063774562440", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2107169.4Hx5QW.006.4489290579513", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2199026.BW6m07.006.7360388694859", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2249549.MIX7bE.006.4598121918597", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2326990.tOsSli.006.7888038073008", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2406099.Sqm_ya.006.3870376151154", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2434928.qQSDWp.006.0111410832633", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2436662.fEDb_J.006.9857059507872", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2467775.iFASnu.006.9373140163082", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2503671.SJJAxR.006.6004529904364", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2508345.rH7Jmx.006.9155872079831", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2530773.vZzH3t.006.3247672973972", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2543004.CN_Urw.006.9383083242890", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2579574.IPJ0_h.006.4470297178564", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2607276.kAHAKI.006.9719289832615", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2620427.S0GPId.006.6556845431909", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2640677.tJZhUP.006.3275708023016", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2659229.tFUmqE.006.2699754508939", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2768377.b65yZa.006.2465291425471", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2811951.KfhzNs.006.1071136264564", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2813635.PmJSgS.006.6559229596528", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2823097.rwEG6d.006.9559616817462", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2867966.O1iEvW.006.8400528388272", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2874085.m9ZBCj.006.6926008557571", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2935897.gCcyF0.006.2120404779982", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2980296.9bMG3X.006.1850666675406", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A2981534.Qr3jFk.006.9466954357415", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3015108.zJBsS8.006.8064551695149", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3065415.tRf2a4.006.5375458618864", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3094861.BxVNFv.006.1428320778400", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3103291.oqjLLY.006.9681234364077", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3181089.c84dOC.006.6849102364166", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3192158.EXtdRS.006.5241419287772", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3221972.5LrA4V.006.6741514442929", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3236084.0KyoMb.006.6325930374418", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3237911.2wiWBj.006.3788637026706", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3242735.B590Id.006.0789662245995", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3243788.FaigZr.006.5070669011170", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3253459.Wg2qVw.006.8645624871878", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3261333.kmf3Da.006.5494327939194", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3384866.pnYPpq.006.3422769889410", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3393327.TLAHdj.006.7025810345883", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3474712.qiCipo.006.3574120304485", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3538628.1u8ie7.006.0861387413780", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3568827.aI6SVu.006.7927924616119", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3587507.oJpXUl.006.0050227223409", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3639028.D7TRcU.006.3524076198815", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3659941.U9_JFB.006.9640903408294", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3706999.olpdUX.006.9985944506720", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3730274._FMaX5.006.1488458332851", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3745297.7yqN7N.006.6203206230519", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3777110.2nI_z1.006.4458932161560", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3836139.rEwbJj.006.5244797579248", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3883637.Em4acn.006.7492507042664", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3899227.54rhqw.006.9198542516974", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A3899600.C6Ahqo.006.2951490717460", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4036255.LSIcy8.006.4004574608122", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4054288.8xvHDB.006.6199273392157", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4068488.NS6rb0.006.9525121033929", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4083098.iB_17X.006.8170576880727", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4092129.ICW8_c.006.2835778900249", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4165221.i0AGqZ.006.0000114393839", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4211671.EzOor8.006.9264465388568", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4215712.IiJiBs.006.3078636075675", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4219006.Ok_npC.006.8717060833704", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4220400.faCGhR.006.8939271143193", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4250172.4KUesf.006.9271102774552", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4297834.DNrDFd.006.1911684462762", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4321780.obqoQT.006.2924612998174", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4346691._LlduX.006.5638546826700", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4352997.cycHF8.006.0390693353762", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4374447.3efcem.006.2904967410566", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4380588.pH6Fdy.006.4273334058926", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4439788.wiesOZ.006.5924892079401", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4450652.hScD6j.006.6991048602566", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4467658.RV0m12.006.4618584382673", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4493406.XGGHRX.006.3941985703164", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4510771.cYFwb3.006.6093583601899", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4540721.GWlZyE.006.3216801552592", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4600227.K2FpI2.006.0348804402510", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4601203.Mtnovz.006.1758782609202", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4632703.VZRyrL.006.1868762621072", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4666224.gbIm_n.006.3153915963290", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4742629.w1Q8sE.006.8413755571036", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4743195.XXuFep.006.9650249440254", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4743973.Ptu4QH.006.2859569760830", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4753915.XfIvAK.006.2647520684202", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4819663.hceLMY.006.4888474957881", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4848233.if6V2s.006.5228249902400", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4894103.gNQSfb.006.0498231218639", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4908086.VCZ_lp.006.2403345043184", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4910518.JVmHGD.006.6285896487087", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4978437.opBZwR.006.0038826280988", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4986395.CCcRHU.006.2754654735559", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A4998771.mlwd2W.006.3091218809916", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5071312.QoQBhY.006.6641136667881", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5083285.2xdBAq.006.5528806886139", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5134375.IFJxMc.006.9089019094156", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5210697.Zj1X60.006.1671443614005", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5255259.6QB4P5.006.5861378094572", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5259810.IHt7wm.006.2042821443503", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5333141.ej2_dh.006.2075553701768", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5388005.PDcM2l.006.7499759541761", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5446137.R5VQju.006.0496023742915", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5470474.PYLkXK.006.2486305145959", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5536132.Sh9I1Q.006.7321416452215", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5578210.vYkHrP.006.3097372279359", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5581046.QznotV.006.2199781681621", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5613054.DSp_0v.006.7488042302935", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5613577.kr3VyR.006.5957040298705", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5615374.kqK1gv.006.9862819253374", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5645384.pwoYe4.006.1647551931437", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5674760.wwUR1r.006.1161313538574", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5779783.iAoyn9.006.0753207782283", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5794321.zBygQV.006.0606713843927", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5843576.ftYID0.006.3852722614544", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5879650.xvPlJj.006.0164200189000", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5895367.uNnp4k.006.6870373694725", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5906713.GCZmcq.006.0996141592045", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5915652.IFBD5L.006.8209803787553", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5916225.iHZvvk.006.1256889269179", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5924160.8v7G3a.006.8071081859957", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5957211.uwomBD.006.1874059172147", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A5959582.Alz3YH.006.9784966634734", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6140781.dwVHRM.006.9795613581334", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6154811._K9e2H.006.0554685839175", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6166519.2Zezz5.006.7680191797368", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6169478.oVibQR.006.5307651972872", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6182322.vuyWZV.006.2878435205612", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6193133.00WOva.006.2742381792467", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6220907.ffSLhU.006.8444268919429", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6221592.0AvUpm.006.4972115341840", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6240892.J_ihzm.006.8232514440182", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6242594.PD8_UC.006.7945609781836", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6294412.iWI3ep.006.6795977534561", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6314532.78zfxS.006.6223531035200", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6321424._ZyOBe.006.5762837656120", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6345909.bstkMx.006.5525239283796", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6346827.RucmvD.006.0338928454861", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6350821.yoZG65.006.3318590275783", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6377697._KygO1.006.6790931369124", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6433508.Cn9VA_.006.4956661543835", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6470016.qcoFaa.006.5498889293816", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6498944.7N8pXl.006.1521246263458", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6504617.OhkNgA.006.8477604720347", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6546816.VEO92z.006.5480489177490", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6575512.mW2bSV.006.6185576608888", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6657595.psbb42.006.8233981132204", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6682927.EoMMj_.006.1855665560268", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6706112.TF1qq9.006.1771098824289", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6780016.8PSUed.006.6143912609815", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6780662.GFErXb.006.0683838822328", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6787983.6uEcJQ.006.0491441481084", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6801171.BhjLbF.006.9713529333826", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6813543.4KPfCP.006.0879840866356", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6829604.KjI8cW.006.9117191472337", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6854369.0mUX3W.006.6320693878688", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6876510.MUAcHG.006.7692816445490", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6879561.g2xHzm.006.5729596282955", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6885606.ZpeFgD.006.6814469969755", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6904037.33OVN7.006.3977486435483", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6954524.9_88bg.006.2010411596751", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6969850.Y9k4Oq.006.9751563750493", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A6988892.FR1RiB.006.5977637509747", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7000581.IWRvrw.006.9637649469071", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7040298.wALe2l.006.8076432241643", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7079341.AFFouJ.006.4177191558605", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7116232.YPJAyy.006.9634780456925", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7121539.rRlj84.006.8568841150181", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7228927.7QRGz7.006.5737505467736", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7235010.9PdjHH.006.1891432762889", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7243264.Vt36ib.006.1578864318653", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7254030.b_TPqs.006.3035275090373", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7272337.DyV01y.006.4585183067998", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7357306.fu6Lfh.006.6722367866085", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7384707.0CQnZb.006.4490242707292", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7391492.pj93zG.006.7762172202424", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7442669.V6hzlo.006.8727283948669", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7511554.eOXCq5.006.1269728944670", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7571652.EXH_Bf.006.1980305546683", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7589953.mQ_rr6.006.5761289422251", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7662314.Q3HPTA.006.2925884807202", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7675910.VVEdpg.006.9881036819285", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7710418.8Y6DPu.006.1050339994866", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7717473.vdce9J.006.4764634793209", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7774307.1xfV3Q.006.2135217284739", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7774534.ospCfh.006.1382047512532", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7775384.tmGlVi.006.3117120868236", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7792926.uxBDKg.006.8459817991128", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7797808.bMQSOj.006.6390170359956", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7809989.0L9HHU.006.6459538384869", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7859847.8969jr.006.4573035209852", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7892638.q2HPu7.006.9408643265275", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7900349.UvVafB.006.5028127642428", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7910742.Mn5yt1.006.2396134919724", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7918975.P4XLCu.006.1743355786218", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A7977416.pWD5iP.006.7074134724949", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8010774.dA2YUh.006.4342162115952", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8011056.BeHzAb.006.4490265608575", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8015009.E4P0ug.006.5741781701080", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8049294.vkawoy.006.0937382245643", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8052708.iCaVer.006.7787777713005", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8060469.ZPahDZ.006.8982046108230", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8073097.j6jSWQ.006.4083585468997", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8093883.QxXolH.006.4493515582587", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8106903.MZq9eP.006.5382401845931", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8124629.hY1dN5.006.3064417321277", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8161961.Kw_Zre.006.5456643911149", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8177133.oZYaja.006.0364568139575", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8195501.KU8yll.006.3296667507482", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8216766.7T40z1.006.4947562988284", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8221290.PIL5Hb.006.9130547287716", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8248086.elRIsi.006.0567672305391", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8250434.CAqXH9.006.5676147389429", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8261494.iS8TuO.006.2282913340704", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8269413.pKogkh.006.4412309077024", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8279197.hToD9P.006.6175739867970", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8281719.gum1ab.006.1459398984505", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8288987.uo_gYS.006.0250896887040", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8295706.kPc8E4.006.7759769767754", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8303794.PMYy_l.006.9511602329121", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8343082.ezfBlw.006.2682603002957", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8385273.QxjKuc.006.6198021802383", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8478053.KaIGMi.006.3840199854152", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8488534.6J0MbI.006.1265985239675", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8499672.ecbNK3.006.5953840974188", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8529721.9lhoO0.006.6507378736299", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8572944.gippDi.006.4878766702019", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8583690.eGuOFF.006.3178725084313", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8586334.HLzyH2.006.4849383317064", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8606985.so7CF1.006.9688821597814", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8641020.MeZI6O.006.9373642031107", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8647926.ihXCFx.006.4788920531385", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8685478.azlSgB.006.2688103987158", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8702349.ZWlsXG.006.4957379951587", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8770143.OGIqeK.006.5547498198971", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8789273.0XW8dF.006.7316776957736", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8802522.sFaJEM.006.2026321336527", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8837879.Bki1Yv.006.9338509978014", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8859597.OS7v_B.006.2492497371139", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8859711.11wb3X.006.7995412937159", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8881987.Vtk46X.006.2569820256169", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8882746.Lekpxo.006.9257961202638", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8895901.bOhVvA.006.7046394153913", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8909863.eydtSS.006.1149409844354", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8932526.XjjHVI.006.3394128582613", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8958133.2PbhKY.006.7237752355433", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8963220.DSYj8h.006.0512930156059", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A8987608.lF3pIc.006.2893793704999", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9041180.MCpHp9.006.8191502640460", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9053660.qjpXaq.006.0165283377258", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9149355.aaHA7v.006.5099150808083", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9164803.RUftmG.006.1807010132692", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9166659.O9jTZH.006.4902641669957", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9211448.ILglHo.006.1801978246969", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9226530.v31XjU.006.2733149852665", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9309522.IdHGiP.006.1511064985561", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9311127.e928Zx.006.9503691370855", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9317680.A72Rxm.006.9227785953020", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9330328.YSRF8v.006.1819704284610", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9355035.W1gKQO.006.6409047050587", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9365735.GCMy2Y.006.2875216040398", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9391469.9XYoae.006.3013397272445", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9414005.cb12cM.006.7479280228146", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9513175._mfpmw.006.1963912704522", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9539025.XQy0KQ.006.3923459802201", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9541790.gDH7wC.006.9947767843106", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9567824.AkANSV.006.8470786106077", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9591289.FvUfbk.006.2520719079816", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9597426.D_ag6a.006.9140139663225", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9645467.OE9YwX.006.7200895500926", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9664423.aIUz39.006.3652981858375", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9677433.OeeR2S.006.9290490472475", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9684065.ecsNmm.006.9181724505840", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9814119.trqgNT.006.7636352907992", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9834656.SLmX8g.006.3493493596874", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9839020.nik1MP.006.2145484187039", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9845048.PcmJ_y.006.6013670131610", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9867427.9ciRAK.006.9977199496795", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9890507.RU4JUk.006.4082866676019", + "ShortName": "MOD14A1", + "Version": "006" + }, + { + "GranuleUR": "MOD14A1.A9990408.N4T1Mn.006.9738328377056", + "ShortName": "MOD14A1", + "Version": "006" + } + ] + }, + "filesInCumulusCmr": { + "okCount": 4, + "onlyInCumulus": [ + { + "fileName": "granule.001.1234.jpg", + "uri": "s3://some-bucket/granule__001/granule.001.1234.jpg", + "granuleId": "granule.001.1234" + } + ], + "onlyInCmr": [ + { + "URL": "https://some-bucket.s3.amazonaws.com/granule__002/granule.002.5678.jpg", + "Type": "GET DATA", + "GranuleUR": "granule.002.5678" + } + ] + } +} diff --git a/cypress/fixtures/seeds/reconciliationReportFixture.json b/cypress/fixtures/seeds/reconciliationReportFixture.json new file mode 100644 index 000000000..167a1e13d --- /dev/null +++ b/cypress/fixtures/seeds/reconciliationReportFixture.json @@ -0,0 +1,25 @@ +{ + "meta": { + "name": "cumulus-api", + "stack": "anyStack", + "table": "reconciliationReport", + "limit": 10, + "page": 1, + "count": 2, + "fixtureComment": "These files need to exist and be uploaded into the localstack s3 buckets that match the location" + }, + "results": [ + { + "name": "inventoryReport-20200114T205238781", + "location": "s3://localbucket/localrun/reconciliation-reports/inventoryReport-20200114T205238781.json", + "type": "Inventory", + "status": "Generated" + }, + { + "name": "inventoryReport-20200114T202529026", + "location": "s3://localbucket/localrun/reconciliation-reports/inventoryReport-20200114T202529026.json", + "type": "Inventory", + "status": "Generated" + } + ] +} diff --git a/cypress/integration/auth_spec.js b/cypress/integration/auth_spec.js index e6185d379..0c2af9974 100644 --- a/cypress/integration/auth_spec.js +++ b/cypress/integration/auth_spec.js @@ -33,7 +33,7 @@ describe('Dashboard authentication', () => { it('should logout user on invalid JWT token', () => { cy.window().its('appStore').then((store) => { - cy.task('generateJWT', {}).then((invalidJwt) => { + cy.task('generateJWT', { expirationTime: 0 }).then((invalidJwt) => { // Dispatch an action to set the token store.dispatch({ type: SET_TOKEN, @@ -62,7 +62,7 @@ describe('Dashboard authentication', () => { }); cy.window().its('appStore').then((store) => { - const expirationTime = (new Date(Date.now() - 24 * 3600 * 1000)).valueOf(); + const expirationTime = (new Date(Date.now() - 24 * 3600 * 1000)).valueOf() / 1000.0; cy.task('generateJWT', { expirationTime }).then((expiredJwt) => { store.dispatch({ type: SET_TOKEN, diff --git a/cypress/integration/collections_spec.js b/cypress/integration/collections_spec.js index 6906ddfa3..625bb0286 100644 --- a/cypress/integration/collections_spec.js +++ b/cypress/integration/collections_spec.js @@ -23,7 +23,7 @@ describe('Dashboard Collections Page', () => { beforeEach(() => { cy.login(); - cy.visit('/'); + cy.visit('/?new_session=true'); cy.server(); cy.route('POST', '/collections').as('postCollection'); cy.route('GET', '/collections?limit=*').as('getCollections'); @@ -38,7 +38,7 @@ describe('Dashboard Collections Page', () => { it('should display a link to view collections', () => { cy.contains('nav li a', 'Collections').as('collections'); - cy.get('@collections').should('have.attr', 'href', '/collections'); + cy.get('@collections').should('have.attr', 'href').and('match', /\/collections\?startDateTime=/); cy.get('@collections').click(); cy.wait('@getActiveCollections'); @@ -55,7 +55,7 @@ describe('Dashboard Collections Page', () => { it('should only display collections with active granules when time filter is applied', () => { cy.contains('nav li a', 'Collections').as('collections'); - cy.get('@collections').should('have.attr', 'href', '/collections'); + cy.get('@collections').should('have.attr', 'href').and('match', /\/collections\?startDateTime=/); cy.get('@collections').click(); cy.wait('@getActiveCollections'); @@ -159,6 +159,10 @@ describe('Dashboard Collections Page', () => { // collections with which to populate the dropdown on the collection // details page. cy.visit('/collections'); + // TODO [DOP, 2020-05-14] Workaround until CUMULUS-1996 is resolved + // Stop timer so that it does not dispatch listCollections and change + // the order of items in the list after date is cleared + cy.get('.form__element__updateToggle .form__element__clickable').click(); cy.clearStartDateTime(); cy.wait('@getCollections'); cy.get('.table .tbody .tr').should('have.length', 5); @@ -451,8 +455,8 @@ describe('Dashboard Collections Page', () => { cy.get(`[data-value="${granuleIds[1]}"] > .td >input[type="checkbox"]`).click(); cy.get('.list-actions').contains('Reingest').click(); cy.get('.button--submit').click(); - cy.get('.modal-content > .modal-title').should('contain.text', 'Error'); - cy.get('.error').should('contain.text', 'Oopsie'); + cy.get('.modal-content .modal-body .alert').should('contain.text', 'Error'); + cy.get('.Collapsible__contentInner').should('contain.text', 'Oopsie'); cy.get('.button--cancel').click(); cy.url().should('match', /\/granules$/); cy.get('.heading--large').should('have.text', 'Granule Overview'); @@ -476,7 +480,7 @@ describe('Dashboard Collections Page', () => { cy.get(`[data-value="${granuleIds[1]}"] > .td >input[type="checkbox"]`).click(); cy.get('.list-actions').contains('Reingest').click(); cy.get('.button--submit').click(); - cy.get('.modal-content > .modal-title').should('contain.text', 'Complete'); + cy.get('.modal-content .modal-body .alert').should('contain.text', 'Success'); cy.get('.modal-content').within(() => { cy.get('.button__goto').click(); }); @@ -486,5 +490,34 @@ describe('Dashboard Collections Page', () => { // Ensure we have closed the modal. cy.get('.modal-content').should('not.be.visible'); }); + + it('Should display Granule Metrics that match the datepicker selection', () => { + cy.visit('/collections/collection/MOD09GQ/006'); + + cy.get('[data-cy=endDateTime]').within(() => { + cy.get('input[name=month]').click().type(3); + cy.get('input[name=day]').click().type(17); + cy.get('input[name=year]').click().type(2009); + cy.get('input[name=hour12]').click().type(3); + cy.get('input[name=minute]').click().type(37); + cy.get('input[name=minute]').should('have.value', '37'); + cy.get('select[name=amPm]').select('PM'); + }); + cy.get('[data-cy=overview-num]').within(() => { + cy.get('li') + .first().should('contain', '--').and('contain', 'Completed') + .next().should('contain', '--').and('contain', 'Failed') + .next().should('contain', '--').and('contain', 'Running'); + }); + + cy.get('[data-cy=endDateTime] > .react-datetime-picker > .react-datetime-picker__wrapper > .react-datetime-picker__clear-button > .react-datetime-picker__clear-button__icon').click(); + + cy.get('[data-cy=overview-num]').within(() => { + cy.get('li') + .first().should('contain', 7).and('contain', 'Completed') + .next().should('contain', 2).and('contain', 'Failed') + .next().should('contain', 2).and('contain', 'Running'); + }); + }); }); }); diff --git a/cypress/integration/granules_spec.js b/cypress/integration/granules_spec.js index 66950ab52..8093587f6 100644 --- a/cypress/integration/granules_spec.js +++ b/cypress/integration/granules_spec.js @@ -39,9 +39,9 @@ describe('Dashboard Granules Page', () => { cy.get('@granulesListFixture').its('results') .each((granule) => { // Wait for this granule to appear before proceeding. - cy.contains(granule['granuleId']); - cy.get(`[data-value="${granule['granuleId']}"]`).children().as('columns'); - cy.get('@columns').should('have.length', 8); + cy.contains(granule.granuleId); + cy.get(`[data-value="${granule.granuleId}"]`).children().as('columns'); + cy.get('@columns').should('have.length', 9); // Granule Status Column is correct cy.get('@columns').eq(1).invoke('text') @@ -82,16 +82,21 @@ describe('Dashboard Granules Page', () => { .should('have.attr', 'href') .and('be.eq', `/collections/collection/${granule.collectionId.replace('___', '/')}`); - // Execution column has link to the detailed execution page + // has link to provider cy.get('@columns').eq(5).children('a') + .should('have.attr', 'href') + .and('be.eq', `/providers/provider/${granule.provider}`); + + // Execution column has link to the detailed execution page + cy.get('@columns').eq(6).children('a') .should('have.attr', 'href') .and('be.eq', `/executions/execution/${granule.execution.split('/').pop()}`); // Duration column - cy.get('@columns').eq(6).invoke('text') + cy.get('@columns').eq(7).invoke('text') .should('be.eq', `${Number(granule.duration.toFixed(2))}s`); // Updated column - cy.get('@columns').eq(7).invoke('text') + cy.get('@columns').eq(8).invoke('text') .should('match', /.+ago$/); }); @@ -104,8 +109,7 @@ describe('Dashboard Granules Page', () => { cy.contains('.heading--xlarge', 'Granules'); - cy.contains('a', 'Download Granule List') - .should('have.attr', 'href').should('include', 'blob:http://'); + cy.contains('a', 'Download Granule List'); }); it('Should update dropdown with label when visiting bookmarkable URL', () => { @@ -193,7 +197,7 @@ describe('Dashboard Granules Page', () => { cy.get(`[data-value="${granuleId}"] > .td >input[type="checkbox"]`).click(); cy.get('.list-actions').contains('Reingest').click(); cy.get('.button--submit').click(); - cy.get('.modal-content > .modal-title').should('contain.text', 'Complete'); + cy.get('.modal-content .modal-body .alert').should('contain.text', 'Success'); cy.get('.button__goto').click(); cy.url().should('include', `granules/granule/${granuleId}`); cy.get('.heading--large').should('have.text', granuleId); @@ -216,7 +220,7 @@ describe('Dashboard Granules Page', () => { cy.get(`[data-value="${granuleIds[1]}"] > .td >input[type="checkbox"]`).click(); cy.get('.list-actions').contains('Reingest').click(); cy.get('.button--submit').click(); - cy.get('.modal-content > .modal-title').should('contain.text', 'Complete'); + cy.get('.modal-content .modal-body .alert').should('contain.text', 'Success'); cy.get('.button__goto').click(); cy.url().should('include', 'granules/processing'); cy.get('.heading--large').should('have.text', 'Running Granules 2'); @@ -239,8 +243,8 @@ describe('Dashboard Granules Page', () => { cy.get(`[data-value="${granuleIds[1]}"] > .td >input[type="checkbox"]`).click(); cy.get('.list-actions').contains('Reingest').click(); cy.get('.button--submit').click(); - cy.get('.modal-content > .modal-title').should('contain.text', 'Error'); - cy.get('.error').should('contain.text', 'Oopsie'); + cy.get('.modal-content .modal-body .alert').should('contain.text', 'Error'); + cy.get('.Collapsible__contentInner').should('contain.text', 'Oopsie'); cy.get('.button--cancel').click(); cy.url().should('match', /\/granules$/); cy.get('.heading--large').should('have.text', 'Granule Overview'); diff --git a/cypress/integration/main_page_spec.js b/cypress/integration/main_page_spec.js index 37791c832..0672653ad 100644 --- a/cypress/integration/main_page_spec.js +++ b/cypress/integration/main_page_spec.js @@ -54,7 +54,6 @@ describe('Dashboard Home Page', () => { beforeEach(() => { cy.login(); cy.server(); - cy.route('POST', 'http://example.com/_search/', 'fixture:elasticsearch.json'); cy.visit('/'); }); @@ -215,6 +214,8 @@ describe('Dashboard Home Page', () => { // Cypress only allows one stub per url. We make multiple POST requests to the same // elasticsearch endpoint. The fixture here returns a combined response of all the // responses for one url, effectively stubbing our elasticsearch searches. + cy.route('POST', 'http://example.com/_search/', 'fixture:elasticsearch.json'); + cy.get('.overview-num__wrapper-home > ul#distributionErrors > :nth-child(5)').contains('0'); cy.get('.overview-num__wrapper-home > ul#distributionErrors > :nth-child(4)').contains('2'); cy.get('.overview-num__wrapper-home > ul#distributionErrors > :nth-child(3)').contains('4'); @@ -281,5 +282,83 @@ describe('Dashboard Home Page', () => { shouldHaveDeletedToken(); }); + + it('adds the default datepicker options to the URL', () => { + const now = Date.UTC(2009, 0, 5, 13, 35, 3); + cy.clock(now); + + cy.visit('/?new_session=true'); + cy.url().should('include', 'startDateTime=20090104133500'); + }); + + it('should not add the initial Datepicker state to the URL once cleared', () => { + const now = Date.UTC(2009, 0, 5, 13, 35, 3); + cy.clock(now); + + cy.visit('/?new_session=true'); + cy.url().should('include', 'startDateTime=20090104133500'); + + cy.get('[data-cy=datetime-clear]').click(); + cy.contains('nav li a', 'Collections').as('collections'); + cy.get('@collections').click(); + cy.get('.logo').click(); + + cy.url().should('not.include', 'startDateTime='); + }); + + it('should update the Datepicker with the params in the URL', () => { + cy.visit('/granules/?startDateTime=20081229133500&endDateTime=20090105133500'); + + cy.get('[data-cy=endDateTime]').within(() => { + cy.get('.react-datetime-picker__inputGroup__year').should('have.value', '2009'); + cy.get('.react-datetime-picker__inputGroup__month').should('have.value', '1'); + cy.get('.react-datetime-picker__inputGroup__day').should('have.value', '5'); + cy.get('.react-datetime-picker__inputGroup__hour').should('have.value', '1'); + cy.get('.react-datetime-picker__inputGroup__minute').should('have.value', '35'); + }); + + cy.get('[data-cy=startDateTime]').within(() => { + cy.get('.react-datetime-picker__inputGroup__year').should('have.value', '2008'); + cy.get('.react-datetime-picker__inputGroup__month').should('have.value', '12'); + cy.get('.react-datetime-picker__inputGroup__day').should('have.value', '29'); + cy.get('.react-datetime-picker__inputGroup__hour').should('have.value', '1'); + cy.get('.react-datetime-picker__inputGroup__minute').should('have.value', '35'); + }); + }); + + it('should not display the Distribution stats when no data is provided', () => { + cy.get('ul#distributionErrors').should('not.exist'); + cy.get('ul#distributionSuccesses').should('not.exist'); + }); + + describe('The Timer', () => { + beforeEach(() => { + cy.visit('/'); + }); + + it('begins in the off state', () => { + cy.get('[data-cy=toggleTimer]').contains('Start'); + cy.get('[data-cy=startStopLabel]').contains('Update'); + }); + it('retains its state during navigation.', () => { + cy.get('[data-cy=toggleTimer]').contains('Start'); + cy.get('[data-cy=toggleTimer]').click(); + cy.get('[data-cy=toggleTimer]').contains('Stop'); + cy.get('[data-cy=startStopLabel]').contains('Next update in:'); + + cy.get('nav').contains('Granules').click(); + + cy.get('[data-cy=toggleTimer]').contains('Stop'); + cy.get('[data-cy=startStopLabel]').contains('Next update in:'); + + cy.get('[data-cy=toggleTimer]').click(); + cy.get('[data-cy=startStopLabel]').contains('Update'); + cy.get('[data-cy=toggleTimer]').contains('Start'); + + cy.get('.logo > a').click(); + cy.get('[data-cy=startStopLabel]').contains('Update'); + cy.get('[data-cy=toggleTimer]').contains('Start'); + }); + }); }); }); diff --git a/cypress/integration/reconciliation_spec.js b/cypress/integration/reconciliation_spec.js index 90b92e3b1..47a7ff526 100644 --- a/cypress/integration/reconciliation_spec.js +++ b/cypress/integration/reconciliation_spec.js @@ -31,157 +31,81 @@ describe('Dashboard Reconciliation Reports Page', () => { it('displays a list of reconciliation reports', () => { cy.visit('/reconciliation-reports'); + cy.contains('.table .thead .th', 'Name'); + cy.contains('.table .thead .th', 'Report Type'); + cy.contains('.table .thead .th', 'Status'); + cy.contains('.table .thead .th', 'Date Generated'); + cy.contains('.table .thead .th', 'Download Report'); + cy.contains('.table .thead .th', 'Delete Report'); cy.get('.table .tbody .tr').should('have.length', 2); - cy.contains('.table .tbody .tr a', 'report-2020-01-14T20:25:29.026Z.json') - .should('have.attr', 'href', '/reconciliation-reports/report/report-2020-01-14T20:25:29.026Z.json'); - cy.contains('.table .tbody .tr a', 'report-2020-01-14T20:52:38.781Z.json') - .should('have.attr', 'href', '/reconciliation-reports/report/report-2020-01-14T20:52:38.781Z.json'); + cy.get('[data-value="inventoryReport-20200114T202529026"] > .table__main-asset > a').should('have.attr', 'href', '/reconciliation-reports/report/inventoryReport-20200114T202529026'); + cy.get('[data-value="inventoryReport-20200114T205238781"] > .table__main-asset > a').should('have.attr', 'href', '/reconciliation-reports/report/inventoryReport-20200114T205238781'); }); it('displays a link to an individual report', () => { cy.visit('/reconciliation-reports'); - cy.contains('.table .tbody .tr a', 'report-2020-01-14T20:52:38.781Z.json') - .should('have.attr', 'href', '/reconciliation-reports/report/report-2020-01-14T20:52:38.781Z.json') + cy.contains('.table .tbody .tr a', 'inventoryReport-20200114T205238781') + .should('have.attr', 'href', '/reconciliation-reports/report/inventoryReport-20200114T205238781') .click(); - cy.contains('.heading--large', 'report-2020-01-14T20:52:38.781Z.json'); - - /** Summary **/ - - cy.contains('dl dd', '2020-01-14T20:52:38.781Z'); - cy.contains('dl dd', 'SUCCESS'); - cy.contains('dl dt', 'Files in DynamoDB and S3') - .next('dd') - .contains('129'); - cy.contains('dl dt', 'Granules in Cumulus and CMR') - .next('dd') - .contains('7'); - cy.contains('dl dt', 'Granule files in Cumulus and CMR') - .next('dd') - .contains('4'); - cy.contains('dl dt', 'Collections in Cumulus and CMR') - .next('dd') - .contains('1'); - - /** Files **/ - - cy.contains('h3', 'Files only in DynamoDB (35)') - .next() - .find('.table .tbody') - .as('dynamoTable'); - cy.get('@dynamoTable').find('.tr').should('have.length', 35); - cy.get('@dynamoTable') - .children('.tr') - .first() - .within(() => { - cy.contains('MOD09GQ.A9218123.TJbx_C.006.7667598863143'); - cy.contains('MOD09GQ.A9218123.TJbx_C.006.7667598863143.hdf.met'); - cy.contains('mhs3-private'); - cy.contains('a', 'Link') - .should('have.attr', 'href', 's3://mhs3-private/MOD09GQ___006/MOD/MOD09GQ.A9218123.TJbx_C.006.7667598863143.hdf.met'); - }); - - cy.contains('h3', 'Files only in S3 (216)') - .next() - .find('.table .tbody') - .as('s3Table'); - cy.get('@s3Table').find('.tr').should('have.length', 216); - cy.get('@s3Table') - .children('.tr') - .first() - .within(() => { - cy.contains('MOD09GQ.A3119781.haeynr.006.4074740546315.hdf.met'); - cy.contains('mhs3-private'); - cy.contains('a', 'Link') - .should('have.attr', 'href', - 's3://mhs3-private/MOD09GQ___006/MOD/mhs3-IngestGranuleDuplicateHandling-1573838955288/MOD09GQ.A3119781.haeynr.006.4074740546315.hdf.met'); - }); - - /** Collections **/ - - cy.contains('h3', 'Collections only in Cumulus (13)') - .next() - .find('.table .tbody') - .as('cumulusCollectionsTable'); - cy.get('@cumulusCollectionsTable').find('.tr').should('have.length', 13); - cy.get('@cumulusCollectionsTable') - .within(() => { - cy.contains('L2_HR_PIXC_test-mhs3-KinesisTestError-1574717133091___000'); - cy.contains('L2_HR_PIXC_test-mhs3-KinesisTestError-1575912896780___000'); - cy.contains('MOD09GQ_test-mhs3-IngestGranuleSuccess-1576108764706___006'); - }); - - cy.contains('h3', 'Collections only in CMR (25)') - .next() - .find('.table .tbody') - .as('cmrCollectionsTable'); - cy.get('@cmrCollectionsTable').find('.tr').should('have.length', 25); - cy.get('@cmrCollectionsTable') - .within(() => { - cy.contains('A2_RainOcn_NRT___0'); - cy.contains('A2_SI12_NRT___0'); - cy.contains('hs3wwlln___1'); - }); - - /** Granules **/ - - cy.contains('h3', 'Granules only in Cumulus (7)') - .next() - .find('.table .tbody') - .as('cumulusGranulesTable'); - cy.get('@cumulusGranulesTable').find('.tr').should('have.length', 7); - cy.get('@cumulusGranulesTable') - .within(() => { - cy.contains('MOD14A1.A4135026.ekHe8x.006.3355759967228'); - cy.contains('MOD14A1.A8959582.q932t8.006.4564896025574'); - }); - - cy.contains('h3', 'Granules only in CMR (365)') - .next() - .find('.table .tbody') - .as('cmrGranulesTable'); - cy.get('@cmrGranulesTable').find('.tr').should('have.length', 365); - cy.get('@cmrGranulesTable') - .within(() => { - cy.contains('MOD14A1.A0031922.9lenyG.006.4681412227733'); - cy.contains('MOD14A1.A0499025.Bl5XRj.006.7345541650084'); - cy.contains('MOD14A1.A9990408.N4T1Mn.006.9738328377056'); - }); - - /** Granule files **/ - - cy.contains('h3', 'Granule files only in Cumulus (1)') - .next() - .find('.table .tbody') - .as('cumulusGranulesFilesTable'); - cy.get('@cumulusGranulesFilesTable').find('.tr').should('have.length', 1); - cy.get('@cumulusGranulesFilesTable') - .children('.tr') - .first() - .within(() => { - cy.contains('granule.001.1234'); - cy.contains('granule.001.1234.jpg'); - cy.contains('some-bucket'); - cy.contains('a', 'Link') - .should('have.attr', 'href', 's3://some-bucket/granule__001/granule.001.1234.jpg'); - }); - - cy.contains('h3', 'Granule files only in CMR (1)') - .next() - .find('.table .tbody') - .as('cmrGranulesFilesTable'); - cy.get('@cmrGranulesFilesTable').find('.tr').should('have.length', 1); - cy.get('@cmrGranulesFilesTable') - .children('.tr') - .first() - .within(() => { - cy.contains('granule.002.5678'); - cy.contains('granule.002.5678.jpg'); - cy.contains('some-bucket'); - cy.contains('a', 'Link') - .should('have.attr', 'href', 's3://some-bucket/granule__002/granule.002.5678.jpg'); - }); + cy.contains('.heading--large', 'inventoryReport-20200114T205238781'); + + /** Table Cards **/ + + const cards = [ + { + title: 'DynamoDB', + count: 35, + firstColumn: 'GranuleId' + }, + { + title: 'S3', + count: 16, + firstColumn: 'Filename' + }, + { + title: 'Cumulus Collections', + count: 13, + firstColumn: 'Collection name' + }, + { + title: 'CMR Collections', + count: 25, + firstColumn: 'Collection name' + }, + { + title: 'Cumulus Granules', + count: 7, + firstColumn: 'Granule ID' + }, + { + title: 'CMR Granules', + count: 365, + firstColumn: 'Granule ID' + }, + { + title: 'Cumulus Only Granules', + count: 1, + firstColumn: 'GranuleId' + }, + { + title: 'CMR Only Granules', + count: 1, + firstColumn: 'GranuleId' + } + ]; + + cy.get('.card').each(($card, index, $cards) => { + const card = cy.wrap($card); + card.click(); + card.should('have.class', 'active'); + card.get('.card-header').contains(cards[index].title); + card.get('.card-title').contains(cards[index].count); + + // verify correct table is displayed on click + cy.get('.table .th').first().contains(cards[index].firstColumn); + }); }); }); }); diff --git a/cypress/integration/rules_spec.js b/cypress/integration/rules_spec.js index efa088420..79065b144 100644 --- a/cypress/integration/rules_spec.js +++ b/cypress/integration/rules_spec.js @@ -14,11 +14,11 @@ describe('Rules page', () => { before(() => { cy.visit('/'); + cy.task('resetState'); }); beforeEach(() => { cy.login(); - cy.task('resetState'); }); it('should display a link to view rules', () => { @@ -164,6 +164,7 @@ describe('Rules page', () => { cy.contains('a', 'Back to Rules').click(); cy.contains('.table .tbody .tr a', ruleName) .should('have.attr', 'href', `/rules/rule/${ruleName}`); + cy.task('resetState'); }); it('copying a rule should add it to the list', () => { @@ -202,6 +203,7 @@ describe('Rules page', () => { cy.contains('a', 'Back to Rules').click(); cy.contains('.table .tbody .tr a', newName) .should('have.attr', 'href', `/rules/rule/${newName}`); + cy.task('resetState'); }); it('editing a rule and returning to the rules page should show the new changes', () => { @@ -262,6 +264,7 @@ describe('Rules page', () => { // cy.wait('@getCollection'); cy.contains('.heading--xlarge', 'Rules'); cy.contains('.heading--large', `${testRuleName}`); + cy.task('resetState'); }); it('deleting a rule should remove it from the list', () => { @@ -279,6 +282,33 @@ describe('Rules page', () => { .click(); cy.contains('.table .tr a', testRuleName) .should('not.exist'); + cy.task('resetState'); + }); + + it('Should trigger workflow when a rule is rerun', () => { + cy.server(); + cy.route('PUT', '/rules/MOD09GK_TEST_kinesisRule', 'fixtures:rule-success.json').as('putRule'); + cy.visit('/rules/rule/MOD09GK_TEST_kinesisRule'); + cy.get('.dropdown__options__btn').click(); + cy.get('.dropdown__menu').contains('Rerun').click(); + cy.get('.button--submit').click(); + cy.get('.modal-content').should('not.be', 'visible'); + }); + + it('Should display error when a rule fails to rerun.', () => { + cy.server(); + cy.route({ + method: 'PUT', + url: '/rules/MOD09GK_TEST_kinesisRule', + status: 503, + response: 'fixtures:rule-error.json' + }).as('putRule'); + cy.visit('/rules/rule/MOD09GK_TEST_kinesisRule'); + cy.get('.dropdown__options__btn').click(); + cy.get('.dropdown__menu').contains('Rerun').click(); + cy.get('.button--submit').click(); + cy.get('.modal-content').should('not.be', 'visible'); + cy.contains('.error__report', 'Error'); }); }); }); diff --git a/cypress/plugins/seedEverything.js b/cypress/plugins/seedEverything.js index 1620fc574..90f54066b 100644 --- a/cypress/plugins/seedEverything.js +++ b/cypress/plugins/seedEverything.js @@ -1,51 +1,90 @@ const { testUtils } = require('@cumulus/api'); +const { promiseS3Upload } = require('@cumulus/aws-client/S3'); +const fs = require('fs'); const serveUtils = require('@cumulus/api/bin/serveUtils'); const { eraseDataStack } = require('@cumulus/api/bin/serve'); -const { localUserName } = require('@cumulus/api/bin/local-test-defaults'); +const { + localUserName, + localStackName, + localSystemBucket, +} = require('@cumulus/api/bin/local-test-defaults'); const collections = require('../fixtures/seeds/collectionsFixture.json'); const executions = require('../fixtures/seeds/executionsFixture.json'); const granules = require('../fixtures/seeds/granulesFixture.json'); const providers = require('../fixtures/seeds/providersFixture.json'); const rules = require('../fixtures/seeds/rulesFixture.json'); +const reconciliationReports = require('../fixtures/seeds/reconciliationReportFixture.json'); +const reconciliationReportDir = `${__dirname}/../fixtures/seeds/reconciliation-reports`; -function resetIt () { +function resetIt() { return Promise.all([ eraseDataStack(), - testUtils.setAuthorizedOAuthUsers([localUserName]) + testUtils.setAuthorizedOAuthUsers([localUserName]), ]); } -function seedProviders () { +function seedProviders() { return serveUtils.addProviders(providers.results); } -function seedCollections () { +function seedCollections() { return serveUtils.addCollections(collections.results); } -function seedGranules () { +function seedGranules() { return serveUtils.addGranules(granules.results); } -function seedExecutions () { +function seedExecutions() { return serveUtils.addExecutions(executions.results); } -function seedRules () { +function seedReconciliationReports() { + return serveUtils.addReconciliationReports(reconciliationReports.results); +} + +function seedRules() { return serveUtils.addRules(rules.results); } -function seedEverything () { - return resetIt() - .then(seedRules) - .then(seedCollections) - .then(seedGranules) - .then(seedExecutions) - .then(seedProviders); +function uploadReconciliationReportFiles() { + const reconcileReportList = fs + .readdirSync(reconciliationReportDir) + .map((f) => ({ + filename: f, + data: JSON.parse( + fs.readFileSync(`${reconciliationReportDir}/${f}`).toString() + ), + })); + + return Promise.all( + reconcileReportList.map((obj) => { + const { filename, data } = obj; + return promiseS3Upload({ + Bucket: `${localSystemBucket}`, + Key: `${localStackName}/reconciliation-reports/${filename}`, + Body: JSON.stringify(data), + }); + }) + ); +} + +function seedEverything() { + return Promise.all([ + resetIt() + .then(seedRules) + .then(seedCollections) + .then(seedGranules) + .then(seedExecutions) + .then(seedProviders) + .then(seedReconciliationReports), + uploadReconciliationReportFiles(), + ]); } module.exports = { + resetIt, seedEverything, - resetIt + uploadReconciliationReportFiles, }; diff --git a/package-lock.json b/package-lock.json index cb3a475d7..936f3612e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "cumulus-dashboard", - "version": "1.7.2", + "version": "1.9.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -1169,23 +1169,23 @@ "dev": true }, "@cumulus/api": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/@cumulus/api/-/api-1.22.1.tgz", - "integrity": "sha512-FjWzMf40+MlxjaSEB29HiVBXkEN0oV9s5q+t44h3h6Phjp6Uh+Tcv4AWsV13yDKLYmcfpsJuyuiRBfgU8Egpmg==", - "dev": true, - "requires": { - "@cumulus/aws-client": "1.22.1", - "@cumulus/cmr-client": "1.22.1", - "@cumulus/cmrjs": "1.22.1", - "@cumulus/collection-config-store": "1.22.1", - "@cumulus/common": "1.22.1", - "@cumulus/errors": "1.22.1", - "@cumulus/ingest": "1.22.1", - "@cumulus/launchpad-auth": "1.22.1", - "@cumulus/logger": "1.22.1", - "@cumulus/message": "1.22.1", - "@cumulus/pvl": "1.22.1", - "@cumulus/sftp-client": "1.22.1", + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/@cumulus/api/-/api-1.23.2.tgz", + "integrity": "sha512-J7Lw30ivirZoRAONlADw1NdCw4E1mwcHiS340oBmLmD2EKfiCBLRUaGQemIMwaSu7Kgs9+ffeEd3hN+jdy/dMw==", + "dev": true, + "requires": { + "@cumulus/aws-client": "1.23.2", + "@cumulus/cmr-client": "1.23.2", + "@cumulus/cmrjs": "1.23.2", + "@cumulus/collection-config-store": "1.23.2", + "@cumulus/common": "1.23.2", + "@cumulus/errors": "1.23.2", + "@cumulus/ingest": "1.23.2", + "@cumulus/launchpad-auth": "1.23.2", + "@cumulus/logger": "1.23.2", + "@cumulus/message": "1.23.2", + "@cumulus/pvl": "1.23.2", + "@cumulus/sftp-client": "1.23.2", "@elastic/elasticsearch": "^5.6.20", "@mapbox/dyno": "^1.4.2", "aggregate-error": "^3.0.1", @@ -1225,17 +1225,61 @@ "xml2js": "^0.4.22" }, "dependencies": { + "@cumulus/aws-client": { + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/@cumulus/aws-client/-/aws-client-1.23.2.tgz", + "integrity": "sha512-Nv2FTJzwKBbY+n085+aH1QeLQjlsrEz7pO+6psiWBpY0Q9fxXJNqhQscxRMC2Gd3Raf+fOtqua0u7/B11UVMIw==", + "dev": true, + "requires": { + "@cumulus/checksum": "1.23.2", + "@cumulus/errors": "1.23.2", + "@cumulus/logger": "1.23.2", + "aws-sdk": "^2.585.0", + "jsonpath-plus": "^1.1.0", + "lodash": "~4.17.15", + "p-map": "^1.2.0", + "p-retry": "^4.2.0", + "pump": "^3.0.0" + }, + "dependencies": { + "p-map": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz", + "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==", + "dev": true + }, + "p-retry": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.2.0.tgz", + "integrity": "sha512-jPH38/MRh263KKcq0wBNOGFJbm+U6784RilTmHjB/HM9kH9V8WlCpVUcdOmip9cjXOh6MxZ5yk1z2SjDUJfWmA==", + "dev": true, + "requires": { + "@types/retry": "^0.12.0", + "retry": "^0.12.0" + } + } + } + }, + "@cumulus/checksum": { + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/@cumulus/checksum/-/checksum-1.23.2.tgz", + "integrity": "sha512-3HqOW4CsZhTMQmHw/Q/JJ+K/f9JQpb/2mw6Cn4RVXGLzg87825Ph2tYE+h4SQFIPc+eHSgVHcSDUUDLDBhwftA==", + "dev": true, + "requires": { + "cksum": "^1.3.0" + } + }, "@cumulus/cmrjs": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/@cumulus/cmrjs/-/cmrjs-1.22.1.tgz", - "integrity": "sha512-PCXDN9z+APCLDWYOxYpSJ8OBoscxGPHccjaLfkDnNYA5sz2zdHyeESVb/QfXPIDywAbGZ8QJUJ+INdhqpD0dMQ==", + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/@cumulus/cmrjs/-/cmrjs-1.23.2.tgz", + "integrity": "sha512-YhG06Kzg5ghJzDeUC0nXDTPkkV12owIt/17kvcS66fLYEJxoKWvf8SGOKGljbYuw6paAAkd7NPo4oWOMKYIlrA==", "dev": true, "requires": { - "@cumulus/aws-client": "1.22.1", - "@cumulus/cmr-client": "1.22.1", - "@cumulus/common": "1.22.1", - "@cumulus/errors": "1.22.1", - "@cumulus/launchpad-auth": "1.22.1", + "@cumulus/aws-client": "1.23.2", + "@cumulus/cmr-client": "1.23.2", + "@cumulus/common": "1.23.2", + "@cumulus/errors": "1.23.2", + "@cumulus/launchpad-auth": "1.23.2", "got": "^8.3.0", "js2xmlparser": "^4.0.0", "lodash": "^4.17.15", @@ -1252,6 +1296,101 @@ } } }, + "@cumulus/common": { + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/@cumulus/common/-/common-1.23.2.tgz", + "integrity": "sha512-8G/bS8sfiJZOr6fKPGuBH3w+iyIHtKolcESjwz2BUHHgkjW4MaL/YHfeWjqIu/aFmEoJmgaDTgN0VJSLzVOHGA==", + "dev": true, + "requires": { + "@cumulus/errors": "1.23.2", + "@cumulus/logger": "1.23.2", + "ajv": "^5.2.2", + "aws-sdk": "^2.585.0", + "follow-redirects": "^1.2.4", + "fs-extra": "^5.0.0", + "got": "^9.2.1", + "js-yaml": "^3.11.0", + "jsonpath-plus": "^3.0.0", + "lodash": "^4.17.15", + "mime-types": "^2.1.22", + "node-forge": "^0.7.1", + "p-limit": "^2.0.0", + "p-map": "^1.2.0", + "p-retry": "^4.2.0", + "parseurl": "^1.3.3", + "randexp": "^0.4.9", + "ssh2": "^0.8.7", + "url-join": "^4.0.0", + "uuid": "^3.2.1" + }, + "dependencies": { + "got": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "dev": true, + "requires": { + "@sindresorhus/is": "^0.14.0", + "@szmarczak/http-timer": "^1.1.2", + "cacheable-request": "^6.0.0", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^4.1.0", + "lowercase-keys": "^1.0.1", + "mimic-response": "^1.0.1", + "p-cancelable": "^1.0.0", + "to-readable-stream": "^1.0.0", + "url-parse-lax": "^3.0.0" + } + }, + "jsonpath-plus": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-3.0.0.tgz", + "integrity": "sha512-WQwgWEBgn+SJU1tlDa/GiY5/ngRpa9yrSj8n4BYPHcwoxTDaMEaYCHMOn42hIHHDd3CrUoRr3+HpsK0hCKoxzA==", + "dev": true + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-map": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz", + "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==", + "dev": true + }, + "p-retry": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.2.0.tgz", + "integrity": "sha512-jPH38/MRh263KKcq0wBNOGFJbm+U6784RilTmHjB/HM9kH9V8WlCpVUcdOmip9cjXOh6MxZ5yk1z2SjDUJfWmA==", + "dev": true, + "requires": { + "@types/retry": "^0.12.0", + "retry": "^0.12.0" + } + } + } + }, + "@cumulus/errors": { + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/@cumulus/errors/-/errors-1.23.2.tgz", + "integrity": "sha512-bkVB0f9YxvVPmB4zYtjRUFxWIA3D+T8ZPxH2GuXAv+VvNuptu3Rukl4oc232lpB38TgGSH1mXkqsgUwCe4QWAQ==", + "dev": true + }, + "@cumulus/logger": { + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/@cumulus/logger/-/logger-1.23.2.tgz", + "integrity": "sha512-wVLukDDqC4E4A6Bd1SHlEiY/lC3/zrOjQDHtJ/9mXQwG9hJ4k+aWI6gVWuNSZXBXBzcvqdpg5+mPvDG60R686Q==", + "dev": true, + "requires": { + "lodash.iserror": "^3.1.1" + } + }, "json2csv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/json2csv/-/json2csv-4.5.4.tgz", @@ -1270,13 +1409,15 @@ "dev": true, "requires": { "p-try": "^1.0.0" + }, + "dependencies": { + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + } } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true } } }, @@ -1325,18 +1466,60 @@ } }, "@cumulus/cmr-client": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/@cumulus/cmr-client/-/cmr-client-1.22.1.tgz", - "integrity": "sha512-A/UusEsnj/Zinp4bLiGhlOaDivI3vHsIrHjC/aJkSMRXtB+0gJ75WXKK7v2AYrbxXsJfX5Y+o241p1OEv3JENQ==", + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/@cumulus/cmr-client/-/cmr-client-1.23.2.tgz", + "integrity": "sha512-Aq1FBCOlqiQg33PZVgKEIBeoY0npG+JDl84G1UJaAhst4s2PLfwz3snbSUGV9PNk+fso9U2+oT5gYElv+ogNGQ==", "dev": true, "requires": { - "@cumulus/logger": "1.22.1", + "@cumulus/aws-client": "1.23.2", + "@cumulus/logger": "1.23.2", "got": "^9.6.0", "lodash": "^4.17.15", "public-ip": "^3.0.0", "xml2js": "^0.4.19" }, "dependencies": { + "@cumulus/aws-client": { + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/@cumulus/aws-client/-/aws-client-1.23.2.tgz", + "integrity": "sha512-Nv2FTJzwKBbY+n085+aH1QeLQjlsrEz7pO+6psiWBpY0Q9fxXJNqhQscxRMC2Gd3Raf+fOtqua0u7/B11UVMIw==", + "dev": true, + "requires": { + "@cumulus/checksum": "1.23.2", + "@cumulus/errors": "1.23.2", + "@cumulus/logger": "1.23.2", + "aws-sdk": "^2.585.0", + "jsonpath-plus": "^1.1.0", + "lodash": "~4.17.15", + "p-map": "^1.2.0", + "p-retry": "^4.2.0", + "pump": "^3.0.0" + } + }, + "@cumulus/checksum": { + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/@cumulus/checksum/-/checksum-1.23.2.tgz", + "integrity": "sha512-3HqOW4CsZhTMQmHw/Q/JJ+K/f9JQpb/2mw6Cn4RVXGLzg87825Ph2tYE+h4SQFIPc+eHSgVHcSDUUDLDBhwftA==", + "dev": true, + "requires": { + "cksum": "^1.3.0" + } + }, + "@cumulus/errors": { + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/@cumulus/errors/-/errors-1.23.2.tgz", + "integrity": "sha512-bkVB0f9YxvVPmB4zYtjRUFxWIA3D+T8ZPxH2GuXAv+VvNuptu3Rukl4oc232lpB38TgGSH1mXkqsgUwCe4QWAQ==", + "dev": true + }, + "@cumulus/logger": { + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/@cumulus/logger/-/logger-1.23.2.tgz", + "integrity": "sha512-wVLukDDqC4E4A6Bd1SHlEiY/lC3/zrOjQDHtJ/9mXQwG9hJ4k+aWI6gVWuNSZXBXBzcvqdpg5+mPvDG60R686Q==", + "dev": true, + "requires": { + "lodash.iserror": "^3.1.1" + } + }, "got": { "version": "9.6.0", "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", @@ -1355,6 +1538,22 @@ "to-readable-stream": "^1.0.0", "url-parse-lax": "^3.0.0" } + }, + "p-map": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz", + "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==", + "dev": true + }, + "p-retry": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.2.0.tgz", + "integrity": "sha512-jPH38/MRh263KKcq0wBNOGFJbm+U6784RilTmHjB/HM9kH9V8WlCpVUcdOmip9cjXOh6MxZ5yk1z2SjDUJfWmA==", + "dev": true, + "requires": { + "@types/retry": "^0.12.0", + "retry": "^0.12.0" + } } } }, @@ -1401,13 +1600,72 @@ } }, "@cumulus/collection-config-store": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/@cumulus/collection-config-store/-/collection-config-store-1.22.1.tgz", - "integrity": "sha512-Td6lo6rmmJRNO6wO/gYt6G52cc3SCaHIoMrxiB/K6nAfEa6FFq79jtU0l/Ju9biKyFWo1m+uqa+Ja3ce3hJ/Fg==", + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/@cumulus/collection-config-store/-/collection-config-store-1.23.2.tgz", + "integrity": "sha512-x8Y8HZeP+w3nx4IK8/K9l6tV4og5rptqAoZDoidqWccGhEujy4MMOrKNfS4uaJyauhuMlI85BU+bGxoC8zxNWA==", "dev": true, "requires": { - "@cumulus/aws-client": "1.22.1", - "@cumulus/message": "1.22.1" + "@cumulus/aws-client": "1.23.2", + "@cumulus/message": "1.23.2" + }, + "dependencies": { + "@cumulus/aws-client": { + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/@cumulus/aws-client/-/aws-client-1.23.2.tgz", + "integrity": "sha512-Nv2FTJzwKBbY+n085+aH1QeLQjlsrEz7pO+6psiWBpY0Q9fxXJNqhQscxRMC2Gd3Raf+fOtqua0u7/B11UVMIw==", + "dev": true, + "requires": { + "@cumulus/checksum": "1.23.2", + "@cumulus/errors": "1.23.2", + "@cumulus/logger": "1.23.2", + "aws-sdk": "^2.585.0", + "jsonpath-plus": "^1.1.0", + "lodash": "~4.17.15", + "p-map": "^1.2.0", + "p-retry": "^4.2.0", + "pump": "^3.0.0" + } + }, + "@cumulus/checksum": { + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/@cumulus/checksum/-/checksum-1.23.2.tgz", + "integrity": "sha512-3HqOW4CsZhTMQmHw/Q/JJ+K/f9JQpb/2mw6Cn4RVXGLzg87825Ph2tYE+h4SQFIPc+eHSgVHcSDUUDLDBhwftA==", + "dev": true, + "requires": { + "cksum": "^1.3.0" + } + }, + "@cumulus/errors": { + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/@cumulus/errors/-/errors-1.23.2.tgz", + "integrity": "sha512-bkVB0f9YxvVPmB4zYtjRUFxWIA3D+T8ZPxH2GuXAv+VvNuptu3Rukl4oc232lpB38TgGSH1mXkqsgUwCe4QWAQ==", + "dev": true + }, + "@cumulus/logger": { + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/@cumulus/logger/-/logger-1.23.2.tgz", + "integrity": "sha512-wVLukDDqC4E4A6Bd1SHlEiY/lC3/zrOjQDHtJ/9mXQwG9hJ4k+aWI6gVWuNSZXBXBzcvqdpg5+mPvDG60R686Q==", + "dev": true, + "requires": { + "lodash.iserror": "^3.1.1" + } + }, + "p-map": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz", + "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==", + "dev": true + }, + "p-retry": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.2.0.tgz", + "integrity": "sha512-jPH38/MRh263KKcq0wBNOGFJbm+U6784RilTmHjB/HM9kH9V8WlCpVUcdOmip9cjXOh6MxZ5yk1z2SjDUJfWmA==", + "dev": true, + "requires": { + "@types/retry": "^0.12.0", + "retry": "^0.12.0" + } + } } }, "@cumulus/common": { @@ -1482,16 +1740,16 @@ "integrity": "sha512-BKZaqw2uOwul7HQR2LggpRLz2GDXPNToD3t/Fl80O+C4+zEt0pDncCy8TAmGZc8trrMYbnHTCpzJmo+iKuw8Fw==" }, "@cumulus/ingest": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/@cumulus/ingest/-/ingest-1.22.1.tgz", - "integrity": "sha512-oGkRzMbaKs1Uat0EGc5FpbaueQX05VNU1+HRj5Wunzsn0ByGXnFaC/W7nxABW8mza8WGGss7Z9em1aFDG/547g==", + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/@cumulus/ingest/-/ingest-1.23.2.tgz", + "integrity": "sha512-C2yJ0DfKZkTxDt67qsM8Tv1xSxwfzi57zYbXC8LDimuzuhwjhx38pHAYRfAP2httYK670tm1Hu7LMiGA66bGmA==", "dev": true, "requires": { - "@cumulus/aws-client": "1.22.1", - "@cumulus/common": "1.22.1", - "@cumulus/errors": "1.22.1", - "@cumulus/message": "1.22.1", - "@cumulus/sftp-client": "1.22.1", + "@cumulus/aws-client": "1.23.2", + "@cumulus/common": "1.23.2", + "@cumulus/errors": "1.23.2", + "@cumulus/message": "1.23.2", + "@cumulus/sftp-client": "1.23.2", "aws-sdk": "^2.585.0", "cksum": "^1.3.0", "encodeurl": "^1.0.2", @@ -1506,6 +1764,83 @@ "simplecrawler": "^1.1.4" }, "dependencies": { + "@cumulus/aws-client": { + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/@cumulus/aws-client/-/aws-client-1.23.2.tgz", + "integrity": "sha512-Nv2FTJzwKBbY+n085+aH1QeLQjlsrEz7pO+6psiWBpY0Q9fxXJNqhQscxRMC2Gd3Raf+fOtqua0u7/B11UVMIw==", + "dev": true, + "requires": { + "@cumulus/checksum": "1.23.2", + "@cumulus/errors": "1.23.2", + "@cumulus/logger": "1.23.2", + "aws-sdk": "^2.585.0", + "jsonpath-plus": "^1.1.0", + "lodash": "~4.17.15", + "p-map": "^1.2.0", + "p-retry": "^4.2.0", + "pump": "^3.0.0" + } + }, + "@cumulus/checksum": { + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/@cumulus/checksum/-/checksum-1.23.2.tgz", + "integrity": "sha512-3HqOW4CsZhTMQmHw/Q/JJ+K/f9JQpb/2mw6Cn4RVXGLzg87825Ph2tYE+h4SQFIPc+eHSgVHcSDUUDLDBhwftA==", + "dev": true, + "requires": { + "cksum": "^1.3.0" + } + }, + "@cumulus/common": { + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/@cumulus/common/-/common-1.23.2.tgz", + "integrity": "sha512-8G/bS8sfiJZOr6fKPGuBH3w+iyIHtKolcESjwz2BUHHgkjW4MaL/YHfeWjqIu/aFmEoJmgaDTgN0VJSLzVOHGA==", + "dev": true, + "requires": { + "@cumulus/errors": "1.23.2", + "@cumulus/logger": "1.23.2", + "ajv": "^5.2.2", + "aws-sdk": "^2.585.0", + "follow-redirects": "^1.2.4", + "fs-extra": "^5.0.0", + "got": "^9.2.1", + "js-yaml": "^3.11.0", + "jsonpath-plus": "^3.0.0", + "lodash": "^4.17.15", + "mime-types": "^2.1.22", + "node-forge": "^0.7.1", + "p-limit": "^2.0.0", + "p-map": "^1.2.0", + "p-retry": "^4.2.0", + "parseurl": "^1.3.3", + "randexp": "^0.4.9", + "ssh2": "^0.8.7", + "url-join": "^4.0.0", + "uuid": "^3.2.1" + }, + "dependencies": { + "jsonpath-plus": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-3.0.0.tgz", + "integrity": "sha512-WQwgWEBgn+SJU1tlDa/GiY5/ngRpa9yrSj8n4BYPHcwoxTDaMEaYCHMOn42hIHHDd3CrUoRr3+HpsK0hCKoxzA==", + "dev": true + } + } + }, + "@cumulus/errors": { + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/@cumulus/errors/-/errors-1.23.2.tgz", + "integrity": "sha512-bkVB0f9YxvVPmB4zYtjRUFxWIA3D+T8ZPxH2GuXAv+VvNuptu3Rukl4oc232lpB38TgGSH1mXkqsgUwCe4QWAQ==", + "dev": true + }, + "@cumulus/logger": { + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/@cumulus/logger/-/logger-1.23.2.tgz", + "integrity": "sha512-wVLukDDqC4E4A6Bd1SHlEiY/lC3/zrOjQDHtJ/9mXQwG9hJ4k+aWI6gVWuNSZXBXBzcvqdpg5+mPvDG60R686Q==", + "dev": true, + "requires": { + "lodash.iserror": "^3.1.1" + } + }, "got": { "version": "9.6.0", "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", @@ -1533,19 +1868,94 @@ "requires": { "ip-regex": "^2.0.0" } + }, + "p-map": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz", + "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==", + "dev": true + }, + "p-retry": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.2.0.tgz", + "integrity": "sha512-jPH38/MRh263KKcq0wBNOGFJbm+U6784RilTmHjB/HM9kH9V8WlCpVUcdOmip9cjXOh6MxZ5yk1z2SjDUJfWmA==", + "dev": true, + "requires": { + "@types/retry": "^0.12.0", + "retry": "^0.12.0" + } } } }, "@cumulus/launchpad-auth": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/@cumulus/launchpad-auth/-/launchpad-auth-1.22.1.tgz", - "integrity": "sha512-HMiMYgZAihL/8GcYKXAVm4yTOOVgXw07JakG2lxBumdodZFdgnZGVd+lr58iIV68jLGObgKT4f9QRlkUWvLfSA==", + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/@cumulus/launchpad-auth/-/launchpad-auth-1.23.2.tgz", + "integrity": "sha512-K82dQ7LZfoWAzhbmp2jftILZlhxsHH6WqUpY80V5rIPkkpb36MXHe39ZLCl/nck7L7Rc3ruIrn0E4kvm7WL1NA==", "dev": true, "requires": { - "@cumulus/aws-client": "1.22.1", - "@cumulus/logger": "1.22.1", + "@cumulus/aws-client": "1.23.2", + "@cumulus/logger": "1.23.2", "lodash": "^4.17.15", "uuid": "^3.2.1" + }, + "dependencies": { + "@cumulus/aws-client": { + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/@cumulus/aws-client/-/aws-client-1.23.2.tgz", + "integrity": "sha512-Nv2FTJzwKBbY+n085+aH1QeLQjlsrEz7pO+6psiWBpY0Q9fxXJNqhQscxRMC2Gd3Raf+fOtqua0u7/B11UVMIw==", + "dev": true, + "requires": { + "@cumulus/checksum": "1.23.2", + "@cumulus/errors": "1.23.2", + "@cumulus/logger": "1.23.2", + "aws-sdk": "^2.585.0", + "jsonpath-plus": "^1.1.0", + "lodash": "~4.17.15", + "p-map": "^1.2.0", + "p-retry": "^4.2.0", + "pump": "^3.0.0" + } + }, + "@cumulus/checksum": { + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/@cumulus/checksum/-/checksum-1.23.2.tgz", + "integrity": "sha512-3HqOW4CsZhTMQmHw/Q/JJ+K/f9JQpb/2mw6Cn4RVXGLzg87825Ph2tYE+h4SQFIPc+eHSgVHcSDUUDLDBhwftA==", + "dev": true, + "requires": { + "cksum": "^1.3.0" + } + }, + "@cumulus/errors": { + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/@cumulus/errors/-/errors-1.23.2.tgz", + "integrity": "sha512-bkVB0f9YxvVPmB4zYtjRUFxWIA3D+T8ZPxH2GuXAv+VvNuptu3Rukl4oc232lpB38TgGSH1mXkqsgUwCe4QWAQ==", + "dev": true + }, + "@cumulus/logger": { + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/@cumulus/logger/-/logger-1.23.2.tgz", + "integrity": "sha512-wVLukDDqC4E4A6Bd1SHlEiY/lC3/zrOjQDHtJ/9mXQwG9hJ4k+aWI6gVWuNSZXBXBzcvqdpg5+mPvDG60R686Q==", + "dev": true, + "requires": { + "lodash.iserror": "^3.1.1" + } + }, + "p-map": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz", + "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==", + "dev": true + }, + "p-retry": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.2.0.tgz", + "integrity": "sha512-jPH38/MRh263KKcq0wBNOGFJbm+U6784RilTmHjB/HM9kH9V8WlCpVUcdOmip9cjXOh6MxZ5yk1z2SjDUJfWmA==", + "dev": true, + "requires": { + "@types/retry": "^0.12.0", + "retry": "^0.12.0" + } + } } }, "@cumulus/logger": { @@ -1557,45 +1967,224 @@ } }, "@cumulus/message": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/@cumulus/message/-/message-1.22.1.tgz", - "integrity": "sha512-HVz0DSSTB5KO60vlFe82rownQnwmBQp4dXUJy5ReUasREOTGZKPdzAowmP7GtNKwJzwuJcJ/z7ZO/CqTPIfXug==", + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/@cumulus/message/-/message-1.23.2.tgz", + "integrity": "sha512-aDBNkijyxPV50Ox3IIb+R+/+2L4SRM5R7+d9P+cgWw0pfQgaExh9IDZJF6oCPiXYhBQIl5JbbkkyYrZHHG3quQ==", "dev": true, "requires": { - "@cumulus/aws-client": "1.22.1", - "@cumulus/logger": "1.22.1", + "@cumulus/aws-client": "1.23.2", + "@cumulus/logger": "1.23.2", "jsonpath-plus": "^3.0.0", "lodash": "^4.17.15", "uuid": "^3.2.1" }, "dependencies": { + "@cumulus/aws-client": { + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/@cumulus/aws-client/-/aws-client-1.23.2.tgz", + "integrity": "sha512-Nv2FTJzwKBbY+n085+aH1QeLQjlsrEz7pO+6psiWBpY0Q9fxXJNqhQscxRMC2Gd3Raf+fOtqua0u7/B11UVMIw==", + "dev": true, + "requires": { + "@cumulus/checksum": "1.23.2", + "@cumulus/errors": "1.23.2", + "@cumulus/logger": "1.23.2", + "aws-sdk": "^2.585.0", + "jsonpath-plus": "^1.1.0", + "lodash": "~4.17.15", + "p-map": "^1.2.0", + "p-retry": "^4.2.0", + "pump": "^3.0.0" + }, + "dependencies": { + "jsonpath-plus": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-1.1.0.tgz", + "integrity": "sha512-ydqTBOuLcFCUr9e7AxJlKCFgxzEQ03HjnIim0hJSdk2NxD8MOsaMOrRgP6XWEm5q3VuDY5+cRT1DM9vLlGo/qA==", + "dev": true + } + } + }, + "@cumulus/checksum": { + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/@cumulus/checksum/-/checksum-1.23.2.tgz", + "integrity": "sha512-3HqOW4CsZhTMQmHw/Q/JJ+K/f9JQpb/2mw6Cn4RVXGLzg87825Ph2tYE+h4SQFIPc+eHSgVHcSDUUDLDBhwftA==", + "dev": true, + "requires": { + "cksum": "^1.3.0" + } + }, + "@cumulus/errors": { + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/@cumulus/errors/-/errors-1.23.2.tgz", + "integrity": "sha512-bkVB0f9YxvVPmB4zYtjRUFxWIA3D+T8ZPxH2GuXAv+VvNuptu3Rukl4oc232lpB38TgGSH1mXkqsgUwCe4QWAQ==", + "dev": true + }, + "@cumulus/logger": { + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/@cumulus/logger/-/logger-1.23.2.tgz", + "integrity": "sha512-wVLukDDqC4E4A6Bd1SHlEiY/lC3/zrOjQDHtJ/9mXQwG9hJ4k+aWI6gVWuNSZXBXBzcvqdpg5+mPvDG60R686Q==", + "dev": true, + "requires": { + "lodash.iserror": "^3.1.1" + } + }, "jsonpath-plus": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-3.0.0.tgz", "integrity": "sha512-WQwgWEBgn+SJU1tlDa/GiY5/ngRpa9yrSj8n4BYPHcwoxTDaMEaYCHMOn42hIHHDd3CrUoRr3+HpsK0hCKoxzA==", "dev": true + }, + "p-map": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz", + "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==", + "dev": true + }, + "p-retry": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.2.0.tgz", + "integrity": "sha512-jPH38/MRh263KKcq0wBNOGFJbm+U6784RilTmHjB/HM9kH9V8WlCpVUcdOmip9cjXOh6MxZ5yk1z2SjDUJfWmA==", + "dev": true, + "requires": { + "@types/retry": "^0.12.0", + "retry": "^0.12.0" + } } } }, "@cumulus/pvl": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/@cumulus/pvl/-/pvl-1.22.1.tgz", - "integrity": "sha512-1xIF2j3MzzZQmp/Jgy7Txnk9QauKG7OzkI95peE1GVgKSgUXVB1SK5HMWtq6JrRS4nS7tKqfEpxXOZv+LxEGcA==", + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/@cumulus/pvl/-/pvl-1.23.2.tgz", + "integrity": "sha512-hlgq+eMF7hkrsar6SIubN7B3RFp9Re0iP5S295Z0Y77kvwdl3VC/TOvU3befjFLUt9xoENaztisb9cLcTOUvfg==", "dev": true, "requires": { "lodash": "^4.17.15" } }, "@cumulus/sftp-client": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/@cumulus/sftp-client/-/sftp-client-1.22.1.tgz", - "integrity": "sha512-a/acZ7uDxYYHOwEAsIdyZ5YVNT/Zg67Xw9+CD1/1ZShsEikSmP2VerruVfHWJ1crai8k/SM24bkSnxGCLOejoQ==", + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/@cumulus/sftp-client/-/sftp-client-1.23.2.tgz", + "integrity": "sha512-GybWiaZMt6g5X7B2pUqB3VKlLnzM8LjNfKT1VZZIgZtq4dk7jURMI8WokGI7snAMqpydJO/84021FBwu3hB9yg==", "dev": true, "requires": { - "@cumulus/aws-client": "1.22.1", - "@cumulus/common": "1.22.1", + "@cumulus/aws-client": "1.23.2", + "@cumulus/common": "1.23.2", "lodash": "^4.17.15", "ssh2": "^0.8.7" + }, + "dependencies": { + "@cumulus/aws-client": { + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/@cumulus/aws-client/-/aws-client-1.23.2.tgz", + "integrity": "sha512-Nv2FTJzwKBbY+n085+aH1QeLQjlsrEz7pO+6psiWBpY0Q9fxXJNqhQscxRMC2Gd3Raf+fOtqua0u7/B11UVMIw==", + "dev": true, + "requires": { + "@cumulus/checksum": "1.23.2", + "@cumulus/errors": "1.23.2", + "@cumulus/logger": "1.23.2", + "aws-sdk": "^2.585.0", + "jsonpath-plus": "^1.1.0", + "lodash": "~4.17.15", + "p-map": "^1.2.0", + "p-retry": "^4.2.0", + "pump": "^3.0.0" + } + }, + "@cumulus/checksum": { + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/@cumulus/checksum/-/checksum-1.23.2.tgz", + "integrity": "sha512-3HqOW4CsZhTMQmHw/Q/JJ+K/f9JQpb/2mw6Cn4RVXGLzg87825Ph2tYE+h4SQFIPc+eHSgVHcSDUUDLDBhwftA==", + "dev": true, + "requires": { + "cksum": "^1.3.0" + } + }, + "@cumulus/common": { + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/@cumulus/common/-/common-1.23.2.tgz", + "integrity": "sha512-8G/bS8sfiJZOr6fKPGuBH3w+iyIHtKolcESjwz2BUHHgkjW4MaL/YHfeWjqIu/aFmEoJmgaDTgN0VJSLzVOHGA==", + "dev": true, + "requires": { + "@cumulus/errors": "1.23.2", + "@cumulus/logger": "1.23.2", + "ajv": "^5.2.2", + "aws-sdk": "^2.585.0", + "follow-redirects": "^1.2.4", + "fs-extra": "^5.0.0", + "got": "^9.2.1", + "js-yaml": "^3.11.0", + "jsonpath-plus": "^3.0.0", + "lodash": "^4.17.15", + "mime-types": "^2.1.22", + "node-forge": "^0.7.1", + "p-limit": "^2.0.0", + "p-map": "^1.2.0", + "p-retry": "^4.2.0", + "parseurl": "^1.3.3", + "randexp": "^0.4.9", + "ssh2": "^0.8.7", + "url-join": "^4.0.0", + "uuid": "^3.2.1" + }, + "dependencies": { + "jsonpath-plus": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-3.0.0.tgz", + "integrity": "sha512-WQwgWEBgn+SJU1tlDa/GiY5/ngRpa9yrSj8n4BYPHcwoxTDaMEaYCHMOn42hIHHDd3CrUoRr3+HpsK0hCKoxzA==", + "dev": true + } + } + }, + "@cumulus/errors": { + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/@cumulus/errors/-/errors-1.23.2.tgz", + "integrity": "sha512-bkVB0f9YxvVPmB4zYtjRUFxWIA3D+T8ZPxH2GuXAv+VvNuptu3Rukl4oc232lpB38TgGSH1mXkqsgUwCe4QWAQ==", + "dev": true + }, + "@cumulus/logger": { + "version": "1.23.2", + "resolved": "https://registry.npmjs.org/@cumulus/logger/-/logger-1.23.2.tgz", + "integrity": "sha512-wVLukDDqC4E4A6Bd1SHlEiY/lC3/zrOjQDHtJ/9mXQwG9hJ4k+aWI6gVWuNSZXBXBzcvqdpg5+mPvDG60R686Q==", + "dev": true, + "requires": { + "lodash.iserror": "^3.1.1" + } + }, + "got": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "dev": true, + "requires": { + "@sindresorhus/is": "^0.14.0", + "@szmarczak/http-timer": "^1.1.2", + "cacheable-request": "^6.0.0", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^4.1.0", + "lowercase-keys": "^1.0.1", + "mimic-response": "^1.0.1", + "p-cancelable": "^1.0.0", + "to-readable-stream": "^1.0.0", + "url-parse-lax": "^3.0.0" + } + }, + "p-map": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz", + "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==", + "dev": true + }, + "p-retry": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.2.0.tgz", + "integrity": "sha512-jPH38/MRh263KKcq0wBNOGFJbm+U6784RilTmHjB/HM9kH9V8WlCpVUcdOmip9cjXOh6MxZ5yk1z2SjDUJfWmA==", + "dev": true, + "requires": { + "@types/retry": "^0.12.0", + "retry": "^0.12.0" + } + } } }, "@cypress/listr-verbose-renderer": { @@ -2342,7 +2931,7 @@ "acorn-jsx": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.2.0.tgz", - "integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==", + "integrity": "sha1-TGYGkXPW/daO2FI5/CViJhgrLr4=", "dev": true }, "acorn-walk": { @@ -3126,9 +3715,9 @@ } }, "aws-sdk": { - "version": "2.669.0", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.669.0.tgz", - "integrity": "sha512-kuVcSRpDzvkgmeSmMX6Q32eTOb8UeihhUdavMrvUOP6fzSU19cNWS9HAIkYOi/jrEDK85cCZxXjxqE3JGZIGcw==", + "version": "2.681.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.681.0.tgz", + "integrity": "sha512-/p8CDJ7LZvB1i4WrJrb32FUbbPdiZFZSN6FI2lv7s/scKypmuv/iJ9kpx6QWSWQZ72kJ3Njk/0o7GuVlw0jHXw==", "requires": { "buffer": "4.9.1", "events": "1.1.1", @@ -4012,7 +4601,7 @@ "chardet": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "integrity": "sha1-kAlISfCTfy7twkJdDSip5fDLrZ4=", "dev": true }, "check-error": { @@ -6554,7 +7143,7 @@ "doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "integrity": "sha1-rd6+rXKmV023g2OdyHoSF3OXOWE=", "dev": true, "requires": { "esutils": "^2.0.2" @@ -7039,7 +7628,7 @@ "eslint": { "version": "6.8.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.8.0.tgz", - "integrity": "sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig==", + "integrity": "sha1-YiYtZylzn5J1cjgkMC+yJ8jJP/s=", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", @@ -7084,7 +7673,7 @@ "ajv": { "version": "6.12.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz", - "integrity": "sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==", + "integrity": "sha1-BtYLlth7hFSlrauobnhU2mKdtLc=", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -7096,13 +7685,13 @@ "ansi-regex": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "integrity": "sha1-i5+PCM8ay4Q3Vqg5yox+MWjFGZc=", "dev": true }, "debug": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "integrity": "sha1-O3ImAlUQnGtYnO4FDx1RYTlmR5E=", "dev": true, "requires": { "ms": "^2.1.1" @@ -7111,7 +7700,7 @@ "eslint-scope": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz", - "integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==", + "integrity": "sha1-6HyIh8c+jR7ITxylkWRcNYv8j7k=", "dev": true, "requires": { "esrecurse": "^4.1.0", @@ -7121,13 +7710,13 @@ "fast-deep-equal": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", - "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==", + "integrity": "sha1-VFFFB3xQFJHjOxXsQIwpQ3bpSuQ=", "dev": true }, "globals": { "version": "12.4.0", "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", - "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "integrity": "sha1-oYgTV2pBsAokqX5/gVkYwuGZJfg=", "dev": true, "requires": { "type-fest": "^0.8.1" @@ -7136,13 +7725,13 @@ "ignore": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "integrity": "sha1-dQ49tYYgh7RzfrrIIH/9HvJ7Jfw=", "dev": true }, "import-fresh": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", - "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", + "integrity": "sha1-Yz/2GFBueTr1rJG/SLcmd+FcvmY=", "dev": true, "requires": { "parent-module": "^1.0.0", @@ -7152,25 +7741,25 @@ "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA=", "dev": true }, "resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "integrity": "sha1-SrzYUq0y3Xuqv+m0DgCjbbXzkuY=", "dev": true }, "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "integrity": "sha1-7gpkyK9ejO6mdoexM3YeG+y9HT0=", "dev": true }, "strip-ansi": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "integrity": "sha1-jJpTb+tq/JYr36WxBKUJHBrZwK4=", "dev": true, "requires": { "ansi-regex": "^4.1.0" @@ -7187,7 +7776,7 @@ "eslint-config-standard": { "version": "14.1.1", "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-14.1.1.tgz", - "integrity": "sha512-Z9B+VR+JIXRxz21udPTL9HpFMyoMUEeX1G251EQ6e05WD9aPVtVBn09XUmZ259wCMlCDmYDSZG62Hhm+ZTJcUg==", + "integrity": "sha1-gwqOROeu995nRkl5rQa0BgJsVuo=", "dev": true }, "eslint-import-resolver-node": { @@ -7507,7 +8096,7 @@ "eslint-plugin-promise": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-4.2.1.tgz", - "integrity": "sha512-VoM09vT7bfA7D+upt+FjeBO5eHIJQBUWki1aPvB+vbNiHS3+oGIJGIeyBtKQTME6UPXXy3vV07OL1tHd3ANuDw==", + "integrity": "sha1-hF/YsiYK2PglZMEiL85ErXHZQYo=", "dev": true }, "eslint-plugin-react": { @@ -7559,13 +8148,13 @@ "eslint-plugin-react-hooks": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-3.0.0.tgz", - "integrity": "sha512-EjxTHxjLKIBWFgDJdhKKzLh5q+vjTFrqNZX36uIxWS4OfyXe5DawqPj3U5qeJ1ngLwatjzQnmR0Lz0J0YH3kxw==", + "integrity": "sha1-noDHGEbraN0pw7IdgycoqmblvTU=", "dev": true }, "eslint-plugin-standard": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/eslint-plugin-standard/-/eslint-plugin-standard-4.0.1.tgz", - "integrity": "sha512-v/KBnfyaOMPmZc/dmc6ozOdWqekGp7bBGq4jLAecEfPGmfKiWS4sA8sC0LqiV9w5qmXAtXVn4M3p1jSyhY85SQ==", + "integrity": "sha1-/wUZ9/+v8RT3bRvXw5lu7w9uILQ=", "dev": true }, "eslint-scope": { @@ -7581,7 +8170,7 @@ "eslint-utils": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz", - "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", + "integrity": "sha1-dP7HxU0Hdrb2fgJRBAtYBlZOmB8=", "dev": true, "requires": { "eslint-visitor-keys": "^1.1.0" @@ -7613,7 +8202,7 @@ "espree": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/espree/-/espree-6.2.1.tgz", - "integrity": "sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==", + "integrity": "sha1-d/xy4f10SiBSwg84pbV1gy6Cc0o=", "dev": true, "requires": { "acorn": "^7.1.1", @@ -7624,7 +8213,7 @@ "acorn": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.1.tgz", - "integrity": "sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==", + "integrity": "sha1-41Zo3gtALzWd5RXFSCoaufiaab8=", "dev": true } } @@ -7663,7 +8252,7 @@ "estraverse": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.0.0.tgz", - "integrity": "sha512-j3acdrMzqrxmJTNj5dbr1YbjacrYgAxVMeF0gK16E3j494mOe7xygM/ZLIguEQ0ETwAg2hlJCtHRGav+y0Ny5A==", + "integrity": "sha1-rIF1C0gsEcyibksH6D7Y91+83CI=", "dev": true } } @@ -8007,7 +8596,7 @@ "external-editor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "integrity": "sha1-ywP3QL764D6k0oPK7SdBqD8zVJU=", "dev": true, "requires": { "chardet": "^0.7.0", @@ -8018,7 +8607,7 @@ "tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "integrity": "sha1-bTQzWIl2jSGyvNoKonfO07G/rfk=", "dev": true, "requires": { "os-tmpdir": "~1.0.2" @@ -8271,7 +8860,7 @@ "file-entry-cache": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", - "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", + "integrity": "sha1-yg9u+m3T1WEzP7FFFQZcL6/fQ5w=", "dev": true, "requires": { "flat-cache": "^2.0.1" @@ -8494,7 +9083,7 @@ "flat-cache": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", - "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "integrity": "sha1-XSltbwS9pEpGMKMBQTvbwuwIXsA=", "dev": true, "requires": { "flatted": "^2.0.0", @@ -8505,7 +9094,7 @@ "rimraf": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "integrity": "sha1-stEE/g2Psnz54KHNqCYt04M8bKs=", "dev": true, "requires": { "glob": "^7.1.3" @@ -8516,7 +9105,7 @@ "flatted": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", - "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", + "integrity": "sha1-RXWyHivO50NKqb5mL0t7X5wrUTg=", "dev": true }, "flatten": { @@ -9778,9 +10367,9 @@ "dev": true }, "http-proxy": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.0.tgz", - "integrity": "sha512-84I2iJM/n1d4Hdgc6y2+qY5mDaz2PUVjlg9znE9byl+q0uC3DeByqBGReQu5tpLK0TAqTIXScRUV+dg7+bUPpQ==", + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", "dev": true, "requires": { "eventemitter3": "^4.0.0", @@ -10135,7 +10724,7 @@ "inquirer": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.1.0.tgz", - "integrity": "sha512-5fJMWEmikSYu0nv/flMc475MhGbB7TSPd/2IpFV4I4rMklboCH2rQjYY5kKiYGHqUF9gvaambupcJFFG9dvReg==", + "integrity": "sha1-EpigGFmIPhfHJkuChwrhA0+S3Sk=", "dev": true, "requires": { "ansi-escapes": "^4.2.1", @@ -10156,13 +10745,13 @@ "ansi-regex": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "integrity": "sha1-OIU59VF5vzkznIGvMKZU1p+Hy3U=", "dev": true }, "ansi-styles": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "integrity": "sha1-kK51xCTQCNJiTFvynq0xd+v881k=", "dev": true, "requires": { "@types/color-name": "^1.1.1", @@ -10172,7 +10761,7 @@ "chalk": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "integrity": "sha1-P3PCv1JlkfV0zEksUeJFY0n4ROQ=", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -10182,7 +10771,7 @@ "color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "integrity": "sha1-ctOmjVmMm9s68q0ehPIdiWq9TeM=", "dev": true, "requires": { "color-name": "~1.1.4" @@ -10191,25 +10780,25 @@ "color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "integrity": "sha1-wqCah6y95pVD3m9j+jmVyCbFNqI=", "dev": true }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "integrity": "sha1-lEdx/ZyByBJlxNaUGGDaBrtZR5s=", "dev": true }, "is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "integrity": "sha1-8Rb4Bk/pCz94RKOJl8C3UFEmnx0=", "dev": true }, "string-width": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "integrity": "sha1-lSGCxGzHssMT0VluYjmSvRY7crU=", "dev": true, "requires": { "emoji-regex": "^8.0.0", @@ -10220,7 +10809,7 @@ "strip-ansi": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "integrity": "sha1-CxVx3XZpzNTz4G4U7x7tJiJa5TI=", "dev": true, "requires": { "ansi-regex": "^5.0.0" @@ -10229,7 +10818,7 @@ "supports-color": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "integrity": "sha1-aOMlkd9z4lrRxLSRCKLsUHliv9E=", "dev": true, "requires": { "has-flag": "^4.0.0" @@ -12538,7 +13127,7 @@ "mute-stream": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "integrity": "sha1-FjDEKyJR/4HiooPelqVJfqkuXg0=", "dev": true }, "nan": { @@ -13748,7 +14337,7 @@ "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "integrity": "sha1-aR0nCeeMefrjoVZiJFLQB2LKqqI=", "dev": true, "requires": { "callsites": "^3.0.0" @@ -13757,7 +14346,7 @@ "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "integrity": "sha1-s2MKvYlDQy9Us/BRkjjjPNffL3M=", "dev": true } } @@ -15444,7 +16033,7 @@ "progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "integrity": "sha1-foz42PW48jnBvGi+tOt4Vn1XLvg=", "dev": true }, "promise": { @@ -15907,7 +16496,7 @@ "react-redux": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.0.tgz", - "integrity": "sha512-EvCAZYGfOLqwV7gh849xy9/pt55rJXPwmYvI4lilPM5rUT/1NxuuN59ipdBksRVSvz0KInbPnp4IfoXJXCqiDA==", + "integrity": "sha1-+XD2IZKzmBZC/sRv0NsYoHT+h50=", "requires": { "@babel/runtime": "^7.5.5", "hoist-non-react-statics": "^3.3.0", @@ -16344,7 +16933,7 @@ "regexpp": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", - "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", + "integrity": "sha1-jRnTHPYySCtYkEn4KB+T28uk0H8=", "dev": true }, "regexpu-core": { @@ -16875,7 +17464,7 @@ "run-async": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.0.tgz", - "integrity": "sha512-xJTbh/d7Lm7SBhc1tNvTpeCHaEzoyxPrqNlvSdMfBTYwaY++UJFyXUOxAtsRUXjlqOfj8luNaR9vjCh4KeV+pg==", + "integrity": "sha1-5ZBUpbhods+uB/Qx0Yy63cWU8eg=", "dev": true, "requires": { "is-promise": "^2.1.0" @@ -18588,7 +19177,7 @@ "table": { "version": "5.4.6", "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", - "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", + "integrity": "sha1-EpLRlQDOP4YFOwXw6Ofko7shB54=", "dev": true, "requires": { "ajv": "^6.10.2", @@ -18600,7 +19189,7 @@ "ajv": { "version": "6.12.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz", - "integrity": "sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==", + "integrity": "sha1-BtYLlth7hFSlrauobnhU2mKdtLc=", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -18612,37 +19201,37 @@ "ansi-regex": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "integrity": "sha1-i5+PCM8ay4Q3Vqg5yox+MWjFGZc=", "dev": true }, "astral-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", - "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "integrity": "sha1-bIw/uCfdQ+45GPJ7gngqt2WKb9k=", "dev": true }, "emoji-regex": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "integrity": "sha1-kzoEBShgyF6DwSJHnEdIqOTHIVY=", "dev": true }, "fast-deep-equal": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", - "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==", + "integrity": "sha1-VFFFB3xQFJHjOxXsQIwpQ3bpSuQ=", "dev": true }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA=", "dev": true }, "slice-ansi": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", - "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "integrity": "sha1-ys12k0YaY3pXiNkqfdT7oGjoFjY=", "dev": true, "requires": { "ansi-styles": "^3.2.0", @@ -18653,7 +19242,7 @@ "string-width": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "integrity": "sha1-InZ74htirxCBV0MG9prFG2IgOWE=", "dev": true, "requires": { "emoji-regex": "^7.0.1", @@ -18664,7 +19253,7 @@ "strip-ansi": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "integrity": "sha1-jJpTb+tq/JYr36WxBKUJHBrZwK4=", "dev": true, "requires": { "ansi-regex": "^4.1.0" @@ -19878,28 +20467,28 @@ "dependencies": { "abbrev": { "version": "1.1.1", - "resolved": "", + "resolved": false, "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true, "optional": true }, "ansi-regex": { "version": "2.1.1", - "resolved": "", + "resolved": false, "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", "dev": true, "optional": true }, "aproba": { "version": "1.2.0", - "resolved": "", + "resolved": false, "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", "dev": true, "optional": true }, "are-we-there-yet": { "version": "1.1.5", - "resolved": "", + "resolved": false, "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", "dev": true, "optional": true, @@ -19910,14 +20499,14 @@ }, "balanced-match": { "version": "1.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true, "optional": true }, "brace-expansion": { "version": "1.1.11", - "resolved": "", + "resolved": false, "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "optional": true, @@ -19928,42 +20517,42 @@ }, "chownr": { "version": "1.1.4", - "resolved": "", + "resolved": false, "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", "dev": true, "optional": true }, "code-point-at": { "version": "1.1.0", - "resolved": "", + "resolved": false, "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", "dev": true, "optional": true }, "concat-map": { "version": "0.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true, "optional": true }, "console-control-strings": { "version": "1.1.0", - "resolved": "", + "resolved": false, "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", "dev": true, "optional": true }, "core-util-is": { "version": "1.0.2", - "resolved": "", + "resolved": false, "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "dev": true, "optional": true }, "debug": { "version": "3.2.6", - "resolved": "", + "resolved": false, "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "dev": true, "optional": true, @@ -19973,28 +20562,28 @@ }, "deep-extend": { "version": "0.6.0", - "resolved": "", + "resolved": false, "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "dev": true, "optional": true }, "delegates": { "version": "1.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", "dev": true, "optional": true }, "detect-libc": { "version": "1.0.3", - "resolved": "", + "resolved": false, "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", "dev": true, "optional": true }, "fs-minipass": { "version": "1.2.7", - "resolved": "", + "resolved": false, "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", "dev": true, "optional": true, @@ -20004,14 +20593,14 @@ }, "fs.realpath": { "version": "1.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true, "optional": true }, "gauge": { "version": "2.7.4", - "resolved": "", + "resolved": false, "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", "dev": true, "optional": true, @@ -20028,7 +20617,7 @@ }, "glob": { "version": "7.1.6", - "resolved": "", + "resolved": false, "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "dev": true, "optional": true, @@ -20043,14 +20632,14 @@ }, "has-unicode": { "version": "2.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", "dev": true, "optional": true }, "iconv-lite": { "version": "0.4.24", - "resolved": "", + "resolved": false, "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, "optional": true, @@ -20060,7 +20649,7 @@ }, "ignore-walk": { "version": "3.0.3", - "resolved": "", + "resolved": false, "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", "dev": true, "optional": true, @@ -20070,7 +20659,7 @@ }, "inflight": { "version": "1.0.6", - "resolved": "", + "resolved": false, "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "optional": true, @@ -20081,21 +20670,21 @@ }, "inherits": { "version": "2.0.4", - "resolved": "", + "resolved": false, "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true, "optional": true }, "ini": { "version": "1.3.5", - "resolved": "", + "resolved": false, "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", "dev": true, "optional": true }, "is-fullwidth-code-point": { "version": "1.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, "optional": true, @@ -20105,14 +20694,14 @@ }, "isarray": { "version": "1.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true, "optional": true }, "minimatch": { "version": "3.0.4", - "resolved": "", + "resolved": false, "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "optional": true, @@ -20122,14 +20711,14 @@ }, "minimist": { "version": "1.2.5", - "resolved": "", + "resolved": false, "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", "dev": true, "optional": true }, "minipass": { "version": "2.9.0", - "resolved": "", + "resolved": false, "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", "dev": true, "optional": true, @@ -20140,7 +20729,7 @@ }, "minizlib": { "version": "1.3.3", - "resolved": "", + "resolved": false, "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", "dev": true, "optional": true, @@ -20150,7 +20739,7 @@ }, "mkdirp": { "version": "0.5.3", - "resolved": "", + "resolved": false, "integrity": "sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg==", "dev": true, "optional": true, @@ -20160,14 +20749,14 @@ }, "ms": { "version": "2.1.2", - "resolved": "", + "resolved": false, "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true, "optional": true }, "needle": { "version": "2.3.3", - "resolved": "", + "resolved": false, "integrity": "sha512-EkY0GeSq87rWp1hoq/sH/wnTWgFVhYlnIkbJ0YJFfRgEFlz2RraCjBpFQ+vrEgEdp0ThfyHADmkChEhcb7PKyw==", "dev": true, "optional": true, @@ -20179,7 +20768,7 @@ }, "node-pre-gyp": { "version": "0.14.0", - "resolved": "", + "resolved": false, "integrity": "sha512-+CvDC7ZttU/sSt9rFjix/P05iS43qHCOOGzcr3Ry99bXG7VX953+vFyEuph/tfqoYu8dttBkE86JSKBO2OzcxA==", "dev": true, "optional": true, @@ -20198,7 +20787,7 @@ }, "nopt": { "version": "4.0.3", - "resolved": "", + "resolved": false, "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", "dev": true, "optional": true, @@ -20209,7 +20798,7 @@ }, "npm-bundled": { "version": "1.1.1", - "resolved": "", + "resolved": false, "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==", "dev": true, "optional": true, @@ -20219,14 +20808,14 @@ }, "npm-normalize-package-bin": { "version": "1.0.1", - "resolved": "", + "resolved": false, "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", "dev": true, "optional": true }, "npm-packlist": { "version": "1.4.8", - "resolved": "", + "resolved": false, "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==", "dev": true, "optional": true, @@ -20238,7 +20827,7 @@ }, "npmlog": { "version": "4.1.2", - "resolved": "", + "resolved": false, "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "dev": true, "optional": true, @@ -20251,21 +20840,21 @@ }, "number-is-nan": { "version": "1.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "dev": true, "optional": true }, "object-assign": { "version": "4.1.1", - "resolved": "", + "resolved": false, "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "dev": true, "optional": true }, "once": { "version": "1.4.0", - "resolved": "", + "resolved": false, "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "optional": true, @@ -20275,21 +20864,21 @@ }, "os-homedir": { "version": "1.0.2", - "resolved": "", + "resolved": false, "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "dev": true, "optional": true }, "os-tmpdir": { "version": "1.0.2", - "resolved": "", + "resolved": false, "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true, "optional": true }, "osenv": { "version": "0.1.5", - "resolved": "", + "resolved": false, "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", "dev": true, "optional": true, @@ -20300,21 +20889,21 @@ }, "path-is-absolute": { "version": "1.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true, "optional": true }, "process-nextick-args": { "version": "2.0.1", - "resolved": "", + "resolved": false, "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true, "optional": true }, "rc": { "version": "1.2.8", - "resolved": "", + "resolved": false, "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "dev": true, "optional": true, @@ -20327,7 +20916,7 @@ }, "readable-stream": { "version": "2.3.7", - "resolved": "", + "resolved": false, "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, "optional": true, @@ -20343,7 +20932,7 @@ }, "rimraf": { "version": "2.7.1", - "resolved": "", + "resolved": false, "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", "dev": true, "optional": true, @@ -20353,49 +20942,49 @@ }, "safe-buffer": { "version": "5.1.2", - "resolved": "", + "resolved": false, "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true, "optional": true }, "safer-buffer": { "version": "2.1.2", - "resolved": "", + "resolved": false, "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true, "optional": true }, "sax": { "version": "1.2.4", - "resolved": "", + "resolved": false, "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "dev": true, "optional": true }, "semver": { "version": "5.7.1", - "resolved": "", + "resolved": false, "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true, "optional": true }, "set-blocking": { "version": "2.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true, "optional": true }, "signal-exit": { "version": "3.0.2", - "resolved": "", + "resolved": false, "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true, "optional": true }, "string-width": { "version": "1.0.2", - "resolved": "", + "resolved": false, "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "optional": true, @@ -20407,7 +20996,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "", + "resolved": false, "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "optional": true, @@ -20417,7 +21006,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "optional": true, @@ -20427,14 +21016,14 @@ }, "strip-json-comments": { "version": "2.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "dev": true, "optional": true }, "tar": { "version": "4.4.13", - "resolved": "", + "resolved": false, "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", "dev": true, "optional": true, @@ -20450,14 +21039,14 @@ }, "util-deprecate": { "version": "1.0.2", - "resolved": "", + "resolved": false, "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true, "optional": true }, "wide-align": { "version": "1.1.3", - "resolved": "", + "resolved": false, "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", "dev": true, "optional": true, @@ -20467,14 +21056,14 @@ }, "wrappy": { "version": "1.0.2", - "resolved": "", + "resolved": false, "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true, "optional": true }, "yallist": { "version": "3.1.1", - "resolved": "", + "resolved": false, "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true, "optional": true @@ -21350,28 +21939,28 @@ "dependencies": { "abbrev": { "version": "1.1.1", - "resolved": "", + "resolved": false, "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true, "optional": true }, "ansi-regex": { "version": "2.1.1", - "resolved": "", + "resolved": false, "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", "dev": true, "optional": true }, "aproba": { "version": "1.2.0", - "resolved": "", + "resolved": false, "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", "dev": true, "optional": true }, "are-we-there-yet": { "version": "1.1.5", - "resolved": "", + "resolved": false, "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", "dev": true, "optional": true, @@ -21382,14 +21971,14 @@ }, "balanced-match": { "version": "1.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true, "optional": true }, "brace-expansion": { "version": "1.1.11", - "resolved": "", + "resolved": false, "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "optional": true, @@ -21400,42 +21989,42 @@ }, "chownr": { "version": "1.1.4", - "resolved": "", + "resolved": false, "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", "dev": true, "optional": true }, "code-point-at": { "version": "1.1.0", - "resolved": "", + "resolved": false, "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", "dev": true, "optional": true }, "concat-map": { "version": "0.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true, "optional": true }, "console-control-strings": { "version": "1.1.0", - "resolved": "", + "resolved": false, "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", "dev": true, "optional": true }, "core-util-is": { "version": "1.0.2", - "resolved": "", + "resolved": false, "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "dev": true, "optional": true }, "debug": { "version": "3.2.6", - "resolved": "", + "resolved": false, "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "dev": true, "optional": true, @@ -21445,28 +22034,28 @@ }, "deep-extend": { "version": "0.6.0", - "resolved": "", + "resolved": false, "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "dev": true, "optional": true }, "delegates": { "version": "1.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", "dev": true, "optional": true }, "detect-libc": { "version": "1.0.3", - "resolved": "", + "resolved": false, "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", "dev": true, "optional": true }, "fs-minipass": { "version": "1.2.7", - "resolved": "", + "resolved": false, "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", "dev": true, "optional": true, @@ -21476,14 +22065,14 @@ }, "fs.realpath": { "version": "1.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true, "optional": true }, "gauge": { "version": "2.7.4", - "resolved": "", + "resolved": false, "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", "dev": true, "optional": true, @@ -21500,7 +22089,7 @@ }, "glob": { "version": "7.1.6", - "resolved": "", + "resolved": false, "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "dev": true, "optional": true, @@ -21515,14 +22104,14 @@ }, "has-unicode": { "version": "2.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", "dev": true, "optional": true }, "iconv-lite": { "version": "0.4.24", - "resolved": "", + "resolved": false, "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, "optional": true, @@ -21532,7 +22121,7 @@ }, "ignore-walk": { "version": "3.0.3", - "resolved": "", + "resolved": false, "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", "dev": true, "optional": true, @@ -21542,7 +22131,7 @@ }, "inflight": { "version": "1.0.6", - "resolved": "", + "resolved": false, "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "optional": true, @@ -21553,21 +22142,21 @@ }, "inherits": { "version": "2.0.4", - "resolved": "", + "resolved": false, "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true, "optional": true }, "ini": { "version": "1.3.5", - "resolved": "", + "resolved": false, "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", "dev": true, "optional": true }, "is-fullwidth-code-point": { "version": "1.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, "optional": true, @@ -21577,14 +22166,14 @@ }, "isarray": { "version": "1.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true, "optional": true }, "minimatch": { "version": "3.0.4", - "resolved": "", + "resolved": false, "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "optional": true, @@ -21594,14 +22183,14 @@ }, "minimist": { "version": "1.2.5", - "resolved": "", + "resolved": false, "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", "dev": true, "optional": true }, "minipass": { "version": "2.9.0", - "resolved": "", + "resolved": false, "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", "dev": true, "optional": true, @@ -21612,7 +22201,7 @@ }, "minizlib": { "version": "1.3.3", - "resolved": "", + "resolved": false, "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", "dev": true, "optional": true, @@ -21622,7 +22211,7 @@ }, "mkdirp": { "version": "0.5.3", - "resolved": "", + "resolved": false, "integrity": "sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg==", "dev": true, "optional": true, @@ -21632,14 +22221,14 @@ }, "ms": { "version": "2.1.2", - "resolved": "", + "resolved": false, "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true, "optional": true }, "needle": { "version": "2.3.3", - "resolved": "", + "resolved": false, "integrity": "sha512-EkY0GeSq87rWp1hoq/sH/wnTWgFVhYlnIkbJ0YJFfRgEFlz2RraCjBpFQ+vrEgEdp0ThfyHADmkChEhcb7PKyw==", "dev": true, "optional": true, @@ -21651,7 +22240,7 @@ }, "node-pre-gyp": { "version": "0.14.0", - "resolved": "", + "resolved": false, "integrity": "sha512-+CvDC7ZttU/sSt9rFjix/P05iS43qHCOOGzcr3Ry99bXG7VX953+vFyEuph/tfqoYu8dttBkE86JSKBO2OzcxA==", "dev": true, "optional": true, @@ -21670,7 +22259,7 @@ }, "nopt": { "version": "4.0.3", - "resolved": "", + "resolved": false, "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", "dev": true, "optional": true, @@ -21681,7 +22270,7 @@ }, "npm-bundled": { "version": "1.1.1", - "resolved": "", + "resolved": false, "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==", "dev": true, "optional": true, @@ -21691,14 +22280,14 @@ }, "npm-normalize-package-bin": { "version": "1.0.1", - "resolved": "", + "resolved": false, "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", "dev": true, "optional": true }, "npm-packlist": { "version": "1.4.8", - "resolved": "", + "resolved": false, "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==", "dev": true, "optional": true, @@ -21710,7 +22299,7 @@ }, "npmlog": { "version": "4.1.2", - "resolved": "", + "resolved": false, "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "dev": true, "optional": true, @@ -21723,21 +22312,21 @@ }, "number-is-nan": { "version": "1.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "dev": true, "optional": true }, "object-assign": { "version": "4.1.1", - "resolved": "", + "resolved": false, "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "dev": true, "optional": true }, "once": { "version": "1.4.0", - "resolved": "", + "resolved": false, "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "optional": true, @@ -21747,21 +22336,21 @@ }, "os-homedir": { "version": "1.0.2", - "resolved": "", + "resolved": false, "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "dev": true, "optional": true }, "os-tmpdir": { "version": "1.0.2", - "resolved": "", + "resolved": false, "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true, "optional": true }, "osenv": { "version": "0.1.5", - "resolved": "", + "resolved": false, "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", "dev": true, "optional": true, @@ -21772,21 +22361,21 @@ }, "path-is-absolute": { "version": "1.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true, "optional": true }, "process-nextick-args": { "version": "2.0.1", - "resolved": "", + "resolved": false, "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true, "optional": true }, "rc": { "version": "1.2.8", - "resolved": "", + "resolved": false, "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "dev": true, "optional": true, @@ -21799,7 +22388,7 @@ }, "readable-stream": { "version": "2.3.7", - "resolved": "", + "resolved": false, "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, "optional": true, @@ -21815,7 +22404,7 @@ }, "rimraf": { "version": "2.7.1", - "resolved": "", + "resolved": false, "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", "dev": true, "optional": true, @@ -21825,49 +22414,49 @@ }, "safe-buffer": { "version": "5.1.2", - "resolved": "", + "resolved": false, "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true, "optional": true }, "safer-buffer": { "version": "2.1.2", - "resolved": "", + "resolved": false, "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true, "optional": true }, "sax": { "version": "1.2.4", - "resolved": "", + "resolved": false, "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "dev": true, "optional": true }, "semver": { "version": "5.7.1", - "resolved": "", + "resolved": false, "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true, "optional": true }, "set-blocking": { "version": "2.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true, "optional": true }, "signal-exit": { "version": "3.0.2", - "resolved": "", + "resolved": false, "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true, "optional": true }, "string-width": { "version": "1.0.2", - "resolved": "", + "resolved": false, "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "optional": true, @@ -21879,7 +22468,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "", + "resolved": false, "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "optional": true, @@ -21889,7 +22478,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "optional": true, @@ -21899,14 +22488,14 @@ }, "strip-json-comments": { "version": "2.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "dev": true, "optional": true }, "tar": { "version": "4.4.13", - "resolved": "", + "resolved": false, "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", "dev": true, "optional": true, @@ -21922,14 +22511,14 @@ }, "util-deprecate": { "version": "1.0.2", - "resolved": "", + "resolved": false, "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true, "optional": true }, "wide-align": { "version": "1.1.3", - "resolved": "", + "resolved": false, "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", "dev": true, "optional": true, @@ -21939,14 +22528,14 @@ }, "wrappy": { "version": "1.0.2", - "resolved": "", + "resolved": false, "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true, "optional": true }, "yallist": { "version": "3.1.1", - "resolved": "", + "resolved": false, "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true, "optional": true @@ -22559,7 +23148,7 @@ "write": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", - "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", + "integrity": "sha1-CADhRSO5I6OH5BUSPIZWFqrg9cM=", "dev": true, "requires": { "mkdirp": "^0.5.1" diff --git a/package.json b/package.json index c143aafca..a2fe73a42 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cumulus-dashboard", - "version": "1.8.0", + "version": "1.9.0", "description": "A dashboard for Cumulus API", "repository": { "type": "git", @@ -19,7 +19,7 @@ "node": ">=10.16.3" }, "scripts": { - "audit-ci": "audit-ci --high -p npm", + "audit-ci": "audit-ci --config $(pwd)/audit-ci.json", "serve": "NODE_ENV=development webpack-dev-server --open --config webpack.dev.js", "serve-hot": "NODE_ENV=development webpack-dev-server --open --hot --config webpack.dev.js", "serve-cypress": "SHOW_DISTRIBUTION_API_METRICS=true ESROOT=http://example.com npm run serve", @@ -96,7 +96,8 @@ "@babel/preset-react": "^7.0.0", "@babel/register": "^7.0.0", "@babel/runtime": "^7.8.4", - "@cumulus/api": "1.22.1", + "@cumulus/api": "1.23.2", + "@cumulus/aws-client": "^1.22.1", "@cypress/webpack-preprocessor": "^4.1.2", "audit-ci": "1.3.0", "autoprefixer": "^9.7.3", @@ -214,7 +215,7 @@ "prop-types": "^15.5.10", "react": "^16.10.2", "react-ace": "^6.2.0", - "react-autocomplete": "^1.7.1", + "react-autocomplete": "^1.8.1", "react-bootstrap": "^1.0.0-beta.14", "react-collapsible": "^2.6.3", "react-datetime-picker": "2.8.0", diff --git a/test/actions/datefilters.js b/test/actions/datefilters.js index 0874b4953..35cd1f81e 100644 --- a/test/actions/datefilters.js +++ b/test/actions/datefilters.js @@ -68,6 +68,8 @@ test('Each of these list action creators will pull data from datepicker state wh { action: 'OPERATIONS_INFLIGHT', dispatcher: listOperations }, { action: 'PDRS_INFLIGHT', dispatcher: listPdrs }, { action: 'PROVIDERS_INFLIGHT', dispatcher: listProviders }, + { action: 'PROVIDERS_INFLIGHT', dispatcher: listProviders }, + { action: 'RECONCILIATIONS_INFLIGHT', dispatcher: listReconciliationReports }, { action: 'RULES_INFLIGHT', dispatcher: listRules }, { action: 'STATS_INFLIGHT', dispatcher: getStats } ]; @@ -95,7 +97,6 @@ test('Each of these list action creators will pull data from datepicker state wh test('Each of these list action creators will not use data from datepicker state when calling the Cumulus API.', (t) => { const endpoints = [ { action: 'WORKFLOWS_INFLIGHT', dispatcher: listWorkflows }, - { action: 'RECONCILIATIONS_INFLIGHT', dispatcher: listReconciliationReports }, { action: 'EXECUTION_LOGS_INFLIGHT', dispatcher: getExecutionLogs } ]; diff --git a/test/actions/login.js b/test/actions/login.js new file mode 100644 index 000000000..db3be3e98 --- /dev/null +++ b/test/actions/login.js @@ -0,0 +1,41 @@ +import test from 'ava'; +import rewire from 'rewire'; + +import configureMockStore from 'redux-mock-store'; +import thunk from 'redux-thunk'; + +import { requestMiddleware } from '../../app/src/js/middleware/request'; + +const actions = rewire('../../app/src/js/actions'); + +const middlewares = [ + requestMiddleware, + thunk +]; +const mockStore = configureMockStore(middlewares); +const store = mockStore({ + api: { + tokens: { + token: 'fake-token' + } + } +}); + +test.beforeEach((t) => { + store.clearActions(); +}); + +test.serial('loginError action creator dispatches DELETE_TOKEN and LOGIN_ERROR actions', async (t) => { + const error = 'fail'; + + return store.dispatch(actions.loginError(error)).then(() => { + const dispatchedActions = store.getActions(); + const expectedActions = [{ + type: 'DELETE_TOKEN' + }, { + type: 'LOGIN_ERROR', + error + }]; + t.deepEqual(dispatchedActions, expectedActions); + }); +}); diff --git a/test/actions/recover.js b/test/actions/recover.js index d8c1f2c04..f33fb2798 100644 --- a/test/actions/recover.js +++ b/test/actions/recover.js @@ -28,6 +28,10 @@ const store = mockStore({ recovered: {}, executed: {}, meta: {} + }, + datepicker: { + startDateTime: null, + endDateTime: null, } }); const getActionType = (action) => action.type; diff --git a/test/components/ReingestGranules/BatchReingestCompleteContent.js b/test/components/ReingestGranules/BatchReingestCompleteContent.js index 079a9585c..c7dafb62e 100644 --- a/test/components/ReingestGranules/BatchReingestCompleteContent.js +++ b/test/components/ReingestGranules/BatchReingestCompleteContent.js @@ -11,20 +11,25 @@ import BatchReingestComplete, { configure({ adapter: new Adapter() }); -const anError = `1 error(s) occurred: -Execution Does Not Exist: 'arn:aws:states:us-east-1:123456789012:execution:TestStateMachine-Rcei43QwgSP6:bab9c234-e492-40e6-a34c-a0f76a4c7968'; Function params: [ +const errors = [ { - "executionArn": "arn:aws:states:us-east-1:123456789012:execution:TestStateMachine-Rcei43QwgSP6:bab9c234-e492-40e6-a34c-a0f76a4c7968" + id: '', + error: `Execution Does Not Exist: 'arn:aws:states:us-east-1:123456789012:execution:TestStateMachine-Rcei43QwgSP6:bab9c234-e492-40e6-a34c-a0f76a4c7968'; Function params: [ + { + "executionArn": "arn:aws:states:us-east-1:123456789012:execution:TestStateMachine-Rcei43QwgSP6:bab9c234-e492-40e6-a34c-a0f76a4c7968" + } + ]` } -]`; +]; test('Renders Error result', (t) => { const wrapper = shallow( - + ); - t.true(wrapper.find('span').hasClass('error')); - t.true(wrapper.text().includes(anError)); + const errorWrapper = wrapper.find('Collapsible').dive(); + + t.true(errorWrapper.find('.Collapsible__contentInner').text().includes(errors[0].error)); }); test('Renders success results with multiple Granules', (t) => { diff --git a/test/components/granules/overview.js b/test/components/granules/overview.js index a14628358..c97987747 100644 --- a/test/components/granules/overview.js +++ b/test/components/granules/overview.js @@ -6,6 +6,7 @@ import React from 'react'; import 'jsdom-global/register'; import { Provider } from 'react-redux'; import { shallow, configure } from 'enzyme'; +import sinon from 'sinon'; import { GranulesOverview } from '../../../app/src/js/components/Granules/overview'; configure({ adapter: new Adapter() }); @@ -35,7 +36,11 @@ const granules = { } } }; -const data = ''; +const data = { + data: 'granuleUr","collectionId","createdAt","startDateTime","endDateTime" "MOD09GQ.A1657416.CbyoRi.006.9697917818587","MOD09GQ___006","2018-09-24T23:28:43.341Z","2017-10-24T00:00:00.000Z","2017-11-08T23:59:59.000Z" "MOD09GQ.A0142558.ee5lpE.006.5112577830916","MOD09GQ___006","2018-09-24T17:53:10.359Z","2017-10-24T00:00:00.000Z","2017-11-08T23:59:59.000Z" "MOD09GQ.A2417309.YZ9tCV.006.4640974889044","MOD09GQ___006","2018-09-24T23:09:52.105Z","","" "MOD09GQ.A8022119.sk3Sph.006.0494433853533","MOD09GQ___006","2018-09-24T17:38:15.121Z","2017-10-24T00:00:00.000Z","2017-11-08T23:59:59.000Z" "MOD09GQ.A9344328.K9yI3O.006.4625818663028","MOD09GQ___006","2018-09-24T17:29:51.858Z","","" "MOD09GQ.A4622742.B7A8Ma.006.7857260550036","MOD09GQ___006","2019-12-11T23:19:23.823Z","2017-10-24T00:00:00Z","2017-11-08T23:59:59Z" "MOD09GQ.A5456658.rso6Y4.006.4979096122140","MOD09GQ___006","2018-09-24T23:11:06.647Z","2017-10-24T00:00:00.000Z","2017-11-08T23:59:59.000Z" "MOD09GQ.A1530852.CljGDp.006.2163412421938","MOD09GQ___006","2018-09-24T23:29:16.154Z","2017-10-24T00:00:00.000Z","2017-11-08T23:59:59.000Z" "MOD09GQ.A2016358.h13v04.006.2016360104606.hdf","MOD09GQ___006","2018-09-24T23:27:50.335Z","","" "MOD09GQ.A2016358.h13v04.006.2016360104606","MOD09GQ___006","2018-11-12T20:05:10.348Z","","" "MOD09GQ.A0501579.PZB_CG.006.8580266395214","MOD09GQ___006","2018-09-24T17:52:32.232Z","2017-10-24T00:00:00.000Z","2017-11-08T23:59:59.000Z"', + inflight: false, + error: null +}; test('GranulesOverview generates bulkAction for recovery button', function (t) { const dispatch = () => {}; @@ -102,3 +107,33 @@ test('GranulesOverview does not generate bulkAction for recovery button', functi const recoverActionList = listBulkActions.filter(recoverFilter); t.is(recoverActionList.length, 0); }); + +test('GranulesOverview will download CSV data when the Download Granule List button is clicked and not leave extra link on the page', function (t) { + window.URL.createObjectURL = sinon.fake.returns('www.example.com'); + const dispatch = () => Promise.resolve(); + const config = { enableRecovery: false }; + const store = { + subscribe: () => {}, + dispatch: dispatch, + getState: () => {} + }; + + const providerWrapper = shallow( + + + ); + + const overviewWrapper = providerWrapper.find('GranulesOverview').dive(); + + const granuleCSVButton = overviewWrapper.find('a.csv__download'); + granuleCSVButton.simulate('click'); + + // should not leave extra link on the page + const downloadLink = overviewWrapper.find('a[href="www.example.com"]'); + t.is(downloadLink.length, 0); +}); diff --git a/test/components/reconciliation-reports/reconciliation-report.js b/test/components/reconciliation-reports/reconciliation-report.js index 9dc4b49a7..febd62c31 100644 --- a/test/components/reconciliation-reports/reconciliation-report.js +++ b/test/components/reconciliation-reports/reconciliation-report.js @@ -18,22 +18,22 @@ const reconciliationReports = { status: 'SUCCESS', error: null, okFileCount: 21, - onlyInS3: [ - 's3://some-bucket/path/to/key-1.hdf', - 's3://some-bucket/path/to/key-2.hdf' - ], - onlyInDynamoDb: [ - { - uri: 's3://some-bucket/path/to/key-123.hdf', - granuleId: 'g-123' - }, - { - uri: 's3://some-bucket/path/to/key-456.hdf', - granuleId: 'g-456' - } - ], filesInCumulus: { - okCount: 129 + okCount: 129, + onlyInS3: [ + 's3://some-bucket/path/to/key-1.hdf', + 's3://some-bucket/path/to/key-2.hdf' + ], + onlyInDynamoDb: [ + { + uri: 's3://some-bucket/path/to/key-123.hdf', + granuleId: 'g-123' + }, + { + uri: 's3://some-bucket/path/to/key-456.hdf', + granuleId: 'g-456' + } + ], }, collectionsInCumulusCmr: { okCount: 1 @@ -57,29 +57,40 @@ const reconciliationReports = { test('show individual report', function (t) { const match = { params: { reconciliationReportName: 'exampleReport' } }; + const dispatch = () => {}; + const report = shallow( ); t.is(report.length, 1); - const Metadata = report.find('Metadata'); - const MetadataWrapper = Metadata.dive(); - const MetadataWrapperChildren = MetadataWrapper.children(); - // ReconciliationReport is configured to use 6 metaAccessors, - // so there will be 6 groups of dt, dd elements - t.is(MetadataWrapperChildren.length, 6); + + const TableCards = report.find('TableCards'); + t.is(TableCards.length, 1); + const TableCardWrapper = TableCards.dive(); + const Cards = TableCardWrapper.find('Card'); + // there should be one card for DynamoDB and one card for S3 + t.is(Cards.length, 2); + + // There should only be one table visible + const Table = report.find('SortableTable'); + t.is(Table.length, 1); }); test('report with error triggers error message', function (t) { const match = { params: { reconciliationReportName: 'exampleReportWithError' } }; + const dispatch = () => {}; + const report = shallow( ); diff --git a/test/components/reconciliation-reports/report-table.js b/test/components/reconciliation-reports/report-table.js deleted file mode 100644 index 75444aeb0..000000000 --- a/test/components/reconciliation-reports/report-table.js +++ /dev/null @@ -1,157 +0,0 @@ -'use strict'; - -import test from 'ava'; -import Adapter from 'enzyme-adapter-react-16'; -import React from 'react'; -import Collapsible from 'react-collapsible'; -import { shallow, configure } from 'enzyme'; - -import ReportTable from '../../../app/src/js/components/ReconciliationReports/report-table'; -import SortableTable from '../../../app/src/js/components/SortableTable/SortableTable'; -import { nullValue } from '../../../app/src/js/utils/format'; - -configure({ adapter: new Adapter() }); - -const tableColumns = [ - { - Header: 'GranuleId', - accessor: 'granuleId' - }, - { - Header: 'Filename', - accessor: 'filename' - }, - { - Header: 'Bucket', - accessor: 'bucket' - }, - { - Header: 'S3 Link', - accessor: row => row ? Link : nullValue, - id: 'link' - } -] - -const tableData = [{ - granuleId: 'g-123', - filename: 'filename.txt', - bucket: 'some-bucket', - path: 's3://some-bucket/filename.txt' -}]; - -test('render nothing when no data is provided', function (t) { - const report = shallow( - - ); - - t.true(report.isEmptyRender()); -}); - -test('render nothing when no data is an empty array', function (t) { - const report = shallow( - - ); - - t.true(report.isEmptyRender()); -}); - -test('render basic table', function (t) { - const report = shallow( - - ); - - t.is(report.find('h3').text(), 'Test table (1)'); - - const Table = report.find(SortableTable).dive(); - - const headerRow = Table.find('.thead .tr').first(); - t.is(headerRow.find('.th > div:first-child').at(0).text(), 'GranuleId'); - t.is(headerRow.find('.th > div:first-child').at(1).text(), 'Filename'); - t.is(headerRow.find('.th > div:first-child').at(2).text(), 'Bucket'); - t.is(headerRow.find('.th > div:first-child').at(3).text(), 'S3 Link'); - - const dataRow = Table.find('.tbody .tr').first(); - t.is(dataRow.find('Cell').at(0).dive().text(), 'g-123'); - t.is(dataRow.find('Cell').at(1).dive().text(), 'filename.txt'); - t.is(dataRow.find('Cell').at(2).dive().text(), 'some-bucket'); - t.true(dataRow.find('Cell').at(3).dive().contains( - Link - )); -}); - -test('render buttons to show/hide table when configured', function (t) { - const data = [ - ...tableData, - ...tableData, - ...tableData - ]; - - const report = shallow( - - ); - - const collapsibleTable = report.find(Collapsible); - t.true(collapsibleTable.exists()); - - const collapsibleTrigger = collapsibleTable.dive().find('.Collapsible__trigger'); - t.true(collapsibleTrigger.exists()); - t.is(collapsibleTrigger.text(), 'Show table (3 rows)'); -}); - -test('do not render buttons to show/hide table when data length is less than threshold', function (t) { - const data = [ - ...tableData, - ...tableData, - ...tableData - ]; - - const report = shallow( - - ); - - const collapsibleTable = report.find(Collapsible); - t.false(collapsibleTable.exists()); - const table = report.find(SortableTable); - t.true(table.exists()); -}); - -test('do not render buttons to show/hide table when disabled', function (t) { - const data = [ - ...tableData, - ...tableData, - ...tableData - ]; - - const report = shallow( - - ); - - const collapsibleTable = report.find(Collapsible); - t.false(collapsibleTable.exists()); - const table = report.find(SortableTable); - t.true(table.exists()); -}); diff --git a/test/components/table/list-view.js b/test/components/table/list-view.js index d0ddefffd..4b78e04f2 100644 --- a/test/components/table/list-view.js +++ b/test/components/table/list-view.js @@ -1,12 +1,17 @@ 'use strict'; import test from 'ava'; +import { configure, mount } from 'enzyme'; import Adapter from 'enzyme-adapter-react-16'; import React from 'react'; -import {mount, configure} from 'enzyme'; -import {listGranules} from '../../../app/src/js/actions'; +import { Provider } from 'react-redux'; +import configureMockStore from 'redux-mock-store'; +import thunk from 'redux-thunk'; +import { listGranules } from '../../../app/src/js/actions'; import { List } from '../../../app/src/js/components/Table/Table'; import Timer from '../../../app/src/js/components/Timer/timer.js'; +import { requestMiddleware } from '../../../app/src/js/middleware/request'; +import { initialState } from '../../../app/src/js/reducers/datepicker'; import { errorTableColumns } from '../../../app/src/js/utils/table-config/granules'; configure({ adapter: new Adapter() }); @@ -16,30 +21,33 @@ test('table should properly initialize timer config prop', async (t) => { const query = { error__exists: true, status: 'failed' }; const list = { meta: {}, - data: [] + data: [], }; + + const middlewares = [requestMiddleware, thunk]; + const mockStore = configureMockStore(middlewares); + const someStore = mockStore({ + timer: { running: false, seconds: -1 }, + datepicker: initialState(), + }); + const listWrapper = mount( - , + + + , { // We must disable lifecycle methods so that when we find the Timer its // properties will still be set to their initial values. Otherwise, a // lifecycle method might cause the properties to change and we will not // be able to check their initial values, which is what we want to do. disableLifecycleMethods: true, - context: { - store: { - subscribe: () => void 0, - dispatch: () => void 0, - getState: () => void 0 - } - } } ); diff --git a/test/middleware/request.js b/test/middleware/request.js index 13f621b86..cb588d12e 100644 --- a/test/middleware/request.js +++ b/test/middleware/request.js @@ -1,22 +1,42 @@ import test from 'ava'; import nock from 'nock'; +import sinon from 'sinon'; +import rewire from 'rewire'; import { CALL_API } from '../../app/src/js/actions/types'; -import { requestMiddleware } from '../../app/src/js/middleware/request'; +const request = rewire('../../app/src/js/middleware/request'); -const port = process.env.FAKEAPIPORT || 5001; +const token = 'fake-token'; -test.beforeEach((t) => { - const doDispatch = () => {}; - const doGetState = () => ({ - api: { - tokens: { - token: 'fake-token' - } +const requestMiddleware = request.__get__('requestMiddleware'); +const loginErrorStub = sinon.stub(); +request.__set__('loginError', () => { + loginErrorStub(); +}); + +const getStateStub = sinon.stub().callsFake(() => ({ + api: { + tokens: { + token } - }); - t.context.nextHandler = requestMiddleware({dispatch: doDispatch, getState: doGetState}); + } +})); +const dispatchStub = sinon.stub(); +const nextStub = sinon.stub(); + +const createTestMiddleware = () => { + const store = { + getState: getStateStub, + dispatch: dispatchStub + }; + const next = nextStub; + + const invokeMiddleware = action => requestMiddleware(store)(next)(action); + return { store, next, invokeMiddleware }; +}; + +test.beforeEach((t) => { t.context.defaultConfig = { json: true, resolveWithFullResponse: true, @@ -24,75 +44,77 @@ test.beforeEach((t) => { }; t.context.expectedHeaders = { - Authorization: 'Bearer fake-token', + Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' }; }); -test('should pass action to next if not an API request action', (t) => { - const actionObj = {}; +test.afterEach.always(() => { + nock.cleanAll(); + dispatchStub.resetHistory(); + getStateStub.resetHistory(); + nextStub.resetHistory(); + loginErrorStub.resetHistory(); +}); - const actionHandler = t.context.nextHandler(action => { - t.deepEqual(action, actionObj); - }); +test.serial('should pass action to next if not an API request action', (t) => { + const actionObj = { + type: 'ANYTHING', + some: 'action' + }; - actionHandler(actionObj); + const { next, invokeMiddleware } = createTestMiddleware(); + invokeMiddleware(actionObj); + + t.deepEqual(next.firstCall.args[0], actionObj); }); -test('should throw error if no method is set on API request action', (t) => { +test.serial('should throw error if no method is set on API request action', (t) => { const actionObj = { [CALL_API]: {} }; - const actionHandler = t.context.nextHandler(() => {}); + const { invokeMiddleware } = createTestMiddleware(); - try { - actionHandler(actionObj); - t.fail('Expected error to be thrown'); - } catch (err) { - t.is(err.message, 'Request action must include a method'); - } + t.throws( + () => invokeMiddleware(actionObj), + { message: 'Request action must include a method' } + ); }); -test.cb('should add correct authorization headers to API request action', (t) => { - nock(`http://localhost:${port}`, { - reqheaders: { - 'Authorization': 'Bearer fake-token' - } - }) - .get('/test-path') - .reply(200); - +test.serial('should add correct authorization headers to API request action', async (t) => { const requestAction = { type: 'TEST', method: 'GET', - url: `http://localhost:${port}/test-path` + url: 'http://anyhost' }; const actionObj = { [CALL_API]: requestAction }; - const actionHandler = t.context.nextHandler(action => { - t.deepEqual(action.config.headers.Authorization, 'Bearer fake-token'); - t.end(); + const requestPromiseStub = sinon.stub().resolves({ + body: {}, + statusCode: 200 }); + const revertRequestStub = request.__set__('requestPromise', requestPromiseStub); - actionHandler(actionObj); -}); + try { + const { invokeMiddleware } = createTestMiddleware(); -test.cb('should be able to use provided authorization headers', (t) => { - nock(`http://localhost:${port}`, { - reqheaders: { - 'Authorization': 'Bearer another-token' - } - }) - .get('/test-path') - .reply(200); + await invokeMiddleware(actionObj); + + const nextAction = requestPromiseStub.firstCall.args[0]; + t.deepEqual(nextAction.headers.Authorization, 'Bearer fake-token'); + } finally { + revertRequestStub(); + } +}); +test.serial('should be able to use provided authorization headers', async (t) => { const requestAction = { type: 'TEST', method: 'GET', - url: `http://localhost:${port}/test-path`, + url: 'http://anyhost', skipAuth: true, headers: { Authorization: 'Bearer another-token' @@ -102,23 +124,35 @@ test.cb('should be able to use provided authorization headers', (t) => { [CALL_API]: requestAction }; - const actionHandler = t.context.nextHandler(action => { - t.deepEqual(action.config.headers.Authorization, 'Bearer another-token'); - t.end(); + const requestPromiseStub = sinon.stub().resolves({ + body: {}, + statusCode: 200 }); + const revertRequestStub = request.__set__('requestPromise', requestPromiseStub); - actionHandler(actionObj); + try { + const { invokeMiddleware } = createTestMiddleware(); + + await invokeMiddleware(actionObj); + + const nextAction = requestPromiseStub.firstCall.args[0]; + t.deepEqual(nextAction.headers.Authorization, 'Bearer another-token'); + } finally { + revertRequestStub(); + } }); -test.cb('should dispatch error action for failed request', (t) => { - nock(`http://localhost:${port}`) +test.serial('should dispatch error action for failed request', async (t) => { + nock('http://anyhost') .get('/test-path') .reply(500, { message: 'Internal server error' }); + const { next, invokeMiddleware } = createTestMiddleware(); + const requestAction = { type: 'TEST', method: 'GET', - url: `http://localhost:${port}/test-path` + url: 'http://anyhost/test-path' }; const actionObj = { [CALL_API]: requestAction @@ -135,25 +169,61 @@ test.cb('should dispatch error action for failed request', (t) => { type: 'TEST_ERROR' }; - const actionHandler = t.context.nextHandler(action => { - t.deepEqual(action, expectedAction); - t.end(); - }); + await invokeMiddleware(actionObj); + t.deepEqual(next.firstCall.args[0], expectedAction); +}); - actionHandler(actionObj); +test.serial('should dispatch login error action for 401 response', async (t) => { + nock('http://anyhost') + .get('/test-path') + .reply(401, { message: 'Unauthorized' }); + + const { invokeMiddleware } = createTestMiddleware(); + + const requestAction = { + type: 'TEST', + method: 'GET', + url: 'http://anyhost/test-path' + }; + const actionObj = { + [CALL_API]: requestAction + }; + + await invokeMiddleware(actionObj); + t.true(loginErrorStub.called); }); -test.cb('should return expected action for GET request action', (t) => { +test.serial('should dispatch login error action for 403 response', async (t) => { + nock('http://anyhost') + .get('/test-path') + .reply(403, { message: 'Forbidden' }); + + const { invokeMiddleware } = createTestMiddleware(); + + const requestAction = { + type: 'TEST', + method: 'GET', + url: 'http://anyhost/test-path' + }; + const actionObj = { + [CALL_API]: requestAction + }; + + await invokeMiddleware(actionObj); + t.true(loginErrorStub.called); +}); + +test.serial('should return expected action for GET request action', async (t) => { const stubbedResponse = { message: 'success' }; - nock(`http://localhost:${port}`) + nock('http://anyhost') .get('/test-path') .reply(200, stubbedResponse); const requestAction = { type: 'TEST', method: 'GET', - url: `http://localhost:${port}/test-path` + url: 'http://anyhost/test-path' }; const actionObj = { [CALL_API]: requestAction @@ -170,22 +240,21 @@ test.cb('should return expected action for GET request action', (t) => { data: stubbedResponse }; - const actionHandler = t.context.nextHandler(action => { - t.deepEqual(action, expectedAction); - t.end(); - }); + const { next, invokeMiddleware } = createTestMiddleware(); + + await invokeMiddleware(actionObj); - actionHandler(actionObj); + t.deepEqual(next.firstCall.args[0], expectedAction); }); -test.cb('should return expected action for GET request action with query state', (t) => { +test.serial('should return expected action for GET request action with query state', async (t) => { const queryParams = { limit: 1, otherParam: 'value' }; const stubbedResponse = { message: 'success' }; - nock(`http://localhost:${port}`) + nock('http://anyhost') .get('/test-path') .query(queryParams) .reply(200, stubbedResponse); @@ -193,7 +262,7 @@ test.cb('should return expected action for GET request action with query state', const requestAction = { type: 'TEST', method: 'GET', - url: `http://localhost:${port}/test-path`, + url: 'http://anyhost/test-path', qs: queryParams }; const actionObj = { @@ -211,16 +280,15 @@ test.cb('should return expected action for GET request action with query state', data: stubbedResponse }; - const actionHandler = t.context.nextHandler(action => { - t.deepEqual(action, expectedAction); - t.end(); - }); + const { next, invokeMiddleware } = createTestMiddleware(); + + await invokeMiddleware(actionObj); - actionHandler(actionObj); + t.deepEqual(next.firstCall.args[0], expectedAction); }); -test.cb('should return expected action for POST request action', (t) => { - nock(`http://localhost:${port}`) +test.serial('should return expected action for POST request action', async (t) => { + nock('http://anyhost') .post('/test-path') .reply(200, (_, requestBody) => { return requestBody; @@ -230,7 +298,7 @@ test.cb('should return expected action for POST request action', (t) => { const requestAction = { type: 'TEST', method: 'POST', - url: `http://localhost:${port}/test-path`, + url: 'http://anyhost/test-path', body: requestBody }; const actionObj = { @@ -248,16 +316,15 @@ test.cb('should return expected action for POST request action', (t) => { data: requestBody }; - const actionHandler = t.context.nextHandler(action => { - t.deepEqual(action, expectedAction); - t.end(); - }); + const { next, invokeMiddleware } = createTestMiddleware(); + + await invokeMiddleware(actionObj); - actionHandler(actionObj); + t.deepEqual(next.firstCall.args[0], expectedAction); }); -test.cb('should return expected action for PUT request action', (t) => { - nock(`http://localhost:${port}`) +test.serial('should return expected action for PUT request action', async (t) => { + nock('http://anyhost') .put('/test-path') .reply(200, (_, requestBody) => { return requestBody; @@ -267,7 +334,7 @@ test.cb('should return expected action for PUT request action', (t) => { const requestAction = { type: 'TEST', method: 'PUT', - url: `http://localhost:${port}/test-path`, + url: 'http://anyhost/test-path', body: requestBody }; const actionObj = { @@ -285,24 +352,23 @@ test.cb('should return expected action for PUT request action', (t) => { data: requestBody }; - const actionHandler = t.context.nextHandler(action => { - t.deepEqual(action, expectedAction); - t.end(); - }); + const { next, invokeMiddleware } = createTestMiddleware(); + + await invokeMiddleware(actionObj); - actionHandler(actionObj); + t.deepEqual(next.firstCall.args[0], expectedAction); }); -test.cb('should return expected action for DELETE request action', (t) => { +test.serial('should return expected action for DELETE request action', async (t) => { const stubbedResponse = { message: 'success' }; - nock(`http://localhost:${port}`) + nock('http://anyhost') .delete('/test-path') .reply(200, stubbedResponse); const requestAction = { type: 'TEST', method: 'DELETE', - url: `http://localhost:${port}/test-path` + url: 'http://anyhost/test-path' }; const actionObj = { [CALL_API]: requestAction @@ -319,10 +385,9 @@ test.cb('should return expected action for DELETE request action', (t) => { data: stubbedResponse }; - const actionHandler = t.context.nextHandler(action => { - t.deepEqual(action, expectedAction); - t.end(); - }); + const { next, invokeMiddleware } = createTestMiddleware(); + + await invokeMiddleware(actionObj); - actionHandler(actionObj); + t.deepEqual(next.firstCall.args[0], expectedAction); }); diff --git a/test/reducers/datepicker.js b/test/reducers/datepicker.js index 1613f4d41..2b36a7cd8 100644 --- a/test/reducers/datepicker.js +++ b/test/reducers/datepicker.js @@ -16,16 +16,13 @@ test('verify initial state', (t) => { t.deepEqual(actual, inputstate); }); -test('reducer sets initial state to "Recent" on first initialization run.', (t) => { - const testStart = Date.now(); - sinon.useFakeTimers(testStart); +test('reducer sets initial state to have no set start/end times.', (t) => { const actual = reducer(undefined, { type: 'ANY' }); - t.is(actual.dateRange.label, 'Recent'); - t.is(actual.dateRange.value, 'Recent'); - t.true(actual.startDateTime.valueOf() - (new Date(testStart - msPerDay)).valueOf() < 1000); + t.is(actual.dateRange.label, 'Custom'); + t.is(actual.dateRange.value, 'Custom'); + t.is(actual.startDateTime, null); t.is(actual.endDateTime, null); t.is(actual.hourFormat, '12HR'); - sinon.restore(); }); test('Dropdown: "Recent" sets start time to 24 hours ago and unsets end time.', (t) => { @@ -52,7 +49,7 @@ test('Dropdown: "Recent" sets start time to 24 hours ago and unsets end time.', sinon.restore(); }); -test('Dropdown: "All" sets displayed start time to -infinity end time to infinity.', (t) => { +test('Dropdown: "All" sets displayed start time to -infinity and end time to infinity.', (t) => { const testNow = Date.now(); sinon.useFakeTimers(testNow); const value = 'All'; @@ -71,7 +68,7 @@ test('Dropdown: "All" sets displayed start time to -infinity end time to infini }); test('Dropdown: "Custom" unsets start and end times.', (t) => { - let state = initialState(); + const state = initialState(); state.startDateTime = new Date('2020-03-16T19:50:24.757Z'); state.endDateTime = new Date('2020-03-17T00:00:00.000Z'); const value = 'Custom'; diff --git a/test/reducers/dist.js b/test/reducers/dist.js index bff626e76..4e0c5d1c6 100644 --- a/test/reducers/dist.js +++ b/test/reducers/dist.js @@ -6,7 +6,7 @@ import { apiGatewayFixture } from '../fixtures/apiGatewayMetrics'; import { apiLambdaFixture } from '../fixtures/apiLambdaMetrics'; import { teaLambdaFixture } from '../fixtures/teaLambdaMetrics'; import { s3AccessFixture } from '../fixtures/s3AccessMetrics'; -import reducer from '../../app/src/js/reducers/dist'; +import reducer, { initialState } from '../../app/src/js/reducers/dist'; import { DIST_APIGATEWAY, DIST_APIGATEWAY_INFLIGHT, @@ -33,19 +33,18 @@ test.after((t) => { }); test('verify initial state', (t) => { - const initialState = { some: 'initialState' }; const newState = reducer(initialState, { data: {}, type: 'ANY' }); t.deepEqual(newState, initialState); }); test('reducers/dist/dist_apigateway', (t) => { - const initialState = {}; const action = { type: DIST_APIGATEWAY, data: JSON.parse(apiGatewayFixture) }; const expectedState = { + ...initialState, apiGateway: { inflight: false, error: null, @@ -65,16 +64,9 @@ test('reducers/dist/dist_apigateway', (t) => { }); test('reducers/dist/dist_apigateway_inflight', (t) => { - const initialState = {}; const action = { type: DIST_APIGATEWAY_INFLIGHT }; - - const expectedState = { - apiGateway: { - inflight: true - } - }; const newState = reducer(initialState, action); - t.deepEqual(expectedState, newState); + t.true(newState.apiGateway.inflight); }); test('reducers/dist/dist_apigateway_Error', (t) => { @@ -96,7 +88,6 @@ test('reducers/dist/dist_apigateway_Error', (t) => { }); test('reducers/dist/dist_apilambda', (t) => { - const initialState = {}; const action = { type: DIST_API_LAMBDA, data: JSON.parse(apiLambdaFixture) @@ -112,22 +103,15 @@ test('reducers/dist/dist_apilambda', (t) => { } }; const newState = reducer(initialState, action); - t.deepEqual(expectedState, newState); + t.deepEqual(expectedState.apiLambda, newState.apiLambda); }); test('reducers/dist/dist_apilambda_inflight', (t) => { - const initialState = {}; const action = { type: DIST_API_LAMBDA_INFLIGHT }; - - const expectedState = { - apiLambda: { - inflight: true - } - }; const newState = reducer(initialState, action); - t.deepEqual(expectedState, newState); + t.true(newState.apiLambda.inflight); }); test('reducers/dist/dist_apilambda_error', (t) => { @@ -152,11 +136,10 @@ test('reducers/dist/dist_apilambda_error', (t) => { } }; const newState = reducer(initialState, action); - t.deepEqual(expectedState, newState); + t.deepEqual(expectedState.apiLambda, newState.apiLambda); }); test('reducers/dist/dist_tea_lambda', (t) => { - const initialState = {}; const action = { type: DIST_TEA_LAMBDA, data: JSON.parse(teaLambdaFixture) @@ -172,22 +155,13 @@ test('reducers/dist/dist_tea_lambda', (t) => { } }; const newState = reducer(initialState, action); - t.deepEqual(expectedState, newState); + t.deepEqual(expectedState.teaLambda, newState.teaLambda); }); test('reducers/dist/dist_tea_lambda_inflight', (t) => { - const initialState = {}; - const action = { - type: DIST_TEA_LAMBDA_INFLIGHT - }; - - const expectedState = { - teaLambda: { - inflight: true - } - }; + const action = { type: DIST_TEA_LAMBDA_INFLIGHT }; const newState = reducer(initialState, action); - t.deepEqual(expectedState, newState); + t.true(newState.teaLambda.inflight); }); test('reducers/dist/dist_tea_lambda_error', (t) => { @@ -212,18 +186,17 @@ test('reducers/dist/dist_tea_lambda_error', (t) => { } }; const newState = reducer(initialState, action); - t.deepEqual(expectedState, newState); + t.deepEqual(expectedState.teaLambda, newState.teaLambda); }); - test('reducers/dist/dist_s3access', (t) => { - const initialState = {}; const action = { type: DIST_S3ACCESS, data: JSON.parse(s3AccessFixture) }; const expectedState = { + ...initialState, s3Access: { inflight: false, error: null, @@ -240,21 +213,20 @@ test('reducers/dist/dist_s3access_inflight', (t) => { const initialState = { apiLambda: { inflight: false - } - }; - const action = { - type: DIST_S3ACCESS_INFLIGHT + }, + s3Access: {} }; - const expectedState = { apiLambda: { - inflight: false + inflight: false, }, s3Access: { - inflight: true - } + inflight: true, + }, }; + const action = { type: DIST_S3ACCESS_INFLIGHT }; const newState = reducer(initialState, action); + t.deepEqual(expectedState, newState); }); @@ -280,5 +252,5 @@ test('reducers/dist/dist_s3access_error', (t) => { } }; const newState = reducer(initialState, action); - t.deepEqual(expectedState, newState); + t.deepEqual(expectedState.s3Access, newState.s3Access); }); diff --git a/test/reducers/granules.js b/test/reducers/granules.js new file mode 100644 index 000000000..1c3f78143 --- /dev/null +++ b/test/reducers/granules.js @@ -0,0 +1,30 @@ +'use strict'; + +import test from 'ava'; +import cloneDeep from 'lodash.clonedeep'; +import reducer, { initialState } from '../../app/src/js/reducers/granules'; +import { BULK_GRANULE } from '../../app/src/js/actions/types'; + +test('BULK_GRANULE', (t) => { + // case BULK_GRANULE: + // const { id, data, config } = action; + // set(state, ['bulk', config.requestId, 'data'], data); + // set(state, ['bulk', config.requestId, 'status'], 'success'); + // set(state, ['bulk', config.requestId, 'error'], null); + // break; + const inputState = cloneDeep(initialState); + const expected = cloneDeep(initialState); + expected.bulk = { + requestedId: { data: 'some Data', status: 'success', error: null } + }; + + const action = { + type: BULK_GRANULE, + config: { requestId: 'requestedId' }, + data: 'some Data' + }; + + const actual = reducer(inputState, action); + + t.deepEqual(expected, actual); +}); diff --git a/test/reducers/providers.js b/test/reducers/providers.js new file mode 100644 index 000000000..7dac51271 --- /dev/null +++ b/test/reducers/providers.js @@ -0,0 +1,22 @@ +'use strict'; + +import test from 'ava'; +import cloneDeep from 'lodash.clonedeep'; +import reducer, { initialState } from '../../app/src/js/reducers/providers'; +import { UPDATE_PROVIDER } from '../../app/src/js/actions/types'; + +test('UPDATE_PROVIDER reducer', (t) => { + const inputState = cloneDeep(initialState); + const expected = cloneDeep(initialState); + + expected.map = { someProviderName: { data: 'some important data' } }; + expected.updated = { someProviderName: { status: 'success' } }; + + const action = { + type: UPDATE_PROVIDER, + data: 'some important data', + id: 'someProviderName' + }; + const actual = reducer(inputState, action); + t.deepEqual(expected, actual); +}); diff --git a/test/reducers/reconciliation-reports.js b/test/reducers/reconciliation-reports.js new file mode 100644 index 000000000..9a50f0ebd --- /dev/null +++ b/test/reducers/reconciliation-reports.js @@ -0,0 +1,29 @@ +'use strict'; + +import test from 'ava'; +import cloneDeep from 'lodash.clonedeep'; +import reducer, { initialState } from '../../app/src/js/reducers/reconciliation-reports'; +import { + RECONCILIATIONS +} from '../../app/src/js/actions/types'; + +test('RECONCILIATIONS reducer', function (t) { + const reports = [ + { name: 'report-1' }, + { name: 'report-2' } + ]; + const inputState = cloneDeep(initialState); + const expected = cloneDeep(initialState); + expected.list.data = reports; + + const action = { + type: RECONCILIATIONS, + data: { + results: reports + } + }; + + const actual = reducer(inputState, action); + + t.deepEqual(expected.list.data, actual.list.data); +}); diff --git a/test/reducers/rules.js b/test/reducers/rules.js new file mode 100644 index 000000000..038e2cd0f --- /dev/null +++ b/test/reducers/rules.js @@ -0,0 +1,40 @@ +'use strict'; + +import test from 'ava'; +import cloneDeep from 'lodash.clonedeep'; +import reducer, { initialState } from '../../app/src/js/reducers/rules'; +import { + RULE_DISABLE_INFLIGHT, + RULE_ENABLE +} from '../../app/src/js/actions/types'; + +test('RULE_ENABLE reducer', (t) => { + const inputState = cloneDeep(initialState); + const id = 'someRuleNameHere'; + inputState.disabled = { + [id]: { status: 'anything', error: 'anything also' } + }; + const expected = cloneDeep(initialState); + expected.enabled = { + [id]: { status: 'success', error: null, data: undefined } + }; + + const action = { type: RULE_ENABLE, id }; + const actual = reducer(inputState, action); + t.falsy(actual.enabled.someRuleNameHere.data); + t.deepEqual(expected, actual); +}); + +test('RULE_DISABLE_INFLIGHT reducer', (t) => { + const inputState = cloneDeep(initialState); + const id = 'someRuleName'; + inputState.disabled = { [id]: { status: 'success' } }; + + const expected = cloneDeep(inputState); + expected.disabled[id].status = 'inflight'; + + const action = { type: RULE_DISABLE_INFLIGHT, id }; + const actual = reducer(inputState, action); + t.falsy(actual.disabled.someRuleName.error); + t.deepEqual(expected, actual); +}); diff --git a/test/reducers/test-removeDeleted.js b/test/reducers/test-removeDeleted.js index 77c354fd6..03333da60 100644 --- a/test/reducers/test-removeDeleted.js +++ b/test/reducers/test-removeDeleted.js @@ -3,7 +3,7 @@ import test from 'ava'; import { randomBytes } from 'crypto'; -import removeDeleted from '../../app/src/js/reducers/remove-deleted'; +import removeDeleted from '../../app/src/js/reducers/utils/remove-deleted'; const randomId = (id) => `${id}${randomBytes(5).toString('hex')}`; @@ -34,7 +34,7 @@ test('removeDeleted, returns filtered list if successful deleted found', (t) => const initialAccessor = 'accessorName'; const deletedId = initialList[1].accessorName; const expectedList = [initialList[0], initialList[2]]; - const deleted = {[deletedId]: {status: 'success'}}; + const deleted = { [deletedId]: { status: 'success' } }; const result = removeDeleted(initialAccessor, initialList, deleted); t.deepEqual(result, expectedList); }); diff --git a/test/reducers/timer.js b/test/reducers/timer.js new file mode 100644 index 000000000..7ee8bfbea --- /dev/null +++ b/test/reducers/timer.js @@ -0,0 +1,44 @@ +'use strict'; + +import test from 'ava'; +import reducer, { initialState } from '../../app/src/js/reducers/timer'; +import { + TIMER_START, + TIMER_SET_COUNTDOWN, + TIMER_STOP, +} from '../../app/src/js/actions/types'; + +test('initialState begins with timer off', (t) => { + const expected = { running: false, seconds: -1 }; + const actual = reducer(undefined, { type: 'ANY' }); + t.deepEqual(expected, actual); +}); + +test('TIMER_START', (t) => { + const inputState = { running: false, seconds: -1 }; + const countdownSeconds = 100; + const expected = { running: true, seconds: countdownSeconds }; + const action = { type: TIMER_START, secondsToRefresh: countdownSeconds }; + const actual = reducer(inputState, action); + t.deepEqual(expected, actual); +}); + +test('TIMER_STOP', (t) => { + const inputState = { running: true, seconds: 100 }; + const expected = { running: false, seconds: -1 }; + const action = { type: TIMER_STOP }; + const actual = reducer(inputState, action); + t.deepEqual(expected, actual); +}); + +test('TIMER_SET_COUNTDOWN', (t) => { + const inputState = { ...initialState }; + const countdownSeconds = 100; + const expected = { ...initialState, seconds: countdownSeconds }; + const action = { + type: TIMER_SET_COUNTDOWN, + secondsToRefresh: countdownSeconds, + }; + const actual = reducer(inputState, action); + t.deepEqual(expected, actual); +}); diff --git a/test/reducers/utils/reducer-creators.js b/test/reducers/utils/reducer-creators.js new file mode 100644 index 000000000..702eb8d71 --- /dev/null +++ b/test/reducers/utils/reducer-creators.js @@ -0,0 +1,129 @@ +'use strict'; + +import test from 'ava'; +import { + createInflightReducer, + createSuccessReducer, + createClearItemReducer, + createErrorReducer +} from '../../../app/src/js/reducers/utils/reducer-creators'; + +test('createInflightReducer, creates a working inflight reducer', (t) => { + const inputState = { key: 'value' }; + const stateProp = 'theStateProp'; + const id = 'SomeIdField'; + + const expected = { + ...inputState, + [stateProp]: { [id]: { status: 'inflight' } } + }; + const theInflightReducer = createInflightReducer(stateProp); + + const action = { id }; + theInflightReducer(inputState, action); + // Tests the mutated input state. + t.deepEqual(expected, inputState); +}); + +test('createSuccessReducer, creates a working success reducer', (t) => { + // Example success reducer + // (state, action) => { + // const { id } = action; + // set(state, ['rerun', id, 'status'], 'success'); + // set(state, ['rerun', id, 'error'], null); + // }; + + const inputState = { key: 'value' }; + + const stateProp = 'rerun'; + const id = 'someIdField'; + + const expected = { + key: 'value', + rerun: { someIdField: { status: 'success', error: null } } + }; + const theSuccessReducer = createSuccessReducer(stateProp); + + const action = { id }; + theSuccessReducer(inputState, action); + + // Tests the mutated input state. + // data is set to undefined in the success reducer when not part of the action. + t.falsy(inputState.rerun.someIdField.data); + delete inputState.rerun.someIdField.data; + t.deepEqual(expected, inputState); +}); + +test('createClearItemReducer, creates a working success reducer on missing values', (t) => { + const inputState = { key: 'value' }; + const theClearReducer = createClearItemReducer('doesnotexist'); + const action = { type: 'CLEAR_TYPE', id: 'what' }; + const expected = { ...inputState }; + + theClearReducer(inputState, action); + + t.deepEqual(expected, inputState); +}); + +test('createClearItemReducer, creates a working success reducer with existing values', (t) => { + const inputState = { key: 'value', exists: { first: 1, second: 2 } }; + const theClearReducer = createClearItemReducer('exists'); + const action = { type: 'CLEAR_TYPE', id: 'second' }; + const expected = { key: 'value', exists: { first: 1 } }; + + theClearReducer(inputState, action); + + t.deepEqual(expected, inputState); +}); + +test('createErrorReducer, creates valid error reducer', (t) => { + const inputState = { property: { someId: { status: 'inflight' } } }; + + const expected = { + property: { someId: { status: 'error', error: 'Bad Things Happened!' } } + }; + + const action = { error: 'Bad Things Happened!', id: 'someId' }; + + const theErrorRedcer = createErrorReducer('property'); + + theErrorRedcer(inputState, action); + + t.deepEqual(expected, inputState); +}); + +test('createErrorReducer, works with extra properties', (t) => { + const inputState = { property: { someWrongId: { status: 'inflight' } } }; + + const expected = { + property: { + someWrongId: { status: 'inflight' }, + someId: { status: 'error', error: 'Bad Things Happened!' } + } + }; + + const action = { error: 'Bad Things Happened!', id: 'someId' }; + + const theErrorRedcer = createErrorReducer('property'); + + theErrorRedcer(inputState, action); + + t.deepEqual(expected, inputState); +}); + +test('createErrorReducer, works with missing properties', (t) => { + const inputState = { wrongProperty: { someId: { status: 'inflight' } } }; + + const expected = { + wrongProperty: { someId: { status: 'inflight' } }, + property: { someId: { status: 'error', error: 'Bad Things Happened!' } } + }; + + const action = { error: 'Bad Things Happened!', id: 'someId' }; + + const theErrorRedcer = createErrorReducer('property'); + + theErrorRedcer(inputState, action); + + t.deepEqual(expected, inputState); +}); diff --git a/test/utils/kibana.js b/test/utils/kibana.js index 4a4f97516..6e6130fdc 100644 --- a/test/utils/kibana.js +++ b/test/utils/kibana.js @@ -122,14 +122,14 @@ test('kibanaGatewayExecutionSuccessesLink() will return a Kibana link to query f }); test('kibanaGatewayAccessErrorsLink() will return a Kibana link to query for Gateway Access Errors', function (t) { - const expectedLink = "http://example.com/app/kibana#/discover?_g=(refreshInterval:(pause:!t,value:0),time:(from:'1970-01-01T00:00:00.000Z',to:now))&_a=(columns:!(message),filters:!(('$state':(store:appState),meta:(alias:!n,disabled:!f,index:cumulus-stack,key:_index,negate:!f,params:(query:'cumulus-stack-cloudwatch*',type:phrase),type:phrase,value:'cumulus-stack-cloudwatch*'),query:(match:(_index:(query:'cumulus-stack-cloudwatch*',type:phrase)))),('$state':(store:appState),meta:(alias:!n,disabled:!f,index:cumulus-stack,key:logGroup,negate:!f,params:(query:'%22API%5C-Gateway%5C-Execution*%22',type:phrase),type:phrase,value:'%22API%5C-Gateway%5C-Execution*%22'),query:(match:(logGroup:(query:'%22API%5C-Gateway%5C-Execution*%22',type:phrase))))),index:cumulus-stack,interval:auto,query:(language:lucene,query:'status:%5B400%20TO%20599%5D'),sort:!('@timestamp',desc))"; + const expectedLink = "http://example.com/app/kibana#/discover?_g=(refreshInterval:(pause:!t,value:0),time:(from:'1970-01-01T00:00:00.000Z',to:now))&_a=(columns:!(message),filters:!(('$state':(store:appState),meta:(alias:!n,disabled:!f,index:cumulus-stack,key:_index,negate:!f,params:(query:'cumulus-stack-cloudwatch*',type:phrase),type:phrase,value:'cumulus-stack-cloudwatch*'),query:(match:(_index:(query:'cumulus-stack-cloudwatch*',type:phrase)))),('$state':(store:appState),meta:(alias:!n,disabled:!f,index:cumulus-stack,key:logGroup,negate:!f,params:(query:'%22API%5C-Gateway%5C-Execution*%22',type:phrase),type:phrase,value:'%22API%5C-Gateway%5C-Execution*%22'),query:(match:(logGroup:(query:'%22API%5C-Gateway%5C-Execution*%22',type:phrase))))),index:cumulus-stack,interval:auto,query:(language:lucene,query:'statusCode:%5B400%20TO%20599%5D'),sort:!('@timestamp',desc))"; const kibanaLink = kibanaGatewayAccessErrorsLink(cumulusInstanceMeta, {}); t.is(kibanaLink, expectedLink); }); test('kibanaGatewayAccessSuccessesLink() will return a Kibana link to query for Gateway Access Successes', function (t) { - const expectedLink = "http://example.com/app/kibana#/discover?_g=(refreshInterval:(pause:!t,value:0),time:(from:'1970-01-01T00:00:00.000Z',to:now))&_a=(columns:!(message),filters:!(('$state':(store:appState),meta:(alias:!n,disabled:!f,index:cumulus-stack,key:_index,negate:!f,params:(query:'cumulus-stack-cloudwatch*',type:phrase),type:phrase,value:'cumulus-stack-cloudwatch*'),query:(match:(_index:(query:'cumulus-stack-cloudwatch*',type:phrase)))),('$state':(store:appState),meta:(alias:!n,disabled:!f,index:cumulus-stack,key:logGroup,negate:!f,params:(query:'%22API%5C-Gateway%5C-Execution*%22',type:phrase),type:phrase,value:'%22API%5C-Gateway%5C-Execution*%22'),query:(match:(logGroup:(query:'%22API%5C-Gateway%5C-Execution*%22',type:phrase))))),index:cumulus-stack,interval:auto,query:(language:lucene,query:'status:%5B200%20TO%20399%5D'),sort:!('@timestamp',desc))"; + const expectedLink = "http://example.com/app/kibana#/discover?_g=(refreshInterval:(pause:!t,value:0),time:(from:'1970-01-01T00:00:00.000Z',to:now))&_a=(columns:!(message),filters:!(('$state':(store:appState),meta:(alias:!n,disabled:!f,index:cumulus-stack,key:_index,negate:!f,params:(query:'cumulus-stack-cloudwatch*',type:phrase),type:phrase,value:'cumulus-stack-cloudwatch*'),query:(match:(_index:(query:'cumulus-stack-cloudwatch*',type:phrase)))),('$state':(store:appState),meta:(alias:!n,disabled:!f,index:cumulus-stack,key:logGroup,negate:!f,params:(query:'%22API%5C-Gateway%5C-Execution*%22',type:phrase),type:phrase,value:'%22API%5C-Gateway%5C-Execution*%22'),query:(match:(logGroup:(query:'%22API%5C-Gateway%5C-Execution*%22',type:phrase))))),index:cumulus-stack,interval:auto,query:(language:lucene,query:'statusCode:%5B200%20TO%20399%5D'),sort:!('@timestamp',desc))"; const kibanaLink = kibanaGatewayAccessSuccessesLink(cumulusInstanceMeta, {}); t.is(kibanaLink, expectedLink);