Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented UX support for configuring doc level monitors. #218

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React, { Component } from 'react';
import _ from 'lodash';
import { connect, FieldArray } from 'formik';
import { EuiButton, EuiSpacer } from '@elastic/eui';
import { inputLimitText } from '../../../../utils/helpers';
import DocumentLevelQuery, { getInitialQueryValues } from './DocumentLevelQuery';

export const MAX_QUERIES = 10; // TODO DRAFT: Placeholder limit

class ConfigureDocumentLevelQueries extends Component {
constructor(props) {
super(props);
this.state = {};
}

renderQueries = (arrayHelpers) => {
const {
dataTypes,
formik: { values },
} = this.props;
if (_.isEmpty(values.queries)) arrayHelpers.push(_.cloneDeep(getInitialQueryValues()));
const numOfQueries = values.queries.length;
return (
<div>
{values.queries.map((query, index) => {
return (
<DocumentLevelQuery
key={`query${index}`}
dataTypes={dataTypes}
formFieldName={`queries.${index}`}
queryIndex={index}
queriesArrayHelpers={arrayHelpers}
query={query}
/>
);
})}

<div style={{ paddingLeft: '10px' }}>
<EuiButton
fill={false}
size={'s'}
onClick={() => arrayHelpers.push(_.cloneDeep(getInitialQueryValues(numOfQueries)))}
disabled={numOfQueries >= MAX_QUERIES}
>
{numOfQueries === 0 ? 'Add query' : 'Add another query'}
</EuiButton>
<EuiSpacer size={'s'} />
{inputLimitText(numOfQueries, MAX_QUERIES, 'query', 'queries')}
</div>
</div>
);
};

render() {
return (
<FieldArray name={'queries'} validateOnChange={false}>
{(arrayHelpers) => this.renderQueries(arrayHelpers)}
</FieldArray>
);
}
}

export default connect(ConfigureDocumentLevelQueries);
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React, { Component } from 'react';
import { connect, FieldArray } from 'formik';
import { EuiButtonEmpty } from '@elastic/eui';
import { inputLimitText } from '../../../../utils/helpers';
import DocumentLevelQueryTag from './DocumentLevelQueryTag';

export const MAX_TAGS = 10; // TODO DRAFT: Placeholder limit

class ConfigureDocumentLevelQueryTags extends Component {
constructor(props) {
super(props);
this.state = {};
}

renderTags(arrayHelpers) {
const {
formik: { values },
formFieldName = '',
query,
queryIndex,
} = this.props;
const numOfTags = query.tags.length;
return (
<div>
{values.queries[queryIndex].tags.map((tag, index) => {
return (
<span style={{ paddingRight: '5px' }} key={`${formFieldName}.tags.${index}`}>
<DocumentLevelQueryTag
tag={tag}
tagIndex={index}
arrayHelpers={arrayHelpers}
formFieldName={`${formFieldName}.tags.${index}`}
/>
</span>
);
})}
<div>
<EuiButtonEmpty
size={'xs'}
onClick={() => arrayHelpers.push('')}
disabled={numOfTags >= MAX_TAGS}
style={{ paddingTop: '5px' }}
>
+ Add tag
</EuiButtonEmpty>
{inputLimitText(numOfTags, MAX_TAGS, 'tag', 'tags')}
</div>
</div>
);
}

render() {
const { formFieldName } = this.props;
return (
<FieldArray name={`${formFieldName}.tags`} validateOnChange={false}>
{(arrayHelpers) => this.renderTags(arrayHelpers)}
</FieldArray>
);
}
}

export default connect(ConfigureDocumentLevelQueryTags);
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React, { Component } from 'react';
import _ from 'lodash';
import { connect } from 'formik';
import {
EuiButton,
EuiFlexGroup,
EuiFlexItem,
EuiHorizontalRule,
EuiSpacer,
EuiText,
} from '@elastic/eui';
import { FormikFieldText, FormikComboBox, FormikSelect } from '../../../../components/FormControls';
import { hasError, isInvalid, required } from '../../../../utils/validate';
import { FORMIK_INITIAL_DOCUMENT_LEVEL_QUERY_VALUES } from '../../containers/CreateMonitor/utils/constants';
import { DOC_LEVEL_TAG_TOOLTIP } from './DocumentLevelQueryTag';
import IconToolTip from '../../../../components/IconToolTip';
import ConfigureDocumentLevelQueryTags from './ConfigureDocumentLevelQueryTags';
import { getIndexFields } from '../MonitorExpressions/expressions/utils/dataTypes';

