mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[ResponseOps][Cases] Fix template's custom fields bugs (#187591)
## Summary Fixes https://github.com/elastic/kibana/issues/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
This commit is contained in:
parent
0c0ce0d9c5
commit
6b0d628053
23 changed files with 1026 additions and 253 deletions
|
@ -600,6 +600,11 @@ describe('CommonFlyout ', () => {
|
|||
type: 'toggle',
|
||||
value: true,
|
||||
},
|
||||
{
|
||||
key: 'test_key_3',
|
||||
type: 'text',
|
||||
value: null,
|
||||
},
|
||||
{
|
||||
key: 'test_key_4',
|
||||
type: 'toggle',
|
||||
|
|
|
@ -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,
|
||||
|
@ -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,
|
||||
|
|
|
@ -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';
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
|
|
@ -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';
|
||||
|
@ -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;
|
||||
|
@ -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';
|
||||
|
|
|
@ -20,6 +20,7 @@ describe('configureTextCustomFieldFactory ', () => {
|
|||
label: 'Text',
|
||||
getEuiTableColumn: expect.any(Function),
|
||||
build: expect.any(Function),
|
||||
convertNullToEmpty: expect.any(Function),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -25,4 +25,5 @@ export const configureTextCustomFieldFactory: CustomFieldFactory<CaseCustomField
|
|||
View,
|
||||
Create,
|
||||
}),
|
||||
convertNullToEmpty: (value: string | boolean | null) => (value == null ? '' : String(value)),
|
||||
});
|
||||
|
|
|
@ -24,6 +24,7 @@ describe('configureToggleCustomFieldFactory ', () => {
|
|||
{ key: 'on', label: 'On', value: true },
|
||||
{ key: 'off', label: 'Off', value: false },
|
||||
],
|
||||
getDefaultValue: expect.any(Function),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -561,6 +561,11 @@ describe('TemplateForm', () => {
|
|||
type: 'toggle',
|
||||
value: true,
|
||||
},
|
||||
{
|
||||
key: 'test_key_3',
|
||||
type: 'text',
|
||||
value: null,
|
||||
},
|
||||
{
|
||||
key: 'test_key_4',
|
||||
type: 'toggle',
|
||||
|
@ -645,6 +650,11 @@ describe('TemplateForm', () => {
|
|||
type: 'toggle',
|
||||
value: true,
|
||||
},
|
||||
{
|
||||
key: 'test_key_3',
|
||||
type: 'text',
|
||||
value: null,
|
||||
},
|
||||
{
|
||||
key: 'test_key_4',
|
||||
type: 'toggle',
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -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,
|
||||
},
|
||||
|
|
|
@ -79,14 +79,19 @@ export const templateSerializer = (
|
|||
return data;
|
||||
}
|
||||
|
||||
const { fields: connectorFields = null, key, name, ...rest } = data;
|
||||
const {
|
||||
fields: connectorFields = null,
|
||||
key,
|
||||
name,
|
||||
customFields: templateCustomFields,
|
||||
...rest
|
||||
} = data;
|
||||
|
||||
const serializedConnectorFields = getConnectorsFormSerializer({ fields: connectorFields });
|
||||
const nonEmptyFields = removeEmptyFields({ ...rest });
|
||||
|
||||
const {
|
||||
connectorId,
|
||||
customFields: templateCustomFields,
|
||||
syncAlerts = false,
|
||||
templateTags,
|
||||
templateDescription,
|
||||
|
|
|
@ -556,51 +556,6 @@ describe('client', () => {
|
|||
});
|
||||
|
||||
describe('customFields', () => {
|
||||
it('throws when there are no customFields in configure and template has customField in the request', async () => {
|
||||
clientArgs.services.caseConfigureService.get.mockResolvedValue({
|
||||
// @ts-ignore: these are all the attributes needed for the test
|
||||
attributes: {
|
||||
templates: [
|
||||
{
|
||||
key: 'template_1',
|
||||
name: 'template 1',
|
||||
description: 'this is test description',
|
||||
caseFields: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
await expect(
|
||||
update(
|
||||
'test-id',
|
||||
{
|
||||
version: 'test-version',
|
||||
templates: [
|
||||
{
|
||||
key: 'template_1',
|
||||
name: 'template 1',
|
||||
description: 'this is test description',
|
||||
caseFields: {
|
||||
customFields: [
|
||||
{
|
||||
key: 'custom_field_key_1',
|
||||
type: CustomFieldTypes.TEXT,
|
||||
value: 'custom field value 1',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
clientArgs,
|
||||
casesClientInternal
|
||||
)
|
||||
).rejects.toThrow(
|
||||
'Failed to get patch configure in route: Error: No custom fields configured.'
|
||||
);
|
||||
});
|
||||
|
||||
it('throws when template has duplicated custom field keys in the request', async () => {
|
||||
clientArgs.services.caseConfigureService.get.mockResolvedValue({
|
||||
// @ts-ignore: these are all the attributes needed for the test
|
||||
|
@ -637,6 +592,14 @@ describe('client', () => {
|
|||
'test-id',
|
||||
{
|
||||
version: 'test-version',
|
||||
customFields: [
|
||||
{
|
||||
key: 'custom_field_key_1',
|
||||
label: 'text label',
|
||||
type: CustomFieldTypes.TEXT,
|
||||
required: false,
|
||||
},
|
||||
],
|
||||
templates: [
|
||||
{
|
||||
key: 'template_1',
|
||||
|
@ -667,67 +630,6 @@ describe('client', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('throws when there are invalid customField keys in the request', async () => {
|
||||
clientArgs.services.caseConfigureService.get.mockResolvedValue({
|
||||
// @ts-ignore: these are all the attributes needed for the test
|
||||
attributes: {
|
||||
customFields: [
|
||||
{
|
||||
key: 'custom_field_key_1',
|
||||
label: 'text label',
|
||||
type: CustomFieldTypes.TEXT,
|
||||
required: false,
|
||||
},
|
||||
],
|
||||
templates: [
|
||||
{
|
||||
key: 'template_1',
|
||||
name: 'template 1',
|
||||
description: 'this is test description',
|
||||
caseFields: {
|
||||
customFields: [
|
||||
{
|
||||
key: 'custom_field_key_1',
|
||||
type: CustomFieldTypes.TEXT,
|
||||
value: 'custom field value 1',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
await expect(
|
||||
update(
|
||||
'test-id',
|
||||
{
|
||||
version: 'test-version',
|
||||
templates: [
|
||||
{
|
||||
key: 'template_1',
|
||||
name: 'template 1',
|
||||
description: 'this is test description',
|
||||
caseFields: {
|
||||
customFields: [
|
||||
{
|
||||
key: 'custom_field_key_2',
|
||||
type: CustomFieldTypes.TEXT,
|
||||
value: 'custom field value 1',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
clientArgs,
|
||||
casesClientInternal
|
||||
)
|
||||
).rejects.toThrow(
|
||||
'Failed to get patch configure in route: Error: Invalid custom field keys: custom_field_key_2'
|
||||
);
|
||||
});
|
||||
|
||||
it('throws when template has customField with invalid type in the request', async () => {
|
||||
clientArgs.services.caseConfigureService.get.mockResolvedValue({
|
||||
// @ts-ignore: these are all the attributes needed for the test
|
||||
|
@ -764,6 +666,14 @@ describe('client', () => {
|
|||
'test-id',
|
||||
{
|
||||
version: 'test-version',
|
||||
customFields: [
|
||||
{
|
||||
key: 'custom_field_key_1',
|
||||
label: 'text label',
|
||||
type: CustomFieldTypes.TEXT,
|
||||
required: false,
|
||||
},
|
||||
],
|
||||
templates: [
|
||||
{
|
||||
key: 'template_1',
|
||||
|
@ -789,6 +699,334 @@ describe('client', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('adds new custom field to template when configuration custom fields have new custom field', async () => {
|
||||
clientArgs.services.caseConfigureService.get.mockResolvedValue({
|
||||
// @ts-ignore: these are all the attributes needed for the test
|
||||
attributes: {
|
||||
connector: {
|
||||
id: 'none',
|
||||
name: 'none',
|
||||
type: ConnectorTypes.none,
|
||||
fields: null,
|
||||
},
|
||||
customFields: [],
|
||||
templates: [],
|
||||
closure_type: 'close-by-user',
|
||||
owner: 'cases',
|
||||
},
|
||||
id: 'test-id',
|
||||
version: 'test-version',
|
||||
});
|
||||
|
||||
await update(
|
||||
'test-id',
|
||||
{
|
||||
version: 'test-version',
|
||||
customFields: [
|
||||
{
|
||||
key: 'custom_field_key_1',
|
||||
label: 'text label',
|
||||
type: CustomFieldTypes.TEXT,
|
||||
required: false,
|
||||
defaultValue: 'custom field 1 default value 1',
|
||||
},
|
||||
],
|
||||
templates: [
|
||||
{
|
||||
key: 'template_1',
|
||||
name: 'template 1',
|
||||
description: 'this is test description',
|
||||
caseFields: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
clientArgs,
|
||||
casesClientInternal
|
||||
);
|
||||
|
||||
expect(clientArgs.services.caseConfigureService.patch).toHaveBeenCalledWith({
|
||||
configurationId: 'test-id',
|
||||
originalConfiguration: {
|
||||
attributes: {
|
||||
closure_type: 'close-by-user',
|
||||
connector: {
|
||||
fields: null,
|
||||
id: 'none',
|
||||
name: 'none',
|
||||
type: '.none',
|
||||
},
|
||||
customFields: [],
|
||||
owner: 'cases',
|
||||
templates: [],
|
||||
},
|
||||
id: 'test-id',
|
||||
version: 'test-version',
|
||||
},
|
||||
unsecuredSavedObjectsClient: expect.anything(),
|
||||
updatedAttributes: {
|
||||
customFields: [
|
||||
{
|
||||
key: 'custom_field_key_1',
|
||||
label: 'text label',
|
||||
type: CustomFieldTypes.TEXT,
|
||||
required: false,
|
||||
defaultValue: 'custom field 1 default value 1',
|
||||
},
|
||||
],
|
||||
templates: [
|
||||
{
|
||||
caseFields: {
|
||||
customFields: [
|
||||
{
|
||||
key: 'custom_field_key_1',
|
||||
type: CustomFieldTypes.TEXT,
|
||||
value: 'custom field 1 default value 1',
|
||||
},
|
||||
],
|
||||
},
|
||||
description: 'this is test description',
|
||||
key: 'template_1',
|
||||
name: 'template 1',
|
||||
},
|
||||
],
|
||||
updated_at: expect.anything(),
|
||||
updated_by: expect.anything(),
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('updates default value of existing custom fields in the configuration correctly', async () => {
|
||||
clientArgs.services.caseConfigureService.get.mockResolvedValue({
|
||||
// @ts-ignore: these are all the attributes needed for the test
|
||||
attributes: {
|
||||
connector: {
|
||||
id: 'none',
|
||||
name: 'none',
|
||||
type: ConnectorTypes.none,
|
||||
fields: null,
|
||||
},
|
||||
customFields: [
|
||||
{
|
||||
key: 'custom_field_key_1',
|
||||
label: 'text label',
|
||||
type: CustomFieldTypes.TEXT,
|
||||
required: false,
|
||||
defaultValue: 'custom field 1 default value 1',
|
||||
},
|
||||
],
|
||||
templates: [
|
||||
{
|
||||
key: 'template_1',
|
||||
name: 'template 1',
|
||||
description: 'this is test description',
|
||||
caseFields: {
|
||||
customFields: [
|
||||
{
|
||||
key: 'custom_field_key_1',
|
||||
type: CustomFieldTypes.TEXT,
|
||||
value: 'custom field 1 default value 1',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
closure_type: 'close-by-user',
|
||||
owner: 'cases',
|
||||
},
|
||||
id: 'test-id',
|
||||
version: 'test-version',
|
||||
});
|
||||
|
||||
await update(
|
||||
'test-id',
|
||||
{
|
||||
version: 'test-version',
|
||||
customFields: [
|
||||
{
|
||||
key: 'custom_field_key_1',
|
||||
label: 'text label',
|
||||
type: CustomFieldTypes.TEXT,
|
||||
required: false,
|
||||
defaultValue: 'updated default value!!',
|
||||
},
|
||||
],
|
||||
templates: [
|
||||
{
|
||||
key: 'template_1',
|
||||
name: 'template 1',
|
||||
description: 'this is test description',
|
||||
caseFields: {
|
||||
customFields: [
|
||||
{
|
||||
key: 'custom_field_key_1',
|
||||
type: CustomFieldTypes.TEXT,
|
||||
value: 'custom field 1 default value 1',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
clientArgs,
|
||||
casesClientInternal
|
||||
);
|
||||
|
||||
expect(clientArgs.services.caseConfigureService.patch).toHaveBeenCalledWith({
|
||||
configurationId: 'test-id',
|
||||
originalConfiguration: {
|
||||
attributes: {
|
||||
closure_type: 'close-by-user',
|
||||
connector: {
|
||||
fields: null,
|
||||
id: 'none',
|
||||
name: 'none',
|
||||
type: '.none',
|
||||
},
|
||||
owner: 'cases',
|
||||
customFields: [
|
||||
{
|
||||
key: 'custom_field_key_1',
|
||||
label: 'text label',
|
||||
type: CustomFieldTypes.TEXT,
|
||||
required: false,
|
||||
defaultValue: 'custom field 1 default value 1',
|
||||
},
|
||||
],
|
||||
templates: [
|
||||
{
|
||||
key: 'template_1',
|
||||
name: 'template 1',
|
||||
description: 'this is test description',
|
||||
caseFields: {
|
||||
customFields: [
|
||||
{
|
||||
key: 'custom_field_key_1',
|
||||
type: CustomFieldTypes.TEXT,
|
||||
value: 'custom field 1 default value 1',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
id: 'test-id',
|
||||
version: 'test-version',
|
||||
},
|
||||
unsecuredSavedObjectsClient: expect.anything(),
|
||||
updatedAttributes: {
|
||||
customFields: [
|
||||
{
|
||||
key: 'custom_field_key_1',
|
||||
label: 'text label',
|
||||
type: CustomFieldTypes.TEXT,
|
||||
required: false,
|
||||
defaultValue: 'updated default value!!',
|
||||
},
|
||||
],
|
||||
templates: [
|
||||
{
|
||||
caseFields: {
|
||||
customFields: [
|
||||
{
|
||||
key: 'custom_field_key_1',
|
||||
type: CustomFieldTypes.TEXT,
|
||||
value: 'custom field 1 default value 1',
|
||||
},
|
||||
],
|
||||
},
|
||||
description: 'this is test description',
|
||||
key: 'template_1',
|
||||
name: 'template 1',
|
||||
},
|
||||
],
|
||||
updated_at: expect.anything(),
|
||||
updated_by: expect.anything(),
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('removes custom field from template when there are no customFields in the request', async () => {
|
||||
clientArgs.services.caseConfigureService.get.mockResolvedValue({
|
||||
// @ts-ignore: these are all the attributes needed for the test
|
||||
attributes: {
|
||||
connector: {
|
||||
id: 'none',
|
||||
name: 'none',
|
||||
type: ConnectorTypes.none,
|
||||
fields: null,
|
||||
},
|
||||
customFields: [],
|
||||
templates: [],
|
||||
closure_type: 'close-by-user',
|
||||
owner: 'cases',
|
||||
},
|
||||
id: 'test-id',
|
||||
version: 'test-version',
|
||||
});
|
||||
|
||||
await update(
|
||||
'test-id',
|
||||
{
|
||||
version: 'test-version',
|
||||
customFields: [],
|
||||
templates: [
|
||||
{
|
||||
key: 'template_1',
|
||||
name: 'template 1',
|
||||
description: 'this is test description',
|
||||
caseFields: {
|
||||
customFields: [
|
||||
{
|
||||
key: 'custom_field_key_1',
|
||||
type: CustomFieldTypes.TEXT,
|
||||
value: 'custom field value 1',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
clientArgs,
|
||||
casesClientInternal
|
||||
);
|
||||
|
||||
expect(clientArgs.services.caseConfigureService.patch).toHaveBeenCalledWith({
|
||||
configurationId: 'test-id',
|
||||
originalConfiguration: {
|
||||
attributes: {
|
||||
closure_type: 'close-by-user',
|
||||
connector: {
|
||||
fields: null,
|
||||
id: 'none',
|
||||
name: 'none',
|
||||
type: '.none',
|
||||
},
|
||||
customFields: [],
|
||||
owner: 'cases',
|
||||
templates: [],
|
||||
},
|
||||
id: 'test-id',
|
||||
version: 'test-version',
|
||||
},
|
||||
unsecuredSavedObjectsClient: expect.anything(),
|
||||
updatedAttributes: {
|
||||
customFields: [],
|
||||
templates: [
|
||||
{
|
||||
key: 'template_1',
|
||||
name: 'template 1',
|
||||
description: 'this is test description',
|
||||
caseFields: {
|
||||
customFields: [],
|
||||
},
|
||||
},
|
||||
],
|
||||
updated_at: expect.anything(),
|
||||
updated_by: expect.anything(),
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('removes deleted custom field from template correctly', async () => {
|
||||
clientArgs.services.caseConfigureService.get.mockResolvedValue({
|
||||
// @ts-ignore: these are all the attributes needed for the test
|
||||
|
@ -1166,7 +1404,7 @@ describe('client', () => {
|
|||
).resolves.not.toThrow();
|
||||
});
|
||||
|
||||
it('throws when there are no customFields in configure and template has customField in the request', async () => {
|
||||
it('throws error when there are no customFields but template has custom fields in the request', async () => {
|
||||
await expect(
|
||||
create(
|
||||
{
|
||||
|
@ -1241,7 +1479,7 @@ describe('client', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('throws when there are invalid customField keys in the request', async () => {
|
||||
it('throw error when there are new customFields in the request but template does not have custom fields', async () => {
|
||||
await expect(
|
||||
create(
|
||||
{
|
||||
|
@ -1259,15 +1497,7 @@ describe('client', () => {
|
|||
key: 'template_1',
|
||||
name: 'template 1',
|
||||
description: 'this is test description',
|
||||
caseFields: {
|
||||
customFields: [
|
||||
{
|
||||
key: 'custom_field_key_2',
|
||||
type: CustomFieldTypes.TEXT,
|
||||
value: 'custom field value 1',
|
||||
},
|
||||
],
|
||||
},
|
||||
caseFields: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -1275,7 +1505,7 @@ describe('client', () => {
|
|||
casesClientInternal
|
||||
)
|
||||
).rejects.toThrow(
|
||||
'Failed to create case configuration: Error: Invalid custom field keys: custom_field_key_2'
|
||||
'Failed to create case configuration: Error: No custom fields added to template.'
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ import type { CasesClientArgs } from '../types';
|
|||
import { getMappings } from './get_mappings';
|
||||
|
||||
import { Operations } from '../../authorization';
|
||||
import { combineAuthorizedAndOwnerFilter, removeCustomFieldFromTemplates } from '../utils';
|
||||
import { combineAuthorizedAndOwnerFilter, transformTemplateCustomFields } from '../utils';
|
||||
import type { MappingsArgs, CreateMappingsArgs, UpdateMappingsArgs } from './types';
|
||||
import { createMappings } from './create_mappings';
|
||||
import { updateMappings } from './update_mappings';
|
||||
|
@ -320,14 +320,14 @@ export async function update(
|
|||
originalCustomFields: configuration.attributes.customFields,
|
||||
});
|
||||
|
||||
await validateTemplates({
|
||||
const updatedTemplates = transformTemplateCustomFields({
|
||||
templates,
|
||||
clientArgs,
|
||||
customFields: configuration.attributes.customFields,
|
||||
customFields: request.customFields,
|
||||
});
|
||||
|
||||
const updatedTemplates = removeCustomFieldFromTemplates({
|
||||
templates,
|
||||
await validateTemplates({
|
||||
templates: updatedTemplates,
|
||||
clientArgs,
|
||||
customFields: request.customFields,
|
||||
});
|
||||
|
||||
|
|
|
@ -198,6 +198,35 @@ describe('validators', () => {
|
|||
).toThrowErrorMatchingInlineSnapshot(`"No custom fields configured."`);
|
||||
});
|
||||
|
||||
it('throws if configuration has custom fields and template has no custom fields', () => {
|
||||
expect(() =>
|
||||
validateTemplatesCustomFieldsInRequest({
|
||||
templates: [
|
||||
{
|
||||
key: 'template_key_1',
|
||||
name: 'first template',
|
||||
description: 'this is a first template value',
|
||||
caseFields: null,
|
||||
},
|
||||
],
|
||||
customFieldsConfiguration: [
|
||||
{
|
||||
key: 'first_key',
|
||||
type: CustomFieldTypes.TEXT,
|
||||
label: 'foo',
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
key: 'second_key',
|
||||
type: CustomFieldTypes.TOGGLE,
|
||||
label: 'foo',
|
||||
required: false,
|
||||
},
|
||||
],
|
||||
})
|
||||
).toThrowErrorMatchingInlineSnapshot(`"No custom fields added to template."`);
|
||||
});
|
||||
|
||||
it('throws for a single invalid type', () => {
|
||||
expect(() =>
|
||||
validateTemplatesCustomFieldsInRequest({
|
||||
|
|
|
@ -60,20 +60,21 @@ export const validateTemplatesCustomFieldsInRequest = ({
|
|||
}
|
||||
|
||||
templates.forEach((template, index) => {
|
||||
if (
|
||||
!template.caseFields ||
|
||||
!template.caseFields.customFields ||
|
||||
!template.caseFields.customFields.length
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (customFieldsConfiguration === undefined) {
|
||||
if (customFieldsConfiguration === undefined && template.caseFields?.customFields?.length) {
|
||||
throw Boom.badRequest('No custom fields configured.');
|
||||
}
|
||||
|
||||
if (
|
||||
(!template.caseFields ||
|
||||
!template.caseFields.customFields ||
|
||||
!template.caseFields.customFields.length) &&
|
||||
customFieldsConfiguration?.length
|
||||
) {
|
||||
throw Boom.badRequest('No custom fields added to template.');
|
||||
}
|
||||
|
||||
const params = {
|
||||
requestCustomFields: template.caseFields.customFields,
|
||||
requestCustomFields: template?.caseFields?.customFields,
|
||||
customFieldsConfiguration,
|
||||
};
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ import {
|
|||
constructQueryOptions,
|
||||
constructSearch,
|
||||
convertSortField,
|
||||
removeCustomFieldFromTemplates,
|
||||
transformTemplateCustomFields,
|
||||
} from './utils';
|
||||
import { CasePersistedSeverity, CasePersistedStatus } from '../common/types/case';
|
||||
import type { CustomFieldsConfiguration } from '../../common/types/domain';
|
||||
|
@ -1132,7 +1132,7 @@ describe('utils', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('removeCustomFieldFromTemplates', () => {
|
||||
describe('transformTemplateCustomFields', () => {
|
||||
const customFields = [
|
||||
{
|
||||
type: CustomFieldTypes.TEXT as const,
|
||||
|
@ -1204,7 +1204,7 @@ describe('utils', () => {
|
|||
];
|
||||
|
||||
it('removes custom field from template correctly', () => {
|
||||
const res = removeCustomFieldFromTemplates({
|
||||
const res = transformTemplateCustomFields({
|
||||
templates,
|
||||
customFields: [customFields[0], customFields[1]],
|
||||
});
|
||||
|
@ -1253,7 +1253,7 @@ describe('utils', () => {
|
|||
});
|
||||
|
||||
it('removes multiple custom fields from template correctly', () => {
|
||||
const res = removeCustomFieldFromTemplates({
|
||||
const res = transformTemplateCustomFields({
|
||||
templates,
|
||||
customFields: [customFields[0]],
|
||||
});
|
||||
|
@ -1292,7 +1292,7 @@ describe('utils', () => {
|
|||
});
|
||||
|
||||
it('removes all custom fields from templates when custom fields are empty', () => {
|
||||
const res = removeCustomFieldFromTemplates({
|
||||
const res = transformTemplateCustomFields({
|
||||
templates,
|
||||
customFields: [],
|
||||
});
|
||||
|
@ -1319,7 +1319,7 @@ describe('utils', () => {
|
|||
});
|
||||
|
||||
it('removes all custom fields from templates when custom fields are undefined', () => {
|
||||
const res = removeCustomFieldFromTemplates({
|
||||
const res = transformTemplateCustomFields({
|
||||
templates,
|
||||
customFields: undefined,
|
||||
});
|
||||
|
@ -1330,8 +1330,8 @@ describe('utils', () => {
|
|||
]);
|
||||
});
|
||||
|
||||
it('does not remove custom field when templates do not have custom fields', () => {
|
||||
const res = removeCustomFieldFromTemplates({
|
||||
it('adds custom fields to templates when templates do not have custom fields', () => {
|
||||
const res = transformTemplateCustomFields({
|
||||
templates: [
|
||||
{
|
||||
key: 'test_template_1',
|
||||
|
@ -1353,7 +1353,20 @@ describe('utils', () => {
|
|||
|
||||
expect(res).toEqual([
|
||||
{
|
||||
caseFields: null,
|
||||
caseFields: {
|
||||
customFields: [
|
||||
{
|
||||
key: customFields[0].key,
|
||||
type: customFields[0].type,
|
||||
value: customFields[0].defaultValue,
|
||||
},
|
||||
{
|
||||
key: customFields[1].key,
|
||||
type: customFields[1].type,
|
||||
value: customFields[1].defaultValue,
|
||||
},
|
||||
],
|
||||
},
|
||||
description: 'This is a first test template',
|
||||
key: 'test_template_1',
|
||||
name: 'First test template',
|
||||
|
@ -1364,13 +1377,25 @@ describe('utils', () => {
|
|||
caseFields: {
|
||||
description: 'this is test',
|
||||
title: 'Test title',
|
||||
customFields: [
|
||||
{
|
||||
key: customFields[0].key,
|
||||
type: customFields[0].type,
|
||||
value: customFields[0].defaultValue,
|
||||
},
|
||||
{
|
||||
key: customFields[1].key,
|
||||
type: customFields[1].type,
|
||||
value: customFields[1].defaultValue,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('does not remove custom field when templates have empty custom fields', () => {
|
||||
const res = removeCustomFieldFromTemplates({
|
||||
it('adds custom fields to templates when template custom fields are empty', () => {
|
||||
const res = transformTemplateCustomFields({
|
||||
templates: [
|
||||
{
|
||||
key: 'test_template_2',
|
||||
|
@ -1382,7 +1407,7 @@ describe('utils', () => {
|
|||
},
|
||||
},
|
||||
],
|
||||
customFields: [customFields[0], customFields[1]],
|
||||
customFields: [customFields[0], customFields[1], customFields[2]],
|
||||
});
|
||||
|
||||
expect(res).toEqual([
|
||||
|
@ -1392,14 +1417,149 @@ describe('utils', () => {
|
|||
caseFields: {
|
||||
title: 'Test title',
|
||||
description: 'this is test',
|
||||
customFields: [],
|
||||
customFields: [
|
||||
{
|
||||
key: customFields[0].key,
|
||||
type: customFields[0].type,
|
||||
value: customFields[0].defaultValue,
|
||||
},
|
||||
{
|
||||
key: customFields[1].key,
|
||||
type: customFields[1].type,
|
||||
value: customFields[1].defaultValue,
|
||||
},
|
||||
{
|
||||
key: customFields[2].key,
|
||||
type: customFields[2].type,
|
||||
value: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('adds custom fields to templates with correct values', () => {
|
||||
const res = transformTemplateCustomFields({
|
||||
templates: [
|
||||
{
|
||||
key: 'test_template_2',
|
||||
name: 'Second test template',
|
||||
caseFields: {
|
||||
title: 'Test title',
|
||||
description: 'this is test',
|
||||
customFields: [],
|
||||
},
|
||||
},
|
||||
],
|
||||
customFields: [
|
||||
...customFields,
|
||||
{
|
||||
type: CustomFieldTypes.TOGGLE as const,
|
||||
key: 'test_key_4',
|
||||
label: 'My test label 4',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(res).toEqual([
|
||||
{
|
||||
key: 'test_template_2',
|
||||
name: 'Second test template',
|
||||
caseFields: {
|
||||
title: 'Test title',
|
||||
description: 'this is test',
|
||||
customFields: [
|
||||
{
|
||||
key: customFields[0].key,
|
||||
type: customFields[0].type,
|
||||
value: customFields[0].defaultValue,
|
||||
},
|
||||
{
|
||||
key: customFields[1].key,
|
||||
type: customFields[1].type,
|
||||
value: customFields[1].defaultValue,
|
||||
},
|
||||
{
|
||||
key: customFields[2].key,
|
||||
type: customFields[2].type,
|
||||
value: null,
|
||||
},
|
||||
{
|
||||
type: CustomFieldTypes.TOGGLE as const,
|
||||
key: 'test_key_4',
|
||||
value: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('does not change the existing template custom field', () => {
|
||||
const res = transformTemplateCustomFields({
|
||||
templates: [
|
||||
{
|
||||
key: 'test_template_2',
|
||||
name: 'Second test template',
|
||||
caseFields: {
|
||||
title: 'Test title',
|
||||
description: 'this is test',
|
||||
customFields: [
|
||||
{
|
||||
key: customFields[0].key,
|
||||
type: CustomFieldTypes.TEXT as const,
|
||||
value: 'updated text value',
|
||||
},
|
||||
{
|
||||
key: customFields[1].key,
|
||||
type: CustomFieldTypes.TOGGLE as const,
|
||||
value: false,
|
||||
},
|
||||
{
|
||||
key: customFields[2].key,
|
||||
type: customFields[2].type,
|
||||
value: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
customFields,
|
||||
});
|
||||
|
||||
expect(res).toEqual([
|
||||
{
|
||||
key: 'test_template_2',
|
||||
name: 'Second test template',
|
||||
caseFields: {
|
||||
title: 'Test title',
|
||||
description: 'this is test',
|
||||
customFields: [
|
||||
{
|
||||
key: customFields[0].key,
|
||||
type: customFields[0].type,
|
||||
value: 'updated text value',
|
||||
},
|
||||
{
|
||||
key: customFields[1].key,
|
||||
type: customFields[1].type,
|
||||
value: false,
|
||||
},
|
||||
{
|
||||
key: customFields[2].key,
|
||||
type: customFields[2].type,
|
||||
value: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('does not remove custom field from empty templates', () => {
|
||||
const res = removeCustomFieldFromTemplates({
|
||||
const res = transformTemplateCustomFields({
|
||||
templates: [],
|
||||
customFields: [customFields[0], customFields[1]],
|
||||
});
|
||||
|
@ -1408,7 +1568,7 @@ describe('utils', () => {
|
|||
});
|
||||
|
||||
it('returns empty array when templates are undefined', () => {
|
||||
const res = removeCustomFieldFromTemplates({
|
||||
const res = transformTemplateCustomFields({
|
||||
templates: undefined,
|
||||
customFields: [customFields[0], customFields[1]],
|
||||
});
|
||||
|
|
|
@ -17,11 +17,13 @@ import { nodeBuilder, fromKueryExpression, escapeKuery } from '@kbn/es-query';
|
|||
import { spaceIdToNamespace } from '@kbn/spaces-plugin/server/lib/utils/namespace';
|
||||
|
||||
import type {
|
||||
CaseCustomField,
|
||||
CaseSeverity,
|
||||
CaseStatuses,
|
||||
CustomFieldsConfiguration,
|
||||
ExternalReferenceAttachmentPayload,
|
||||
TemplatesConfiguration,
|
||||
CustomFieldTypes,
|
||||
} from '../../common/types/domain';
|
||||
import {
|
||||
ActionsAttachmentPayloadRt,
|
||||
|
@ -607,9 +609,9 @@ export const constructSearch = (
|
|||
};
|
||||
|
||||
/**
|
||||
* remove deleted custom field from template
|
||||
* remove deleted custom field from template or add newly added custom field to template
|
||||
*/
|
||||
export const removeCustomFieldFromTemplates = ({
|
||||
export const transformTemplateCustomFields = ({
|
||||
templates,
|
||||
customFields,
|
||||
}: {
|
||||
|
@ -621,21 +623,40 @@ export const removeCustomFieldFromTemplates = ({
|
|||
}
|
||||
|
||||
return templates.map((template) => {
|
||||
if (!template.caseFields?.customFields || !template.caseFields?.customFields.length) {
|
||||
return template;
|
||||
}
|
||||
const templateCustomFields = template.caseFields?.customFields ?? [];
|
||||
|
||||
if (!customFields || !customFields?.length) {
|
||||
if (!customFields || !customFields.length) {
|
||||
return { ...template, caseFields: { ...template.caseFields, customFields: [] } };
|
||||
}
|
||||
|
||||
const templateCustomFields = template.caseFields.customFields.filter((templateCustomField) =>
|
||||
// remove deleted custom field from template
|
||||
const transformedTemplateCustomFields = templateCustomFields.filter((templateCustomField) =>
|
||||
customFields?.find((customField) => customField.key === templateCustomField.key)
|
||||
);
|
||||
|
||||
// add new custom fields to template
|
||||
if (customFields.length >= transformedTemplateCustomFields.length) {
|
||||
customFields.forEach((field) => {
|
||||
if (
|
||||
!transformedTemplateCustomFields.find(
|
||||
(templateCustomField) => templateCustomField.key === field.key
|
||||
)
|
||||
) {
|
||||
const { getDefaultValue } = casesCustomFields.get(field.type) ?? {};
|
||||
const value = getDefaultValue?.() ?? null;
|
||||
|
||||
transformedTemplateCustomFields.push({
|
||||
key: field.key,
|
||||
type: field.type as CustomFieldTypes,
|
||||
value: field.defaultValue ?? value,
|
||||
} as CaseCustomField);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
...template,
|
||||
caseFields: { ...template.caseFields, customFields: templateCustomFields },
|
||||
caseFields: { ...template.caseFields, customFields: transformedTemplateCustomFields },
|
||||
};
|
||||
});
|
||||
};
|
||||
|
|
|
@ -19,4 +19,5 @@ export const getCasesToggleCustomField = () => ({
|
|||
}
|
||||
});
|
||||
},
|
||||
getDefaultValue: () => false,
|
||||
});
|
||||
|
|
|
@ -12,6 +12,7 @@ export interface ICasesCustomField {
|
|||
isSortable: boolean;
|
||||
savedObjectMappingType: string;
|
||||
validateFilteringValues: (values: Array<string | number | boolean | null>) => void;
|
||||
getDefaultValue?: () => boolean | string | null;
|
||||
}
|
||||
|
||||
export interface CasesCustomFieldsMap {
|
||||
|
|
|
@ -85,46 +85,48 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
});
|
||||
|
||||
it('should return a configuration with templates', async () => {
|
||||
const mockTemplates = [
|
||||
{
|
||||
key: 'test_template_1',
|
||||
name: 'First test template',
|
||||
description: 'This is a first test template',
|
||||
tags: [],
|
||||
caseFields: null,
|
||||
},
|
||||
{
|
||||
key: 'test_template_2',
|
||||
name: 'Second test template',
|
||||
description: 'This is a second test template',
|
||||
tags: ['foobar'],
|
||||
caseFields: {
|
||||
title: 'Case with sample template 2',
|
||||
description: 'case desc',
|
||||
severity: CaseSeverity.LOW,
|
||||
category: null,
|
||||
tags: ['sample-4'],
|
||||
assignees: [],
|
||||
customFields: [],
|
||||
connector: {
|
||||
id: 'none',
|
||||
name: 'My Connector',
|
||||
type: ConnectorTypes.none,
|
||||
fields: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'test_template_3',
|
||||
name: 'Third test template',
|
||||
description: 'This is a third test template',
|
||||
caseFields: {
|
||||
title: 'Case with sample template 3',
|
||||
tags: ['sample-3'],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const templates = {
|
||||
templates: [
|
||||
{
|
||||
key: 'test_template_1',
|
||||
name: 'First test template',
|
||||
description: 'This is a first test template',
|
||||
tags: [],
|
||||
caseFields: null,
|
||||
},
|
||||
{
|
||||
key: 'test_template_2',
|
||||
name: 'Second test template',
|
||||
description: 'This is a second test template',
|
||||
tags: ['foobar'],
|
||||
caseFields: {
|
||||
title: 'Case with sample template 2',
|
||||
description: 'case desc',
|
||||
severity: CaseSeverity.LOW,
|
||||
category: null,
|
||||
tags: ['sample-4'],
|
||||
assignees: [],
|
||||
customFields: [],
|
||||
connector: {
|
||||
id: 'none',
|
||||
name: 'My Connector',
|
||||
type: ConnectorTypes.none,
|
||||
fields: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'test_template_3',
|
||||
name: 'Third test template',
|
||||
description: 'This is a third test template',
|
||||
caseFields: {
|
||||
title: 'Case with sample template 3',
|
||||
tags: ['sample-3'],
|
||||
},
|
||||
},
|
||||
],
|
||||
templates: mockTemplates,
|
||||
};
|
||||
|
||||
await createConfiguration(
|
||||
|
@ -136,7 +138,11 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
const configuration = await getConfiguration({ supertest });
|
||||
|
||||
const data = removeServerGeneratedPropertiesFromSavedObject(configuration[0]);
|
||||
expect(data).to.eql(getConfigurationOutput(false, templates));
|
||||
expect(data).to.eql(
|
||||
getConfigurationOutput(false, {
|
||||
templates: mockTemplates,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should get a single configuration', async () => {
|
||||
|
|
|
@ -147,7 +147,7 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
},
|
||||
];
|
||||
|
||||
const templates = [
|
||||
const mockTemplates = [
|
||||
{
|
||||
key: 'test_template_1',
|
||||
name: 'First test template',
|
||||
|
@ -196,23 +196,61 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
tags: ['sample-3'],
|
||||
},
|
||||
},
|
||||
] as ConfigurationPatchRequest['templates'];
|
||||
];
|
||||
|
||||
const configuration = await createConfiguration(supertest, {
|
||||
...getConfigurationRequest(),
|
||||
customFields: customFieldsConfiguration as ConfigurationPatchRequest['customFields'],
|
||||
});
|
||||
|
||||
const newConfiguration = await updateConfiguration(supertest, configuration.id, {
|
||||
version: configuration.version,
|
||||
customFields: customFieldsConfiguration,
|
||||
templates,
|
||||
templates: mockTemplates as ConfigurationPatchRequest['templates'],
|
||||
});
|
||||
|
||||
const data = removeServerGeneratedPropertiesFromSavedObject(newConfiguration);
|
||||
expect(data).to.eql({
|
||||
...getConfigurationOutput(true),
|
||||
customFields: customFieldsConfiguration as ConfigurationPatchRequest['customFields'],
|
||||
templates,
|
||||
templates: [
|
||||
{
|
||||
...mockTemplates[0],
|
||||
caseFields: {
|
||||
customFields: [
|
||||
{
|
||||
key: 'text_field_1',
|
||||
type: CustomFieldTypes.TEXT,
|
||||
value: null,
|
||||
},
|
||||
{
|
||||
key: 'toggle_field_1',
|
||||
value: false,
|
||||
type: CustomFieldTypes.TOGGLE,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{ ...mockTemplates[1] },
|
||||
{
|
||||
...mockTemplates[2],
|
||||
caseFields: {
|
||||
...mockTemplates[2].caseFields,
|
||||
customFields: [
|
||||
{
|
||||
key: 'text_field_1',
|
||||
type: CustomFieldTypes.TEXT,
|
||||
value: null,
|
||||
},
|
||||
{
|
||||
key: 'toggle_field_1',
|
||||
value: false,
|
||||
type: CustomFieldTypes.TOGGLE,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
] as ConfigurationPatchRequest['templates'],
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -432,35 +470,6 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
);
|
||||
});
|
||||
|
||||
it("should not update a configuration with templates with custom fields that don't exist in the configuration", async () => {
|
||||
const configuration = await createConfiguration(supertest);
|
||||
|
||||
await updateConfiguration(
|
||||
supertest,
|
||||
configuration.id,
|
||||
{
|
||||
version: configuration.version,
|
||||
templates: [
|
||||
{
|
||||
key: 'test_template_1',
|
||||
name: 'First test template',
|
||||
description: 'This is a first test template',
|
||||
caseFields: {
|
||||
customFields: [
|
||||
{
|
||||
key: 'random_key',
|
||||
type: CustomFieldTypes.TEXT,
|
||||
value: 'Test',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
400
|
||||
);
|
||||
});
|
||||
|
||||
it('should not patch a configuration with duplicated template keys', async () => {
|
||||
const configuration = await createConfiguration(supertest);
|
||||
await updateConfiguration(
|
||||
|
|
|
@ -123,7 +123,20 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
key: 'test_template_1',
|
||||
name: 'First test template',
|
||||
description: 'This is a first test template',
|
||||
caseFields: null,
|
||||
caseFields: {
|
||||
customFields: [
|
||||
{
|
||||
key: 'text_field_1',
|
||||
type: CustomFieldTypes.TEXT,
|
||||
value: null,
|
||||
},
|
||||
{
|
||||
key: 'toggle_field_1',
|
||||
value: false,
|
||||
type: CustomFieldTypes.TOGGLE,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'test_template_2',
|
||||
|
@ -165,6 +178,18 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
caseFields: {
|
||||
title: 'Case with sample template 3',
|
||||
tags: ['sample-3'],
|
||||
customFields: [
|
||||
{
|
||||
key: 'text_field_1',
|
||||
type: CustomFieldTypes.TEXT,
|
||||
value: null,
|
||||
},
|
||||
{
|
||||
key: 'toggle_field_1',
|
||||
value: false,
|
||||
type: CustomFieldTypes.TOGGLE,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
@ -177,7 +202,11 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
);
|
||||
|
||||
const data = removeServerGeneratedPropertiesFromSavedObject(configuration);
|
||||
expect(data).to.eql({ ...getConfigurationOutput(false), customFields, templates });
|
||||
expect(data).to.eql({
|
||||
...getConfigurationOutput(false),
|
||||
customFields,
|
||||
templates,
|
||||
});
|
||||
});
|
||||
|
||||
it('should keep only the latest configuration', async () => {
|
||||
|
@ -493,11 +522,40 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
);
|
||||
});
|
||||
|
||||
it("should not create a configuration with templates with custom fields that don't exist in the configuration", async () => {
|
||||
it('should not create a configuration with duplicated template keys', async () => {
|
||||
await createConfiguration(
|
||||
supertest,
|
||||
getConfigurationRequest({
|
||||
overrides: {
|
||||
templates: [
|
||||
{
|
||||
key: 'test_template_1',
|
||||
name: 'First test template',
|
||||
description: 'This is a first test template',
|
||||
caseFields: null,
|
||||
},
|
||||
{
|
||||
key: 'test_template_1',
|
||||
name: 'Third test template',
|
||||
description: 'This is a third test template',
|
||||
caseFields: {
|
||||
title: 'Case with sample template 3',
|
||||
tags: ['sample-3'],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
400
|
||||
);
|
||||
});
|
||||
|
||||
it("should not create a configuration when templates have custom fields and custom fields don't exist in the configuration", async () => {
|
||||
await createConfiguration(
|
||||
supertest,
|
||||
getConfigurationRequest({
|
||||
overrides: {
|
||||
customFields: [],
|
||||
templates: [
|
||||
{
|
||||
key: 'test_template_1',
|
||||
|
@ -520,11 +578,20 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
);
|
||||
});
|
||||
|
||||
it('should not create a configuration with duplicated template keys', async () => {
|
||||
it('should not create a configuration when templates do not have custom fields and custom fields exist in the configuration', async () => {
|
||||
await createConfiguration(
|
||||
supertest,
|
||||
getConfigurationRequest({
|
||||
overrides: {
|
||||
customFields: [
|
||||
{
|
||||
key: 'random_key',
|
||||
type: CustomFieldTypes.TEXT,
|
||||
label: 'New custom field',
|
||||
defaultValue: 'Test',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
templates: [
|
||||
{
|
||||
key: 'test_template_1',
|
||||
|
@ -532,15 +599,6 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
description: 'This is a first test template',
|
||||
caseFields: null,
|
||||
},
|
||||
{
|
||||
key: 'test_template_1',
|
||||
name: 'Third test template',
|
||||
description: 'This is a third test template',
|
||||
caseFields: {
|
||||
title: 'Case with sample template 3',
|
||||
tags: ['sample-3'],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue