Skip to content

Commit

Permalink
Extended rule execution logging to Event Log
Browse files Browse the repository at this point in the history
  • Loading branch information
banderror committed Jul 25, 2022
1 parent 8d88c78 commit 767a8c6
Show file tree
Hide file tree
Showing 239 changed files with 6,517 additions and 3,092 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import * as t from 'io-ts';
import { pipe } from 'fp-ts/lib/pipeable';
import { left } from 'fp-ts/lib/Either';
import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
import { defaultCsvArray } from '.';

describe('defaultCsvArray', () => {
describe('Creates a schema of an array that works in the following way:', () => {
type TestType = t.TypeOf<typeof TestType>;
const TestType = t.union(
[t.literal('foo'), t.literal('bar'), t.literal('42'), t.null, t.undefined],
'TestType'
);

const TestCsvArray = defaultCsvArray(TestType);

describe('Name of the schema', () => {
it('has a default value', () => {
const CsvArray = defaultCsvArray(TestType);
expect(CsvArray.name).toEqual('DefaultCsvArray<TestType>');
});

it('can be overriden', () => {
const CsvArray = defaultCsvArray(TestType, 'CustomName');
expect(CsvArray.name).toEqual('CustomName');
});
});

describe('Validation succeeds', () => {
describe('when input is a single valid string value', () => {
const cases = [{ input: 'foo' }, { input: 'bar' }, { input: '42' }];

cases.forEach(({ input }) => {
it(`${input}`, () => {
const decoded = TestCsvArray.decode(input);
const message = pipe(decoded, foldLeftRight);
const expectedOutput = [input]; // note that it's an array after decode

expect(getPaths(left(message.errors))).toEqual([]);
expect(message.schema).toEqual(expectedOutput);
});
});
});

describe('when input is an array of valid string values', () => {
const cases = [
{ input: ['foo'] },
{ input: ['foo', 'bar'] },
{ input: ['foo', 'bar', '42'] },
];

cases.forEach(({ input }) => {
it(`${input}`, () => {
const decoded = TestCsvArray.decode(input);
const message = pipe(decoded, foldLeftRight);
const expectedOutput = input;

expect(getPaths(left(message.errors))).toEqual([]);
expect(message.schema).toEqual(expectedOutput);
});
});
});

describe('when input is a string which is a comma-separated array of valid values', () => {
const cases = [
{
input: 'foo,bar',
expectedOutput: ['foo', 'bar'],
},
{
input: 'foo,bar,42',
expectedOutput: ['foo', 'bar', '42'],
},
];

cases.forEach(({ input, expectedOutput }) => {
it(`${input}`, () => {
const decoded = TestCsvArray.decode(input);
const message = pipe(decoded, foldLeftRight);

expect(getPaths(left(message.errors))).toEqual([]);
expect(message.schema).toEqual(expectedOutput);
});
});
});
});

describe('Validation fails', () => {
describe('when input is a single invalid value', () => {
const cases = [
{
input: 'val',
expectedErrors: ['Invalid value "val" supplied to "DefaultCsvArray<TestType>"'],
},
{
input: '5',
expectedErrors: ['Invalid value "5" supplied to "DefaultCsvArray<TestType>"'],
},
{
input: 5,
expectedErrors: ['Invalid value "5" supplied to "DefaultCsvArray<TestType>"'],
},
{
input: {},
expectedErrors: ['Invalid value "{}" supplied to "DefaultCsvArray<TestType>"'],
},
];

cases.forEach(({ input, expectedErrors }) => {
it(`${input}`, () => {
const decoded = TestCsvArray.decode(input);
const message = pipe(decoded, foldLeftRight);

expect(getPaths(left(message.errors))).toEqual(expectedErrors);
expect(message.schema).toEqual({});
});
});
});

describe('when input is an array of invalid values', () => {
const cases = [
{
input: ['value 1', 5],
expectedErrors: [
'Invalid value "value 1" supplied to "DefaultCsvArray<TestType>"',
'Invalid value "5" supplied to "DefaultCsvArray<TestType>"',
],
},
{
input: ['value 1', 'foo'],
expectedErrors: ['Invalid value "value 1" supplied to "DefaultCsvArray<TestType>"'],
},
{
input: ['', 5, {}],
expectedErrors: [
'Invalid value "" supplied to "DefaultCsvArray<TestType>"',
'Invalid value "5" supplied to "DefaultCsvArray<TestType>"',
'Invalid value "{}" supplied to "DefaultCsvArray<TestType>"',
],
},
];

cases.forEach(({ input, expectedErrors }) => {
it(`${input}`, () => {
const decoded = TestCsvArray.decode(input);
const message = pipe(decoded, foldLeftRight);

expect(getPaths(left(message.errors))).toEqual(expectedErrors);
expect(message.schema).toEqual({});
});
});
});

describe('when input is a string which is a comma-separated array of invalid values', () => {
const cases = [
{
input: 'value 1,5',
expectedErrors: [
'Invalid value "value 1" supplied to "DefaultCsvArray<TestType>"',
'Invalid value "5" supplied to "DefaultCsvArray<TestType>"',
],
},
{
input: 'value 1,foo',
expectedErrors: ['Invalid value "value 1" supplied to "DefaultCsvArray<TestType>"'],
},
{
input: ',5,{}',
expectedErrors: [
'Invalid value "" supplied to "DefaultCsvArray<TestType>"',
'Invalid value "5" supplied to "DefaultCsvArray<TestType>"',
'Invalid value "{}" supplied to "DefaultCsvArray<TestType>"',
],
},
];

cases.forEach(({ input, expectedErrors }) => {
it(`${input}`, () => {
const decoded = TestCsvArray.decode(input);
const message = pipe(decoded, foldLeftRight);

expect(getPaths(left(message.errors))).toEqual(expectedErrors);
expect(message.schema).toEqual({});
});
});
});
});

describe('Validation returns default value (an empty array)', () => {
describe('when input is', () => {
const cases = [{ input: null }, { input: undefined }, { input: '' }, { input: [] }];

cases.forEach(({ input }) => {
it(`${input}`, () => {
const decoded = TestCsvArray.decode(input);
const message = pipe(decoded, foldLeftRight);
const expectedOutput: string[] = [];

expect(getPaths(left(message.errors))).toEqual([]);
expect(message.schema).toEqual(expectedOutput);
});
});
});
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import * as t from 'io-ts';
import { Either } from 'fp-ts/lib/Either';

/**
* Creates a schema of an array that works in the following way:
* - If input is a CSV string, it will be parsed to an array which will be validated.
* - If input is an array, each item is validated to match `itemSchema`.
* - If input is a single string, it is validated to match `itemSchema`.
* - If input is not specified, the result will be set to [] (empty array):
* - null, undefined, empty string, empty array
*
* In all cases when an input is valid, the resulting decoded value will be an array,
* either an empty one or containing valid items.
*
* @param itemSchema Schema of the array's items.
* @param name (Optional) Name of the resulting schema.
*/
export const defaultCsvArray = <TItem>(
itemSchema: t.Type<TItem>,
name?: string
): t.Type<TItem[]> => {
return new t.Type<TItem[]>(
name ?? `DefaultCsvArray<${itemSchema.name}>`,
t.array(itemSchema).is,
(input, context): Either<t.Errors, TItem[]> => {
if (input == null) {
return t.success([]);
} else if (typeof input === 'string') {
if (input === '') {
return t.success([]);
} else {
return t.array(itemSchema).validate(input.split(','), context);
}
} else {
return t.array(itemSchema).validate(input, context);
}
},
t.identity
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import * as t from 'io-ts';
import { pipe } from 'fp-ts/lib/pipeable';
import { left } from 'fp-ts/lib/Either';
import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils';
import { defaultValue } from '.';

describe('defaultValue', () => {
describe('Creates a schema that sets a default value if the input value is not specified', () => {
type TestType = t.TypeOf<typeof TestType>;
const TestType = t.union([t.string, t.number, t.null, t.undefined], 'TestType');

const DefaultValue = defaultValue(TestType, 42);

describe('Name of the schema', () => {
it('has a default value', () => {
expect(defaultValue(TestType, 42).name).toEqual('DefaultValue<TestType>');
});

it('can be overriden', () => {
expect(defaultValue(TestType, 42, 'CustomName').name).toEqual('CustomName');
});
});

describe('Validation succeeds', () => {
describe('when input is a valid value', () => {
const cases = [
{ input: 'foo' },
{ input: '42' },
{ input: 42 },
// including all "falsey" values which are not null or undefined
{ input: '' },
{ input: 0 },
];

cases.forEach(({ input }) => {
it(`${input}`, () => {
const decoded = DefaultValue.decode(input);
const message = pipe(decoded, foldLeftRight);
const expectedOutput = input;

expect(getPaths(left(message.errors))).toEqual([]);
expect(message.schema).toEqual(expectedOutput);
});
});
});
});

describe('Validation fails', () => {
describe('when input is an invalid value', () => {
const cases = [
{
input: {},
expectedErrors: ['Invalid value "{}" supplied to "DefaultValue<TestType>"'],
},
{
input: { foo: 42 },
expectedErrors: ['Invalid value "{"foo":42}" supplied to "DefaultValue<TestType>"'],
},
{
input: [],
expectedErrors: ['Invalid value "[]" supplied to "DefaultValue<TestType>"'],
},
{
input: ['foo', 42],
expectedErrors: ['Invalid value "["foo",42]" supplied to "DefaultValue<TestType>"'],
},
];

cases.forEach(({ input, expectedErrors }) => {
it(`${input}`, () => {
const decoded = DefaultValue.decode(input);
const message = pipe(decoded, foldLeftRight);

expect(getPaths(left(message.errors))).toEqual(expectedErrors);
expect(message.schema).toEqual({});
});
});
});
});

describe('Validation returns specified default value', () => {
describe('when input is', () => {
const cases = [{ input: null }, { input: undefined }];

cases.forEach(({ input }) => {
it(`${input}`, () => {
const decoded = DefaultValue.decode(input);
const message = pipe(decoded, foldLeftRight);
const expectedOutput = 42;

expect(getPaths(left(message.errors))).toEqual([]);
expect(message.schema).toEqual(expectedOutput);
});
});
});
});
});
});
Loading

0 comments on commit 767a8c6

Please sign in to comment.