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

[PoC] Dashboard queries API #173416

Draft
wants to merge 14 commits into
base: main
Choose a base branch
from
Draft
5 changes: 3 additions & 2 deletions src/plugins/dashboard/kibana.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"urlForwarding",
"presentationUtil",
"visualizations",
"unifiedSearch"
"unifiedSearch",
],
"optionalPlugins": [
"home",
Expand All @@ -35,7 +35,8 @@
"usageCollection",
"taskManager",
"serverless",
"noDataPage"
"noDataPage",
"lens"
],
"requiredBundles": ["kibanaReact", "kibanaUtils", "presentationUtil"]
}
Expand Down
5 changes: 5 additions & 0 deletions src/plugins/dashboard/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { UsageCollectionSetup } from '@kbn/usage-collection-plugin/server';
import { ContentManagementServerSetup } from '@kbn/content-management-plugin/server';
import { PluginInitializerContext, CoreSetup, CoreStart, Plugin, Logger } from '@kbn/core/server';

import { LensServerPluginSetup } from '@kbn/lens-plugin/server';
import {
initializeDashboardTelemetryTask,
scheduleDashboardTelemetry,
Expand All @@ -28,12 +29,14 @@ import { createDashboardSavedObjectType } from './dashboard_saved_object';
import { CONTENT_ID, LATEST_VERSION } from '../common/content_management';
import { registerDashboardUsageCollector } from './usage/register_collector';
import { dashboardPersistableStateServiceFactory } from './dashboard_container/dashboard_container_embeddable_factory';
import { setupQueryExtractionRoute } from './query_extraction/query_extraction_route';

interface SetupDeps {
embeddable: EmbeddableSetup;
usageCollection: UsageCollectionSetup;
taskManager: TaskManagerSetupContract;
contentManagement: ContentManagementServerSetup;
lens: LensServerPluginSetup;
}

interface StartDeps {
Expand All @@ -60,6 +63,8 @@ export class DashboardPlugin
})
);

setupQueryExtractionRoute(core, plugins.lens.extractQueries);

