Skip to content

Commit

Permalink
Super rough example
Browse files Browse the repository at this point in the history
  • Loading branch information
Kerry350 committed Sep 9, 2020
1 parent 28c5f27 commit daf4351
Show file tree
Hide file tree
Showing 5 changed files with 180 additions and 5 deletions.
1 change: 0 additions & 1 deletion x-pack/plugins/infra/kibana.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
"features",
"usageCollection",
"spaces",

"data",
"dataEnhanced",
"visTypeTimeseries",
Expand Down
49 changes: 46 additions & 3 deletions x-pack/plugins/infra/public/pages/logs/page_content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,9 @@

import { EuiFlexGroup, EuiFlexItem, EuiButtonEmpty } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React from 'react';
import React, { useEffect, useState, useCallback} from 'react';
import { Route, Switch } from 'react-router-dom';
import { useMount } from 'react-use';

import { useKibana } from '../../../../../../src/plugins/kibana_react/public';
import { DocumentTitle } from '../../components/document_title';
import { Header } from '../../components/header';
Expand All @@ -24,6 +23,45 @@ import { LogEntryRatePage } from './log_entry_rate';
import { LogsSettingsPage } from './settings';
import { StreamPage } from './stream';
import { AlertDropdown } from '../../components/alerting/logs/alert_dropdown';
import { LogEntriesQueries } from '../../../server/search_strategy/provider';

const SearchStrategyTest = () => {
const abortController = new AbortController();
const { services: { data } } = useKibana();
const [response, setResponse] = useState({});
const [request, setRequest] = useState({queryType: LogEntriesQueries.item});

// NOTE: This is just a rough example. Cancellation and abort signals are not handled properly here,
// we'd need to handle getData being called more than once, we'd also need to make this all hook-centric.
// This is primarily to show how we'd use our search strategy, and how we'd handle partial results and errors etc.
const getData = useCallback((request) => {
const searchSubscription$ = data.search.search<any, any>({queryType: LogEntriesQueries.item}, {
strategy: 'infraSearchStrategy',
signal: abortController.signal,
})
.subscribe({
next: (response) => {
const { isPartial, isRunning } = response;
// Set partial results
setResponse(response);
if (!isPartial && !isRunning) {
// We have all results
searchSubscription$.unsubscribe();
} else if (isPartial && !isRunning) {
// Something went wrong
}
}
});
}, [data.search]);

useEffect(() => {
getData(request);
}, [request])

return (
<div>{JSON.stringify(response,null,'\t')}</div>
)
};

export const LogsPageContent: React.FunctionComponent = () => {
const uiCapabilities = useKibana().services.application?.capabilities;
Expand Down Expand Up @@ -77,7 +115,11 @@ export const LogsPageContent: React.FunctionComponent = () => {
<AppNavigation aria-label={pageTitle}>
<EuiFlexGroup gutterSize={'none'} alignItems={'center'}>
<EuiFlexItem>
<RoutedTabs tabs={[streamTab, anomaliesTab, logCategoriesTab, settingsTab]} />
<RoutedTabs tabs={[streamTab, anomaliesTab, logCategoriesTab, settingsTab, {
app: 'logs',
title: 'Search strategy test',
pathname: '/search-strategy',
}]} />
</EuiFlexItem>
<EuiFlexItem grow={false}>
<AlertDropdown />
Expand All @@ -99,6 +141,7 @@ export const LogsPageContent: React.FunctionComponent = () => {
<Route path={anomaliesTab.pathname} component={LogEntryRatePage} />
<Route path={logCategoriesTab.pathname} component={LogEntryCategoriesPage} />
<Route path={settingsTab.pathname} component={LogsSettingsPage} />
<Route path={'/search-strategy'} component={SearchStrategyTest} />
<RedirectWithQueryParams from={'/analysis'} to={anomaliesTab.pathname} exact />
<RedirectWithQueryParams from={'/log-rate'} to={anomaliesTab.pathname} exact />
<RedirectWithQueryParams from={'/'} to={streamTab.pathname} exact />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { PluginSetupContract as FeaturesPluginSetup } from '../../../../../../pl
import { SpacesPluginSetup } from '../../../../../../plugins/spaces/server';
import { PluginSetupContract as AlertingPluginContract } from '../../../../../alerts/server';
import { MlPluginSetup } from '../../../../../ml/server';
import { DataPluginSetup, DataPluginStart } from '../../../../../../../src/plugins/data/server/plugin';

export interface InfraServerPluginDeps {
home: HomeServerPluginSetup;
Expand All @@ -25,6 +26,7 @@ export interface InfraServerPluginDeps {
apm: APMPluginSetup;
alerts: AlertingPluginContract;
ml?: MlPluginSetup;
data: DataPluginSetup;
}

export interface CallWithRequestParams extends GenericParams {
Expand Down
11 changes: 10 additions & 1 deletion x-pack/plugins/infra/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { infraSourceConfigurationSavedObjectType } from './lib/sources';
import { metricsExplorerViewSavedObjectType } from '../common/saved_objects/metrics_explorer_view';
import { inventoryViewSavedObjectType } from '../common/saved_objects/inventory_view';
import { InfraRequestHandlerContext } from './types';
import { infraSearchStrategyProvider } from './search_strategy/provider';

export const config = {
schema: schema.object({
Expand Down Expand Up @@ -166,7 +167,15 @@ export class InfraServerPlugin {

// Telemetry
UsageCollector.registerUsageCollector(plugins.usageCollection);


// Register custom search strategy
core.getStartServices().then(([_, startPlugins]) => {
const infraSearchStrategy = infraSearchStrategyProvider(startPlugins.data);
plugins.data.search.registerSearchStrategy(
'infraSearchStrategy',
infraSearchStrategy
);
});
return {
defineInternalSourceConfiguration(sourceId, sourceProperties) {
sources.defineInternalSourceConfiguration(sourceId, sourceProperties);
Expand Down
122 changes: 122 additions & 0 deletions x-pack/plugins/infra/server/search_strategy/provider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { ISearchStrategy, PluginStart } from '../../../../../src/plugins/data/server';
import { IEsSearchResponse, ISearchRequestParams } from '../../../../../src/plugins/data/common';

// NOTE: Types are shown as super rough examples here. We usually stay away from enums, so they'd likely be
// keyof via io-ts. And 'any' is used in some places as it just sped up putting the example together.
interface ExampleRequestOptions {
queryType: FactoryQueryTypes,
params?: {
size: 10,
body: {
query: {}
}
}
}

type LogEntriesEntriesReqestOptions = ExampleRequestOptions;
type LogEntriesItemReqestOptions = ExampleRequestOptions;

interface ExampleResponse extends IEsSearchResponse {
results: {
name: string
}[]
};

type LogEntriesEntriesStrategyResponse = ExampleResponse;
type LogEntriesItemStrategyResponse = ExampleResponse;

export enum LogEntriesQueries {
entries = 'entries',
highlights = 'highlights',
summaryBuckets = 'summaryBuckets',
item = 'item',
}

type FactoryQueryTypes = LogEntriesQueries;

type StrategyRequestType<T extends FactoryQueryTypes> = T extends LogEntriesQueries.entries
? LogEntriesEntriesReqestOptions
: T extends LogEntriesQueries.item
? LogEntriesItemReqestOptions
: never;

export type StrategyResponseType<T extends FactoryQueryTypes> = T extends LogEntriesQueries.entries
? LogEntriesEntriesStrategyResponse
: T extends LogEntriesQueries.item
? LogEntriesItemStrategyResponse
: never;



export interface InfraQueryTypeFactory<T extends FactoryQueryTypes> {
buildDsl: (options: StrategyRequestType<T>) => ISearchRequestParams;
parse: (
options: StrategyRequestType<T>,
response: IEsSearchResponse
) => Promise<StrategyResponseType<T>>;
}


// NOTE: Switch harcoded ID to something you know exists to try this out
const queryTypeFactories = {
[LogEntriesQueries.item]: {
buildDsl: (options: any) => {
return {
"index":"logs-*,filebeat-*,kibana_sample_data_logs*",
"terminate_after":1,
"body":{
"size":1,
"sort":[
{
"@timestamp":"desc"
},
{
"_doc":"desc"
}
],
"query":{
"ids":{
"values":[
"ww5WbXQBHyE_1lJ-g4vn"
]
}
}
}
}
},
parse: async (
options: any,
response: any,
): Promise<any> => {
return { ...response }
},
}
}
export const infraSearchStrategyProvider = <T extends FactoryQueryTypes>(
data: PluginStart
): ISearchStrategy<StrategyRequestType<T>, StrategyResponseType<T>> => {
const es = data.search.getSearchStrategy('es');

return {
search: async (context, request, options) => {
if (request.queryType == null) {
throw new Error('queryType is required');
}
const queryFactory: InfraQueryTypeFactory<T> = queryTypeFactories[request.queryType];
const dsl = queryFactory.buildDsl(request);
const esSearchRes = await es.search(context, { ...request, params: dsl }, options);
return queryFactory.parse(request, esSearchRes);
},
cancel: async (context, id) => {
if (es.cancel) {
es.cancel(context, id);
}
},
};
};

0 comments on commit daf4351

Please sign in to comment.