-
Notifications
You must be signed in to change notification settings - Fork 48
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
* create index pattern modal Signed-off-by: Aleksandar Djindjic <djindjic@gmail.com> * validate index pattern name and fill timeFields Signed-off-by: Aleksandar Djindjic <djindjic@gmail.com> * reuse SavedObjectsService from dashboards core Signed-off-by: Aleksandar Djindjic <djindjic@gmail.com> * take title and time field from the form Signed-off-by: Aleksandar Djindjic <djindjic@gmail.com> * reuse index patterns service from core dashboards Signed-off-by: Aleksandar Djindjic <djindjic@gmail.com> * improve ux for create index pattern Signed-off-by: Aleksandar Djindjic <djindjic@gmail.com> * rename props Signed-off-by: Aleksandar Djindjic <djindjic@gmail.com> * submit button loading state from formik submiting Signed-off-by: Aleksandar Djindjic <djindjic@gmail.com> * typos and minor changes Signed-off-by: Aleksandar Djindjic <djindjic@gmail.com> * indexPatternsService marked as mandatory dep Signed-off-by: Aleksandar Djindjic <djindjic@gmail.com> * better typing on injected dependencies Signed-off-by: Aleksandar Djindjic <djindjic@gmail.com> * cypress test for create index pattern Signed-off-by: Aleksandar Djindjic <djindjic@gmail.com> * align cypress test with new convention Signed-off-by: Aleksandar Djindjic <djindjic@gmail.com> * useCallback event handler wrapper Signed-off-by: Aleksandar Djindjic <djindjic@gmail.com> --------- Signed-off-by: Aleksandar Djindjic <djindjic@gmail.com> (cherry picked from commit 2f08f22) Co-authored-by: Aleksandar Djindjic <djindjic@gmail.com>
- Loading branch information
1 parent
f9627d5
commit 0306d92
Showing
12 changed files
with
370 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
210 changes: 210 additions & 0 deletions
210
public/pages/Findings/components/CreateIndexPatternForm.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,210 @@ | ||
/* | ||
* Copyright OpenSearch Contributors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
import React, { useEffect, useState, useCallback } from 'react'; | ||
import { Formik, Form, FormikErrors } from 'formik'; | ||
import { | ||
EuiFlexGroup, | ||
EuiFlexItem, | ||
EuiFormRow, | ||
EuiFieldText, | ||
EuiButton, | ||
EuiSpacer, | ||
EuiComboBox, | ||
EuiText, | ||
EuiCallOut, | ||
} from '@elastic/eui'; | ||
import { IndexPatternsService } from '../../../services'; | ||
|
||
const ILLEGAL_CHARACTERS = [' ', '\\', '/', '?', '"', '<', '>', '|']; | ||
|
||
const containsIllegalCharacters = (pattern: string) => { | ||
return ILLEGAL_CHARACTERS.some((char) => pattern.includes(char)); | ||
}; | ||
|
||
export interface CreateIndexPatternFormModel { | ||
name: string; | ||
timeField: string; | ||
} | ||
|
||
export interface CreateIndexPatternFormProps { | ||
initialValue: { | ||
name: string; | ||
}; | ||
created: (values: string) => void; | ||
close: () => void; | ||
indexPatternsService: IndexPatternsService; | ||
} | ||
|
||
export const CreateIndexPatternForm: React.FC<CreateIndexPatternFormProps> = ({ | ||
initialValue, | ||
created, | ||
close, | ||
indexPatternsService, | ||
}) => { | ||
const [timeFields, setTimeFields] = useState<string[]>([]); | ||
const [createdIndex, setCreatedIndex] = useState<{ id?: string; title: string }>(); | ||
|
||
const getTimeFields = useCallback( | ||
async (name: string): Promise<string[]> => { | ||
if (!indexPatternsService) { | ||
return []; | ||
} | ||
|
||
return indexPatternsService | ||
.getFieldsForWildcard({ | ||
pattern: `${name}`, | ||
metaFields: ['_source', '_id', '_type', '_index', '_score'], | ||
params: {}, | ||
}) | ||
.then((res) => { | ||
return res.filter((f) => f.type === 'date').map((f) => f.name); | ||
}) | ||
.catch(() => { | ||
return []; | ||
}); | ||
}, | ||
[initialValue] | ||
); | ||
|
||
useEffect(() => { | ||
getTimeFields(initialValue.name).then((fields) => { | ||
setTimeFields(fields); | ||
}); | ||
}, [initialValue.name]); | ||
|
||
return createdIndex ? ( | ||
<> | ||
<EuiCallOut title={`${createdIndex?.title} has been successfully created`} color="success"> | ||
<p>You may now view surrounding documents within the index</p> | ||
</EuiCallOut> | ||
<EuiSpacer /> | ||
<EuiFlexGroup justifyContent="flexEnd"> | ||
<EuiFlexItem grow={false}> | ||
<EuiButton | ||
fill | ||
onClick={() => { | ||
created(createdIndex?.id || ''); | ||
}} | ||
> | ||
View surrounding documents | ||
</EuiButton> | ||
</EuiFlexItem> | ||
</EuiFlexGroup> | ||
</> | ||
) : ( | ||
<Formik | ||
initialValues={{ ...initialValue, timeField: '' }} | ||
validate={(values) => { | ||
const errors: FormikErrors<CreateIndexPatternFormModel> = {}; | ||
|
||
if (!values.name) { | ||
errors.name = 'Index patter name is required'; | ||
} | ||
|
||
if (!values.timeField) { | ||
errors.timeField = 'Time field is required'; | ||
} | ||
|
||
if (containsIllegalCharacters(values.name)) { | ||
errors.name = | ||
'A index pattern cannot contain spaces or the characters: , /, ?, ", <, >, |'; | ||
} | ||
|
||
return errors; | ||
}} | ||
onSubmit={async (values, { setSubmitting }) => { | ||
try { | ||
const newIndex = await indexPatternsService.createAndSave({ | ||
title: values.name, | ||
timeFieldName: values.timeField, | ||
}); | ||
setCreatedIndex({ id: newIndex.id, title: newIndex.title }); | ||
} catch (e) { | ||
console.warn(e); | ||
} | ||
setSubmitting(false); | ||
}} | ||
> | ||
{(props) => ( | ||
<Form> | ||
<EuiText> | ||
An index pattern is required to view all surrounding documents within the index. Create | ||
an index pattern to continue. | ||
</EuiText> | ||
<EuiSpacer /> | ||
<EuiFormRow | ||
label={ | ||
<EuiText size={'s'}> | ||
<strong>Specify index pattern name</strong> | ||
</EuiText> | ||
} | ||
isInvalid={props.touched.name && !!props.errors?.name} | ||
error={props.errors.name} | ||
> | ||
<EuiFieldText | ||
isInvalid={props.touched.name && !!props.errors.name} | ||
placeholder="Enter index pattern name" | ||
data-test-subj={'index_pattern_name_field'} | ||
onChange={async (e) => { | ||
props.handleChange('name')(e); | ||
const fields = await getTimeFields(e.target.value); | ||
setTimeFields(fields); | ||
props.setFieldValue('timeField', ''); | ||
}} | ||
onBlur={props.handleBlur('name')} | ||
value={props.values.name} | ||
/> | ||
</EuiFormRow> | ||
|
||
<EuiFormRow | ||
label={ | ||
<EuiText size={'s'}> | ||
<strong>Time filed</strong> | ||
</EuiText> | ||
} | ||
isInvalid={props.touched.timeField && !!props.errors?.timeField} | ||
error={props.errors.timeField} | ||
> | ||
<EuiComboBox | ||
isInvalid={props.touched.timeField && !!props.errors.timeField} | ||
placeholder="Select a time field" | ||
data-test-subj={'index_pattern_time_field_dropdown'} | ||
options={timeFields.map((field: string) => ({ value: field, label: field }))} | ||
singleSelection={{ asPlainText: true }} | ||
onChange={(e) => { | ||
props.handleChange('timeField')(e[0]?.value ? e[0].value : ''); | ||
}} | ||
onBlur={props.handleBlur('timeField')} | ||
selectedOptions={ | ||
props.values.timeField | ||
? [{ value: props.values.timeField, label: props.values.timeField }] | ||
: [] | ||
} | ||
/> | ||
</EuiFormRow> | ||
|
||
<EuiSpacer /> | ||
|
||
<EuiFlexGroup justifyContent="flexEnd"> | ||
<EuiFlexItem grow={false}> | ||
<EuiButton onClick={() => close()}>Cancel</EuiButton> | ||
</EuiFlexItem> | ||
<EuiFlexItem grow={false}> | ||
<EuiButton | ||
data-test-subj={'index_pattern_form_submit_button'} | ||
isLoading={props.isSubmitting} | ||
fill | ||
onClick={() => props.handleSubmit()} | ||
> | ||
Create index pattern | ||
</EuiButton> | ||
</EuiFlexItem> | ||
</EuiFlexGroup> | ||
</Form> | ||
)} | ||
</Formik> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.