plugins.contentManagement.register({
id: CONTENT_ID,
storage: new DashboardStorage({
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*
* 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 { getKbnServerError, reportServerError } from '@kbn/kibana-utils-plugin/server';
import type { CoreSetup } from '@kbn/core/server';
import { schema } from '@kbn/config-schema';
import type { LensByValueInput } from '@kbn/lens-plugin/public/embeddable/embeddable';
import type { LensServerPluginSetup } from '@kbn/lens-plugin/server';
import type { SavedDashboardPanel } from '../../common/content_management';
import { convertSavedDashboardPanelToPanelState, type DashboardAttributes } from '../../common';

export const setupQueryExtractionRoute = (
{ http, getStartServices }: CoreSetup,
extractQueries: LensServerPluginSetup['extractQueries']
) => {
const router = http.createRouter();

router.versioned
.get({
access: 'public',
path: '/api/dashboards/dashboard/{id}/getQueries',
options: {
authRequired: false,
},
})
.addVersion(
{
version: '2023-10-31',
validate: {
request: {
params: schema.object(
{
id: schema.string({
minLength: 1,
maxLength: 1_000,
}),
},
{ unknowns: 'allow' }
),
},
response: schema.any(),
},
},
async (context, request, response) => {
try {
const [coreStart] = await getStartServices();
const client = coreStart.savedObjects.getScopedClient(request);

const SO = await client.get('dashboard', request.params.id);

const panels = JSON.parse(
(SO.attributes as DashboardAttributes).panelsJSON
) as SavedDashboardPanel[];

const supportedPanels = panels.filter(({ type }) => type === 'lens');

const queriesByPanel: Array<{ title?: string; queries: object[] }> = [];

for (const panel of supportedPanels) {
const panelState = convertSavedDashboardPanelToPanelState<LensByValueInput>(panel);

// TODO not sure if this is type safe
const attributes = panelState.explicitInput.attributes!;

// copied from initializeSavedVis in Lens embeddable
const savedVis = {
...attributes,
type: 'lens',
// savedObjectId: (input as LensByReferenceInput)?.savedObjectId,
};

const queries = await extractQueries(
savedVis,
{
elasticsearch: (await context.core).elasticsearch.client.asCurrentUser,
savedObjects: client,
},
request
);

queriesByPanel.push({
title: panel.title,
queries,
});
}

return response.ok({
body: {
panels: queriesByPanel,
},
});
} catch (e) {
const kbnErr = getKbnServerError(e);
return reportServerError(response, kbnErr);
}
}
);
};
2 changes: 2 additions & 0 deletions src/plugins/data/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ export type {
} from './types';
export { KBN_FIELD_TYPES, ES_FIELD_TYPES } from './types';

export { NowProvider } from './now_provider';

export {
createEscapeValue,
tableHasFormulas,
Expand Down
7 changes: 5 additions & 2 deletions src/plugins/data/public/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,8 +244,11 @@ export {
QueryService,
} from './query';

export { NowProvider } from './now_provider';
export type { NowProviderInternalContract, NowProviderPublicContract } from './now_provider';
export { NowProvider } from '../common/now_provider';
export type {
NowProviderInternalContract,
NowProviderPublicContract,
} from '../common/now_provider';

export type {
QueryState,
Expand Down
2 changes: 1 addition & 1 deletion src/plugins/data/public/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { createDatatableUtilitiesMock } from '../common/mocks';
import { DataPlugin } from '.';
import { searchServiceMock } from './search/mocks';
import { queryServiceMock } from './query/mocks';
import { createNowProviderMock } from './now_provider/mocks';
import { createNowProviderMock } from '../common/now_provider/mocks';
import { dataViewPluginMocks } from './data_views/mocks';

export type Setup = jest.Mocked<ReturnType<DataPlugin['setup']>>;
Expand Down
4 changes: 2 additions & 2 deletions src/plugins/data/public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ import {
} from './actions';
import { applyFilterTrigger } from './triggers';
import { getTableViewDescription } from './utils/table_inspector_view';
import { NowProvider, NowProviderInternalContract } from './now_provider';
import { getAggsFormats, DatatableUtilitiesService } from '../common';
import type { NowProviderInternalContract } from '../common/now_provider';
import { NowProvider, getAggsFormats, DatatableUtilitiesService } from '../common';

export class DataPublicPlugin
implements
Expand Down
2 changes: 1 addition & 1 deletion src/plugins/data/public/query/query_service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { Storage } from '@kbn/kibana-utils-plugin/public';
import { QueryService, QueryStart } from './query_service';
import { StubBrowserStorage } from '@kbn/test-jest-helpers';
import { TimefilterContract } from './timefilter';
import { createNowProviderMock } from '../now_provider/mocks';
import { createNowProviderMock } from '../../common/now_provider/mocks';

const setupMock = coreMock.createSetup();
const startMock = coreMock.createStart();
Expand Down
2 changes: 1 addition & 1 deletion src/plugins/data/public/query/query_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import type { QueryStringContract } from './query_string';
import { QueryStringManager } from './query_string';
import { getEsQueryConfig } from '../../common';
import { getUiSettings } from '../services';
import { NowProviderInternalContract } from '../now_provider';
import { NowProviderInternalContract } from '../../common/now_provider';
import {
extract,
getAllMigrations,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { StubBrowserStorage } from '@kbn/test-jest-helpers';
import { connectToQueryState } from './connect_to_query_state';
import { TimefilterContract } from '../timefilter';
import { QueryState } from '../query_state';
import { createNowProviderMock } from '../../now_provider/mocks';
import { createNowProviderMock } from '../../../common/now_provider/mocks';

const connectToQueryGlobalState = (query: QueryStart, state: BaseStateContainer<QueryState>) =>
connectToQueryState(query, state, {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { StubBrowserStorage } from '@kbn/test-jest-helpers';
import { TimefilterContract } from '../timefilter';
import { syncQueryStateWithUrl } from './sync_state_with_url';
import { GlobalQueryStateFromUrl } from './types';
import { createNowProviderMock } from '../../now_provider/mocks';
import { createNowProviderMock } from '../../../common/now_provider/mocks';

const setupMock = coreMock.createSetup();
const startMock = coreMock.createStart();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { TimeRange } from '@kbn/es-query';
import { AutoRefreshDoneFn, Timefilter } from './timefilter';
import { Subscription } from 'rxjs';
import { RefreshInterval } from '../../../common';
import { createNowProviderMock } from '../../now_provider/mocks';
import { createNowProviderMock } from '../../../common/now_provider/mocks';

import { timefilterServiceMock } from './timefilter_service.mock';
const timefilterSetupMock = timefilterServiceMock.createSetupContract();
Expand Down
2 changes: 1 addition & 1 deletion src/plugins/data/public/query/timefilter/timefilter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { TimeRange } from '@kbn/es-query';
import type { DataView } from '@kbn/data-views-plugin/common';
import { areRefreshIntervalsDifferent, areTimeRangesDifferent } from './lib/diff_time_picker_vals';
import type { TimefilterConfig, InputTimeRange, TimeRangeBounds } from './types';
import { NowProviderInternalContract } from '../../now_provider';
import { NowProviderInternalContract } from '../../../common/now_provider';
import {
calculateBounds,
getAbsoluteTimeRange,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { IUiSettingsClient } from '@kbn/core/public';
import { IStorageWrapper } from '@kbn/kibana-utils-plugin/public';
import { TimeHistory, Timefilter, TimeHistoryContract, TimefilterContract } from '.';
import { UI_SETTINGS } from '../../../common';
import { NowProviderInternalContract } from '../../now_provider';
import { NowProviderInternalContract } from '../../../common/now_provider';

/**
* Filter Service
Expand Down
2 changes: 1 addition & 1 deletion src/plugins/data/public/search/aggs/aggs_service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
AggsStartDependencies,
createGetConfig,
} from './aggs_service';
import { createNowProviderMock } from '../../now_provider/mocks';
import { createNowProviderMock } from '../../../common/now_provider/mocks';

const { uiSettings } = coreMock.createSetup();

Expand Down
2 changes: 1 addition & 1 deletion src/plugins/data/public/search/aggs/aggs_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
} from '../../../common/search/aggs';
import { calculateBounds, TimeRange } from '../../../common';
import type { AggsSetup, AggsStart } from './types';
import type { NowProviderInternalContract } from '../../now_provider';
import type { NowProviderInternalContract } from '../../../common/now_provider';

/**
* Aggs needs synchronous access to specific uiSettings. Since settings can change
Expand Down
2 changes: 1 addition & 1 deletion src/plugins/data/public/search/search_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ import {
} from '../../common/search/aggs/buckets/shard_delay';
import { aggShardDelay } from '../../common/search/aggs/buckets/shard_delay_fn';
import { ConfigSchema } from '../../config';
import { NowProviderInternalContract } from '../now_provider';
import { NowProviderInternalContract } from '../../common/now_provider';
import { DataPublicPluginStart, DataStartDependencies } from '../types';
import { AggsService } from './aggs';
import { createUsageCollector, SearchUsageCollector } from './collectors';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ import { ISessionService, SessionService } from './session_service';
import { BehaviorSubject } from 'rxjs';
import { fakeSchedulers } from 'rxjs-marbles/jest';
import { SearchSessionState } from './search_session_state';
import { NowProviderInternalContract } from '../../now_provider';
import { NowProviderInternalContract } from '../../../common/now_provider';
import { coreMock } from '@kbn/core/public/mocks';
import { createNowProviderMock } from '../../now_provider/mocks';
import { createNowProviderMock } from '../../../common/now_provider/mocks';
import { SEARCH_SESSIONS_MANAGEMENT_ID } from './constants';
import { getSessionsClientMock } from './mocks';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import { first, take, toArray } from 'rxjs/operators';
import { getSessionsClientMock } from './mocks';
import { BehaviorSubject } from 'rxjs';
import { SearchSessionState } from './search_session_state';
import { createNowProviderMock } from '../../now_provider/mocks';
import { NowProviderInternalContract } from '../../now_provider';
import { createNowProviderMock } from '../../../common/now_provider/mocks';
import { NowProviderInternalContract } from '../../../common/now_provider';
import { SEARCH_SESSIONS_MANAGEMENT_ID } from './constants';
import type { ISessionsClient, SearchSessionSavedObject } from './sessions_client';
import { CoreStart } from '@kbn/core/public';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ import {
} from './search_session_state';
import { ISessionsClient } from './sessions_client';
import { ISearchOptions } from '../../../common';
import { NowProviderInternalContract } from '../../now_provider';
import { NowProviderInternalContract } from '../../../common/now_provider';
import { SEARCH_SESSIONS_MANAGEMENT_ID } from './constants';
import { formatSessionName } from './lib/session_name_formatter';

Expand Down
2 changes: 1 addition & 1 deletion src/plugins/data/public/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import {
import type { ISearchSetup, ISearchStart } from './search';
import { QuerySetup, QueryStart } from './query';
import { DataViewsContract } from './data_views';
import { NowProviderPublicContract } from './now_provider';
import { NowProviderPublicContract } from '../common/now_provider';

export interface DataSetupDependencies {
bfetch: BfetchPublicSetup;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,13 @@

import type { ExpressionFunctionDefinition } from '@kbn/expressions-plugin/common';
import { i18n } from '@kbn/i18n';
import { IndexPatternExpressionType } from '@kbn/data-views-plugin/common';
import {
DataViewPersistableStateService,
IndexPatternExpressionType,
} from '@kbn/data-views-plugin/common';
import { EventAnnotationGroupConfig } from '@kbn/event-annotation-common';
import type { EventAnnotationOutput } from '../types';
import { EventAnnotationGroupSavedObject } from '../content_management';

export interface EventAnnotationGroupOutput {
type: 'event_annotation_group';
Expand Down Expand Up @@ -79,3 +84,26 @@ export function eventAnnotationGroup(): ExpressionFunctionDefinition<
},
};
}

export const mapSavedObjectToGroupConfig = (
savedObject: EventAnnotationGroupSavedObject
): EventAnnotationGroupConfig => {
const adHocDataViewSpec = savedObject.attributes.dataViewSpec
? DataViewPersistableStateService.inject(
savedObject.attributes.dataViewSpec,
savedObject.references
)
: undefined;

return {
title: savedObject.attributes.title,
description: savedObject.attributes.description,
tags: savedObject.references.filter((ref) => ref.type === 'tag').map(({ id }) => id),
ignoreGlobalFilters: savedObject.attributes.ignoreGlobalFilters,
indexPatternId: adHocDataViewSpec
? adHocDataViewSpec.id!
: savedObject.references.find((ref) => ref.type === 'index-pattern')?.id!,
annotations: savedObject.attributes.annotations,
dataViewSpec: adHocDataViewSpec,
};
};
2 changes: 1 addition & 1 deletion src/plugins/event_annotation/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export type {
} from './query_point_event_annotation/types';
export { manualPointEventAnnotation, manualRangeEventAnnotation } from './manual_event_annotation';
export { queryPointEventAnnotation } from './query_point_event_annotation';
export { eventAnnotationGroup } from './event_annotation_group';
export { eventAnnotationGroup, mapSavedObjectToGroupConfig } from './event_annotation_group';
export type { EventAnnotationGroupArgs } from './event_annotation_group';

export type { FetchEventAnnotationsArgs } from './fetch_event_annotations/types';
Expand Down
Loading