Skip to content

Commit 763b308

Browse files
authored
Merge branch 'main' into renovate/main-oas
2 parents ce2a51c + b4bec7e commit 763b308

File tree

171 files changed

+2889
-1106
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

171 files changed

+2889
-1106
lines changed

.github/CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1204,6 +1204,7 @@ x-pack/platform/test/serverless/api_integration/test_suites/platform_security @e
12041204
/src/platform/test/functional/fixtures/kbn_archiver/discover.json @elastic/kibana-data-discovery
12051205
/src/platform/test/functional/fixtures/kbn_archiver/date_nested.json @elastic/kibana-data-discovery
12061206
/src/platform/test/functional/fixtures/kbn_archiver/date_* @elastic/kibana-data-discovery
1207+
/src/platform/test/functional/fixtures/kbn_archiver/managed_data_view.json @elastic/kibana-data-discovery
12071208
/src/platform/test/functional/fixtures/es_archiver/unmapped_fields @elastic/kibana-data-discovery # Assigned per the only use: https://github.com/elastic/kibana/blob/main/test/functional/apps/discover/group7/_indexpattern_with_unmapped_fields.ts#L26
12081209
/src/platform/test/functional/fixtures/es_archiver/message_with_newline @elastic/kibana-data-discovery # Assigned per the only use: https://github.com/elastic/kibana/blob/main/test/functional/apps/discover/classic/_doc_table_newline.ts#L24
12091210
/src/platform/test/functional/fixtures/es_archiver/hamlet @elastic/kibana-data-discovery # Assigned per the only use: https://github.com/elastic/kibana/blob/main/test/functional/apps/discover/group5/_large_string.ts#L30

packages/kbn-optimizer/limits.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ pageLoadAssetSize:
4747
embeddableAlertsTable: 6596
4848
embeddableEnhanced: 10332
4949
enterpriseSearch: 38494
50-
esql: 12323
50+
esql: 13323
5151
esqlDataGrid: 10209
5252
esUiShared: 100338
5353
eventAnnotation: 22361

src/platform/packages/private/kbn-esql-editor/src/custom_commands/use_lookup_index_editor.test.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,14 +70,14 @@ describe('getMonacoCommandString', () => {
7070
canEditIndex: true,
7171
});
7272
expect(result).toBe(
73-
'[Create lookup index](command:esql.lookup_index.create?%7B%22indexName%22%3A%22test-index%22%2C%22doesIndexExist%22%3Afalse%2C%22canEditIndex%22%3Atrue%7D)'
73+
'[Create lookup index](command:esql.lookup_index.create?%7B%22indexName%22%3A%22test-index%22%2C%22doesIndexExist%22%3Afalse%2C%22canEditIndex%22%3Atrue%2C%22triggerSource%22%3A%22esql_hover%22%7D)'
7474
);
7575
});
7676

7777
it('should return edit command for existing index with edit permissions', () => {
7878
const result = getMonacoCommandString('test-index', true, { canEditIndex: true });
7979
expect(result).toBe(
80-
'[Edit lookup index](command:esql.lookup_index.create?%7B%22indexName%22%3A%22test-index%22%2C%22doesIndexExist%22%3Atrue%2C%22canEditIndex%22%3Atrue%7D)'
80+
'[Edit lookup index](command:esql.lookup_index.create?%7B%22indexName%22%3A%22test-index%22%2C%22doesIndexExist%22%3Atrue%2C%22canEditIndex%22%3Atrue%2C%22triggerSource%22%3A%22esql_hover%22%7D)'
8181
);
8282
});
8383

@@ -87,7 +87,7 @@ describe('getMonacoCommandString', () => {
8787
canEditIndex: false,
8888
});
8989
expect(result).toBe(
90-
'[View lookup index](command:esql.lookup_index.create?%7B%22indexName%22%3A%22test-index%22%2C%22doesIndexExist%22%3Atrue%2C%22canEditIndex%22%3Afalse%7D)'
90+
'[View lookup index](command:esql.lookup_index.create?%7B%22indexName%22%3A%22test-index%22%2C%22doesIndexExist%22%3Atrue%2C%22canEditIndex%22%3Afalse%2C%22triggerSource%22%3A%22esql_hover%22%7D)'
9191
);
9292
});
9393

@@ -252,7 +252,7 @@ describe('useLookupIndexCommand', () => {
252252
options: expect.objectContaining({
253253
hoverMessage: expect.objectContaining({
254254
value:
255-
'[Create lookup index](command:esql.lookup_index.create?%7B%22indexName%22%3A%22test-index%22%2C%22doesIndexExist%22%3Afalse%2C%22canEditIndex%22%3Afalse%7D)',
255+
'[Create lookup index](command:esql.lookup_index.create?%7B%22indexName%22%3A%22test-index%22%2C%22doesIndexExist%22%3Afalse%2C%22canEditIndex%22%3Afalse%2C%22triggerSource%22%3A%22esql_hover%22%7D)',
256256
}),
257257
}),
258258
}),

src/platform/packages/private/kbn-esql-editor/src/custom_commands/use_lookup_index_editor.tsx

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ export function getMonacoCommandString(
7979
indexName,
8080
doesIndexExist: isExistingIndex,
8181
canEditIndex,
82+
triggerSource: 'esql_hover',
8283
})
8384
)})`;
8485
}
@@ -254,11 +255,17 @@ export const useLookupIndexCommand = (
254255
);
255256

256257
const openFlyout = useCallback(
257-
async (indexName: string, doesIndexExist?: boolean, canEditIndex = true) => {
258+
async (
259+
indexName: string,
260+
doesIndexExist?: boolean,
261+
canEditIndex = true,
262+
triggerSource = 'esql_autocomplete'
263+
) => {
258264
await uiActions.getTrigger('EDIT_LOOKUP_INDEX_CONTENT_TRIGGER_ID').exec({
259265
indexName,
260266
doesIndexExist,
261267
canEditIndex,
268+
triggerSource,
262269
onClose: async ({ indexName: resultIndexName, indexCreatedDuringFlyout }) => {
263270
await onFlyoutClose(indexName, resultIndexName, indexCreatedDuringFlyout);
264271
},
@@ -275,9 +282,17 @@ export const useLookupIndexCommand = (
275282
useEffect(function registerCommandOnMount() {
276283
const disposable = monaco.editor.registerCommand(
277284
COMMAND_ID,
278-
async (_, args: { indexName: string; doesIndexExist?: boolean; canEditIndex?: boolean }) => {
279-
const { indexName, doesIndexExist, canEditIndex } = args;
280-
await openFlyoutRef.current(indexName, doesIndexExist, canEditIndex);
285+
async (
286+
_,
287+
args: {
288+
indexName: string;
289+
doesIndexExist?: boolean;
290+
canEditIndex?: boolean;
291+
triggerSource?: string;
292+
}
293+
) => {
294+
const { indexName, doesIndexExist, canEditIndex, triggerSource } = args;
295+
await openFlyoutRef.current(indexName, doesIndexExist, canEditIndex, triggerSource);
281296
}
282297
);
283298
return () => {

src/platform/packages/private/kbn-index-editor/README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ import {
4545
} from '@kbn/index-editor';
4646
import type { EditLookupIndexFlyoutDeps } from '@kbn/index-editor';
4747

48+
// Register the index editor telemetry events inside setup
49+
registerIndexEditorAnalyticsEvents(core.analytics);
50+
51+
4852
// Register the index editor actions with required dependencies
4953
const deps: EditLookupIndexFlyoutDeps = {
5054
coreStart,
@@ -69,9 +73,10 @@ import type { EditLookupIndexContentContext } from '@kbn/index-editor';
6973

7074
// Create context for the index editor
7175
const context: EditLookupIndexContentContext = {
72-
indexName: 'my-lookup-index', // Set to
76+
indexName: 'my-lookup-index',
7377
doesIndexExist: true,
7478
canEditIndex: true,
79+
triggerSource: 'your_source', // Used only for telemetry
7580
onClose: (result) => {
7681
console.log('Index editor closed:', result);
7782
}

src/platform/packages/private/kbn-index-editor/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,7 @@ export {
1313
ACTION_EDIT_LOOKUP_INDEX,
1414
registerIndexEditorActions,
1515
} from './src/ui_action';
16+
17+
export { registerIndexEditorAnalyticsEvents } from './src/telemetry/events_registration';
18+
1619
export type { EditLookupIndexContentContext, EditLookupIndexFlyoutDeps } from './src/types';
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the "Elastic License
4+
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
5+
* Public License v 1"; you may not use this file except in compliance with, at
6+
* your election, the "Elastic License 2.0", the "GNU Affero General Public
7+
* License v3.0 only", or the "Server Side Public License, v 1".
8+
*/
9+
10+
import { createFlyout } from './create_flyout';
11+
import { BehaviorSubject, of, Subject } from 'rxjs';
12+
import {
13+
httpServiceMock,
14+
notificationServiceMock,
15+
overlayServiceMock,
16+
} from '@kbn/core/public/mocks';
17+
import type { FlyoutDeps } from '../types';
18+
import { IndexEditorTelemetryService } from '../telemetry/telemetry_service';
19+
import { dataPluginMock } from '@kbn/data-plugin/public/mocks';
20+
21+
describe('createFlyout', () => {
22+
const coreStart = {
23+
http: httpServiceMock.createStartContract(),
24+
overlays: overlayServiceMock.createStartContract(),
25+
notifications: notificationServiceMock.createStartContract(),
26+
data: dataPluginMock.createStartContract(),
27+
application: {
28+
currentAppId$: new BehaviorSubject('my-app'),
29+
},
30+
};
31+
32+
const indexEditorTelemetryService = new IndexEditorTelemetryService(
33+
{
34+
reportEvent: jest.fn(),
35+
} as any,
36+
true,
37+
true,
38+
'test-source'
39+
);
40+
41+
const indexUpdateService = {
42+
setIndexName: jest.fn(),
43+
setIndexCreated: jest.fn(),
44+
exit: jest.fn(),
45+
completed$: of(),
46+
totalHits$: new BehaviorSubject<number>(0),
47+
dataTableColumns$: new Subject<any[]>(),
48+
};
49+
50+
const deps: FlyoutDeps = {
51+
coreStart: coreStart as any,
52+
data: coreStart.data,
53+
indexUpdateService: indexUpdateService as any,
54+
indexEditorTelemetryService,
55+
uiActions: {} as any,
56+
fieldFormats: {} as any,
57+
share: {} as any,
58+
fileUpload: {} as any,
59+
storage: {} as any,
60+
fileManager: {} as any,
61+
};
62+
63+
beforeEach(() => {
64+
jest.clearAllMocks();
65+
indexUpdateService.totalHits$.next(0);
66+
});
67+
68+
it('should call trackFlyoutOpened with correct data when index exists', async () => {
69+
const trackFlyoutOpenedSpy = jest.spyOn(deps.indexEditorTelemetryService, 'trackFlyoutOpened');
70+
71+
createFlyout(deps, {
72+
doesIndexExist: true,
73+
indexName: 'test-index',
74+
canEditIndex: true,
75+
triggerSource: 'test-source',
76+
});
77+
78+
indexUpdateService.totalHits$.next(10);
79+
indexUpdateService.dataTableColumns$.next([{ name: 'field1' }, { name: 'field2' }]);
80+
81+
expect(trackFlyoutOpenedSpy).toHaveBeenCalledWith({
82+
docCount: 10,
83+
fieldCount: 2,
84+
});
85+
});
86+
87+
it('should call trackFlyoutOpened with zero counts when index does not exist', () => {
88+
const trackFlyoutOpenedSpy = jest.spyOn(deps.indexEditorTelemetryService, 'trackFlyoutOpened');
89+
90+
createFlyout(deps, {
91+
doesIndexExist: false,
92+
canEditIndex: true,
93+
triggerSource: 'test-source',
94+
});
95+
96+
expect(trackFlyoutOpenedSpy).toHaveBeenCalledWith({
97+
docCount: 0,
98+
fieldCount: 0,
99+
});
100+
});
101+
});

src/platform/packages/private/kbn-index-editor/src/components/create_flyout.tsx

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@ import { FormattedMessage } from '@kbn/i18n-react';
1212
import { toMountPoint } from '@kbn/react-kibana-mount';
1313
import type { FC } from 'react';
1414
import React, { Suspense, lazy } from 'react';
15-
import { distinctUntilChanged, from, skip, takeUntil } from 'rxjs';
15+
import { combineLatest, distinctUntilChanged, first, from, skip, takeUntil } from 'rxjs';
1616
import type { EditLookupIndexContentContext, FlyoutDeps } from '../types';
17+
import { isPlaceholderColumn } from '../utils';
1718

1819
export function createFlyout(deps: FlyoutDeps, props: EditLookupIndexContentContext) {
1920
const {
@@ -31,6 +32,23 @@ export function createFlyout(deps: FlyoutDeps, props: EditLookupIndexContentCont
3132
}
3233
indexUpdateService.setIndexCreated(props.doesIndexExist);
3334

35+
// Track telemetry event when flyout is opened
36+
if (props.doesIndexExist) {
37+
combineLatest([
38+
indexUpdateService.totalHits$.pipe(skip(1)), // skip initial value
39+
indexUpdateService.dataTableColumns$,
40+
])
41+
.pipe(first())
42+
.subscribe(([docCount, columns]) => {
43+
deps.indexEditorTelemetryService.trackFlyoutOpened({
44+
docCount,
45+
fieldCount: columns.filter((c) => !isPlaceholderColumn(c.name)).length,
46+
});
47+
});
48+
} else {
49+
deps.indexEditorTelemetryService.trackFlyoutOpened({ docCount: 0, fieldCount: 0 });
50+
}
51+
3452
const LazyFlyoutContent = lazy(async () => {
3553
const { FlyoutContent } = await import('./flyout_content');
3654
return {

src/platform/packages/private/kbn-index-editor/src/index_update_service.test.ts

Lines changed: 77 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,48 @@ import type { DataPublicPluginStart } from '@kbn/data-plugin/public';
1414
import { ROW_PLACEHOLDER_PREFIX } from './constants';
1515
import { IndexUpdateService } from './index_update_service';
1616
import { dataPluginMock } from '@kbn/data-plugin/public/mocks';
17-
import { httpServiceMock, notificationServiceMock } from '@kbn/core/public/mocks';
17+
import {
18+
analyticsServiceMock,
19+
httpServiceMock,
20+
notificationServiceMock,
21+
} from '@kbn/core/public/mocks';
22+
import { IndexEditorTelemetryService } from './telemetry/telemetry_service';
23+
import type { AnalyticsServiceStart } from '@kbn/core/server';
24+
import { getESQLAdHocDataview } from '@kbn/esql-utils';
25+
26+
jest.mock('@kbn/esql-utils', () => ({
27+
getESQLAdHocDataview: jest.fn(),
28+
}));
1829

1930
describe('IndexUpdateService', () => {
2031
let http: HttpStart;
2132
let data: DataPublicPluginStart;
2233
let service: IndexUpdateService;
2334
let notifications: NotificationsStart;
35+
let analytics: AnalyticsServiceStart;
36+
let indexEditorTelemetryService: IndexEditorTelemetryService;
2437

2538
beforeEach(() => {
2639
http = httpServiceMock.createStartContract();
2740
data = dataPluginMock.createStartContract();
2841
notifications = notificationServiceMock.createStartContract();
42+
analytics = analyticsServiceMock.createAnalyticsServiceStart();
43+
indexEditorTelemetryService = new IndexEditorTelemetryService(
44+
analytics,
45+
true,
46+
true,
47+
'esql_hover'
48+
);
49+
50+
(getESQLAdHocDataview as jest.Mock).mockResolvedValue({
51+
fields: {
52+
getByName: () => {},
53+
create: () => ({}),
54+
concat: () => [],
55+
},
56+
});
2957

30-
service = new IndexUpdateService(http, data, notifications, true);
58+
service = new IndexUpdateService(http, data, notifications, indexEditorTelemetryService, true);
3159
});
3260

3361
afterEach(() => {
@@ -179,4 +207,51 @@ describe('IndexUpdateService', () => {
179207
expect(rowsAfterDeletion.length).toBe(1); // An empty placeholder row should always be visible
180208
expect(rowsAfterDeletion[0].raw).toMatchObject({ _id: expect.anything() });
181209
});
210+
211+
describe('flush operations', () => {
212+
it('should call telemetry on successful flush', async () => {
213+
(http.post as jest.Mock).mockResolvedValue({
214+
errors: false,
215+
items: [],
216+
took: 0,
217+
} satisfies BulkResponse);
218+
const telemetrySpy = jest.spyOn(indexEditorTelemetryService, 'trackSaveSubmitted');
219+
220+
service.setIndexName('my-index');
221+
service.setIndexCreated(true);
222+
await firstValueFrom(service.dataView$);
223+
224+
// Adding and modifying a new row counts as 1 row added and 0 cells edited
225+
service.addEmptyRow();
226+
const placeholderRow = (await firstValueFrom(service.rows$))[0];
227+
service.updateDoc(placeholderRow.id, { field: 'value' });
228+
229+
// Adding a column and editing its name counts as 1 col added
230+
service.addNewColumn();
231+
const newColumn = (await firstValueFrom(service.pendingColumnsToBeSaved$))[0];
232+
service.editColumn('newColumn', newColumn.name);
233+
234+
// Counts as 2 cell edited
235+
service.updateDoc('123', { field: 'value' });
236+
service.updateDoc('123', { newColumn: 'value' });
237+
238+
// Wait for the changes to be registered
239+
await firstValueFrom(service.hasUnsavedChanges$);
240+
241+
// Save the changes
242+
service.flush();
243+
244+
// wait for async operations to complete
245+
await new Promise((resolve) => process.nextTick(resolve));
246+
247+
expect(telemetrySpy).toHaveBeenCalledWith({
248+
pendingRowsAdded: 1,
249+
pendingColsAdded: 1,
250+
pendingCellsEdited: 2,
251+
action: 'save',
252+
outcome: 'success',
253+
latency: expect.any(Number),
254+
});
255+
});
256+
});
182257
});

0 commit comments

Comments
 (0)