Skip to content

Commit

Permalink
Merge branch '8.11' into backport/8.11/pr-164235
Browse files Browse the repository at this point in the history
  • Loading branch information
stanek-michal authored Oct 6, 2023
2 parents 5cbe28c + fc4090b commit ca6b503
Show file tree
Hide file tree
Showing 9 changed files with 218 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { ScriptError } from '../components/preview/types';
import { RuntimeFieldPainlessError, PainlessErrorCode } from '../types';

export const getErrorCodeFromErrorReason = (reason: string = ''): PainlessErrorCode => {
if (reason.startsWith('Cannot cast from')) {
if (reason.includes('Cannot cast from')) {
return 'CAST_ERROR';
}
return 'UNKNOWN';
Expand Down
34 changes: 34 additions & 0 deletions x-pack/plugins/cases/common/types/api/case/v1.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,40 @@ describe('CasePostRequestRt', () => {
`The length of the value is too long. The maximum length is ${MAX_CUSTOM_FIELD_TEXT_VALUE_LENGTH}.`
);
});

it('throws an error when a text customFields is an empty array', () => {
expect(
PathReporter.report(
CasePostRequestRt.decode({
...defaultRequest,
customFields: [
{
key: 'first_custom_field_key',
type: 'text',
value: [],
},
],
})
)
).toContain('The length of the field value is too short. Array must be of length >= 1.');
});

it('throws an error when a text customField is an array with an empty string', () => {
expect(
PathReporter.report(
CasePostRequestRt.decode({
...defaultRequest,
customFields: [
{
key: 'first_custom_field_key',
type: 'text',
value: [''],
},
],
})
)
).toContain('The value field cannot be an empty string.');
});
});

