Skip to content

Commit

Permalink
[ResponseOps][Cases] Fix template's custom fields bugs (elastic#187591)
Browse files Browse the repository at this point in the history
## Summary

Fixes elastic#187333

## Testing behaviour: 
Issue 1: verify similar behaviour from API as well.

1. Create a template
2. Add new toggle custom field with default value as true
3. Go to create case, See that new toggle custom field has value: true
4. Select recently created template
5. Toggle custom field new custom field with it's default value

Issue 2: verify similar behaviour from API as well.
1. Create a text custom field with default value
2. Create a template
3. Set text custom field value to empty
4. Save template
5. Go to create case
6. Select recently created template
7. See that text custom field value is updated as per template's custom
field value
  • Loading branch information
js-jankisalvi committed Jul 11, 2024
1 parent 0c0ce0d commit 6b0d628
Show file tree
Hide file tree
Showing 23 changed files with 976 additions and 203 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -600,6 +600,11 @@ describe('CommonFlyout ', () => {
type: 'toggle',
value: true,
},
{
key: 'test_key_3',
type: 'text',
value: null,
},
{
key: 'test_key_4',
type: 'toggle',
Expand Down
165 changes: 165 additions & 0 deletions x-pack/plugins/cases/public/components/configure_cases/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -724,6 +724,166 @@ describe('ConfigureCases', () => {
});
});

it('deletes a custom field from template while deleting custom field from configuration', async () => {
useGetCaseConfigurationMock.mockImplementation(() => ({
...useCaseConfigureResponse,
data: {
...useCaseConfigureResponse.data,
customFields: customFieldsConfigurationMock,
templates: [
{
key: 'test_template_4',
name: 'Fourth test template',
caseFields: {
title: 'Case with sample template 4',
description: 'case desc',
customFields: [
{
key: customFieldsConfigurationMock[0].key,
type: CustomFieldTypes.TEXT,
value: 'this is a text field value',
},
],
},
},
],
},
}));

appMockRender.render(<ConfigureCases />);

const list = await screen.findByTestId('custom-fields-list');

userEvent.click(
within(list).getByTestId(`${customFieldsConfigurationMock[0].key}-custom-field-delete`)
);

expect(await screen.findByTestId('confirm-delete-modal')).toBeInTheDocument();

userEvent.click(screen.getByText('Delete'));

await waitFor(() => {
expect(persistCaseConfigure).toHaveBeenCalledWith({
connector: {
id: 'none',
name: 'none',
type: ConnectorTypes.none,
fields: null,
},
closureType: 'close-by-user',
customFields: [
{ ...customFieldsConfigurationMock[1] },
{ ...customFieldsConfigurationMock[2] },
{ ...customFieldsConfigurationMock[3] },
],
templates: [
{
key: 'test_template_4',
name: 'Fourth test template',
caseFields: {
title: 'Case with sample template 4',
description: 'case desc',
customFields: [],
},
},
],
id: '',
version: '',
});
});
});

it('adds a custom field to template while adding a new custom field', async () => {
useGetCaseConfigurationMock.mockImplementation(() => ({
...useCaseConfigureResponse,
data: {
...useCaseConfigureResponse.data,
customFields: customFieldsConfigurationMock,
templates: [
{
key: 'test_template_4',
name: 'Fourth test template',
caseFields: null,
},
],
},
}));

appMockRender.render(<ConfigureCases />);

userEvent.click(await screen.findByTestId(`add-custom-field`));

expect(await screen.findByTestId('common-flyout')).toBeInTheDocument();

userEvent.paste(screen.getByTestId('custom-field-label-input'), 'New custom field');
userEvent.click(screen.getByTestId('text-custom-field-required'));
userEvent.paste(
screen.getByTestId('text-custom-field-default-value'),
'This is a default value'
);

userEvent.click(screen.getByTestId('common-flyout-save'));

await waitFor(() => {
expect(persistCaseConfigure).toHaveBeenCalledWith({
connector: {
id: 'none',
name: 'none',
type: ConnectorTypes.none,
fields: null,
},
closureType: 'close-by-user',
customFields: [
...customFieldsConfigurationMock,
{
key: expect.anything(),
label: 'New custom field',
type: CustomFieldTypes.TEXT as const,
required: true,
defaultValue: 'This is a default value',
},
],
templates: [
{
key: 'test_template_4',
name: 'Fourth test template',
caseFields: {
customFields: [
{
key: customFieldsConfigurationMock[0].key,
type: customFieldsConfigurationMock[0].type,
value: customFieldsConfigurationMock[0].defaultValue,
},
{
key: customFieldsConfigurationMock[1].key,
type: customFieldsConfigurationMock[1].type,
value: customFieldsConfigurationMock[1].defaultValue,
},
{
key: customFieldsConfigurationMock[2].key,
type: customFieldsConfigurationMock[2].type,
value: null,
},
{
key: customFieldsConfigurationMock[3].key,
type: customFieldsConfigurationMock[3].type,
value: false,
},
{
key: expect.anything(),
type: CustomFieldTypes.TEXT as const,
value: 'This is a default value',
},
],
},
},
],
id: '',
version: '',
});
});
});

