Skip to content

Commit 3ecc632

Browse files
committed
[Ingest] Data source configuration validation UI (#61180)
* Initial pass at datasource configuration validation * Show error icon and red text at input and stream levels * Add tests, fix bugs in validation method * Fix typings
1 parent 2cadd34 commit 3ecc632

12 files changed

Lines changed: 1038 additions & 92 deletions

File tree

x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/datasource_input_config.tsx

Lines changed: 50 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,38 @@
66
import React, { useState, Fragment } from 'react';
77
import { FormattedMessage } from '@kbn/i18n/react';
88
import {
9-
EuiFlexGrid,
109
EuiFlexGroup,
1110
EuiFlexItem,
1211
EuiText,
12+
EuiTextColor,
1313
EuiSpacer,
1414
EuiButtonEmpty,
1515
EuiTitle,
16+
EuiIconTip,
1617
} from '@elastic/eui';
1718
import { DatasourceInput, RegistryVarsEntry } from '../../../../types';
18-
import { isAdvancedVar } from '../services';
19+
import { isAdvancedVar, DatasourceConfigValidationResults, validationHasErrors } from '../services';
1920
import { DatasourceInputVarField } from './datasource_input_var_field';
2021

2122
export const DatasourceInputConfig: React.FunctionComponent<{
2223
packageInputVars?: RegistryVarsEntry[];
2324
datasourceInput: DatasourceInput;
2425
updateDatasourceInput: (updatedInput: Partial<DatasourceInput>) => void;
25-
}> = ({ packageInputVars, datasourceInput, updateDatasourceInput }) => {
26+
inputVarsValidationResults: DatasourceConfigValidationResults;
27+
forceShowErrors?: boolean;
28+
}> = ({
29+
packageInputVars,
30+
datasourceInput,
31+
updateDatasourceInput,
32+
inputVarsValidationResults,
33+
forceShowErrors,
34+
}) => {
2635
// Showing advanced options toggle state
2736
const [isShowingAdvanced, setIsShowingAdvanced] = useState<boolean>(false);
2837

38+
// Errors state
39+
const hasErrors = forceShowErrors && validationHasErrors(inputVarsValidationResults);
40+
2941
const requiredVars: RegistryVarsEntry[] = [];
3042
const advancedVars: RegistryVarsEntry[] = [];
3143

@@ -40,15 +52,36 @@ export const DatasourceInputConfig: React.FunctionComponent<{
4052
}
4153

4254
return (
43-
<EuiFlexGrid columns={2}>
44-
<EuiFlexItem>
55+
<EuiFlexGroup alignItems="flexStart">
56+
<EuiFlexItem grow={1}>
4557
<EuiTitle size="s">
46-
<h4>
47-
<FormattedMessage
48-
id="xpack.ingestManager.createDatasource.stepConfigure.inputSettingsTitle"
49-
defaultMessage="Settings"
50-
/>
51-
</h4>
58+
<EuiFlexGroup alignItems="center" gutterSize="s">
59+
<EuiFlexItem grow={false}>
60+
<h4>
61+
<EuiTextColor color={hasErrors ? 'danger' : 'default'}>
62+
<FormattedMessage
63+
id="xpack.ingestManager.createDatasource.stepConfigure.inputSettingsTitle"
64+
defaultMessage="Settings"
65+
/>
66+
</EuiTextColor>
67+
</h4>
68+
</EuiFlexItem>
69+
{hasErrors ? (
70+
<EuiFlexItem grow={false}>
71+
<EuiIconTip
72+
content={
73+
<FormattedMessage
74+
id="xpack.ingestManager.createDatasource.stepConfigure.inputConfigErrorsTooltip"
75+
defaultMessage="Fix configuration errors"
76+
/>
77+
}
78+
position="right"
79+
type="alert"
80+
iconProps={{ color: 'danger' }}
81+
/>
82+
</EuiFlexItem>
83+
) : null}
84+
</EuiFlexGroup>
5285
</EuiTitle>
5386
<EuiSpacer size="m" />
5487
<EuiText color="subdued" size="s">
@@ -60,7 +93,7 @@ export const DatasourceInputConfig: React.FunctionComponent<{
6093
</p>
6194
</EuiText>
6295
</EuiFlexItem>
63-
<EuiFlexItem>
96+
<EuiFlexItem grow={1}>
6497
<EuiFlexGroup direction="column" gutterSize="m">
6598
{requiredVars.map(varDef => {
6699
const { name: varName, type: varType } = varDef;
@@ -81,6 +114,8 @@ export const DatasourceInputConfig: React.FunctionComponent<{
81114
},
82115
});
83116
}}
117+
errors={inputVarsValidationResults.config![varName]}
118+
forceShowErrors={forceShowErrors}
84119
/>
85120
</EuiFlexItem>
86121
);
@@ -123,6 +158,8 @@ export const DatasourceInputConfig: React.FunctionComponent<{
123158
},
124159
});
125160
}}
161+
errors={inputVarsValidationResults.config![varName]}
162+
forceShowErrors={forceShowErrors}
126163
/>
127164
</EuiFlexItem>
128165
);
@@ -132,6 +169,6 @@ export const DatasourceInputConfig: React.FunctionComponent<{
132169
) : null}
133170
</EuiFlexGroup>
134171
</EuiFlexItem>
135-
</EuiFlexGrid>
172+
</EuiFlexGroup>
136173
);
137174
};

