diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.adapters.data.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.adapters.data.md
new file mode 100644
index 0000000000000..0ddbcb3546d1e
--- /dev/null
+++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.adapters.data.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [Adapters](./kibana-plugin-plugins-embeddable-public.adapters.md) > [data](./kibana-plugin-plugins-embeddable-public.adapters.data.md)
+
+## Adapters.data property
+
+Signature:
+
+```typescript
+data?: DataAdapter;
+```
diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.adapters.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.adapters.md
index 9635b36cdf05a..47484dc79d88c 100644
--- a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.adapters.md
+++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.adapters.md
@@ -11,3 +11,11 @@ The interface that the adapters used to open an inspector have to fullfill.
```typescript
export interface Adapters
```
+
+## Properties
+
+| Property | Type | Description |
+| --- | --- | --- |
+| [data](./kibana-plugin-plugins-embeddable-public.adapters.data.md) | DataAdapter
| |
+| [requests](./kibana-plugin-plugins-embeddable-public.adapters.requests.md) | RequestAdapter
| |
+
diff --git a/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.adapters.requests.md b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.adapters.requests.md
new file mode 100644
index 0000000000000..2954ad86138ff
--- /dev/null
+++ b/docs/development/plugins/embeddable/public/kibana-plugin-plugins-embeddable-public.adapters.requests.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-embeddable-public](./kibana-plugin-plugins-embeddable-public.md) > [Adapters](./kibana-plugin-plugins-embeddable-public.adapters.md) > [requests](./kibana-plugin-plugins-embeddable-public.adapters.requests.md)
+
+## Adapters.requests property
+
+Signature:
+
+```typescript
+requests?: RequestAdapter;
+```
diff --git a/src/plugins/data/common/search/aggs/agg_type.ts b/src/plugins/data/common/search/aggs/agg_type.ts
index 3ffac0c12eb22..4f4a593764b1e 100644
--- a/src/plugins/data/common/search/aggs/agg_type.ts
+++ b/src/plugins/data/common/search/aggs/agg_type.ts
@@ -54,7 +54,7 @@ export interface AggTypeConfig<
aggConfigs: IAggConfigs,
aggConfig: TAggConfig,
searchSource: ISearchSource,
- inspectorRequestAdapter: RequestAdapter,
+ inspectorRequestAdapter?: RequestAdapter,
abortSignal?: AbortSignal
) => Promise;
getSerializedFormat?: (agg: TAggConfig) => SerializedFieldFormat;
@@ -189,7 +189,7 @@ export class AggType<
aggConfigs: IAggConfigs,
aggConfig: TAggConfig,
searchSource: ISearchSource,
- inspectorRequestAdapter: RequestAdapter,
+ inspectorRequestAdapter?: RequestAdapter,
abortSignal?: AbortSignal
) => Promise;
/**
diff --git a/src/plugins/data/common/search/aggs/buckets/terms.ts b/src/plugins/data/common/search/aggs/buckets/terms.ts
index 3d543e6c5f574..ac65e7fa813b3 100644
--- a/src/plugins/data/common/search/aggs/buckets/terms.ts
+++ b/src/plugins/data/common/search/aggs/buckets/terms.ts
@@ -19,6 +19,7 @@
import { noop } from 'lodash';
import { i18n } from '@kbn/i18n';
+import type { RequestAdapter } from 'src/plugins/inspector/common';
import { BucketAggType, IBucketAggConfig } from './bucket_agg_type';
import { BUCKET_TYPES } from './bucket_agg_types';
@@ -111,27 +112,32 @@ export const getTermsBucketAgg = () =>
nestedSearchSource.setField('aggs', filterAgg);
- const request = inspectorRequestAdapter.start(
- i18n.translate('data.search.aggs.buckets.terms.otherBucketTitle', {
- defaultMessage: 'Other bucket',
- }),
- {
- description: i18n.translate('data.search.aggs.buckets.terms.otherBucketDescription', {
- defaultMessage:
- 'This request counts the number of documents that fall ' +
- 'outside the criterion of the data buckets.',
+ let request: ReturnType | undefined;
+ if (inspectorRequestAdapter) {
+ request = inspectorRequestAdapter.start(
+ i18n.translate('data.search.aggs.buckets.terms.otherBucketTitle', {
+ defaultMessage: 'Other bucket',
}),
- }
- );
- nestedSearchSource.getSearchRequestBody().then((body) => {
- request.json(body);
- });
- request.stats(getRequestInspectorStats(nestedSearchSource));
+ {
+ description: i18n.translate('data.search.aggs.buckets.terms.otherBucketDescription', {
+ defaultMessage:
+ 'This request counts the number of documents that fall ' +
+ 'outside the criterion of the data buckets.',
+ }),
+ }
+ );
+ nestedSearchSource.getSearchRequestBody().then((body) => {
+ request!.json(body);
+ });
+ request.stats(getRequestInspectorStats(nestedSearchSource));
+ }
const response = await nestedSearchSource.fetch({ abortSignal });
- request
- .stats(getResponseInspectorStats(response, nestedSearchSource))
- .ok({ json: response });
+ if (request) {
+ request
+ .stats(getResponseInspectorStats(response, nestedSearchSource))
+ .ok({ json: response });
+ }
resp = mergeOtherBucketAggResponse(aggConfigs, resp, response, aggConfig, filterAgg());
}
if (aggConfig.params.missingBucket) {
diff --git a/src/plugins/data/public/autocomplete/autocomplete_service.ts b/src/plugins/data/public/autocomplete/autocomplete_service.ts
index 2136a405baad6..5e9aede0760fe 100644
--- a/src/plugins/data/public/autocomplete/autocomplete_service.ts
+++ b/src/plugins/data/public/autocomplete/autocomplete_service.ts
@@ -18,6 +18,7 @@
*/
import { CoreSetup, PluginInitializerContext } from 'src/core/public';
+import { TimefilterSetup } from '../query';
import { QuerySuggestionGetFn } from './providers/query_suggestion_provider';
import {
getEmptyValueSuggestions,
@@ -57,9 +58,9 @@ export class AutocompleteService {
private hasQuerySuggestions = (language: string) => this.querySuggestionProviders.has(language);
/** @public **/
- public setup(core: CoreSetup) {
+ public setup(core: CoreSetup, { timefilter }: { timefilter: TimefilterSetup }) {
this.getValueSuggestions = this.autocompleteConfig.valueSuggestions.enabled
- ? setupValueSuggestionProvider(core)
+ ? setupValueSuggestionProvider(core, { timefilter })
: getEmptyValueSuggestions;
return {
diff --git a/src/plugins/data/public/autocomplete/providers/value_suggestion_provider.test.ts b/src/plugins/data/public/autocomplete/providers/value_suggestion_provider.test.ts
index 0ef5b7db958e4..4e1745ffcabb2 100644
--- a/src/plugins/data/public/autocomplete/providers/value_suggestion_provider.test.ts
+++ b/src/plugins/data/public/autocomplete/providers/value_suggestion_provider.test.ts
@@ -18,29 +18,10 @@
*/
import { stubIndexPattern, stubFields } from '../../stubs';
+import { TimefilterSetup } from '../../query';
import { setupValueSuggestionProvider, ValueSuggestionsGetFn } from './value_suggestion_provider';
import { IUiSettingsClient, CoreSetup } from 'kibana/public';
-jest.mock('../../services', () => ({
- getQueryService: () => ({
- timefilter: {
- timefilter: {
- createFilter: () => {
- return {
- time: 'fake',
- };
- },
- getTime: () => {
- return {
- to: 'now',
- from: 'now-15m',
- };
- },
- },
- },
- }),
-}));
-
describe('FieldSuggestions', () => {
let getValueSuggestions: ValueSuggestionsGetFn;
let http: any;
@@ -50,7 +31,23 @@ describe('FieldSuggestions', () => {
const uiSettings = { get: (key: string) => shouldSuggestValues } as IUiSettingsClient;
http = { fetch: jest.fn() };
- getValueSuggestions = setupValueSuggestionProvider({ http, uiSettings } as CoreSetup);
+ getValueSuggestions = setupValueSuggestionProvider({ http, uiSettings } as CoreSetup, {
+ timefilter: ({
+ timefilter: {
+ createFilter: () => {
+ return {
+ time: 'fake',
+ };
+ },
+ getTime: () => {
+ return {
+ to: 'now',
+ from: 'now-15m',
+ };
+ },
+ },
+ } as unknown) as TimefilterSetup,
+ });
});
describe('with value suggestions disabled', () => {
diff --git a/src/plugins/data/public/autocomplete/providers/value_suggestion_provider.ts b/src/plugins/data/public/autocomplete/providers/value_suggestion_provider.ts
index fe9f939a0261d..ee92fce02dda5 100644
--- a/src/plugins/data/public/autocomplete/providers/value_suggestion_provider.ts
+++ b/src/plugins/data/public/autocomplete/providers/value_suggestion_provider.ts
@@ -21,7 +21,7 @@ import dateMath from '@elastic/datemath';
import { memoize } from 'lodash';
import { CoreSetup } from 'src/core/public';
import { IIndexPattern, IFieldType, UI_SETTINGS, buildQueryFromFilters } from '../../../common';
-import { getQueryService } from '../../services';
+import { TimefilterSetup } from '../../query';
function resolver(title: string, field: IFieldType, query: string, filters: any[]) {
// Only cache results for a minute
@@ -40,8 +40,10 @@ interface ValueSuggestionsGetFnArgs {
signal?: AbortSignal;
}
-const getAutocompleteTimefilter = (indexPattern: IIndexPattern) => {
- const { timefilter } = getQueryService().timefilter;
+const getAutocompleteTimefilter = (
+ { timefilter }: TimefilterSetup,
+ indexPattern: IIndexPattern
+) => {
const timeRange = timefilter.getTime();
// Use a rounded timerange so that memoizing works properly
@@ -54,7 +56,10 @@ const getAutocompleteTimefilter = (indexPattern: IIndexPattern) => {
export const getEmptyValueSuggestions = (() => Promise.resolve([])) as ValueSuggestionsGetFn;
-export const setupValueSuggestionProvider = (core: CoreSetup): ValueSuggestionsGetFn => {
+export const setupValueSuggestionProvider = (
+ core: CoreSetup,
+ { timefilter }: { timefilter: TimefilterSetup }
+): ValueSuggestionsGetFn => {
const requestSuggestions = memoize(
(index: string, field: IFieldType, query: string, filters: any = [], signal?: AbortSignal) =>
core.http.fetch(`/api/kibana/suggestions/values/${index}`, {
@@ -86,7 +91,9 @@ export const setupValueSuggestionProvider = (core: CoreSetup): ValueSuggestionsG
return [];
}
- const timeFilter = useTimeRange ? getAutocompleteTimefilter(indexPattern) : undefined;
+ const timeFilter = useTimeRange
+ ? getAutocompleteTimefilter(timefilter, indexPattern)
+ : undefined;
const filterQuery = timeFilter ? buildQueryFromFilters([timeFilter], indexPattern).filter : [];
const filters = [...(boolFilter ? boolFilter : []), ...filterQuery];
return await requestSuggestions(title, field, query, filters, signal);
diff --git a/src/plugins/data/public/index_patterns/index_pattern.stub.ts b/src/plugins/data/public/index_patterns/index_pattern.stub.ts
index e5c6c008e3e28..804f0d7d89225 100644
--- a/src/plugins/data/public/index_patterns/index_pattern.stub.ts
+++ b/src/plugins/data/public/index_patterns/index_pattern.stub.ts
@@ -20,20 +20,9 @@
import sinon from 'sinon';
import { CoreSetup } from 'src/core/public';
-import { FieldFormat as FieldFormatImpl } from '../../common/field_formats';
import { IFieldType, FieldSpec } from '../../common/index_patterns';
-import { FieldFormatsStart } from '../field_formats';
import { IndexPattern, indexPatterns, KBN_FIELD_TYPES, fieldList } from '../';
import { getFieldFormatsRegistry } from '../test_utils';
-import { setFieldFormats } from '../services';
-
-setFieldFormats(({
- getDefaultInstance: () =>
- ({
- getConverterFor: () => (value: any) => value,
- convert: (value: any) => JSON.stringify(value),
- } as FieldFormatImpl),
-} as unknown) as FieldFormatsStart);
export function getStubIndexPattern(
pattern: string,
diff --git a/src/plugins/data/public/plugin.ts b/src/plugins/data/public/plugin.ts
index 601b5f0ccda79..4334774300698 100644
--- a/src/plugins/data/public/plugin.ts
+++ b/src/plugins/data/public/plugin.ts
@@ -42,16 +42,14 @@ import {
UiSettingsPublicToCommon,
} from './index_patterns';
import {
- setFieldFormats,
setIndexPatterns,
setNotifications,
setOverlays,
- setQueryService,
setSearchService,
setUiSettings,
} from './services';
import { createSearchBar } from './ui/search_bar/create_search_bar';
-import { esaggs } from './search/expressions';
+import { getEsaggs } from './search/expressions';
import {
SELECT_RANGE_TRIGGER,
VALUE_CLICK_TRIGGER,
@@ -112,8 +110,22 @@ export class DataPublicPlugin
): DataPublicPluginSetup {
const startServices = createStartServicesGetter(core.getStartServices);
- expressions.registerFunction(esaggs);
expressions.registerFunction(indexPatternLoad);
+ expressions.registerFunction(
+ getEsaggs({
+ getStartDependencies: async () => {
+ const [, , self] = await core.getStartServices();
+ const { fieldFormats, indexPatterns, query, search } = self;
+ return {
+ addFilters: query.filterManager.addFilters.bind(query.filterManager),
+ aggs: search.aggs,
+ deserializeFieldFormat: fieldFormats.deserialize.bind(fieldFormats),
+ indexPatterns,
+ searchSource: search.searchSource,
+ };
+ },
+ })
+ );
this.usageCollection = usageCollection;
@@ -146,7 +158,7 @@ export class DataPublicPlugin
});
return {
- autocomplete: this.autocomplete.setup(core),
+ autocomplete: this.autocomplete.setup(core, { timefilter: queryService.timefilter }),
search: searchService,
fieldFormats: this.fieldFormatsService.setup(core),
query: queryService,
@@ -163,7 +175,6 @@ export class DataPublicPlugin
setUiSettings(uiSettings);
const fieldFormats = this.fieldFormatsService.start();
- setFieldFormats(fieldFormats);
const indexPatterns = new IndexPatternsService({
uiSettings: new UiSettingsPublicToCommon(uiSettings),
@@ -191,7 +202,6 @@ export class DataPublicPlugin
savedObjectsClient: savedObjects.client,
uiSettings,
});
- setQueryService(query);
const search = this.searchService.start(core, { fieldFormats, indexPatterns });
setSearchService(search);
diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md
index 029bb85b86c36..6c526ab613d76 100644
--- a/src/plugins/data/public/public.api.md
+++ b/src/plugins/data/public/public.api.md
@@ -27,6 +27,7 @@ import { EuiButtonEmptyProps } from '@elastic/eui';
import { EuiComboBoxProps } from '@elastic/eui';
import { EuiConfirmModalProps } from '@elastic/eui';
import { EuiGlobalToastListToast } from '@elastic/eui';
+import { EventEmitter } from 'events';
import { ExclusiveUnion } from '@elastic/eui';
import { ExecutionContext } from 'src/plugins/expressions/common';
import { ExpressionAstFunction } from 'src/plugins/expressions/common';
@@ -66,7 +67,7 @@ import * as React_2 from 'react';
import { RecursiveReadonly } from '@kbn/utility-types';
import { Reporter } from '@kbn/analytics';
import { RequestAdapter } from 'src/plugins/inspector/common';
-import { RequestStatistics } from 'src/plugins/inspector/common';
+import { RequestStatistics as RequestStatistics_2 } from 'src/plugins/inspector/common';
import { Required } from '@kbn/utility-types';
import * as Rx from 'rxjs';
import { SavedObject } from 'src/core/server';
diff --git a/src/plugins/data/public/search/expressions/esaggs.ts b/src/plugins/data/public/search/expressions/esaggs.ts
deleted file mode 100644
index 3932484801fa8..0000000000000
--- a/src/plugins/data/public/search/expressions/esaggs.ts
+++ /dev/null
@@ -1,323 +0,0 @@
-/*
- * Licensed to Elasticsearch B.V. under one or more contributor
- * license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright
- * ownership. Elasticsearch B.V. licenses this file to you under
- * the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import { get, hasIn } from 'lodash';
-import { i18n } from '@kbn/i18n';
-import { Datatable, DatatableColumn } from 'src/plugins/expressions/public';
-import { PersistedState } from '../../../../../plugins/visualizations/public';
-import { Adapters } from '../../../../../plugins/inspector/public';
-
-import {
- calculateBounds,
- EsaggsExpressionFunctionDefinition,
- Filter,
- getTime,
- IIndexPattern,
- isRangeFilter,
- Query,
- TimeRange,
-} from '../../../common';
-import {
- getRequestInspectorStats,
- getResponseInspectorStats,
- IAggConfigs,
- ISearchSource,
- tabifyAggResponse,
-} from '../../../common/search';
-
-import { FilterManager } from '../../query';
-import {
- getFieldFormats,
- getIndexPatterns,
- getQueryService,
- getSearchService,
-} from '../../services';
-import { buildTabularInspectorData } from './build_tabular_inspector_data';
-
-export interface RequestHandlerParams {
- searchSource: ISearchSource;
- aggs: IAggConfigs;
- timeRange?: TimeRange;
- timeFields?: string[];
- indexPattern?: IIndexPattern;
- query?: Query;
- filters?: Filter[];
- filterManager: FilterManager;
- uiState?: PersistedState;
- partialRows?: boolean;
- inspectorAdapters: Adapters;
- metricsAtAllLevels?: boolean;
- visParams?: any;
- abortSignal?: AbortSignal;
- searchSessionId?: string;
-}
-
-const name = 'esaggs';
-
-const handleCourierRequest = async ({
- searchSource,
- aggs,
- timeRange,
- timeFields,
- indexPattern,
- query,
- filters,
- partialRows,
- metricsAtAllLevels,
- inspectorAdapters,
- filterManager,
- abortSignal,
- searchSessionId,
-}: RequestHandlerParams) => {
- // Create a new search source that inherits the original search source
- // but has the appropriate timeRange applied via a filter.
- // This is a temporary solution until we properly pass down all required
- // information for the request to the request handler (https://github.com/elastic/kibana/issues/16641).
- // Using callParentStartHandlers: true we make sure, that the parent searchSource
- // onSearchRequestStart will be called properly even though we use an inherited
- // search source.
- const timeFilterSearchSource = searchSource.createChild({ callParentStartHandlers: true });
- const requestSearchSource = timeFilterSearchSource.createChild({ callParentStartHandlers: true });
-
- aggs.setTimeRange(timeRange as TimeRange);
-
- // For now we need to mirror the history of the passed search source, since
- // the request inspector wouldn't work otherwise.
- Object.defineProperty(requestSearchSource, 'history', {
- get() {
- return searchSource.history;
- },
- set(history) {
- return (searchSource.history = history);
- },
- });
-
- requestSearchSource.setField('aggs', function () {
- return aggs.toDsl(metricsAtAllLevels);
- });
-
- requestSearchSource.onRequestStart((paramSearchSource, options) => {
- return aggs.onSearchRequestStart(paramSearchSource, options);
- });
-
- // If timeFields have been specified, use the specified ones, otherwise use primary time field of index
- // pattern if it's available.
- const defaultTimeField = indexPattern?.getTimeField?.();
- const defaultTimeFields = defaultTimeField ? [defaultTimeField.name] : [];
- const allTimeFields = timeFields && timeFields.length > 0 ? timeFields : defaultTimeFields;
-
- // If a timeRange has been specified and we had at least one timeField available, create range
- // filters for that those time fields
- if (timeRange && allTimeFields.length > 0) {
- timeFilterSearchSource.setField('filter', () => {
- return allTimeFields
- .map((fieldName) => getTime(indexPattern, timeRange, { fieldName }))
- .filter(isRangeFilter);
- });
- }
-
- requestSearchSource.setField('filter', filters);
- requestSearchSource.setField('query', query);
-
- inspectorAdapters.requests.reset();
- const request = inspectorAdapters.requests.start(
- i18n.translate('data.functions.esaggs.inspector.dataRequest.title', {
- defaultMessage: 'Data',
- }),
- {
- description: i18n.translate('data.functions.esaggs.inspector.dataRequest.description', {
- defaultMessage:
- 'This request queries Elasticsearch to fetch the data for the visualization.',
- }),
- searchSessionId,
- }
- );
- request.stats(getRequestInspectorStats(requestSearchSource));
-
- try {
- const response = await requestSearchSource.fetch({
- abortSignal,
- sessionId: searchSessionId,
- });
-
- request.stats(getResponseInspectorStats(response, searchSource)).ok({ json: response });
-
- (searchSource as any).rawResponse = response;
- } catch (e) {
- // Log any error during request to the inspector
- request.error({ json: e });
- throw e;
- } finally {
- // Add the request body no matter if things went fine or not
- requestSearchSource.getSearchRequestBody().then((req: unknown) => {
- request.json(req);
- });
- }
-
- // Note that rawResponse is not deeply cloned here, so downstream applications using courier
- // must take care not to mutate it, or it could have unintended side effects, e.g. displaying
- // response data incorrectly in the inspector.
- let resp = (searchSource as any).rawResponse;
- for (const agg of aggs.aggs) {
- if (hasIn(agg, 'type.postFlightRequest')) {
- resp = await agg.type.postFlightRequest(
- resp,
- aggs,
- agg,
- requestSearchSource,
- inspectorAdapters.requests,
- abortSignal
- );
- }
- }
-
- (searchSource as any).finalResponse = resp;
-
- const parsedTimeRange = timeRange ? calculateBounds(timeRange) : null;
- const tabifyParams = {
- metricsAtAllLevels,
- partialRows,
- timeRange: parsedTimeRange
- ? { from: parsedTimeRange.min, to: parsedTimeRange.max, timeFields: allTimeFields }
- : undefined,
- };
-
- const response = tabifyAggResponse(aggs, (searchSource as any).finalResponse, tabifyParams);
-
- (searchSource as any).tabifiedResponse = response;
-
- inspectorAdapters.data.setTabularLoader(
- () =>
- buildTabularInspectorData((searchSource as any).tabifiedResponse, {
- queryFilter: filterManager,
- deserializeFieldFormat: getFieldFormats().deserialize,
- }),
- { returnsFormattedValues: true }
- );
-
- return response;
-};
-
-export const esaggs = (): EsaggsExpressionFunctionDefinition => ({
- name,
- type: 'datatable',
- inputTypes: ['kibana_context', 'null'],
- help: i18n.translate('data.functions.esaggs.help', {
- defaultMessage: 'Run AggConfig aggregation',
- }),
- args: {
- index: {
- types: ['string'],
- help: '',
- },
- metricsAtAllLevels: {
- types: ['boolean'],
- default: false,
- help: '',
- },
- partialRows: {
- types: ['boolean'],
- default: false,
- help: '',
- },
- includeFormatHints: {
- types: ['boolean'],
- default: false,
- help: '',
- },
- aggConfigs: {
- types: ['string'],
- default: '""',
- help: '',
- },
- timeFields: {
- types: ['string'],
- help: '',
- multi: true,
- },
- },
- async fn(input, args, { inspectorAdapters, abortSignal, getSearchSessionId }) {
- const indexPatterns = getIndexPatterns();
- const { filterManager } = getQueryService();
- const searchService = getSearchService();
-
- const aggConfigsState = JSON.parse(args.aggConfigs);
- const indexPattern = await indexPatterns.get(args.index);
- const aggs = searchService.aggs.createAggConfigs(indexPattern, aggConfigsState);
-
- // we should move searchSource creation inside courier request handler
- const searchSource = await searchService.searchSource.create();
-
- searchSource.setField('index', indexPattern);
- searchSource.setField('size', 0);
-
- const resolvedTimeRange = input?.timeRange && calculateBounds(input.timeRange);
-
- const response = await handleCourierRequest({
- searchSource,
- aggs,
- indexPattern,
- timeRange: get(input, 'timeRange', undefined),
- query: get(input, 'query', undefined) as any,
- filters: get(input, 'filters', undefined),
- timeFields: args.timeFields,
- metricsAtAllLevels: args.metricsAtAllLevels,
- partialRows: args.partialRows,
- inspectorAdapters: inspectorAdapters as Adapters,
- filterManager,
- abortSignal: (abortSignal as unknown) as AbortSignal,
- searchSessionId: getSearchSessionId(),
- });
-
- const table: Datatable = {
- type: 'datatable',
- rows: response.rows,
- columns: response.columns.map((column) => {
- const cleanedColumn: DatatableColumn = {
- id: column.id,
- name: column.name,
- meta: {
- type: column.aggConfig.params.field?.type || 'number',
- field: column.aggConfig.params.field?.name,
- index: indexPattern.title,
- params: column.aggConfig.toSerializedFieldFormat(),
- source: 'esaggs',
- sourceParams: {
- indexPatternId: indexPattern.id,
- appliedTimeRange:
- column.aggConfig.params.field?.name &&
- input?.timeRange &&
- args.timeFields &&
- args.timeFields.includes(column.aggConfig.params.field?.name)
- ? {
- from: resolvedTimeRange?.min?.toISOString(),
- to: resolvedTimeRange?.max?.toISOString(),
- }
- : undefined,
- ...column.aggConfig.serialize(),
- },
- },
- };
- return cleanedColumn;
- }),
- };
-
- return table;
- },
-});
diff --git a/src/plugins/data/public/search/expressions/build_tabular_inspector_data.ts b/src/plugins/data/public/search/expressions/esaggs/build_tabular_inspector_data.ts
similarity index 78%
rename from src/plugins/data/public/search/expressions/build_tabular_inspector_data.ts
rename to src/plugins/data/public/search/expressions/esaggs/build_tabular_inspector_data.ts
index 7eff6f25fd828..79dedf4131764 100644
--- a/src/plugins/data/public/search/expressions/build_tabular_inspector_data.ts
+++ b/src/plugins/data/public/search/expressions/esaggs/build_tabular_inspector_data.ts
@@ -18,35 +18,41 @@
*/
import { set } from '@elastic/safer-lodash-set';
-import { FormattedData } from '../../../../../plugins/inspector/public';
-import { TabbedTable } from '../../../common';
-import { FormatFactory } from '../../../common/field_formats/utils';
-import { createFilter } from './create_filter';
+import {
+ FormattedData,
+ TabularData,
+ TabularDataValue,
+} from '../../../../../../plugins/inspector/common';
+import { Filter, TabbedTable } from '../../../../common';
+import { FormatFactory } from '../../../../common/field_formats/utils';
+import { createFilter } from '../create_filter';
/**
- * @deprecated
+ * Type borrowed from the client-side FilterManager['addFilters'].
*
- * Do not use this function.
- *
- * @todo This function is used only by Courier. Courier will
- * soon be removed, and this function will be deleted, too. If Courier is not removed,
- * move this function inside Courier.
- *
- * ---
+ * We need to use a custom type to make this isomorphic since FilterManager
+ * doesn't exist on the server.
*
+ * @internal
+ */
+export type AddFilters = (filters: Filter[] | Filter, pinFilterStatus?: boolean) => void;
+
+/**
* This function builds tabular data from the response and attaches it to the
* inspector. It will only be called when the data view in the inspector is opened.
+ *
+ * @internal
*/
export async function buildTabularInspectorData(
table: TabbedTable,
{
- queryFilter,
+ addFilters,
deserializeFieldFormat,
}: {
- queryFilter: { addFilters: (filter: any) => void };
+ addFilters?: AddFilters;
deserializeFieldFormat: FormatFactory;
}
-) {
+): Promise {
const aggConfigs = table.columns.map((column) => column.aggConfig);
const rows = table.rows.map((row) => {
return table.columns.reduce>((prev, cur, colIndex) => {
@@ -74,20 +80,22 @@ export async function buildTabularInspectorData(
name: col.name,
field: `col-${colIndex}-${col.aggConfig.id}`,
filter:
+ addFilters &&
isCellContentFilterable &&
- ((value: { raw: unknown }) => {
+ ((value: TabularDataValue) => {
const rowIndex = rows.findIndex(
(row) => row[`col-${colIndex}-${col.aggConfig.id}`].raw === value.raw
);
const filter = createFilter(aggConfigs, table, colIndex, rowIndex, value.raw);
if (filter) {
- queryFilter.addFilters(filter);
+ addFilters(filter);
}
}),
filterOut:
+ addFilters &&
isCellContentFilterable &&
- ((value: { raw: unknown }) => {
+ ((value: TabularDataValue) => {
const rowIndex = rows.findIndex(
(row) => row[`col-${colIndex}-${col.aggConfig.id}`].raw === value.raw
);
@@ -101,7 +109,7 @@ export async function buildTabularInspectorData(
} else {
set(filter, 'meta.negate', notOther && notMissing);
}
- queryFilter.addFilters(filter);
+ addFilters(filter);
}
}),
};
diff --git a/src/plugins/data/public/search/expressions/esaggs/esaggs_fn.ts b/src/plugins/data/public/search/expressions/esaggs/esaggs_fn.ts
new file mode 100644
index 0000000000000..ce3bd9bdaee76
--- /dev/null
+++ b/src/plugins/data/public/search/expressions/esaggs/esaggs_fn.ts
@@ -0,0 +1,155 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { get } from 'lodash';
+import { i18n } from '@kbn/i18n';
+
+import { Datatable, DatatableColumn } from 'src/plugins/expressions/common';
+import { Adapters } from 'src/plugins/inspector/common';
+
+import { calculateBounds, EsaggsExpressionFunctionDefinition } from '../../../../common';
+import { FormatFactory } from '../../../../common/field_formats/utils';
+import { IndexPatternsContract } from '../../../../common/index_patterns/index_patterns';
+import { ISearchStartSearchSource, AggsStart } from '../../../../common/search';
+
+import { AddFilters } from './build_tabular_inspector_data';
+import { handleRequest } from './request_handler';
+
+const name = 'esaggs';
+
+interface StartDependencies {
+ addFilters: AddFilters;
+ aggs: AggsStart;
+ deserializeFieldFormat: FormatFactory;
+ indexPatterns: IndexPatternsContract;
+ searchSource: ISearchStartSearchSource;
+}
+
+export function getEsaggs({
+ getStartDependencies,
+}: {
+ getStartDependencies: () => Promise;
+}) {
+ return (): EsaggsExpressionFunctionDefinition => ({
+ name,
+ type: 'datatable',
+ inputTypes: ['kibana_context', 'null'],
+ help: i18n.translate('data.functions.esaggs.help', {
+ defaultMessage: 'Run AggConfig aggregation',
+ }),
+ args: {
+ index: {
+ types: ['string'],
+ help: '',
+ },
+ metricsAtAllLevels: {
+ types: ['boolean'],
+ default: false,
+ help: '',
+ },
+ partialRows: {
+ types: ['boolean'],
+ default: false,
+ help: '',
+ },
+ includeFormatHints: {
+ types: ['boolean'],
+ default: false,
+ help: '',
+ },
+ aggConfigs: {
+ types: ['string'],
+ default: '""',
+ help: '',
+ },
+ timeFields: {
+ types: ['string'],
+ help: '',
+ multi: true,
+ },
+ },
+ async fn(input, args, { inspectorAdapters, abortSignal, getSearchSessionId }) {
+ const {
+ addFilters,
+ aggs,
+ deserializeFieldFormat,
+ indexPatterns,
+ searchSource,
+ } = await getStartDependencies();
+
+ const aggConfigsState = JSON.parse(args.aggConfigs);
+ const indexPattern = await indexPatterns.get(args.index);
+ const aggConfigs = aggs.createAggConfigs(indexPattern, aggConfigsState);
+
+ const resolvedTimeRange = input?.timeRange && calculateBounds(input.timeRange);
+
+ const response = await handleRequest({
+ abortSignal: (abortSignal as unknown) as AbortSignal,
+ addFilters,
+ aggs: aggConfigs,
+ deserializeFieldFormat,
+ filters: get(input, 'filters', undefined),
+ indexPattern,
+ inspectorAdapters: inspectorAdapters as Adapters,
+ metricsAtAllLevels: args.metricsAtAllLevels,
+ partialRows: args.partialRows,
+ query: get(input, 'query', undefined) as any,
+ searchSessionId: getSearchSessionId(),
+ searchSourceService: searchSource,
+ timeFields: args.timeFields,
+ timeRange: get(input, 'timeRange', undefined),
+ });
+
+ const table: Datatable = {
+ type: 'datatable',
+ rows: response.rows,
+ columns: response.columns.map((column) => {
+ const cleanedColumn: DatatableColumn = {
+ id: column.id,
+ name: column.name,
+ meta: {
+ type: column.aggConfig.params.field?.type || 'number',
+ field: column.aggConfig.params.field?.name,
+ index: indexPattern.title,
+ params: column.aggConfig.toSerializedFieldFormat(),
+ source: name,
+ sourceParams: {
+ indexPatternId: indexPattern.id,
+ appliedTimeRange:
+ column.aggConfig.params.field?.name &&
+ input?.timeRange &&
+ args.timeFields &&
+ args.timeFields.includes(column.aggConfig.params.field?.name)
+ ? {
+ from: resolvedTimeRange?.min?.toISOString(),
+ to: resolvedTimeRange?.max?.toISOString(),
+ }
+ : undefined,
+ ...column.aggConfig.serialize(),
+ },
+ },
+ };
+ return cleanedColumn;
+ }),
+ };
+
+ return table;
+ },
+ });
+}
diff --git a/src/plugins/data/public/search/expressions/esaggs/index.ts b/src/plugins/data/public/search/expressions/esaggs/index.ts
new file mode 100644
index 0000000000000..cbd3fb9cc5e91
--- /dev/null
+++ b/src/plugins/data/public/search/expressions/esaggs/index.ts
@@ -0,0 +1,20 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+export * from './esaggs_fn';
diff --git a/src/plugins/data/public/search/expressions/esaggs/request_handler.ts b/src/plugins/data/public/search/expressions/esaggs/request_handler.ts
new file mode 100644
index 0000000000000..93b5705b821c0
--- /dev/null
+++ b/src/plugins/data/public/search/expressions/esaggs/request_handler.ts
@@ -0,0 +1,213 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { i18n } from '@kbn/i18n';
+import { Adapters } from 'src/plugins/inspector/common';
+
+import {
+ calculateBounds,
+ Filter,
+ getTime,
+ IndexPattern,
+ isRangeFilter,
+ Query,
+ TimeRange,
+} from '../../../../common';
+import {
+ getRequestInspectorStats,
+ getResponseInspectorStats,
+ IAggConfigs,
+ ISearchStartSearchSource,
+ tabifyAggResponse,
+} from '../../../../common/search';
+import { FormatFactory } from '../../../../common/field_formats/utils';
+
+import { AddFilters, buildTabularInspectorData } from './build_tabular_inspector_data';
+
+interface RequestHandlerParams {
+ abortSignal?: AbortSignal;
+ addFilters?: AddFilters;
+ aggs: IAggConfigs;
+ deserializeFieldFormat: FormatFactory;
+ filters?: Filter[];
+ indexPattern?: IndexPattern;
+ inspectorAdapters: Adapters;
+ metricsAtAllLevels?: boolean;
+ partialRows?: boolean;
+ query?: Query;
+ searchSessionId?: string;
+ searchSourceService: ISearchStartSearchSource;
+ timeFields?: string[];
+ timeRange?: TimeRange;
+}
+
+export const handleRequest = async ({
+ abortSignal,
+ addFilters,
+ aggs,
+ deserializeFieldFormat,
+ filters,
+ indexPattern,
+ inspectorAdapters,
+ metricsAtAllLevels,
+ partialRows,
+ query,
+ searchSessionId,
+ searchSourceService,
+ timeFields,
+ timeRange,
+}: RequestHandlerParams) => {
+ const searchSource = await searchSourceService.create();
+
+ searchSource.setField('index', indexPattern);
+ searchSource.setField('size', 0);
+
+ // Create a new search source that inherits the original search source
+ // but has the appropriate timeRange applied via a filter.
+ // This is a temporary solution until we properly pass down all required
+ // information for the request to the request handler (https://github.com/elastic/kibana/issues/16641).
+ // Using callParentStartHandlers: true we make sure, that the parent searchSource
+ // onSearchRequestStart will be called properly even though we use an inherited
+ // search source.
+ const timeFilterSearchSource = searchSource.createChild({ callParentStartHandlers: true });
+ const requestSearchSource = timeFilterSearchSource.createChild({ callParentStartHandlers: true });
+
+ aggs.setTimeRange(timeRange as TimeRange);
+
+ // For now we need to mirror the history of the passed search source, since
+ // the request inspector wouldn't work otherwise.
+ Object.defineProperty(requestSearchSource, 'history', {
+ get() {
+ return searchSource.history;
+ },
+ set(history) {
+ return (searchSource.history = history);
+ },
+ });
+
+ requestSearchSource.setField('aggs', function () {
+ return aggs.toDsl(metricsAtAllLevels);
+ });
+
+ requestSearchSource.onRequestStart((paramSearchSource, options) => {
+ return aggs.onSearchRequestStart(paramSearchSource, options);
+ });
+
+ // If timeFields have been specified, use the specified ones, otherwise use primary time field of index
+ // pattern if it's available.
+ const defaultTimeField = indexPattern?.getTimeField?.();
+ const defaultTimeFields = defaultTimeField ? [defaultTimeField.name] : [];
+ const allTimeFields = timeFields && timeFields.length > 0 ? timeFields : defaultTimeFields;
+
+ // If a timeRange has been specified and we had at least one timeField available, create range
+ // filters for that those time fields
+ if (timeRange && allTimeFields.length > 0) {
+ timeFilterSearchSource.setField('filter', () => {
+ return allTimeFields
+ .map((fieldName) => getTime(indexPattern, timeRange, { fieldName }))
+ .filter(isRangeFilter);
+ });
+ }
+
+ requestSearchSource.setField('filter', filters);
+ requestSearchSource.setField('query', query);
+
+ let request;
+ if (inspectorAdapters.requests) {
+ inspectorAdapters.requests.reset();
+ request = inspectorAdapters.requests.start(
+ i18n.translate('data.functions.esaggs.inspector.dataRequest.title', {
+ defaultMessage: 'Data',
+ }),
+ {
+ description: i18n.translate('data.functions.esaggs.inspector.dataRequest.description', {
+ defaultMessage:
+ 'This request queries Elasticsearch to fetch the data for the visualization.',
+ }),
+ searchSessionId,
+ }
+ );
+ request.stats(getRequestInspectorStats(requestSearchSource));
+ }
+
+ try {
+ const response = await requestSearchSource.fetch({
+ abortSignal,
+ sessionId: searchSessionId,
+ });
+
+ if (request) {
+ request.stats(getResponseInspectorStats(response, searchSource)).ok({ json: response });
+ }
+
+ (searchSource as any).rawResponse = response;
+ } catch (e) {
+ // Log any error during request to the inspector
+ if (request) {
+ request.error({ json: e });
+ }
+ throw e;
+ } finally {
+ // Add the request body no matter if things went fine or not
+ if (request) {
+ request.json(await requestSearchSource.getSearchRequestBody());
+ }
+ }
+
+ // Note that rawResponse is not deeply cloned here, so downstream applications using courier
+ // must take care not to mutate it, or it could have unintended side effects, e.g. displaying
+ // response data incorrectly in the inspector.
+ let response = (searchSource as any).rawResponse;
+ for (const agg of aggs.aggs) {
+ if (typeof agg.type.postFlightRequest === 'function') {
+ response = await agg.type.postFlightRequest(
+ response,
+ aggs,
+ agg,
+ requestSearchSource,
+ inspectorAdapters.requests,
+ abortSignal
+ );
+ }
+ }
+
+ const parsedTimeRange = timeRange ? calculateBounds(timeRange) : null;
+ const tabifyParams = {
+ metricsAtAllLevels,
+ partialRows,
+ timeRange: parsedTimeRange
+ ? { from: parsedTimeRange.min, to: parsedTimeRange.max, timeFields: allTimeFields }
+ : undefined,
+ };
+
+ const tabifiedResponse = tabifyAggResponse(aggs, response, tabifyParams);
+
+ if (inspectorAdapters.data) {
+ inspectorAdapters.data.setTabularLoader(
+ () =>
+ buildTabularInspectorData(tabifiedResponse, {
+ addFilters,
+ deserializeFieldFormat,
+ }),
+ { returnsFormattedValues: true }
+ );
+ }
+
+ return tabifiedResponse;
+};
diff --git a/src/plugins/data/public/services.ts b/src/plugins/data/public/services.ts
index 032bce6d8d2aa..28fb4ff8b53ae 100644
--- a/src/plugins/data/public/services.ts
+++ b/src/plugins/data/public/services.ts
@@ -18,7 +18,6 @@
*/
import { NotificationsStart, CoreStart } from 'src/core/public';
-import { FieldFormatsStart } from './field_formats';
import { createGetterSetter } from '../../kibana_utils/public';
import { IndexPatternsContract } from './index_patterns';
import { DataPublicPluginStart } from './types';
@@ -31,20 +30,12 @@ export const [getUiSettings, setUiSettings] = createGetterSetter(
- 'FieldFormats'
-);
-
export const [getOverlays, setOverlays] = createGetterSetter('Overlays');
export const [getIndexPatterns, setIndexPatterns] = createGetterSetter(
'IndexPatterns'
);
-export const [getQueryService, setQueryService] = createGetterSetter<
- DataPublicPluginStart['query']
->('Query');
-
export const [getSearchService, setSearchService] = createGetterSetter<
DataPublicPluginStart['search']
>('Search');
diff --git a/src/plugins/discover/public/application/embeddable/search_embeddable.ts b/src/plugins/discover/public/application/embeddable/search_embeddable.ts
index 170078076ec6f..980e90d0acf20 100644
--- a/src/plugins/discover/public/application/embeddable/search_embeddable.ts
+++ b/src/plugins/discover/public/application/embeddable/search_embeddable.ts
@@ -84,7 +84,7 @@ export class SearchEmbeddable
private readonly savedSearch: SavedSearch;
private $rootScope: ng.IRootScopeService;
private $compile: ng.ICompileService;
- private inspectorAdaptors: Adapters;
+ private inspectorAdapters: Adapters;
private searchScope?: SearchScope;
private panelTitle: string = '';
private filtersSearchSource?: ISearchSource;
@@ -131,7 +131,7 @@ export class SearchEmbeddable
this.savedSearch = savedSearch;
this.$rootScope = $rootScope;
this.$compile = $compile;
- this.inspectorAdaptors = {
+ this.inspectorAdapters = {
requests: new RequestAdapter(),
};
this.initializeSearchScope();
@@ -150,7 +150,7 @@ export class SearchEmbeddable
}
public getInspectorAdapters() {
- return this.inspectorAdaptors;
+ return this.inspectorAdapters;
}
public getSavedSearch() {
@@ -195,7 +195,7 @@ export class SearchEmbeddable
const searchScope: SearchScope = (this.searchScope = this.$rootScope.$new());
searchScope.description = this.savedSearch.description;
- searchScope.inspectorAdapters = this.inspectorAdaptors;
+ searchScope.inspectorAdapters = this.inspectorAdapters;
const { searchSource } = this.savedSearch;
const indexPattern = (searchScope.indexPattern = searchSource.getField('index'))!;
@@ -287,7 +287,7 @@ export class SearchEmbeddable
);
// Log request to inspector
- this.inspectorAdaptors.requests.reset();
+ this.inspectorAdapters.requests!.reset();
const title = i18n.translate('discover.embeddable.inspectorRequestDataTitle', {
defaultMessage: 'Data',
});
@@ -295,7 +295,7 @@ export class SearchEmbeddable
defaultMessage: 'This request queries Elasticsearch to fetch the data for the search.',
});
- const inspectorRequest = this.inspectorAdaptors.requests.start(title, {
+ const inspectorRequest = this.inspectorAdapters.requests!.start(title, {
description,
searchSessionId,
});
diff --git a/src/plugins/embeddable/public/public.api.md b/src/plugins/embeddable/public/public.api.md
index 6a2565edf2f67..1bdfbe9d01a2f 100644
--- a/src/plugins/embeddable/public/public.api.md
+++ b/src/plugins/embeddable/public/public.api.md
@@ -28,6 +28,7 @@ import { EuiComboBoxProps } from '@elastic/eui';
import { EuiConfirmModalProps } from '@elastic/eui';
import { EuiContextMenuPanelDescriptor } from '@elastic/eui';
import { EuiGlobalToastListToast } from '@elastic/eui';
+import { EventEmitter } from 'events';
import { ExclusiveUnion } from '@elastic/eui';
import { ExpressionAstFunction } from 'src/plugins/expressions/common';
import { History } from 'history';
@@ -59,7 +60,7 @@ import { PublicMethodsOf } from '@kbn/utility-types';
import { PublicUiSettingsParams } from 'src/core/server/types';
import React from 'react';
import { RecursiveReadonly } from '@kbn/utility-types';
-import { RequestAdapter } from 'src/plugins/inspector/common';
+import { RequestAdapter as RequestAdapter_2 } from 'src/plugins/inspector/common';
import { Required } from '@kbn/utility-types';
import * as Rx from 'rxjs';
import { SavedObject as SavedObject_2 } from 'src/core/server';
@@ -100,6 +101,14 @@ export const ACTION_EDIT_PANEL = "editPanel";
export interface Adapters {
// (undocumented)
[key: string]: any;
+ // Warning: (ae-forgotten-export) The symbol "DataAdapter" needs to be exported by the entry point index.d.ts
+ //
+ // (undocumented)
+ data?: DataAdapter;
+ // Warning: (ae-forgotten-export) The symbol "RequestAdapter" needs to be exported by the entry point index.d.ts
+ //
+ // (undocumented)
+ requests?: RequestAdapter;
}
// Warning: (ae-forgotten-export) The symbol "ActionContext" needs to be exported by the entry point index.d.ts
diff --git a/src/plugins/inspector/common/adapters/data/data_adapter.ts b/src/plugins/inspector/common/adapters/data/data_adapter.ts
index 34e6c278c693f..a21aa7db39145 100644
--- a/src/plugins/inspector/common/adapters/data/data_adapter.ts
+++ b/src/plugins/inspector/common/adapters/data/data_adapter.ts
@@ -20,7 +20,7 @@
import { EventEmitter } from 'events';
import { TabularCallback, TabularHolder, TabularLoaderOptions } from './types';
-class DataAdapter extends EventEmitter {
+export class DataAdapter extends EventEmitter {
private tabular?: TabularCallback;
private tabularOptions?: TabularLoaderOptions;
@@ -38,5 +38,3 @@ class DataAdapter extends EventEmitter {
return Promise.resolve(this.tabular()).then((data) => ({ data, options }));
}
}
-
-export { DataAdapter };
diff --git a/src/plugins/inspector/common/adapters/data/data_adapters.test.ts b/src/plugins/inspector/common/adapters/data/data_adapters.test.ts
index 287024ca1b59e..7cc52807548f0 100644
--- a/src/plugins/inspector/common/adapters/data/data_adapters.test.ts
+++ b/src/plugins/inspector/common/adapters/data/data_adapters.test.ts
@@ -35,33 +35,37 @@ describe('DataAdapter', () => {
});
it('should call the provided callback and resolve with its value', async () => {
- const spy = jest.fn(() => 'foo');
+ const data = { columns: [], rows: [] };
+ const spy = jest.fn(() => data);
adapter.setTabularLoader(spy);
expect(spy).not.toBeCalled();
const result = await adapter.getTabular();
expect(spy).toBeCalled();
- expect(result.data).toBe('foo');
+ expect(result.data).toBe(data);
});
it('should pass through options specified via setTabularLoader', async () => {
- adapter.setTabularLoader(() => 'foo', { returnsFormattedValues: true });
+ const data = { columns: [], rows: [] };
+ adapter.setTabularLoader(() => data, { returnsFormattedValues: true });
const result = await adapter.getTabular();
expect(result.options).toEqual({ returnsFormattedValues: true });
});
it('should return options set when starting loading data', async () => {
- adapter.setTabularLoader(() => 'foo', { returnsFormattedValues: true });
+ const data = { columns: [], rows: [] };
+ adapter.setTabularLoader(() => data, { returnsFormattedValues: true });
const waitForResult = adapter.getTabular();
- adapter.setTabularLoader(() => 'bar', { returnsFormattedValues: false });
+ adapter.setTabularLoader(() => data, { returnsFormattedValues: false });
const result = await waitForResult;
expect(result.options).toEqual({ returnsFormattedValues: true });
});
});
it('should emit a "tabular" event when a new tabular loader is specified', () => {
+ const data = { columns: [], rows: [] };
const spy = jest.fn();
adapter.once('change', spy);
- adapter.setTabularLoader(() => 42);
+ adapter.setTabularLoader(() => data);
expect(spy).toBeCalled();
});
});
diff --git a/src/plugins/inspector/common/adapters/data/formatted_data.ts b/src/plugins/inspector/common/adapters/data/formatted_data.ts
index c752e8670aca3..08c956f27d011 100644
--- a/src/plugins/inspector/common/adapters/data/formatted_data.ts
+++ b/src/plugins/inspector/common/adapters/data/formatted_data.ts
@@ -17,8 +17,6 @@
* under the License.
*/
-class FormattedData {
+export class FormattedData {
constructor(public readonly raw: any, public readonly formatted: any) {}
}
-
-export { FormattedData };
diff --git a/src/plugins/inspector/common/adapters/data/index.ts b/src/plugins/inspector/common/adapters/data/index.ts
index 920e298ab455f..a8b1abcd8cd7e 100644
--- a/src/plugins/inspector/common/adapters/data/index.ts
+++ b/src/plugins/inspector/common/adapters/data/index.ts
@@ -17,5 +17,6 @@
* under the License.
*/
-export { FormattedData } from './formatted_data';
-export { DataAdapter } from './data_adapter';
+export * from './data_adapter';
+export * from './formatted_data';
+export * from './types';
diff --git a/src/plugins/inspector/common/adapters/data/types.ts b/src/plugins/inspector/common/adapters/data/types.ts
index 1c7b17c143eca..040724f4ae36e 100644
--- a/src/plugins/inspector/common/adapters/data/types.ts
+++ b/src/plugins/inspector/common/adapters/data/types.ts
@@ -17,8 +17,25 @@
* under the License.
*/
-// TODO: add a more specific TabularData type.
-export type TabularData = any;
+export interface TabularDataValue {
+ formatted: string;
+ raw: unknown;
+}
+
+export interface TabularDataColumn {
+ name: string;
+ field: string;
+ filter?: (value: TabularDataValue) => void;
+ filterOut?: (value: TabularDataValue) => void;
+}
+
+export type TabularDataRow = Record;
+
+export interface TabularData {
+ columns: TabularDataColumn[];
+ rows: TabularDataRow[];
+}
+
export type TabularCallback = () => TabularData | Promise;
export interface TabularHolder {
diff --git a/src/plugins/inspector/common/adapters/index.ts b/src/plugins/inspector/common/adapters/index.ts
index 1e7a44a2c60b1..0c6319a2905a8 100644
--- a/src/plugins/inspector/common/adapters/index.ts
+++ b/src/plugins/inspector/common/adapters/index.ts
@@ -17,12 +17,6 @@
* under the License.
*/
-export { Adapters } from './types';
-export { DataAdapter, FormattedData } from './data';
-export {
- RequestAdapter,
- RequestStatistic,
- RequestStatistics,
- RequestStatus,
- RequestResponder,
-} from './request';
+export * from './data';
+export * from './request';
+export * from './types';
diff --git a/src/plugins/inspector/common/adapters/request/request_adapter.ts b/src/plugins/inspector/common/adapters/request/request_adapter.ts
index af10d1b77b16d..5f5728e1cf331 100644
--- a/src/plugins/inspector/common/adapters/request/request_adapter.ts
+++ b/src/plugins/inspector/common/adapters/request/request_adapter.ts
@@ -29,7 +29,7 @@ import { Request, RequestParams, RequestStatus } from './types';
* instead it offers a generic API to log requests of any kind.
* @extends EventEmitter
*/
-class RequestAdapter extends EventEmitter {
+export class RequestAdapter extends EventEmitter {
private requests: Map;
constructor() {
@@ -78,5 +78,3 @@ class RequestAdapter extends EventEmitter {
this.emit('change');
}
}
-
-export { RequestAdapter };
diff --git a/src/plugins/inspector/common/adapters/types.ts b/src/plugins/inspector/common/adapters/types.ts
index 362c69e299c9d..b51c3e56c749f 100644
--- a/src/plugins/inspector/common/adapters/types.ts
+++ b/src/plugins/inspector/common/adapters/types.ts
@@ -17,9 +17,14 @@
* under the License.
*/
+import type { DataAdapter } from './data';
+import type { RequestAdapter } from './request';
+
/**
* The interface that the adapters used to open an inspector have to fullfill.
*/
export interface Adapters {
+ data?: DataAdapter;
+ requests?: RequestAdapter;
[key: string]: any;
}
diff --git a/src/plugins/inspector/common/index.ts b/src/plugins/inspector/common/index.ts
index 06ab36a577d98..c5755b22095dc 100644
--- a/src/plugins/inspector/common/index.ts
+++ b/src/plugins/inspector/common/index.ts
@@ -17,4 +17,17 @@
* under the License.
*/
-export * from './adapters';
+export {
+ Adapters,
+ DataAdapter,
+ FormattedData,
+ RequestAdapter,
+ RequestStatistic,
+ RequestStatistics,
+ RequestStatus,
+ RequestResponder,
+ TabularData,
+ TabularDataColumn,
+ TabularDataRow,
+ TabularDataValue,
+} from './adapters';
diff --git a/src/plugins/inspector/public/test/is_available.test.ts b/src/plugins/inspector/public/test/is_available.test.ts
index 0604129a0734a..c38d9d7a3f825 100644
--- a/src/plugins/inspector/public/test/is_available.test.ts
+++ b/src/plugins/inspector/public/test/is_available.test.ts
@@ -18,8 +18,7 @@
*/
import { inspectorPluginMock } from '../mocks';
-import { DataAdapter } from '../../common/adapters/data/data_adapter';
-import { RequestAdapter } from '../../common/adapters/request/request_adapter';
+import { DataAdapter, RequestAdapter } from '../../common/adapters';
const adapter1 = new DataAdapter();
const adapter2 = new RequestAdapter();
diff --git a/src/plugins/inspector/public/views/data/components/data_view.tsx b/src/plugins/inspector/public/views/data/components/data_view.tsx
index 100fa7787321c..324094d8f93d0 100644
--- a/src/plugins/inspector/public/views/data/components/data_view.tsx
+++ b/src/plugins/inspector/public/views/data/components/data_view.tsx
@@ -35,7 +35,7 @@ import { Adapters } from '../../../../common';
import {
TabularLoaderOptions,
TabularData,
- TabularCallback,
+ TabularHolder,
} from '../../../../common/adapters/data/types';
import { IUiSettingsClient } from '../../../../../../core/public';
import { withKibana, KibanaReactContextValue } from '../../../../../kibana_react/public';
@@ -44,7 +44,7 @@ interface DataViewComponentState {
tabularData: TabularData | null;
tabularOptions: TabularLoaderOptions;
adapters: Adapters;
- tabularPromise: TabularCallback | null;
+ tabularPromise: Promise | null;
}
interface DataViewComponentProps extends InspectorViewProps {
@@ -73,7 +73,7 @@ class DataViewComponent extends Component string;
+
export interface DataViewColumn {
name: string;
field: string;
- sortable: (item: DataViewRow) => string | number;
+ sortable: (item: TabularDataRow) => string | number;
render: DataViewColumnRender;
}
-type DataViewColumnRender = (value: string, _item: DataViewRow) => string;
-
-export interface DataViewRow {
- [fields: string]: {
- formatted: string;
- raw: any;
- };
-}
+export type DataViewRow = TabularDataRow;
diff --git a/src/plugins/inspector/public/views/requests/components/requests_view.tsx b/src/plugins/inspector/public/views/requests/components/requests_view.tsx
index 7762689daf4e6..e1879f7a6b6c8 100644
--- a/src/plugins/inspector/public/views/requests/components/requests_view.tsx
+++ b/src/plugins/inspector/public/views/requests/components/requests_view.tsx
@@ -31,7 +31,7 @@ import { RequestDetails } from './request_details';
interface RequestSelectorState {
requests: Request[];
- request: Request;
+ request: Request | null;
}
export class RequestsViewComponent extends Component {
@@ -43,9 +43,9 @@ export class RequestsViewComponent extends Component {
- const requests = this.props.adapters.requests.getRequests();
+ const requests = this.props.adapters.requests!.getRequests();
const newState = { requests } as RequestSelectorState;
- if (!requests.includes(this.state.request)) {
+ if (!this.state.request || !requests.includes(this.state.request)) {
newState.request = requests.length ? requests[0] : null;
}
this.setState(newState);
@@ -69,7 +69,7 @@ export class RequestsViewComponent extends Component
-
-
+ {this.state.request && (
+ <>
+
+
+ >
+ )}
{this.state.request && this.state.request.description && (
diff --git a/src/plugins/ui_actions/public/public.api.md b/src/plugins/ui_actions/public/public.api.md
index 3e40c94e116fb..3a14f49169e09 100644
--- a/src/plugins/ui_actions/public/public.api.md
+++ b/src/plugins/ui_actions/public/public.api.md
@@ -8,6 +8,7 @@ import { CoreSetup } from 'src/core/public';
import { CoreStart } from 'src/core/public';
import { EnvironmentMode } from '@kbn/config';
import { EuiContextMenuPanelDescriptor } from '@elastic/eui';
+import { EventEmitter } from 'events';
import { Observable } from 'rxjs';
import { PackageInfo } from '@kbn/config';
import { Plugin } from 'src/core/public';
diff --git a/x-pack/plugins/maps/public/classes/sources/es_source/es_source.ts b/x-pack/plugins/maps/public/classes/sources/es_source/es_source.ts
index bef0b8c6ea7af..103fd11263330 100644
--- a/x-pack/plugins/maps/public/classes/sources/es_source/es_source.ts
+++ b/x-pack/plugins/maps/public/classes/sources/es_source/es_source.ts
@@ -134,7 +134,7 @@ export class AbstractESSource extends AbstractVectorSource implements IESSource
destroy() {
const inspectorAdapters = this.getInspectorAdapters();
- if (inspectorAdapters) {
+ if (inspectorAdapters?.requests) {
inspectorAdapters.requests.resetRequest(this.getId());
}
}
@@ -164,7 +164,7 @@ export class AbstractESSource extends AbstractVectorSource implements IESSource
const inspectorAdapters = this.getInspectorAdapters();
let inspectorRequest: RequestResponder | undefined;
- if (inspectorAdapters) {
+ if (inspectorAdapters?.requests) {
inspectorRequest = inspectorAdapters.requests.start(requestName, {
id: requestId,
description: requestDescription,