mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[Cases] Add the ability to filter cases by date in the find and status endpoints (#128652)
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
e22deff7a6
commit
4246f3e631
15 changed files with 588 additions and 18 deletions
|
@ -34,6 +34,9 @@ Defaults to `OR`.
|
|||
`fields`::
|
||||
(Optional, array of strings) The fields in the entity to return in the response.
|
||||
|
||||
`from`::
|
||||
(Optional, string) Returns only cases that were created after a specific date. The date must be specified as a <<kuery-query,KQL>> data range or date match expression. preview:[]
|
||||
|
||||
`owner`::
|
||||
(Optional, string or array of strings) A filter to limit the retrieved cases to
|
||||
a specific set of applications. Valid values are: `cases`, `observability`,
|
||||
|
@ -78,6 +81,9 @@ Defaults to `desc`.
|
|||
`tags`::
|
||||
(Optional, string or array of strings) Filters the returned cases by tags.
|
||||
|
||||
`to`::
|
||||
(Optional, string) Returns only cases that were created before a specific date. The date must be specified as a <<kuery-query,KQL>> data range or date match expression. preview:[]
|
||||
|
||||
=== Response code
|
||||
|
||||
`200`::
|
||||
|
|
|
@ -154,6 +154,10 @@ export const CasesFindRequestRt = rt.partial({
|
|||
* The fields in the entity to return in the response
|
||||
*/
|
||||
fields: rt.union([rt.array(rt.string), rt.string]),
|
||||
/**
|
||||
* A KQL date. If used all cases created after (gte) the from date will be returned
|
||||
*/
|
||||
from: rt.string,
|
||||
/**
|
||||
* The page of objects to return
|
||||
*/
|
||||
|
@ -180,11 +184,17 @@ export const CasesFindRequestRt = rt.partial({
|
|||
* The order to sort by
|
||||
*/
|
||||
sortOrder: rt.union([rt.literal('desc'), rt.literal('asc')]),
|
||||
|
||||
/**
|
||||
* A KQL date. If used all cases created before (lte) the to date will be returned.
|
||||
*/
|
||||
to: rt.string,
|
||||
/**
|
||||
* The owner(s) to filter by. The user making the request must have privileges to retrieve cases of that
|
||||
* ownership or they will be ignored. If no owner is included, then all ownership types will be included in the response
|
||||
* that the user has access to.
|
||||
*/
|
||||
|
||||
owner: rt.union([rt.array(rt.string), rt.string]),
|
||||
});
|
||||
|
||||
|
|
|
@ -28,6 +28,14 @@ export const CasesStatusResponseRt = rt.type({
|
|||
});
|
||||
|
||||
export const CasesStatusRequestRt = rt.partial({
|
||||
/**
|
||||
* A KQL date. If used all cases created after (gte) the from date will be returned
|
||||
*/
|
||||
from: rt.string,
|
||||
/**
|
||||
* A KQL date. If used all cases created before (lte) the to date will be returned.
|
||||
*/
|
||||
to: rt.string,
|
||||
/**
|
||||
* The owner of the cases to retrieve the status stats from. If no owner is provided the stats for all cases
|
||||
* that the user has access to will be returned.
|
||||
|
|
|
@ -51,6 +51,7 @@ export const SAVED_OBJECT_TYPES = [
|
|||
*/
|
||||
|
||||
export const CASES_URL = '/api/cases' as const;
|
||||
export const CASE_FIND_URL = `${CASES_URL}/_find` as const;
|
||||
export const CASE_DETAILS_URL = `${CASES_URL}/{case_id}` as const;
|
||||
export const CASE_CONFIGURE_URL = `${CASES_URL}/configure` as const;
|
||||
export const CASE_CONFIGURE_DETAILS_URL = `${CASES_URL}/configure/{configuration_id}` as const;
|
||||
|
|
|
@ -7,16 +7,16 @@
|
|||
|
||||
import { httpServiceMock } from '../../../../../../src/core/public/mocks';
|
||||
import { createClientAPI } from '.';
|
||||
import { allCases, casesStatus } from '../../containers/mock';
|
||||
|
||||
describe('createClientAPI', () => {
|
||||
const http = httpServiceMock.createStartContract({ basePath: '' });
|
||||
const api = createClientAPI({ http });
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('getRelatedCases', () => {
|
||||
const http = httpServiceMock.createStartContract({ basePath: '' });
|
||||
const api = createClientAPI({ http });
|
||||
const res = [
|
||||
{
|
||||
id: 'test-id',
|
||||
|
@ -43,4 +43,40 @@ describe('createClientAPI', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('cases', () => {
|
||||
describe('find', () => {
|
||||
const http = httpServiceMock.createStartContract({ basePath: '' });
|
||||
const api = createClientAPI({ http });
|
||||
http.get.mockResolvedValue(allCases);
|
||||
|
||||
it('should return the correct response', async () => {
|
||||
expect(await api.cases.find({ from: 'now-1d' })).toEqual(allCases);
|
||||
});
|
||||
|
||||
it('should have been called with the correct path', async () => {
|
||||
await api.cases.find({ perPage: 10 });
|
||||
expect(http.get).toHaveBeenCalledWith('/api/cases/_find', {
|
||||
query: { perPage: 10 },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getAllCasesMetrics', () => {
|
||||
const http = httpServiceMock.createStartContract({ basePath: '' });
|
||||
const api = createClientAPI({ http });
|
||||
http.get.mockResolvedValue(casesStatus);
|
||||
|
||||
it('should return the correct response', async () => {
|
||||
expect(await api.cases.getAllCasesMetrics({ from: 'now-1d' })).toEqual(casesStatus);
|
||||
});
|
||||
|
||||
it('should have been called with the correct path', async () => {
|
||||
await api.cases.getAllCasesMetrics({ from: 'now-1d' });
|
||||
expect(http.get).toHaveBeenCalledWith('/api/cases/status', {
|
||||
query: { from: 'now-1d' },
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -6,7 +6,16 @@
|
|||
*/
|
||||
|
||||
import { HttpStart } from 'kibana/public';
|
||||
import { CasesByAlertId, CasesByAlertIDRequest, getCasesFromAlertsUrl } from '../../../common/api';
|
||||
import {
|
||||
CasesByAlertId,
|
||||
CasesByAlertIDRequest,
|
||||
CasesFindRequest,
|
||||
getCasesFromAlertsUrl,
|
||||
CasesResponse,
|
||||
CasesStatusRequest,
|
||||
CasesStatusResponse,
|
||||
} from '../../../common/api';
|
||||
import { CASE_FIND_URL, CASE_STATUS_URL } from '../../../common/constants';
|
||||
import { CasesUiStart } from '../../types';
|
||||
|
||||
export const createClientAPI = ({ http }: { http: HttpStart }): CasesUiStart['api'] => {
|
||||
|
@ -16,5 +25,11 @@ export const createClientAPI = ({ http }: { http: HttpStart }): CasesUiStart['ap
|
|||
query: CasesByAlertIDRequest
|
||||
): Promise<CasesByAlertId> =>
|
||||
http.get<CasesByAlertId>(getCasesFromAlertsUrl(alertId), { query }),
|
||||
cases: {
|
||||
find: (query: CasesFindRequest): Promise<CasesResponse> =>
|
||||
http.get<CasesResponse>(CASE_FIND_URL, { query }),
|
||||
getAllCasesMetrics: (query: CasesStatusRequest): Promise<CasesStatusResponse> =>
|
||||
http.get<CasesStatusResponse>(CASE_STATUS_URL, { query }),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
|
|
@ -10,6 +10,7 @@ import { CasesUiStart } from './types';
|
|||
|
||||
const apiMock: jest.Mocked<CasesUiStart['api']> = {
|
||||
getRelatedCases: jest.fn(),
|
||||
cases: { find: jest.fn(), getAllCasesMetrics: jest.fn() },
|
||||
};
|
||||
|
||||
const uiMock: jest.Mocked<CasesUiStart['ui']> = {
|
||||
|
|
|
@ -23,6 +23,10 @@ import type { TriggersAndActionsUIPublicPluginStart as TriggersActionsStart } fr
|
|||
import {
|
||||
CasesByAlertId,
|
||||
CasesByAlertIDRequest,
|
||||
CasesFindRequest,
|
||||
CasesResponse,
|
||||
CasesStatusRequest,
|
||||
CasesStatusResponse,
|
||||
CommentRequestAlertType,
|
||||
CommentRequestUserType,
|
||||
} from '../common/api';
|
||||
|
@ -74,6 +78,10 @@ export interface RenderAppProps {
|
|||
export interface CasesUiStart {
|
||||
api: {
|
||||
getRelatedCases: (alertId: string, query: CasesByAlertIDRequest) => Promise<CasesByAlertId>;
|
||||
cases: {
|
||||
find: (query: CasesFindRequest) => Promise<CasesResponse>;
|
||||
getAllCasesMetrics: (query: CasesStatusRequest) => Promise<CasesStatusResponse>;
|
||||
};
|
||||
};
|
||||
ui: {
|
||||
/**
|
||||
|
|
|
@ -54,6 +54,8 @@ export const find = async (
|
|||
sortByField: queryParams.sortField,
|
||||
status: queryParams.status,
|
||||
owner: queryParams.owner,
|
||||
from: queryParams.from,
|
||||
to: queryParams.to,
|
||||
};
|
||||
|
||||
const statusStatsOptions = constructQueryOptions({
|
||||
|
|
|
@ -41,6 +41,8 @@ export async function getStatusTotalsByType(
|
|||
|
||||
const options = constructQueryOptions({
|
||||
owner: queryParams.owner,
|
||||
from: queryParams.from,
|
||||
to: queryParams.to,
|
||||
authorizationFilter,
|
||||
});
|
||||
|
||||
|
|
|
@ -8,31 +8,32 @@
|
|||
import { CaseConnector, ConnectorTypes } from '../../common/api';
|
||||
import { newCase } from '../routes/api/__mocks__/request_responses';
|
||||
import { transformNewCase } from '../common/utils';
|
||||
import { sortToSnake } from './utils';
|
||||
import { buildRangeFilter, sortToSnake } from './utils';
|
||||
import { toElasticsearchQuery } from '@kbn/es-query';
|
||||
|
||||
describe('utils', () => {
|
||||
describe('sortToSnake', () => {
|
||||
it('it transforms status correctly', () => {
|
||||
it('transforms status correctly', () => {
|
||||
expect(sortToSnake('status')).toBe('status');
|
||||
});
|
||||
|
||||
it('it transforms createdAt correctly', () => {
|
||||
it('transforms createdAt correctly', () => {
|
||||
expect(sortToSnake('createdAt')).toBe('created_at');
|
||||
});
|
||||
|
||||
it('it transforms created_at correctly', () => {
|
||||
it('transforms created_at correctly', () => {
|
||||
expect(sortToSnake('created_at')).toBe('created_at');
|
||||
});
|
||||
|
||||
it('it transforms closedAt correctly', () => {
|
||||
it('transforms closedAt correctly', () => {
|
||||
expect(sortToSnake('closedAt')).toBe('closed_at');
|
||||
});
|
||||
|
||||
it('it transforms closed_at correctly', () => {
|
||||
it('transforms closed_at correctly', () => {
|
||||
expect(sortToSnake('closed_at')).toBe('closed_at');
|
||||
});
|
||||
|
||||
it('it transforms default correctly', () => {
|
||||
it('transforms default correctly', () => {
|
||||
expect(sortToSnake('not-exist')).toBe('created_at');
|
||||
});
|
||||
});
|
||||
|
@ -103,4 +104,154 @@ describe('utils', () => {
|
|||
`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('buildRangeFilter', () => {
|
||||
it('returns undefined if both the from and or are undefined', () => {
|
||||
const node = buildRangeFilter({});
|
||||
expect(node).toBeFalsy();
|
||||
});
|
||||
|
||||
it('returns undefined if both the from and or are null', () => {
|
||||
// @ts-expect-error
|
||||
const node = buildRangeFilter({ from: null, to: null });
|
||||
expect(node).toBeFalsy();
|
||||
});
|
||||
|
||||
it('returns undefined if the from is malformed', () => {
|
||||
expect(() => buildRangeFilter({ from: '<' })).toThrowError(
|
||||
'Invalid "from" and/or "to" query parameters'
|
||||
);
|
||||
});
|
||||
|
||||
it('returns undefined if the to is malformed', () => {
|
||||
expect(() => buildRangeFilter({ to: '<' })).toThrowError(
|
||||
'Invalid "from" and/or "to" query parameters'
|
||||
);
|
||||
});
|
||||
|
||||
it('creates a range filter with only the from correctly', () => {
|
||||
const node = buildRangeFilter({ from: 'now-1M' });
|
||||
expect(toElasticsearchQuery(node!)).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"bool": Object {
|
||||
"minimum_should_match": 1,
|
||||
"should": Array [
|
||||
Object {
|
||||
"range": Object {
|
||||
"cases.attributes.created_at": Object {
|
||||
"gte": "now-1M",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('creates a range filter with only the to correctly', () => {
|
||||
const node = buildRangeFilter({ to: 'now' });
|
||||
expect(toElasticsearchQuery(node!)).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"bool": Object {
|
||||
"minimum_should_match": 1,
|
||||
"should": Array [
|
||||
Object {
|
||||
"range": Object {
|
||||
"cases.attributes.created_at": Object {
|
||||
"lte": "now",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('creates a range filter correctly', () => {
|
||||
const node = buildRangeFilter({ from: 'now-1M', to: 'now' });
|
||||
expect(toElasticsearchQuery(node!)).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"bool": Object {
|
||||
"filter": Array [
|
||||
Object {
|
||||
"bool": Object {
|
||||
"minimum_should_match": 1,
|
||||
"should": Array [
|
||||
Object {
|
||||
"range": Object {
|
||||
"cases.attributes.created_at": Object {
|
||||
"gte": "now-1M",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"bool": Object {
|
||||
"minimum_should_match": 1,
|
||||
"should": Array [
|
||||
Object {
|
||||
"range": Object {
|
||||
"cases.attributes.created_at": Object {
|
||||
"lte": "now",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('creates a range filter with different field and saved object type provided', () => {
|
||||
const node = buildRangeFilter({
|
||||
from: 'now-1M',
|
||||
to: 'now',
|
||||
field: 'test',
|
||||
savedObjectType: 'test-type',
|
||||
});
|
||||
expect(toElasticsearchQuery(node!)).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"bool": Object {
|
||||
"filter": Array [
|
||||
Object {
|
||||
"bool": Object {
|
||||
"minimum_should_match": 1,
|
||||
"should": Array [
|
||||
Object {
|
||||
"range": Object {
|
||||
"test-type.attributes.test": Object {
|
||||
"gte": "now-1M",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"bool": Object {
|
||||
"minimum_should_match": 1,
|
||||
"should": Array [
|
||||
Object {
|
||||
"range": Object {
|
||||
"test-type.attributes.test": Object {
|
||||
"lte": "now",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -183,6 +183,35 @@ export function stringToKueryNode(expression?: string): KueryNode | undefined {
|
|||
return fromKueryExpression(expression);
|
||||
}
|
||||
|
||||
export const buildRangeFilter = ({
|
||||
from,
|
||||
to,
|
||||
field = 'created_at',
|
||||
savedObjectType = CASE_SAVED_OBJECT,
|
||||
}: {
|
||||
from?: string;
|
||||
to?: string;
|
||||
field?: string;
|
||||
savedObjectType?: string;
|
||||
}): KueryNode | undefined => {
|
||||
if (from == null && to == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const fromKQL = from != null ? `${savedObjectType}.attributes.${field} >= ${from}` : undefined;
|
||||
const toKQL = to != null ? `${savedObjectType}.attributes.${field} <= ${to}` : undefined;
|
||||
|
||||
const rangeKQLQuery = `${fromKQL != null ? fromKQL : ''} ${
|
||||
fromKQL != null && toKQL != null ? 'and' : ''
|
||||
} ${toKQL != null ? toKQL : ''}`;
|
||||
|
||||
return stringToKueryNode(rangeKQLQuery);
|
||||
} catch (error) {
|
||||
throw badRequest('Invalid "from" and/or "to" query parameters');
|
||||
}
|
||||
};
|
||||
|
||||
export const constructQueryOptions = ({
|
||||
tags,
|
||||
reporters,
|
||||
|
@ -190,6 +219,8 @@ export const constructQueryOptions = ({
|
|||
sortByField,
|
||||
owner,
|
||||
authorizationFilter,
|
||||
from,
|
||||
to,
|
||||
}: {
|
||||
tags?: string | string[];
|
||||
reporters?: string | string[];
|
||||
|
@ -197,6 +228,8 @@ export const constructQueryOptions = ({
|
|||
sortByField?: string;
|
||||
owner?: string | string[];
|
||||
authorizationFilter?: KueryNode;
|
||||
from?: string;
|
||||
to?: string;
|
||||
}): SavedObjectFindOptionsKueryNode => {
|
||||
const kueryNodeExists = (filter: KueryNode | null | undefined): filter is KueryNode =>
|
||||
filter != null;
|
||||
|
@ -211,10 +244,15 @@ export const constructQueryOptions = ({
|
|||
const ownerFilter = buildFilter({ filters: owner ?? [], field: OWNER_FIELD, operator: 'or' });
|
||||
|
||||
const statusFilter = status != null ? addStatusFilter({ status }) : undefined;
|
||||
const rangeFilter = buildRangeFilter({ from, to });
|
||||
|
||||
const filters: KueryNode[] = [statusFilter, tagsFilter, reportersFilter, ownerFilter].filter(
|
||||
kueryNodeExists
|
||||
);
|
||||
const filters: KueryNode[] = [
|
||||
statusFilter,
|
||||
tagsFilter,
|
||||
reportersFilter,
|
||||
rangeFilter,
|
||||
ownerFilter,
|
||||
].filter(kueryNodeExists);
|
||||
|
||||
const caseFilters = filters.length > 1 ? nodeBuilder.and(filters) : filters[0];
|
||||
|
||||
|
|
|
@ -52,6 +52,7 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
const es = getService('es');
|
||||
const supertestWithoutAuth = getService('supertestWithoutAuth');
|
||||
const esArchiver = getService('esArchiver');
|
||||
const kibanaServer = getService('kibanaServer');
|
||||
|
||||
describe('find_cases', () => {
|
||||
describe('basic tests', () => {
|
||||
|
@ -478,6 +479,53 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('range queries', () => {
|
||||
before(async () => {
|
||||
await kibanaServer.importExport.load(
|
||||
'x-pack/test/functional/fixtures/kbn_archiver/cases/8.2.0/cases_various_dates.json'
|
||||
);
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await kibanaServer.importExport.unload(
|
||||
'x-pack/test/functional/fixtures/kbn_archiver/cases/8.2.0/cases_various_dates.json'
|
||||
);
|
||||
await deleteAllCaseItems(es);
|
||||
});
|
||||
|
||||
it('returns all cases without a range filter', async () => {
|
||||
const EXPECTED_CASES = 3;
|
||||
const cases = await findCases({ supertest });
|
||||
|
||||
expect(cases.total).to.be(EXPECTED_CASES);
|
||||
expect(cases.count_open_cases).to.be(EXPECTED_CASES);
|
||||
expect(cases.cases.length).to.be(EXPECTED_CASES);
|
||||
});
|
||||
|
||||
it('respects the range parameters', async () => {
|
||||
const queries = [
|
||||
{ expectedCases: 2, query: { from: '2022-03-16' } },
|
||||
{ expectedCases: 2, query: { to: '2022-03-21' } },
|
||||
{ expectedCases: 2, query: { from: '2022-03-15', to: '2022-03-21' } },
|
||||
];
|
||||
|
||||
for (const query of queries) {
|
||||
const cases = await findCases({
|
||||
supertest,
|
||||
query: query.query,
|
||||
});
|
||||
|
||||
expect(cases.total).to.be(query.expectedCases);
|
||||
expect(cases.count_open_cases).to.be(query.expectedCases);
|
||||
expect(cases.cases.length).to.be(query.expectedCases);
|
||||
}
|
||||
});
|
||||
|
||||
it('returns a bad request on malformed parameter', async () => {
|
||||
await findCases({ supertest, query: { from: '<' }, expectedHttpCode: 400 });
|
||||
});
|
||||
});
|
||||
|
||||
describe('rbac', () => {
|
||||
afterEach(async () => {
|
||||
await deleteAllCaseItems(es);
|
||||
|
@ -717,6 +765,40 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
// Only security solution cases are being returned
|
||||
ensureSavedObjectIsAuthorized(res.cases, 1, ['securitySolutionFixture']);
|
||||
});
|
||||
|
||||
describe('range queries', () => {
|
||||
before(async () => {
|
||||
await kibanaServer.importExport.load(
|
||||
'x-pack/test/functional/fixtures/kbn_archiver/cases/8.2.0/cases_various_dates.json',
|
||||
{ space: 'space1' }
|
||||
);
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await kibanaServer.importExport.unload(
|
||||
'x-pack/test/functional/fixtures/kbn_archiver/cases/8.2.0/cases_various_dates.json',
|
||||
{ space: 'space1' }
|
||||
);
|
||||
await deleteAllCaseItems(es);
|
||||
});
|
||||
|
||||
it('should respect the owner filter when using range queries', async () => {
|
||||
const res = await findCases({
|
||||
supertest: supertestWithoutAuth,
|
||||
query: {
|
||||
from: '2022-03-15',
|
||||
to: '2022-03-21',
|
||||
},
|
||||
auth: {
|
||||
user: secOnly,
|
||||
space: 'space1',
|
||||
},
|
||||
});
|
||||
|
||||
// Only security solution cases are being returned
|
||||
ensureSavedObjectIsAuthorized(res.cases, 1, ['securitySolutionFixture']);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
|
|
@ -34,12 +34,9 @@ import { assertWarningHeader } from '../../../../../common/lib/validation';
|
|||
export default ({ getService }: FtrProviderContext): void => {
|
||||
const supertest = getService('supertest');
|
||||
const es = getService('es');
|
||||
const kibanaServer = getService('kibanaServer');
|
||||
|
||||
describe('get_status', () => {
|
||||
afterEach(async () => {
|
||||
await deleteAllCaseItems(es);
|
||||
});
|
||||
|
||||
it('should return case statuses', async () => {
|
||||
const [, inProgressCase, postedCase] = await Promise.all([
|
||||
createCase(supertest, postCaseReq),
|
||||
|
@ -74,7 +71,58 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('range queries', () => {
|
||||
before(async () => {
|
||||
await deleteAllCaseItems(es);
|
||||
await kibanaServer.importExport.load(
|
||||
'x-pack/test/functional/fixtures/kbn_archiver/cases/8.2.0/cases_various_dates.json'
|
||||
);
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await kibanaServer.importExport.unload(
|
||||
'x-pack/test/functional/fixtures/kbn_archiver/cases/8.2.0/cases_various_dates.json'
|
||||
);
|
||||
await deleteAllCaseItems(es);
|
||||
});
|
||||
|
||||
it('returns all cases without a range filter', async () => {
|
||||
const statuses = await getAllCasesStatuses({ supertest });
|
||||
|
||||
expect(statuses).to.eql({
|
||||
count_open_cases: 3,
|
||||
count_closed_cases: 0,
|
||||
count_in_progress_cases: 0,
|
||||
});
|
||||
});
|
||||
|
||||
it('respects the range parameters', async () => {
|
||||
const queries = [
|
||||
{ expectedCases: 2, query: { from: '2022-03-16' } },
|
||||
{ expectedCases: 2, query: { to: '2022-03-21' } },
|
||||
{ expectedCases: 2, query: { from: '2022-03-15', to: '2022-03-21' } },
|
||||
];
|
||||
|
||||
for (const query of queries) {
|
||||
const statuses = await getAllCasesStatuses({ supertest, query: query.query });
|
||||
expect(statuses).to.eql({
|
||||
count_open_cases: query.expectedCases,
|
||||
count_closed_cases: 0,
|
||||
count_in_progress_cases: 0,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it('returns a bad request on malformed parameter', async () => {
|
||||
await getAllCasesStatuses({ supertest, query: { from: '<' }, expectedHttpCode: 400 });
|
||||
});
|
||||
});
|
||||
|
||||
describe('rbac', () => {
|
||||
afterEach(async () => {
|
||||
await deleteAllCaseItems(es);
|
||||
});
|
||||
|
||||
const supertestWithoutAuth = getService('supertestWithoutAuth');
|
||||
|
||||
it('should return the correct status stats', async () => {
|
||||
|
@ -183,6 +231,43 @@ export default ({ getService }: FtrProviderContext): void => {
|
|||
});
|
||||
});
|
||||
}
|
||||
|
||||
describe('range queries', () => {
|
||||
before(async () => {
|
||||
await kibanaServer.importExport.load(
|
||||
'x-pack/test/functional/fixtures/kbn_archiver/cases/8.2.0/cases_various_dates.json',
|
||||
{ space: 'space1' }
|
||||
);
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await kibanaServer.importExport.unload(
|
||||
'x-pack/test/functional/fixtures/kbn_archiver/cases/8.2.0/cases_various_dates.json',
|
||||
{ space: 'space1' }
|
||||
);
|
||||
await deleteAllCaseItems(es);
|
||||
});
|
||||
|
||||
it('should respect the owner filter when using range queries', async () => {
|
||||
const res = await getAllCasesStatuses({
|
||||
supertest: supertestWithoutAuth,
|
||||
query: {
|
||||
from: '2022-03-15',
|
||||
to: '2022-03-21',
|
||||
},
|
||||
auth: {
|
||||
user: secOnly,
|
||||
space: 'space1',
|
||||
},
|
||||
});
|
||||
|
||||
expect(res).to.eql({
|
||||
count_open_cases: 1,
|
||||
count_closed_cases: 0,
|
||||
count_in_progress_cases: 0,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('deprecations', () => {
|
||||
|
|
|
@ -0,0 +1,125 @@
|
|||
{
|
||||
"attributes": {
|
||||
"closed_at": null,
|
||||
"closed_by": null,
|
||||
"connector": {
|
||||
"fields": null,
|
||||
"name": "none",
|
||||
"type": ".none"
|
||||
},
|
||||
"created_at": "2022-03-15T10:16:56.252Z",
|
||||
"created_by": {
|
||||
"email": "",
|
||||
"full_name": "",
|
||||
"username": "cnasikas"
|
||||
},
|
||||
"description": "test",
|
||||
"external_service": null,
|
||||
"owner": "securitySolutionFixture",
|
||||
"settings": {
|
||||
"syncAlerts": false
|
||||
},
|
||||
"status": "open",
|
||||
"tags": [],
|
||||
"title": "stack",
|
||||
"updated_at": "2022-03-29T10:33:09.754Z",
|
||||
"updated_by": {
|
||||
"email": "",
|
||||
"full_name": "",
|
||||
"username": "cnasikas"
|
||||
}
|
||||
},
|
||||
"coreMigrationVersion": "8.2.0",
|
||||
"id": "1537b380-a512-11ec-b94f-85999e89e434",
|
||||
"migrationVersion": {
|
||||
"cases": "8.1.0"
|
||||
},
|
||||
"references": [],
|
||||
"type": "cases",
|
||||
"updated_at": "2022-03-29T10:33:09.754Z",
|
||||
"version": "WzE2OTYyNCwxNF0="
|
||||
}
|
||||
|
||||
{
|
||||
"attributes": {
|
||||
"closed_at": null,
|
||||
"closed_by": null,
|
||||
"connector": {
|
||||
"fields": null,
|
||||
"name": "none",
|
||||
"type": ".none"
|
||||
},
|
||||
"created_at": "2022-03-20T10:16:56.252Z",
|
||||
"created_by": {
|
||||
"email": "",
|
||||
"full_name": "",
|
||||
"username": "cnasikas"
|
||||
},
|
||||
"description": "test 2",
|
||||
"external_service": null,
|
||||
"owner": "observabilityFixture",
|
||||
"settings": {
|
||||
"syncAlerts": false
|
||||
},
|
||||
"status": "open",
|
||||
"tags": [],
|
||||
"title": "stack",
|
||||
"updated_at": "2022-03-29T10:33:09.754Z",
|
||||
"updated_by": {
|
||||
"email": "",
|
||||
"full_name": "",
|
||||
"username": "cnasikas"
|
||||
}
|
||||
},
|
||||
"coreMigrationVersion": "8.2.0",
|
||||
"id": "3537b580-a512-11ec-b94f-85979e89e434",
|
||||
"migrationVersion": {
|
||||
"cases": "8.1.0"
|
||||
},
|
||||
"references": [],
|
||||
"type": "cases",
|
||||
"updated_at": "2022-03-29T10:33:09.754Z",
|
||||
"version": "WzE2OTYyNCwxNF0="
|
||||
}
|
||||
|
||||
{
|
||||
"attributes": {
|
||||
"closed_at": null,
|
||||
"closed_by": null,
|
||||
"connector": {
|
||||
"fields": null,
|
||||
"name": "none",
|
||||
"type": ".none"
|
||||
},
|
||||
"created_at": "2022-03-25T10:16:56.252Z",
|
||||
"created_by": {
|
||||
"email": "",
|
||||
"full_name": "",
|
||||
"username": "cnasikas"
|
||||
},
|
||||
"description": "test 2",
|
||||
"external_service": null,
|
||||
"owner": "securitySolutionFixture",
|
||||
"settings": {
|
||||
"syncAlerts": false
|
||||
},
|
||||
"status": "open",
|
||||
"tags": [],
|
||||
"title": "stack",
|
||||
"updated_at": "2022-03-29T10:33:09.754Z",
|
||||
"updated_by": {
|
||||
"email": "",
|
||||
"full_name": "",
|
||||
"username": "cnasikas"
|
||||
}
|
||||
},
|
||||
"coreMigrationVersion": "8.2.0",
|
||||
"id": "4537b380-a512-11ec-b92f-859b9e89e434",
|
||||
"migrationVersion": {
|
||||
"cases": "8.1.0"
|
||||
},
|
||||
"references": [],
|
||||
"type": "cases",
|
||||
"updated_at": "2022-03-29T10:33:09.754Z",
|
||||
"version": "WzE2OTYyNCwxNF0="
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue