import { Badge } from '@mongodb-js/compass-components'; import React from 'react'; import type { Reducer } from 'redux'; import { RESET_FORM } from '../reset-form'; import { isAction } from '../../utils/is-action'; export const UNSUPPORTED_COLUMNSTORE_INDEX_OPTIONS: OptionNames[] = [ 'sparse', 'unique', ]; export const OPTIONS = { unique: { type: 'checkbox', label: 'Create unique index', description: 'A unique index ensures that the indexed fields do not store duplicate values; i.e. enforces uniqueness for the indexed fields.', }, name: { type: 'text', label: 'Index name', description: 'Enter the name of the index to create, or leave blank to have MongoDB create a default name for the index.', units: undefined, optional: true, }, expireAfterSeconds: { type: 'number', label: 'Create TTL', description: 'TTL indexes are special single-field indexes that MongoDB can use to automatically remove documents from a collection after a certain amount of time or at a specific clock time.', units: 'seconds', optional: false, }, partialFilterExpression: { type: 'code', label: 'Partial Filter Expression', description: 'Partial indexes only index the documents in a collection that meet a specified filter expression.', units: undefined, optional: false, }, wildcardProjection: { type: 'code', label: 'Wildcard Projection', description: 'Wildcard indexes support queries against unknown or arbitrary fields.', units: undefined, optional: false, }, collation: { type: 'code', label: 'Use Custom Collation', description: 'Collation allows users to specify language-specific rules for string comparison, such as rules for lettercase and accent marks.', units: undefined, optional: false, }, columnstoreProjection: { type: 'code', label: ( <> Columnstore Projection Preview ), description: 'Columnstore indexes support queries against unknown or arbitrary fields.', units: undefined, optional: false, }, sparse: { type: 'checkbox', label: 'Create sparse index', description: 'Sparse indexes only contain entries for documents that have the indexed field, even if the index field contains a null value. The index skips over any document that is missing the indexed field.', }, } as const; export type OptionNames = keyof typeof OPTIONS; export type CheckboxOptions = { [k in OptionNames]: typeof OPTIONS[k]['type'] extends 'checkbox' ? k : never; }[OptionNames]; export type InputOptions = Exclude; type State = { [k in OptionNames]: { enabled: boolean; } & (typeof OPTIONS[k]['type'] extends 'checkbox' ? { value: boolean } : { value: string }); }; type ValueForOption = typeof OPTIONS[O]['type'] extends 'checkbox' ? boolean : string; enum Actions { ChangeOption = 'compass-indexes/create-index/change-option', ChangeOptionEnabled = 'compass-indexes/create-index/change-option-enabled', } type ChangeOptionAction = { type: Actions.ChangeOption; name: O; value: ValueForOption; }; export function changeOption( optionName: O, newValue: ValueForOption ): ChangeOptionAction { return { type: Actions.ChangeOption, name: optionName, value: newValue }; } type ChangeOptionEnabledAction = { type: Actions.ChangeOptionEnabled; name: O; enabled: boolean; }; export function changeOptionEnabled( optionName: O, enabled: boolean ): ChangeOptionEnabledAction { return { type: Actions.ChangeOptionEnabled, name: optionName, enabled }; } export const INITIAL_STATE = Object.fromEntries( (Object.keys(OPTIONS) as OptionNames[]).map((name) => { return [ name, { value: OPTIONS[name].type === 'checkbox' ? false : '', enabled: false }, ]; }) ) as State; const reducer: Reducer = (state = INITIAL_STATE, action) => { if (isAction(action, Actions.ChangeOption)) { return { ...state, [action.name]: { value: action.value, // "enable" checkbox-type inputs on first change (they are not hidden // behind a checkbox), otherwise keep the current value enabled: OPTIONS[action.name].type === 'checkbox' ? true : state[action.name].enabled, }, }; } if ( isAction(action, Actions.ChangeOptionEnabled) ) { return { ...state, [action.name]: { ...state[action.name], enabled: action.enabled, }, }; } if (action.type === RESET_FORM) { // Deep clone on reset return JSON.parse(JSON.stringify(INITIAL_STATE)); } return state; }; export default reducer;