const ALLOWED_DATA_TYPES = ['number', 'text', 'keyword', 'boolean'];

export const QUERY_OPERATORS = [
{ text: 'is', value: '==' },
{ text: 'is not', value: '!=' },
];

export const getInitialQueryValues = (queryIndexNum = 0) =>
_.cloneDeep({
...FORMIK_INITIAL_DOCUMENT_LEVEL_QUERY_VALUES,
queryName: `Query ${queryIndexNum + 1}`,
});

class DocumentLevelQuery extends Component {
constructor(props) {
super(props);
this.state = {};
}

render() {
const { dataTypes, formFieldName = '', query, queryIndex, queriesArrayHelpers } = this.props;
return (
<div style={{ padding: '0px 10px' }}>
<EuiFlexGroup>
<EuiFlexItem>
<FormikFieldText
name={`${formFieldName}.queryName`}
formRow
fieldProps={{ validate: required }} // TODO DRAFT: implement validation
rowProps={{
label: 'Query name',
style: { width: '300px' },
isInvalid,
error: hasError,
}}
inputProps={{ isInvalid }}
/>
</EuiFlexItem>

{queryIndex > 0 && (
<EuiFlexItem grow={false}>
<EuiButton color={'danger'} onClick={() => queriesArrayHelpers.remove(queryIndex)}>
Remove query
</EuiButton>
</EuiFlexItem>
)}
</EuiFlexGroup>

<EuiSpacer size={'m'} />

<EuiFlexGroup alignItems={'flexStart'} direction={'row'} gutterSize={'m'}>
<EuiFlexItem grow={false}>
<FormikComboBox
name={`${formFieldName}.field`}
formRow
fieldProps={{ validate: required }} // TODO DRAFT: implement validation
rowProps={{
label: 'Field',
style: { width: '300px' },
isInvalid,
error: hasError,
}}
inputProps={{
placeholder: 'Enter the field to query',
options: getIndexFields(dataTypes, ALLOWED_DATA_TYPES),
onChange: (e, field, form) => form.setFieldValue(field.name, e[0].label),
onBlur: (e, field, form) => form.setFieldTouched(field.name, true),
singleSelection: { asPlainText: true },
}}
/>
</EuiFlexItem>

<EuiFlexItem grow={false}>
<FormikSelect
name={`${formFieldName}.operator`}
formRow={true}
rowProps={{ hasEmptyLabelSpace: true }}
inputProps={{
onChange: (e, field) => field.onChange(e),
options: QUERY_OPERATORS,
}}
/>
</EuiFlexItem>

<EuiFlexItem grow={false}>
<FormikFieldText
name={`${formFieldName}.query`}
formRow
fieldProps={{ validate: required }} // TODO DRAFT: What constraints should we implement?
rowProps={{
hasEmptyLabelSpace: true,
style: { width: '300px' },
isInvalid,
error: hasError,
}}
inputProps={{
placeholder: 'Enter the search term',
fullWidth: true,
isInvalid,
}}
/>
</EuiFlexItem>
</EuiFlexGroup>

<EuiSpacer size={'m'} />

<EuiText size={'xs'}>
<strong>Tags</strong>
<i> - optional </i>
<IconToolTip content={DOC_LEVEL_TAG_TOOLTIP} iconType={'questionInCircle'} />
</EuiText>
<EuiSpacer size={'s'} />

{_.isEmpty(query.tags) && (
<div>
<EuiText size={'xs'}>No tags defined.</EuiText>
</div>
)}

<ConfigureDocumentLevelQueryTags
formFieldName={formFieldName}
query={query}
queryIndex={queryIndex}
/>

<EuiHorizontalRule margin={'l'} />
</div>
);
}
}

export default connect(DocumentLevelQuery);
Loading