x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/datasource_input_panel.tsx

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@ import {
1717
EuiButtonIcon,
1818
EuiHorizontalRule,
1919
EuiSpacer,
20+
EuiIconTip,
2021
} from '@elastic/eui';
2122
import { DatasourceInput, DatasourceInputStream, RegistryInput } from '../../../../types';
23+
import { DatasourceInputValidationResults, validationHasErrors } from '../services';
2224
import { DatasourceInputConfig } from './datasource_input_config';
2325
import { DatasourceInputStreamConfig } from './datasource_input_stream_config';
2426

@@ -32,20 +34,54 @@ export const DatasourceInputPanel: React.FunctionComponent<{
3234
packageInput: RegistryInput;
3335
datasourceInput: DatasourceInput;
3436
updateDatasourceInput: (updatedInput: Partial<DatasourceInput>) => void;
35-
}> = ({ packageInput, datasourceInput, updateDatasourceInput }) => {
37+
inputValidationResults: DatasourceInputValidationResults;
38+
forceShowErrors?: boolean;
39+
}> = ({
40+
packageInput,
41+
datasourceInput,
42+
updateDatasourceInput,
43+
inputValidationResults,
44+
forceShowErrors,
45+
}) => {
3646
// Showing streams toggle state
3747
const [isShowingStreams, setIsShowingStreams] = useState<boolean>(false);
3848

49+
// Errors state
50+
const hasErrors = forceShowErrors && validationHasErrors(inputValidationResults);
51+
3952
return (
4053
<EuiPanel>
4154
{/* Header / input-level toggle */}
4255
<EuiFlexGroup justifyContent="spaceBetween" alignItems="center">
4356
<EuiFlexItem grow={false}>
4457
<EuiSwitch
4558
label={
46-
<EuiText>
47-
<h4>{packageInput.title || packageInput.type}</h4>
48-
</EuiText>
59+
<EuiFlexGroup alignItems="center" gutterSize="s">
60+
<EuiFlexItem grow={false}>
61+
<EuiText>
62+
<h4>
63+
<EuiTextColor color={hasErrors ? 'danger' : 'default'}>
64+
{packageInput.title || packageInput.type}
65+
</EuiTextColor>
66+
</h4>
67+
</EuiText>
68+
</EuiFlexItem>
69+
{hasErrors ? (
70+
<EuiFlexItem grow={false}>
71+
<EuiIconTip
72+
content={
73+
<FormattedMessage
74+
id="xpack.ingestManager.createDatasource.stepConfigure.inputLevelErrorsTooltip"
75+
defaultMessage="Fix configuration errors"
76+
/>
77+
}
78+
position="right"
79+
type="alert"
80+
iconProps={{ color: 'danger' }}
81+
/>
82+
</EuiFlexItem>
83+
) : null}
84+
</EuiFlexGroup>
4985
}
5086
checked={datasourceInput.enabled}
5187
onChange={e => {
@@ -122,6 +158,8 @@ export const DatasourceInputPanel: React.FunctionComponent<{
122158
packageInputVars={packageInput.vars}
123159
datasourceInput={datasourceInput}
124160
updateDatasourceInput={updateDatasourceInput}
161+
inputVarsValidationResults={{ config: inputValidationResults.config }}
162+
forceShowErrors={forceShowErrors}
125163
/>
126164
<EuiHorizontalRule margin="m" />
127165
</Fragment>
@@ -165,6 +203,10 @@ export const DatasourceInputPanel: React.FunctionComponent<{
165203

166204
updateDatasourceInput(updatedInput);
167205
}}
206+
inputStreamValidationResults={
207+
inputValidationResults.streams![datasourceInputStream.id]
208+
}
209+
forceShowErrors={forceShowErrors}
168210
/>
169211
<EuiSpacer size="m" />
170212
<EuiHorizontalRule margin="none" />

x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/datasource_input_stream_config.tsx

Lines changed: 47 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,26 +7,38 @@ import React, { useState, Fragment } from 'react';
77
import ReactMarkdown from 'react-markdown';
88
import { FormattedMessage } from '@kbn/i18n/react';
99
import {
10-
EuiFlexGrid,
1110
EuiFlexGroup,
1211
EuiFlexItem,
1312
EuiSwitch,
1413
EuiText,
1514
EuiSpacer,
1615
EuiButtonEmpty,
16+
EuiTextColor,
17+
EuiIconTip,
1718
} from '@elastic/eui';
1819
import { DatasourceInputStream, RegistryStream, RegistryVarsEntry } from '../../../../types';
19-
import { isAdvancedVar } from '../services';
20+
import { isAdvancedVar, DatasourceConfigValidationResults, validationHasErrors } from '../services';
2021
import { DatasourceInputVarField } from './datasource_input_var_field';
2122

2223
export const DatasourceInputStreamConfig: React.FunctionComponent<{
2324
packageInputStream: RegistryStream;
2425
datasourceInputStream: DatasourceInputStream;
2526
updateDatasourceInputStream: (updatedStream: Partial<DatasourceInputStream>) => void;
26-
}> = ({ packageInputStream, datasourceInputStream, updateDatasourceInputStream }) => {
27+
inputStreamValidationResults: DatasourceConfigValidationResults;
28+
forceShowErrors?: boolean;
29+
}> = ({
30+
packageInputStream,
31+
datasourceInputStream,
32+
updateDatasourceInputStream,
33+
inputStreamValidationResults,
34+
forceShowErrors,
35+
}) => {
2736
// Showing advanced options toggle state
2837
const [isShowingAdvanced, setIsShowingAdvanced] = useState<boolean>(false);
2938

39+
// Errors state
40+
const hasErrors = forceShowErrors && validationHasErrors(inputStreamValidationResults);
41+
3042
const requiredVars: RegistryVarsEntry[] = [];
3143
const advancedVars: RegistryVarsEntry[] = [];
3244

@@ -41,10 +53,33 @@ export const DatasourceInputStreamConfig: React.FunctionComponent<{
4153
}
4254

4355
return (
44-
<EuiFlexGrid columns={2}>
45-
<EuiFlexItem>
56+
<EuiFlexGroup>
57+
<EuiFlexItem grow={1}>
4658
<EuiSwitch
47-
label={packageInputStream.title || packageInputStream.dataset}
59+
label={
60+
<EuiFlexGroup alignItems="center" gutterSize="s">
61+
<EuiFlexItem grow={false}>
62+
<EuiTextColor color={hasErrors ? 'danger' : 'default'}>
63+
{packageInputStream.title || packageInputStream.dataset}
64+
</EuiTextColor>
65+
</EuiFlexItem>
66+
{hasErrors ? (
67+
<EuiFlexItem grow={false}>
68+
<EuiIconTip
69+
content={
70+
<FormattedMessage
71+
id="xpack.ingestManager.createDatasource.stepConfigure.streamLevelErrorsTooltip"
72+
defaultMessage="Fix configuration errors"
73+
/>
74+
}
75+
position="right"
76+
type="alert"
77+
iconProps={{ color: 'danger' }}
78+
/>
79+
</EuiFlexItem>
80+
) : null}
81+
</EuiFlexGroup>
82+
}
4883
checked={datasourceInputStream.enabled}
4984
onChange={e => {
5085
const enabled = e.target.checked;
@@ -62,7 +97,7 @@ export const DatasourceInputStreamConfig: React.FunctionComponent<{
6297
</Fragment>
6398
) : null}
6499
</EuiFlexItem>
65-
<EuiFlexItem>
100+
<EuiFlexItem grow={1}>
66101
<EuiFlexGroup direction="column" gutterSize="m">
67102
{requiredVars.map(varDef => {
68103
const { name: varName, type: varType } = varDef;
@@ -83,6 +118,8 @@ export const DatasourceInputStreamConfig: React.FunctionComponent<{
83118
},
84119
});
85120
}}
121+
errors={inputStreamValidationResults.config![varName]}
122+
forceShowErrors={forceShowErrors}
86123
/>
87124
</EuiFlexItem>
88125
);
@@ -125,6 +162,8 @@ export const DatasourceInputStreamConfig: React.FunctionComponent<{
125162
},
126163
});
127164
}}
165+
errors={inputStreamValidationResults.config![varName]}
166+
forceShowErrors={forceShowErrors}
128167
/>
129168
</EuiFlexItem>
130169
);
@@ -134,6 +173,6 @@ export const DatasourceInputStreamConfig: React.FunctionComponent<{
134173
) : null}
135174
</EuiFlexGroup>
136175
</EuiFlexItem>
137-
</EuiFlexGrid>
176+
</EuiFlexGroup>
138177
);
139178
};

0 commit comments

Comments
 (0)