/* * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 */ import React, { Component } from "react"; import { EuiSpacer, EuiLink, EuiFlexItem, EuiFlexGroup, EuiButton, EuiButtonEmpty } from "@elastic/eui"; import FormGenerator, { IField, IFormGeneratorRef, IFieldComponentProps } from "../../../../components/FormGenerator"; import { IndexItem } from "../../../../../models/interfaces"; import IndexDetail from "../../../../containers/IndexDetail"; import ContentPanel from "../../../../components/ContentPanel/ContentPanel"; import AliasSelect, { AliasSelectProps } from "../../../../components/AliasSelect"; import EuiToolTipWrapper from "../../../../components/EuiToolTipWrapper"; import { INDEX_NAMING_PATTERN, INDEX_NAMING_MESSAGE, INDEX_SETTINGS_URL, REPLICA_NUMBER_MESSAGE, ALIAS_SELECT_RULE, } from "../../../../utils/constants"; import NotificationConfig, { NotificationConfigRef } from "../../../../containers/NotificationConfig"; import { ServerResponse } from "../../../../../server/models/types"; import { ActionType, OperationType } from "../../../Notifications/constant"; const WrappedAliasSelect = EuiToolTipWrapper(AliasSelect, { disabledKey: "isDisabled", }); interface SplitIndexComponentProps { sourceIndex: string; reasons: React.ReactChild[]; sourceShards: string; shardsSelectOptions: { label: string }[]; onSplitIndex: (targetIndex: string, settingsPayload: Required["settings"]) => Promise>; onCancel: () => void; getAlias: AliasSelectProps["refreshOptions"]; loading: boolean; } export default class SplitIndexForm extends Component { state = { settings: {} as Required["settings"], sourceIndexSettings: {} as IndexItem, }; formRef: IFormGeneratorRef | null = null; notificationRef: NotificationConfigRef | null = null; onSubmit = async () => { // trigger the validation manually here const validateResult = await this.formRef?.validatePromise(); const notificationResult = await this.notificationRef?.validatePromise(); const { targetIndex, ...others } = this.state.settings; const { errors } = validateResult || {}; if (errors) { return; } if (notificationResult?.errors) { return; } try { const result = await this.props.onSplitIndex(targetIndex, others); if (result && result.ok) { this.notificationRef?.associateWithTask({ taskId: result.response.task, }); } this.props.onCancel(); } catch (err) { // no need to log anything since getIndexSettings will log the error } }; render() { const { sourceIndex, sourceShards, reasons, getAlias } = this.props; const blockNameList = ["targetIndex"]; let shardMessage = "The number must be 2x times of the primary shard count of the source index."; if (sourceShards === "1") { shardMessage = "The number must be an integer greater than 1 but fewer or equal to 1024."; } const formFields: IField[] = [ { rowProps: { label: "Target Index Name", helpText: INDEX_NAMING_MESSAGE, position: "bottom", }, name: "targetIndex", type: "Input", options: { rules: [ { trigger: "onBlur", validator: (rule, value) => { if (!value || value === "") { // do not pass the validation // return a rejected promise with error message return Promise.reject("Target index name is required"); } else if (!INDEX_NAMING_PATTERN.test(value)) { return Promise.reject(`Target index name ${value} is invalid`); } // pass the validation, return a resolved promise return Promise.resolve(); }, }, ], props: { "data-test-subj": "targetIndexNameInput", placeholder: "Specify a name for the new split index", }, }, }, { rowProps: { label: "Number of primary shards", helpText: ( <>

Specify the number of primary shards for the new split index.

{shardMessage}

), }, name: "index.number_of_shards", type: "ComboBoxSingle", options: { rules: [ { trigger: "onBlur", validator: (rule, value) => { if (!value || value === "") { // do not pass the validation // return a rejected promise with error message return Promise.reject("Number of shards is required"); } else if (!this.props.shardsSelectOptions.find((item) => "" + item.label === "" + value)) { return Promise.reject(`Number of shards ${value} is invalid`); } // pass the validation, return a resolved promise return Promise.resolve(); }, }, ], props: { "data-test-subj": "numberOfShardsInput", options: this.props.shardsSelectOptions, placeholder: "Specify primary shard count", onCreateOption: undefined, }, }, }, { rowProps: { label: "Number of replicas", helpText: REPLICA_NUMBER_MESSAGE, }, name: "index.number_of_replicas", type: "Number", options: { props: { "data-test-subj": "numberOfReplicasInput", placeholder: "Specify number of replica", min: 0, }, }, }, { name: "aliases", rowProps: { label: "Index alias", isOptional: true, helpText: "Allow this index to be referenced by existing aliases or specify a new alias.", }, options: { props: { refreshOptions: getAlias, }, rules: ALIAS_SELECT_RULE, }, component: WrappedAliasSelect as React.ComponentType, }, ]; const readyForSplit = reasons.length === 0; return (
    {reasons.map((reason, reasonIndex) => (
  • {reason}
  • ))}
{readyForSplit && ( <> this.setState({ settings: totalValue, }) } formFields={formFields} ref={(ref) => (this.formRef = ref)} hasAdvancedSettings={true} advancedSettingsProps={{ accordionProps: { initialIsOpen: false, id: "accordionForCreateIndexSettings", buttonContent:

Advanced settings

, }, blockedNameList: blockNameList, rowProps: { label: "Specify advanced index settings", helpText: ( <> Specify a comma-delimited list of settings.  View index settings ), }, }} />
)} (this.notificationRef = ref)} actionType={ActionType.RESIZE} operationType={OperationType.SHRINK} /> Cancel Split
); } }