From ad3e68153424568d679d8b606666a157812d0824 Mon Sep 17 00:00:00 2001 From: Basit Date: Tue, 23 Aug 2022 21:27:34 +0200 Subject: [PATCH 01/12] feat(indexes): refresh indexes --- package-lock.json | 4 - packages/compass-indexes/package.json | 2 - .../create-index-modal/create-index-modal.tsx | 2 +- .../indexes-table/indexes-table.spec.tsx | 8 +- .../indexes-table/indexes-table.tsx | 41 ++-- .../components/indexes-table/name-field.tsx | 4 +- .../indexes-table/property-field.tsx | 12 +- .../components/indexes-table/type-field.tsx | 10 +- .../indexes-toolbar/indexes-toolbar.spec.tsx | 2 + .../indexes-toolbar/indexes-toolbar.tsx | 25 ++ .../src/components/indexes/indexes.spec.tsx | 3 + .../src/components/indexes/indexes.tsx | 20 +- .../src/modules/create-index/index.ts | 3 +- .../src/modules/description.js | 2 - .../src/modules/drop-index/index.js | 3 +- packages/compass-indexes/src/modules/error.ts | 89 ++++--- .../src/modules/{index.js => index.ts} | 21 +- .../compass-indexes/src/modules/indexes.js | 224 ----------------- .../src/modules/indexes.spec.js | 10 +- .../compass-indexes/src/modules/indexes.ts | 229 ++++++++++++++++++ .../src/modules/is-readonly.js | 4 - .../src/modules/is-refreshing.ts | 46 ++++ .../src/modules/sort-column.js | 23 -- .../src/modules/sort-column.ts | 24 ++ .../compass-indexes/src/modules/sort-order.js | 23 -- .../compass-indexes/src/modules/sort-order.ts | 24 ++ .../src/stores/create-index.js | 3 +- .../compass-indexes/src/stores/drop-index.js | 3 +- packages/compass-indexes/src/stores/store.js | 10 +- packages/compass-indexes/src/typings.d.ts | 4 + 30 files changed, 477 insertions(+), 401 deletions(-) rename packages/compass-indexes/src/modules/{index.js => index.ts} (84%) delete mode 100644 packages/compass-indexes/src/modules/indexes.js create mode 100644 packages/compass-indexes/src/modules/indexes.ts create mode 100644 packages/compass-indexes/src/modules/is-refreshing.ts delete mode 100644 packages/compass-indexes/src/modules/sort-column.js create mode 100644 packages/compass-indexes/src/modules/sort-column.ts delete mode 100644 packages/compass-indexes/src/modules/sort-order.js create mode 100644 packages/compass-indexes/src/modules/sort-order.ts diff --git a/package-lock.json b/package-lock.json index c102a834a70..ecc239218a2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -70008,8 +70008,6 @@ "hadron-app": "^5.0.0", "hadron-app-registry": "^9.0.0", "lodash.contains": "^2.4.3", - "lodash.map": "^4.6.0", - "lodash.max": "^4.0.1", "mocha": "^8.4.0", "mongodb": "^4.6.0", "mongodb-data-service": "^22.0.0", @@ -107550,8 +107548,6 @@ "hadron-app-registry": "^9.0.0", "hadron-react-components": "^6.0.0", "lodash.contains": "^2.4.3", - "lodash.map": "^4.6.0", - "lodash.max": "^4.0.1", "mocha": "^8.4.0", "mongodb": "^4.6.0", "mongodb-data-service": "^22.0.0", diff --git a/packages/compass-indexes/package.json b/packages/compass-indexes/package.json index 0abd12a4946..10124d521d1 100644 --- a/packages/compass-indexes/package.json +++ b/packages/compass-indexes/package.json @@ -80,8 +80,6 @@ "hadron-app": "^5.0.0", "hadron-app-registry": "^9.0.0", "lodash.contains": "^2.4.3", - "lodash.map": "^4.6.0", - "lodash.max": "^4.0.1", "mocha": "^8.4.0", "mongodb": "^4.6.0", "mongodb-data-service": "^22.0.0", diff --git a/packages/compass-indexes/src/components/create-index-modal/create-index-modal.tsx b/packages/compass-indexes/src/components/create-index-modal/create-index-modal.tsx index 5035ac206d9..b712074953d 100644 --- a/packages/compass-indexes/src/components/create-index-modal/create-index-modal.tsx +++ b/packages/compass-indexes/src/components/create-index-modal/create-index-modal.tsx @@ -110,7 +110,7 @@ function CreateIndexModal({ resetForm: () => void; isVisible: boolean; namespace: string; - error?: string; + error: string | null; clearError: () => void; inProgress: boolean; createIndex: () => void; diff --git a/packages/compass-indexes/src/components/indexes-table/indexes-table.spec.tsx b/packages/compass-indexes/src/components/indexes-table/indexes-table.spec.tsx index b569c9af300..da557dffa67 100644 --- a/packages/compass-indexes/src/components/indexes-table/indexes-table.spec.tsx +++ b/packages/compass-indexes/src/components/indexes-table/indexes-table.spec.tsx @@ -5,9 +5,9 @@ import userEvent from '@testing-library/user-event'; import { spy } from 'sinon'; import { IndexesTable } from './indexes-table'; -import type { IndexModel } from './indexes-table'; +import type { IndexDefinition } from '../../modules/indexes'; -const indexes: IndexModel[] = [ +const indexes: IndexDefinition[] = [ { cardinality: 'single', name: '_id_', @@ -26,6 +26,7 @@ const indexes: IndexModel[] = [ ]; }, }, + usageCount: 10, }, { cardinality: 'compound', @@ -49,6 +50,7 @@ const indexes: IndexModel[] = [ ]; }, }, + usageCount: 15, }, { cardinality: 'compound', @@ -73,6 +75,7 @@ const indexes: IndexModel[] = [ ]; }, }, + usageCount: 20, }, { cardinality: 'single', @@ -97,6 +100,7 @@ const indexes: IndexModel[] = [ ]; }, }, + usageCount: 25, }, ]; diff --git a/packages/compass-indexes/src/components/indexes-table/indexes-table.tsx b/packages/compass-indexes/src/components/indexes-table/indexes-table.tsx index 58107d05988..7f6ad7c409b 100644 --- a/packages/compass-indexes/src/components/indexes-table/indexes-table.tsx +++ b/packages/compass-indexes/src/components/indexes-table/indexes-table.tsx @@ -15,6 +15,11 @@ import SizeField from './size-field'; import UsageField from './usage-field'; import PropertyField from './property-field'; import DropField from './drop-field'; +import type { + IndexDefinition, + SortColumn, + SortDirection, +} from '../../modules/indexes'; // When row is hovered, we show the delete button const rowStyles = css({ @@ -53,27 +58,11 @@ const nameFieldStyles = css({ paddingBottom: spacing[2], }); -// todo: move to redux store when converting that to ts -export type IndexModel = { - name: string; - fields: { - serialize: () => { field: string; value: number | string }[]; - }; - type: 'geo' | 'hashed' | 'text' | 'wildcard' | 'clustered' | 'columnstore'; - cardinality: 'single' | 'compound'; - properties: ('unique' | 'sparse' | 'partial' | 'ttl' | 'collation')[]; - extra: Record>; - size: number; - relativeSize: number; - usageCount?: number; - usageSince?: Date; -}; - type IndexesTableProps = { darkMode?: boolean; - indexes: IndexModel[]; + indexes: IndexDefinition[]; canDeleteIndex: boolean; - onSortTable: (name: string, direction: 'asc' | 'desc') => void; + onSortTable: (column: SortColumn, direction: SortDirection) => void; onDeleteIndex: (name: string) => void; }; @@ -84,13 +73,15 @@ export const IndexesTable: React.FunctionComponent = ({ onDeleteIndex, }) => { const columns = useMemo(() => { - const _columns = [ - 'Name and Definition', - 'Type', - 'Size', - 'Usage', - 'Properties', - ].map((name) => { + const _columns = ( + [ + 'Name and Definition', + 'Type', + 'Size', + 'Usage', + 'Properties', + ] as SortColumn[] + ).map((name) => { return ( ; + keys: ReturnType; }; const NameField: React.FunctionComponent = ({ name, keys }) => { diff --git a/packages/compass-indexes/src/components/indexes-table/property-field.tsx b/packages/compass-indexes/src/components/indexes-table/property-field.tsx index 49bf53dc9af..8a644aa4702 100644 --- a/packages/compass-indexes/src/components/indexes-table/property-field.tsx +++ b/packages/compass-indexes/src/components/indexes-table/property-field.tsx @@ -2,7 +2,7 @@ import React from 'react'; import getIndexHelpLink from '../../utils/index-link-helper'; import { spacing, css, Tooltip, Body } from '@mongodb-js/compass-components'; -import type { IndexModel } from './indexes-table'; +import type { IndexDefinition } from '../../modules/indexes'; import BadgeWithIconLink from './badge-with-icon-link'; const containerStyles = css({ @@ -19,8 +19,8 @@ const ttlTooltip = (expireAfterSeconds: number) => { }; export const getPropertyTooltip = ( - property: IndexModel['properties'][0], - extra: IndexModel['extra'] + property: IndexDefinition['properties'][0], + extra: IndexDefinition['extra'] ): string | null => { return property === 'ttl' ? ttlTooltip(extra.expireAfterSeconds as number) @@ -50,9 +50,9 @@ const PropertyBadgeWithTooltip: React.FunctionComponent<{ }; type PropertyFieldProps = { - extra: IndexModel['extra']; - properties: IndexModel['properties']; - cardinality: IndexModel['cardinality']; + extra: IndexDefinition['extra']; + properties: IndexDefinition['properties']; + cardinality: IndexDefinition['cardinality']; }; const PropertyField: React.FunctionComponent = ({ diff --git a/packages/compass-indexes/src/components/indexes-table/type-field.tsx b/packages/compass-indexes/src/components/indexes-table/type-field.tsx index d0a6381bc30..2f954b45450 100644 --- a/packages/compass-indexes/src/components/indexes-table/type-field.tsx +++ b/packages/compass-indexes/src/components/indexes-table/type-field.tsx @@ -2,20 +2,20 @@ import React from 'react'; import getIndexHelpLink from '../../utils/index-link-helper'; import { Tooltip, Body } from '@mongodb-js/compass-components'; -import type { IndexModel } from './indexes-table'; +import type { IndexDefinition } from '../../modules/indexes'; import BadgeWithIconLink from './badge-with-icon-link'; -export const canRenderTooltip = (type: IndexModel['type']) => { +export const canRenderTooltip = (type: IndexDefinition['type']) => { return ['text', 'wildcard', 'columnstore'].indexOf(type) !== -1; }; type TypeFieldProps = { - type: IndexModel['type']; - extra: IndexModel['extra']; + type: IndexDefinition['type']; + extra: IndexDefinition['extra']; }; export const IndexTypeTooltip: React.FunctionComponent<{ - extra: IndexModel['extra']; + extra: IndexDefinition['extra']; }> = ({ extra }) => { const allowedProps = [ 'weights', diff --git a/packages/compass-indexes/src/components/indexes-toolbar/indexes-toolbar.spec.tsx b/packages/compass-indexes/src/components/indexes-toolbar/indexes-toolbar.spec.tsx index dfd2a0af0c8..8fd210b1735 100644 --- a/packages/compass-indexes/src/components/indexes-toolbar/indexes-toolbar.spec.tsx +++ b/packages/compass-indexes/src/components/indexes-toolbar/indexes-toolbar.spec.tsx @@ -20,6 +20,8 @@ const renderIndexesToolbar = ( isWritable={true} localAppRegistry={appRegistry} writeStateDescription={undefined} + onRefreshIndexes={() => {}} + isRefreshing={false} {...props} /> ); diff --git a/packages/compass-indexes/src/components/indexes-toolbar/indexes-toolbar.tsx b/packages/compass-indexes/src/components/indexes-toolbar/indexes-toolbar.tsx index 3c10a168f73..d70034af53f 100644 --- a/packages/compass-indexes/src/components/indexes-toolbar/indexes-toolbar.tsx +++ b/packages/compass-indexes/src/components/indexes-toolbar/indexes-toolbar.tsx @@ -8,6 +8,8 @@ import { css, mergeProps, spacing, + Icon, + SpinLoader, } from '@mongodb-js/compass-components'; import type AppRegistry from 'hadron-app-registry'; @@ -23,6 +25,8 @@ const toolbarButtonsContainer = css({ justifyContent: 'flex-end', }); +const spinnerStyles = css({ marginRight: spacing[2] }); + const createIndexButtonContainerStyles = css({ display: 'inline-block', width: 'fit-content', @@ -34,7 +38,9 @@ type IndexesToolbarProps = { isReadonlyView: boolean; isWritable: boolean; localAppRegistry: AppRegistry; + isRefreshing: boolean; writeStateDescription?: string; + onRefreshIndexes: () => void; }; export const IndexesToolbar: React.FunctionComponent = ({ @@ -43,17 +49,36 @@ export const IndexesToolbar: React.FunctionComponent = ({ isReadonlyView, isWritable, localAppRegistry, + isRefreshing, writeStateDescription, + onRefreshIndexes, }) => { const onClickCreateIndex = useCallback(() => { localAppRegistry.emit('toggle-create-index-modal', true); }, [localAppRegistry]); const showCreateIndexButton = !isReadonly && !isReadonlyView && !errorMessage; + const refreshButtonIcon = isRefreshing ? ( +
+ +
+ ) : ( + + ); return (
+ {showCreateIndexButton && ( {}} + onRefresh={() => {}} {...props} /> ); @@ -79,6 +81,7 @@ describe('Indexes Component', function () { ]; }, }, + usageCount: 20, }, ], isReadonlyView: false, diff --git a/packages/compass-indexes/src/components/indexes/indexes.tsx b/packages/compass-indexes/src/components/indexes/indexes.tsx index 56c2e88c653..2f5c905e4d3 100644 --- a/packages/compass-indexes/src/components/indexes/indexes.tsx +++ b/packages/compass-indexes/src/components/indexes/indexes.tsx @@ -4,10 +4,15 @@ import { connect } from 'react-redux'; import type AppRegistry from 'hadron-app-registry'; import { sortIndexes } from '../../modules/indexes'; +import type { + IndexDefinition, + SortColumn, + SortDirection, +} from '../../modules/indexes'; import { IndexesToolbar } from '../indexes-toolbar/indexes-toolbar'; import { IndexesTable } from '../indexes-table/indexes-table'; -import type { IndexModel } from '../indexes-table/indexes-table'; +import { refreshIndexes } from '../../modules/is-refreshing'; const containerStyles = css({ margin: spacing[3], @@ -30,14 +35,16 @@ const indexTableStyles = css({ }); type IndexesProps = { - indexes: IndexModel[]; + indexes: IndexDefinition[]; isWritable: boolean; isReadonly: boolean; isReadonlyView: boolean; description?: string; error?: string; localAppRegistry: AppRegistry; - onSortTable: (name: string, direction: 'asc' | 'desc') => void; + isRefreshing: boolean; + onSortTable: (name: SortColumn, direction: SortDirection) => void; + onRefresh: () => void; }; export const Indexes: React.FunctionComponent = ({ @@ -48,7 +55,9 @@ export const Indexes: React.FunctionComponent = ({ description, error, localAppRegistry, + isRefreshing, onSortTable, + onRefresh, }) => { const onDeleteIndex = (name: string) => { return localAppRegistry.emit('toggle-drop-index-modal', true, name); @@ -62,7 +71,9 @@ export const Indexes: React.FunctionComponent = ({ isReadonlyView={isReadonlyView} errorMessage={error} localAppRegistry={localAppRegistry} + isRefreshing={isRefreshing} writeStateDescription={description} + onRefreshIndexes={() => onRefresh()} />
{!isReadonlyView && !error && ( @@ -86,6 +97,7 @@ const mapState = ({ isReadonlyView, description, error, + isRefreshing, appRegistry: { localAppRegistry }, }: any) => ({ indexes, @@ -95,10 +107,12 @@ const mapState = ({ description, error, localAppRegistry, + isRefreshing, }); const mapDispatch = { onSortTable: sortIndexes, + onRefresh: refreshIndexes, }; export default connect(mapState, mapDispatch)(Indexes as any); diff --git a/packages/compass-indexes/src/modules/create-index/index.ts b/packages/compass-indexes/src/modules/create-index/index.ts index fde3a429831..d9a169082d9 100644 --- a/packages/compass-indexes/src/modules/create-index/index.ts +++ b/packages/compass-indexes/src/modules/create-index/index.ts @@ -71,7 +71,6 @@ import schemaFields from '../create-index/schema-fields'; import newIndexField from '../create-index/new-index-field'; import { RESET_FORM } from '../reset-form'; import { RESET, reset } from '../reset'; -import { parseErrorMsg } from '../indexes'; const { track } = createLoggerAndTelemetry('COMPASS-INDEXES-UI'); @@ -277,7 +276,7 @@ export const createIndex = () => { dispatch(toggleIsVisible(false)); } else { dispatch(toggleInProgress(false)); - dispatch(handleError(parseErrorMsg(createErr))); + dispatch(handleError(createErr)); } }); }; diff --git a/packages/compass-indexes/src/modules/description.js b/packages/compass-indexes/src/modules/description.js index ffaa6ca2bbb..3a5bf124d3e 100644 --- a/packages/compass-indexes/src/modules/description.js +++ b/packages/compass-indexes/src/modules/description.js @@ -18,8 +18,6 @@ export const INITIAL_STATE = 'Topology type not yet discovered.'; * * @param {Boolean} state - The status state. * @param {Object} action - The action. - * - * @returns {Boolean} The new state. */ const reducer = (state = INITIAL_STATE, action) => { if (action.type === GET_DESCRIPTION) { diff --git a/packages/compass-indexes/src/modules/drop-index/index.js b/packages/compass-indexes/src/modules/drop-index/index.js index df6491017af..8fc55e35fc0 100644 --- a/packages/compass-indexes/src/modules/drop-index/index.js +++ b/packages/compass-indexes/src/modules/drop-index/index.js @@ -25,7 +25,6 @@ import confirmName, { import { RESET_FORM } from '../reset-form'; import { RESET, reset } from '../reset'; -import { parseErrorMsg } from '../indexes'; import namespace from '../namespace'; const { track } = createLoggerAndTelemetry('COMPASS-INDEXES-UI'); @@ -90,7 +89,7 @@ export const dropIndex = (indexName) => { dispatch(toggleIsVisible(false)); } else { dispatch(toggleInProgress(false)); - dispatch(handleError(parseErrorMsg(err))); + dispatch(handleError(err)); } }); }; diff --git a/packages/compass-indexes/src/modules/error.ts b/packages/compass-indexes/src/modules/error.ts index 3ca559e31f6..2dbbe02a030 100644 --- a/packages/compass-indexes/src/modules/error.ts +++ b/packages/compass-indexes/src/modules/error.ts @@ -1,59 +1,58 @@ -import type { AnyAction } from 'redux'; +import { MongoError } from 'mongodb'; -/** - * The action name prefix. - */ -const PREFIX = 'indexes/error'; +export type IndexesError = MongoError | Error | string | null | undefined; -/** - * Handle error action name. - */ -export const HANDLE_ERROR = `${PREFIX}/HANDLE_ERROR`; +enum ActionTypes { + HandleError = 'indexes/error/HANDLE_ERROR', + ClearError = 'indexes/error/CLEAR_ERROR', +} -/** - * Clear error action name. - */ -export const CLEAR_ERROR = `${PREFIX}/CLEAR_ERROR`; +export type HandleErrorAction = { + type: ActionTypes.HandleError; + error: IndexesError; +}; -/** - * The initial state of the error. - */ -export const INITIAL_STATE = null; +type ClearErrorAction = { + type: ActionTypes.ClearError; +}; -/** - * Reducer function for handle state changes to errors. - * - * @param {Error} state - The error state. - * @param {Object} action - The action. - * - * @returns {Error} The new state. - */ -export default function reducer(state = INITIAL_STATE, action: AnyAction) { - if (action.type === HANDLE_ERROR) { - return action.error; - } else if (action.type === CLEAR_ERROR) { - return null; +export type Actions = HandleErrorAction | ClearErrorAction; + +type State = string | null; +export const INITIAL_STATE: State = null; + +export default function reducer(state: State = INITIAL_STATE, action: Actions) { + if (action.type === ActionTypes.HandleError) { + return _parseError(action.error); + } else if (action.type === ActionTypes.ClearError) { + return INITIAL_STATE; } return state; } -/** - * Handle error action creator. - * - * @param {String} error - The error. - * - * @returns {String} The action. - */ -export const handleError = (error: string) => ({ - type: HANDLE_ERROR, +export const handleError = (error: IndexesError): HandleErrorAction => ({ + type: ActionTypes.HandleError, error, }); +export const clearError = (): ClearErrorAction => ({ + type: ActionTypes.ClearError, +}); + /** - * Clear error action creator. - * - * @returns {Object} The action. + * Data Service attaches string message property for some errors, but not all + * that can happen during index creation/dropping. + * Check for data service custom error, then node driver errmsg. */ -export const clearError = () => ({ - type: CLEAR_ERROR, -}); +export const _parseError = (err: IndexesError): string => { + if (typeof err === 'string') { + return err; + } + if (typeof err?.message === 'string') { + return err.message; + } + if (err instanceof MongoError && err.errmsg === 'string') { + return err.errmsg; + } + return 'Unknown error'; +}; diff --git a/packages/compass-indexes/src/modules/index.js b/packages/compass-indexes/src/modules/index.ts similarity index 84% rename from packages/compass-indexes/src/modules/index.js rename to packages/compass-indexes/src/modules/index.ts index f2241288f50..a3c63d96de4 100644 --- a/packages/compass-indexes/src/modules/index.js +++ b/packages/compass-indexes/src/modules/index.ts @@ -1,4 +1,5 @@ import { combineReducers } from 'redux'; +import type { AnyAction } from 'redux'; import appRegistry from '@mongodb-js/mongodb-redux-common/app-registry'; import dataService from './data-service'; import { RESET } from './reset'; @@ -24,10 +25,10 @@ import serverVersion, { INITIAL_STATE as SV_INITIAL_STATE } from './error'; import namespace, { INITIAL_STATE as NAMESPACE_INITIAL_STATE, } from './namespace'; +import isRefreshing, { + INITIAL_STATE as REFRESHING_INITIAL_STATE, +} from './is-refreshing'; -/** - * The main reducer. - */ const reducer = combineReducers({ indexes, isWritable, @@ -41,17 +42,12 @@ const reducer = combineReducers({ error, serverVersion, namespace, + isRefreshing, }); -/** - * The root reducer. - * - * @param {Object} state - The state. - * @param {Object} action - The action. - * - * @returns {Object} The new state. - */ -const rootReducer = (state, action) => { +export type RootState = ReturnType; + +const rootReducer = (state: RootState, action: AnyAction): RootState => { if (action.type === RESET) { return { ...state, @@ -65,6 +61,7 @@ const rootReducer = (state, action) => { sortColumn: SORT_COLUMN_INITIAL_STATE, error: ERROR_INITIAL_STATE, namespace: NAMESPACE_INITIAL_STATE, + isRefreshing: REFRESHING_INITIAL_STATE, }; } return reducer(state, action); diff --git a/packages/compass-indexes/src/modules/indexes.js b/packages/compass-indexes/src/modules/indexes.js deleted file mode 100644 index 53c6e9c5619..00000000000 --- a/packages/compass-indexes/src/modules/indexes.js +++ /dev/null @@ -1,224 +0,0 @@ -const debug = require('debug')('mongodb-compass:modules:indexes'); - -import IndexModel from 'mongodb-index-model'; -import map from 'lodash.map'; -import max from 'lodash.max'; -import { handleError } from './error'; -import { localAppRegistryEmit } from '@mongodb-js/mongodb-redux-common/app-registry'; - -/** - * The module action prefix. - */ -const PREFIX = 'indexes'; - -/** - * The loadIndexes action type. - */ -export const LOAD_INDEXES = `${PREFIX}/indexes/LOAD_INDEXES`; - -/** - * The sortIndexes action type. - */ -export const SORT_INDEXES = `${PREFIX}/indexes/SORT_INDEXES`; - -/** - * Default sortOrder - */ -export const DEFAULT = 'Name and Definition'; -export const ASC = 'asc'; -export const DESC = 'desc'; -export const USAGE = 'Usage'; - -/** - * The initial state. - */ -export const INITIAL_STATE = []; - -/** - * Get the comparator for properties. - * - * @param {Integer} order - The order. - * - * @returns {Function} The comparator function. - */ -const _propertiesComparator = (order) => { - return function (a, b) { - const aValue = - a.cardinality === 'compound' ? 'compound' : a.properties[0] || ''; - const bValue = - b.cardinality === 'compound' ? 'compound' : b.properties[0] || ''; - if (aValue > bValue) { - return order; - } - if (aValue < bValue) { - return -order; - } - return 0; - }; -}; - -/** - * Get a comparator function for the sort. - * - * @param {String} field - The field to sort on. - * @param {String} odr - The order. - * - * @returns {Function} The function. - */ -const _comparator = (field, odr) => { - const order = odr === ASC ? 1 : -1; - if (field === 'properties') { - return _propertiesComparator(order); - } - return function (a, b) { - if (a[field] > b[field]) { - return order; - } - if (a[field] < b[field]) { - return -order; - } - return 0; - }; -}; - -/** - * Get the name of the field to sort on based on the column header. - * - * @param {String} f - The field. - * @returns {String} The field. - */ -const _field = (f) => { - if (f === DEFAULT) { - return 'name'; - } else if (f === USAGE) { - return 'usageCount'; - } - return f.toLowerCase(); -}; - -/** - * Converts the raw index data to Index models and does calculations. - * - * @param {Array} indexes - The indexes. - * - * @returns {Array} The index models. - */ -const _convertToModels = (indexes) => { - const maxSize = max( - indexes.map((index) => { - return index.size; - }) - ); - return map(indexes, (index) => { - const model = new IndexModel(new IndexModel().parse(index)); - model.relativeSize = (model.size / maxSize) * 100; - return model; - }); -}; - -export const modelAndSort = (indexes, sortColumn, sortOrder) => { - return _convertToModels(indexes).sort( - _comparator(_field(sortColumn), sortOrder) - ); -}; - -/** - * Data Service attaches string message property for some errors, but not all - * that can happen during index creation/dropping. Check first for data service - * custom error, then node driver errmsg, lastly use default error message. - * - * @param {Object} err - The error to parse a message from - * - * @returns {string} - The found error message, or the default message. - */ -export const parseErrorMsg = (err) => { - if (typeof err.message === 'string') { - return err.message; - } else if (typeof err.errmsg === 'string') { - return err.errmsg; - } - return 'Unknown error'; -}; - -/** - * Reducer function for handle state changes to indexes. - * - * @param {Array} state - The indexes state. - * @param {Object} action - The action. - * - * @returns {Array} The new state. - */ -export default function reducer(state = INITIAL_STATE, action) { - if (action.type === SORT_INDEXES) { - return [...action.indexes].sort( - _comparator(_field(action.column), action.order) - ); - } else if (action.type === LOAD_INDEXES) { - return action.indexes; - } - return state; -} - -/** - * Action creator for load indexes events. - * - * @param {Array} indexes - The raw indexes list. - * - * @returns {Object} The load indexes action. - */ -export const loadIndexes = (indexes) => ({ - type: LOAD_INDEXES, - indexes: indexes, -}); - -export const sortIndexes = (column, order) => { - return (dispatch, getState) => { - const { indexes } = getState(); - return dispatch({ - type: SORT_INDEXES, - indexes, - column, - order, - }); - }; -}; - -/** - * Load indexes from DB. - * - * @param {String} ns - The namespace. - * - * @returns {Function} The thunk function. - */ -export const loadIndexesFromDb = () => { - return (dispatch, getState) => { - const state = getState(); - if (state.isReadonly) { - dispatch(loadIndexes([])); - dispatch(localAppRegistryEmit('indexes-changed', [])); - } else if (state.dataService && state.dataService.isConnected()) { - const ns = state.namespace; - state.dataService.indexes(state.namespace, {}, (err, indexes) => { - if (err) { - dispatch(handleError(parseErrorMsg(err))); - dispatch(loadIndexes([])); - dispatch(localAppRegistryEmit('indexes-changed', [])); - } else { - // Set the `ns` field manually as it is not returned from the server - // since version 4.4. - for (const index of indexes) { - index.ns = ns; - } - const ixs = modelAndSort(indexes, state.sortColumn, state.sortOrder); - dispatch(loadIndexes(ixs)); - dispatch(localAppRegistryEmit('indexes-changed', ixs)); - } - }); - } else if (state.dataService && !state.dataService.isConnected()) { - debug( - 'warning: trying to load indexes but dataService is disconnected', - state.dataService - ); - } - }; -}; diff --git a/packages/compass-indexes/src/modules/indexes.spec.js b/packages/compass-indexes/src/modules/indexes.spec.js index 60d6fd1baf9..b06c2c581cc 100644 --- a/packages/compass-indexes/src/modules/indexes.spec.js +++ b/packages/compass-indexes/src/modules/indexes.spec.js @@ -3,7 +3,7 @@ import { expect } from 'chai'; import sinon from 'sinon'; import reducer, { - loadIndexesFromDb, + fetchIndexes, loadIndexes, sortIndexes, LOAD_INDEXES, @@ -128,7 +128,7 @@ describe('indexes module', function () { }); }); }); - describe('#loadIndexesFromDb', function () { + describe('#fetchIndexes', function () { let actionSpy; let emitSpy; beforeEach(function () { @@ -153,7 +153,7 @@ describe('indexes module', function () { isReadonly: true, namespace: 'citibike.trips', }); - loadIndexesFromDb()(dispatch, state); + fetchIndexes()(dispatch, state); expect(actionSpy.calledOnce).to.equal(true); }); @@ -187,7 +187,7 @@ describe('indexes module', function () { }, namespace: 'citibike.trips', }); - loadIndexesFromDb()(dispatch, state); + fetchIndexes()(dispatch, state); expect(actionSpy.calledTwice).to.equal(true); }); @@ -217,7 +217,7 @@ describe('indexes module', function () { sortOrder: ASC, namespace: 'citibike.trips', }); - loadIndexesFromDb()(dispatch, state); + fetchIndexes()(dispatch, state); expect(actionSpy.calledOnce).to.equal(true); }); }); diff --git a/packages/compass-indexes/src/modules/indexes.ts b/packages/compass-indexes/src/modules/indexes.ts new file mode 100644 index 00000000000..9570c325236 --- /dev/null +++ b/packages/compass-indexes/src/modules/indexes.ts @@ -0,0 +1,229 @@ +import IndexModel from 'mongodb-index-model'; +import type { AmpersandIndexModel } from 'mongodb-index-model'; +import { localAppRegistryEmit } from '@mongodb-js/mongodb-redux-common/app-registry'; +import type { ThunkAction } from 'redux-thunk'; +import _debug from 'debug'; + +import type { RootState } from './index'; +import { handleError } from './error'; +import type { HandleErrorAction, IndexesError } from './error'; +import { ActionTypes as RefreshActionTypes } from './is-refreshing'; +import type { RefreshFinishedAction } from './is-refreshing'; +import { ActionTypes as SortOrderActions } from './sort-order'; +import type { SortOrderChangedAction } from './sort-order'; +import { ActionTypes as SortColumnActions } from './sort-column'; +import type { SortColumnChangedAction } from './sort-column'; + +const debug = _debug('mongodb-compass:modules:indexes'); + +type SortField = keyof Pick< + IndexDefinition, + 'name' | 'type' | 'size' | 'usageCount' | 'properties' +>; +export type SortColumn = keyof typeof sortColumnToProps; +export type SortDirection = 'asc' | 'desc'; +const sortColumnToProps: Record = { + 'Name and Definition': 'name', + Type: 'type', + Size: 'size', + Usage: 'usageCount', + Properties: 'properties', +}; + +export type IndexDefinition = { + name: string; + fields: { + serialize: () => { field: string; value: number | string }[]; + }; + type: 'geo' | 'hashed' | 'text' | 'wildcard' | 'clustered' | 'columnstore'; + cardinality: 'single' | 'compound'; + properties: ('unique' | 'sparse' | 'partial' | 'ttl' | 'collation')[]; + extra: Record>; + size: number; + relativeSize: number; + usageCount: number; + usageSince?: Date; +}; + +enum ActionTypes { + LoadIndexes = 'indexes/indexes/LOAD_INDEXES', + SortIndexes = 'indexes/indexes/SORT_INDEXES', +} + +type LoadIndexesAction = { + type: ActionTypes.LoadIndexes; + indexes: IndexDefinition[]; +}; + +type SortIndexesAction = { + type: ActionTypes.SortIndexes; + indexes: IndexDefinition[]; + column: SortColumn; + order: SortDirection; +}; + +type Actions = LoadIndexesAction | SortIndexesAction; +type State = IndexDefinition[]; + +export const INITIAL_STATE: State = []; + +export default function reducer(state: State = INITIAL_STATE, action: Actions) { + if (action.type === ActionTypes.SortIndexes) { + return [...action.indexes].sort( + _getSortFunction(_mapColumnToProp(action.column), action.order) + ); + } + if (action.type === ActionTypes.LoadIndexes) { + return action.indexes; + } + return state; +} + +export const loadIndexes = ( + indexes: IndexDefinition[] +): ThunkAction< + void, + RootState, + void, + LoadIndexesAction | RefreshFinishedAction +> => { + return (dispatch) => { + dispatch({ type: RefreshActionTypes.RefreshFinished }); + dispatch({ + type: ActionTypes.LoadIndexes, + indexes, + }); + }; +}; + +export const sortIndexes = ( + column: SortColumn, + order: SortDirection +): ThunkAction< + void, + RootState, + void, + SortIndexesAction | SortOrderChangedAction | SortColumnChangedAction +> => { + return (dispatch, getState) => { + const { indexes } = getState(); + dispatch({ type: SortOrderActions.SortOrderChanged, order }); + dispatch({ type: SortColumnActions.SortColumnChanged, column }); + dispatch({ + type: ActionTypes.SortIndexes, + indexes, + column, + order, + }); + }; +}; + +export const fetchIndexes = (): ThunkAction< + void, + RootState, + void, + LoadIndexesAction | HandleErrorAction +> => { + return (dispatch, getState) => { + const { isReadonly, dataService, namespace, sortColumn, sortOrder } = + getState(); + + if (isReadonly) { + dispatch(loadIndexes([])); + dispatch(localAppRegistryEmit('indexes-changed', [])); + return; + } + + if (dataService && !dataService.isConnected()) { + debug( + 'warning: trying to load indexes but dataService is disconnected', + dataService + ); + return; + } + + dataService.indexes( + namespace, + {}, + (err: IndexesError, indexes: AmpersandIndexModel[]) => { + if (err) { + dispatch(handleError(err)); + dispatch(loadIndexes([])); + dispatch(localAppRegistryEmit('indexes-changed', [])); + return; + } + // Set the `ns` field manually as it is not returned from the server + // since version 4.4. + for (const index of indexes) { + index.ns = namespace; + } + const ixs = _mapAndSort(indexes, sortColumn, sortOrder); + dispatch(loadIndexes(ixs)); + dispatch(localAppRegistryEmit('indexes-changed', ixs)); + } + ); + }; +}; + +const _getSortFunctionForProperties = (order: 1 | -1) => { + return function (a: IndexDefinition, b: IndexDefinition) { + const aValue = + a.cardinality === 'compound' ? 'compound' : a.properties[0] || ''; + const bValue = + b.cardinality === 'compound' ? 'compound' : b.properties[0] || ''; + if (aValue > bValue) { + return order; + } + if (aValue < bValue) { + return -order; + } + return 0; + }; +}; + +const _getSortFunction = (field: SortField, order: SortDirection) => { + const _order = order === 'asc' ? 1 : -1; + if (field === 'properties') { + return _getSortFunctionForProperties(_order); + } + return function (a: IndexDefinition, b: IndexDefinition) { + if (a[field] > b[field]) { + return _order; + } + if (a[field] < b[field]) { + return -_order; + } + return 0; + }; +}; + +const _mapColumnToProp = (column: SortColumn): SortField => { + return sortColumnToProps[column]; +}; + +/** + * Converts the raw index data (from ampersand) to + * Index models (IndexDefinition) and adds computed props. + */ +const _convertToModels = ( + indexes: AmpersandIndexModel[] +): IndexDefinition[] => { + const sizes = indexes.map((index) => index.size); + const maxSize = Math.max(...sizes); + + return indexes.map((index) => { + const model = new IndexModel(new IndexModel().parse(index)); + model.relativeSize = (model.size / maxSize) * 100; + return model as IndexDefinition; + }); +}; + +const _mapAndSort = ( + indexes: AmpersandIndexModel[], + sortColumn: SortColumn, + sortOrder: SortDirection +) => { + return _convertToModels(indexes).sort( + _getSortFunction(_mapColumnToProp(sortColumn), sortOrder) + ); +}; diff --git a/packages/compass-indexes/src/modules/is-readonly.js b/packages/compass-indexes/src/modules/is-readonly.js index 11f8cbf86ee..486c93e65a2 100644 --- a/packages/compass-indexes/src/modules/is-readonly.js +++ b/packages/compass-indexes/src/modules/is-readonly.js @@ -6,10 +6,6 @@ export const INITIAL_STATE = /** * Reducer function doesn't do anything since we're based on process. - * - * @param {Array} state - The state. - * - * @returns {Array} The state. */ export default function reducer(state = INITIAL_STATE) { return state; diff --git a/packages/compass-indexes/src/modules/is-refreshing.ts b/packages/compass-indexes/src/modules/is-refreshing.ts new file mode 100644 index 00000000000..2d0f9b60088 --- /dev/null +++ b/packages/compass-indexes/src/modules/is-refreshing.ts @@ -0,0 +1,46 @@ +import type { ThunkAction } from 'redux-thunk'; +import type { RootState } from './index'; +import { fetchIndexes } from './indexes'; + +export enum ActionTypes { + RefreshStarted = 'indexes/is-refreshing/RefreshStarted', + RefreshFinished = 'indexes/is-refreshing/RefreshFinished', +} + +type RefreshStartedAction = { + type: ActionTypes.RefreshStarted; +}; + +export type RefreshFinishedAction = { + type: ActionTypes.RefreshFinished; +}; + +type Actions = RefreshStartedAction | RefreshFinishedAction; + +type State = boolean; + +export const INITIAL_STATE: State = false; + +export default function reducer(state = INITIAL_STATE, action: Actions) { + if (action.type === ActionTypes.RefreshStarted) { + return true; + } + if (action.type === ActionTypes.RefreshFinished) { + return false; + } + return state; +} + +export const refreshIndexes = (): ThunkAction< + void, + RootState, + void, + Actions +> => { + return (dispatch) => { + dispatch(fetchIndexes()); + dispatch({ + type: ActionTypes.RefreshStarted, + }); + }; +}; diff --git a/packages/compass-indexes/src/modules/sort-column.js b/packages/compass-indexes/src/modules/sort-column.js deleted file mode 100644 index 71a22c32edd..00000000000 --- a/packages/compass-indexes/src/modules/sort-column.js +++ /dev/null @@ -1,23 +0,0 @@ -import { SORT_INDEXES } from './indexes'; - -/** - * The initial state of the sort column attribute. - */ -export const INITIAL_STATE = 'Name and Definition'; - -/** - * Reducer function for handle state changes to sortColumn. - * - * @param {Boolean} state - The status state. - * @param {Object} action - The action. - * - * @returns {Boolean} The new state. - */ -const reducer = (state = INITIAL_STATE, action) => { - if (action.type === SORT_INDEXES) { - return action.column; - } - return state; -}; - -export default reducer; diff --git a/packages/compass-indexes/src/modules/sort-column.ts b/packages/compass-indexes/src/modules/sort-column.ts new file mode 100644 index 00000000000..818adf3301d --- /dev/null +++ b/packages/compass-indexes/src/modules/sort-column.ts @@ -0,0 +1,24 @@ +import type { SortColumn } from './indexes'; + +export enum ActionTypes { + SortColumnChanged = 'indexes/sort-column/sortColumnChanged', +} + +export type SortColumnChangedAction = { + type: ActionTypes.SortColumnChanged; + column: SortColumn; +}; + +type State = SortColumn; + +export const INITIAL_STATE: State = 'Name and Definition'; + +export default function reducer( + state = INITIAL_STATE, + action: SortColumnChangedAction +) { + if (action.type === ActionTypes.SortColumnChanged) { + return action.column; + } + return state; +} diff --git a/packages/compass-indexes/src/modules/sort-order.js b/packages/compass-indexes/src/modules/sort-order.js deleted file mode 100644 index 62397248256..00000000000 --- a/packages/compass-indexes/src/modules/sort-order.js +++ /dev/null @@ -1,23 +0,0 @@ -import { SORT_INDEXES, ASC } from './indexes'; - -/** - * The initial state of the sort order attribute. - */ -export const INITIAL_STATE = ASC; - -/** - * Reducer function for handle state changes to sortOrder. - * - * @param {Boolean} state - The status state. - * @param {Object} action - The action. - * - * @returns {Boolean} The new state. - */ -const reducer = (state = INITIAL_STATE, action) => { - if (action.type === SORT_INDEXES) { - return action.order; - } - return state; -}; - -export default reducer; diff --git a/packages/compass-indexes/src/modules/sort-order.ts b/packages/compass-indexes/src/modules/sort-order.ts new file mode 100644 index 00000000000..0558993737c --- /dev/null +++ b/packages/compass-indexes/src/modules/sort-order.ts @@ -0,0 +1,24 @@ +import type { SortDirection } from './indexes'; + +export enum ActionTypes { + SortOrderChanged = 'indexes/sort-order/sortOrderChanged', +} + +export type SortOrderChangedAction = { + type: ActionTypes.SortOrderChanged; + order: SortDirection; +}; + +type State = SortDirection; + +export const INITIAL_STATE: State = 'asc'; + +export default function reducer( + state = INITIAL_STATE, + action: SortOrderChangedAction +) { + if (action.type === ActionTypes.SortOrderChanged) { + return action.order; + } + return state; +} diff --git a/packages/compass-indexes/src/stores/create-index.js b/packages/compass-indexes/src/stores/create-index.js index 8d396dfb83c..15578804409 100644 --- a/packages/compass-indexes/src/stores/create-index.js +++ b/packages/compass-indexes/src/stores/create-index.js @@ -8,7 +8,6 @@ import { } from '@mongodb-js/mongodb-redux-common/app-registry'; import { createLoggerAndTelemetry } from '@mongodb-js/compass-logging'; import { changeSchemaFields } from '../modules/create-index/schema-fields'; -import { parseErrorMsg } from '../modules/indexes'; import { handleError } from '../modules/error'; import { toggleIsVisible } from '../modules/is-visible'; import { namespaceChanged } from '../modules/namespace'; @@ -25,7 +24,7 @@ const { track } = createLoggerAndTelemetry('COMPASS-INDEXES-UI'); */ export const setDataProvider = (store, error, provider) => { if (error !== null) { - store.dispatch(handleError(parseErrorMsg(error))); + store.dispatch(handleError(error)); } else { store.dispatch(dataServiceConnected(provider)); } diff --git a/packages/compass-indexes/src/stores/drop-index.js b/packages/compass-indexes/src/stores/drop-index.js index 08c9db4bc22..eebe6532e2b 100644 --- a/packages/compass-indexes/src/stores/drop-index.js +++ b/packages/compass-indexes/src/stores/drop-index.js @@ -6,7 +6,6 @@ import { localAppRegistryActivated, globalAppRegistryActivated, } from '@mongodb-js/mongodb-redux-common/app-registry'; -import { parseErrorMsg } from '../modules/indexes'; import { handleError } from '../modules/error'; import { toggleIsVisible } from '../modules/is-visible'; import { nameChanged } from '../modules/drop-index/name'; @@ -21,7 +20,7 @@ import { namespaceChanged } from '../modules/namespace'; */ export const setDataProvider = (store, error, provider) => { if (error !== null) { - store.dispatch(handleError(parseErrorMsg(error))); + store.dispatch(handleError(error)); } else { store.dispatch(dataServiceConnected(provider)); } diff --git a/packages/compass-indexes/src/stores/store.js b/packages/compass-indexes/src/stores/store.js index e9db5a7e4be..5e1626fe67c 100644 --- a/packages/compass-indexes/src/stores/store.js +++ b/packages/compass-indexes/src/stores/store.js @@ -9,7 +9,7 @@ import { writeStateChanged } from '../modules/is-writable'; import { readonlyViewChanged } from '../modules/is-readonly-view'; import { getDescription } from '../modules/description'; import { dataServiceConnected } from '../modules/data-service'; -import { loadIndexesFromDb, parseErrorMsg } from '../modules/indexes'; +import { fetchIndexes } from '../modules/indexes'; import { handleError } from '../modules/error'; import { namespaceChanged } from '../modules/namespace'; import { serverVersionChanged } from '../modules/server-version'; @@ -23,10 +23,10 @@ import { serverVersionChanged } from '../modules/server-version'; */ export const setDataProvider = (store, error, provider) => { if (error !== null) { - store.dispatch(handleError(parseErrorMsg(error))); + store.dispatch(handleError(error)); } else { store.dispatch(dataServiceConnected(provider)); - store.dispatch(loadIndexesFromDb()); + store.dispatch(fetchIndexes()); } }; @@ -39,7 +39,7 @@ const configureStore = (options = {}) => { store.dispatch(localAppRegistryActivated(localAppRegistry)); localAppRegistry.on('refresh-data', () => { - store.dispatch(loadIndexesFromDb()); + store.dispatch(fetchIndexes()); }); // TODO: could save the version to check for wildcard indexes } @@ -64,7 +64,7 @@ const configureStore = (options = {}) => { }); globalAppRegistry.on('refresh-data', () => { - store.dispatch(loadIndexesFromDb()); + store.dispatch(fetchIndexes()); }); } diff --git a/packages/compass-indexes/src/typings.d.ts b/packages/compass-indexes/src/typings.d.ts index d9688b91af8..a4da61f1ed9 100644 --- a/packages/compass-indexes/src/typings.d.ts +++ b/packages/compass-indexes/src/typings.d.ts @@ -10,3 +10,7 @@ declare module 'hadron-react-components'; declare module 'hadron-app'; declare module '@mongodb-js/mongodb-redux-common/app-registry'; declare module 'lodash.contains'; +declare module 'mongodb-index-model' { + export type AmpersandIndexModel = Record; + export default any; +} From 04da0cd8c9d51958b9f6e2424569781da02c77d3 Mon Sep 17 00:00:00 2001 From: Basit Date: Tue, 23 Aug 2022 22:33:47 +0200 Subject: [PATCH 02/12] data service ts --- .../src/modules/create-index/index.ts | 35 ++++------ .../src/modules/data-service.ts | 44 ++++-------- packages/compass-indexes/src/modules/error.ts | 3 +- .../compass-indexes/src/modules/indexes.ts | 67 ++++++++----------- .../src/modules/sort-column.ts | 18 ++--- .../compass-indexes/src/modules/sort-order.ts | 16 ++--- packages/compass-indexes/src/typings.d.ts | 5 +- packages/data-service/src/data-service.ts | 44 ++++++------ 8 files changed, 88 insertions(+), 144 deletions(-) diff --git a/packages/compass-indexes/src/modules/create-index/index.ts b/packages/compass-indexes/src/modules/create-index/index.ts index d9a169082d9..3d651b79003 100644 --- a/packages/compass-indexes/src/modules/create-index/index.ts +++ b/packages/compass-indexes/src/modules/create-index/index.ts @@ -3,7 +3,7 @@ import { combineReducers } from 'redux'; import type { AnyAction, Dispatch } from 'redux'; import { createLoggerAndTelemetry } from '@mongodb-js/compass-logging'; import queryParser from 'mongodb-query-parser'; -import type { CollationOptions } from 'mongodb'; +import type { IndexSpecification, CreateIndexesOptions, IndexDirection, AnyError } from 'mongodb'; import dataService from '../data-service'; import appRegistry, { @@ -74,8 +74,6 @@ import { RESET, reset } from '../reset'; const { track } = createLoggerAndTelemetry('COMPASS-INDEXES-UI'); -type CreateIndexSpec = { [name: string]: string | number }; - /** * The main reducer. */ @@ -151,7 +149,7 @@ export default rootReducer; export const createIndex = () => { return (dispatch: Dispatch, getState: () => RootState) => { const state = getState(); - const spec = {} as CreateIndexSpec; + const spec: IndexSpecification = {}; // Check for field errors. if ( @@ -164,28 +162,20 @@ export const createIndex = () => { } // Check for collaction errors. - const collation = queryParser.isCollationValid(state.collationString); + const collation = queryParser.isCollationValid(state.collationString) || undefined; if (state.useCustomCollation && !collation) { dispatch(handleError('You must provide a valid collation object')); return; } state.fields.forEach((field: IndexField) => { - let type: string | number = field.type; - if (type === '1 (asc)') type = 1; - if (type === '-1 (desc)') type = -1; + let type = field.type as IndexDirection; + if ((type as string) === '1 (asc)') type = 1; + if ((type as string) === '-1 (desc)') type = -1; spec[field.name] = type; }); - const options: { - unique?: boolean; - name?: string; - collation?: false | CollationOptions | null; - expireAfterSeconds?: number; - wildcardProjection?: EJSON.SerializableTypes; - columnstoreProjection?: EJSON.SerializableTypes; - partialFilterExpression?: EJSON.SerializableTypes; - } = {}; + const options: CreateIndexesOptions = {}; options.unique = state.isUnique; // The server will generate a name when we don't provide one. if (state.name !== '') { @@ -203,7 +193,7 @@ export const createIndex = () => { } if (state.useWildcardProjection) { try { - options.wildcardProjection = EJSON.parse(state.wildcardProjection); + options.wildcardProjection = EJSON.parse(state.wildcardProjection) as Document; } catch (err) { dispatch(handleError(`Bad WildcardProjection: ${String(err)}`)); return; @@ -220,9 +210,10 @@ export const createIndex = () => { if (state.useColumnstoreProjection) { try { - options.columnstoreProjection = EJSON.parse( + // columnstoreProjection is not part of CreateIndexesOptions yet + (options as any).columnstoreProjection = EJSON.parse( state.columnstoreProjection - ); + ) as Document; } catch (err) { dispatch(handleError(`Bad ColumnstoreProjection: ${String(err)}`)); return; @@ -232,7 +223,7 @@ export const createIndex = () => { try { options.partialFilterExpression = EJSON.parse( state.partialFilterExpression - ); + ) as Document; } catch (err) { dispatch(handleError(`Bad PartialFilterExpression: ${String(err)}`)); return; @@ -241,7 +232,7 @@ export const createIndex = () => { dispatch(toggleInProgress(true)); const ns = state.namespace; - state.dataService.createIndex(ns, spec, options, (createErr: Error) => { + state.dataService?.createIndex(ns, spec, options, (createErr: any) => { if (!createErr) { const trackEvent = { unique: state.isUnique, diff --git a/packages/compass-indexes/src/modules/data-service.ts b/packages/compass-indexes/src/modules/data-service.ts index 8e9566277d1..c6c90621030 100644 --- a/packages/compass-indexes/src/modules/data-service.ts +++ b/packages/compass-indexes/src/modules/data-service.ts @@ -1,44 +1,26 @@ -import type { AnyAction } from 'redux'; import type { DataService } from 'mongodb-data-service'; -/** - * The prefix. - */ -const PREFIX = 'indexes/data-service'; +enum ActionTypes { + DataServiceConnected = 'indexes/data-service/DATA_SERVICE_CONNECTED', +}; -/** - * Data service connected. - */ -export const DATA_SERVICE_CONNECTED = `${PREFIX}/DATA_SERVICE_CONNECTED`; +type DataServiceConnectedAction = { + type: ActionTypes.DataServiceConnected; + dataService: DataService; +}; -/** - * The initial state. - */ -export const INITIAL_STATE = null; +type State = DataService | null; -/** - * Reducer function for handling data service connected actions. - * - * @param {Object} state - The data service state. - * @param {Object} action - The action. - * - * @returns {DataService} The data service connected action. - */ -export default function reducer(state = INITIAL_STATE, action: AnyAction) { - if (action.type === DATA_SERVICE_CONNECTED) { +const INITIAL_STATE: State = null; + +export default function reducer(state = INITIAL_STATE, action: DataServiceConnectedAction) { + if (action.type === ActionTypes.DataServiceConnected) { return action.dataService; } return state; } -/** - * Action creator for data service connected events. - * - * @param {DataService} dataService - The data service. - * - * @returns {Object} The data service connected action. - */ export const dataServiceConnected = (dataService: DataService) => ({ - type: DATA_SERVICE_CONNECTED, + type: ActionTypes.DataServiceConnected, dataService, }); diff --git a/packages/compass-indexes/src/modules/error.ts b/packages/compass-indexes/src/modules/error.ts index 2dbbe02a030..9bd5f1a6676 100644 --- a/packages/compass-indexes/src/modules/error.ts +++ b/packages/compass-indexes/src/modules/error.ts @@ -1,6 +1,7 @@ import { MongoError } from 'mongodb'; +import type { AnyError } from 'mongodb'; -export type IndexesError = MongoError | Error | string | null | undefined; +export type IndexesError = AnyError | string | null | undefined; enum ActionTypes { HandleError = 'indexes/error/HANDLE_ERROR', diff --git a/packages/compass-indexes/src/modules/indexes.ts b/packages/compass-indexes/src/modules/indexes.ts index 9570c325236..c728bb382e8 100644 --- a/packages/compass-indexes/src/modules/indexes.ts +++ b/packages/compass-indexes/src/modules/indexes.ts @@ -1,7 +1,7 @@ import IndexModel from 'mongodb-index-model'; -import type { AmpersandIndexModel } from 'mongodb-index-model'; +import type { Document } from 'mongodb'; import { localAppRegistryEmit } from '@mongodb-js/mongodb-redux-common/app-registry'; -import type { ThunkAction } from 'redux-thunk'; +import type { ThunkAction, ThunkDispatch } from 'redux-thunk'; import _debug from 'debug'; import type { RootState } from './index'; @@ -9,10 +9,6 @@ import { handleError } from './error'; import type { HandleErrorAction, IndexesError } from './error'; import { ActionTypes as RefreshActionTypes } from './is-refreshing'; import type { RefreshFinishedAction } from './is-refreshing'; -import { ActionTypes as SortOrderActions } from './sort-order'; -import type { SortOrderChangedAction } from './sort-order'; -import { ActionTypes as SortColumnActions } from './sort-column'; -import type { SortColumnChangedAction } from './sort-column'; const debug = _debug('mongodb-compass:modules:indexes'); @@ -45,7 +41,7 @@ export type IndexDefinition = { usageSince?: Date; }; -enum ActionTypes { +export enum ActionTypes { LoadIndexes = 'indexes/indexes/LOAD_INDEXES', SortIndexes = 'indexes/indexes/SORT_INDEXES', } @@ -55,7 +51,7 @@ type LoadIndexesAction = { indexes: IndexDefinition[]; }; -type SortIndexesAction = { +export type SortIndexesAction = { type: ActionTypes.SortIndexes; indexes: IndexDefinition[]; column: SortColumn; @@ -81,20 +77,10 @@ export default function reducer(state: State = INITIAL_STATE, action: Actions) { export const loadIndexes = ( indexes: IndexDefinition[] -): ThunkAction< - void, - RootState, - void, - LoadIndexesAction | RefreshFinishedAction -> => { - return (dispatch) => { - dispatch({ type: RefreshActionTypes.RefreshFinished }); - dispatch({ - type: ActionTypes.LoadIndexes, - indexes, - }); - }; -}; +): LoadIndexesAction => ({ + type: ActionTypes.LoadIndexes, + indexes, +}); export const sortIndexes = ( column: SortColumn, @@ -103,12 +89,10 @@ export const sortIndexes = ( void, RootState, void, - SortIndexesAction | SortOrderChangedAction | SortColumnChangedAction + SortIndexesAction > => { return (dispatch, getState) => { const { indexes } = getState(); - dispatch({ type: SortOrderActions.SortOrderChanged, order }); - dispatch({ type: SortColumnActions.SortColumnChanged, column }); dispatch({ type: ActionTypes.SortIndexes, indexes, @@ -118,23 +102,31 @@ export const sortIndexes = ( }; }; +const _handleIndexesChanged = ( + dispatch: ThunkDispatch, + indexes: IndexDefinition[] +) => { + dispatch(loadIndexes(indexes)); + dispatch(localAppRegistryEmit('indexes-changed', indexes)); + dispatch({ type: RefreshActionTypes.RefreshFinished }); +}; + export const fetchIndexes = (): ThunkAction< void, RootState, void, - LoadIndexesAction | HandleErrorAction + LoadIndexesAction | RefreshFinishedAction | HandleErrorAction > => { return (dispatch, getState) => { const { isReadonly, dataService, namespace, sortColumn, sortOrder } = getState(); if (isReadonly) { - dispatch(loadIndexes([])); - dispatch(localAppRegistryEmit('indexes-changed', [])); - return; + return _handleIndexesChanged(dispatch, []); } - if (dataService && !dataService.isConnected()) { + if (!dataService) { + dispatch({ type: RefreshActionTypes.RefreshFinished }); debug( 'warning: trying to load indexes but dataService is disconnected', dataService @@ -145,12 +137,10 @@ export const fetchIndexes = (): ThunkAction< dataService.indexes( namespace, {}, - (err: IndexesError, indexes: AmpersandIndexModel[]) => { + (err: any, indexes: Document[]) => { if (err) { - dispatch(handleError(err)); - dispatch(loadIndexes([])); - dispatch(localAppRegistryEmit('indexes-changed', [])); - return; + dispatch(handleError(err as IndexesError)); + return _handleIndexesChanged(dispatch, []); } // Set the `ns` field manually as it is not returned from the server // since version 4.4. @@ -158,8 +148,7 @@ export const fetchIndexes = (): ThunkAction< index.ns = namespace; } const ixs = _mapAndSort(indexes, sortColumn, sortOrder); - dispatch(loadIndexes(ixs)); - dispatch(localAppRegistryEmit('indexes-changed', ixs)); + return _handleIndexesChanged(dispatch, ixs); } ); }; @@ -206,7 +195,7 @@ const _mapColumnToProp = (column: SortColumn): SortField => { * Index models (IndexDefinition) and adds computed props. */ const _convertToModels = ( - indexes: AmpersandIndexModel[] + indexes: Document[] ): IndexDefinition[] => { const sizes = indexes.map((index) => index.size); const maxSize = Math.max(...sizes); @@ -219,7 +208,7 @@ const _convertToModels = ( }; const _mapAndSort = ( - indexes: AmpersandIndexModel[], + indexes: Document[], sortColumn: SortColumn, sortOrder: SortDirection ) => { diff --git a/packages/compass-indexes/src/modules/sort-column.ts b/packages/compass-indexes/src/modules/sort-column.ts index 818adf3301d..0aa817bc1b6 100644 --- a/packages/compass-indexes/src/modules/sort-column.ts +++ b/packages/compass-indexes/src/modules/sort-column.ts @@ -1,13 +1,5 @@ -import type { SortColumn } from './indexes'; - -export enum ActionTypes { - SortColumnChanged = 'indexes/sort-column/sortColumnChanged', -} - -export type SortColumnChangedAction = { - type: ActionTypes.SortColumnChanged; - column: SortColumn; -}; +import { ActionTypes as IndexesActionTypes } from './indexes'; +import type { SortColumn, SortIndexesAction } from './indexes'; type State = SortColumn; @@ -15,10 +7,10 @@ export const INITIAL_STATE: State = 'Name and Definition'; export default function reducer( state = INITIAL_STATE, - action: SortColumnChangedAction + action: SortIndexesAction ) { - if (action.type === ActionTypes.SortColumnChanged) { + if (action.type === IndexesActionTypes.SortIndexes) { return action.column; } return state; -} +} \ No newline at end of file diff --git a/packages/compass-indexes/src/modules/sort-order.ts b/packages/compass-indexes/src/modules/sort-order.ts index 0558993737c..75e4ec79c04 100644 --- a/packages/compass-indexes/src/modules/sort-order.ts +++ b/packages/compass-indexes/src/modules/sort-order.ts @@ -1,13 +1,5 @@ -import type { SortDirection } from './indexes'; - -export enum ActionTypes { - SortOrderChanged = 'indexes/sort-order/sortOrderChanged', -} - -export type SortOrderChangedAction = { - type: ActionTypes.SortOrderChanged; - order: SortDirection; -}; +import { ActionTypes as IndexesActionTypes } from './indexes'; +import type { SortDirection, SortIndexesAction } from './indexes'; type State = SortDirection; @@ -15,9 +7,9 @@ export const INITIAL_STATE: State = 'asc'; export default function reducer( state = INITIAL_STATE, - action: SortOrderChangedAction + action: SortIndexesAction ) { - if (action.type === ActionTypes.SortOrderChanged) { + if (action.type === IndexesActionTypes.SortIndexes) { return action.order; } return state; diff --git a/packages/compass-indexes/src/typings.d.ts b/packages/compass-indexes/src/typings.d.ts index a4da61f1ed9..bff4ede34c0 100644 --- a/packages/compass-indexes/src/typings.d.ts +++ b/packages/compass-indexes/src/typings.d.ts @@ -10,7 +10,4 @@ declare module 'hadron-react-components'; declare module 'hadron-app'; declare module '@mongodb-js/mongodb-redux-common/app-registry'; declare module 'lodash.contains'; -declare module 'mongodb-index-model' { - export type AmpersandIndexModel = Record; - export default any; -} +declare module 'mongodb-index-model'; \ No newline at end of file diff --git a/packages/data-service/src/data-service.ts b/packages/data-service/src/data-service.ts index 735855cd12d..c67cfc1e625 100644 --- a/packages/data-service/src/data-service.ts +++ b/packages/data-service/src/data-service.ts @@ -279,8 +279,8 @@ export interface DataService { options?: { nameOnly?: true; privileges?: - | ConnectionStatusWithPrivileges['authInfo']['authenticatedUserPrivileges'] - | null; + | ConnectionStatusWithPrivileges['authInfo']['authenticatedUserPrivileges'] + | null; } ): Promise[]>; @@ -290,11 +290,11 @@ export interface DataService { listDatabases(options?: { nameOnly?: true; privileges?: - | ConnectionStatusWithPrivileges['authInfo']['authenticatedUserPrivileges'] - | null; + | ConnectionStatusWithPrivileges['authInfo']['authenticatedUserPrivileges'] + | null; roles?: - | ConnectionStatusWithPrivileges['authInfo']['authenticatedUserRoles'] - | null; + | ConnectionStatusWithPrivileges['authInfo']['authenticatedUserRoles'] + | null; }): Promise<{ _id: string; name: string }[]>; connect(): Promise; @@ -539,7 +539,7 @@ export interface DataService { * @param options - The options (unused). * @param callback - The callback. */ - indexes(ns: string, options: unknown, callback: Callback): void; + indexes(ns: string, options: unknown, callback: Callback): void; /** * Get the current instance details. @@ -1004,8 +1004,8 @@ export class DataServiceImpl extends EventEmitter implements DataService { }: { nameOnly?: true; privileges?: - | ConnectionStatusWithPrivileges['authInfo']['authenticatedUserPrivileges'] - | null; + | ConnectionStatusWithPrivileges['authInfo']['authenticatedUserPrivileges'] + | null; } = {} ): Promise[]> { const logop = this._startLogOp( @@ -1103,11 +1103,11 @@ export class DataServiceImpl extends EventEmitter implements DataService { }: { nameOnly?: true; privileges?: - | ConnectionStatusWithPrivileges['authInfo']['authenticatedUserPrivileges'] - | null; + | ConnectionStatusWithPrivileges['authInfo']['authenticatedUserPrivileges'] + | null; roles?: - | ConnectionStatusWithPrivileges['authInfo']['authenticatedUserRoles'] - | null; + | ConnectionStatusWithPrivileges['authInfo']['authenticatedUserRoles'] + | null; } = {}): Promise<{ _id: string; name: string }[]> { const logop = this._startLogOp( mongoLogId(1_001_000_033), @@ -1402,9 +1402,9 @@ export class DataServiceImpl extends EventEmitter implements DataService { ?.close(true) .catch((err) => debug('failed to close MongoClient', err)), this._crudClient !== this._metadataClient && - this._crudClient - ?.close(true) - .catch((err) => debug('failed to close MongoClient', err)), + this._crudClient + ?.close(true) + .catch((err) => debug('failed to close MongoClient', err)), this._tunnel ?.close() .catch((err) => debug('failed to close tunnel', err)), @@ -1655,7 +1655,7 @@ export class DataServiceImpl extends EventEmitter implements DataService { ); } - indexes(ns: string, options: unknown, callback: Callback): void { + indexes(ns: string, options: unknown, callback: Callback): void { const logop = this._startLogOp( mongoLogId(1_001_000_047), 'Listing indexes', @@ -2582,11 +2582,11 @@ export class DataServiceImpl extends EventEmitter implements DataService { tlsOptions: autoEncryptionOptions.tlsOptions, proxyOptions: proxyHost ? { - proxyHost, - proxyPort, - proxyUsername, - proxyPassword, - } + proxyHost, + proxyPort, + proxyUsername, + proxyPassword, + } : undefined, }); } From 44c96d9e2cef0d509dd0dcd8461b5ad70b6be2c2 Mon Sep 17 00:00:00 2001 From: Basit Date: Tue, 23 Aug 2022 22:39:27 +0200 Subject: [PATCH 03/12] lint --- .../src/modules/create-index/index.ts | 13 +++-- .../src/modules/data-service.ts | 7 ++- .../compass-indexes/src/modules/indexes.ts | 49 ++++++++----------- .../src/modules/sort-column.ts | 2 +- packages/compass-indexes/src/typings.d.ts | 2 +- 5 files changed, 37 insertions(+), 36 deletions(-) diff --git a/packages/compass-indexes/src/modules/create-index/index.ts b/packages/compass-indexes/src/modules/create-index/index.ts index 3d651b79003..3fb28b0c282 100644 --- a/packages/compass-indexes/src/modules/create-index/index.ts +++ b/packages/compass-indexes/src/modules/create-index/index.ts @@ -3,7 +3,11 @@ import { combineReducers } from 'redux'; import type { AnyAction, Dispatch } from 'redux'; import { createLoggerAndTelemetry } from '@mongodb-js/compass-logging'; import queryParser from 'mongodb-query-parser'; -import type { IndexSpecification, CreateIndexesOptions, IndexDirection, AnyError } from 'mongodb'; +import type { + IndexSpecification, + CreateIndexesOptions, + IndexDirection, +} from 'mongodb'; import dataService from '../data-service'; import appRegistry, { @@ -162,7 +166,8 @@ export const createIndex = () => { } // Check for collaction errors. - const collation = queryParser.isCollationValid(state.collationString) || undefined; + const collation = + queryParser.isCollationValid(state.collationString) || undefined; if (state.useCustomCollation && !collation) { dispatch(handleError('You must provide a valid collation object')); return; @@ -193,7 +198,9 @@ export const createIndex = () => { } if (state.useWildcardProjection) { try { - options.wildcardProjection = EJSON.parse(state.wildcardProjection) as Document; + options.wildcardProjection = EJSON.parse( + state.wildcardProjection + ) as Document; } catch (err) { dispatch(handleError(`Bad WildcardProjection: ${String(err)}`)); return; diff --git a/packages/compass-indexes/src/modules/data-service.ts b/packages/compass-indexes/src/modules/data-service.ts index c6c90621030..451962437e8 100644 --- a/packages/compass-indexes/src/modules/data-service.ts +++ b/packages/compass-indexes/src/modules/data-service.ts @@ -2,7 +2,7 @@ import type { DataService } from 'mongodb-data-service'; enum ActionTypes { DataServiceConnected = 'indexes/data-service/DATA_SERVICE_CONNECTED', -}; +} type DataServiceConnectedAction = { type: ActionTypes.DataServiceConnected; @@ -13,7 +13,10 @@ type State = DataService | null; const INITIAL_STATE: State = null; -export default function reducer(state = INITIAL_STATE, action: DataServiceConnectedAction) { +export default function reducer( + state = INITIAL_STATE, + action: DataServiceConnectedAction +) { if (action.type === ActionTypes.DataServiceConnected) { return action.dataService; } diff --git a/packages/compass-indexes/src/modules/indexes.ts b/packages/compass-indexes/src/modules/indexes.ts index c728bb382e8..5370f276f5f 100644 --- a/packages/compass-indexes/src/modules/indexes.ts +++ b/packages/compass-indexes/src/modules/indexes.ts @@ -75,9 +75,7 @@ export default function reducer(state: State = INITIAL_STATE, action: Actions) { return state; } -export const loadIndexes = ( - indexes: IndexDefinition[] -): LoadIndexesAction => ({ +export const loadIndexes = (indexes: IndexDefinition[]): LoadIndexesAction => ({ type: ActionTypes.LoadIndexes, indexes, }); @@ -85,12 +83,7 @@ export const loadIndexes = ( export const sortIndexes = ( column: SortColumn, order: SortDirection -): ThunkAction< - void, - RootState, - void, - SortIndexesAction -> => { +): ThunkAction => { return (dispatch, getState) => { const { indexes } = getState(); dispatch({ @@ -103,7 +96,11 @@ export const sortIndexes = ( }; const _handleIndexesChanged = ( - dispatch: ThunkDispatch, + dispatch: ThunkDispatch< + RootState, + void, + RefreshFinishedAction | LoadIndexesAction + >, indexes: IndexDefinition[] ) => { dispatch(loadIndexes(indexes)); @@ -134,23 +131,19 @@ export const fetchIndexes = (): ThunkAction< return; } - dataService.indexes( - namespace, - {}, - (err: any, indexes: Document[]) => { - if (err) { - dispatch(handleError(err as IndexesError)); - return _handleIndexesChanged(dispatch, []); - } - // Set the `ns` field manually as it is not returned from the server - // since version 4.4. - for (const index of indexes) { - index.ns = namespace; - } - const ixs = _mapAndSort(indexes, sortColumn, sortOrder); - return _handleIndexesChanged(dispatch, ixs); + dataService.indexes(namespace, {}, (err: any, indexes: Document[]) => { + if (err) { + dispatch(handleError(err as IndexesError)); + return _handleIndexesChanged(dispatch, []); } - ); + // Set the `ns` field manually as it is not returned from the server + // since version 4.4. + for (const index of indexes) { + index.ns = namespace; + } + const ixs = _mapAndSort(indexes, sortColumn, sortOrder); + return _handleIndexesChanged(dispatch, ixs); + }); }; }; @@ -194,9 +187,7 @@ const _mapColumnToProp = (column: SortColumn): SortField => { * Converts the raw index data (from ampersand) to * Index models (IndexDefinition) and adds computed props. */ -const _convertToModels = ( - indexes: Document[] -): IndexDefinition[] => { +const _convertToModels = (indexes: Document[]): IndexDefinition[] => { const sizes = indexes.map((index) => index.size); const maxSize = Math.max(...sizes); diff --git a/packages/compass-indexes/src/modules/sort-column.ts b/packages/compass-indexes/src/modules/sort-column.ts index 0aa817bc1b6..2b2b187b560 100644 --- a/packages/compass-indexes/src/modules/sort-column.ts +++ b/packages/compass-indexes/src/modules/sort-column.ts @@ -13,4 +13,4 @@ export default function reducer( return action.column; } return state; -} \ No newline at end of file +} diff --git a/packages/compass-indexes/src/typings.d.ts b/packages/compass-indexes/src/typings.d.ts index bff4ede34c0..c76448ce1e0 100644 --- a/packages/compass-indexes/src/typings.d.ts +++ b/packages/compass-indexes/src/typings.d.ts @@ -10,4 +10,4 @@ declare module 'hadron-react-components'; declare module 'hadron-app'; declare module '@mongodb-js/mongodb-redux-common/app-registry'; declare module 'lodash.contains'; -declare module 'mongodb-index-model'; \ No newline at end of file +declare module 'mongodb-index-model'; From f63cb7e6b2fb603a4a2d1e92c459d3dc5994b592 Mon Sep 17 00:00:00 2001 From: Basit Date: Wed, 24 Aug 2022 10:57:18 +0200 Subject: [PATCH 04/12] ts clean up --- .../components/create-index-modal/create-index-modal.tsx | 2 +- .../compass-indexes/src/components/indexes/indexes.tsx | 9 +++++---- packages/compass-indexes/src/modules/error.ts | 4 ++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/compass-indexes/src/components/create-index-modal/create-index-modal.tsx b/packages/compass-indexes/src/components/create-index-modal/create-index-modal.tsx index b712074953d..5035ac206d9 100644 --- a/packages/compass-indexes/src/components/create-index-modal/create-index-modal.tsx +++ b/packages/compass-indexes/src/components/create-index-modal/create-index-modal.tsx @@ -110,7 +110,7 @@ function CreateIndexModal({ resetForm: () => void; isVisible: boolean; namespace: string; - error: string | null; + error?: string; clearError: () => void; inProgress: boolean; createIndex: () => void; diff --git a/packages/compass-indexes/src/components/indexes/indexes.tsx b/packages/compass-indexes/src/components/indexes/indexes.tsx index 2f5c905e4d3..a7ddb1b2934 100644 --- a/packages/compass-indexes/src/components/indexes/indexes.tsx +++ b/packages/compass-indexes/src/components/indexes/indexes.tsx @@ -13,6 +13,7 @@ import type { import { IndexesToolbar } from '../indexes-toolbar/indexes-toolbar'; import { IndexesTable } from '../indexes-table/indexes-table'; import { refreshIndexes } from '../../modules/is-refreshing'; +import type { RootState } from '../../modules'; const containerStyles = css({ margin: spacing[3], @@ -98,15 +99,15 @@ const mapState = ({ description, error, isRefreshing, - appRegistry: { localAppRegistry }, -}: any) => ({ + appRegistry, +}: RootState) => ({ indexes, isWritable, isReadonly, isReadonlyView, description, error, - localAppRegistry, + localAppRegistry: (appRegistry as any).localAppRegistry, isRefreshing, }); @@ -115,4 +116,4 @@ const mapDispatch = { onRefresh: refreshIndexes, }; -export default connect(mapState, mapDispatch)(Indexes as any); +export default connect(mapState, mapDispatch)(Indexes); diff --git a/packages/compass-indexes/src/modules/error.ts b/packages/compass-indexes/src/modules/error.ts index 9bd5f1a6676..5d16bec5475 100644 --- a/packages/compass-indexes/src/modules/error.ts +++ b/packages/compass-indexes/src/modules/error.ts @@ -19,8 +19,8 @@ type ClearErrorAction = { export type Actions = HandleErrorAction | ClearErrorAction; -type State = string | null; -export const INITIAL_STATE: State = null; +type State = string | undefined; +export const INITIAL_STATE: State = undefined; export default function reducer(state: State = INITIAL_STATE, action: Actions) { if (action.type === ActionTypes.HandleError) { From fc99df762937d0709c7752e17ad8338bdfc546d5 Mon Sep 17 00:00:00 2001 From: Basit Date: Wed, 24 Aug 2022 13:24:28 +0200 Subject: [PATCH 05/12] fix tests --- .../src/modules/create-index/fields.spec.js | 4 +- .../src/modules/create-index/index.spec.js | 18 +- ...a-service.spec.js => data-service.spec.ts} | 20 +- .../src/modules/data-service.ts | 2 +- .../src/modules/drop-index/index.spec.js | 10 +- .../modules/{error.spec.js => error.spec.ts} | 16 +- packages/compass-indexes/src/modules/error.ts | 8 +- .../{indexes.spec.js => indexes.spec.ts} | 299 +++++++----------- .../compass-indexes/src/modules/indexes.ts | 2 +- .../src/modules/sort-column.spec.js | 24 -- .../src/modules/sort-column.spec.ts | 21 ++ .../src/modules/sort-column.ts | 2 +- .../src/modules/sort-order.spec.js | 24 -- .../src/modules/sort-order.spec.ts | 33 ++ .../compass-indexes/src/modules/sort-order.ts | 2 +- 15 files changed, 210 insertions(+), 275 deletions(-) rename packages/compass-indexes/src/modules/{data-service.spec.js => data-service.spec.ts} (55%) rename packages/compass-indexes/src/modules/{error.spec.js => error.spec.ts} (66%) rename packages/compass-indexes/src/modules/{indexes.spec.js => indexes.spec.ts} (54%) delete mode 100644 packages/compass-indexes/src/modules/sort-column.spec.js create mode 100644 packages/compass-indexes/src/modules/sort-column.spec.ts delete mode 100644 packages/compass-indexes/src/modules/sort-order.spec.js create mode 100644 packages/compass-indexes/src/modules/sort-order.spec.ts diff --git a/packages/compass-indexes/src/modules/create-index/fields.spec.js b/packages/compass-indexes/src/modules/create-index/fields.spec.js index b3a7d606bbb..37e9097de16 100644 --- a/packages/compass-indexes/src/modules/create-index/fields.spec.js +++ b/packages/compass-indexes/src/modules/create-index/fields.spec.js @@ -13,7 +13,7 @@ import reducer, { changeFields, CHANGE_FIELDS, } from '../create-index/fields'; -import { HANDLE_ERROR } from '../error'; +import { ActionTypes as ErrorActionTypes } from '../error'; import { CHANGE_SCHEMA_FIELDS } from '../create-index/schema-fields'; import { ActionTypes } from '../create-index/new-index-field'; @@ -159,7 +159,7 @@ describe('create index fields module', function () { it('returns handleError action with duplicate name', function () { const dispatch = (res) => { expect(res).to.deep.equal({ - type: HANDLE_ERROR, + type: ErrorActionTypes.HandleError, error: 'Index keys must be unique', }); actionSpy(); diff --git a/packages/compass-indexes/src/modules/create-index/index.spec.js b/packages/compass-indexes/src/modules/create-index/index.spec.js index 19aeef4215f..ad49af76dea 100644 --- a/packages/compass-indexes/src/modules/create-index/index.spec.js +++ b/packages/compass-indexes/src/modules/create-index/index.spec.js @@ -2,7 +2,7 @@ import { expect } from 'chai'; import sinon from 'sinon'; import { createIndex } from '../create-index'; -import { HANDLE_ERROR, CLEAR_ERROR } from '../error'; +import { ActionTypes as ErrorActionTypes } from '../error'; import { TOGGLE_IN_PROGRESS } from '../in-progress'; import { TOGGLE_IS_VISIBLE } from '../is-visible'; import { RESET } from '../reset'; @@ -34,7 +34,7 @@ describe('create index module', function () { it('errors if fields are undefined', function () { const dispatch = (res) => { expect(res).to.deep.equal({ - type: HANDLE_ERROR, + type: ErrorActionTypes.HandleError, error: 'You must select a field name and type', }); errorSpy(); @@ -48,7 +48,7 @@ describe('create index module', function () { it('errors if TTL is not number', function () { const dispatch = (res) => { expect(res).to.deep.equal({ - type: HANDLE_ERROR, + type: ErrorActionTypes.HandleError, error: 'Bad TTL: "abc"', }); errorSpy(); @@ -64,7 +64,7 @@ describe('create index module', function () { it('errors if PFE is not JSON', function () { const dispatch = (res) => { expect(res).to.deep.equal({ - type: HANDLE_ERROR, + type: ErrorActionTypes.HandleError, error: 'Bad PartialFilterExpression: SyntaxError: Unexpected token a in JSON at position 0', }); @@ -88,7 +88,7 @@ describe('create index module', function () { case RESET: resetSpy(); break; - case CLEAR_ERROR: + case ErrorActionTypes.ClearError: clearErrorSpy(); break; case TOGGLE_IS_VISIBLE: @@ -158,7 +158,7 @@ describe('create index module', function () { case RESET: resetSpy(); break; - case CLEAR_ERROR: + case ErrorActionTypes.ClearError: clearErrorSpy(); break; case TOGGLE_IS_VISIBLE: @@ -221,10 +221,10 @@ describe('create index module', function () { case TOGGLE_IN_PROGRESS: progressSpy(); break; - case HANDLE_ERROR: + case ErrorActionTypes.HandleError: expect(res).to.deep.equal({ - type: HANDLE_ERROR, - error: 'test err', + type: ErrorActionTypes.HandleError, + error: { message: 'test err' }, }); errorSpy(); break; diff --git a/packages/compass-indexes/src/modules/data-service.spec.js b/packages/compass-indexes/src/modules/data-service.spec.ts similarity index 55% rename from packages/compass-indexes/src/modules/data-service.spec.js rename to packages/compass-indexes/src/modules/data-service.spec.ts index 2d25a902fa1..f6ed2ff3e19 100644 --- a/packages/compass-indexes/src/modules/data-service.spec.js +++ b/packages/compass-indexes/src/modules/data-service.spec.ts @@ -1,16 +1,21 @@ import { expect } from 'chai'; +import type { DataService } from 'mongodb-data-service'; import reducer, { dataServiceConnected, - DATA_SERVICE_CONNECTED, + ActionTypes as DataServiceActions } from './data-service'; +const mockDataService = new class { + indexes() { } +} as any as DataService; + describe('data service module', function () { describe('#dataServiceConnected', function () { - it('returns the DATA_SERVICE_CONNECTED action', function () { - expect(dataServiceConnected('ds')).to.deep.equal({ - type: DATA_SERVICE_CONNECTED, - dataService: 'ds', + it('returns the connected action', function () { + expect(dataServiceConnected(mockDataService)).to.deep.equal({ + type: DataServiceActions.DataServiceConnected, + dataService: mockDataService, }); }); }); @@ -24,9 +29,8 @@ describe('data service module', function () { context('when the action is data service connected', function () { it('returns the new state', function () { - expect(reducer(undefined, dataServiceConnected('ds'))).to.deep.equal( - 'ds' - ); + expect(reducer(undefined, dataServiceConnected(mockDataService))).to.deep.equal( + mockDataService); }); }); }); diff --git a/packages/compass-indexes/src/modules/data-service.ts b/packages/compass-indexes/src/modules/data-service.ts index 451962437e8..61f5846e7dd 100644 --- a/packages/compass-indexes/src/modules/data-service.ts +++ b/packages/compass-indexes/src/modules/data-service.ts @@ -1,6 +1,6 @@ import type { DataService } from 'mongodb-data-service'; -enum ActionTypes { +export enum ActionTypes { DataServiceConnected = 'indexes/data-service/DATA_SERVICE_CONNECTED', } diff --git a/packages/compass-indexes/src/modules/drop-index/index.spec.js b/packages/compass-indexes/src/modules/drop-index/index.spec.js index 2b82bb780d5..80ff06f9091 100644 --- a/packages/compass-indexes/src/modules/drop-index/index.spec.js +++ b/packages/compass-indexes/src/modules/drop-index/index.spec.js @@ -2,7 +2,7 @@ import { expect } from 'chai'; import sinon from 'sinon'; import { dropIndex } from '../drop-index'; -import { HANDLE_ERROR, CLEAR_ERROR } from '../error'; +import { ActionTypes as ErrorActionTypes } from '../error'; import { TOGGLE_IN_PROGRESS } from '../in-progress'; import { TOGGLE_IS_VISIBLE } from '../is-visible'; import { RESET } from '../reset'; @@ -41,7 +41,7 @@ describe('drop index module', function () { case RESET: resetSpy(); break; - case CLEAR_ERROR: + case ErrorActionTypes.ClearError: clearErrorSpy(); break; case TOGGLE_IS_VISIBLE: @@ -88,10 +88,10 @@ describe('drop index module', function () { case TOGGLE_IN_PROGRESS: progressSpy(); break; - case HANDLE_ERROR: + case ErrorActionTypes.HandleError: expect(res).to.deep.equal({ - type: HANDLE_ERROR, - error: 'test err', + type: ErrorActionTypes.HandleError, + error: { message: 'test err' }, }); errorSpy(); break; diff --git a/packages/compass-indexes/src/modules/error.spec.js b/packages/compass-indexes/src/modules/error.spec.ts similarity index 66% rename from packages/compass-indexes/src/modules/error.spec.js rename to packages/compass-indexes/src/modules/error.spec.ts index af760639eb1..95229842654 100644 --- a/packages/compass-indexes/src/modules/error.spec.js +++ b/packages/compass-indexes/src/modules/error.spec.ts @@ -1,11 +1,11 @@ import { expect } from 'chai'; +import { MongoError } from 'mongodb'; import reducer, { + ActionTypes as ErrorActionTypes, INITIAL_STATE, clearError, handleError, - CLEAR_ERROR, - HANDLE_ERROR, } from './error'; describe('handle error name module', function () { @@ -14,8 +14,12 @@ describe('handle error name module', function () { describe('#reducer', function () { context('when an action is provided', function () { context('when the action is handle error', function () { - it('returns the new state', function () { - expect(reducer(undefined, handleError(error))).to.equal(error); + it('processes the error', function () { + expect(reducer(undefined, handleError(error))).to.equal(error.message); + expect(reducer(undefined, handleError('something random'))).to.equal('something random'); + expect(reducer(undefined, handleError(new MongoError('legacy error')))).to.equal('legacy error'); + expect(reducer(undefined, handleError(undefined))).to.equal('Unknown error'); + expect(reducer(undefined, handleError(null))).to.equal('Unknown error'); }); }); @@ -36,7 +40,7 @@ describe('handle error name module', function () { describe('#handleError', function () { it('returns the action', function () { expect(handleError(error)).to.deep.equal({ - type: HANDLE_ERROR, + type: ErrorActionTypes.HandleError, error: error, }); }); @@ -45,7 +49,7 @@ describe('handle error name module', function () { describe('#clearError', function () { it('returns the action', function () { expect(clearError()).to.deep.equal({ - type: CLEAR_ERROR, + type: ErrorActionTypes.ClearError, }); }); }); diff --git a/packages/compass-indexes/src/modules/error.ts b/packages/compass-indexes/src/modules/error.ts index 5d16bec5475..86f80c13886 100644 --- a/packages/compass-indexes/src/modules/error.ts +++ b/packages/compass-indexes/src/modules/error.ts @@ -3,7 +3,7 @@ import type { AnyError } from 'mongodb'; export type IndexesError = AnyError | string | null | undefined; -enum ActionTypes { +export enum ActionTypes { HandleError = 'indexes/error/HANDLE_ERROR', ClearError = 'indexes/error/CLEAR_ERROR', } @@ -19,8 +19,8 @@ type ClearErrorAction = { export type Actions = HandleErrorAction | ClearErrorAction; -type State = string | undefined; -export const INITIAL_STATE: State = undefined; +type State = string | null; +export const INITIAL_STATE: State = null; export default function reducer(state: State = INITIAL_STATE, action: Actions) { if (action.type === ActionTypes.HandleError) { @@ -45,7 +45,7 @@ export const clearError = (): ClearErrorAction => ({ * that can happen during index creation/dropping. * Check for data service custom error, then node driver errmsg. */ -export const _parseError = (err: IndexesError): string => { +const _parseError = (err: IndexesError): string => { if (typeof err === 'string') { return err; } diff --git a/packages/compass-indexes/src/modules/indexes.spec.js b/packages/compass-indexes/src/modules/indexes.spec.ts similarity index 54% rename from packages/compass-indexes/src/modules/indexes.spec.js rename to packages/compass-indexes/src/modules/indexes.spec.ts index b06c2c581cc..bd2bfbeadf0 100644 --- a/packages/compass-indexes/src/modules/indexes.spec.js +++ b/packages/compass-indexes/src/modules/indexes.spec.ts @@ -1,224 +1,145 @@ -/* eslint-disable no-use-before-define */ import { expect } from 'chai'; -import sinon from 'sinon'; - +import type { Store } from 'redux'; +import type { DataService } from 'mongodb-data-service'; +import { spy } from 'sinon'; import reducer, { + ActionTypes as IndexesActionTypes, fetchIndexes, loadIndexes, sortIndexes, - LOAD_INDEXES, - SORT_INDEXES, - ASC, - DESC, - DEFAULT, - USAGE, } from './indexes'; - -import { HANDLE_ERROR } from './error'; +import { ActionTypes as IsRefreshingActionTypes } from './is-refreshing'; +import { dataServiceConnected } from './data-service'; +import type { RootState } from '.'; +import configureStore from '../stores/store'; describe('indexes module', function () { + let store: Store; + beforeEach(function () { + store = configureStore({ namespace: 'citibike.trips' }); + }); describe('#reducer', function () { - context('when an action is provided', function () { - context('when the action is LOAD_INDEXES', function () { - it('returns the default sorted', function () { - expect(reducer(undefined, loadIndexes(defaultSort))).to.deep.equal( - defaultSort - ); - }); + describe('when loading indexes', function () { + it('uses default sort order and column when user has not selected any', function () { + expect(reducer(undefined, loadIndexes(defaultSort as any))).to.deep.equal( + defaultSort + ); }); + }); - context('when the action is SORT_INDEXES', function () { - context('when the column is Usage', function () { - context('when sorting asc', function () { - it('returns the sorted indexes list', function () { - const dispatch = (args) => args; - const getState = () => { - return { - indexes: defaultSort, - }; - }; - const result = reducer( - undefined, - sortIndexes(USAGE, ASC)(dispatch, getState) - ); - expect(result).to.deep.equal(usageSort); - }); - }); - - context('when sorting desc', function () { - it('returns the sorted indexes list', function () { - const dispatch = (args) => args; - const getState = () => { - return { - indexes: defaultSort, - }; - }; - const result = reducer( - undefined, - sortIndexes(USAGE, DESC)(dispatch, getState) - ); - expect(result).to.deep.equal(usageSortDesc); - }); - }); + describe('sorting indexes', function () { + describe('Usage column', function () { + it('asc sort', function () { + store.dispatch(loadIndexes(defaultSort as any)); + store.dispatch(sortIndexes('Usage', 'asc') as any); + const state = store.getState(); + expect(state.indexes).to.deep.equal(usageSort); }); - - context('when the column is Name and Definition', function () { - context('when sorting asc', function () { - it('returns the sorted indexes list', function () { - const dispatch = (args) => args; - const getState = () => { - return { - indexes: usageSort, - }; - }; - const result = reducer( - undefined, - sortIndexes(DEFAULT, ASC)(dispatch, getState) - ); - expect(result).to.deep.equal(defaultSort); - }); - }); - - context('when sorting desc', function () { - it('returns the sorted indexes list', function () { - const dispatch = (args) => args; - const getState = () => { - return { - indexes: usageSort, - }; - }; - const result = reducer( - undefined, - sortIndexes(DEFAULT, DESC)(dispatch, getState) - ); - expect(result).to.deep.equal(defaultSortDesc); - }); - }); + it('desc sort', function () { + store.dispatch(loadIndexes(defaultSort as any)); + store.dispatch(sortIndexes('Usage', 'desc') as any); + const state = store.getState(); + expect(state.indexes).to.deep.equal(usageSortDesc); }); }); - context('when an action is not provided', function () { - it('returns the default state', function () { - expect(reducer(undefined, {})).to.deep.equal([]); + describe('Name and Definition column', function () { + it('asc sort', function () { + store.dispatch(loadIndexes(usageSort as any)); + store.dispatch(sortIndexes('Name and Definition', 'asc') as any); + const state = store.getState(); + expect(state.indexes).to.deep.equal(defaultSort); + }); + it('desc sort', function () { + store.dispatch(loadIndexes(usageSort as any)); + store.dispatch(sortIndexes('Name and Definition', 'desc') as any); + const state = store.getState(); + expect(state.indexes).to.deep.equal(defaultSortDesc); }); }); }); - }); - describe('#loadIndexes', function () { - it('returns the action', function () { - expect(loadIndexes([])).to.deep.equal({ - type: LOAD_INDEXES, - indexes: [], + context('when no action is provided', function () { + it('returns the default state', function () { + expect(reducer(undefined, {})).to.deep.equal([]); }); }); }); - describe('#sortIndexes', function () { - it('returns the action', function () { - const dispatch = (x) => x; - const getState = () => ({ indexes: [] }); - expect( - sortIndexes('Database Name', DESC)(dispatch, getState) - ).to.deep.equal({ - type: SORT_INDEXES, - indexes: [], - column: 'Database Name', - order: DESC, - }); - }); + it('#loadIndexes action', function () { + store.dispatch(loadIndexes([])); + expect(store.getState().indexes).to.deep.equal([]); + + store.dispatch(loadIndexes(usageSort as any)); + expect(JSON.stringify(store.getState().indexes)).to.equal(JSON.stringify(usageSort)); }); + + it('#sortIndexes action', function () { + store.dispatch(loadIndexes(defaultSort as any)); + store.dispatch(sortIndexes('Name and Definition', 'desc') as any); + const state = store.getState(); + expect(state.sortColumn).to.equal('Name and Definition'); + expect(state.sortOrder).to.equal('desc'); + expect(JSON.stringify(state.indexes)).to.equal(JSON.stringify(defaultSortDesc)); + }); + describe('#fetchIndexes', function () { - let actionSpy; - let emitSpy; - beforeEach(function () { - actionSpy = sinon.spy(); - emitSpy = sinon.spy(); - }); - afterEach(function () { - actionSpy = null; - emitSpy = null; - }); - it('returns loadIndexes action with empty list for readonly', function () { - const dispatch = (res) => { - if (typeof res !== 'function') { - expect(res).to.deep.equal({ type: LOAD_INDEXES, indexes: [] }); - actionSpy(); - } + it('sets indexes to empty array for readonly', function () { + + const dispatchSpy = spy(); + const dispatch = (x: any) => { + dispatchSpy(x); + return x; }; - const state = () => ({ - appRegistry: { - emit: emitSpy, - }, - isReadonly: true, - namespace: 'citibike.trips', - }); - fetchIndexes()(dispatch, state); - expect(actionSpy.calledOnce).to.equal(true); + const getState = () => ({ isReadonly: true } as any as RootState); + + fetchIndexes()(dispatch, getState); + + expect(dispatchSpy.callCount).to.equal(3); + + expect(dispatchSpy.getCall(0).args[0]).to.deep.equal({ type: IndexesActionTypes.LoadIndexes, indexes: [] }); + expect(dispatchSpy.getCall(1).args[0]).to.deep.equal({ type: IsRefreshingActionTypes.RefreshFinished }); + expect(typeof dispatchSpy.getCall(2).args[0] === 'function').to.true; }); - it('returns loadIndexes action with error for error state', function () { - const dispatch = (res) => { - if (typeof res !== 'function') { - if (res.type === LOAD_INDEXES) { - expect(res).to.deep.equal({ type: LOAD_INDEXES, indexes: [] }); - actionSpy(); - } else if (res.type === HANDLE_ERROR) { - expect(res).to.deep.equal({ - type: HANDLE_ERROR, - error: 'error message!', - }); - actionSpy(); - } else { - expect(true, 'unknown action called').to.be.false(); - } - } - }; - const state = () => ({ - appRegistry: { - emit: emitSpy, - }, - isReadonly: false, - dataService: { - indexes: (ns, opts, cb) => { - cb({ message: 'error message!' }); - }, - isConnected: () => true, - }, - namespace: 'citibike.trips', + it('sets indexes to empty array when there is an error', function () { + const error = new Error('failed to connect to server'); + // Set some data to validate the empty array condition + store.dispatch({ + type: IndexesActionTypes.LoadIndexes, + indexes: defaultSort }); - fetchIndexes()(dispatch, state); - expect(actionSpy.calledTwice).to.equal(true); + // Mock dataService.indexes + store.dispatch(dataServiceConnected({ + indexes: (ns: any, opts: any, cb: any) => { + cb(error); + } + } as DataService)); + + store.dispatch(fetchIndexes() as any); + + const state = store.getState(); + expect(state.indexes).to.deep.equal([]); + expect(state.error).to.equal(error.message); + expect(state.isRefreshing).to.equal(false); }); - it('returns loadIndexes action with sorted and modelled indexes', function () { - const dispatch = (res) => { - if (typeof res !== 'function') { - expect(Object.keys(res)).to.deep.equal(['type', 'indexes']); - expect(res.type).to.equal(LOAD_INDEXES); - expect(JSON.stringify(res.indexes, null, '\n')).to.equal( - JSON.stringify(defaultSort, null, '\n') - ); - actionSpy(); + it('sets indexes when fetched successfully', function () { + // Set indexes to empty + store.dispatch(loadIndexes([])); + store.dispatch(sortIndexes('Name and Definition', 'asc') as any); + store.dispatch(dataServiceConnected({ + indexes: (_ns: any, _opts: any, cb: any) => { + cb(null, fromDB); } - }; - const state = () => ({ - appRegistry: { - emit: emitSpy, - }, - isReadonly: false, - dataService: { - indexes: (ns, opts, cb) => { - cb(null, fromDB); - }, - isConnected: () => true, - }, - sortColumn: DEFAULT, - sortOrder: ASC, - namespace: 'citibike.trips', - }); - fetchIndexes()(dispatch, state); - expect(actionSpy.calledOnce).to.equal(true); + } as DataService)); + + store.dispatch(fetchIndexes() as any); + + const state = store.getState(); + expect(JSON.stringify(state.indexes)).to.equal(JSON.stringify(defaultSort)); + expect(state.error).to.be.null; + expect(state.isRefreshing).to.equal(false); }); }); }); diff --git a/packages/compass-indexes/src/modules/indexes.ts b/packages/compass-indexes/src/modules/indexes.ts index 5370f276f5f..5ade73af31f 100644 --- a/packages/compass-indexes/src/modules/indexes.ts +++ b/packages/compass-indexes/src/modules/indexes.ts @@ -104,8 +104,8 @@ const _handleIndexesChanged = ( indexes: IndexDefinition[] ) => { dispatch(loadIndexes(indexes)); - dispatch(localAppRegistryEmit('indexes-changed', indexes)); dispatch({ type: RefreshActionTypes.RefreshFinished }); + dispatch(localAppRegistryEmit('indexes-changed', indexes)); }; export const fetchIndexes = (): ThunkAction< diff --git a/packages/compass-indexes/src/modules/sort-column.spec.js b/packages/compass-indexes/src/modules/sort-column.spec.js deleted file mode 100644 index 6c5ad7e5541..00000000000 --- a/packages/compass-indexes/src/modules/sort-column.spec.js +++ /dev/null @@ -1,24 +0,0 @@ -import { expect } from 'chai'; - -import reducer, { INITIAL_STATE } from './sort-column'; -import { sortIndexes } from './indexes'; - -describe('sort column module', function () { - describe('#reducer', function () { - context('when an action is provided', function () { - it('returns the new column', function () { - const dispatch = (x) => x; - const getState = () => ({}); - expect( - reducer(undefined, sortIndexes('Size', '')(dispatch, getState)) - ).to.equal('Size'); - }); - }); - - context('when an action is not provided', function () { - it('returns the default state', function () { - expect(reducer(undefined, {})).to.equal(INITIAL_STATE); - }); - }); - }); -}); diff --git a/packages/compass-indexes/src/modules/sort-column.spec.ts b/packages/compass-indexes/src/modules/sort-column.spec.ts new file mode 100644 index 00000000000..8dc7d733df0 --- /dev/null +++ b/packages/compass-indexes/src/modules/sort-column.spec.ts @@ -0,0 +1,21 @@ +import { expect } from 'chai'; + +import reducer, { INITIAL_STATE } from './sort-column'; +import { ActionTypes as IndexesActionTypes } from './indexes'; + +describe('sort column reducer', function () { + it('when action is provied with column', function () { + expect( + reducer(undefined, { type: IndexesActionTypes.SortIndexes, column: 'Size' }) + ).to.equal('Size'); + expect( + reducer('Size', { type: IndexesActionTypes.SortIndexes, column: 'Type' }) + ).to.equal('Type'); + expect( + reducer('Type', { type: IndexesActionTypes.SortIndexes, column: 'Usage' }) + ).to.equal('Usage'); + }); + it('when action is not provided, it returns default state', function () { + expect(reducer(undefined, {} as any)).to.equal(INITIAL_STATE); + }); +}); diff --git a/packages/compass-indexes/src/modules/sort-column.ts b/packages/compass-indexes/src/modules/sort-column.ts index 2b2b187b560..1ebe4e2e13d 100644 --- a/packages/compass-indexes/src/modules/sort-column.ts +++ b/packages/compass-indexes/src/modules/sort-column.ts @@ -7,7 +7,7 @@ export const INITIAL_STATE: State = 'Name and Definition'; export default function reducer( state = INITIAL_STATE, - action: SortIndexesAction + action: Pick ) { if (action.type === IndexesActionTypes.SortIndexes) { return action.column; diff --git a/packages/compass-indexes/src/modules/sort-order.spec.js b/packages/compass-indexes/src/modules/sort-order.spec.js deleted file mode 100644 index 32dcec67430..00000000000 --- a/packages/compass-indexes/src/modules/sort-order.spec.js +++ /dev/null @@ -1,24 +0,0 @@ -import { expect } from 'chai'; - -import reducer, { INITIAL_STATE } from './sort-order'; -import { sortIndexes } from './indexes'; - -describe('sort order module', function () { - describe('#reducer', function () { - context('when an action is provided', function () { - it('returns the new order', function () { - const dispatch = (x) => x; - const getState = () => ({}); - expect( - reducer(undefined, sortIndexes('', 'desc')(dispatch, getState)) - ).to.equal('desc'); - }); - }); - - context('when an action is not provided', function () { - it('returns the default state', function () { - expect(reducer(undefined, {})).to.equal(INITIAL_STATE); - }); - }); - }); -}); diff --git a/packages/compass-indexes/src/modules/sort-order.spec.ts b/packages/compass-indexes/src/modules/sort-order.spec.ts new file mode 100644 index 00000000000..70af22a8e23 --- /dev/null +++ b/packages/compass-indexes/src/modules/sort-order.spec.ts @@ -0,0 +1,33 @@ +import { expect } from 'chai'; + +import reducer, { INITIAL_STATE } from './sort-order'; +import { ActionTypes as IndexesActionTypes } from './indexes'; + +describe('sort order reducer', function () { + it('when action is provided', function () { + expect( + reducer('desc', { + type: IndexesActionTypes.SortIndexes, + order: 'asc' + }) + ).to.equal('asc'); + + expect( + reducer('asc', { + type: IndexesActionTypes.SortIndexes, + order: 'desc' + }) + ).to.equal('desc'); + + expect( + reducer(undefined, { + type: IndexesActionTypes.SortIndexes, + order: 'desc' + }) + ).to.equal('desc'); + }); + + it('when action is not provided, it returns the default state', function () { + expect(reducer(undefined, {} as any)).to.equal(INITIAL_STATE); + }); +}); diff --git a/packages/compass-indexes/src/modules/sort-order.ts b/packages/compass-indexes/src/modules/sort-order.ts index 75e4ec79c04..c93e387a767 100644 --- a/packages/compass-indexes/src/modules/sort-order.ts +++ b/packages/compass-indexes/src/modules/sort-order.ts @@ -7,7 +7,7 @@ export const INITIAL_STATE: State = 'asc'; export default function reducer( state = INITIAL_STATE, - action: SortIndexesAction + action: Pick ) { if (action.type === IndexesActionTypes.SortIndexes) { return action.order; From 4187544842dc4a7ba0e57bdfb4bb16ab3ab70526 Mon Sep 17 00:00:00 2001 From: Basit Date: Wed, 24 Aug 2022 13:27:48 +0200 Subject: [PATCH 06/12] fix ts --- .../src/components/create-index-modal/create-index-modal.tsx | 2 +- .../src/components/indexes-toolbar/indexes-toolbar.spec.tsx | 2 +- .../src/components/indexes-toolbar/indexes-toolbar.tsx | 2 +- .../compass-indexes/src/components/indexes/indexes.spec.tsx | 2 +- packages/compass-indexes/src/components/indexes/indexes.tsx | 2 +- packages/compass-indexes/src/modules/data-service.spec.ts | 2 +- packages/compass-indexes/src/modules/error.spec.ts | 2 +- packages/compass-indexes/src/modules/indexes.spec.ts | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/compass-indexes/src/components/create-index-modal/create-index-modal.tsx b/packages/compass-indexes/src/components/create-index-modal/create-index-modal.tsx index 5035ac206d9..b712074953d 100644 --- a/packages/compass-indexes/src/components/create-index-modal/create-index-modal.tsx +++ b/packages/compass-indexes/src/components/create-index-modal/create-index-modal.tsx @@ -110,7 +110,7 @@ function CreateIndexModal({ resetForm: () => void; isVisible: boolean; namespace: string; - error?: string; + error: string | null; clearError: () => void; inProgress: boolean; createIndex: () => void; diff --git a/packages/compass-indexes/src/components/indexes-toolbar/indexes-toolbar.spec.tsx b/packages/compass-indexes/src/components/indexes-toolbar/indexes-toolbar.spec.tsx index 8fd210b1735..06779a118bd 100644 --- a/packages/compass-indexes/src/components/indexes-toolbar/indexes-toolbar.spec.tsx +++ b/packages/compass-indexes/src/components/indexes-toolbar/indexes-toolbar.spec.tsx @@ -14,7 +14,7 @@ const renderIndexesToolbar = ( render( {}} diff --git a/packages/compass-indexes/src/components/indexes/indexes.tsx b/packages/compass-indexes/src/components/indexes/indexes.tsx index a7ddb1b2934..f71b2970788 100644 --- a/packages/compass-indexes/src/components/indexes/indexes.tsx +++ b/packages/compass-indexes/src/components/indexes/indexes.tsx @@ -41,7 +41,7 @@ type IndexesProps = { isReadonly: boolean; isReadonlyView: boolean; description?: string; - error?: string; + error: string | null; localAppRegistry: AppRegistry; isRefreshing: boolean; onSortTable: (name: SortColumn, direction: SortDirection) => void; diff --git a/packages/compass-indexes/src/modules/data-service.spec.ts b/packages/compass-indexes/src/modules/data-service.spec.ts index f6ed2ff3e19..ad71429f98e 100644 --- a/packages/compass-indexes/src/modules/data-service.spec.ts +++ b/packages/compass-indexes/src/modules/data-service.spec.ts @@ -23,7 +23,7 @@ describe('data service module', function () { describe('#reducer', function () { context('when the action is not data service connected', function () { it('returns the default state', function () { - expect(reducer(undefined, { type: 'test' })).to.deep.equal(null); + expect(reducer(undefined, { type: 'test' } as any)).to.deep.equal(null); }); }); diff --git a/packages/compass-indexes/src/modules/error.spec.ts b/packages/compass-indexes/src/modules/error.spec.ts index 95229842654..0426f1413fc 100644 --- a/packages/compass-indexes/src/modules/error.spec.ts +++ b/packages/compass-indexes/src/modules/error.spec.ts @@ -32,7 +32,7 @@ describe('handle error name module', function () { context('when an action is not provided', function () { it('returns the default state', function () { - expect(reducer(undefined, {})).to.equal(INITIAL_STATE); + expect(reducer(undefined, {} as any)).to.equal(INITIAL_STATE); }); }); }); diff --git a/packages/compass-indexes/src/modules/indexes.spec.ts b/packages/compass-indexes/src/modules/indexes.spec.ts index bd2bfbeadf0..010fae1b8e0 100644 --- a/packages/compass-indexes/src/modules/indexes.spec.ts +++ b/packages/compass-indexes/src/modules/indexes.spec.ts @@ -61,7 +61,7 @@ describe('indexes module', function () { context('when no action is provided', function () { it('returns the default state', function () { - expect(reducer(undefined, {})).to.deep.equal([]); + expect(reducer(undefined, {} as any)).to.deep.equal([]); }); }); }); From cf15231d2c3890bef8b46cda1038f87323765f4d Mon Sep 17 00:00:00 2001 From: Basit Date: Wed, 24 Aug 2022 13:28:26 +0200 Subject: [PATCH 07/12] lint --- .../src/modules/data-service.spec.ts | 13 ++--- .../compass-indexes/src/modules/error.spec.ts | 20 +++++-- .../src/modules/indexes.spec.ts | 54 ++++++++++++------- .../src/modules/sort-column.spec.ts | 5 +- .../src/modules/sort-order.spec.ts | 6 +-- 5 files changed, 63 insertions(+), 35 deletions(-) diff --git a/packages/compass-indexes/src/modules/data-service.spec.ts b/packages/compass-indexes/src/modules/data-service.spec.ts index ad71429f98e..dc2b443b461 100644 --- a/packages/compass-indexes/src/modules/data-service.spec.ts +++ b/packages/compass-indexes/src/modules/data-service.spec.ts @@ -3,12 +3,12 @@ import type { DataService } from 'mongodb-data-service'; import reducer, { dataServiceConnected, - ActionTypes as DataServiceActions + ActionTypes as DataServiceActions, } from './data-service'; -const mockDataService = new class { - indexes() { } -} as any as DataService; +const mockDataService = new (class { + indexes() {} +})() as any as DataService; describe('data service module', function () { describe('#dataServiceConnected', function () { @@ -29,8 +29,9 @@ describe('data service module', function () { context('when the action is data service connected', function () { it('returns the new state', function () { - expect(reducer(undefined, dataServiceConnected(mockDataService))).to.deep.equal( - mockDataService); + expect( + reducer(undefined, dataServiceConnected(mockDataService)) + ).to.deep.equal(mockDataService); }); }); }); diff --git a/packages/compass-indexes/src/modules/error.spec.ts b/packages/compass-indexes/src/modules/error.spec.ts index 0426f1413fc..4bf65dcdf8e 100644 --- a/packages/compass-indexes/src/modules/error.spec.ts +++ b/packages/compass-indexes/src/modules/error.spec.ts @@ -15,11 +15,21 @@ describe('handle error name module', function () { context('when an action is provided', function () { context('when the action is handle error', function () { it('processes the error', function () { - expect(reducer(undefined, handleError(error))).to.equal(error.message); - expect(reducer(undefined, handleError('something random'))).to.equal('something random'); - expect(reducer(undefined, handleError(new MongoError('legacy error')))).to.equal('legacy error'); - expect(reducer(undefined, handleError(undefined))).to.equal('Unknown error'); - expect(reducer(undefined, handleError(null))).to.equal('Unknown error'); + expect(reducer(undefined, handleError(error))).to.equal( + error.message + ); + expect(reducer(undefined, handleError('something random'))).to.equal( + 'something random' + ); + expect( + reducer(undefined, handleError(new MongoError('legacy error'))) + ).to.equal('legacy error'); + expect(reducer(undefined, handleError(undefined))).to.equal( + 'Unknown error' + ); + expect(reducer(undefined, handleError(null))).to.equal( + 'Unknown error' + ); }); }); diff --git a/packages/compass-indexes/src/modules/indexes.spec.ts b/packages/compass-indexes/src/modules/indexes.spec.ts index 010fae1b8e0..4a7ccef631f 100644 --- a/packages/compass-indexes/src/modules/indexes.spec.ts +++ b/packages/compass-indexes/src/modules/indexes.spec.ts @@ -21,9 +21,9 @@ describe('indexes module', function () { describe('#reducer', function () { describe('when loading indexes', function () { it('uses default sort order and column when user has not selected any', function () { - expect(reducer(undefined, loadIndexes(defaultSort as any))).to.deep.equal( - defaultSort - ); + expect( + reducer(undefined, loadIndexes(defaultSort as any)) + ).to.deep.equal(defaultSort); }); }); @@ -71,7 +71,9 @@ describe('indexes module', function () { expect(store.getState().indexes).to.deep.equal([]); store.dispatch(loadIndexes(usageSort as any)); - expect(JSON.stringify(store.getState().indexes)).to.equal(JSON.stringify(usageSort)); + expect(JSON.stringify(store.getState().indexes)).to.equal( + JSON.stringify(usageSort) + ); }); it('#sortIndexes action', function () { @@ -80,12 +82,13 @@ describe('indexes module', function () { const state = store.getState(); expect(state.sortColumn).to.equal('Name and Definition'); expect(state.sortOrder).to.equal('desc'); - expect(JSON.stringify(state.indexes)).to.equal(JSON.stringify(defaultSortDesc)); + expect(JSON.stringify(state.indexes)).to.equal( + JSON.stringify(defaultSortDesc) + ); }); describe('#fetchIndexes', function () { it('sets indexes to empty array for readonly', function () { - const dispatchSpy = spy(); const dispatch = (x: any) => { dispatchSpy(x); @@ -97,8 +100,13 @@ describe('indexes module', function () { expect(dispatchSpy.callCount).to.equal(3); - expect(dispatchSpy.getCall(0).args[0]).to.deep.equal({ type: IndexesActionTypes.LoadIndexes, indexes: [] }); - expect(dispatchSpy.getCall(1).args[0]).to.deep.equal({ type: IsRefreshingActionTypes.RefreshFinished }); + expect(dispatchSpy.getCall(0).args[0]).to.deep.equal({ + type: IndexesActionTypes.LoadIndexes, + indexes: [], + }); + expect(dispatchSpy.getCall(1).args[0]).to.deep.equal({ + type: IsRefreshingActionTypes.RefreshFinished, + }); expect(typeof dispatchSpy.getCall(2).args[0] === 'function').to.true; }); @@ -107,14 +115,16 @@ describe('indexes module', function () { // Set some data to validate the empty array condition store.dispatch({ type: IndexesActionTypes.LoadIndexes, - indexes: defaultSort + indexes: defaultSort, }); // Mock dataService.indexes - store.dispatch(dataServiceConnected({ - indexes: (ns: any, opts: any, cb: any) => { - cb(error); - } - } as DataService)); + store.dispatch( + dataServiceConnected({ + indexes: (ns: any, opts: any, cb: any) => { + cb(error); + }, + } as DataService) + ); store.dispatch(fetchIndexes() as any); @@ -128,16 +138,20 @@ describe('indexes module', function () { // Set indexes to empty store.dispatch(loadIndexes([])); store.dispatch(sortIndexes('Name and Definition', 'asc') as any); - store.dispatch(dataServiceConnected({ - indexes: (_ns: any, _opts: any, cb: any) => { - cb(null, fromDB); - } - } as DataService)); + store.dispatch( + dataServiceConnected({ + indexes: (_ns: any, _opts: any, cb: any) => { + cb(null, fromDB); + }, + } as DataService) + ); store.dispatch(fetchIndexes() as any); const state = store.getState(); - expect(JSON.stringify(state.indexes)).to.equal(JSON.stringify(defaultSort)); + expect(JSON.stringify(state.indexes)).to.equal( + JSON.stringify(defaultSort) + ); expect(state.error).to.be.null; expect(state.isRefreshing).to.equal(false); }); diff --git a/packages/compass-indexes/src/modules/sort-column.spec.ts b/packages/compass-indexes/src/modules/sort-column.spec.ts index 8dc7d733df0..e9b80631913 100644 --- a/packages/compass-indexes/src/modules/sort-column.spec.ts +++ b/packages/compass-indexes/src/modules/sort-column.spec.ts @@ -6,7 +6,10 @@ import { ActionTypes as IndexesActionTypes } from './indexes'; describe('sort column reducer', function () { it('when action is provied with column', function () { expect( - reducer(undefined, { type: IndexesActionTypes.SortIndexes, column: 'Size' }) + reducer(undefined, { + type: IndexesActionTypes.SortIndexes, + column: 'Size', + }) ).to.equal('Size'); expect( reducer('Size', { type: IndexesActionTypes.SortIndexes, column: 'Type' }) diff --git a/packages/compass-indexes/src/modules/sort-order.spec.ts b/packages/compass-indexes/src/modules/sort-order.spec.ts index 70af22a8e23..e5afee789fb 100644 --- a/packages/compass-indexes/src/modules/sort-order.spec.ts +++ b/packages/compass-indexes/src/modules/sort-order.spec.ts @@ -8,21 +8,21 @@ describe('sort order reducer', function () { expect( reducer('desc', { type: IndexesActionTypes.SortIndexes, - order: 'asc' + order: 'asc', }) ).to.equal('asc'); expect( reducer('asc', { type: IndexesActionTypes.SortIndexes, - order: 'desc' + order: 'desc', }) ).to.equal('desc'); expect( reducer(undefined, { type: IndexesActionTypes.SortIndexes, - order: 'desc' + order: 'desc', }) ).to.equal('desc'); }); From c9c9cdaad8d742c362df4eb0bc9af998bf94f120 Mon Sep 17 00:00:00 2001 From: Basit Date: Wed, 24 Aug 2022 13:46:57 +0200 Subject: [PATCH 08/12] refresh button tests --- .../indexes-toolbar/indexes-toolbar.spec.tsx | 34 ++++++++++++++++++- .../indexes-toolbar/indexes-toolbar.tsx | 4 +-- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/packages/compass-indexes/src/components/indexes-toolbar/indexes-toolbar.spec.tsx b/packages/compass-indexes/src/components/indexes-toolbar/indexes-toolbar.spec.tsx index 06779a118bd..9847d035315 100644 --- a/packages/compass-indexes/src/components/indexes-toolbar/indexes-toolbar.spec.tsx +++ b/packages/compass-indexes/src/components/indexes-toolbar/indexes-toolbar.spec.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { cleanup, render, screen } from '@testing-library/react'; +import { cleanup, render, screen, within } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { expect } from 'chai'; import AppRegistry from 'hadron-app-registry'; @@ -130,4 +130,36 @@ describe('IndexesToolbar Component', function () { expect(emitSpy.firstCall.args[1]).to.equal(true); }); }); + + describe('when the refresh button is clicked', function () { + it('renders refresh button - enabled state', function () { + renderIndexesToolbar({ + isRefreshing: false, + }); + const refreshButton = screen.getByTestId('refresh-indexes-button'); + expect(refreshButton).to.exist; + expect(refreshButton.getAttribute('disabled')).to.be.null; + }); + + it('renders refresh button - disabled state', function () { + renderIndexesToolbar({ + isRefreshing: true, + }); + const refreshButton = screen.getByTestId('refresh-indexes-button'); + expect(refreshButton).to.exist; + expect(refreshButton.getAttribute('disabled')).to.not.be.null; + expect(within(refreshButton).getByTitle(/refreshing indexes/i)).to.exist; + }); + + it('should call onRefreshIndexes', function () { + const onRefreshIndexesSpy = sinon.spy(); + renderIndexesToolbar({ + onRefreshIndexes: onRefreshIndexesSpy, + }); + const refreshButton = screen.getByTestId('refresh-indexes-button'); + expect(onRefreshIndexesSpy.callCount).to.equal(0); + userEvent.click(refreshButton); + expect(onRefreshIndexesSpy).to.have.been.calledOnce; + }); + }); }); diff --git a/packages/compass-indexes/src/components/indexes-toolbar/indexes-toolbar.tsx b/packages/compass-indexes/src/components/indexes-toolbar/indexes-toolbar.tsx index d7fa4f70f53..1221f311deb 100644 --- a/packages/compass-indexes/src/components/indexes-toolbar/indexes-toolbar.tsx +++ b/packages/compass-indexes/src/components/indexes-toolbar/indexes-toolbar.tsx @@ -60,10 +60,10 @@ export const IndexesToolbar: React.FunctionComponent = ({ const showCreateIndexButton = !isReadonly && !isReadonlyView && !errorMessage; const refreshButtonIcon = isRefreshing ? (
- +
) : ( - + ); return ( From 050cab9d58392f30c0d4d9dfba1eceadbef1fa31 Mon Sep 17 00:00:00 2001 From: Basit Date: Wed, 24 Aug 2022 14:13:28 +0200 Subject: [PATCH 09/12] lint ds --- packages/data-service/src/data-service.ts | 40 +++++++++++------------ 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/packages/data-service/src/data-service.ts b/packages/data-service/src/data-service.ts index c67cfc1e625..fed167e6c28 100644 --- a/packages/data-service/src/data-service.ts +++ b/packages/data-service/src/data-service.ts @@ -279,8 +279,8 @@ export interface DataService { options?: { nameOnly?: true; privileges?: - | ConnectionStatusWithPrivileges['authInfo']['authenticatedUserPrivileges'] - | null; + | ConnectionStatusWithPrivileges['authInfo']['authenticatedUserPrivileges'] + | null; } ): Promise[]>; @@ -290,11 +290,11 @@ export interface DataService { listDatabases(options?: { nameOnly?: true; privileges?: - | ConnectionStatusWithPrivileges['authInfo']['authenticatedUserPrivileges'] - | null; + | ConnectionStatusWithPrivileges['authInfo']['authenticatedUserPrivileges'] + | null; roles?: - | ConnectionStatusWithPrivileges['authInfo']['authenticatedUserRoles'] - | null; + | ConnectionStatusWithPrivileges['authInfo']['authenticatedUserRoles'] + | null; }): Promise<{ _id: string; name: string }[]>; connect(): Promise; @@ -1004,8 +1004,8 @@ export class DataServiceImpl extends EventEmitter implements DataService { }: { nameOnly?: true; privileges?: - | ConnectionStatusWithPrivileges['authInfo']['authenticatedUserPrivileges'] - | null; + | ConnectionStatusWithPrivileges['authInfo']['authenticatedUserPrivileges'] + | null; } = {} ): Promise[]> { const logop = this._startLogOp( @@ -1103,11 +1103,11 @@ export class DataServiceImpl extends EventEmitter implements DataService { }: { nameOnly?: true; privileges?: - | ConnectionStatusWithPrivileges['authInfo']['authenticatedUserPrivileges'] - | null; + | ConnectionStatusWithPrivileges['authInfo']['authenticatedUserPrivileges'] + | null; roles?: - | ConnectionStatusWithPrivileges['authInfo']['authenticatedUserRoles'] - | null; + | ConnectionStatusWithPrivileges['authInfo']['authenticatedUserRoles'] + | null; } = {}): Promise<{ _id: string; name: string }[]> { const logop = this._startLogOp( mongoLogId(1_001_000_033), @@ -1402,9 +1402,9 @@ export class DataServiceImpl extends EventEmitter implements DataService { ?.close(true) .catch((err) => debug('failed to close MongoClient', err)), this._crudClient !== this._metadataClient && - this._crudClient - ?.close(true) - .catch((err) => debug('failed to close MongoClient', err)), + this._crudClient + ?.close(true) + .catch((err) => debug('failed to close MongoClient', err)), this._tunnel ?.close() .catch((err) => debug('failed to close tunnel', err)), @@ -2582,11 +2582,11 @@ export class DataServiceImpl extends EventEmitter implements DataService { tlsOptions: autoEncryptionOptions.tlsOptions, proxyOptions: proxyHost ? { - proxyHost, - proxyPort, - proxyUsername, - proxyPassword, - } + proxyHost, + proxyPort, + proxyUsername, + proxyPassword, + } : undefined, }); } From 549d771632e3414b374a803245874c839f4e8a63 Mon Sep 17 00:00:00 2001 From: Basit <1305718+mabaasit@users.noreply.github.com> Date: Wed, 24 Aug 2022 14:21:00 +0200 Subject: [PATCH 10/12] Update packages/compass-indexes/src/components/indexes-table/indexes-table.tsx Co-authored-by: Anna Henningsen --- .../components/indexes-table/indexes-table.tsx | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/packages/compass-indexes/src/components/indexes-table/indexes-table.tsx b/packages/compass-indexes/src/components/indexes-table/indexes-table.tsx index 7f6ad7c409b..7a40f5292c4 100644 --- a/packages/compass-indexes/src/components/indexes-table/indexes-table.tsx +++ b/packages/compass-indexes/src/components/indexes-table/indexes-table.tsx @@ -73,15 +73,14 @@ export const IndexesTable: React.FunctionComponent = ({ onDeleteIndex, }) => { const columns = useMemo(() => { - const _columns = ( - [ - 'Name and Definition', - 'Type', - 'Size', - 'Usage', - 'Properties', - ] as SortColumn[] - ).map((name) => { + const sortColumns: SortColumn[] = [ + 'Name and Definition', + 'Type', + 'Size', + 'Usage', + 'Properties', + ]; + const _columns = sortColumns.map((name) => { return ( Date: Thu, 25 Aug 2022 14:44:37 +0200 Subject: [PATCH 11/12] fix tests --- .../src/modules/indexes.spec.ts | 27 +++++++++++++++++++ .../compass-indexes/src/modules/indexes.ts | 2 +- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/packages/compass-indexes/src/modules/indexes.spec.ts b/packages/compass-indexes/src/modules/indexes.spec.ts index 4a7ccef631f..874cafbc05f 100644 --- a/packages/compass-indexes/src/modules/indexes.spec.ts +++ b/packages/compass-indexes/src/modules/indexes.spec.ts @@ -110,6 +110,27 @@ describe('indexes module', function () { expect(typeof dispatchSpy.getCall(2).args[0] === 'function').to.true; }); + it('when dataService is not connected, sets refreshing to false', function () { + store.dispatch({ + type: IndexesActionTypes.LoadIndexes, + indexes: defaultSort, + }); + // Mock dataService.indexes + store.dispatch( + dataServiceConnected({ + isConnected() { + return false; + }, + } as DataService) + ); + + store.dispatch(fetchIndexes() as any); + + const state = store.getState(); + expect(state.indexes).to.deep.equal(defaultSort); + expect(state.isRefreshing).to.equal(false); + }); + it('sets indexes to empty array when there is an error', function () { const error = new Error('failed to connect to server'); // Set some data to validate the empty array condition @@ -120,6 +141,9 @@ describe('indexes module', function () { // Mock dataService.indexes store.dispatch( dataServiceConnected({ + isConnected() { + return true; + }, indexes: (ns: any, opts: any, cb: any) => { cb(error); }, @@ -140,6 +164,9 @@ describe('indexes module', function () { store.dispatch(sortIndexes('Name and Definition', 'asc') as any); store.dispatch( dataServiceConnected({ + isConnected() { + return true; + }, indexes: (_ns: any, _opts: any, cb: any) => { cb(null, fromDB); }, diff --git a/packages/compass-indexes/src/modules/indexes.ts b/packages/compass-indexes/src/modules/indexes.ts index 5ade73af31f..226fdbb6b77 100644 --- a/packages/compass-indexes/src/modules/indexes.ts +++ b/packages/compass-indexes/src/modules/indexes.ts @@ -122,7 +122,7 @@ export const fetchIndexes = (): ThunkAction< return _handleIndexesChanged(dispatch, []); } - if (!dataService) { + if (!dataService || !dataService.isConnected()) { dispatch({ type: RefreshActionTypes.RefreshFinished }); debug( 'warning: trying to load indexes but dataService is disconnected', From 93743c8cee5fc723e831a63250b18cb49d0d96cf Mon Sep 17 00:00:00 2001 From: Basit Date: Thu, 25 Aug 2022 15:57:03 +0200 Subject: [PATCH 12/12] fix tests --- packages/compass-indexes/src/modules/indexes.spec.ts | 12 +++++++----- packages/compass-indexes/src/modules/indexes.ts | 4 ++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/packages/compass-indexes/src/modules/indexes.spec.ts b/packages/compass-indexes/src/modules/indexes.spec.ts index 874cafbc05f..eebfac3c0dd 100644 --- a/packages/compass-indexes/src/modules/indexes.spec.ts +++ b/packages/compass-indexes/src/modules/indexes.spec.ts @@ -117,11 +117,13 @@ describe('indexes module', function () { }); // Mock dataService.indexes store.dispatch( - dataServiceConnected({ - isConnected() { - return false; - }, - } as DataService) + dataServiceConnected( + new (class { + isConnected() { + return false; + } + })() as DataService + ) ); store.dispatch(fetchIndexes() as any); diff --git a/packages/compass-indexes/src/modules/indexes.ts b/packages/compass-indexes/src/modules/indexes.ts index 226fdbb6b77..7c3700f4920 100644 --- a/packages/compass-indexes/src/modules/indexes.ts +++ b/packages/compass-indexes/src/modules/indexes.ts @@ -18,13 +18,13 @@ type SortField = keyof Pick< >; export type SortColumn = keyof typeof sortColumnToProps; export type SortDirection = 'asc' | 'desc'; -const sortColumnToProps: Record = { +const sortColumnToProps = { 'Name and Definition': 'name', Type: 'type', Size: 'size', Usage: 'usageCount', Properties: 'properties', -}; +} as const; export type IndexDefinition = { name: string;