[Enterprise Search] Add connector configurable field dependencies (#155039)

This PR hides a configurable field if its dependencies (document field
`depends_on`) are not satisfied.
Fields that have one or many dependencies also have some styling
changes.
This commit is contained in:
Navarone Feekery 2023-04-18 17:29:05 +02:00 committed by GitHub
parent 7172905d63
commit a7c9880263
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 104 additions and 14 deletions

View file

@ -13,6 +13,7 @@ export const NATIVE_CONNECTOR_DEFINITIONS: Record<string, NativeConnector | unde
mongodb: {
configuration: {
host: {
depends_on: [],
display: 'textbox',
label: i18n.translate(
'xpack.enterpriseSearch.nativeConnectors.mongodb.configuration.hostLabel',
@ -27,6 +28,7 @@ export const NATIVE_CONNECTOR_DEFINITIONS: Record<string, NativeConnector | unde
value: '',
},
user: {
depends_on: [],
display: 'textbox',
label: i18n.translate(
'xpack.enterpriseSearch.nativeConnectors.mongodb.configuration.usernameLabel',
@ -41,6 +43,7 @@ export const NATIVE_CONNECTOR_DEFINITIONS: Record<string, NativeConnector | unde
value: '',
},
password: {
depends_on: [],
display: 'textbox',
label: i18n.translate(
'xpack.enterpriseSearch.nativeConnectors.mongodb.configuration.passwordLabel',
@ -55,6 +58,7 @@ export const NATIVE_CONNECTOR_DEFINITIONS: Record<string, NativeConnector | unde
value: '',
},
database: {
depends_on: [],
display: 'textbox',
label: i18n.translate(
'xpack.enterpriseSearch.nativeConnectors.mongodb.configuration.databaseLabel',
@ -69,6 +73,7 @@ export const NATIVE_CONNECTOR_DEFINITIONS: Record<string, NativeConnector | unde
value: '',
},
collection: {
depends_on: [],
display: 'textbox',
label: i18n.translate(
'xpack.enterpriseSearch.nativeConnectors.mongodb.configuration.collectionLabel',
@ -83,6 +88,7 @@ export const NATIVE_CONNECTOR_DEFINITIONS: Record<string, NativeConnector | unde
value: '',
},
direct_connection: {
depends_on: [],
display: 'toggle',
label: i18n.translate(
'xpack.enterpriseSearch.nativeConnectors.mongodb.configuration.directConnectionLabel',
@ -113,6 +119,7 @@ export const NATIVE_CONNECTOR_DEFINITIONS: Record<string, NativeConnector | unde
mysql: {
configuration: {
host: {
depends_on: [],
display: 'textbox',
label: i18n.translate(
'xpack.enterpriseSearch.nativeConnectors.mysql.configuration.hostLabel',
@ -127,6 +134,7 @@ export const NATIVE_CONNECTOR_DEFINITIONS: Record<string, NativeConnector | unde
value: '',
},
port: {
depends_on: [],
display: 'numeric',
label: i18n.translate(
'xpack.enterpriseSearch.nativeConnectors.mysql.configuration.portLabel',
@ -141,6 +149,7 @@ export const NATIVE_CONNECTOR_DEFINITIONS: Record<string, NativeConnector | unde
value: '',
},
user: {
depends_on: [],
display: 'textbox',
label: i18n.translate(
'xpack.enterpriseSearch.nativeConnectors.mysql.configuration.usernameLabel',
@ -155,6 +164,7 @@ export const NATIVE_CONNECTOR_DEFINITIONS: Record<string, NativeConnector | unde
value: '',
},
password: {
depends_on: [],
display: 'textbox',
label: i18n.translate(
'xpack.enterpriseSearch.nativeConnectors.mysql.configuration.passwordLabel',
@ -169,6 +179,7 @@ export const NATIVE_CONNECTOR_DEFINITIONS: Record<string, NativeConnector | unde
value: '',
},
database: {
depends_on: [],
display: 'textbox',
label: i18n.translate(
'xpack.enterpriseSearch.nativeConnectors.mysql.configuration.databaseLabel',
@ -183,6 +194,7 @@ export const NATIVE_CONNECTOR_DEFINITIONS: Record<string, NativeConnector | unde
value: '',
},
tables: {
depends_on: [],
display: 'textarea',
label: i18n.translate(
'xpack.enterpriseSearch.nativeConnectors.mysql.configuration.tablesLabel',
@ -197,6 +209,7 @@ export const NATIVE_CONNECTOR_DEFINITIONS: Record<string, NativeConnector | unde
value: '',
},
ssl_enabled: {
depends_on: [],
display: 'toggle',
label: i18n.translate(
'xpack.enterpriseSearch.nativeConnectors.mysql.configuration.sslEnabledLabel',
@ -211,6 +224,7 @@ export const NATIVE_CONNECTOR_DEFINITIONS: Record<string, NativeConnector | unde
value: false,
},
ssl_ca: {
depends_on: [{ field: 'ssl_enabled', value: true }],
display: 'textbox',
label: i18n.translate(
'xpack.enterpriseSearch.nativeConnectors.mysql.configuration.sslCertificateLabel',

View file

@ -5,15 +5,23 @@
* 2.0.
*/
export interface SelectOptions {
export interface SelectOption {
label: string;
value: string;
}
export interface Dependency {
field: string;
value: string | number | boolean | null;
}
export type DependencyLookup = Record<string, string | number | boolean | null>;
export interface ConnectorConfigProperties {
depends_on: Dependency[];
display: string;
label: string;
options: SelectOptions[];
options: SelectOption[];
order?: number | null;
required: boolean;
sensitive: boolean;

View file

@ -34,6 +34,7 @@ export const indices: ElasticsearchIndexWithIngestion[] = [
api_key_id: null,
configuration: {
foo: {
depends_on: [],
display: 'textbox',
key: 'foo',
label: 'bar',
@ -141,6 +142,7 @@ export const indices: ElasticsearchIndexWithIngestion[] = [
api_key_id: null,
configuration: {
foo: {
depends_on: [],
display: 'textbox',
key: 'foo',
label: 'bar',

View file

@ -44,6 +44,7 @@ export const connectorIndex: ConnectorViewIndex = {
api_key_id: null,
configuration: {
foo: {
depends_on: [],
display: 'textbox',
key: 'foo',
label: 'bar',
@ -155,6 +156,7 @@ export const crawlerIndex: CrawlerViewIndex = {
api_key_id: null,
configuration: {
foo: {
depends_on: [],
display: 'textbox',
key: 'foo',
label: 'bar',

View file

@ -16,16 +16,22 @@ import {
EuiFlexItem,
EuiButton,
EuiButtonEmpty,
EuiPanel,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { Status } from '../../../../../../common/types/api';
import { DependencyLookup } from '../../../../../../common/types/connectors';
import { ConnectorConfigurationApiLogic } from '../../../api/connector/update_connector_configuration_api_logic';
import { ConnectorConfigurationField } from './connector_configuration_field';
import { ConnectorConfigurationLogic } from './connector_configuration_logic';
import {
ConfigEntry,
ConnectorConfigurationLogic,
dependenciesSatisfied,
} from './connector_configuration_logic';
export const ConnectorConfigurationForm = () => {
const { status } = useValues(ConnectorConfigurationApiLogic);
@ -33,6 +39,14 @@ export const ConnectorConfigurationForm = () => {
const { localConfigView } = useValues(ConnectorConfigurationLogic);
const { saveConfig, setIsEditing } = useActions(ConnectorConfigurationLogic);
const dependencyLookup: DependencyLookup = localConfigView.reduce(
(prev: Record<string, string | number | boolean | null>, configEntry: ConfigEntry) => ({
...prev,
[configEntry.key]: configEntry.value,
}),
{}
);
return (
<EuiForm
onSubmit={(event) => {
@ -42,9 +56,20 @@ export const ConnectorConfigurationForm = () => {
component="form"
>
{localConfigView.map((configEntry) => {
const { key, label } = configEntry;
const { depends_on: dependencies, key, label } = configEntry;
const hasDependencies = dependencies.length > 0;
return (
return hasDependencies ? (
dependenciesSatisfied(dependencies, dependencyLookup) ? (
<EuiPanel color="subdued" borderRadius="none">
<EuiFormRow label={label ?? ''} key={key}>
<ConnectorConfigurationField configEntry={configEntry} />
</EuiFormRow>
</EuiPanel>
) : (
<></>
)
) : (
<EuiFormRow label={label ?? ''} key={key}>
<ConnectorConfigurationField configEntry={configEntry} />
</EuiFormRow>

View file

@ -52,6 +52,7 @@ describe('ConnectorConfigurationLogic', () => {
ConnectorConfigurationApiLogic.actions.apiSuccess({
configuration: {
foo: {
depends_on: [],
display: 'textbox',
label: 'newBar',
options: [],
@ -67,6 +68,7 @@ describe('ConnectorConfigurationLogic', () => {
...DEFAULT_VALUES,
configState: {
foo: {
depends_on: [],
display: 'textbox',
label: 'newBar',
options: [],
@ -78,6 +80,7 @@ describe('ConnectorConfigurationLogic', () => {
},
configView: [
{
depends_on: [],
display: 'textbox',
key: 'foo',
label: 'newBar',
@ -93,6 +96,7 @@ describe('ConnectorConfigurationLogic', () => {
it('should set config on setConfigState', () => {
ConnectorConfigurationLogic.actions.setConfigState({
foo: {
depends_on: [],
display: 'textbox',
label: 'thirdBar',
options: [],
@ -106,6 +110,7 @@ describe('ConnectorConfigurationLogic', () => {
...DEFAULT_VALUES,
configState: {
foo: {
depends_on: [],
display: 'textbox',
label: 'thirdBar',
options: [],
@ -117,6 +122,7 @@ describe('ConnectorConfigurationLogic', () => {
},
configView: [
{
depends_on: [],
display: 'textbox',
key: 'foo',
label: 'thirdBar',
@ -133,6 +139,7 @@ describe('ConnectorConfigurationLogic', () => {
it('should set local config entry and sort keys', () => {
ConnectorConfigurationLogic.actions.setConfigState({
bar: {
depends_on: [],
display: 'textbox',
label: 'foo',
options: [],
@ -142,6 +149,7 @@ describe('ConnectorConfigurationLogic', () => {
value: 'foofoo',
},
password: {
depends_on: [],
display: 'textbox',
label: 'thirdBar',
options: [],
@ -153,6 +161,7 @@ describe('ConnectorConfigurationLogic', () => {
});
ConnectorConfigurationLogic.actions.setLocalConfigState({
bar: {
depends_on: [],
display: 'textbox',
label: 'foo',
options: [],
@ -162,6 +171,7 @@ describe('ConnectorConfigurationLogic', () => {
value: 'foofoo',
},
password: {
depends_on: [],
display: 'textbox',
label: 'thirdBar',
options: [],
@ -172,6 +182,7 @@ describe('ConnectorConfigurationLogic', () => {
},
});
ConnectorConfigurationLogic.actions.setLocalConfigEntry({
depends_on: [],
display: 'textbox',
key: 'bar',
label: 'foo',
@ -185,6 +196,7 @@ describe('ConnectorConfigurationLogic', () => {
...DEFAULT_VALUES,
configState: {
bar: {
depends_on: [],
display: 'textbox',
label: 'foo',
options: [],
@ -194,6 +206,7 @@ describe('ConnectorConfigurationLogic', () => {
value: 'foofoo',
},
password: {
depends_on: [],
display: 'textbox',
label: 'thirdBar',
options: [],
@ -205,6 +218,7 @@ describe('ConnectorConfigurationLogic', () => {
},
configView: [
{
depends_on: [],
display: 'textbox',
key: 'bar',
label: 'foo',
@ -215,6 +229,7 @@ describe('ConnectorConfigurationLogic', () => {
value: 'foofoo',
},
{
depends_on: [],
display: 'textbox',
key: 'password',
label: 'thirdBar',
@ -227,6 +242,7 @@ describe('ConnectorConfigurationLogic', () => {
],
localConfigState: {
bar: {
depends_on: [],
display: 'textbox',
label: 'foo',
options: [],
@ -236,6 +252,7 @@ describe('ConnectorConfigurationLogic', () => {
value: 'fafa',
},
password: {
depends_on: [],
display: 'textbox',
label: 'thirdBar',
options: [],
@ -247,6 +264,7 @@ describe('ConnectorConfigurationLogic', () => {
},
localConfigView: [
{
depends_on: [],
display: 'textbox',
key: 'bar',
label: 'foo',
@ -257,6 +275,7 @@ describe('ConnectorConfigurationLogic', () => {
value: 'fafa',
},
{
depends_on: [],
display: 'textbox',
key: 'password',
label: 'thirdBar',
@ -278,6 +297,7 @@ describe('ConnectorConfigurationLogic', () => {
configState: connectorIndex.connector.configuration,
configView: [
{
depends_on: [],
display: 'textbox',
key: 'foo',
label: 'bar',
@ -311,6 +331,7 @@ describe('ConnectorConfigurationLogic', () => {
configState: connectorIndex.connector.configuration,
configView: [
{
depends_on: [],
display: 'textbox',
key: 'foo',
label: 'bar',
@ -329,6 +350,7 @@ describe('ConnectorConfigurationLogic', () => {
localConfigState: connectorIndex.connector.configuration,
localConfigView: [
{
depends_on: [],
display: 'textbox',
key: 'foo',
label: 'bar',
@ -349,6 +371,7 @@ describe('ConnectorConfigurationLogic', () => {
ConnectorConfigurationLogic.actions.fetchIndexApiSuccess(connectorIndex);
ConnectorConfigurationLogic.actions.setLocalConfigState({
foo: {
depends_on: [],
display: 'textbox',
label: 'bar',
options: [],

View file

@ -7,7 +7,13 @@
import { kea, MakeLogicType } from 'kea';
import { ConnectorConfiguration, ConnectorStatus } from '../../../../../../common/types/connectors';
import {
ConnectorConfiguration,
ConnectorStatus,
Dependency,
DependencyLookup,
SelectOption,
} from '../../../../../../common/types/connectors';
import { isNotNullish } from '../../../../../../common/utils/is_not_nullish';
import {
@ -48,16 +54,12 @@ interface ConnectorConfigurationValues {
shouldStartInEditMode: boolean;
}
interface SelectOptions {
label: string;
value: string;
}
export interface ConfigEntry {
depends_on: Dependency[];
display: string;
key: string;
label: string;
options: SelectOptions[];
options: SelectOption[];
order?: number;
required: boolean;
sensitive: boolean;
@ -107,6 +109,19 @@ export function ensureBooleanType(value: string | number | boolean | null): bool
return Boolean(value);
}
export function dependenciesSatisfied(
dependencies: Dependency[],
dependencyLookup: DependencyLookup
): boolean {
for (const dependency of dependencies) {
if (dependency.value !== dependencyLookup[dependency.field]) {
return false;
}
}
return true;
}
export const ConnectorConfigurationLogic = kea<
MakeLogicType<ConnectorConfigurationValues, ConnectorConfigurationActions>
>({
@ -214,10 +229,11 @@ export const ConnectorConfigurationLogic = kea<
{
setLocalConfigEntry: (
configState,
{ key, display, label, options, order, required, sensitive, value }
// eslint-disable-next-line @typescript-eslint/naming-convention
{ key, depends_on, display, label, options, order, required, sensitive, value }
) => ({
...configState,
[key]: { display, label, options, order, required, sensitive, value },
[key]: { depends_on, display, label, options, order, required, sensitive, value },
}),
setLocalConfigState: (_, { configState }) => configState,
},