Skip to content

Commit 76e6d95

Browse files
[Cases] Allow to add additional fields to IBM resilient connector (elastic#236144)
## Summary Allows to send additional properties via the IBM resilient connector <img width="1252" height="355" alt="Screenshot 2025-09-25 at 16 20 14" src="https://github.com/user-attachments/assets/78bbec79-22d2-47d5-8238-fcb14cfaf887" /> <img width="500" height="162" alt="Screenshot 2025-09-25 at 16 20 37" src="https://github.com/user-attachments/assets/cd615bdb-648d-48a1-ade0-8c8db1977d01" /> Fixes elastic#213890, Fixes elastic#213896 ### How to test - Set up the IBM resilient connector and enable it - Create a new case and add `{"test_text":"test"}` to `Additional fields` - Save the case and check that the field exists in IBM resilient - Go back to the case in Kibana, update the additional fields, push the update and check that the new value is present in IBM resilient **(Advanced)**: Try setting one of these values: ```json { "testing": "test", "test_text": "the text", "test_boolean": true, "test_number": 123, "test_date_picker": 2390748293, "test_date_time_picker": 2390748298, "test_select": 4, "test_multi_select": [1,3], "test_text_area": "" } ``` Try setting `test_select` or `test_multi_select`. They are values that are validated on the server. --------- Co-authored-by: Michael Olorunnisola <michael.olorunnisola@elastic.co>
1 parent 0870546 commit 76e6d95

46 files changed

Lines changed: 1536 additions & 257 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

packages/kbn-optimizer/limits.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ pageLoadAssetSize:
162162
snapshotRestore: 28452
163163
spaces: 32135
164164
stackAlerts: 31499
165-
stackConnectors: 68502
165+
stackConnectors: 68838
166166
streams: 9000
167167
streamsApp: 17000
168168
synthetics: 31571

src/platform/test/plugin_functional/test_suites/core_plugins/rendering.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,7 @@ export default function ({ getService }: PluginFunctionalProviderContext) {
226226
'xpack.cases.files.maxSize (number?)',
227227
'xpack.cases.markdownPlugins.lens (boolean?)',
228228
'xpack.cases.stack.enabled (boolean?)',
229+
'xpack.cases.resilient.additionalFields.enabled (boolean?)',
229230
'xpack.cases.incrementalId.enabled (boolean?)',
230231
'xpack.ccr.ui.enabled (boolean?)',
231232
'xpack.cloud.base_url (string?)',
@@ -370,6 +371,7 @@ export default function ({ getService }: PluginFunctionalProviderContext) {
370371
'xpack.snapshot_restore.slm_ui.enabled (boolean?)',
371372
'xpack.snapshot_restore.ui.enabled (boolean?)',
372373
'xpack.stack_connectors.enableExperimental (array?)',
374+
'xpack.stack_connectors.resilient.additionalFields.enabled (boolean?)',
373375
'xpack.trigger_actions_ui.enableExperimental (array?)',
374376
'xpack.trigger_actions_ui.enableGeoTrackingThresholdAlert (boolean?)',
375377
'xpack.trigger_actions_ui.rules.enabled (boolean?)',

x-pack/platform/plugins/shared/actions/server/integration_tests/__snapshots__/connector_types.test.ts.snap

Lines changed: 79 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

x-pack/platform/plugins/shared/actions/server/sub_action_framework/executor.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,11 @@ export const buildExecutor = <
8585
);
8686
}
8787

88+
let _subActionParams = subActionParams ?? {};
89+
8890
if (action.schema) {
8991
try {
90-
action.schema.validate(subActionParams);
92+
_subActionParams = action.schema.validate(subActionParams) as typeof subActionParams;
9193
} catch (reqValidationError) {
9294
throw createTaskRunError(
9395
new Error(`Request validation failed (${reqValidationError})`),
@@ -96,7 +98,7 @@ export const buildExecutor = <
9698
}
9799
}
98100

99-
const data = await func.call(service, subActionParams, connectorUsageCollector);
101+
const data = await func.call(service, _subActionParams, connectorUsageCollector);
100102
return { status: 'ok', data: data ?? {}, actionId };
101103
};
102104
};

