Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
86acf21
[Cases] Add incremental id service and expose the ID in the UI (#222874)
janmonschke Jun 25, 2025
d683059
[Cases] Incremental id telemetry and config improvements (#226935)
janmonschke Jul 9, 2025
b9f05c5
enable case id
christineweng Aug 8, 2025
c258d98
add raw mapping
michaelolo24 Aug 21, 2025
f4735e1
remove advanced setting
michaelolo24 Sep 2, 2025
42ec4de
[CI] Auto-commit changed files from 'node scripts/check_mappings_upda…
kibanamachine Sep 2, 2025
6e0a0b4
update subfield to keyword
michaelolo24 Sep 4, 2025
f53201a
add mapping updates
michaelolo24 Sep 4, 2025
7e4558d
remove screenshot
michaelolo24 Sep 7, 2025
5e1d23d
remove unused import
michaelolo24 Sep 8, 2025
6ed1f8c
[CI] Auto-commit changed files from 'node scripts/jest_integration -u…
kibanamachine Sep 8, 2025
4ec38f6
re-unify versions
michaelolo24 Sep 9, 2025
bac5ede
fix duplicate import
michaelolo24 Sep 11, 2025
abf2e0f
update mock and add logging to script
michaelolo24 Sep 12, 2025
7a65184
fix type
michaelolo24 Sep 12, 2025
efe1ed6
Merge branch 'main' into cases-incremental-id
michaelolo24 Sep 12, 2025
90d25f9
Merge remote-tracking branch 'upstream/main' into cases-incremental-id
michaelolo24 Sep 16, 2025
fb7f985
update tests
michaelolo24 Sep 16, 2025
dbff686
fix tests
michaelolo24 Sep 17, 2025
53b280a
fix tests
michaelolo24 Sep 17, 2025
a2cf356
use text field for phrase prefix queries
michaelolo24 Sep 17, 2025
9815bc5
Merge branch 'update-case-id-mapping-2' into cases-incremental-id
michaelolo24 Sep 17, 2025
5b546a2
update to use .text
michaelolo24 Sep 17, 2025
1833162
update additional tests
michaelolo24 Sep 17, 2025
931edc5
Merge remote-tracking branch 'upstream/main' into cases-incremental-id
michaelolo24 Sep 17, 2025
cb5d152
fix mapping change
michaelolo24 Sep 17, 2025
de52a0b
update registered types
michaelolo24 Sep 17, 2025
0bf4d89
fix failing tests
michaelolo24 Sep 17, 2025
d68e178
fix type error
michaelolo24 Sep 18, 2025
14b006e
fix tests
michaelolo24 Sep 18, 2025
2c5ab95
remove unnecessary config schema fields
michaelolo24 Sep 19, 2025
e27251d
Merge remote-tracking branch 'upstream/main' into update-case-id-mapp…
michaelolo24 Sep 19, 2025
9fc6abb
[CI] Auto-commit changed files from 'node scripts/jest_integration -u…
kibanamachine Sep 19, 2025
5591214
additional pr feedback
michaelolo24 Sep 19, 2025
6761022
Merge branch 'update-case-id-mapping-2' into cases-incremental-id
michaelolo24 Sep 19, 2025
32c5b29
Merge remote-tracking branch 'upstream/main' into cases-incremental-id
michaelolo24 Sep 23, 2025
71c500e
update tests
michaelolo24 Sep 23, 2025
2a0b83c
minor cleanup
michaelolo24 Sep 25, 2025
16bac44
disable by default, to enable after additional FF testing
michaelolo24 Sep 25, 2025
3e15f2a
Merge remote-tracking branch 'upstream/main' into cases-incremental-id
michaelolo24 Sep 25, 2025
994d158
fix tests
michaelolo24 Sep 26, 2025
5b92290
Merge remote-tracking branch 'upstream/main' into cases-incremental-id
michaelolo24 Sep 26, 2025
1e43653
Merge remote-tracking branch 'upstream/main' into cases-incremental-id
michaelolo24 Sep 29, 2025
4bb5685
Merge branch 'main' into cases-incremental-id
michaelolo24 Sep 29, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -660,6 +660,12 @@ export const stackManagementSchema: MakeSchemaFrom<UsageStats> = {
description: 'Enable the new logs overview component.',
},
},
'cases:incrementalIdDisplay:enabled': {
type: 'boolean',
_meta: {
description: 'Display the incremental id of a case in the relevant pages',
},
},
'observability:streamsEnableSignificantEvents': {
type: 'boolean',
_meta: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ export interface UsageStats {
'securitySolution:excludedDataTiersForRuleExecution': string[];
'securitySolution:maxUnassociatedNotes': number;
'observability:searchExcludedDataTiers': string[];
'cases:incrementalIdDisplay:enabled': boolean;
'observability:enableDiagnosticMode': boolean;
'observability:streamsEnableSignificantEvents': boolean;
'genAiSettings:defaultAIConnector': string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11036,6 +11036,12 @@
"description": "Enable the new logs overview component."
}
},
"cases:incrementalIdDisplay:enabled": {
"type": "boolean",
"_meta": {
"description": "Display the incremental id of a case in the relevant pages"
}
},
"observability:streamsEnableSignificantEvents": {
"type": "boolean",
"_meta": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ export default function ({ getService }: PluginFunctionalProviderContext) {
'xpack.cases.markdownPlugins.lens (boolean?)',
'xpack.cases.stack.enabled (boolean?)',
'xpack.cases.unsafe.enableCaseSummary (boolean?)',
'xpack.cases.incrementalId.enabled (boolean?)',
'xpack.ccr.ui.enabled (boolean?)',
'xpack.cloud.base_url (string?)',
'xpack.cloud.cname (string?)',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/*
* 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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

export const DEFAULT_TASK_INTERVAL_MINUTES = 10;
export const DEFAULT_TASK_START_DELAY_MINUTES = 10;
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export const CASE_RULES_SAVED_OBJECT = 'cases-rules' as const;
export const CASE_ID_INCREMENTER_SAVED_OBJECT = 'cases-incrementing-id' as const;

/**
* If more values are added here please also add them here: x-pack/platform/test/cases_api_integration/common/plugins
* If more values are added here please also add them here: x-pack/test/cases_api_integration/common/plugins
*/
export const SAVED_OBJECT_TYPES = [
CASE_SAVED_OBJECT,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,7 @@ describe('CasesFindRequestRt', () => {
page: '1',
perPage: '10',
search: 'search text',
searchFields: ['title', 'description'],
searchFields: ['title', 'description', 'incremental_id.text'],
to: '1w',
sortOrder: 'desc',
sortField: 'createdAt',
Expand Down Expand Up @@ -536,7 +536,7 @@ describe('CasesSearchRequestRt', () => {
page: '1',
perPage: '10',
search: 'search text',
searchFields: ['title', 'description'],
searchFields: ['title', 'description', 'incremental_id.text'],
to: '1w',
sortOrder: 'desc',
sortField: 'createdAt',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ export const BulkCreateCasesResponseRt = rt.strict({
export const CasesFindRequestSearchFieldsRt = rt.keyof({
description: null,
title: null,
'incremental_id.text': null,
});

export const CasesFindRequestSortFieldsRt = rt.keyof({
Expand Down
3 changes: 3 additions & 0 deletions x-pack/platform/plugins/shared/cases/common/ui/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@ export interface CasesUiConfigType {
unsafe?: {
enableCaseSummary: boolean;
};
incrementalId: {
enabled: boolean;
};
}

export const UserActionTypeAll = 'all' as const;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,10 @@ describe('AllCasesListGeneric', () => {
beforeAll(() => {
patchGetComputedStyle();
mockKibana();
const actionTypeRegistry = useKibanaMock().services.triggersActionsUi.actionTypeRegistry;
const {
triggersActionsUi: { actionTypeRegistry },
} = useKibanaMock().services;

registerConnectorsToMockActionRegistry(actionTypeRegistry, connectorsMock);
});

Expand Down Expand Up @@ -193,6 +196,10 @@ describe('AllCasesListGeneric', () => {
(await screen.findAllByTestId('case-user-profile-avatar-damaged_raccoon'))[0]
).toHaveTextContent('DR');

const incrementalIdTextElements = screen.getAllByTestId('cases-incremental-id-text');
expect(incrementalIdTextElements).toHaveLength(1);
expect(incrementalIdTextElements[0]).toHaveTextContent('#1');

expect((await screen.findAllByTestId('case-table-column-tags-coke'))[0]).toHaveAttribute(
'title',
useGetCasesMockState.data.cases[0].tags[0]
Expand Down Expand Up @@ -515,7 +522,6 @@ describe('AllCasesListGeneric', () => {
expect(useGetCasesMock).toHaveBeenLastCalledWith({
filterOptions: {
...DEFAULT_FILTER_OPTIONS,
searchFields: ['title', 'description'],
category: ['twix'],
},
queryParams: DEFAULT_QUERY_PARAMS,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ describe('CasesTableFilters ', () => {
"searchFields": Array [
"title",
"description",
"incremental_id.text",
],
"severity": Array [],
"status": Array [],
Expand Down Expand Up @@ -267,6 +268,7 @@ describe('CasesTableFilters ', () => {
"searchFields": Array [
"title",
"description",
"incremental_id.text",
],
"severity": Array [],
"status": Array [],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
*/

import React from 'react';
import { mount } from 'enzyme';
import { licensingMock } from '@kbn/licensing-plugin/public/mocks';

import type { GetCasesColumn } from './use_cases_columns';
Expand Down Expand Up @@ -630,70 +629,56 @@ describe('useCasesColumns ', () => {

describe('ExternalServiceColumn ', () => {
it('Not pushed render', () => {
const wrapper = mount(
<TestProviders>
<ExternalServiceColumn
theCase={useGetCasesMockState.data.cases[0]}
connectors={connectors}
/>
</TestProviders>
renderWithTestingProviders(
<ExternalServiceColumn
theCase={useGetCasesMockState.data.cases[0]}
connectors={connectors}
/>
);

expect(
wrapper.find(`[data-test-subj="case-table-column-external-notPushed"]`).last().exists()
).toBeTruthy();
expect(screen.getByTestId('case-table-column-external-notPushed')).toBeInTheDocument();
});

it('Up to date', () => {
const wrapper = mount(
<TestProviders>
<ExternalServiceColumn
theCase={useGetCasesMockState.data.cases[1]}
connectors={connectors}
/>
</TestProviders>
renderWithTestingProviders(
<ExternalServiceColumn
theCase={useGetCasesMockState.data.cases[1]}
connectors={connectors}
/>
);

expect(
wrapper.find(`[data-test-subj="case-table-column-external-upToDate"]`).last().exists()
).toBeTruthy();
expect(screen.getByTestId('case-table-column-external-upToDate')).toBeInTheDocument();
});

it('Needs update', () => {
const wrapper = mount(
<TestProviders>
<ExternalServiceColumn
theCase={useGetCasesMockState.data.cases[2]}
connectors={connectors}
/>
</TestProviders>
renderWithTestingProviders(
<ExternalServiceColumn
theCase={useGetCasesMockState.data.cases[2]}
connectors={connectors}
/>
);

expect(
wrapper.find(`[data-test-subj="case-table-column-external-requiresUpdate"]`).last().exists()
).toBeTruthy();
expect(screen.getByTestId('case-table-column-external-requiresUpdate')).toBeInTheDocument();
});

it('it does not throw when accessing the icon if the connector type is not registered', () => {
// If the component throws the test will fail
expect(() =>
mount(
<TestProviders>
<ExternalServiceColumn
theCase={useGetCasesMockState.data.cases[2]}
connectors={[
{
id: 'none',
actionTypeId: '.none',
name: 'None',
config: {},
isPreconfigured: false,
isSystemAction: false,
isDeprecated: false,
},
]}
/>
</TestProviders>
renderWithTestingProviders(
<ExternalServiceColumn
theCase={useGetCasesMockState.data.cases[2]}
connectors={[
{
id: 'none',
actionTypeId: '.none',
name: 'None',
config: {},
isPreconfigured: false,
isSystemAction: false,
isDeprecated: false,
},
]}
/>
)
).not.toThrowError();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import { SeverityHealth } from '../severity/config';
import { AssigneesColumn } from './assignees_column';
import { builderMap as customFieldsBuilderMap } from '../custom_fields/builder';
import { useGetCaseConfiguration } from '../../containers/configure/use_get_case_configuration';
import { IncrementalIdText } from '../incremental_id';

type CasesColumns =
| EuiTableActionsColumnType<CaseUI>
Expand Down Expand Up @@ -112,9 +113,14 @@ export const useCasesColumns = ({
const caseDetailsLinkComponent = isSelectorView ? (
theCase.title
) : (
<CaseDetailsLink detailName={theCase.id} title={theCase.title}>
<TruncatedText text={theCase.title} />
</CaseDetailsLink>
<div>
<CaseDetailsLink detailName={theCase.id} title={theCase.title}>
<TruncatedText text={theCase.title} />
</CaseDetailsLink>
{typeof theCase.incrementalId === 'number' ? (
<IncrementalIdText incrementalId={theCase.incrementalId} />
) : null}
</div>
);

return caseDetailsLinkComponent;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ describe('allCasesUrlStateDeserializer', () => {
"searchFields": Array [
"title",
"description",
"incremental_id.text",
],
"severity": Array [],
"status": Array [],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ describe('stringifyUrlParams', () => {
};

expect(stringifyUrlParams(urlParams)).toMatchInlineSnapshot(
`"cases=(assignees:!(),category:!(),customFields:(my_field:!(foo,bar)),owner:!(),page:1,perPage:10,reporters:!(),search:'',searchFields:!(title,description),severity:!(),sortField:createdAt,sortOrder:desc,status:!(),tags:!())"`
`"cases=(assignees:!(),category:!(),customFields:(my_field:!(foo,bar)),owner:!(),page:1,perPage:10,reporters:!(),search:'',searchFields:!(title,description,incremental_id.text),severity:!(),sortField:createdAt,sortOrder:desc,status:!(),tags:!())"`
);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ export const CaseViewPage = React.memo<CaseViewPageProps>(
/>
}
title={caseData.title}
incrementalId={caseData.incrementalId}
>
<CaseActionBar
caseData={caseData}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,26 @@ describe('HeaderPage', () => {
expect(screen.getByText('Test supplement')).toBeInTheDocument();
});

it('renders the `incremental_id` when provided', () => {
renderWithTestingProviders(
<TestProviders>
<HeaderPage border title="Test title" incrementalId={1337} />
</TestProviders>
);

expect(screen.getByText('#1337')).toBeInTheDocument();
});

it('does not render the `incremental_id` when not provided', () => {
renderWithTestingProviders(
<TestProviders>
<HeaderPage border title="Test title" />
</TestProviders>
);

expect(screen.queryByTestId('cases-incremental-id-text')).not.toBeInTheDocument();
});

it('DOES NOT render the back link when not provided', () => {
const wrapper = mount(
<TestProviders>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { css } from '@emotion/react';

import { Title } from './title';
import { useCasesContext } from '../cases_context/use_cases_context';
import { IncrementalIdText } from '../incremental_id';

interface HeaderProps {
border?: boolean;
Expand All @@ -22,6 +23,7 @@ export interface HeaderPageProps extends HeaderProps {
children?: React.ReactNode;
title: string | React.ReactNode;
titleNode?: React.ReactElement;
incrementalId?: number | null;
'data-test-subj'?: string;
}

Expand All @@ -43,14 +45,15 @@ const HeaderPageComponent: React.FC<HeaderPageProps> = ({
isLoading,
title,
titleNode,
incrementalId,
'data-test-subj': dataTestSubj,
}) => {
const { releasePhase } = useCasesContext();
const { euiTheme } = useEuiTheme();

return (
<header css={getHeaderCss(euiTheme, border)} data-test-subj={dataTestSubj}>
<EuiFlexGroup alignItems="center">
<EuiFlexGroup alignItems="center" gutterSize="s">
<EuiFlexItem
css={css`
overflow: hidden;
Expand All @@ -61,7 +64,6 @@ const HeaderPageComponent: React.FC<HeaderPageProps> = ({

{border && isLoading && <EuiProgress size="xs" color="accent" />}
</EuiFlexItem>

{children && (
<EuiFlexItem
data-test-subj="header-page-supplements"
Expand All @@ -74,6 +76,13 @@ const HeaderPageComponent: React.FC<HeaderPageProps> = ({
</EuiFlexItem>
)}
</EuiFlexGroup>
<EuiFlexGroup>
{typeof incrementalId === 'number' && (
<EuiFlexItem>
<IncrementalIdText incrementalId={incrementalId} />
</EuiFlexItem>
)}
</EuiFlexGroup>
</header>
);
};
Expand Down
Loading