mirror of
https://github.com/elastic/kibana.git
synced 2025-06-27 10:40:07 -04:00
[data / data views] Disable rollup functionality when rollup plugin is disabled (#162674)
## Summary Rollup functionality will be disabled on serverless instances. The `rollup` plugin will be disabled. While it won't be possible to create rollups on serverless, it will be possible to import data view saved objects which may be configured for use with rollups. We will make a 'best effort' to improve functionality as to help users transition away from rollups. - In classic environments, the `rollup` plugin will enable `dataViews` and `data` plugin rollup functionality. - The data plugin will run an async search instead of a rollup search. - The data views plugin will fetch normal fields, omitting a query for rollup fields - which would fail anyway. Closes https://github.com/elastic/kibana/issues/152708 --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
874801bf7e
commit
a69870b950
25 changed files with 252 additions and 115 deletions
|
@ -28,7 +28,6 @@
|
|||
],
|
||||
"optionalPlugins": [
|
||||
"usageCollection",
|
||||
"taskManager",
|
||||
"security"
|
||||
],
|
||||
"requiredBundles": [
|
||||
|
|
|
@ -12,10 +12,6 @@ import { BfetchServerSetup } from '@kbn/bfetch-plugin/server';
|
|||
import { PluginStart as DataViewsServerPluginStart } from '@kbn/data-views-plugin/server';
|
||||
import { UsageCollectionSetup } from '@kbn/usage-collection-plugin/server';
|
||||
import { FieldFormatsSetup, FieldFormatsStart } from '@kbn/field-formats-plugin/server';
|
||||
import type {
|
||||
TaskManagerSetupContract,
|
||||
TaskManagerStartContract,
|
||||
} from '@kbn/task-manager-plugin/server';
|
||||
import type { SecurityPluginSetup } from '@kbn/security-plugin/server';
|
||||
import { ConfigSchema } from '../config';
|
||||
import type { ISearchSetup, ISearchStart } from './search';
|
||||
|
@ -55,7 +51,6 @@ export interface DataPluginSetupDependencies {
|
|||
expressions: ExpressionsServerSetup;
|
||||
usageCollection?: UsageCollectionSetup;
|
||||
fieldFormats: FieldFormatsSetup;
|
||||
taskManager?: TaskManagerSetupContract;
|
||||
security?: SecurityPluginSetup;
|
||||
}
|
||||
|
||||
|
@ -63,7 +58,6 @@ export interface DataPluginStartDependencies {
|
|||
fieldFormats: FieldFormatsStart;
|
||||
logger: Logger;
|
||||
dataViews: DataViewsServerPluginStart;
|
||||
taskManager?: TaskManagerStartContract;
|
||||
}
|
||||
|
||||
export class DataServerPlugin
|
||||
|
@ -90,14 +84,7 @@ export class DataServerPlugin
|
|||
|
||||
public setup(
|
||||
core: CoreSetup<DataPluginStartDependencies, DataPluginStart>,
|
||||
{
|
||||
bfetch,
|
||||
expressions,
|
||||
usageCollection,
|
||||
fieldFormats,
|
||||
taskManager,
|
||||
security,
|
||||
}: DataPluginSetupDependencies
|
||||
{ bfetch, expressions, usageCollection, fieldFormats, security }: DataPluginSetupDependencies
|
||||
) {
|
||||
this.scriptsService.setup(core);
|
||||
const querySetup = this.queryService.setup(core);
|
||||
|
@ -110,7 +97,6 @@ export class DataServerPlugin
|
|||
expressions,
|
||||
usageCollection,
|
||||
security,
|
||||
taskManager,
|
||||
});
|
||||
|
||||
return {
|
||||
|
@ -120,14 +106,10 @@ export class DataServerPlugin
|
|||
};
|
||||
}
|
||||
|
||||
public start(
|
||||
core: CoreStart,
|
||||
{ fieldFormats, dataViews, taskManager }: DataPluginStartDependencies
|
||||
) {
|
||||
public start(core: CoreStart, { fieldFormats, dataViews }: DataPluginStartDependencies) {
|
||||
const search = this.searchService.start(core, {
|
||||
fieldFormats,
|
||||
indexPatterns: dataViews,
|
||||
taskManager,
|
||||
});
|
||||
const datatableUtilities = new DatatableUtilitiesService(
|
||||
search.aggs,
|
||||
|
|
|
@ -17,6 +17,7 @@ export function createSearchSetupMock(): jest.Mocked<ISearchSetup> {
|
|||
aggs: searchAggsSetupMock(),
|
||||
registerSearchStrategy: jest.fn(),
|
||||
searchSource: searchSourceMock.createSetupContract(),
|
||||
enableRollups: jest.fn(),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -25,10 +25,6 @@ import { ExpressionsServerSetup } from '@kbn/expressions-plugin/server';
|
|||
import { FieldFormatsStart } from '@kbn/field-formats-plugin/server';
|
||||
import { UsageCollectionSetup } from '@kbn/usage-collection-plugin/server';
|
||||
import { KbnServerError } from '@kbn/kibana-utils-plugin/server';
|
||||
import type {
|
||||
TaskManagerSetupContract,
|
||||
TaskManagerStartContract,
|
||||
} from '@kbn/task-manager-plugin/server';
|
||||
import type { SecurityPluginSetup } from '@kbn/security-plugin/server';
|
||||
import type { DataViewsServerPluginStart } from '@kbn/data-views-plugin/server';
|
||||
import type {
|
||||
|
@ -107,7 +103,6 @@ export interface SearchServiceSetupDependencies {
|
|||
bfetch: BfetchServerSetup;
|
||||
expressions: ExpressionsServerSetup;
|
||||
usageCollection?: UsageCollectionSetup;
|
||||
taskManager?: TaskManagerSetupContract;
|
||||
security?: SecurityPluginSetup;
|
||||
}
|
||||
|
||||
|
@ -115,7 +110,6 @@ export interface SearchServiceSetupDependencies {
|
|||
export interface SearchServiceStartDependencies {
|
||||
fieldFormats: FieldFormatsStart;
|
||||
indexPatterns: DataViewsServerPluginStart;
|
||||
taskManager?: TaskManagerStartContract;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
|
@ -131,6 +125,7 @@ export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
|
|||
private sessionService: SearchSessionService;
|
||||
private asScoped!: ISearchStart['asScoped'];
|
||||
private searchAsInternalUser!: ISearchStrategy;
|
||||
private rollupsEnabled: boolean = false;
|
||||
|
||||
constructor(
|
||||
private initializerContext: PluginInitializerContext<ConfigSchema>,
|
||||
|
@ -145,7 +140,7 @@ export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
|
|||
|
||||
public setup(
|
||||
core: CoreSetup<DataPluginStartDependencies, DataPluginStart>,
|
||||
{ bfetch, expressions, usageCollection, taskManager, security }: SearchServiceSetupDependencies
|
||||
{ bfetch, expressions, usageCollection, security }: SearchServiceSetupDependencies
|
||||
): ISearchSetup {
|
||||
core.savedObjects.registerType(searchSessionSavedObjectType);
|
||||
const usage = usageCollection ? usageProvider(core) : undefined;
|
||||
|
@ -261,12 +256,13 @@ export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
|
|||
registerSearchStrategy: this.registerSearchStrategy,
|
||||
usage,
|
||||
searchSource: this.searchSourceService.setup(),
|
||||
enableRollups: () => (this.rollupsEnabled = true),
|
||||
};
|
||||
}
|
||||
|
||||
public start(
|
||||
core: CoreStart,
|
||||
{ fieldFormats, indexPatterns, taskManager }: SearchServiceStartDependencies
|
||||
{ fieldFormats, indexPatterns }: SearchServiceStartDependencies
|
||||
): ISearchStart {
|
||||
const { elasticsearch, savedObjects, uiSettings } = core;
|
||||
|
||||
|
@ -278,7 +274,7 @@ export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
|
|||
indexPatterns,
|
||||
});
|
||||
|
||||
this.asScoped = this.asScopedProvider(core);
|
||||
this.asScoped = this.asScopedProvider(core, this.rollupsEnabled);
|
||||
return {
|
||||
aggs,
|
||||
searchAsInternalUser: this.searchAsInternalUser,
|
||||
|
@ -516,7 +512,7 @@ export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
|
|||
return deps.searchSessionsClient.extend(sessionId, expires);
|
||||
};
|
||||
|
||||
private asScopedProvider = (core: CoreStart) => {
|
||||
private asScopedProvider = (core: CoreStart, rollupsEnabled: boolean = false) => {
|
||||
const { elasticsearch, savedObjects, uiSettings } = core;
|
||||
const getSessionAsScoped = this.sessionService.asScopedProvider(core);
|
||||
return (request: KibanaRequest): IScopedSearchClient => {
|
||||
|
@ -530,6 +526,7 @@ export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
|
|||
uiSettings.asScopedToClient(savedObjectsClient)
|
||||
),
|
||||
request,
|
||||
rollupsEnabled,
|
||||
};
|
||||
return {
|
||||
search: <
|
||||
|
|
|
@ -64,6 +64,7 @@ describe('ES search strategy', () => {
|
|||
},
|
||||
},
|
||||
searchSessionsClient: createSearchSessionsClientMock(),
|
||||
rollupsEnabled: true,
|
||||
} as unknown as SearchStrategyDependencies;
|
||||
const mockLegacyConfig$ = new BehaviorSubject<any>({
|
||||
elasticsearch: {
|
||||
|
@ -233,6 +234,31 @@ describe('ES search strategy', () => {
|
|||
expect(method).toBe('POST');
|
||||
expect(path).toBe('/foo-%E7%A8%8B/_rollup_search');
|
||||
});
|
||||
|
||||
it("doesn't call the rollup API if the index is a rollup type BUT rollups are disabled", async () => {
|
||||
mockApiCaller.mockResolvedValueOnce(mockRollupResponse);
|
||||
mockSubmitCaller.mockResolvedValueOnce(mockAsyncResponse);
|
||||
|
||||
const params = { index: 'foo-程', body: { query: {} } };
|
||||
const esSearch = await enhancedEsSearchStrategyProvider(
|
||||
mockLegacyConfig$,
|
||||
mockSearchConfig,
|
||||
mockLogger
|
||||
);
|
||||
|
||||
await esSearch
|
||||
.search(
|
||||
{
|
||||
indexType: 'rollup',
|
||||
params,
|
||||
},
|
||||
{},
|
||||
{ ...mockDeps, rollupsEnabled: false }
|
||||
)
|
||||
.toPromise();
|
||||
|
||||
expect(mockApiCaller).toBeCalledTimes(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with sessionId', () => {
|
||||
|
|
|
@ -154,7 +154,7 @@ export const enhancedEsSearchStrategyProvider = (
|
|||
throw new KbnServerError('Unknown indexType', 400);
|
||||
}
|
||||
|
||||
if (request.indexType === undefined) {
|
||||
if (request.indexType === undefined || !deps.rollupsEnabled) {
|
||||
return asyncSearch(request, options, deps);
|
||||
} else {
|
||||
return from(rollupSearch(request, options, deps));
|
||||
|
|
|
@ -35,6 +35,7 @@ export interface SearchStrategyDependencies {
|
|||
uiSettingsClient: Pick<IUiSettingsClient, 'get'>;
|
||||
searchSessionsClient: IScopedSearchSessionsClient;
|
||||
request: KibanaRequest;
|
||||
rollupsEnabled?: boolean;
|
||||
}
|
||||
|
||||
export interface ISearchSetup {
|
||||
|
@ -55,7 +56,7 @@ export interface ISearchSetup {
|
|||
* Used internally for telemetry
|
||||
*/
|
||||
usage?: SearchUsage;
|
||||
|
||||
enableRollups: () => void;
|
||||
searchSource: ReturnType<SearchSourceService['setup']>;
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
"@kbn/field-formats-plugin",
|
||||
"@kbn/data-views-plugin",
|
||||
"@kbn/screenshot-mode-plugin",
|
||||
"@kbn/task-manager-plugin",
|
||||
"@kbn/security-plugin",
|
||||
"@kbn/expressions-plugin",
|
||||
"@kbn/field-types",
|
||||
|
|
|
@ -25,6 +25,7 @@ interface DataViewsServiceFactoryDeps {
|
|||
uiSettings: UiSettingsServiceStart;
|
||||
fieldFormats: FieldFormatsStart;
|
||||
capabilities: CoreStart['capabilities'];
|
||||
rollupsEnabled: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -38,14 +39,18 @@ export const dataViewsServiceFactory = (deps: DataViewsServiceFactoryDeps) =>
|
|||
request?: KibanaRequest,
|
||||
byPassCapabilities?: boolean
|
||||
) {
|
||||
const { logger, uiSettings, fieldFormats, capabilities } = deps;
|
||||
const { logger, uiSettings, fieldFormats, capabilities, rollupsEnabled } = deps;
|
||||
const uiSettingsClient = uiSettings.asScopedToClient(savedObjectsClient);
|
||||
const formats = await fieldFormats.fieldFormatServiceFactory(uiSettingsClient);
|
||||
|
||||
return new DataViewsService({
|
||||
uiSettings: new UiSettingsServerToCommon(uiSettingsClient),
|
||||
savedObjectsClient: new SavedObjectsClientWrapper(savedObjectsClient),
|
||||
apiClient: new IndexPatternsApiServer(elasticsearchClient, savedObjectsClient),
|
||||
apiClient: new IndexPatternsApiServer(
|
||||
elasticsearchClient,
|
||||
savedObjectsClient,
|
||||
rollupsEnabled
|
||||
),
|
||||
fieldFormats: formats,
|
||||
onError: (error) => {
|
||||
logger.error(error);
|
||||
|
|
|
@ -10,6 +10,19 @@ import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
|
|||
import { IndexPatternsFetcher } from '.';
|
||||
import { elasticsearchServiceMock } from '@kbn/core/server/mocks';
|
||||
|
||||
const rollupResponse = {
|
||||
foo: {
|
||||
rollup_jobs: [
|
||||
{
|
||||
index_pattern: 'foo',
|
||||
job_id: '123',
|
||||
rollup_index: 'foo',
|
||||
fields: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
describe('Index Pattern Fetcher - server', () => {
|
||||
let indexPatterns: IndexPatternsFetcher;
|
||||
let esClient: ReturnType<typeof elasticsearchServiceMock.createElasticsearchClient>;
|
||||
|
@ -21,12 +34,40 @@ describe('Index Pattern Fetcher - server', () => {
|
|||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
esClient = elasticsearchServiceMock.createElasticsearchClient();
|
||||
indexPatterns = new IndexPatternsFetcher(esClient);
|
||||
indexPatterns = new IndexPatternsFetcher(esClient, false, true);
|
||||
});
|
||||
it('calls fieldcaps once', async () => {
|
||||
esClient.fieldCaps.mockResponse(response as unknown as estypes.FieldCapsResponse);
|
||||
indexPatterns = new IndexPatternsFetcher(esClient, true);
|
||||
indexPatterns = new IndexPatternsFetcher(esClient, true, true);
|
||||
await indexPatterns.getFieldsForWildcard({ pattern: patternList });
|
||||
expect(esClient.fieldCaps).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('calls rollup api when given rollup data view', async () => {
|
||||
esClient.fieldCaps.mockResponse(response as unknown as estypes.FieldCapsResponse);
|
||||
esClient.rollup.getRollupIndexCaps.mockResponse(
|
||||
rollupResponse as unknown as estypes.RollupGetRollupIndexCapsResponse
|
||||
);
|
||||
indexPatterns = new IndexPatternsFetcher(esClient, true, true);
|
||||
await indexPatterns.getFieldsForWildcard({
|
||||
pattern: patternList,
|
||||
type: 'rollup',
|
||||
rollupIndex: 'foo',
|
||||
});
|
||||
expect(esClient.rollup.getRollupIndexCaps).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("doesn't call rollup api when given rollup data view and rollups are disabled", async () => {
|
||||
esClient.fieldCaps.mockResponse(response as unknown as estypes.FieldCapsResponse);
|
||||
esClient.rollup.getRollupIndexCaps.mockResponse(
|
||||
rollupResponse as unknown as estypes.RollupGetRollupIndexCapsResponse
|
||||
);
|
||||
indexPatterns = new IndexPatternsFetcher(esClient, true, false);
|
||||
await indexPatterns.getFieldsForWildcard({
|
||||
pattern: patternList,
|
||||
type: 'rollup',
|
||||
rollupIndex: 'foo',
|
||||
});
|
||||
expect(esClient.rollup.getRollupIndexCaps).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -40,10 +40,16 @@ interface FieldSubType {
|
|||
export class IndexPatternsFetcher {
|
||||
private elasticsearchClient: ElasticsearchClient;
|
||||
private allowNoIndices: boolean;
|
||||
private rollupsEnabled: boolean;
|
||||
|
||||
constructor(elasticsearchClient: ElasticsearchClient, allowNoIndices: boolean = false) {
|
||||
constructor(
|
||||
elasticsearchClient: ElasticsearchClient,
|
||||
allowNoIndices: boolean = false,
|
||||
rollupsEnabled: boolean = false
|
||||
) {
|
||||
this.elasticsearchClient = elasticsearchClient;
|
||||
this.allowNoIndices = allowNoIndices;
|
||||
this.rollupsEnabled = rollupsEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -81,7 +87,7 @@ export class IndexPatternsFetcher {
|
|||
fields: options.fields || ['*'],
|
||||
});
|
||||
|
||||
if (type === 'rollup' && rollupIndex) {
|
||||
if (this.rollupsEnabled && type === 'rollup' && rollupIndex) {
|
||||
const rollupFields: FieldDescriptor[] = [];
|
||||
const capabilityCheck = getCapabilitiesForRollupIndices(
|
||||
await this.elasticsearchClient.rollup.getRollupIndexCaps({
|
||||
|
|
|
@ -10,6 +10,7 @@ export { getFieldByName, findIndexPatternById } from './utils';
|
|||
export type { FieldDescriptor, RollupIndexCapability } from './fetcher';
|
||||
export { IndexPatternsFetcher, getCapabilitiesForRollupIndices } from './fetcher';
|
||||
export type {
|
||||
DataViewsServerPluginSetup,
|
||||
DataViewsServerPluginStart,
|
||||
DataViewsServerPluginSetupDependencies,
|
||||
DataViewsServerPluginStartDependencies,
|
||||
|
|
|
@ -16,7 +16,8 @@ export class IndexPatternsApiServer implements IDataViewsApiClient {
|
|||
esClient: ElasticsearchClient;
|
||||
constructor(
|
||||
elasticsearchClient: ElasticsearchClient,
|
||||
private readonly savedObjectsClient: SavedObjectsClientContract
|
||||
private readonly savedObjectsClient: SavedObjectsClientContract,
|
||||
private readonly rollupsEnabled: boolean
|
||||
) {
|
||||
this.esClient = elasticsearchClient;
|
||||
}
|
||||
|
@ -29,7 +30,11 @@ export class IndexPatternsApiServer implements IDataViewsApiClient {
|
|||
indexFilter,
|
||||
fields,
|
||||
}: GetFieldsOptions) {
|
||||
const indexPatterns = new IndexPatternsFetcher(this.esClient, allowNoIndex);
|
||||
const indexPatterns = new IndexPatternsFetcher(
|
||||
this.esClient,
|
||||
allowNoIndex,
|
||||
this.rollupsEnabled
|
||||
);
|
||||
return await indexPatterns
|
||||
.getFieldsForWildcard({
|
||||
pattern,
|
||||
|
|
|
@ -33,6 +33,7 @@ export class DataViewsServerPlugin
|
|||
>
|
||||
{
|
||||
private readonly logger: Logger;
|
||||
private rollupsEnabled: boolean = false;
|
||||
|
||||
constructor(initializerContext: PluginInitializerContext) {
|
||||
this.logger = initializerContext.logger.get('dataView');
|
||||
|
@ -46,7 +47,12 @@ export class DataViewsServerPlugin
|
|||
core.capabilities.registerProvider(capabilitiesProvider);
|
||||
const dataViewRestCounter = usageCollection?.createUsageCounter('dataViewsRestApi');
|
||||
|
||||
registerRoutes(core.http, core.getStartServices, dataViewRestCounter);
|
||||
registerRoutes(
|
||||
core.http,
|
||||
core.getStartServices,
|
||||
() => this.rollupsEnabled,
|
||||
dataViewRestCounter
|
||||
);
|
||||
|
||||
expressions.registerFunction(getIndexPatternLoad({ getStartServices: core.getStartServices }));
|
||||
registerIndexPatternsUsageCollector(core.getStartServices, usageCollection);
|
||||
|
@ -60,7 +66,9 @@ export class DataViewsServerPlugin
|
|||
},
|
||||
});
|
||||
|
||||
return {};
|
||||
return {
|
||||
enableRollups: () => (this.rollupsEnabled = true),
|
||||
};
|
||||
}
|
||||
|
||||
public start(
|
||||
|
@ -72,6 +80,7 @@ export class DataViewsServerPlugin
|
|||
uiSettings,
|
||||
fieldFormats,
|
||||
capabilities,
|
||||
rollupsEnabled: this.rollupsEnabled,
|
||||
});
|
||||
|
||||
return {
|
||||
|
|
|
@ -111,86 +111,93 @@ const validate: FullValidationConfig<any, any, any> = {
|
|||
},
|
||||
};
|
||||
|
||||
const handler: RequestHandler<{}, IQuery, IBody> = async (context, request, response) => {
|
||||
const { asCurrentUser } = (await context.core).elasticsearch.client;
|
||||
const indexPatterns = new IndexPatternsFetcher(asCurrentUser);
|
||||
const {
|
||||
pattern,
|
||||
meta_fields: metaFields,
|
||||
type,
|
||||
rollup_index: rollupIndex,
|
||||
allow_no_index: allowNoIndex,
|
||||
include_unmapped: includeUnmapped,
|
||||
} = request.query;
|
||||
|
||||
// not available to get request
|
||||
const indexFilter = request.body?.index_filter;
|
||||
|
||||
let parsedFields: string[] = [];
|
||||
let parsedMetaFields: string[] = [];
|
||||
try {
|
||||
parsedMetaFields = parseFields(metaFields);
|
||||
parsedFields = parseFields(request.query.fields ?? []);
|
||||
} catch (error) {
|
||||
return response.badRequest();
|
||||
}
|
||||
|
||||
try {
|
||||
const { fields, indices } = await indexPatterns.getFieldsForWildcard({
|
||||
const handler: (isRollupsEnabled: () => boolean) => RequestHandler<{}, IQuery, IBody> =
|
||||
(isRollupsEnabled) => async (context, request, response) => {
|
||||
const { asCurrentUser } = (await context.core).elasticsearch.client;
|
||||
const indexPatterns = new IndexPatternsFetcher(asCurrentUser, undefined, isRollupsEnabled());
|
||||
const {
|
||||
pattern,
|
||||
metaFields: parsedMetaFields,
|
||||
meta_fields: metaFields,
|
||||
type,
|
||||
rollupIndex,
|
||||
fieldCapsOptions: {
|
||||
allow_no_indices: allowNoIndex || false,
|
||||
includeUnmapped,
|
||||
},
|
||||
indexFilter,
|
||||
...(parsedFields.length > 0 ? { fields: parsedFields } : {}),
|
||||
});
|
||||
rollup_index: rollupIndex,
|
||||
allow_no_index: allowNoIndex,
|
||||
include_unmapped: includeUnmapped,
|
||||
} = request.query;
|
||||
|
||||
const body: { fields: FieldDescriptorRestResponse[]; indices: string[] } = { fields, indices };
|
||||
// not available to get request
|
||||
const indexFilter = request.body?.index_filter;
|
||||
|
||||
return response.ok({
|
||||
body,
|
||||
headers: {
|
||||
'content-type': 'application/json',
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
if (
|
||||
typeof error === 'object' &&
|
||||
!!error?.isBoom &&
|
||||
!!error?.output?.payload &&
|
||||
typeof error?.output?.payload === 'object'
|
||||
) {
|
||||
const payload = error?.output?.payload;
|
||||
return response.notFound({
|
||||
body: {
|
||||
message: payload.message,
|
||||
attributes: payload,
|
||||
let parsedFields: string[] = [];
|
||||
let parsedMetaFields: string[] = [];
|
||||
try {
|
||||
parsedMetaFields = parseFields(metaFields);
|
||||
parsedFields = parseFields(request.query.fields ?? []);
|
||||
} catch (error) {
|
||||
return response.badRequest();
|
||||
}
|
||||
|
||||
try {
|
||||
const { fields, indices } = await indexPatterns.getFieldsForWildcard({
|
||||
pattern,
|
||||
metaFields: parsedMetaFields,
|
||||
type,
|
||||
rollupIndex,
|
||||
fieldCapsOptions: {
|
||||
allow_no_indices: allowNoIndex || false,
|
||||
includeUnmapped,
|
||||
},
|
||||
indexFilter,
|
||||
...(parsedFields.length > 0 ? { fields: parsedFields } : {}),
|
||||
});
|
||||
|
||||
const body: { fields: FieldDescriptorRestResponse[]; indices: string[] } = {
|
||||
fields,
|
||||
indices,
|
||||
};
|
||||
|
||||
return response.ok({
|
||||
body,
|
||||
headers: {
|
||||
'content-type': 'application/json',
|
||||
},
|
||||
});
|
||||
} else {
|
||||
return response.notFound();
|
||||
} catch (error) {
|
||||
if (
|
||||
typeof error === 'object' &&
|
||||
!!error?.isBoom &&
|
||||
!!error?.output?.payload &&
|
||||
typeof error?.output?.payload === 'object'
|
||||
) {
|
||||
const payload = error?.output?.payload;
|
||||
return response.notFound({
|
||||
body: {
|
||||
message: payload.message,
|
||||
attributes: payload,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
return response.notFound();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const registerFieldForWildcard = (
|
||||
export const registerFieldForWildcard = async (
|
||||
router: IRouter,
|
||||
getStartServices: StartServicesAccessor<
|
||||
DataViewsServerPluginStartDependencies,
|
||||
DataViewsServerPluginStart
|
||||
>
|
||||
>,
|
||||
isRollupsEnabled: () => boolean
|
||||
) => {
|
||||
const configuredHandler = handler(isRollupsEnabled);
|
||||
|
||||
// handler
|
||||
router.versioned.put({ path, access }).addVersion({ version, validate }, handler);
|
||||
router.versioned.post({ path, access }).addVersion({ version, validate }, handler);
|
||||
router.versioned.put({ path, access }).addVersion({ version, validate }, configuredHandler);
|
||||
router.versioned.post({ path, access }).addVersion({ version, validate }, configuredHandler);
|
||||
router.versioned
|
||||
.get({ path, access })
|
||||
.addVersion(
|
||||
{ version, validate: { request: { query: querySchema }, response: validate.response } },
|
||||
handler
|
||||
configuredHandler
|
||||
);
|
||||
};
|
||||
|
|
|
@ -20,12 +20,13 @@ export function registerRoutes(
|
|||
DataViewsServerPluginStartDependencies,
|
||||
DataViewsServerPluginStart
|
||||
>,
|
||||
isRollupsEnabled: () => boolean,
|
||||
dataViewRestCounter?: UsageCounter
|
||||
) {
|
||||
const router = http.createRouter();
|
||||
|
||||
routes.forEach((route) => route(router, getStartServices, dataViewRestCounter));
|
||||
|
||||
registerFieldForWildcard(router, getStartServices);
|
||||
registerFieldForWildcard(router, getStartServices, isRollupsEnabled);
|
||||
registerHasDataViewsRoute(router);
|
||||
}
|
||||
|
|
|
@ -53,8 +53,9 @@ export interface DataViewsServerPluginStart {
|
|||
/**
|
||||
* DataViews server plugin setup api
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
export interface DataViewsServerPluginSetup {}
|
||||
export interface DataViewsServerPluginSetup {
|
||||
enableRollups: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Data Views server setup dependencies
|
||||
|
|
|
@ -298,6 +298,7 @@ const createSearchStrategyDependenciesMock = (): SearchStrategyDependencies => (
|
|||
savedObjectsClient: savedObjectsClientMock.create(),
|
||||
searchSessionsClient: createSearchSessionsClientMock(),
|
||||
request: httpServerMock.createKibanaRequest(),
|
||||
rollupsEnabled: true,
|
||||
});
|
||||
|
||||
// using the official data mock from within x-pack doesn't type-check successfully,
|
||||
|
|
|
@ -13,13 +13,14 @@
|
|||
"requiredPlugins": [
|
||||
"management",
|
||||
"licensing",
|
||||
"features"
|
||||
"features",
|
||||
"dataViews",
|
||||
"data"
|
||||
],
|
||||
"optionalPlugins": [
|
||||
"home",
|
||||
"indexManagement",
|
||||
"usageCollection",
|
||||
"visTypeTimeseries"
|
||||
"usageCollection"
|
||||
],
|
||||
"requiredBundles": [
|
||||
"kibanaUtils",
|
||||
|
|
|
@ -30,8 +30,8 @@ export class RollupPlugin implements Plugin<void, void, any, any> {
|
|||
}
|
||||
|
||||
public setup(
|
||||
{ http, uiSettings, savedObjects, getStartServices }: CoreSetup,
|
||||
{ features, licensing, indexManagement, visTypeTimeseries, usageCollection }: Dependencies
|
||||
{ http, uiSettings, getStartServices }: CoreSetup,
|
||||
{ features, licensing, indexManagement, usageCollection, dataViews, data }: Dependencies
|
||||
) {
|
||||
this.license.setup(
|
||||
{
|
||||
|
@ -103,6 +103,8 @@ export class RollupPlugin implements Plugin<void, void, any, any> {
|
|||
if (indexManagement && indexManagement.indexDataEnricher) {
|
||||
indexManagement.indexDataEnricher.add(rollupDataEnricher);
|
||||
}
|
||||
dataViews.enableRollups();
|
||||
data.search.enableRollups();
|
||||
}
|
||||
|
||||
start() {}
|
||||
|
|
|
@ -12,6 +12,8 @@ import { VisTypeTimeseriesSetup } from '@kbn/vis-type-timeseries-plugin/server';
|
|||
import { getCapabilitiesForRollupIndices } from '@kbn/data-plugin/server';
|
||||
import { IndexManagementPluginSetup } from '@kbn/index-management-plugin/server';
|
||||
import { PluginSetupContract as FeaturesPluginSetup } from '@kbn/features-plugin/server';
|
||||
import { DataViewsServerPluginSetup } from '@kbn/data-views-plugin/server';
|
||||
import { PluginSetup as DataPluginSetup } from '@kbn/data-plugin/server';
|
||||
import { LicensingPluginSetup } from '@kbn/licensing-plugin/server';
|
||||
import { License } from './services';
|
||||
import { IndexPatternsFetcher } from './shared_imports';
|
||||
|
@ -24,6 +26,8 @@ export interface Dependencies {
|
|||
usageCollection?: UsageCollectionSetup;
|
||||
licensing: LicensingPluginSetup;
|
||||
features: FeaturesPluginSetup;
|
||||
dataViews: DataViewsServerPluginSetup;
|
||||
data: DataPluginSetup;
|
||||
}
|
||||
|
||||
export interface RouteDependencies {
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
"@kbn/i18n-react",
|
||||
"@kbn/config-schema",
|
||||
"@kbn/shared-ux-router",
|
||||
"@kbn/data-views-plugin",
|
||||
|
||||
],
|
||||
"exclude": [
|
||||
|
|
|
@ -12,5 +12,6 @@ export default function ({ loadTestFile }: FtrProviderContext) {
|
|||
loadTestFile(require.resolve('./security_users'));
|
||||
loadTestFile(require.resolve('./spaces'));
|
||||
loadTestFile(require.resolve('./security_response_headers'));
|
||||
loadTestFile(require.resolve('./rollups'));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import expect from 'expect';
|
||||
import { ELASTIC_HTTP_VERSION_HEADER } from '@kbn/core-http-common';
|
||||
import { INITIAL_REST_VERSION_INTERNAL } from '@kbn/data-views-plugin/server/constants';
|
||||
import { X_ELASTIC_INTERNAL_ORIGIN_REQUEST } from '@kbn/core-http-common/src/constants';
|
||||
import { FIELDS_FOR_WILDCARD_PATH as BASE_URI } from '@kbn/data-views-plugin/common/constants';
|
||||
import { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
||||
export default function ({ getService }: FtrProviderContext) {
|
||||
const supertest = getService('supertest');
|
||||
const esArchiver = getService('esArchiver');
|
||||
|
||||
describe('rollup data views - fields for wildcard', function () {
|
||||
before(async () => {
|
||||
await esArchiver.load('test/api_integration/fixtures/es_archiver/index_patterns/basic_index');
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await esArchiver.unload(
|
||||
'test/api_integration/fixtures/es_archiver/index_patterns/basic_index'
|
||||
);
|
||||
});
|
||||
it('returns 200 and best effort response despite lack of rollup support', async () => {
|
||||
const response = await supertest
|
||||
.get(BASE_URI)
|
||||
.query({
|
||||
pattern: 'basic_index',
|
||||
type: 'rollup',
|
||||
rollup_index: 'bar',
|
||||
})
|
||||
.set(ELASTIC_HTTP_VERSION_HEADER, INITIAL_REST_VERSION_INTERNAL)
|
||||
.set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'true');
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body.fields.length).toEqual(5);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -44,5 +44,7 @@
|
|||
"@kbn/fleet-plugin",
|
||||
"@kbn/cases-plugin",
|
||||
"@kbn/test-subj-selector",
|
||||
"@kbn/core-http-common",
|
||||
"@kbn/data-views-plugin",
|
||||
]
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue