diff --git a/x-pack/plugins/remote_clusters/__jest__/client_integration/add/remote_clusters_add.test.ts b/x-pack/plugins/remote_clusters/__jest__/client_integration/add/remote_clusters_add.test.ts index 43157fa157f0b..6e6f5d7692b75 100644 --- a/x-pack/plugins/remote_clusters/__jest__/client_integration/add/remote_clusters_add.test.ts +++ b/x-pack/plugins/remote_clusters/__jest__/client_integration/add/remote_clusters_add.test.ts @@ -11,6 +11,7 @@ import { act } from 'react-dom/test-utils'; import { setupEnvironment, RemoteClustersActions } from '../helpers'; import { setup } from './remote_clusters_add.helpers'; import { NON_ALPHA_NUMERIC_CHARS, ACCENTED_CHARS } from './special_characters'; +import { MAX_NODE_CONNECTIONS } from '../../../common/constants'; const notInArray = (array: string[]) => (value: string) => array.indexOf(value) < 0; @@ -276,6 +277,17 @@ describe('Create Remote cluster', () => { }); }); + describe('node connections', () => { + test('should require a valid number of node connections', async () => { + await actions.saveButton.click(); + + actions.nodeConnectionsInput.setValue(String(MAX_NODE_CONNECTIONS + 1)); + expect(actions.getErrorMessages()).toContain( + `This number must be equal or less than ${MAX_NODE_CONNECTIONS}.` + ); + }); + }); + test('server name is optional (proxy connection)', () => { actions.connectionModeSwitch.toggle(); actions.saveButton.click(); diff --git a/x-pack/plugins/remote_clusters/__jest__/client_integration/helpers/remote_clusters_actions.ts b/x-pack/plugins/remote_clusters/__jest__/client_integration/helpers/remote_clusters_actions.ts index f657058231a84..00e33def31ef6 100644 --- a/x-pack/plugins/remote_clusters/__jest__/client_integration/helpers/remote_clusters_actions.ts +++ b/x-pack/plugins/remote_clusters/__jest__/client_integration/helpers/remote_clusters_actions.ts @@ -42,6 +42,9 @@ export interface RemoteClustersActions { setValue: (seed: string) => void; getValue: () => string; }; + nodeConnectionsInput: { + setValue: (connections: string) => void; + }; proxyAddressInput: { setValue: (proxyAddress: string) => void; exists: () => boolean; @@ -154,6 +157,16 @@ export const createRemoteClustersActions = (testBed: TestBed): RemoteClustersAct }; }; + const createNodeConnectionsInputActions = () => { + const nodeConnectionsInputSelector = 'remoteClusterFormNodeConnectionsInput'; + return { + nodeConnectionsInput: { + setValue: (connections: string) => + form.setInputValue(nodeConnectionsInputSelector, connections), + }, + }; + }; + const createProxyAddressActions = () => { const proxyAddressSelector = 'remoteClusterFormProxyAddressInput'; return { @@ -266,6 +279,7 @@ export const createRemoteClustersActions = (testBed: TestBed): RemoteClustersAct ...createConnectionModeActions(), ...createCloudAdvancedOptionsSwitchActions(), ...createSeedsInputActions(), + ...createNodeConnectionsInputActions(), ...createCloudRemoteAddressInputActions(), ...createProxyAddressActions(), ...createServerNameActions(), diff --git a/x-pack/plugins/remote_clusters/common/constants.ts b/x-pack/plugins/remote_clusters/common/constants.ts index 1357de2cd4640..889b5afc7e1fc 100644 --- a/x-pack/plugins/remote_clusters/common/constants.ts +++ b/x-pack/plugins/remote_clusters/common/constants.ts @@ -42,3 +42,6 @@ export const getSecurityModel = (type: string) => { return type; }; + +// Hardcoded limit of maximum node connections allowed +export const MAX_NODE_CONNECTIONS = 2 ** 31 - 1; // 2147483647 diff --git a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/components/sniff_connection.tsx b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/components/sniff_connection.tsx index 2d1608bcc4a70..c29a1ce5c2169 100644 --- a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/components/sniff_connection.tsx +++ b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/components/sniff_connection.tsx @@ -28,14 +28,16 @@ export const SniffConnection: FunctionComponent = ({ }) => { const [localSeedErrors, setLocalSeedErrors] = useState([]); const { seeds = [], nodeConnections } = fields; - const { seeds: seedsError } = fieldsErrors; + const { seeds: seedsError, nodeConnections: nodeError } = fieldsErrors; // Show errors if there is a general form error or local errors. const areFormErrorsVisible = Boolean(areErrorsVisible && seedsError); - const showErrors = areFormErrorsVisible || localSeedErrors.length !== 0; - const errors = + const showLocalSeedErrors = areFormErrorsVisible || localSeedErrors.length !== 0; + const errorsInLocalSeeds = areFormErrorsVisible && seedsError ? localSeedErrors.concat(seedsError) : localSeedErrors; const formattedSeeds: EuiComboBoxOptionOption[] = seeds.map((seed: string) => ({ label: seed })); + const showNodeConnectionErrors = Boolean(nodeError); + const onCreateSeed = (newSeed?: string) => { // If the user just hit enter without typing anything, treat it as a no-op. if (!newSeed) { @@ -107,8 +109,8 @@ export const SniffConnection: FunctionComponent = ({ }} /> } - isInvalid={showErrors} - error={errors} + isInvalid={showLocalSeedErrors} + error={errorsInLocalSeeds} fullWidth > = ({ onFieldsChange({ seeds: options.map(({ label }) => label) }) } onSearchChange={onSeedsInputChange} - isInvalid={showErrors} + isInvalid={showLocalSeedErrors} fullWidth data-test-subj="remoteClusterFormSeedsInput" /> @@ -146,11 +148,15 @@ export const SniffConnection: FunctionComponent = ({ /> } fullWidth + isInvalid={showNodeConnectionErrors} + error={nodeError} > onFieldsChange({ nodeConnections: Number(e.target.value) })} + isInvalid={showNodeConnectionErrors} fullWidth + data-test-subj="remoteClusterFormNodeConnectionsInput" /> diff --git a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/__snapshots__/validate_node_connections.test.tsx.snap b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/__snapshots__/validate_node_connections.test.tsx.snap new file mode 100644 index 0000000000000..0c1ec0f1db18c --- /dev/null +++ b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/__snapshots__/validate_node_connections.test.tsx.snap @@ -0,0 +1,13 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`validateNodeConnections rejects numbers larger than MaxValue 1`] = ` + +`; diff --git a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/index.ts b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/index.ts index 6a710dae744ba..1fca3c5e83a5c 100644 --- a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/index.ts +++ b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/index.ts @@ -16,3 +16,4 @@ export { validateCloudRemoteAddress, convertCloudRemoteAddressToProxyConnection, } from './validate_cloud_url'; +export { validateNodeConnections } from './validate_node_connections'; diff --git a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_cluster.tsx b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_cluster.tsx index aba0b0462cdf5..6d3f9e31c74b6 100644 --- a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_cluster.tsx +++ b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_cluster.tsx @@ -11,6 +11,7 @@ import { validateSeeds } from './validate_seeds'; import { validateProxy } from './validate_proxy'; import { validateCloudRemoteAddress } from './validate_cloud_url'; import { FormFields } from '../remote_cluster_form'; +import { validateNodeConnections } from './validate_node_connections'; type ClusterError = JSX.Element | null; @@ -19,14 +20,16 @@ export interface ClusterErrors { seeds?: ClusterError; proxyAddress?: ClusterError; cloudRemoteAddress?: ClusterError; + nodeConnections?: ClusterError; } export const validateCluster = (fields: FormFields, isCloudEnabled: boolean): ClusterErrors => { - const { name, seeds = [], mode, proxyAddress, cloudRemoteAddress } = fields; + const { name, seeds = [], mode, proxyAddress, cloudRemoteAddress, nodeConnections } = fields; return { name: validateName(name), seeds: mode === SNIFF_MODE ? validateSeeds(seeds) : null, proxyAddress: mode === PROXY_MODE ? validateProxy(proxyAddress) : null, cloudRemoteAddress: isCloudEnabled ? validateCloudRemoteAddress(cloudRemoteAddress) : null, + nodeConnections: mode === SNIFF_MODE ? validateNodeConnections(nodeConnections) : null, }; }; diff --git a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_node_connections.test.tsx b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_node_connections.test.tsx new file mode 100644 index 0000000000000..20f39395692b7 --- /dev/null +++ b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_node_connections.test.tsx @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { MAX_NODE_CONNECTIONS } from '../../../../../../common/constants'; +import { validateNodeConnections } from './validate_node_connections'; + +describe('validateNodeConnections', () => { + test('rejects numbers larger than MaxValue', () => { + expect(validateNodeConnections(MAX_NODE_CONNECTIONS + 1)).toMatchSnapshot(); + }); + + test('accepts numbers equal than MaxValue', () => { + expect(validateNodeConnections(MAX_NODE_CONNECTIONS)).toBe(null); + }); + + test('accepts numbers smaller than MaxValue', () => { + expect(validateNodeConnections(MAX_NODE_CONNECTIONS - 1)).toBe(null); + }); +}); diff --git a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_node_connections.tsx b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_node_connections.tsx new file mode 100644 index 0000000000000..4adadb6fc1d6d --- /dev/null +++ b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_node_connections.tsx @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { MAX_NODE_CONNECTIONS } from '../../../../../../common/constants'; + +export function validateNodeConnections(connections?: number | null): null | JSX.Element { + if (connections && connections > MAX_NODE_CONNECTIONS) { + return ( + + ); + } + + return null; +} diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 0e7b40b66924b..fc240ba84ad29 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -47476,4 +47476,4 @@ "xpack.watcher.watchEdit.thresholdWatchExpression.aggType.fieldIsRequiredValidationMessage": "Ce champ est requis.", "xpack.watcher.watcherDescription": "Détectez les modifications survenant dans vos données en créant, gérant et monitorant des alertes." } -} \ No newline at end of file +} diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index f3d9422842083..4d39022965da0 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -47214,4 +47214,4 @@ "xpack.watcher.watchEdit.thresholdWatchExpression.aggType.fieldIsRequiredValidationMessage": "フィールドを選択してください。", "xpack.watcher.watcherDescription": "アラートの作成、管理、監視によりデータへの変更を検知します。" } -} \ No newline at end of file +} diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 48712e7f2fb69..27ee1dd00a563 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -47267,4 +47267,4 @@ "xpack.watcher.watchEdit.thresholdWatchExpression.aggType.fieldIsRequiredValidationMessage": "此字段必填。", "xpack.watcher.watcherDescription": "通过创建、管理和监测警报来检测数据中的更改。" } -} \ No newline at end of file +}