describe('CasesFindRequestRt', () => {
Expand Down
8 changes: 4 additions & 4 deletions x-pack/plugins/cases/common/types/api/case/v1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,21 +44,21 @@ import { CaseConnectorRt } from '../../domain/connector/v1';
import { CaseUserProfileRt, UserRt } from '../../domain/user/v1';
import { CasesStatusResponseRt } from '../stats/v1';

const CaseCustomFieldWithValidationValueRt = limitedArraySchema({
const CaseCustomFieldTextWithValidationValueRt = limitedArraySchema({
codec: limitedStringSchema({
fieldName: 'value',
min: 0,
min: 1,
max: MAX_CUSTOM_FIELD_TEXT_VALUE_LENGTH,
}),
fieldName: 'value',
min: 0,
min: 1,
max: MAX_CUSTOM_FIELD_TEXT_VALUE_ITEMS,
});

const CaseCustomFieldTextWithValidationRt = rt.strict({
key: rt.string,
type: CustomFieldTextTypeRt,
value: rt.union([CaseCustomFieldWithValidationValueRt, rt.null]),
value: rt.union([CaseCustomFieldTextWithValidationValueRt, rt.null]),
});

const CustomFieldRt = rt.union([CaseCustomFieldTextWithValidationRt, CaseCustomFieldToggleRt]);
Expand Down
46 changes: 46 additions & 0 deletions x-pack/plugins/cases/server/client/cases/create.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,52 @@ describe('create', () => {
);
});

it('should throw an error when required customFields are null', async () => {
casesClient.configure.get = jest.fn().mockResolvedValue([
{
owner: theCase.owner,
customFields: [
{
key: 'first_key',
type: CustomFieldTypes.TEXT,
label: 'foo',
required: true,
},
{
key: 'second_key',
type: CustomFieldTypes.TOGGLE,
label: 'foo',
required: true,
},
],
},
]);

await expect(
create(
{
...theCase,
customFields: [
{
key: 'first_key',
type: CustomFieldTypes.TEXT,
value: null,
},
{
key: 'second_key',
type: CustomFieldTypes.TOGGLE,
value: null,
},
],
},
clientArgs,
casesClient
)
).rejects.toThrowErrorMatchingInlineSnapshot(
`"Failed to create case: Error: Missing required custom fields: first_key,second_key"`
);
});

it('throws error when the customFields array is too long', async () => {
await expect(
create(
Expand Down
34 changes: 33 additions & 1 deletion x-pack/plugins/cases/server/client/cases/validators.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ describe('validators', () => {
{
key: 'second_key',
type: CustomFieldTypes.TOGGLE as const,
value: null,
value: true,
},
];
expect(() =>
Expand Down Expand Up @@ -373,6 +373,38 @@ describe('validators', () => {
).toThrowErrorMatchingInlineSnapshot(`"Missing required custom fields: first_key"`);
});

it('throws if required custom fields have null value', () => {
const requestCustomFields: CaseCustomFields = [
{
key: 'second_key',
type: CustomFieldTypes.TOGGLE,
value: null,
},
];
const customFieldsConfiguration: CustomFieldsConfiguration = [
{
key: 'first_key',
type: CustomFieldTypes.TEXT,
label: 'foo',
required: true,
},
{
key: 'second_key',
type: CustomFieldTypes.TOGGLE,
label: 'foo',
required: true,
},
];
expect(() =>
validateRequiredCustomFields({
requestCustomFields,
customFieldsConfiguration,
})
).toThrowErrorMatchingInlineSnapshot(
`"Missing required custom fields: first_key,second_key"`
);
});

it('throws if configuration is missing and request has custom fields', () => {
const requestCustomFields: CaseCustomFields = [
{
Expand Down
14 changes: 14 additions & 0 deletions x-pack/plugins/cases/server/client/cases/validators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,12 +107,26 @@ export const validateRequiredCustomFields = ({
(customField) => customField.required
);

if (!requiredCustomFields.length) {
return;
}

const missingRequiredCustomFields = differenceWith(
requiredCustomFields,
requestCustomFields ?? [],
(requiredVal, requestedVal) => requiredVal.key === requestedVal.key
).map((e) => e.key);

requiredCustomFields.forEach((requiredField) => {
const found = requestCustomFields?.find(
(requestField) => requestField.key === requiredField.key
);

if (found && found.value === null) {
missingRequiredCustomFields.push(found.key);
}
});

if (missingRequiredCustomFields.length) {
throw Boom.badRequest(`Missing required custom fields: ${missingRequiredCustomFields}`);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1085,6 +1085,54 @@ export default ({ getService }: FtrProviderContext): void => {
});
});

it('400s when trying to patch a case with a required custom field with null value', async () => {
await createConfiguration(
supertest,
getConfigurationRequest({
overrides: {
customFields: [
{
key: 'test_custom_field',
label: 'text',
type: CustomFieldTypes.TEXT,
required: true,
},
],
},
})
);

const postedCase = await createCase(supertest, {
...postCaseReq,
customFields: [
{
key: 'test_custom_field',
type: CustomFieldTypes.TEXT,
value: ['hello'],
},
],
});

await updateCase({
supertest,
params: {
cases: [
{
id: postedCase.id,
version: postedCase.version,
customFields: [
{
key: 'test_custom_field',
type: CustomFieldTypes.TEXT,
value: null,
},
],
},
],
},
expectedHttpCode: 400,
});
});
it('400s when trying to patch a case with a custom field with the wrong type', async () => {
await createConfiguration(
supertest,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -482,14 +482,14 @@ export default ({ getService }: FtrProviderContext): void => {
);
});

it('400s when trying to create case with a required custom field', async () => {
it('400s when creating a case with a missing required custom field', async () => {
await createConfiguration(
supertest,
getConfigurationRequest({
overrides: {
customFields: [
{
key: 'test_custom_field',
key: 'text_custom_field',
label: 'text',
type: CustomFieldTypes.TEXT,
required: false,
Expand All @@ -509,9 +509,40 @@ export default ({ getService }: FtrProviderContext): void => {
getPostCaseRequest({
customFields: [
{
key: 'test_custom_field',
type: CustomFieldTypes.TOGGLE,
value: true,
key: 'text_custom_field',
type: CustomFieldTypes.TEXT,
value: ['a'],
},
],
}),
400
);
});

it('400s when trying to create case with a required custom field as null', async () => {
await createConfiguration(
supertest,
getConfigurationRequest({
overrides: {
customFields: [
{
key: 'text_custom_field',
label: 'text',
type: CustomFieldTypes.TEXT,
required: true,
},
],
},
})
);
await createCase(
supertest,
getPostCaseRequest({
customFields: [
{
key: 'text_custom_field',
type: CustomFieldTypes.TEXT,
value: null,
},
],
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,7 @@ export default function ({ getService }: FtrProviderContext) {
});
};

// Failing: See https://github.com/elastic/kibana/issues/165868
describe.skip('Field preview', function () {
describe('Field preview', function () {
before(async () => await createIndex());
after(async () => await deleteIndex());

Expand Down Expand Up @@ -149,10 +148,7 @@ export default function ({ getService }: FtrProviderContext) {
// As ES does not return error codes we will add a test to make sure its error message string
// does not change overtime as we rely on it to extract our own error code.
// If this test fail we'll need to update the "getErrorCodeFromErrorReason()" handler
// TODO: `response.error?.caused_by?.reason` returns
// `class_cast_exception: Cannot cast from [int] to [java.lang.String].`
// in Serverless, which causes `getErrorCodeFromErrorReason` to fail
it.skip('should detect a script casting error', async () => {
it('should detect a script casting error', async () => {
const { body: response } = await supertest
.post(FIELD_PREVIEW_PATH)
.set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION)
Expand Down

0 comments on commit ca6b503

Please sign in to comment.