it('updates a custom field correctly', async () => {
useGetCaseConfigurationMock.mockImplementation(() => ({
...useCaseConfigureResponse,
Expand Down Expand Up @@ -929,6 +1089,11 @@ describe('ConfigureCases', () => {
type: customFieldsConfigurationMock[1].type,
value: customFieldsConfigurationMock[1].defaultValue,
},
{
key: customFieldsConfigurationMock[2].key,
type: customFieldsConfigurationMock[2].type,
value: null,
},
{
key: customFieldsConfigurationMock[3].key,
type: customFieldsConfigurationMock[3].type,
Expand Down
50 changes: 48 additions & 2 deletions x-pack/plugins/cases/public/components/configure_cases/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,11 @@ import {

import type { ActionConnectorTableItem } from '@kbn/triggers-actions-ui-plugin/public/types';
import { CasesConnectorFeatureId } from '@kbn/actions-plugin/common';
import type { CustomFieldConfiguration, TemplateConfiguration } from '../../../common/types/domain';
import type {
CustomFieldConfiguration,
TemplateConfiguration,
CustomFieldTypes,
} from '../../../common/types/domain';
import { useKibana } from '../../common/lib/kibana';
import { useGetActionTypes } from '../../containers/configure/use_action_types';
import { useGetCaseConfiguration } from '../../containers/configure/use_get_case_configuration';
Expand All @@ -48,6 +52,8 @@ import { Templates } from '../templates';
import type { TemplateFormProps } from '../templates/types';
import { CustomFieldsForm } from '../custom_fields/form';
import { TemplateForm } from '../templates/form';
import type { CasesConfigurationUI, CaseUI } from '../../containers/types';
import { builderMap as customFieldsBuilderMap } from '../custom_fields/builder';

const sectionWrapperCss = css`
box-sizing: content-box;
Expand All @@ -68,6 +74,40 @@ interface Flyout {
visible: boolean;
}

const addNewCustomFieldToTemplates = ({
templates,
customFields,
}: Pick<CasesConfigurationUI, 'templates' | 'customFields'>) => {
return templates.map((template) => {
const templateCustomFields = template.caseFields?.customFields ?? [];

customFields.forEach((field) => {
if (
!templateCustomFields.length ||
!templateCustomFields.find((templateCustomField) => templateCustomField.key === field.key)
) {
const customFieldFactory = customFieldsBuilderMap[field.type];
const { getDefaultValue } = customFieldFactory();
const value = getDefaultValue?.() ?? null;

templateCustomFields.push({
key: field.key,
type: field.type as CustomFieldTypes,
value: field.defaultValue ?? value,
} as CaseUI['customFields'][number]);
}
});

return {
...template,
caseFields: {
...template.caseFields,
customFields: [...templateCustomFields],
},
};
});
};

export const ConfigureCases: React.FC = React.memo(() => {
const { permissions } = useCasesContext();
const { triggersActionsUi } = useKibana().services;
Expand Down Expand Up @@ -334,10 +374,16 @@ export const ConfigureCases: React.FC = React.memo(() => {
(data: CustomFieldConfiguration) => {
const updatedCustomFields = addOrReplaceField(customFields, data);

// add the new custom field to each template as well
const updatedTemplates = addNewCustomFieldToTemplates({
templates,
customFields: updatedCustomFields,
});

persistCaseConfigure({
connector,
customFields: updatedCustomFields,
templates,
templates: updatedTemplates,
id: configurationId,
version: configurationVersion,
closureType,
Expand Down
20 changes: 18 additions & 2 deletions x-pack/plugins/cases/public/components/create/form_fields.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
import { css } from '@emotion/react';
import { useFormContext } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib';

import type { CasePostRequest } from '../../../common';
import type { CasePostRequest, CaseUI } from '../../../common';
import type { ActionConnector } from '../../../common/types/domain';
import { Connector } from '../case_form_fields/connector';
import * as i18n from './translations';
Expand All @@ -28,6 +28,7 @@ import { useCasesFeatures } from '../../common/use_cases_features';
import { TemplateSelector } from './templates';
import { getInitialCaseValue } from './utils';
import { CaseFormFields } from '../case_form_fields';
import { builderMap as customFieldsBuilderMap } from '../custom_fields/builder';

export interface CreateCaseFormFieldsProps {
configuration: CasesConfigurationUI;
Expand All @@ -42,7 +43,22 @@ const transformTemplateCaseFieldsToCaseFormFields = (
caseTemplateFields: CasesConfigurationUITemplate['caseFields']
): CasePostRequest => {
const caseFields = removeEmptyFields(caseTemplateFields ?? {});
return getInitialCaseValue({ owner, ...caseFields });
const transFormedCustomFields = caseFields?.customFields?.map((customField) => {
const customFieldFactory = customFieldsBuilderMap[customField.type];
const { convertNullToEmpty } = customFieldFactory();
const value = convertNullToEmpty ? convertNullToEmpty(customField.value) : customField.value;

return {
...customField,
value,
};
});

return getInitialCaseValue({
owner,
...caseFields,
customFields: transFormedCustomFields as CaseUI['customFields'],
});
};

const DEFAULT_EMPTY_TEMPLATE_KEY = 'defaultEmptyTemplateKey';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ describe('configureTextCustomFieldFactory ', () => {
label: 'Text',
getEuiTableColumn: expect.any(Function),
build: expect.any(Function),
convertNullToEmpty: expect.any(Function),
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,5 @@ export const configureTextCustomFieldFactory: CustomFieldFactory<CaseCustomField
View,
Create,
}),
convertNullToEmpty: (value: string | boolean | null) => (value == null ? '' : String(value)),
});
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ describe('configureToggleCustomFieldFactory ', () => {
{ key: 'on', label: 'On', value: true },
{ key: 'off', label: 'Off', value: false },
],
getDefaultValue: expect.any(Function),
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,5 @@ export const configureToggleCustomFieldFactory: CustomFieldFactory<CaseCustomFie
{ key: 'on', label: i18n.TOGGLE_FIELD_ON_LABEL, value: true },
{ key: 'off', label: i18n.TOGGLE_FIELD_OFF_LABEL, value: false },
],
getDefaultValue: () => false,
});
2 changes: 2 additions & 0 deletions x-pack/plugins/cases/public/components/custom_fields/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ export type CustomFieldFactory<T extends CaseUICustomField> = () => {
getEuiTableColumn: (params: { label: string }) => CustomFieldEuiTableColumn;
build: () => CustomFieldType<T>;
filterOptions?: CustomFieldFactoryFilterOption[];
getDefaultValue?: () => string | boolean | null;
convertNullToEmpty?: (value: string | boolean | null) => string;
};

export type CustomFieldBuilderMap = {
Expand Down
10 changes: 10 additions & 0 deletions x-pack/plugins/cases/public/components/templates/form.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -561,6 +561,11 @@ describe('TemplateForm', () => {
type: 'toggle',
value: true,
},
{
key: 'test_key_3',
type: 'text',
value: null,
},
{
key: 'test_key_4',
type: 'toggle',
Expand Down Expand Up @@ -645,6 +650,11 @@ describe('TemplateForm', () => {
type: 'toggle',
value: true,
},
{
key: 'test_key_3',
type: 'text',
value: null,
},
{
key: 'test_key_4',
type: 'toggle',
Expand Down
12 changes: 8 additions & 4 deletions x-pack/plugins/cases/public/components/templates/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,9 +117,9 @@ describe('utils', () => {
name: 'template 1',
templateDescription: '',
customFields: {
custom_field_1: 'foobar',
custom_fields_2: '',
custom_field_3: true,
test_key_1: 'foobar',
test_key_3: '',
test_key_2: true,
},
});

Expand All @@ -131,7 +131,11 @@ describe('utils', () => {
name: 'none',
type: '.none',
},
customFields: [],
customFields: [
{ key: 'test_key_1', type: 'text', value: 'foobar' },
{ key: 'test_key_3', type: 'text', value: null },
{ key: 'test_key_2', type: 'toggle', value: true },
],
settings: {
syncAlerts: false,
},
Expand Down
Loading

0 comments on commit 6b0d628

Please sign in to comment.