Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ResponseOps][Cases] Introduce number custom field type #195245

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
ba12d5d
change domain schema with new number custom field type
guskovaue Oct 3, 2024
186ef5f
change api schema with new number custom field type
guskovaue Oct 7, 2024
3e08932
Merge branch 'main' into MX-187208-introduce-number-custom-field-type
guskovaue Oct 7, 2024
f11a492
add number type to integration tests
guskovaue Oct 7, 2024
3d3a060
Merge branch 'main' into MX-187208-introduce-number-custom-field-type
guskovaue Oct 7, 2024
cf72145
Merge branch 'MX-187208-introduce-number-custom-field-type' of github…
guskovaue Oct 7, 2024
c190325
fix types errors
guskovaue Oct 7, 2024
0e5f958
fix types and unit test
guskovaue Oct 8, 2024
6bf831e
fix
guskovaue Oct 8, 2024
b750c6b
Merge branch 'main' into MX-187208-introduce-number-custom-field-type
guskovaue Oct 8, 2024
3ae99be
changes after code review
guskovaue Oct 9, 2024
e41b8f8
fix not accepting 0 issue
guskovaue Oct 11, 2024
49e4b3a
UI
guskovaue Oct 15, 2024
d526b39
change from number to int
guskovaue Oct 16, 2024
51ebf98
add func tests
guskovaue Oct 16, 2024
2ebd770
return number type in domain folder for FE
guskovaue Oct 16, 2024
154d209
Merge branch 'main' into MX-187208-introduce-number-custom-field-type
guskovaue Oct 16, 2024
9952d50
partly fix unit tests
guskovaue Oct 17, 2024
b5e2508
Merge branch 'MX-187208-introduce-number-custom-field-type' of github…
guskovaue Oct 17, 2024
60a16ca
write new tests and fix old ones
guskovaue Oct 18, 2024
92fa604
add safe integer validation
guskovaue Oct 18, 2024
fda0e4d
Merge branch 'main' into MX-187208-introduce-number-custom-field-type
guskovaue Oct 18, 2024
a720478
fix wrong changed file
guskovaue Oct 18, 2024
00c94e5
delete unrelated tests
guskovaue Oct 18, 2024
709838e
fron ineger to safe integer
guskovaue Oct 18, 2024
b13c1e8
Merge branch 'main' into MX-187208-introduce-number-custom-field-type
guskovaue Oct 21, 2024
2c0b762
fix some nit after code review
guskovaue Oct 24, 2024
3d9c737
changes after code review
guskovaue Oct 29, 2024
fb7777d
Merge branch 'main' into MX-187208-introduce-number-custom-field-type
guskovaue Oct 29, 2024
acfcd16
fix tests after changes
guskovaue Oct 29, 2024
3215fbe
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Oct 29, 2024
be65372
Merge branch 'main' into MX-187208-introduce-number-custom-field-type
guskovaue Oct 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions x-pack/plugins/cases/common/schema/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
limitedStringSchema,
NonEmptyString,
paginationSchema,
limitedNumberAsIntegerSchema,
} from '.';
import { MAX_DOCS_PER_PAGE } from '../constants';

Expand Down Expand Up @@ -319,4 +320,69 @@ describe('schema', () => {
`);
});
});

describe('limitedNumberAsIntegerSchema', () => {
it('works correctly the number is safe integer', () => {
expect(PathReporter.report(limitedNumberAsIntegerSchema({ fieldName: 'foo' }).decode(1)))
.toMatchInlineSnapshot(`
Array [
"No errors!",
]
`);
});

it('fails when given a number that is lower than the minimum', () => {
expect(
PathReporter.report(
limitedNumberAsIntegerSchema({ fieldName: 'foo' }).decode(Number.MIN_SAFE_INTEGER - 1)
)
).toMatchInlineSnapshot(`
Array [
"The foo field should be an integer between -(2^53 - 1) and 2^53 - 1, inclusive.",
]
`);
});

it('fails when given a number that is higher than the maximum', () => {
expect(
PathReporter.report(
limitedNumberAsIntegerSchema({ fieldName: 'foo' }).decode(Number.MAX_SAFE_INTEGER + 1)
)
).toMatchInlineSnapshot(`
Array [
"The foo field should be an integer between -(2^53 - 1) and 2^53 - 1, inclusive.",
]
`);
});

it('fails when given a null instead of a number', () => {
expect(PathReporter.report(limitedNumberAsIntegerSchema({ fieldName: 'foo' }).decode(null)))
.toMatchInlineSnapshot(`
Array [
"Invalid value null supplied to : LimitedNumberAsInteger",
]
`);
});

it('fails when given a string instead of a number', () => {
expect(
PathReporter.report(
limitedNumberAsIntegerSchema({ fieldName: 'foo' }).decode('some string')
)
).toMatchInlineSnapshot(`
Array [
"Invalid value \\"some string\\" supplied to : LimitedNumberAsInteger",
]
`);
});

it('fails when given a float number instead of an safe integer number', () => {
expect(PathReporter.report(limitedNumberAsIntegerSchema({ fieldName: 'foo' }).decode(1.2)))
.toMatchInlineSnapshot(`
Array [
"The foo field should be an integer between -(2^53 - 1) and 2^53 - 1, inclusive.",
]
`);
});
});
});
18 changes: 18 additions & 0 deletions x-pack/plugins/cases/common/schema/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,24 @@ export const limitedNumberSchema = ({ fieldName, min, max }: LimitedSchemaType)
rt.identity
);

export const limitedNumberAsIntegerSchema = ({ fieldName }: { fieldName: string }) =>
new rt.Type<number, number, unknown>(
'LimitedNumberAsInteger',
rt.number.is,
(input, context) =>
either.chain(rt.number.validate(input, context), (s) => {
if (!Number.isSafeInteger(s)) {
return rt.failure(
input,
context,
`The ${fieldName} field should be an integer between -(2^53 - 1) and 2^53 - 1, inclusive.`
);
}
return rt.success(s);
}),
rt.identity
);

export interface RegexStringSchemaType {
codec: rt.Type<string, string, unknown>;
pattern: string;
Expand Down
55 changes: 54 additions & 1 deletion x-pack/plugins/cases/common/types/api/case/v1.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,15 @@ const basicCase: Case = {
value: true,
},
{
key: 'second_custom_field_key',
key: 'third_custom_field_key',
type: CustomFieldTypes.TEXT,
value: 'www.example.com',
},
{
key: 'fourth_custom_field_key',
type: CustomFieldTypes.NUMBER,
value: 3,
},
],
};

Expand Down Expand Up @@ -149,6 +154,11 @@ describe('CasePostRequestRt', () => {
type: CustomFieldTypes.TOGGLE,
value: true,
},
{
key: 'third_custom_field_key',
type: CustomFieldTypes.NUMBER,
value: 3,
},
],
};

Expand Down Expand Up @@ -322,6 +332,44 @@ describe('CasePostRequestRt', () => {
);
});

it(`throws an error when a number customFields is more than ${Number.MAX_SAFE_INTEGER}`, () => {
expect(
PathReporter.report(
CasePostRequestRt.decode({
...defaultRequest,
customFields: [
{
key: 'first_custom_field_key',
type: CustomFieldTypes.NUMBER,
value: Number.MAX_SAFE_INTEGER + 1,
},
],
})
)
).toContain(
`The value field should be an integer between -(2^53 - 1) and 2^53 - 1, inclusive.`
);
});

it(`throws an error when a number customFields is less than ${Number.MIN_SAFE_INTEGER}`, () => {
expect(
PathReporter.report(
CasePostRequestRt.decode({
...defaultRequest,
customFields: [
{
key: 'first_custom_field_key',
type: CustomFieldTypes.NUMBER,
value: Number.MIN_SAFE_INTEGER - 1,
},
],
})
)
).toContain(
`The value field should be an integer between -(2^53 - 1) and 2^53 - 1, inclusive.`
);
});

it('throws an error when a text customField is an empty string', () => {
expect(
PathReporter.report(
Expand Down Expand Up @@ -665,6 +713,11 @@ describe('CasePatchRequestRt', () => {
type: 'toggle',
value: true,
},
{
key: 'third_custom_field_key',
type: 'number',
value: 123,
},
],
};

Expand Down
23 changes: 20 additions & 3 deletions x-pack/plugins/cases/common/types/api/case/v1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@ import {
NonEmptyString,
paginationSchema,
} from '../../../schema';
import { CaseCustomFieldToggleRt, CustomFieldTextTypeRt } from '../../domain';
import {
CaseCustomFieldToggleRt,
CustomFieldTextTypeRt,
CustomFieldNumberTypeRt,
} from '../../domain';
import {
CaseRt,
CaseSettingsRt,
Expand All @@ -41,15 +45,28 @@ import {
import { CaseConnectorRt } from '../../domain/connector/v1';
import { CaseUserProfileRt, UserRt } from '../../domain/user/v1';
import { CasesStatusResponseRt } from '../stats/v1';
import { CaseCustomFieldTextWithValidationValueRt } from '../custom_field/v1';
import {
CaseCustomFieldTextWithValidationValueRt,
CaseCustomFieldNumberWithValidationValueRt,
} from '../custom_field/v1';

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

const CustomFieldRt = rt.union([CaseCustomFieldTextWithValidationRt, CaseCustomFieldToggleRt]);
const CaseCustomFieldNumberWithValidationRt = rt.strict({
key: rt.string,
type: CustomFieldNumberTypeRt,
value: rt.union([CaseCustomFieldNumberWithValidationValueRt({ fieldName: 'value' }), rt.null]),
});

const CustomFieldRt = rt.union([
CaseCustomFieldTextWithValidationRt,
CaseCustomFieldToggleRt,
CaseCustomFieldNumberWithValidationRt,
]);

export const CaseRequestCustomFieldsRt = limitedArraySchema({
codec: CustomFieldRt,
Expand Down
94 changes: 94 additions & 0 deletions x-pack/plugins/cases/common/types/api/configure/v1.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import {
CustomFieldConfigurationWithoutTypeRt,
TextCustomFieldConfigurationRt,
ToggleCustomFieldConfigurationRt,
NumberCustomFieldConfigurationRt,
TemplateConfigurationRt,
} from './v1';

Expand Down Expand Up @@ -79,6 +80,12 @@ describe('configure', () => {
type: CustomFieldTypes.TOGGLE,
required: false,
},
{
key: 'number_custom_field',
label: 'Number custom field',
type: CustomFieldTypes.NUMBER,
required: false,
},
],
};
const query = ConfigurationRequestRt.decode(request);
Expand Down Expand Up @@ -512,6 +519,93 @@ describe('configure', () => {
});
});

describe('NumberCustomFieldConfigurationRt', () => {
const defaultRequest = {
key: 'my_number_custom_field',
label: 'Number Custom Field',
type: CustomFieldTypes.NUMBER,
required: true,
};

it('has expected attributes in request', () => {
const query = NumberCustomFieldConfigurationRt.decode(defaultRequest);

expect(query).toStrictEqual({
_tag: 'Right',
right: { ...defaultRequest },
});
});

it('has expected attributes in request with defaultValue', () => {
const query = NumberCustomFieldConfigurationRt.decode({
...defaultRequest,
defaultValue: 1,
});

expect(query).toStrictEqual({
_tag: 'Right',
right: { ...defaultRequest, defaultValue: 1 },
});
});

it('removes foo:bar attributes from request', () => {
const query = NumberCustomFieldConfigurationRt.decode({ ...defaultRequest, foo: 'bar' });

expect(query).toStrictEqual({
_tag: 'Right',
right: { ...defaultRequest },
});
});

it('defaultValue fails if the type is string', () => {
expect(
PathReporter.report(
NumberCustomFieldConfigurationRt.decode({
...defaultRequest,
defaultValue: 'string',
})
)[0]
).toContain('Invalid value "string" supplied');
});

it('defaultValue fails if the type is boolean', () => {
expect(
PathReporter.report(
NumberCustomFieldConfigurationRt.decode({
...defaultRequest,
defaultValue: false,
})
)[0]
).toContain('Invalid value false supplied');
});

it(`throws an error if the default value is more than ${Number.MAX_SAFE_INTEGER}`, () => {
expect(
PathReporter.report(
NumberCustomFieldConfigurationRt.decode({
...defaultRequest,
defaultValue: Number.MAX_SAFE_INTEGER + 1,
})
)[0]
).toContain(
'The defaultValue field should be an integer between -(2^53 - 1) and 2^53 - 1, inclusive.'
);
});

it(`throws an error if the default value is less than ${Number.MIN_SAFE_INTEGER}`, () => {
expect(
PathReporter.report(
NumberCustomFieldConfigurationRt.decode({
...defaultRequest,
defaultValue: Number.MIN_SAFE_INTEGER - 1,
})
)[0]
).toContain(
'The defaultValue field should be an integer between -(2^53 - 1) and 2^53 - 1, inclusive.'
);
});
});

describe('TemplateConfigurationRt', () => {
const defaultRequest = {
key: 'template_key_1',
Expand Down
30 changes: 27 additions & 3 deletions x-pack/plugins/cases/common/types/api/configure/v1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,19 @@ import {
MAX_TEMPLATE_TAG_LENGTH,
} from '../../../constants';
import { limitedArraySchema, limitedStringSchema, regexStringRt } from '../../../schema';
import { CustomFieldTextTypeRt, CustomFieldToggleTypeRt } from '../../domain';
import {
CustomFieldTextTypeRt,
CustomFieldToggleTypeRt,
CustomFieldNumberTypeRt,
} from '../../domain';
import type { Configurations, Configuration } from '../../domain/configure/v1';
import { ConfigurationBasicWithoutOwnerRt, ClosureTypeRt } from '../../domain/configure/v1';
import { CaseConnectorRt } from '../../domain/connector/v1';
import { CaseBaseOptionalFieldsRequestRt } from '../case/v1';
import { CaseCustomFieldTextWithValidationValueRt } from '../custom_field/v1';
import {
CaseCustomFieldTextWithValidationValueRt,
CaseCustomFieldNumberWithValidationValueRt,
} from '../custom_field/v1';

export const CustomFieldConfigurationWithoutTypeRt = rt.strict({
/**
Expand Down Expand Up @@ -64,8 +71,25 @@ export const ToggleCustomFieldConfigurationRt = rt.intersection([
),
]);

export const NumberCustomFieldConfigurationRt = rt.intersection([
rt.strict({ type: CustomFieldNumberTypeRt }),
CustomFieldConfigurationWithoutTypeRt,
rt.exact(
rt.partial({
defaultValue: rt.union([
CaseCustomFieldNumberWithValidationValueRt({ fieldName: 'defaultValue' }),
rt.null,
]),
})
),
]);

export const CustomFieldsConfigurationRt = limitedArraySchema({
codec: rt.union([TextCustomFieldConfigurationRt, ToggleCustomFieldConfigurationRt]),
codec: rt.union([
TextCustomFieldConfigurationRt,
ToggleCustomFieldConfigurationRt,
NumberCustomFieldConfigurationRt,
]),
min: 0,
max: MAX_CUSTOM_FIELDS_PER_CASE,
fieldName: 'customFields',
Expand Down
Loading