Make Explore plugin exports explicit

This commit is contained in:
David Kaltschmidt
2018-10-30 14:38:34 +01:00
parent d0776937b5
commit cf19ecc8f5
10 changed files with 55 additions and 62 deletions

View File

@ -2,7 +2,6 @@ import { DEFAULT_RANGE, serializeStateToUrlParam, parseUrlState } from './explor
import { ExploreState } from 'app/types/explore'; import { ExploreState } from 'app/types/explore';
const DEFAULT_EXPLORE_STATE: ExploreState = { const DEFAULT_EXPLORE_STATE: ExploreState = {
customComponents: {},
datasource: null, datasource: null,
datasourceError: null, datasourceError: null,
datasourceLoading: null, datasourceLoading: null,

View File

@ -17,13 +17,14 @@ import IndicatorsContainer from 'app/core/components/Picker/IndicatorsContainer'
import NoOptionsMessage from 'app/core/components/Picker/NoOptionsMessage'; import NoOptionsMessage from 'app/core/components/Picker/NoOptionsMessage';
import TableModel, { mergeTablesIntoModel } from 'app/core/table_model'; import TableModel, { mergeTablesIntoModel } from 'app/core/table_model';
import DefaultQueryRows from './QueryRows'; import QueryRows from './QueryRows';
import DefaultGraph from './Graph'; import Graph from './Graph';
import DefaultLogs from './Logs'; import Logs from './Logs';
import DefaultTable from './Table'; import Table from './Table';
import ErrorBoundary from './ErrorBoundary'; import ErrorBoundary from './ErrorBoundary';
import TimePicker from './TimePicker'; import TimePicker from './TimePicker';
import { ensureQueries, generateQueryKey, hasQuery } from './utils/query'; import { ensureQueries, generateQueryKey, hasQuery } from './utils/query';
import { DataSource } from 'app/types/datasources';
const MAX_HISTORY_ITEMS = 100; const MAX_HISTORY_ITEMS = 100;
@ -96,7 +97,6 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
initialQueries = ensureQueries(queries); initialQueries = ensureQueries(queries);
const initialRange = range || { ...DEFAULT_RANGE }; const initialRange = range || { ...DEFAULT_RANGE };
this.state = { this.state = {
customComponents: {},
datasource: null, datasource: null,
datasourceError: null, datasourceError: null,
datasourceLoading: null, datasourceLoading: null,
@ -149,7 +149,7 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
} }
} }
async setDatasource(datasource) { async setDatasource(datasource: DataSource) {
const supportsGraph = datasource.meta.metrics; const supportsGraph = datasource.meta.metrics;
const supportsLogs = datasource.meta.logs; const supportsLogs = datasource.meta.logs;
const supportsTable = datasource.meta.metrics; const supportsTable = datasource.meta.metrics;
@ -177,13 +177,12 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
query: this.queryExpressions[i], query: this.queryExpressions[i],
})); }));
const customComponents = { // Custom components
...datasource.exploreComponents, const StartPage = datasource.pluginExports.ExploreStartPage;
};
this.setState( this.setState(
{ {
customComponents, StartPage,
datasource, datasource,
datasourceError, datasourceError,
history, history,
@ -398,9 +397,9 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
q.query = this.queryExpressions[i]; q.query = this.queryExpressions[i];
return i === index return i === index
? { ? {
key: generateQueryKey(index), key: generateQueryKey(index),
query: datasource.modifyQuery(q.query, action), query: datasource.modifyQuery(q.query, action),
} }
: q; : q;
}); });
nextQueryTransactions = queryTransactions nextQueryTransactions = queryTransactions
@ -734,7 +733,7 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
render() { render() {
const { position, split } = this.props; const { position, split } = this.props;
const { const {
customComponents, StartPage,
datasource, datasource,
datasourceError, datasourceError,
datasourceLoading, datasourceLoading,
@ -774,14 +773,7 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
queryTransactions.filter(qt => qt.resultType === 'Logs' && qt.done && qt.result).map(qt => qt.result) queryTransactions.filter(qt => qt.resultType === 'Logs' && qt.done && qt.result).map(qt => qt.result)
); );
const loading = queryTransactions.some(qt => !qt.done); const loading = queryTransactions.some(qt => !qt.done);
const showStartPages = queryTransactions.length === 0 && customComponents.StartPage; const showStartPages = StartPage && queryTransactions.length === 0;
// Custom components
const Graph = customComponents.Graph || DefaultGraph;
const Logs = customComponents.Logs || DefaultLogs;
const QueryRows = customComponents.QueryRows || DefaultQueryRows;
const StartPage = customComponents.StartPage;
const Table = customComponents.Table || DefaultTable;
return ( return (
<div className={exploreClass} ref={this.getRef}> <div className={exploreClass} ref={this.getRef}>
@ -794,12 +786,12 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
</a> </a>
</div> </div>
) : ( ) : (
<div className="navbar-buttons explore-first-button"> <div className="navbar-buttons explore-first-button">
<button className="btn navbar-button" onClick={this.onClickCloseSplit}> <button className="btn navbar-button" onClick={this.onClickCloseSplit}>
Close Split Close Split
</button> </button>
</div> </div>
)} )}
{!datasourceMissing ? ( {!datasourceMissing ? (
<div className="navbar-buttons"> <div className="navbar-buttons">
<Select <Select
@ -858,7 +850,6 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
{datasource && !datasourceError ? ( {datasource && !datasourceError ? (
<div className="explore-container"> <div className="explore-container">
<QueryRows <QueryRows
customComponents={customComponents}
datasource={datasource} datasource={datasource}
history={history} history={history}
queries={queries} queries={queries}
@ -879,17 +870,17 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
{supportsGraph ? ( {supportsGraph ? (
<button className={`btn toggle-btn ${graphButtonActive}`} onClick={this.onClickGraphButton}> <button className={`btn toggle-btn ${graphButtonActive}`} onClick={this.onClickGraphButton}>
Graph Graph
</button> </button>
) : null} ) : null}
{supportsTable ? ( {supportsTable ? (
<button className={`btn toggle-btn ${tableButtonActive}`} onClick={this.onClickTableButton}> <button className={`btn toggle-btn ${tableButtonActive}`} onClick={this.onClickTableButton}>
Table Table
</button> </button>
) : null} ) : null}
{supportsLogs ? ( {supportsLogs ? (
<button className={`btn toggle-btn ${logsButtonActive}`} onClick={this.onClickLogsButton}> <button className={`btn toggle-btn ${logsButtonActive}`} onClick={this.onClickLogsButton}>
Logs Logs
</button> </button>
) : null} ) : null}
</div> </div>

View File

@ -27,7 +27,7 @@ function hasSuggestions(suggestions: CompletionItemGroup[]): boolean {
return suggestions && suggestions.length > 0; return suggestions && suggestions.length > 0;
} }
interface TypeaheadFieldProps { interface QueryFieldProps {
additionalPlugins?: any[]; additionalPlugins?: any[];
cleanText?: (text: string) => string; cleanText?: (text: string) => string;
initialValue: string | null; initialValue: string | null;
@ -35,14 +35,14 @@ interface TypeaheadFieldProps {
onFocus?: () => void; onFocus?: () => void;
onTypeahead?: (typeahead: TypeaheadInput) => TypeaheadOutput; onTypeahead?: (typeahead: TypeaheadInput) => TypeaheadOutput;
onValueChanged?: (value: Value) => void; onValueChanged?: (value: Value) => void;
onWillApplySuggestion?: (suggestion: string, state: TypeaheadFieldState) => string; onWillApplySuggestion?: (suggestion: string, state: QueryFieldState) => string;
placeholder?: string; placeholder?: string;
portalOrigin?: string; portalOrigin?: string;
syntax?: string; syntax?: string;
syntaxLoaded?: boolean; syntaxLoaded?: boolean;
} }
export interface TypeaheadFieldState { export interface QueryFieldState {
suggestions: CompletionItemGroup[]; suggestions: CompletionItemGroup[];
typeaheadContext: string | null; typeaheadContext: string | null;
typeaheadIndex: number; typeaheadIndex: number;
@ -60,7 +60,7 @@ export interface TypeaheadInput {
wrapperNode: Element; wrapperNode: Element;
} }
class QueryField extends React.PureComponent<TypeaheadFieldProps, TypeaheadFieldState> { export class QueryField extends React.PureComponent<QueryFieldProps, QueryFieldState> {
menuEl: HTMLElement | null; menuEl: HTMLElement | null;
placeholdersBuffer: PlaceholdersBuffer; placeholdersBuffer: PlaceholdersBuffer;
plugins: any[]; plugins: any[];
@ -102,7 +102,7 @@ class QueryField extends React.PureComponent<TypeaheadFieldProps, TypeaheadField
} }
} }
componentWillReceiveProps(nextProps: TypeaheadFieldProps) { componentWillReceiveProps(nextProps: QueryFieldProps) {
if (nextProps.syntaxLoaded && !this.props.syntaxLoaded) { if (nextProps.syntaxLoaded && !this.props.syntaxLoaded) {
// Need a bogus edit to re-render the editor after syntax has fully loaded // Need a bogus edit to re-render the editor after syntax has fully loaded
const change = this.state.value const change = this.state.value

View File

@ -4,6 +4,7 @@ import { QueryTransaction, HistoryItem, Query, QueryHint } from 'app/types/explo
import DefaultQueryField from './QueryField'; import DefaultQueryField from './QueryField';
import QueryTransactionStatus from './QueryTransactionStatus'; import QueryTransactionStatus from './QueryTransactionStatus';
import { DataSource } from 'app/types';
function getFirstHintFromTransactions(transactions: QueryTransaction[]): QueryHint { function getFirstHintFromTransactions(transactions: QueryTransaction[]): QueryHint {
const transaction = transactions.find(qt => qt.hints && qt.hints.length > 0); const transaction = transactions.find(qt => qt.hints && qt.hints.length > 0);
@ -23,8 +24,7 @@ interface QueryRowEventHandlers {
interface QueryRowCommonProps { interface QueryRowCommonProps {
className?: string; className?: string;
customComponents: any; datasource: DataSource;
datasource: any;
history: HistoryItem[]; history: HistoryItem[];
// Temporarily // Temporarily
supportsLogs?: boolean; supportsLogs?: boolean;
@ -37,7 +37,7 @@ type QueryRowProps = QueryRowCommonProps &
query: string; query: string;
}; };
class DefaultQueryRow extends PureComponent<QueryRowProps> { class QueryRow extends PureComponent<QueryRowProps> {
onChangeQuery = (value, override?: boolean) => { onChangeQuery = (value, override?: boolean) => {
const { index, onChangeQuery } = this.props; const { index, onChangeQuery } = this.props;
if (onChangeQuery) { if (onChangeQuery) {
@ -78,11 +78,11 @@ class DefaultQueryRow extends PureComponent<QueryRowProps> {
}; };
render() { render() {
const { customComponents, datasource, history, query, supportsLogs, transactions } = this.props; const { datasource, history, query, supportsLogs, transactions } = this.props;
const transactionWithError = transactions.find(t => t.error !== undefined); const transactionWithError = transactions.find(t => t.error !== undefined);
const hint = getFirstHintFromTransactions(transactions); const hint = getFirstHintFromTransactions(transactions);
const queryError = transactionWithError ? transactionWithError.error : null; const queryError = transactionWithError ? transactionWithError.error : null;
const QueryField = customComponents.QueryField || DefaultQueryField; const QueryField = datasource.pluginExports.ExploreQueryField || DefaultQueryField;
return ( return (
<div className="query-row"> <div className="query-row">
<div className="query-row-status"> <div className="query-row-status">
@ -124,14 +124,12 @@ type QueryRowsProps = QueryRowCommonProps &
export default class QueryRows extends PureComponent<QueryRowsProps> { export default class QueryRows extends PureComponent<QueryRowsProps> {
render() { render() {
const { className = '', customComponents, queries, transactions, ...handlers } = this.props; const { className = '', queries, transactions, ...handlers } = this.props;
const QueryRow = customComponents.QueryRow || DefaultQueryRow;
return ( return (
<div className={className}> <div className={className}>
{queries.map((q, index) => ( {queries.map((q, index) => (
<QueryRow <QueryRow
key={q.key} key={q.key}
customComponents={customComponents}
index={index} index={index}
query={q.query} query={q.query}
transactions={transactions.filter(t => t.rowIndex === index)} transactions={transactions.filter(t => t.rowIndex === index)}

View File

@ -8,9 +8,10 @@ import { importPluginModule } from './plugin_loader';
// Types // Types
import { DataSourceApi } from 'app/types/series'; import { DataSourceApi } from 'app/types/series';
import { DataSource } from 'app/types';
export class DatasourceSrv { export class DatasourceSrv {
datasources: any; datasources: { [name: string]: DataSource };
/** @ngInject */ /** @ngInject */
constructor(private $q, private $injector, private $rootScope, private templateSrv) { constructor(private $q, private $injector, private $rootScope, private templateSrv) {
@ -61,10 +62,10 @@ export class DatasourceSrv {
throw new Error('Plugin module is missing Datasource constructor'); throw new Error('Plugin module is missing Datasource constructor');
} }
const instance = this.$injector.instantiate(plugin.Datasource, { instanceSettings: dsConfig }); const instance: DataSource = this.$injector.instantiate(plugin.Datasource, { instanceSettings: dsConfig });
instance.meta = pluginDef; instance.meta = pluginDef;
instance.name = name; instance.name = name;
instance.exploreComponents = plugin.ExploreComponents; instance.pluginExports = plugin;
this.datasources[name] = instance; this.datasources[name] = instance;
deferred.resolve(instance); deferred.resolve(instance);
}) })

View File

@ -10,7 +10,7 @@ import { TypeaheadOutput } from 'app/types/explore';
import { getNextCharacter, getPreviousCousin } from 'app/features/explore/utils/dom'; import { getNextCharacter, getPreviousCousin } from 'app/features/explore/utils/dom';
import BracesPlugin from 'app/features/explore/slate-plugins/braces'; import BracesPlugin from 'app/features/explore/slate-plugins/braces';
import RunnerPlugin from 'app/features/explore/slate-plugins/runner'; import RunnerPlugin from 'app/features/explore/slate-plugins/runner';
import TypeaheadField, { TypeaheadInput, TypeaheadFieldState } from 'app/features/explore/QueryField'; import TypeaheadField, { TypeaheadInput, QueryFieldState } from 'app/features/explore/QueryField';
const HISTOGRAM_GROUP = '__histograms__'; const HISTOGRAM_GROUP = '__histograms__';
const METRIC_MARK = 'metric'; const METRIC_MARK = 'metric';
@ -50,10 +50,7 @@ export function groupMetricsByPrefix(metrics: string[], delimiter = '_'): Cascad
return [...options, ...metricsOptions]; return [...options, ...metricsOptions];
} }
export function willApplySuggestion( export function willApplySuggestion(suggestion: string, { typeaheadContext, typeaheadText }: QueryFieldState): string {
suggestion: string,
{ typeaheadContext, typeaheadText }: TypeaheadFieldState
): string {
// Modify suggestion based on context // Modify suggestion based on context
switch (typeaheadContext) { switch (typeaheadContext) {
case 'context-labels': { case 'context-labels': {

View File

@ -9,15 +9,11 @@ class PrometheusAnnotationsQueryCtrl {
static templateUrl = 'partials/annotations.editor.html'; static templateUrl = 'partials/annotations.editor.html';
} }
const ExploreComponents = {
QueryField: PromQueryField,
StartPage: PrometheusStartPage,
};
export { export {
PrometheusDatasource as Datasource, PrometheusDatasource as Datasource,
PrometheusQueryCtrl as QueryCtrl, PrometheusQueryCtrl as QueryCtrl,
PrometheusConfigCtrl as ConfigCtrl, PrometheusConfigCtrl as ConfigCtrl,
PrometheusAnnotationsQueryCtrl as AnnotationsQueryCtrl, PrometheusAnnotationsQueryCtrl as AnnotationsQueryCtrl,
ExploreComponents, PromQueryField as ExploreQueryField,
PrometheusStartPage as ExploreStartPage,
}; };

View File

@ -1,5 +1,5 @@
import { LayoutMode } from '../core/components/LayoutSelector/LayoutSelector'; import { LayoutMode } from '../core/components/LayoutSelector/LayoutSelector';
import { Plugin } from './plugins'; import { Plugin, PluginExports, PluginMeta } from './plugins';
export interface DataSource { export interface DataSource {
id: number; id: number;
@ -16,6 +16,10 @@ export interface DataSource {
isDefault: boolean; isDefault: boolean;
jsonData: { authType: string; defaultRegion: string }; jsonData: { authType: string; defaultRegion: string };
readOnly: boolean; readOnly: boolean;
meta?: PluginMeta;
pluginExports?: PluginExports;
init?: () => void;
testDatasource?: () => Promise<any>;
} }
export interface DataSourcesState { export interface DataSourcesState {

View File

@ -146,7 +146,7 @@ export interface TextMatch {
} }
export interface ExploreState { export interface ExploreState {
customComponents: any; StartPage?: any;
datasource: any; datasource: any;
datasourceError: any; datasourceError: any;
datasourceLoading: boolean | null; datasourceLoading: boolean | null;

View File

@ -6,7 +6,8 @@ export interface PluginExports {
ConfigCtrl?: any; ConfigCtrl?: any;
AnnotationsQueryCtrl?: any; AnnotationsQueryCtrl?: any;
PanelOptions?: any; PanelOptions?: any;
ExploreComponents?: any; ExploreQueryField?: any;
ExploreStartPage?: any;
} }
export interface PanelPlugin { export interface PanelPlugin {
@ -26,6 +27,12 @@ export interface PluginMeta {
name: string; name: string;
info: PluginMetaInfo; info: PluginMetaInfo;
includes: PluginInclude[]; includes: PluginInclude[];
// Datasource-specific
metrics?: boolean;
logs?: boolean;
explore?: boolean;
annotations?: boolean;
} }
export interface PluginInclude { export interface PluginInclude {