x-pack/platform/plugins/shared/cases/common/types/domain/connector/v1.ts

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,17 +50,31 @@ const ConnectorJiraTypeFieldsRt = rt.strict({
5050
* Resilient
5151
*/
5252

53-
export const ResilientFieldsRt = rt.strict({
54-
incidentTypes: rt.union([rt.array(rt.string), rt.null]),
55-
severityCode: rt.union([rt.string, rt.null]),
56-
});
53+
export const ResilientFieldsRt = rt.intersection([
54+
rt.strict({
55+
incidentTypes: rt.union([rt.array(rt.string), rt.null]),
56+
severityCode: rt.union([rt.string, rt.null]),
57+
}),
58+
rt.exact(
59+
rt.partial({
60+
additionalFields: rt.union([rt.string, rt.null]),
61+
})
62+
),
63+
]);
5764

5865
export type ResilientFieldsType = rt.TypeOf<typeof ResilientFieldsRt>;
5966

60-
const ConnectorResilientTypeFieldsRt = rt.strict({
61-
type: rt.literal(ConnectorTypes.resilient),
62-
fields: rt.union([ResilientFieldsRt, rt.null]),
63-
});
67+
const ConnectorResilientTypeFieldsRt = rt.intersection([
68+
rt.strict({
69+
type: rt.literal(ConnectorTypes.resilient),
70+
fields: rt.union([ResilientFieldsRt, rt.null]),
71+
}),
72+
rt.exact(
73+
rt.partial({
74+
additionalFields: rt.union([rt.string, rt.null]),
75+
})
76+
),
77+
]);
6478

6579
/**
6680
* ServiceNow

x-pack/platform/plugins/shared/cases/common/ui/types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,11 @@ export interface CasesUiConfigType {
7575
stack: {
7676
enabled: boolean;
7777
};
78+
resilient: {
79+
additionalFields: {
80+
enabled: boolean;
81+
};
82+
};
7883
incrementalId: {
7984
enabled: boolean;
8085
};

x-pack/platform/plugins/shared/cases/public/components/connectors/servicenow/json_editor_field.test.tsx renamed to x-pack/platform/plugins/shared/cases/public/components/connectors/json_editor_field.test.tsx

File renamed without changes.

x-pack/platform/plugins/shared/cases/public/components/connectors/servicenow/json_editor_field.tsx renamed to x-pack/platform/plugins/shared/cases/public/components/connectors/json_editor_field.tsx

File renamed without changes.

x-pack/platform/plugins/shared/cases/public/components/connectors/resilient/case_fields.test.tsx

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { waitFor, screen, within } from '@testing-library/react';
1010
import userEvent, { type UserEvent } from '@testing-library/user-event';
1111

1212
import { connector, resilientIncidentTypes, resilientSeverity } from '../mock';
13+
import { KibanaServices } from '../../../common/lib/kibana';
1314
import { useGetIncidentTypes } from './use_get_incident_types';
1415
import { useGetSeverity } from './use_get_severity';
1516
import Fields from './case_fields';
@@ -21,6 +22,7 @@ jest.mock('../../../common/lib/kibana');
2122
jest.mock('./use_get_incident_types');
2223
jest.mock('./use_get_severity');
2324

25+
const getConfigMock = KibanaServices.getConfig as jest.Mock;
2426
const useGetIncidentTypesMock = useGetIncidentTypes as jest.Mock;
2527
const useGetSeverityMock = useGetSeverity as jest.Mock;
2628

@@ -57,6 +59,13 @@ describe('ResilientParamsFields renders', () => {
5759
});
5860

5961
beforeEach(() => {
62+
getConfigMock.mockReturnValue({
63+
resilient: {
64+
additionalFields: {
65+
enabled: true,
66+
},
67+
},
68+
});
6069
// Workaround for timeout via https://github.com/testing-library/user-event/issues/833#issuecomment-1171452841
6170
user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime });
6271

@@ -74,6 +83,23 @@ describe('ResilientParamsFields renders', () => {
7483

7584
expect(screen.getByText('Malware')).toBeInTheDocument();
7685
expect(screen.getByTestId('severitySelect')).toHaveValue('6');
86+
expect(screen.getByTestId('additionalFieldsEditor')).toBeInTheDocument();
87+
});
88+
89+
it('does not render the additional fields when it is disabled', () => {
90+
getConfigMock.mockReturnValue({
91+
resilient: {
92+
additionalFields: {
93+
enabled: false,
94+
},
95+
},
96+
});
97+
renderWithTestingProviders(
98+
<MockFormWrapperComponent fields={fields}>
99+
<Fields connector={connector} />
100+
</MockFormWrapperComponent>
101+
);
102+
expect(screen.queryByTestId('additionalFieldsEditor')).not.toBeInTheDocument();
77103
});
78104

79105
it('disabled the fields when loading incident types', async () => {

x-pack/platform/plugins/shared/cases/public/components/connectors/resilient/case_fields.tsx

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,18 @@ import {
1414
UseField,
1515
} from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib';
1616
import { SelectField } from '@kbn/es-ui-shared-plugin/static/forms/components';
17-
import { useKibana } from '../../../common/lib/kibana';
17+
import { KibanaServices, useKibana } from '../../../common/lib/kibana';
1818
import type { ConnectorFieldsProps } from '../types';
1919
import { useGetIncidentTypes } from './use_get_incident_types';
2020
import { useGetSeverity } from './use_get_severity';
2121

2222
import * as i18n from './translations';
23+
import { generateJSONValidator } from '../validate_json';
24+
import { JsonEditorField } from '../json_editor_field';
2325

2426
const ResilientFieldsComponent: React.FunctionComponent<ConnectorFieldsProps> = ({ connector }) => {
2527
const { http } = useKibana().services;
28+
const showAdditionalFields = KibanaServices.getConfig()?.resilient.additionalFields.enabled;
2629

2730
const {
2831
isLoading: isLoadingIncidentTypesData,
@@ -129,6 +132,32 @@ const ResilientFieldsComponent: React.FunctionComponent<ConnectorFieldsProps> =
129132
},
130133
}}
131134
/>
135+
{showAdditionalFields && (
136+
<UseField
137+
path="fields.additionalFields"
138+
component={JsonEditorField}
139+
config={{
140+
label: i18n.ADDITIONAL_FIELDS_LABEL,
141+
validations: [
142+
{
143+
validator: generateJSONValidator({ maxAdditionalFields: 50 }),
144+
},
145+
],
146+
}}
147+
componentProps={{
148+
euiCodeEditorProps: {
149+
fullWidth: true,
150+
height: '200px',
151+
options: {
152+
fontSize: '12px',
153+
renderValidationDecorations: 'off',
154+
},
155+
},
156+
dataTestSubj: 'additionalFieldsEditor',
157+
}}
158+
/>
159+
)}
160+
132161
<EuiSpacer size="m" />
133162
</span>
134163
);

0 commit comments

Comments
 (0)