|
@@ -1,3 +1,4 @@
|
|
|
|
|
+import _ from 'lodash';
|
|
|
import Plain from 'slate-plain-serializer';
|
|
import Plain from 'slate-plain-serializer';
|
|
|
|
|
|
|
|
import QueryField from './query_field';
|
|
import QueryField from './query_field';
|
|
@@ -25,21 +26,43 @@ interface SuggestionGroup {
|
|
|
skipFilter?: boolean;
|
|
skipFilter?: boolean;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+interface KustoSchema {
|
|
|
|
|
+ Databases: {
|
|
|
|
|
+ Default?: KustoDBSchema;
|
|
|
|
|
+ };
|
|
|
|
|
+ Plugins?: any[];
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+interface KustoDBSchema {
|
|
|
|
|
+ Name?: string;
|
|
|
|
|
+ Functions?: any;
|
|
|
|
|
+ Tables?: any;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const defaultSchema = () => ({
|
|
|
|
|
+ Databases: {
|
|
|
|
|
+ Default: {}
|
|
|
|
|
+ }
|
|
|
|
|
+});
|
|
|
|
|
+
|
|
|
const cleanText = s => s.replace(/[{}[\]="(),!~+\-*/^%]/g, '').trim();
|
|
const cleanText = s => s.replace(/[{}[\]="(),!~+\-*/^%]/g, '').trim();
|
|
|
const wrapText = text => ({ text });
|
|
const wrapText = text => ({ text });
|
|
|
|
|
|
|
|
export default class KustoQueryField extends QueryField {
|
|
export default class KustoQueryField extends QueryField {
|
|
|
fields: any;
|
|
fields: any;
|
|
|
events: any;
|
|
events: any;
|
|
|
|
|
+ schema: KustoSchema;
|
|
|
|
|
|
|
|
constructor(props, context) {
|
|
constructor(props, context) {
|
|
|
super(props, context);
|
|
super(props, context);
|
|
|
|
|
+ this.schema = defaultSchema();
|
|
|
|
|
|
|
|
this.onTypeahead = debounce(this.onTypeahead, TYPEAHEAD_DELAY);
|
|
this.onTypeahead = debounce(this.onTypeahead, TYPEAHEAD_DELAY);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
componentDidMount() {
|
|
componentDidMount() {
|
|
|
this.updateMenu();
|
|
this.updateMenu();
|
|
|
|
|
+ this.fetchSchema();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
onTypeahead = () => {
|
|
onTypeahead = () => {
|
|
@@ -128,7 +151,13 @@ export default class KustoQueryField extends QueryField {
|
|
|
suggestionGroups = this._getKeywordSuggestions();
|
|
suggestionGroups = this._getKeywordSuggestions();
|
|
|
} else if (Plain.serialize(this.state.value) === '') {
|
|
} else if (Plain.serialize(this.state.value) === '') {
|
|
|
typeaheadContext = 'context-new';
|
|
typeaheadContext = 'context-new';
|
|
|
- suggestionGroups = this._getInitialSuggestions();
|
|
|
|
|
|
|
+ if (this.schema) {
|
|
|
|
|
+ suggestionGroups = this._getInitialSuggestions();
|
|
|
|
|
+ } else {
|
|
|
|
|
+ this.fetchSchema();
|
|
|
|
|
+ setTimeout(this.onTypeahead, 0);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
let results = 0;
|
|
let results = 0;
|
|
@@ -263,7 +292,7 @@ export default class KustoQueryField extends QueryField {
|
|
|
{
|
|
{
|
|
|
prefixMatch: true,
|
|
prefixMatch: true,
|
|
|
label: 'Operators',
|
|
label: 'Operators',
|
|
|
- items: operatorTokens.map((s: any) => { s.type = 'function'; return s; })
|
|
|
|
|
|
|
+ items: operatorTokens
|
|
|
},
|
|
},
|
|
|
{
|
|
{
|
|
|
prefixMatch: true,
|
|
prefixMatch: true,
|
|
@@ -274,34 +303,46 @@ export default class KustoQueryField extends QueryField {
|
|
|
prefixMatch: true,
|
|
prefixMatch: true,
|
|
|
label: 'Macros',
|
|
label: 'Macros',
|
|
|
items: grafanaMacros.map((s: any) => { s.type = 'function'; return s; })
|
|
items: grafanaMacros.map((s: any) => { s.type = 'function'; return s; })
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ prefixMatch: true,
|
|
|
|
|
+ label: 'Tables',
|
|
|
|
|
+ items: _.map(this.schema.Databases.Default.Tables, (t: any) => ({ text: t.Name }))
|
|
|
}
|
|
}
|
|
|
];
|
|
];
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private _getInitialSuggestions(): SuggestionGroup[] {
|
|
private _getInitialSuggestions(): SuggestionGroup[] {
|
|
|
- // TODO: return datbase tables as an initial suggestion
|
|
|
|
|
return [
|
|
return [
|
|
|
{
|
|
{
|
|
|
prefixMatch: true,
|
|
prefixMatch: true,
|
|
|
- label: 'Keywords',
|
|
|
|
|
- items: KEYWORDS.map(wrapText)
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- prefixMatch: true,
|
|
|
|
|
- label: 'Operators',
|
|
|
|
|
- items: operatorTokens.map((s: any) => { s.type = 'function'; return s; })
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- prefixMatch: true,
|
|
|
|
|
- label: 'Functions',
|
|
|
|
|
- items: functionTokens.map((s: any) => { s.type = 'function'; return s; })
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- prefixMatch: true,
|
|
|
|
|
- label: 'Macros',
|
|
|
|
|
- items: grafanaMacros.map((s: any) => { s.type = 'function'; return s; })
|
|
|
|
|
|
|
+ label: 'Tables',
|
|
|
|
|
+ items: _.map(this.schema.Databases.Default.Tables, (t: any) => ({ text: t.Name }))
|
|
|
}
|
|
}
|
|
|
];
|
|
];
|
|
|
|
|
+
|
|
|
|
|
+ // return [
|
|
|
|
|
+ // {
|
|
|
|
|
+ // prefixMatch: true,
|
|
|
|
|
+ // label: 'Keywords',
|
|
|
|
|
+ // items: KEYWORDS.map(wrapText)
|
|
|
|
|
+ // },
|
|
|
|
|
+ // {
|
|
|
|
|
+ // prefixMatch: true,
|
|
|
|
|
+ // label: 'Operators',
|
|
|
|
|
+ // items: operatorTokens.map((s: any) => { s.type = 'function'; return s; })
|
|
|
|
|
+ // },
|
|
|
|
|
+ // {
|
|
|
|
|
+ // prefixMatch: true,
|
|
|
|
|
+ // label: 'Functions',
|
|
|
|
|
+ // items: functionTokens.map((s: any) => { s.type = 'function'; return s; })
|
|
|
|
|
+ // },
|
|
|
|
|
+ // {
|
|
|
|
|
+ // prefixMatch: true,
|
|
|
|
|
+ // label: 'Macros',
|
|
|
|
|
+ // items: grafanaMacros.map((s: any) => { s.type = 'function'; return s; })
|
|
|
|
|
+ // }
|
|
|
|
|
+ // ];
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private async _fetchEvents() {
|
|
private async _fetchEvents() {
|
|
@@ -329,4 +370,13 @@ export default class KustoQueryField extends QueryField {
|
|
|
// Stub
|
|
// Stub
|
|
|
this.fields = [];
|
|
this.fields = [];
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ private async fetchSchema() {
|
|
|
|
|
+ const schema = await this.props.getSchema();
|
|
|
|
|
+ if (schema) {
|
|
|
|
|
+ this.schema = schema;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ this.schema = defaultSchema();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|