mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[saved search] Remove saved object client from data views plugin for saved search usage (#159109)
## Summary Previously the data plugin relied on the data view plugin to load saved searches since the saved searches depend upon the data plugin and circular dependencies needed to be avoided. This is innovative and perhaps a bit crazy. What this PR does - Data view api no longer loads saved searches, removing browser saved object client usage - Moves `kibana_context` expression and getKibanaContext function from data plugin to saved search plugin since it loads saved searches - Rename data views `SavedObjectsClientCommon` to `PersistenceAPI` - this is the abstraction around saved object loading that no longer is exclusive to the saved objects api. - Adds saved search server api (plugin contract) for loading saved searches. - Functional tests on browser and server for kibana_context expression when loading saved searches --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
672c90a9c1
commit
d9d1404119
43 changed files with 504 additions and 374 deletions
|
@ -6,22 +6,15 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { isEqual, uniqBy } from 'lodash';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { ExpressionFunctionDefinition, ExecutionContext } from '@kbn/expressions-plugin/common';
|
||||
import { Adapters } from '@kbn/inspector-plugin/common';
|
||||
import { Filter, fromCombinedFilter } from '@kbn/es-query';
|
||||
import { Query, uniqFilters } from '@kbn/es-query';
|
||||
import { unboxExpressionValue } from '@kbn/expressions-plugin/common';
|
||||
import { SavedObjectReference } from '@kbn/core/types';
|
||||
import { SavedObjectsClientCommon } from '@kbn/data-views-plugin/common';
|
||||
import { ExecutionContextSearch, KibanaContext, KibanaFilter } from './kibana_context_type';
|
||||
import { KibanaQueryOutput } from './kibana_context_type';
|
||||
import { KibanaTimerangeOutput } from './timerange';
|
||||
|
||||
export interface KibanaContextStartDependencies {
|
||||
savedObjectsClient: SavedObjectsClientCommon;
|
||||
}
|
||||
import {
|
||||
KibanaTimerangeOutput,
|
||||
ExecutionContextSearch,
|
||||
KibanaContext,
|
||||
KibanaFilter,
|
||||
KibanaQueryOutput,
|
||||
} from '../..';
|
||||
|
||||
interface Arguments {
|
||||
q?: KibanaQueryOutput[] | null;
|
||||
|
@ -37,125 +30,3 @@ export type ExpressionFunctionKibanaContext = ExpressionFunctionDefinition<
|
|||
Promise<KibanaContext>,
|
||||
ExecutionContext<Adapters, ExecutionContextSearch>
|
||||
>;
|
||||
|
||||
const getParsedValue = (data: any, defaultValue: any) =>
|
||||
typeof data === 'string' && data.length ? JSON.parse(data) || defaultValue : defaultValue;
|
||||
|
||||
const mergeQueries = (first: Query | Query[] = [], second: Query | Query[]) =>
|
||||
uniqBy<Query>(
|
||||
[...(Array.isArray(first) ? first : [first]), ...(Array.isArray(second) ? second : [second])],
|
||||
(n: any) => JSON.stringify(n.query)
|
||||
);
|
||||
|
||||
export const getKibanaContextFn = (
|
||||
getStartDependencies: (
|
||||
getKibanaRequest: ExecutionContext['getKibanaRequest']
|
||||
) => Promise<KibanaContextStartDependencies>
|
||||
) => {
|
||||
const kibanaContextFunction: ExpressionFunctionKibanaContext = {
|
||||
name: 'kibana_context',
|
||||
type: 'kibana_context',
|
||||
inputTypes: ['kibana_context', 'null'],
|
||||
help: i18n.translate('data.search.functions.kibana_context.help', {
|
||||
defaultMessage: 'Updates kibana global context',
|
||||
}),
|
||||
args: {
|
||||
q: {
|
||||
types: ['kibana_query', 'null'],
|
||||
multi: true,
|
||||
aliases: ['query', '_'],
|
||||
help: i18n.translate('data.search.functions.kibana_context.q.help', {
|
||||
defaultMessage: 'Specify Kibana free form text query',
|
||||
}),
|
||||
},
|
||||
filters: {
|
||||
types: ['kibana_filter', 'null'],
|
||||
multi: true,
|
||||
help: i18n.translate('data.search.functions.kibana_context.filters.help', {
|
||||
defaultMessage: 'Specify Kibana generic filters',
|
||||
}),
|
||||
},
|
||||
timeRange: {
|
||||
types: ['timerange', 'null'],
|
||||
default: null,
|
||||
help: i18n.translate('data.search.functions.kibana_context.timeRange.help', {
|
||||
defaultMessage: 'Specify Kibana time range filter',
|
||||
}),
|
||||
},
|
||||
savedSearchId: {
|
||||
types: ['string', 'null'],
|
||||
default: null,
|
||||
help: i18n.translate('data.search.functions.kibana_context.savedSearchId.help', {
|
||||
defaultMessage: 'Specify saved search ID to be used for queries and filters',
|
||||
}),
|
||||
},
|
||||
},
|
||||
|
||||
extract(state) {
|
||||
const references: SavedObjectReference[] = [];
|
||||
if (state.savedSearchId.length && typeof state.savedSearchId[0] === 'string') {
|
||||
const refName = 'kibana_context.savedSearchId';
|
||||
references.push({
|
||||
name: refName,
|
||||
type: 'search',
|
||||
id: state.savedSearchId[0] as string,
|
||||
});
|
||||
return {
|
||||
state: {
|
||||
...state,
|
||||
savedSearchId: [refName],
|
||||
},
|
||||
references,
|
||||
};
|
||||
}
|
||||
return { state, references };
|
||||
},
|
||||
|
||||
inject(state, references) {
|
||||
const reference = references.find((r) => r.name === 'kibana_context.savedSearchId');
|
||||
if (reference) {
|
||||
state.savedSearchId[0] = reference.id;
|
||||
}
|
||||
return state;
|
||||
},
|
||||
|
||||
async fn(input, args, { getKibanaRequest }) {
|
||||
const { savedObjectsClient } = await getStartDependencies(getKibanaRequest);
|
||||
|
||||
const timeRange = args.timeRange || input?.timeRange;
|
||||
let queries = mergeQueries(input?.query, args?.q?.filter(Boolean) || []);
|
||||
const filterFromArgs = (args?.filters?.map(unboxExpressionValue) || []) as Filter[];
|
||||
|
||||
let filters = [...(input?.filters || [])];
|
||||
|
||||
if (args.savedSearchId) {
|
||||
const obj = await savedObjectsClient.getSavedSearch(args.savedSearchId);
|
||||
const search = (obj.attributes as any).kibanaSavedObjectMeta.searchSourceJSON as string;
|
||||
const { query, filter } = getParsedValue(search, {});
|
||||
|
||||
if (query) {
|
||||
queries = mergeQueries(queries, query);
|
||||
}
|
||||
if (filter) {
|
||||
filters = [...filters, ...(Array.isArray(filter) ? filter : [filter])];
|
||||
}
|
||||
}
|
||||
const uniqueArgFilters = filterFromArgs.filter(
|
||||
(argF) =>
|
||||
!filters.some((f) => {
|
||||
return isEqual(fromCombinedFilter(f).query, argF.query);
|
||||
})
|
||||
);
|
||||
|
||||
filters = [...filters, ...uniqueArgFilters];
|
||||
|
||||
return {
|
||||
type: 'kibana_context',
|
||||
query: queries,
|
||||
filters: uniqFilters(filters.filter((f: any) => !f.meta?.disabled)),
|
||||
timeRange,
|
||||
};
|
||||
},
|
||||
};
|
||||
return kibanaContextFunction;
|
||||
};
|
||||
|
|
|
@ -6,9 +6,9 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
import { Filter } from '@kbn/es-query';
|
||||
import { ExpressionValueBoxed, ExpressionValueFilter } from '@kbn/expressions-plugin/common';
|
||||
import { ExpressionValueBoxed } from '@kbn/expressions-plugin/common';
|
||||
import { Query, TimeRange } from '../../query';
|
||||
import { adaptToExpressionValueFilter, DataViewField } from '../..';
|
||||
import { DataViewField } from '../..';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
|
||||
export type ExecutionContextSearch = {
|
||||
|
@ -30,29 +30,3 @@ export type KibanaField = ExpressionValueBoxed<'kibana_field', DataViewField>;
|
|||
// TODO: These two are exported for legacy reasons - remove them eventually.
|
||||
export type KIBANA_CONTEXT_NAME = 'kibana_context';
|
||||
export type KibanaContext = ExpressionValueSearchContext;
|
||||
|
||||
export const kibanaContext = {
|
||||
name: 'kibana_context',
|
||||
from: {
|
||||
null: () => {
|
||||
return {
|
||||
type: 'kibana_context',
|
||||
};
|
||||
},
|
||||
},
|
||||
to: {
|
||||
null: () => {
|
||||
return {
|
||||
type: 'null',
|
||||
};
|
||||
},
|
||||
filter: (input: KibanaContext): ExpressionValueFilter => {
|
||||
const { filters = [] } = input;
|
||||
return {
|
||||
type: 'filter',
|
||||
filterType: 'filter',
|
||||
and: filters.map(adaptToExpressionValueFilter),
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -38,7 +38,6 @@ import {
|
|||
ipRangeFunction,
|
||||
ISearchGeneric,
|
||||
kibana,
|
||||
kibanaContext,
|
||||
kibanaFilterFunction,
|
||||
kibanaTimerangeFunction,
|
||||
kqlFunction,
|
||||
|
@ -65,7 +64,7 @@ import { DataPublicPluginStart, DataStartDependencies } from '../types';
|
|||
import { AggsService } from './aggs';
|
||||
import { createUsageCollector, SearchUsageCollector } from './collectors';
|
||||
import { getEql, getEsaggs, getEsdsl, getEssql } from './expressions';
|
||||
import { getKibanaContext } from './expressions/kibana_context';
|
||||
|
||||
import { handleWarnings } from './fetch/handle_warnings';
|
||||
import { ISearchInterceptor, SearchInterceptor } from './search_interceptor';
|
||||
import { ISessionsClient, ISessionService, SessionsClient, SessionService } from './session';
|
||||
|
@ -143,11 +142,6 @@ export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
|
|||
})
|
||||
);
|
||||
expressions.registerFunction(kibana);
|
||||
expressions.registerFunction(
|
||||
getKibanaContext({ getStartServices } as {
|
||||
getStartServices: StartServicesAccessor<DataStartDependencies, DataPublicPluginStart>;
|
||||
})
|
||||
);
|
||||
expressions.registerFunction(cidrFunction);
|
||||
expressions.registerFunction(dateRangeFunction);
|
||||
expressions.registerFunction(extendedBoundsFunction);
|
||||
|
@ -167,7 +161,6 @@ export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
|
|||
expressions.registerFunction(removeFilterFunction);
|
||||
expressions.registerFunction(selectFilterFunction);
|
||||
expressions.registerFunction(phraseFilterFunction);
|
||||
expressions.registerType(kibanaContext);
|
||||
|
||||
expressions.registerFunction(
|
||||
getEsdsl({ getStartServices } as {
|
||||
|
|
|
@ -67,7 +67,6 @@ import {
|
|||
ipRangeFunction,
|
||||
ISearchOptions,
|
||||
kibana,
|
||||
kibanaContext,
|
||||
kibanaFilterFunction,
|
||||
kibanaTimerangeFunction,
|
||||
kqlFunction,
|
||||
|
@ -94,7 +93,6 @@ import { aggShardDelay } from '../../common/search/aggs/buckets/shard_delay_fn';
|
|||
import { ConfigSchema } from '../../config';
|
||||
import { SearchSessionService } from './session';
|
||||
import { registerBsearchRoute } from './routes/bsearch';
|
||||
import { getKibanaContext } from './expressions/kibana_context';
|
||||
import { enhancedEsSearchStrategyProvider } from './strategies/ese_search';
|
||||
import { eqlSearchStrategyProvider } from './strategies/eql_search';
|
||||
import { NoSearchIdInSessionError } from './errors/no_search_id_in_session';
|
||||
|
@ -233,7 +231,6 @@ export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
|
|||
expressions.registerFunction(luceneFunction);
|
||||
expressions.registerFunction(kqlFunction);
|
||||
expressions.registerFunction(kibanaTimerangeFunction);
|
||||
expressions.registerFunction(getKibanaContext({ getStartServices: core.getStartServices }));
|
||||
expressions.registerFunction(fieldFunction);
|
||||
expressions.registerFunction(numericalRangeFunction);
|
||||
expressions.registerFunction(rangeFunction);
|
||||
|
@ -244,7 +241,7 @@ export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
|
|||
expressions.registerFunction(removeFilterFunction);
|
||||
expressions.registerFunction(selectFilterFunction);
|
||||
expressions.registerFunction(phraseFilterFunction);
|
||||
expressions.registerType(kibanaContext);
|
||||
|
||||
expressions.registerType(esRawResponse);
|
||||
expressions.registerType(eqlRawResponse);
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@
|
|||
"@kbn/core-application-browser",
|
||||
"@kbn/core-saved-objects-server",
|
||||
"@kbn/core-saved-objects-utils-server",
|
||||
"@kbn/data-service",
|
||||
"@kbn/data-service"
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
|
|
|
@ -13,7 +13,7 @@ import { fieldFormatsMock } from '@kbn/field-formats-plugin/common/mocks';
|
|||
|
||||
import {
|
||||
UiSettingsCommon,
|
||||
SavedObjectsClientCommon,
|
||||
PersistenceAPI,
|
||||
SavedObject,
|
||||
DataViewSpec,
|
||||
IDataViewsApiClient,
|
||||
|
@ -60,7 +60,7 @@ const savedObject = {
|
|||
describe('IndexPatterns', () => {
|
||||
let indexPatterns: DataViewsService;
|
||||
let indexPatternsNoAccess: DataViewsService;
|
||||
let savedObjectsClient: SavedObjectsClientCommon;
|
||||
let savedObjectsClient: PersistenceAPI;
|
||||
let SOClientGetDelay = 0;
|
||||
let apiClient: IDataViewsApiClient;
|
||||
const uiSettings = {
|
||||
|
@ -73,7 +73,7 @@ describe('IndexPatterns', () => {
|
|||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
savedObjectsClient = {} as SavedObjectsClientCommon;
|
||||
savedObjectsClient = {} as PersistenceAPI;
|
||||
savedObjectsClient.find = jest.fn(
|
||||
() => Promise.resolve([indexPatternObj]) as Promise<Array<SavedObject<any>>>
|
||||
);
|
||||
|
@ -107,7 +107,7 @@ describe('IndexPatterns', () => {
|
|||
|
||||
indexPatterns = new DataViewsService({
|
||||
uiSettings,
|
||||
savedObjectsClient: savedObjectsClient as unknown as SavedObjectsClientCommon,
|
||||
savedObjectsClient: savedObjectsClient as unknown as PersistenceAPI,
|
||||
apiClient,
|
||||
fieldFormats,
|
||||
onNotification: () => {},
|
||||
|
@ -119,7 +119,7 @@ describe('IndexPatterns', () => {
|
|||
|
||||
indexPatternsNoAccess = new DataViewsService({
|
||||
uiSettings,
|
||||
savedObjectsClient: savedObjectsClient as unknown as SavedObjectsClientCommon,
|
||||
savedObjectsClient: savedObjectsClient as unknown as PersistenceAPI,
|
||||
apiClient,
|
||||
fieldFormats,
|
||||
onNotification: () => {},
|
||||
|
|
|
@ -11,7 +11,7 @@ import type { PublicMethodsOf } from '@kbn/utility-types';
|
|||
import { castEsToKbnFieldTypeName } from '@kbn/field-types';
|
||||
import { FieldFormatsStartCommon, FORMATS_UI_SETTINGS } from '@kbn/field-formats-plugin/common';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { SavedObjectsClientCommon } from '../types';
|
||||
import { PersistenceAPI } from '../types';
|
||||
|
||||
import { createDataViewCache } from '.';
|
||||
import type { RuntimeField, RuntimeFieldSpec, RuntimeType } from '../types';
|
||||
|
@ -89,7 +89,7 @@ export interface DataViewsServiceDeps {
|
|||
/**
|
||||
* Saved objects client interface wrapped in a common interface
|
||||
*/
|
||||
savedObjectsClient: SavedObjectsClientCommon;
|
||||
savedObjectsClient: PersistenceAPI;
|
||||
/**
|
||||
* Wrapper around http call functionality so it can be used on client or server
|
||||
*/
|
||||
|
@ -292,7 +292,7 @@ export interface DataViewsServicePublicMethods {
|
|||
*/
|
||||
export class DataViewsService {
|
||||
private config: UiSettingsCommon;
|
||||
private savedObjectsClient: SavedObjectsClientCommon;
|
||||
private savedObjectsClient: PersistenceAPI;
|
||||
private savedObjectsCache?: Array<SavedObject<DataViewSavedObjectAttrs>> | null;
|
||||
private apiClient: IDataViewsApiClient;
|
||||
private fieldFormats: FieldFormatsStartCommon;
|
||||
|
|
|
@ -38,7 +38,7 @@ export type {
|
|||
OnNotification,
|
||||
OnError,
|
||||
UiSettingsCommon,
|
||||
SavedObjectsClientCommon,
|
||||
PersistenceAPI,
|
||||
GetFieldsOptions,
|
||||
IDataViewsApiClient,
|
||||
SavedObject,
|
||||
|
|
|
@ -252,10 +252,10 @@ export interface SavedObjectsClientCommonFindArgs {
|
|||
}
|
||||
|
||||
/**
|
||||
* Common interface for the saved objects client
|
||||
* Common interface for the saved objects client on server and content management in browser
|
||||
* @public
|
||||
*/
|
||||
export interface SavedObjectsClientCommon {
|
||||
export interface PersistenceAPI {
|
||||
/**
|
||||
* Search for saved objects
|
||||
* @param options - options for search
|
||||
|
@ -269,14 +269,6 @@ export interface SavedObjectsClientCommon {
|
|||
* @param id - id of saved object
|
||||
*/
|
||||
get: (id: string) => Promise<SavedObject<DataViewAttributes>>;
|
||||
/**
|
||||
* Update a saved object by id
|
||||
* @param type - type of saved object
|
||||
* @param id - id of saved object
|
||||
* @param attributes - attributes to update
|
||||
* @param options - client options
|
||||
*/
|
||||
getSavedSearch: (id: string) => Promise<SavedObject>;
|
||||
/**
|
||||
* Update a saved object by id
|
||||
* @param type - type of saved object
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { SavedObjectsClientCommon } from './types';
|
||||
import type { PersistenceAPI } from './types';
|
||||
|
||||
/**
|
||||
* Returns an object matching a given name
|
||||
|
@ -15,7 +15,7 @@ import type { SavedObjectsClientCommon } from './types';
|
|||
* @param name {string}
|
||||
* @returns {SavedObject|undefined}
|
||||
*/
|
||||
export async function findByName(client: SavedObjectsClientCommon, name: string) {
|
||||
export async function findByName(client: PersistenceAPI, name: string) {
|
||||
if (name) {
|
||||
const savedObjects = await client.find({
|
||||
perPage: 10,
|
||||
|
|
|
@ -6,13 +6,11 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { SavedObjectsClientPublicToCommon } from './saved_objects_client_wrapper';
|
||||
import { ContentMagementWrapper } from './content_management_wrapper';
|
||||
import { ContentClient } from '@kbn/content-management-plugin/public';
|
||||
import { savedObjectsServiceMock } from '@kbn/core/public/mocks';
|
||||
import { DataViewSavedObjectConflictError } from '../common';
|
||||
|
||||
describe('SavedObjectsClientPublicToCommon', () => {
|
||||
const soClient = savedObjectsServiceMock.createStartContract().client;
|
||||
describe('ContentMagementWrapper', () => {
|
||||
const cmClient = {} as ContentClient;
|
||||
|
||||
test('get saved object - exactMatch', async () => {
|
||||
|
@ -22,7 +20,7 @@ describe('SavedObjectsClientPublicToCommon', () => {
|
|||
cmClient.get = jest
|
||||
.fn()
|
||||
.mockResolvedValue({ meta: { outcome: 'exactMatch' }, item: mockedSavedObject });
|
||||
const service = new SavedObjectsClientPublicToCommon(cmClient, soClient);
|
||||
const service = new ContentMagementWrapper(cmClient);
|
||||
const result = await service.get('1');
|
||||
expect(result).toStrictEqual(mockedSavedObject);
|
||||
});
|
||||
|
@ -34,7 +32,7 @@ describe('SavedObjectsClientPublicToCommon', () => {
|
|||
cmClient.get = jest
|
||||
.fn()
|
||||
.mockResolvedValue({ meta: { outcome: 'aliasMatch' }, item: mockedSavedObject });
|
||||
const service = new SavedObjectsClientPublicToCommon(cmClient, soClient);
|
||||
const service = new ContentMagementWrapper(cmClient);
|
||||
const result = await service.get('1');
|
||||
expect(result).toStrictEqual(mockedSavedObject);
|
||||
});
|
||||
|
@ -47,7 +45,7 @@ describe('SavedObjectsClientPublicToCommon', () => {
|
|||
cmClient.get = jest
|
||||
.fn()
|
||||
.mockResolvedValue({ meta: { outcome: 'conflict' }, item: mockedSavedObject });
|
||||
const service = new SavedObjectsClientPublicToCommon(cmClient, soClient);
|
||||
const service = new ContentMagementWrapper(cmClient);
|
||||
|
||||
await expect(service.get('1')).rejects.toThrow(DataViewSavedObjectConflictError);
|
||||
});
|
|
@ -8,12 +8,11 @@
|
|||
|
||||
import type { ContentClient } from '@kbn/content-management-plugin/public';
|
||||
import { SavedObjectNotFound } from '@kbn/kibana-utils-plugin/common';
|
||||
import type { SavedObjectsClientContract } from '@kbn/core/public';
|
||||
import { DataViewSavedObjectConflictError } from '../common/errors';
|
||||
import {
|
||||
DataViewAttributes,
|
||||
SavedObject,
|
||||
SavedObjectsClientCommon,
|
||||
PersistenceAPI,
|
||||
SavedObjectsClientCommonFindArgs,
|
||||
} from '../common/types';
|
||||
|
||||
|
@ -21,15 +20,11 @@ import type { DataViewCrudTypes } from '../common/content_management';
|
|||
|
||||
import { DataViewSOType } from '../common/content_management';
|
||||
|
||||
type SOClient = Pick<SavedObjectsClientContract, 'resolve'>;
|
||||
|
||||
export class SavedObjectsClientPublicToCommon implements SavedObjectsClientCommon {
|
||||
export class ContentMagementWrapper implements PersistenceAPI {
|
||||
private contentManagementClient: ContentClient;
|
||||
private savedObjectClient: SOClient;
|
||||
|
||||
constructor(contentManagementClient: ContentClient, savedObjectClient: SOClient) {
|
||||
constructor(contentManagementClient: ContentClient) {
|
||||
this.contentManagementClient = contentManagementClient;
|
||||
this.savedObjectClient = savedObjectClient;
|
||||
}
|
||||
|
||||
async find(options: SavedObjectsClientCommonFindArgs) {
|
||||
|
@ -75,15 +70,6 @@ export class SavedObjectsClientPublicToCommon implements SavedObjectsClientCommo
|
|||
return response.item;
|
||||
}
|
||||
|
||||
async getSavedSearch(id: string) {
|
||||
const response = await this.savedObjectClient.resolve('search', id);
|
||||
|
||||
if (response.outcome === 'conflict') {
|
||||
throw new DataViewSavedObjectConflictError(id);
|
||||
}
|
||||
return response.saved_object;
|
||||
}
|
||||
|
||||
async update(
|
||||
id: string,
|
||||
attributes: DataViewAttributes,
|
|
@ -19,7 +19,7 @@ export type {
|
|||
DataViewSpec,
|
||||
FieldSpec,
|
||||
DataViewAttributes,
|
||||
SavedObjectsClientCommon,
|
||||
PersistenceAPI,
|
||||
RuntimeField,
|
||||
} from '../common';
|
||||
export {
|
||||
|
@ -48,7 +48,6 @@ export type {
|
|||
export { DataViewsApiClient, DataViewsService, DataView } from './data_views';
|
||||
export type { DataViewListItem } from './data_views';
|
||||
export { UiSettingsPublicToCommon } from './ui_settings_wrapper';
|
||||
export { SavedObjectsClientPublicToCommon } from './saved_objects_client_wrapper';
|
||||
|
||||
/*
|
||||
* Plugin setup
|
||||
|
|
|
@ -17,7 +17,7 @@ import {
|
|||
} from './types';
|
||||
|
||||
import { DataViewsApiClient } from '.';
|
||||
import { SavedObjectsClientPublicToCommon } from './saved_objects_client_wrapper';
|
||||
import { ContentMagementWrapper } from './content_management_wrapper';
|
||||
|
||||
import { UiSettingsPublicToCommon } from './ui_settings_wrapper';
|
||||
|
||||
|
@ -63,7 +63,7 @@ export class DataViewsPublicPlugin
|
|||
core: CoreStart,
|
||||
{ fieldFormats, contentManagement }: DataViewsPublicStartDependencies
|
||||
): DataViewsPublicPluginStart {
|
||||
const { uiSettings, http, notifications, application, savedObjects } = core;
|
||||
const { uiSettings, http, notifications, application } = core;
|
||||
|
||||
const onNotifDebounced = debounceByKey(
|
||||
notifications.toasts.add.bind(notifications.toasts),
|
||||
|
@ -77,10 +77,7 @@ export class DataViewsPublicPlugin
|
|||
return new DataViewsServicePublic({
|
||||
hasData: this.hasData.start(core),
|
||||
uiSettings: new UiSettingsPublicToCommon(uiSettings),
|
||||
savedObjectsClient: new SavedObjectsClientPublicToCommon(
|
||||
contentManagement.client,
|
||||
savedObjects.client
|
||||
),
|
||||
savedObjectsClient: new ContentMagementWrapper(contentManagement.client),
|
||||
apiClient: new DataViewsApiClient(http),
|
||||
fieldFormats,
|
||||
onNotification: (toastInputFields, key) => {
|
||||
|
|
|
@ -18,7 +18,7 @@ import { FieldFormatsStart } from '@kbn/field-formats-plugin/server';
|
|||
import { DataViewsService } from '../common';
|
||||
import { UiSettingsServerToCommon } from './ui_settings_wrapper';
|
||||
import { IndexPatternsApiServer } from './index_patterns_api_client';
|
||||
import { SavedObjectsClientServerToCommon } from './saved_objects_client_wrapper';
|
||||
import { SavedObjectsClientWrapper } from './saved_objects_client_wrapper';
|
||||
|
||||
interface DataViewsServiceFactoryDeps {
|
||||
logger: Logger;
|
||||
|
@ -44,7 +44,7 @@ export const dataViewsServiceFactory = (deps: DataViewsServiceFactoryDeps) =>
|
|||
|
||||
return new DataViewsService({
|
||||
uiSettings: new UiSettingsServerToCommon(uiSettingsClient),
|
||||
savedObjectsClient: new SavedObjectsClientServerToCommon(savedObjectsClient),
|
||||
savedObjectsClient: new SavedObjectsClientWrapper(savedObjectsClient),
|
||||
apiClient: new IndexPatternsApiServer(elasticsearchClient, savedObjectsClient),
|
||||
fieldFormats: formats,
|
||||
onError: (error) => {
|
||||
|
|
|
@ -57,5 +57,5 @@ export {
|
|||
|
||||
export type { SERVICE_KEY_TYPE } from './constants';
|
||||
|
||||
export type { FieldSpec, SavedObjectsClientCommon } from '../common/types';
|
||||
export type { FieldSpec } from '../common/types';
|
||||
export { DataViewsService, DataView } from '../common/data_views';
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { SavedObjectsClientServerToCommon } from './saved_objects_client_wrapper';
|
||||
import { SavedObjectsClientWrapper } from './saved_objects_client_wrapper';
|
||||
import { SavedObjectsClientContract } from '@kbn/core/server';
|
||||
|
||||
import { DataViewSavedObjectConflictError } from '../common';
|
||||
|
@ -21,7 +21,7 @@ describe('SavedObjectsClientPublicToCommon', () => {
|
|||
soClient.resolve = jest
|
||||
.fn()
|
||||
.mockResolvedValue({ outcome: 'exactMatch', saved_object: mockedSavedObject });
|
||||
const service = new SavedObjectsClientServerToCommon(soClient);
|
||||
const service = new SavedObjectsClientWrapper(soClient);
|
||||
const result = await service.get('1');
|
||||
expect(result).toStrictEqual(mockedSavedObject);
|
||||
});
|
||||
|
@ -33,7 +33,7 @@ describe('SavedObjectsClientPublicToCommon', () => {
|
|||
soClient.resolve = jest
|
||||
.fn()
|
||||
.mockResolvedValue({ outcome: 'aliasMatch', saved_object: mockedSavedObject });
|
||||
const service = new SavedObjectsClientServerToCommon(soClient);
|
||||
const service = new SavedObjectsClientWrapper(soClient);
|
||||
const result = await service.get('1');
|
||||
expect(result).toStrictEqual(mockedSavedObject);
|
||||
});
|
||||
|
@ -46,7 +46,7 @@ describe('SavedObjectsClientPublicToCommon', () => {
|
|||
soClient.resolve = jest
|
||||
.fn()
|
||||
.mockResolvedValue({ outcome: 'conflict', saved_object: mockedSavedObject });
|
||||
const service = new SavedObjectsClientServerToCommon(soClient);
|
||||
const service = new SavedObjectsClientWrapper(soClient);
|
||||
|
||||
await expect(service.get('1')).rejects.toThrow(DataViewSavedObjectConflictError);
|
||||
});
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
import { SavedObjectsClientContract, SavedObject } from '@kbn/core/server';
|
||||
import {
|
||||
DataViewAttributes,
|
||||
SavedObjectsClientCommon,
|
||||
PersistenceAPI,
|
||||
SavedObjectsClientCommonFindArgs,
|
||||
} from '../common/types';
|
||||
import { DataViewSavedObjectConflictError } from '../common/errors';
|
||||
|
@ -17,7 +17,7 @@ import { DataViewSavedObjectConflictError } from '../common/errors';
|
|||
import type { DataViewCrudTypes } from '../common/content_management';
|
||||
import { DATA_VIEW_SAVED_OBJECT_TYPE } from '../common';
|
||||
|
||||
export class SavedObjectsClientServerToCommon implements SavedObjectsClientCommon {
|
||||
export class SavedObjectsClientWrapper implements PersistenceAPI {
|
||||
private savedObjectClient: SavedObjectsClientContract;
|
||||
constructor(savedObjectClient: SavedObjectsClientContract) {
|
||||
this.savedObjectClient = savedObjectClient;
|
||||
|
|
9
src/plugins/saved_search/common/expressions/index.ts
Normal file
9
src/plugins/saved_search/common/expressions/index.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 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.
|
||||
*/
|
||||
|
||||
export { kibanaContext } from './kibana_context_type';
|
|
@ -9,17 +9,16 @@
|
|||
import { FilterStateStore, buildFilter, FILTERS } from '@kbn/es-query';
|
||||
import type { DeeplyMockedKeys } from '@kbn/utility-types-jest';
|
||||
import type { ExecutionContext } from '@kbn/expressions-plugin/common';
|
||||
import { KibanaContext } from './kibana_context_type';
|
||||
import { KibanaContext, ExpressionFunctionKibanaContext } from '@kbn/data-plugin/common';
|
||||
import { fromSavedSearchAttributes } from '../service/saved_searches_utils';
|
||||
import type { SavedSearchAttributes, SavedSearch } from '../types';
|
||||
|
||||
import {
|
||||
getKibanaContextFn,
|
||||
ExpressionFunctionKibanaContext,
|
||||
KibanaContextStartDependencies,
|
||||
} from './kibana_context';
|
||||
import { getKibanaContextFn, KibanaContextStartDependencies } from './kibana_context';
|
||||
|
||||
type StartServicesMock = DeeplyMockedKeys<KibanaContextStartDependencies>;
|
||||
|
||||
const createExecutionContextMock = (): DeeplyMockedKeys<ExecutionContext> => ({
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
abortSignal: {} as any,
|
||||
getExecutionContext: jest.fn(),
|
||||
getSearchContext: jest.fn(),
|
||||
|
@ -41,23 +40,26 @@ describe('kibanaContextFn', () => {
|
|||
beforeEach(async () => {
|
||||
kibanaContextFn = getKibanaContextFn(getStartServicesMock);
|
||||
startServicesMock = {
|
||||
savedObjectsClient: {
|
||||
create: jest.fn(),
|
||||
delete: jest.fn(),
|
||||
find: jest.fn(),
|
||||
get: jest.fn(),
|
||||
getSavedSearch: jest.fn(),
|
||||
update: jest.fn(),
|
||||
},
|
||||
getSavedSearch: jest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
it('merges and deduplicates queries from different sources', async () => {
|
||||
const { fn } = kibanaContextFn;
|
||||
startServicesMock.savedObjectsClient.getSavedSearch.mockResolvedValue({
|
||||
attributes: {
|
||||
kibanaSavedObjectMeta: {
|
||||
searchSourceJSON: JSON.stringify({
|
||||
startServicesMock.getSavedSearch.mockResolvedValue(
|
||||
fromSavedSearchAttributes(
|
||||
'abc',
|
||||
{
|
||||
kibanaSavedObjectMeta: {
|
||||
searchSourceJSON: JSON.stringify({
|
||||
query: [],
|
||||
}),
|
||||
},
|
||||
} as SavedSearchAttributes,
|
||||
[],
|
||||
undefined,
|
||||
{
|
||||
getFields: () => ({
|
||||
query: [
|
||||
{
|
||||
language: 'kuery',
|
||||
|
@ -84,10 +86,12 @@ describe('kibanaContextFn', () => {
|
|||
},
|
||||
},
|
||||
],
|
||||
filter: [],
|
||||
}),
|
||||
},
|
||||
},
|
||||
} as any);
|
||||
} as unknown as SavedSearch['searchSource'],
|
||||
{} as SavedSearch['sharingSavedObjectProps']
|
||||
)
|
||||
);
|
||||
const args = {
|
||||
...emptyArgs,
|
||||
q: [
|
139
src/plugins/saved_search/common/expressions/kibana_context.ts
Normal file
139
src/plugins/saved_search/common/expressions/kibana_context.ts
Normal file
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* 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 { isEqual, uniqBy } from 'lodash';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { ExecutionContext } from '@kbn/expressions-plugin/common';
|
||||
import { Filter, fromCombinedFilter } from '@kbn/es-query';
|
||||
import { Query, uniqFilters } from '@kbn/es-query';
|
||||
import { unboxExpressionValue } from '@kbn/expressions-plugin/common';
|
||||
import { SavedObjectReference } from '@kbn/core/server';
|
||||
import { ExpressionFunctionKibanaContext } from '@kbn/data-plugin/common';
|
||||
import { SavedSearch } from '../types';
|
||||
|
||||
export interface KibanaContextStartDependencies {
|
||||
getSavedSearch: (id: string) => Promise<SavedSearch>;
|
||||
}
|
||||
|
||||
const mergeQueries = (first: Query | Query[] = [], second: Query | Query[]) =>
|
||||
uniqBy<Query>(
|
||||
[...(Array.isArray(first) ? first : [first]), ...(Array.isArray(second) ? second : [second])],
|
||||
(n) => JSON.stringify(n.query)
|
||||
);
|
||||
|
||||
export const getKibanaContextFn = (
|
||||
getStartDependencies: (
|
||||
getKibanaRequest: ExecutionContext['getKibanaRequest']
|
||||
) => Promise<KibanaContextStartDependencies>
|
||||
) => {
|
||||
const kibanaContextFunction: ExpressionFunctionKibanaContext = {
|
||||
name: 'kibana_context',
|
||||
type: 'kibana_context',
|
||||
inputTypes: ['kibana_context', 'null'],
|
||||
help: i18n.translate('savedSearch.kibana_context.help', {
|
||||
defaultMessage: 'Updates kibana global context',
|
||||
}),
|
||||
args: {
|
||||
q: {
|
||||
types: ['kibana_query', 'null'],
|
||||
multi: true,
|
||||
aliases: ['query', '_'],
|
||||
help: i18n.translate('savedSearch.kibana_context.q.help', {
|
||||
defaultMessage: 'Specify Kibana free form text query',
|
||||
}),
|
||||
},
|
||||
filters: {
|
||||
types: ['kibana_filter', 'null'],
|
||||
multi: true,
|
||||
help: i18n.translate('savedSearch.kibana_context.filters.help', {
|
||||
defaultMessage: 'Specify Kibana generic filters',
|
||||
}),
|
||||
},
|
||||
timeRange: {
|
||||
types: ['timerange', 'null'],
|
||||
default: null,
|
||||
help: i18n.translate('savedSearch.kibana_context.timeRange.help', {
|
||||
defaultMessage: 'Specify Kibana time range filter',
|
||||
}),
|
||||
},
|
||||
savedSearchId: {
|
||||
types: ['string', 'null'],
|
||||
default: null,
|
||||
help: i18n.translate('savedSearch.kibana_context.savedSearchId.help', {
|
||||
defaultMessage: 'Specify saved search ID to be used for queries and filters',
|
||||
}),
|
||||
},
|
||||
},
|
||||
|
||||
extract(state) {
|
||||
const references: SavedObjectReference[] = [];
|
||||
if (state.savedSearchId.length && typeof state.savedSearchId[0] === 'string') {
|
||||
const refName = 'kibana_context.savedSearchId';
|
||||
references.push({
|
||||
name: refName,
|
||||
type: 'search',
|
||||
id: state.savedSearchId[0] as string,
|
||||
});
|
||||
return {
|
||||
state: {
|
||||
...state,
|
||||
savedSearchId: [refName],
|
||||
},
|
||||
references,
|
||||
};
|
||||
}
|
||||
return { state, references };
|
||||
},
|
||||
|
||||
inject(state, references) {
|
||||
const reference = references.find((r) => r.name === 'kibana_context.savedSearchId');
|
||||
if (reference) {
|
||||
state.savedSearchId[0] = reference.id;
|
||||
}
|
||||
return state;
|
||||
},
|
||||
|
||||
async fn(input, args, { getKibanaRequest }) {
|
||||
const { getSavedSearch } = await getStartDependencies(getKibanaRequest);
|
||||
|
||||
const timeRange = args.timeRange || input?.timeRange;
|
||||
let queries = mergeQueries(input?.query, args?.q?.filter(Boolean) || []);
|
||||
const filterFromArgs = (args?.filters?.map(unboxExpressionValue) || []) as Filter[];
|
||||
|
||||
let filters = [...(input?.filters || [])];
|
||||
|
||||
if (args.savedSearchId) {
|
||||
const obj = await getSavedSearch(args.savedSearchId);
|
||||
const { query, filter } = obj.searchSource.getFields();
|
||||
|
||||
if (query) {
|
||||
queries = mergeQueries(queries, query as Query);
|
||||
}
|
||||
if (filter) {
|
||||
filters = [...filters, ...(Array.isArray(filter) ? filter : [filter])] as Filter[];
|
||||
}
|
||||
}
|
||||
const uniqueArgFilters = filterFromArgs.filter(
|
||||
(argF) =>
|
||||
!filters.some((f) => {
|
||||
return isEqual(fromCombinedFilter(f).query, argF.query);
|
||||
})
|
||||
);
|
||||
|
||||
filters = [...filters, ...uniqueArgFilters];
|
||||
|
||||
return {
|
||||
type: 'kibana_context',
|
||||
query: queries,
|
||||
filters: uniqFilters(filters.filter((f: Filter) => !f.meta?.disabled)),
|
||||
timeRange,
|
||||
};
|
||||
},
|
||||
};
|
||||
return kibanaContextFunction;
|
||||
};
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* 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 { ExpressionValueFilter } from '@kbn/expressions-plugin/common';
|
||||
import { adaptToExpressionValueFilter, KibanaContext } from '@kbn/data-plugin/common';
|
||||
|
||||
export const kibanaContext = {
|
||||
name: 'kibana_context',
|
||||
from: {
|
||||
null: () => {
|
||||
return {
|
||||
type: 'kibana_context',
|
||||
};
|
||||
},
|
||||
},
|
||||
to: {
|
||||
null: () => {
|
||||
return {
|
||||
type: 'null',
|
||||
};
|
||||
},
|
||||
filter: (input: KibanaContext): ExpressionValueFilter => {
|
||||
const { filters = [] } = input;
|
||||
return {
|
||||
type: 'filter',
|
||||
filterType: 'filter',
|
||||
and: filters.map(adaptToExpressionValueFilter),
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
|
@ -23,3 +23,4 @@ export enum VIEW_MODE {
|
|||
|
||||
export { SavedSearchType } from './constants';
|
||||
export { LATEST_VERSION } from './constants';
|
||||
export { getKibanaContextFn } from './expressions/kibana_context';
|
||||
|
|
|
@ -6,26 +6,25 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
import type { DataPublicPluginStart } from '@kbn/data-plugin/public';
|
||||
import { contentManagementMock } from '@kbn/content-management-plugin/public/mocks';
|
||||
import type { ContentManagementPublicStart } from '@kbn/content-management-plugin/public';
|
||||
|
||||
import { dataPluginMock } from '@kbn/data-plugin/public/mocks';
|
||||
|
||||
import { getSavedSearch } from './get_saved_searches';
|
||||
import { SavedObjectsTaggingApi } from '@kbn/saved-objects-tagging-oss-plugin/public';
|
||||
import type { GetSavedSearchDependencies } from './get_saved_searches';
|
||||
|
||||
describe('getSavedSearch', () => {
|
||||
let search: DataPublicPluginStart['search'];
|
||||
let cmClient: ContentManagementPublicStart['client'];
|
||||
let searchSourceCreate: DataPublicPluginStart['search']['searchSource']['create'];
|
||||
let getSavedSrch: GetSavedSearchDependencies['getSavedSrch'];
|
||||
|
||||
beforeEach(() => {
|
||||
cmClient = contentManagementMock.createStartContract().client;
|
||||
search = dataPluginMock.createStartContract().search;
|
||||
getSavedSrch = jest.fn();
|
||||
searchSourceCreate = dataPluginMock.createStartContract().search.searchSource.create;
|
||||
});
|
||||
|
||||
test('should throw an error if so not found', async () => {
|
||||
let errorMessage = 'No error thrown.';
|
||||
cmClient.get = jest.fn().mockReturnValue({
|
||||
getSavedSrch = jest.fn().mockReturnValue({
|
||||
statusCode: 404,
|
||||
error: 'Not Found',
|
||||
message: 'Saved object [ccf1af80-2297-11ec-86e0-1155ffb9c7a7] not found',
|
||||
|
@ -33,8 +32,8 @@ describe('getSavedSearch', () => {
|
|||
|
||||
try {
|
||||
await getSavedSearch('ccf1af80-2297-11ec-86e0-1155ffb9c7a7', {
|
||||
contentManagement: cmClient,
|
||||
search,
|
||||
getSavedSrch,
|
||||
searchSourceCreate,
|
||||
});
|
||||
} catch (error) {
|
||||
errorMessage = error.message;
|
||||
|
@ -46,7 +45,7 @@ describe('getSavedSearch', () => {
|
|||
});
|
||||
|
||||
test('should find saved search', async () => {
|
||||
cmClient.get = jest.fn().mockReturnValue({
|
||||
getSavedSrch = jest.fn().mockReturnValue({
|
||||
item: {
|
||||
attributes: {
|
||||
kibanaSavedObjectMeta: {
|
||||
|
@ -77,11 +76,11 @@ describe('getSavedSearch', () => {
|
|||
});
|
||||
|
||||
const savedSearch = await getSavedSearch('ccf1af80-2297-11ec-86e0-1155ffb9c7a7', {
|
||||
contentManagement: cmClient,
|
||||
search,
|
||||
getSavedSrch,
|
||||
searchSourceCreate,
|
||||
});
|
||||
|
||||
expect(cmClient.get).toHaveBeenCalled();
|
||||
expect(getSavedSrch).toHaveBeenCalled();
|
||||
expect(savedSearch).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"breakdownField": undefined,
|
||||
|
@ -150,7 +149,7 @@ describe('getSavedSearch', () => {
|
|||
});
|
||||
|
||||
test('should find saved search with sql mode', async () => {
|
||||
cmClient.get = jest.fn().mockReturnValue({
|
||||
getSavedSrch = jest.fn().mockReturnValue({
|
||||
item: {
|
||||
attributes: {
|
||||
kibanaSavedObjectMeta: {
|
||||
|
@ -182,11 +181,11 @@ describe('getSavedSearch', () => {
|
|||
});
|
||||
|
||||
const savedSearch = await getSavedSearch('ccf1af80-2297-11ec-86e0-1155ffb9c7a7', {
|
||||
contentManagement: cmClient,
|
||||
search,
|
||||
getSavedSrch,
|
||||
searchSourceCreate,
|
||||
});
|
||||
|
||||
expect(cmClient.get).toHaveBeenCalled();
|
||||
expect(getSavedSrch).toHaveBeenCalled();
|
||||
expect(savedSearch).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"breakdownField": undefined,
|
||||
|
@ -255,7 +254,7 @@ describe('getSavedSearch', () => {
|
|||
});
|
||||
|
||||
it('should call savedObjectsTagging.ui.getTagIdsFromReferences', async () => {
|
||||
cmClient.get = jest.fn().mockReturnValue({
|
||||
getSavedSrch = jest.fn().mockReturnValue({
|
||||
item: {
|
||||
attributes: {
|
||||
kibanaSavedObjectMeta: {
|
||||
|
@ -296,8 +295,8 @@ describe('getSavedSearch', () => {
|
|||
},
|
||||
} as unknown as SavedObjectsTaggingApi;
|
||||
await getSavedSearch('ccf1af80-2297-11ec-86e0-1155ffb9c7a7', {
|
||||
contentManagement: cmClient,
|
||||
search,
|
||||
getSavedSrch,
|
||||
searchSourceCreate,
|
||||
savedObjectsTagging,
|
||||
});
|
||||
expect(savedObjectsTagging.ui.getTagIdsFromReferences).toHaveBeenCalledWith([
|
|
@ -6,20 +6,21 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { DataPublicPluginStart } from '@kbn/data-plugin/public';
|
||||
import { injectSearchSourceReferences, parseSearchSourceJSON } from '@kbn/data-plugin/public';
|
||||
import type { ISearchStartSearchSource } from '@kbn/data-plugin/common';
|
||||
import { injectReferences, parseSearchSourceJSON } from '@kbn/data-plugin/common';
|
||||
// these won't exist in on server
|
||||
import type { SpacesApi } from '@kbn/spaces-plugin/public';
|
||||
import type { SavedObjectsTaggingApi } from '@kbn/saved-objects-tagging-oss-plugin/public';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import type { ContentManagementPublicStart } from '@kbn/content-management-plugin/public';
|
||||
import type { SavedSearch } from './types';
|
||||
import { SAVED_SEARCH_TYPE } from './constants';
|
||||
import { fromSavedSearchAttributes } from './saved_searches_utils';
|
||||
import type { SavedSearchCrudTypes } from '../../../common/content_management';
|
||||
|
||||
interface GetSavedSearchDependencies {
|
||||
search: DataPublicPluginStart['search'];
|
||||
contentManagement: ContentManagementPublicStart['client'];
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import type { SavedSearch } from '../types';
|
||||
import { SavedSearchType as SAVED_SEARCH_TYPE } from '..';
|
||||
import { fromSavedSearchAttributes } from './saved_searches_utils';
|
||||
import type { SavedSearchCrudTypes } from '../content_management';
|
||||
|
||||
export interface GetSavedSearchDependencies {
|
||||
searchSourceCreate: ISearchStartSearchSource['create'];
|
||||
getSavedSrch: (id: string) => Promise<SavedSearchCrudTypes['GetOut']>;
|
||||
spaces?: SpacesApi;
|
||||
savedObjectsTagging?: SavedObjectsTaggingApi;
|
||||
}
|
||||
|
@ -32,15 +33,9 @@ const getSavedSearchUrlConflictMessage = async (json: string) =>
|
|||
|
||||
export const getSavedSearch = async (
|
||||
savedSearchId: string,
|
||||
{ search, spaces, savedObjectsTagging, contentManagement }: GetSavedSearchDependencies
|
||||
{ searchSourceCreate, spaces, savedObjectsTagging, getSavedSrch }: GetSavedSearchDependencies
|
||||
) => {
|
||||
const so = await contentManagement.get<
|
||||
SavedSearchCrudTypes['GetIn'],
|
||||
SavedSearchCrudTypes['GetOut']
|
||||
>({
|
||||
contentTypeId: SAVED_SEARCH_TYPE,
|
||||
id: savedSearchId,
|
||||
});
|
||||
const so = await getSavedSrch(savedSearchId);
|
||||
|
||||
// @ts-expect-error
|
||||
if (so.error) {
|
||||
|
@ -53,6 +48,7 @@ export const getSavedSearch = async (
|
|||
JSON.stringify({
|
||||
targetType: SAVED_SEARCH_TYPE,
|
||||
sourceId: savedSearchId,
|
||||
// front end only
|
||||
targetSpace: (await spaces?.getActiveSpace())?.id,
|
||||
})
|
||||
)
|
||||
|
@ -65,11 +61,12 @@ export const getSavedSearch = async (
|
|||
savedSearch.attributes.kibanaSavedObjectMeta?.searchSourceJSON ?? '{}'
|
||||
);
|
||||
|
||||
const searchSourceValues = injectSearchSourceReferences(
|
||||
parsedSearchSourceJSON as Parameters<typeof injectSearchSourceReferences>[0],
|
||||
const searchSourceValues = injectReferences(
|
||||
parsedSearchSourceJSON as Parameters<typeof injectReferences>[0],
|
||||
savedSearch.references
|
||||
);
|
||||
|
||||
// front end only
|
||||
const tags = savedObjectsTagging
|
||||
? savedObjectsTagging.ui.getTagIdsFromReferences(savedSearch.references)
|
||||
: undefined;
|
||||
|
@ -79,7 +76,7 @@ export const getSavedSearch = async (
|
|||
savedSearch.attributes,
|
||||
tags,
|
||||
savedSearch.references,
|
||||
await search.searchSource.create(searchSourceValues),
|
||||
await searchSourceCreate(searchSourceValues),
|
||||
so.meta
|
||||
);
|
||||
|
||||
|
@ -92,9 +89,9 @@ export const getSavedSearch = async (
|
|||
* @param search
|
||||
*/
|
||||
export const getNewSavedSearch = ({
|
||||
search,
|
||||
searchSource,
|
||||
}: {
|
||||
search: DataPublicPluginStart['search'];
|
||||
searchSource: ISearchStartSearchSource;
|
||||
}): SavedSearch => ({
|
||||
searchSource: search.searchSource.createEmpty(),
|
||||
searchSource: searchSource.createEmpty(),
|
||||
});
|
|
@ -10,8 +10,7 @@ import { fromSavedSearchAttributes, toSavedSearchAttributes } from './saved_sear
|
|||
|
||||
import { createSearchSourceMock } from '@kbn/data-plugin/public/mocks';
|
||||
|
||||
import type { SavedSearchAttributes } from '../../../common';
|
||||
import type { SavedSearch } from './types';
|
||||
import type { SavedSearch, SavedSearchAttributes } from '../types';
|
||||
|
||||
describe('saved_searches_utils', () => {
|
||||
describe('fromSavedSearchAttributes', () => {
|
|
@ -8,11 +8,10 @@
|
|||
|
||||
import { pick } from 'lodash';
|
||||
import type { SavedObjectReference } from '@kbn/core-saved-objects-server';
|
||||
import type { SavedSearchAttributes } from '../../../common';
|
||||
import { fromSavedSearchAttributes as fromSavedSearchAttributesCommon } from '../../../common';
|
||||
import type { SavedSearch } from './types';
|
||||
import type { SavedSearchAttributes, SavedSearch } from '..';
|
||||
import { fromSavedSearchAttributes as fromSavedSearchAttributesCommon } from '..';
|
||||
|
||||
export { getSavedSearchUrl, getSavedSearchFullPathUrl } from '../../../common';
|
||||
export { getSavedSearchUrl, getSavedSearchFullPathUrl } from '..';
|
||||
|
||||
export const fromSavedSearchAttributes = (
|
||||
id: string,
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
import type { ISearchSource, RefreshInterval, TimeRange } from '@kbn/data-plugin/common';
|
||||
import type { SavedObjectReference } from '@kbn/core-saved-objects-server';
|
||||
import type { SavedObjectsResolveResponse } from '@kbn/core/server';
|
||||
import { VIEW_MODE } from '.';
|
||||
|
||||
export interface DiscoverGridSettings {
|
||||
|
@ -75,4 +76,10 @@ export interface SavedSearch {
|
|||
rowsPerPage?: number;
|
||||
breakdownField?: string;
|
||||
references?: SavedObjectReference[];
|
||||
sharingSavedObjectProps?: {
|
||||
outcome?: SavedObjectsResolveResponse['outcome'];
|
||||
aliasTargetId?: SavedObjectsResolveResponse['alias_target_id'];
|
||||
aliasPurpose?: SavedObjectsResolveResponse['alias_purpose'];
|
||||
errorJSON?: string;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -9,7 +9,8 @@
|
|||
"browser": true,
|
||||
"requiredPlugins": [
|
||||
"data",
|
||||
"contentManagement"
|
||||
"contentManagement",
|
||||
"expressions"
|
||||
],
|
||||
"optionalPlugins": [
|
||||
"spaces",
|
||||
|
|
|
@ -7,9 +7,8 @@
|
|||
*/
|
||||
|
||||
import { StartServicesAccessor } from '@kbn/core/public';
|
||||
import { SavedObjectsClientCommon } from '@kbn/data-views-plugin/public';
|
||||
import { getKibanaContextFn } from '../../../common/search/expressions';
|
||||
import { DataPublicPluginStart, DataStartDependencies } from '../../types';
|
||||
import { getKibanaContextFn } from '../../common';
|
||||
import { SavedSearchPublicPluginStart, SavedSearchPublicStartDependencies } from '../plugin';
|
||||
|
||||
/**
|
||||
* This is some glue code that takes in `core.getStartServices`, extracts the dependencies
|
||||
|
@ -25,15 +24,17 @@ import { DataPublicPluginStart, DataStartDependencies } from '../../types';
|
|||
*
|
||||
* @internal
|
||||
*/
|
||||
|
||||
export function getKibanaContext({
|
||||
getStartServices,
|
||||
}: {
|
||||
getStartServices: StartServicesAccessor<DataStartDependencies, DataPublicPluginStart>;
|
||||
getStartServices: StartServicesAccessor<
|
||||
SavedSearchPublicStartDependencies,
|
||||
SavedSearchPublicPluginStart
|
||||
>;
|
||||
}) {
|
||||
return getKibanaContextFn(async () => {
|
||||
const [core] = await getStartServices();
|
||||
return {
|
||||
savedObjectsClient: core.savedObjects.client as unknown as SavedObjectsClientCommon,
|
||||
};
|
||||
const [, , { get: getSavedSearch }] = await getStartServices();
|
||||
return { getSavedSearch };
|
||||
});
|
||||
}
|
|
@ -6,10 +6,11 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { CoreSetup, CoreStart, Plugin } from '@kbn/core/public';
|
||||
import { CoreSetup, CoreStart, Plugin, StartServicesAccessor } from '@kbn/core/public';
|
||||
import type { DataPublicPluginStart } from '@kbn/data-plugin/public';
|
||||
import type { SpacesApi } from '@kbn/spaces-plugin/public';
|
||||
import type { SavedObjectTaggingOssPluginStart } from '@kbn/saved-objects-tagging-oss-plugin/public';
|
||||
import { ExpressionsSetup } from '@kbn/expressions-plugin/public';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import type {
|
||||
ContentManagementPublicSetup,
|
||||
|
@ -25,6 +26,8 @@ import {
|
|||
import { SavedSearch, SavedSearchAttributes } from '../common/types';
|
||||
import { SavedSearchType, LATEST_VERSION } from '../common';
|
||||
import { SavedSearchesService } from './services/saved_searches/saved_searches_service';
|
||||
import { kibanaContext } from '../common/expressions';
|
||||
import { getKibanaContext } from './expressions/kibana_context';
|
||||
|
||||
/**
|
||||
* Saved search plugin public Setup contract
|
||||
|
@ -50,6 +53,7 @@ export interface SavedSearchPublicPluginStart {
|
|||
*/
|
||||
export interface SavedSearchPublicSetupDependencies {
|
||||
contentManagement: ContentManagementPublicSetup;
|
||||
expressions: ExpressionsSetup;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -71,7 +75,10 @@ export class SavedSearchPublicPlugin
|
|||
SavedSearchPublicStartDependencies
|
||||
>
|
||||
{
|
||||
public setup(core: CoreSetup, { contentManagement }: SavedSearchPublicSetupDependencies) {
|
||||
public setup(
|
||||
{ getStartServices }: CoreSetup,
|
||||
{ contentManagement, expressions }: SavedSearchPublicSetupDependencies
|
||||
) {
|
||||
contentManagement.registry.register({
|
||||
id: SavedSearchType,
|
||||
version: {
|
||||
|
@ -82,6 +89,17 @@ export class SavedSearchPublicPlugin
|
|||
}),
|
||||
});
|
||||
|
||||
expressions.registerFunction(
|
||||
getKibanaContext({ getStartServices } as {
|
||||
getStartServices: StartServicesAccessor<
|
||||
SavedSearchPublicStartDependencies,
|
||||
SavedSearchPublicPluginStart
|
||||
>;
|
||||
})
|
||||
);
|
||||
|
||||
expressions.registerType(kibanaContext);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
|
|
@ -6,8 +6,11 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export { getSavedSearch, getNewSavedSearch } from './get_saved_searches';
|
||||
export { getSavedSearchUrl, getSavedSearchFullPathUrl } from './saved_searches_utils';
|
||||
export { getSavedSearch, getNewSavedSearch } from '../../../common/service/get_saved_searches';
|
||||
export {
|
||||
getSavedSearchUrl,
|
||||
getSavedSearchFullPathUrl,
|
||||
} from '../../../common/service/saved_searches_utils';
|
||||
export type { SaveSavedSearchOptions } from './save_saved_searches';
|
||||
export { saveSavedSearch } from './save_saved_searches';
|
||||
export { SAVED_SEARCH_TYPE } from './constants';
|
||||
|
|
|
@ -10,7 +10,7 @@ import type { SavedObjectsTaggingApi } from '@kbn/saved-objects-tagging-oss-plug
|
|||
import type { ContentManagementPublicStart } from '@kbn/content-management-plugin/public';
|
||||
import type { SavedSearch } from './types';
|
||||
import { SAVED_SEARCH_TYPE } from './constants';
|
||||
import { toSavedSearchAttributes } from './saved_searches_utils';
|
||||
import { toSavedSearchAttributes } from '../../../common/service/saved_searches_utils';
|
||||
import type { SavedSearchCrudTypes } from '../../../common/content_management';
|
||||
|
||||
export interface SaveSavedSearchOptions {
|
||||
|
|
|
@ -27,10 +27,16 @@ export class SavedSearchesService {
|
|||
|
||||
get = (savedSearchId: string) => {
|
||||
const { search, contentManagement, spaces, savedObjectsTaggingOss } = this.deps;
|
||||
const getViaCm = (id: string) =>
|
||||
contentManagement.get<SavedSearchCrudTypes['GetIn'], SavedSearchCrudTypes['GetOut']>({
|
||||
contentTypeId: SavedSearchType,
|
||||
id,
|
||||
});
|
||||
|
||||
return getSavedSearch(savedSearchId, {
|
||||
search,
|
||||
contentManagement,
|
||||
getSavedSrch: getViaCm,
|
||||
spaces,
|
||||
searchSourceCreate: search.searchSource.create,
|
||||
savedObjectsTagging: savedObjectsTaggingOss?.getTaggingApi(),
|
||||
});
|
||||
};
|
||||
|
@ -45,7 +51,7 @@ export class SavedSearchesService {
|
|||
});
|
||||
return result.hits;
|
||||
};
|
||||
getNew = () => getNewSavedSearch({ search: this.deps.search });
|
||||
getNew = () => getNewSavedSearch({ searchSource: this.deps.search.searchSource });
|
||||
|
||||
find = async (search: string) => {
|
||||
const { contentManagement } = this.deps;
|
||||
|
|
|
@ -7,9 +7,10 @@
|
|||
*/
|
||||
|
||||
import { StartServicesAccessor } from '@kbn/core/server';
|
||||
import { SavedObjectsClientCommon } from '@kbn/data-views-plugin/server';
|
||||
import { getKibanaContextFn } from '../../../common/search/expressions';
|
||||
import { DataPluginStart, DataPluginStartDependencies } from '../../plugin';
|
||||
import { getKibanaContextFn } from '../../common';
|
||||
import { SavedSearchServerStartDeps } from '../plugin';
|
||||
import { getSavedSearch } from '../../common/service/get_saved_searches';
|
||||
import { SavedSearchAttributes } from '../../common/types';
|
||||
|
||||
/**
|
||||
* This is some glue code that takes in `core.getStartServices`, extracts the dependencies
|
||||
|
@ -25,20 +26,36 @@ import { DataPluginStart, DataPluginStartDependencies } from '../../plugin';
|
|||
*
|
||||
* @internal
|
||||
*/
|
||||
export function getKibanaContext({
|
||||
getStartServices,
|
||||
}: {
|
||||
getStartServices: StartServicesAccessor<DataPluginStartDependencies, DataPluginStart>;
|
||||
}) {
|
||||
export function getKibanaContext(
|
||||
getStartServices: StartServicesAccessor<SavedSearchServerStartDeps>
|
||||
) {
|
||||
return getKibanaContextFn(async (getKibanaRequest) => {
|
||||
const request = getKibanaRequest && getKibanaRequest();
|
||||
if (!request) {
|
||||
throw new Error('KIBANA_CONTEXT_KIBANA_REQUEST_MISSING');
|
||||
}
|
||||
|
||||
const [{ savedObjects }] = await getStartServices();
|
||||
const [{ savedObjects }, { data }] = await getStartServices();
|
||||
return {
|
||||
savedObjectsClient: savedObjects.getScopedClient(request) as any as SavedObjectsClientCommon,
|
||||
getSavedSearch: async (id: string) => {
|
||||
const searchSourceCreate = (await data.search.searchSource.asScoped(request)).create;
|
||||
const getSavedSrch = async (searchId: string) => {
|
||||
const so = await savedObjects
|
||||
.getScopedClient(request)
|
||||
.resolve<SavedSearchAttributes>('search', searchId);
|
||||
|
||||
return {
|
||||
item: so.saved_object,
|
||||
meta: {
|
||||
outcome: so.outcome,
|
||||
aliasTargetId: so.alias_target_id,
|
||||
aliasPurpose: so.alias_purpose,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
return getSavedSearch(id, { searchSourceCreate, getSavedSrch });
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
|
@ -7,21 +7,41 @@
|
|||
*/
|
||||
|
||||
import { CoreSetup, CoreStart, Plugin } from '@kbn/core/server';
|
||||
import type { PluginSetup as DataPluginSetup } from '@kbn/data-plugin/server';
|
||||
import { StartServicesAccessor } from '@kbn/core/server';
|
||||
import type {
|
||||
PluginSetup as DataPluginSetup,
|
||||
PluginStart as DataPluginStart,
|
||||
} from '@kbn/data-plugin/server';
|
||||
import type { ContentManagementServerSetup } from '@kbn/content-management-plugin/server';
|
||||
import { ExpressionsServerSetup } from '@kbn/expressions-plugin/server';
|
||||
import { getSavedSearchObjectType } from './saved_objects';
|
||||
import { SavedSearchType, LATEST_VERSION } from '../common';
|
||||
import { SavedSearchStorage } from './content_management';
|
||||
import { kibanaContext } from '../common/expressions';
|
||||
import { getKibanaContext } from './expressions/kibana_context';
|
||||
import { getSavedSearch } from '../common/service/get_saved_searches';
|
||||
|
||||
export class SavedSearchServerPlugin implements Plugin<object, object> {
|
||||
/**
|
||||
* Saved search plugin server Setup contract
|
||||
*/
|
||||
export interface SavedSearchPublicSetupDependencies {
|
||||
data: DataPluginSetup;
|
||||
contentManagement: ContentManagementServerSetup;
|
||||
expressions: ExpressionsServerSetup;
|
||||
}
|
||||
|
||||
export interface SavedSearchServerStartDeps {
|
||||
data: DataPluginStart;
|
||||
}
|
||||
|
||||
export class SavedSearchServerPlugin
|
||||
implements Plugin<object, object, object, SavedSearchServerStartDeps>
|
||||
{
|
||||
public setup(
|
||||
core: CoreSetup,
|
||||
plugins: {
|
||||
data: DataPluginSetup;
|
||||
contentManagement: ContentManagementServerSetup;
|
||||
}
|
||||
{ data, contentManagement, expressions }: SavedSearchPublicSetupDependencies
|
||||
) {
|
||||
plugins.contentManagement.register({
|
||||
contentManagement.register({
|
||||
id: SavedSearchType,
|
||||
storage: new SavedSearchStorage(),
|
||||
version: {
|
||||
|
@ -29,16 +49,23 @@ export class SavedSearchServerPlugin implements Plugin<object, object> {
|
|||
},
|
||||
});
|
||||
|
||||
const getSearchSourceMigrations = plugins.data.search.searchSource.getAllMigrations.bind(
|
||||
plugins.data.search.searchSource
|
||||
);
|
||||
const searchSource = data.search.searchSource;
|
||||
|
||||
const getSearchSourceMigrations = searchSource.getAllMigrations.bind(searchSource);
|
||||
core.savedObjects.registerType(getSavedSearchObjectType(getSearchSourceMigrations));
|
||||
|
||||
expressions.registerType(kibanaContext);
|
||||
expressions.registerFunction(
|
||||
getKibanaContext(core.getStartServices as StartServicesAccessor<SavedSearchServerStartDeps>)
|
||||
);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
public start(core: CoreStart) {
|
||||
return {};
|
||||
return {
|
||||
getSavedSearch,
|
||||
};
|
||||
}
|
||||
|
||||
public stop() {}
|
||||
|
|
|
@ -22,6 +22,9 @@
|
|||
"@kbn/object-versioning",
|
||||
"@kbn/content-management-utils",
|
||||
"@kbn/content-management-plugin",
|
||||
"@kbn/es-query",
|
||||
"@kbn/utility-types-jest",
|
||||
"@kbn/expressions-plugin",
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
|
|
27
test/functional/fixtures/kbn_archiver/saved_search.json
Normal file
27
test/functional/fixtures/kbn_archiver/saved_search.json
Normal file
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"attributes": {
|
||||
"columns": [
|
||||
"_source"
|
||||
],
|
||||
"description": "A Saved Search Description",
|
||||
"hits": 0,
|
||||
"kibanaSavedObjectMeta": {
|
||||
"searchSourceJSON": "{\"query\":{\"query\":\"geo.src :\\\"FR\\\" \",\"language\":\"kuery\"},\"filter\":[]}"
|
||||
},
|
||||
"sort": [],
|
||||
"title": "A Saved Search",
|
||||
"version": 1,
|
||||
"timeRange" : {
|
||||
"from": "2006-09-21T00:00:00Z",
|
||||
"to": "2015-09-22T00:00:00Z"
|
||||
}
|
||||
},
|
||||
"coreMigrationVersion": "7.17.1",
|
||||
"id": "ab12e3c0-f231-11e6-9486-733b1ac9221a",
|
||||
"migrationVersion": {
|
||||
"search": "7.9.3"
|
||||
},
|
||||
"references": [],
|
||||
"type": "search",
|
||||
"version": "WzQzLDJd"
|
||||
}
|
|
@ -23,6 +23,7 @@ export default function ({
|
|||
updateBaselines,
|
||||
}: FtrProviderContext & { updateBaselines: boolean }) {
|
||||
const supertest = getService('supertest');
|
||||
const kibanaServer = getService('kibanaServer');
|
||||
let expectExpression: ExpectExpression;
|
||||
|
||||
const expectClientToMatchServer = async (title: string, expression: string) => {
|
||||
|
@ -92,6 +93,43 @@ export default function ({
|
|||
});
|
||||
});
|
||||
|
||||
describe('loads a saved search', () => {
|
||||
before(async () => {
|
||||
await kibanaServer.importExport.load(
|
||||
'test/functional/fixtures/kbn_archiver/saved_search.json'
|
||||
);
|
||||
});
|
||||
after(async () => {
|
||||
await kibanaServer.importExport.unload(
|
||||
'test/functional/fixtures/kbn_archiver/saved_search.json'
|
||||
);
|
||||
});
|
||||
|
||||
const expression = `
|
||||
kibana_context savedSearchId="ab12e3c0-f231-11e6-9486-733b1ac9221a"
|
||||
| esaggs index={indexPatternLoad id='logstash-*'}
|
||||
aggs={aggCount id="1" enabled=true schema="metric"}
|
||||
`;
|
||||
|
||||
it('correctly applies filter from saved search', async () => {
|
||||
const result = await expectExpression('esaggs_saved_searches', expression).getResponse();
|
||||
expect(getCell(result, 0, 0)).to.be(119);
|
||||
});
|
||||
|
||||
it('correctly applies filter - on the server', async () => {
|
||||
await supertest
|
||||
.post('/api/interpreter_functional/run_expression')
|
||||
.set('kbn-xsrf', 'anything')
|
||||
.send({ expression, input: undefined })
|
||||
.expect(200)
|
||||
.expect(({ body }) => {
|
||||
expect(body.columns[0].meta.index).to.be('logstash-*');
|
||||
expect(body.columns[0].meta.source).to.be('esaggs');
|
||||
expect(getCell(body, 0, 0)).to.be(119);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('correctly runs on the server', () => {
|
||||
it('runs the provided agg on the server', async () => {
|
||||
const expression = `
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import type { IUiSettingsClient } from '@kbn/core/public';
|
||||
import { DataView } from '@kbn/data-views-plugin/common';
|
||||
import { createSearchItems } from './new_job_utils';
|
||||
import { fromSavedSearchAttributes } from '@kbn/saved-search-plugin/public/services/saved_searches/saved_searches_utils';
|
||||
import { fromSavedSearchAttributes } from '@kbn/saved-search-plugin/common';
|
||||
import type { ISearchSource } from '@kbn/data-plugin/public';
|
||||
|
||||
describe('createSearchItems', () => {
|
||||
|
@ -42,17 +42,9 @@ describe('createSearchItems', () => {
|
|||
isTextBasedQuery: false,
|
||||
},
|
||||
[],
|
||||
[
|
||||
{
|
||||
name: 'kibanaSavedObjectMeta.searchSourceJSON.index',
|
||||
type: 'index-pattern',
|
||||
id: '7e252840-bd27-11ea-8a6c-75d1a0bd08ab',
|
||||
},
|
||||
],
|
||||
{
|
||||
getField: getFieldMock(searchSource),
|
||||
} as unknown as ISearchSource,
|
||||
{}
|
||||
} as unknown as ISearchSource
|
||||
);
|
||||
|
||||
test('should match data view', () => {
|
||||
|
|
|
@ -2004,11 +2004,11 @@
|
|||
"data.search.functions.ipRange.from.help": "Spécifier l'adresse de début",
|
||||
"data.search.functions.ipRange.help": "Créer une plage d'IP",
|
||||
"data.search.functions.ipRange.to.help": "Spécifier l'adresse de fin",
|
||||
"data.search.functions.kibana_context.filters.help": "Spécifier des filtres génériques Kibana",
|
||||
"data.search.functions.kibana_context.help": "Met à jour le contexte général de Kibana.",
|
||||
"data.search.functions.kibana_context.q.help": "Spécifier une recherche en texte libre Kibana",
|
||||
"data.search.functions.kibana_context.savedSearchId.help": "Spécifier l'ID de recherche enregistrée à utiliser pour les requêtes et les filtres",
|
||||
"data.search.functions.kibana_context.timeRange.help": "Spécifier le filtre de plage temporelle Kibana",
|
||||
"savedSearch.kibana_context.filters.help": "Spécifier des filtres génériques Kibana",
|
||||
"savedSearch.kibana_context.help": "Met à jour le contexte général de Kibana.",
|
||||
"savedSearch.kibana_context.q.help": "Spécifier une recherche en texte libre Kibana",
|
||||
"savedSearch.kibana_context.savedSearchId.help": "Spécifier l'ID de recherche enregistrée à utiliser pour les requêtes et les filtres",
|
||||
"savedSearch.kibana_context.timeRange.help": "Spécifier le filtre de plage temporelle Kibana",
|
||||
"data.search.functions.kibana.help": "Permet d’obtenir le contexte général de Kibana.",
|
||||
"data.search.functions.kibanaFilter.disabled.help": "Si le filtre doit être désactivé",
|
||||
"data.search.functions.kibanaFilter.field.help": "Spécifier une recherche en texte libre esdsl",
|
||||
|
|
|
@ -2004,11 +2004,11 @@
|
|||
"data.search.functions.ipRange.from.help": "開始アドレスを指定",
|
||||
"data.search.functions.ipRange.help": "IP範囲を作成",
|
||||
"data.search.functions.ipRange.to.help": "終了アドレスを指定",
|
||||
"data.search.functions.kibana_context.filters.help": "Kibana ジェネリックフィルターを指定します",
|
||||
"data.search.functions.kibana_context.help": "Kibana グローバルコンテキストを更新します",
|
||||
"data.search.functions.kibana_context.q.help": "自由形式の Kibana テキストクエリを指定します",
|
||||
"data.search.functions.kibana_context.savedSearchId.help": "クエリとフィルターに使用する保存検索ID を指定します。",
|
||||
"data.search.functions.kibana_context.timeRange.help": "Kibana 時間範囲フィルターを指定します",
|
||||
"savedSearch.kibana_context.filters.help": "Kibana ジェネリックフィルターを指定します",
|
||||
"savedSearch.kibana_context.help": "Kibana グローバルコンテキストを更新します",
|
||||
"savedSearch.kibana_context.q.help": "自由形式の Kibana テキストクエリを指定します",
|
||||
"savedSearch.kibana_context.savedSearchId.help": "クエリとフィルターに使用する保存検索ID を指定します。",
|
||||
"savedSearch.kibana_context.timeRange.help": "Kibana 時間範囲フィルターを指定します",
|
||||
"data.search.functions.kibana.help": "Kibana グローバルコンテキストを取得します",
|
||||
"data.search.functions.kibanaFilter.disabled.help": "フィルターは無効でなければなりません",
|
||||
"data.search.functions.kibanaFilter.field.help": "フリーフォームesdslクエリを指定",
|
||||
|
|
|
@ -2004,11 +2004,11 @@
|
|||
"data.search.functions.ipRange.from.help": "指定开始地址",
|
||||
"data.search.functions.ipRange.help": "创建 IP 范围",
|
||||
"data.search.functions.ipRange.to.help": "指定结束地址",
|
||||
"data.search.functions.kibana_context.filters.help": "指定 Kibana 常规筛选",
|
||||
"data.search.functions.kibana_context.help": "更新 kibana 全局上下文",
|
||||
"data.search.functions.kibana_context.q.help": "指定 Kibana 自由格式文本查询",
|
||||
"data.search.functions.kibana_context.savedSearchId.help": "指定要用于查询和筛选的已保存搜索 ID",
|
||||
"data.search.functions.kibana_context.timeRange.help": "指定 Kibana 时间范围筛选",
|
||||
"savedSearch.kibana_context.filters.help": "指定 Kibana 常规筛选",
|
||||
"savedSearch.kibana_context.help": "更新 kibana 全局上下文",
|
||||
"savedSearch.kibana_context.q.help": "指定 Kibana 自由格式文本查询",
|
||||
"savedSearch.kibana_context.savedSearchId.help": "指定要用于查询和筛选的已保存搜索 ID",
|
||||
"savedSearch.kibana_context.timeRange.help": "指定 Kibana 时间范围筛选",
|
||||
"data.search.functions.kibana.help": "获取 kibana 全局上下文",
|
||||
"data.search.functions.kibanaFilter.disabled.help": "如果禁用该筛选",
|
||||
"data.search.functions.kibanaFilter.field.help": "指定自由格式 esdsl 查询",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue