Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
N
nexpie-grafana-theme
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Registry
Registry
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Kornkitt Poolsup
nexpie-grafana-theme
Commits
6f2315d5
Commit
6f2315d5
authored
Oct 25, 2018
by
David Kaltschmidt
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Moved prom language features to datasource language provider
parent
54a3e2d1
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
320 additions
and
332 deletions
+320
-332
public/app/features/explore/Explore.tsx
+1
-6
public/app/features/explore/PromQueryField.test.tsx
+1
-228
public/app/features/explore/PromQueryField.tsx
+0
-0
public/app/features/explore/QueryField.tsx
+7
-81
public/app/features/explore/QueryRows.tsx
+3
-3
public/app/features/explore/Typeahead.tsx
+10
-10
public/app/plugins/datasource/prometheus/datasource.ts
+3
-0
public/app/plugins/datasource/prometheus/language_provider.ts
+0
-0
public/app/plugins/datasource/prometheus/language_utils.ts
+0
-3
public/app/plugins/datasource/prometheus/promql.ts
+0
-0
public/app/plugins/datasource/prometheus/specs/language_provider.test.ts
+202
-0
public/app/plugins/datasource/prometheus/specs/language_utils.test.ts
+1
-1
public/app/types/explore.ts
+92
-0
No files found.
public/app/features/explore/Explore.tsx
View file @
6f2315d5
...
...
@@ -695,11 +695,6 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
});
}
request
=
url
=>
{
const
{
datasource
}
=
this
.
state
;
return
datasource
.
metadataRequest
(
url
);
};
cloneState
():
ExploreState
{
// Copy state, but copy queries including modifications
return
{
...
...
@@ -831,9 +826,9 @@ export class Explore extends React.PureComponent<ExploreProps, ExploreState> {
{
datasource
&&
!
datasourceError
?
(
<
div
className=
"explore-container"
>
<
QueryRows
datasource=
{
datasource
}
history=
{
history
}
queries=
{
queries
}
request=
{
this
.
request
}
onAddQueryRow=
{
this
.
onAddQueryRow
}
onChangeQuery=
{
this
.
onChangeQuery
}
onClickHintFix=
{
this
.
onModifyQueries
}
...
...
public/app/features/explore/PromQueryField.test.tsx
View file @
6f2315d5
import
React
from
'react'
;
import
Enzyme
,
{
shallow
}
from
'enzyme'
;
import
Adapter
from
'enzyme-adapter-react-16'
;
import
Plain
from
'slate-plain-serializer'
;
import
PromQueryField
,
{
groupMetricsByPrefix
,
RECORDING_RULES_GROUP
}
from
'./PromQueryField'
;
Enzyme
.
configure
({
adapter
:
new
Adapter
()
});
describe
(
'PromQueryField typeahead handling'
,
()
=>
{
const
defaultProps
=
{
request
:
()
=>
({
data
:
{
data
:
[]
}
}),
};
it
(
'returns default suggestions on emtpty context'
,
()
=>
{
const
instance
=
shallow
(<
PromQueryField
{
...
defaultProps
}
/>).
instance
()
as
PromQueryField
;
const
result
=
instance
.
getTypeahead
({
text
:
''
,
prefix
:
''
,
wrapperClasses
:
[]
});
expect
(
result
.
context
).
toBeUndefined
();
expect
(
result
.
refresher
).
toBeUndefined
();
expect
(
result
.
suggestions
.
length
).
toEqual
(
2
);
});
describe
(
'range suggestions'
,
()
=>
{
it
(
'returns range suggestions in range context'
,
()
=>
{
const
instance
=
shallow
(<
PromQueryField
{
...
defaultProps
}
/>).
instance
()
as
PromQueryField
;
const
result
=
instance
.
getTypeahead
({
text
:
'1'
,
prefix
:
'1'
,
wrapperClasses
:
[
'context-range'
]
});
expect
(
result
.
context
).
toBe
(
'context-range'
);
expect
(
result
.
refresher
).
toBeUndefined
();
expect
(
result
.
suggestions
).
toEqual
([
{
items
:
[{
label
:
'1m'
},
{
label
:
'5m'
},
{
label
:
'10m'
},
{
label
:
'30m'
},
{
label
:
'1h'
}],
label
:
'Range vector'
,
},
]);
});
});
describe
(
'metric suggestions'
,
()
=>
{
it
(
'returns metrics suggestions by default'
,
()
=>
{
const
instance
=
shallow
(
<
PromQueryField
{
...
defaultProps
}
metrics=
{
[
'foo'
,
'bar'
]
}
/>
).
instance
()
as
PromQueryField
;
const
result
=
instance
.
getTypeahead
({
text
:
'a'
,
prefix
:
'a'
,
wrapperClasses
:
[]
});
expect
(
result
.
context
).
toBeUndefined
();
expect
(
result
.
refresher
).
toBeUndefined
();
expect
(
result
.
suggestions
.
length
).
toEqual
(
2
);
});
it
(
'returns default suggestions after a binary operator'
,
()
=>
{
const
instance
=
shallow
(
<
PromQueryField
{
...
defaultProps
}
metrics=
{
[
'foo'
,
'bar'
]
}
/>
).
instance
()
as
PromQueryField
;
const
result
=
instance
.
getTypeahead
({
text
:
'*'
,
prefix
:
''
,
wrapperClasses
:
[]
});
expect
(
result
.
context
).
toBeUndefined
();
expect
(
result
.
refresher
).
toBeUndefined
();
expect
(
result
.
suggestions
.
length
).
toEqual
(
2
);
});
});
describe
(
'label suggestions'
,
()
=>
{
it
(
'returns default label suggestions on label context and no metric'
,
()
=>
{
const
instance
=
shallow
(<
PromQueryField
{
...
defaultProps
}
/>).
instance
()
as
PromQueryField
;
const
value
=
Plain
.
deserialize
(
'{}'
);
const
range
=
value
.
selection
.
merge
({
anchorOffset
:
1
,
});
const
valueWithSelection
=
value
.
change
().
select
(
range
).
value
;
const
result
=
instance
.
getTypeahead
({
text
:
''
,
prefix
:
''
,
wrapperClasses
:
[
'context-labels'
],
value
:
valueWithSelection
,
});
expect
(
result
.
context
).
toBe
(
'context-labels'
);
expect
(
result
.
suggestions
).
toEqual
([{
items
:
[{
label
:
'job'
},
{
label
:
'instance'
}],
label
:
'Labels'
}]);
});
it
(
'returns label suggestions on label context and metric'
,
()
=>
{
const
instance
=
shallow
(
<
PromQueryField
{
...
defaultProps
}
labelKeys=
{
{
'{__name__="metric"}'
:
[
'bar'
]
}
}
/>
).
instance
()
as
PromQueryField
;
const
value
=
Plain
.
deserialize
(
'metric{}'
);
const
range
=
value
.
selection
.
merge
({
anchorOffset
:
7
,
});
const
valueWithSelection
=
value
.
change
().
select
(
range
).
value
;
const
result
=
instance
.
getTypeahead
({
text
:
''
,
prefix
:
''
,
wrapperClasses
:
[
'context-labels'
],
value
:
valueWithSelection
,
});
expect
(
result
.
context
).
toBe
(
'context-labels'
);
expect
(
result
.
suggestions
).
toEqual
([{
items
:
[{
label
:
'bar'
}],
label
:
'Labels'
}]);
});
it
(
'returns label suggestions on label context but leaves out labels that already exist'
,
()
=>
{
const
instance
=
shallow
(
<
PromQueryField
{
...
defaultProps
}
labelKeys=
{
{
'{job1="foo",job2!="foo",job3=~"foo"}'
:
[
'bar'
,
'job1'
,
'job2'
,
'job3'
]
}
}
/>
).
instance
()
as
PromQueryField
;
const
value
=
Plain
.
deserialize
(
'{job1="foo",job2!="foo",job3=~"foo",}'
);
const
range
=
value
.
selection
.
merge
({
anchorOffset
:
36
,
});
const
valueWithSelection
=
value
.
change
().
select
(
range
).
value
;
const
result
=
instance
.
getTypeahead
({
text
:
''
,
prefix
:
''
,
wrapperClasses
:
[
'context-labels'
],
value
:
valueWithSelection
,
});
expect
(
result
.
context
).
toBe
(
'context-labels'
);
expect
(
result
.
suggestions
).
toEqual
([{
items
:
[{
label
:
'bar'
}],
label
:
'Labels'
}]);
});
it
(
'returns label value suggestions inside a label value context after a negated matching operator'
,
()
=>
{
const
instance
=
shallow
(
<
PromQueryField
{
...
defaultProps
}
labelKeys=
{
{
'{}'
:
[
'label'
]
}
}
labelValues=
{
{
'{}'
:
{
label
:
[
'a'
,
'b'
,
'c'
]
}
}
}
/>
).
instance
()
as
PromQueryField
;
const
value
=
Plain
.
deserialize
(
'{label!=}'
);
const
range
=
value
.
selection
.
merge
({
anchorOffset
:
8
});
const
valueWithSelection
=
value
.
change
().
select
(
range
).
value
;
const
result
=
instance
.
getTypeahead
({
text
:
'!='
,
prefix
:
''
,
wrapperClasses
:
[
'context-labels'
],
labelKey
:
'label'
,
value
:
valueWithSelection
,
});
expect
(
result
.
context
).
toBe
(
'context-label-values'
);
expect
(
result
.
suggestions
).
toEqual
([
{
items
:
[{
label
:
'a'
},
{
label
:
'b'
},
{
label
:
'c'
}],
label
:
'Label values for "label"'
,
},
]);
});
it
(
'returns a refresher on label context and unavailable metric'
,
()
=>
{
const
instance
=
shallow
(
<
PromQueryField
{
...
defaultProps
}
labelKeys=
{
{
'{__name__="foo"}'
:
[
'bar'
]
}
}
/>
).
instance
()
as
PromQueryField
;
const
value
=
Plain
.
deserialize
(
'metric{}'
);
const
range
=
value
.
selection
.
merge
({
anchorOffset
:
7
,
});
const
valueWithSelection
=
value
.
change
().
select
(
range
).
value
;
const
result
=
instance
.
getTypeahead
({
text
:
''
,
prefix
:
''
,
wrapperClasses
:
[
'context-labels'
],
value
:
valueWithSelection
,
});
expect
(
result
.
context
).
toBeUndefined
();
expect
(
result
.
refresher
).
toBeInstanceOf
(
Promise
);
expect
(
result
.
suggestions
).
toEqual
([]);
});
it
(
'returns label values on label context when given a metric and a label key'
,
()
=>
{
const
instance
=
shallow
(
<
PromQueryField
{
...
defaultProps
}
labelKeys=
{
{
'{__name__="metric"}'
:
[
'bar'
]
}
}
labelValues=
{
{
'{__name__="metric"}'
:
{
bar
:
[
'baz'
]
}
}
}
/>
).
instance
()
as
PromQueryField
;
const
value
=
Plain
.
deserialize
(
'metric{bar=ba}'
);
const
range
=
value
.
selection
.
merge
({
anchorOffset
:
13
,
});
const
valueWithSelection
=
value
.
change
().
select
(
range
).
value
;
const
result
=
instance
.
getTypeahead
({
text
:
'=ba'
,
prefix
:
'ba'
,
wrapperClasses
:
[
'context-labels'
],
labelKey
:
'bar'
,
value
:
valueWithSelection
,
});
expect
(
result
.
context
).
toBe
(
'context-label-values'
);
expect
(
result
.
suggestions
).
toEqual
([{
items
:
[{
label
:
'baz'
}],
label
:
'Label values for "bar"'
}]);
});
it
(
'returns label suggestions on aggregation context and metric w/ selector'
,
()
=>
{
const
instance
=
shallow
(
<
PromQueryField
{
...
defaultProps
}
labelKeys=
{
{
'{__name__="metric",foo="xx"}'
:
[
'bar'
]
}
}
/>
).
instance
()
as
PromQueryField
;
const
value
=
Plain
.
deserialize
(
'sum(metric{foo="xx"}) by ()'
);
const
range
=
value
.
selection
.
merge
({
anchorOffset
:
26
,
});
const
valueWithSelection
=
value
.
change
().
select
(
range
).
value
;
const
result
=
instance
.
getTypeahead
({
text
:
''
,
prefix
:
''
,
wrapperClasses
:
[
'context-aggregation'
],
value
:
valueWithSelection
,
});
expect
(
result
.
context
).
toBe
(
'context-aggregation'
);
expect
(
result
.
suggestions
).
toEqual
([{
items
:
[{
label
:
'bar'
}],
label
:
'Labels'
}]);
});
it
(
'returns label suggestions on aggregation context and metric w/o selector'
,
()
=>
{
const
instance
=
shallow
(
<
PromQueryField
{
...
defaultProps
}
labelKeys=
{
{
'{__name__="metric"}'
:
[
'bar'
]
}
}
/>
).
instance
()
as
PromQueryField
;
const
value
=
Plain
.
deserialize
(
'sum(metric) by ()'
);
const
range
=
value
.
selection
.
merge
({
anchorOffset
:
16
,
});
const
valueWithSelection
=
value
.
change
().
select
(
range
).
value
;
const
result
=
instance
.
getTypeahead
({
text
:
''
,
prefix
:
''
,
wrapperClasses
:
[
'context-aggregation'
],
value
:
valueWithSelection
,
});
expect
(
result
.
context
).
toBe
(
'context-aggregation'
);
expect
(
result
.
suggestions
).
toEqual
([{
items
:
[{
label
:
'bar'
}],
label
:
'Labels'
}]);
});
});
});
import
{
groupMetricsByPrefix
,
RECORDING_RULES_GROUP
}
from
'./PromQueryField'
;
describe
(
'groupMetricsByPrefix()'
,
()
=>
{
it
(
'returns an empty group for no metrics'
,
()
=>
{
...
...
public/app/features/explore/PromQueryField.tsx
View file @
6f2315d5
This diff is collapsed.
Click to expand it.
public/app/features/explore/QueryField.tsx
View file @
6f2315d5
...
...
@@ -5,6 +5,8 @@ import { Change, Value } from 'slate';
import
{
Editor
}
from
'slate-react'
;
import
Plain
from
'slate-plain-serializer'
;
import
{
CompletionItem
,
CompletionItemGroup
,
TypeaheadOutput
}
from
'app/types/explore'
;
import
ClearPlugin
from
'./slate-plugins/clear'
;
import
NewlinePlugin
from
'./slate-plugins/newline'
;
...
...
@@ -13,87 +15,17 @@ import { makeFragment, makeValue } from './Value';
export
const
TYPEAHEAD_DEBOUNCE
=
100
;
function
getSuggestionByIndex
(
suggestions
:
SuggestionGroup
[],
index
:
number
):
Suggestion
{
function
getSuggestionByIndex
(
suggestions
:
CompletionItemGroup
[],
index
:
number
):
CompletionItem
{
// Flatten suggestion groups
const
flattenedSuggestions
=
suggestions
.
reduce
((
acc
,
g
)
=>
acc
.
concat
(
g
.
items
),
[]);
const
correctedIndex
=
Math
.
max
(
index
,
0
)
%
flattenedSuggestions
.
length
;
return
flattenedSuggestions
[
correctedIndex
];
}
function
hasSuggestions
(
suggestions
:
Suggestion
Group
[]):
boolean
{
function
hasSuggestions
(
suggestions
:
CompletionItem
Group
[]):
boolean
{
return
suggestions
&&
suggestions
.
length
>
0
;
}
export
interface
Suggestion
{
/**
* The label of this completion item. By default
* this is also the text that is inserted when selecting
* this completion.
*/
label
:
string
;
/**
* The kind of this completion item. Based on the kind
* an icon is chosen by the editor.
*/
kind
?:
string
;
/**
* A human-readable string with additional information
* about this item, like type or symbol information.
*/
detail
?:
string
;
/**
* A human-readable string, can be Markdown, that represents a doc-comment.
*/
documentation
?:
string
;
/**
* A string that should be used when comparing this item
* with other items. When `falsy` the `label` is used.
*/
sortText
?:
string
;
/**
* A string that should be used when filtering a set of
* completion items. When `falsy` the `label` is used.
*/
filterText
?:
string
;
/**
* A string or snippet that should be inserted in a document when selecting
* this completion. When `falsy` the `label` is used.
*/
insertText
?:
string
;
/**
* Delete number of characters before the caret position,
* by default the letters from the beginning of the word.
*/
deleteBackwards
?:
number
;
/**
* Number of steps to move after the insertion, can be negative.
*/
move
?:
number
;
}
export
interface
SuggestionGroup
{
/**
* Label that will be displayed for all entries of this group.
*/
label
:
string
;
/**
* List of suggestions of this group.
*/
items
:
Suggestion
[];
/**
* If true, match only by prefix (and not mid-word).
*/
prefixMatch
?:
boolean
;
/**
* If true, do not filter items in this group based on the search.
*/
skipFilter
?:
boolean
;
/**
* If true, do not sort items.
*/
skipSort
?:
boolean
;
}
interface
TypeaheadFieldProps
{
additionalPlugins
?:
any
[];
cleanText
?:
(
text
:
string
)
=>
string
;
...
...
@@ -110,7 +42,7 @@ interface TypeaheadFieldProps {
}
export
interface
TypeaheadFieldState
{
suggestions
:
Suggestion
Group
[];
suggestions
:
CompletionItem
Group
[];
typeaheadContext
:
string
|
null
;
typeaheadIndex
:
number
;
typeaheadPrefix
:
string
;
...
...
@@ -127,12 +59,6 @@ export interface TypeaheadInput {
wrapperNode
:
Element
;
}
export
interface
TypeaheadOutput
{
context
?:
string
;
refresher
?:
Promise
<
{}
>
;
suggestions
:
SuggestionGroup
[];
}
class
QueryField
extends
React
.
PureComponent
<
TypeaheadFieldProps
,
TypeaheadFieldState
>
{
menuEl
:
HTMLElement
|
null
;
plugins
:
any
[];
...
...
@@ -293,7 +219,7 @@ class QueryField extends React.PureComponent<TypeaheadFieldProps, TypeaheadField
}
},
TYPEAHEAD_DEBOUNCE
);
applyTypeahead
(
change
:
Change
,
suggestion
:
Suggestion
):
Change
{
applyTypeahead
(
change
:
Change
,
suggestion
:
CompletionItem
):
Change
{
const
{
cleanText
,
onWillApplySuggestion
,
syntax
}
=
this
.
props
;
const
{
typeaheadPrefix
,
typeaheadText
}
=
this
.
state
;
let
suggestionText
=
suggestion
.
insertText
||
suggestion
.
label
;
...
...
@@ -422,7 +348,7 @@ class QueryField extends React.PureComponent<TypeaheadFieldProps, TypeaheadField
}
};
onClickMenu
=
(
item
:
Suggestion
)
=>
{
onClickMenu
=
(
item
:
CompletionItem
)
=>
{
// Manually triggering change
const
change
=
this
.
applyTypeahead
(
this
.
state
.
value
.
change
(),
item
);
this
.
onChange
(
change
);
...
...
public/app/features/explore/QueryRows.tsx
View file @
6f2315d5
...
...
@@ -24,8 +24,8 @@ interface QueryRowEventHandlers {
interface
QueryRowCommonProps
{
className
?:
string
;
datasource
:
any
;
history
:
HistoryItem
[];
request
:
(
url
:
string
)
=>
Promise
<
any
>
;
// Temporarily
supportsLogs
?:
boolean
;
transactions
:
QueryTransaction
[];
...
...
@@ -78,7 +78,7 @@ class QueryRow extends PureComponent<QueryRowProps> {
};
render
()
{
const
{
history
,
query
,
request
,
supportsLogs
,
transactions
}
=
this
.
props
;
const
{
datasource
,
history
,
query
,
supportsLogs
,
transactions
}
=
this
.
props
;
const
transactionWithError
=
transactions
.
find
(
t
=>
t
.
error
!==
undefined
);
const
hint
=
getFirstHintFromTransactions
(
transactions
);
const
queryError
=
transactionWithError
?
transactionWithError
.
error
:
null
;
...
...
@@ -89,6 +89,7 @@ class QueryRow extends PureComponent<QueryRowProps> {
</
div
>
<
div
className=
"query-row-field"
>
<
QueryField
datasource=
{
datasource
}
error=
{
queryError
}
hint=
{
hint
}
initialQuery=
{
query
}
...
...
@@ -96,7 +97,6 @@ class QueryRow extends PureComponent<QueryRowProps> {
onClickHintFix=
{
this
.
onClickHintFix
}
onPressEnter=
{
this
.
onPressEnter
}
onQueryChange=
{
this
.
onChangeQuery
}
request=
{
request
}
supportsLogs=
{
supportsLogs
}
/>
</
div
>
...
...
public/app/features/explore/Typeahead.tsx
View file @
6f2315d5
import
React
from
'react'
;
import
Highlighter
from
'react-highlight-words'
;
import
{
Suggestion
,
SuggestionGroup
}
from
'./QueryField
'
;
import
{
CompletionItem
,
CompletionItemGroup
}
from
'app/types/explore
'
;
function
scrollIntoView
(
el
:
HTMLElement
)
{
if
(
!
el
||
!
el
.
offsetParent
)
{
...
...
@@ -15,12 +15,12 @@ function scrollIntoView(el: HTMLElement) {
interface
TypeaheadItemProps
{
isSelected
:
boolean
;
item
:
Suggestion
;
item
:
CompletionItem
;
onClickItem
:
(
Suggestion
)
=>
void
;
prefix
?:
string
;
}
class
TypeaheadItem
extends
React
.
PureComponent
<
TypeaheadItemProps
,
{}
>
{
class
TypeaheadItem
extends
React
.
PureComponent
<
TypeaheadItemProps
>
{
el
:
HTMLElement
;
componentDidUpdate
(
prevProps
)
{
...
...
@@ -53,14 +53,14 @@ class TypeaheadItem extends React.PureComponent<TypeaheadItemProps, {}> {
}
interface
TypeaheadGroupProps
{
items
:
Suggestion
[];
items
:
CompletionItem
[];
label
:
string
;
onClickItem
:
(
Suggestion
)
=>
void
;
selected
:
Suggestion
;
onClickItem
:
(
CompletionItem
)
=>
void
;
selected
:
CompletionItem
;
prefix
?:
string
;
}
class
TypeaheadGroup
extends
React
.
PureComponent
<
TypeaheadGroupProps
,
{}
>
{
class
TypeaheadGroup
extends
React
.
PureComponent
<
TypeaheadGroupProps
>
{
render
()
{
const
{
items
,
label
,
selected
,
onClickItem
,
prefix
}
=
this
.
props
;
return
(
...
...
@@ -85,13 +85,13 @@ class TypeaheadGroup extends React.PureComponent<TypeaheadGroupProps, {}> {
}
interface
TypeaheadProps
{
groupedItems
:
Suggestion
Group
[];
groupedItems
:
CompletionItem
Group
[];
menuRef
:
any
;
selectedItem
:
Suggestion
|
null
;
selectedItem
:
CompletionItem
|
null
;
onClickItem
:
(
Suggestion
)
=>
void
;
prefix
?:
string
;
}
class
Typeahead
extends
React
.
PureComponent
<
TypeaheadProps
,
{}
>
{
class
Typeahead
extends
React
.
PureComponent
<
TypeaheadProps
>
{
render
()
{
const
{
groupedItems
,
menuRef
,
selectedItem
,
onClickItem
,
prefix
}
=
this
.
props
;
return
(
...
...
public/app/plugins/datasource/prometheus/datasource.ts
View file @
6f2315d5
...
...
@@ -5,6 +5,7 @@ import kbn from 'app/core/utils/kbn';
import
*
as
dateMath
from
'app/core/utils/datemath'
;
import
PrometheusMetricFindQuery
from
'./metric_find_query'
;
import
{
ResultTransformer
}
from
'./result_transformer'
;
import
PrometheusLanguageProvider
from
'./language_provider'
;
import
{
BackendSrv
}
from
'app/core/services/backend_srv'
;
import
addLabelToQuery
from
'./add_label_to_query'
;
...
...
@@ -60,6 +61,7 @@ export class PrometheusDatasource {
interval: string;
queryTimeout: string;
httpMethod: string;
languageProvider: PrometheusLanguageProvider;
resultTransformer: ResultTransformer;
/** @ngInject */
...
...
@@ -76,6 +78,7 @@ export class PrometheusDatasource {
this.httpMethod = instanceSettings.jsonData.httpMethod || '
GET
';
this.resultTransformer = new ResultTransformer(templateSrv);
this.ruleMappings = {};
this.languageProvider = new PrometheusLanguageProvider(this);
}
init() {
...
...
public/app/plugins/datasource/prometheus/language_provider.ts
0 → 100644
View file @
6f2315d5
This diff is collapsed.
Click to expand it.
public/app/
features/explore/utils/prometheu
s.ts
→
public/app/
plugins/datasource/prometheus/language_util
s.ts
View file @
6f2315d5
...
...
@@ -23,9 +23,6 @@ export function processLabels(labels, withName = false) {
return
{
values
,
keys
:
Object
.
keys
(
values
)
};
}
// Strip syntax chars
export
const
cleanText
=
s
=>
s
.
replace
(
/
[
{}[
\]
="(),!~+
\-
*
/
^%
]
/g
,
''
).
trim
();
// const cleanSelectorRegexp = /\{(\w+="[^"\n]*?")(,\w+="[^"\n]*?")*\}/;
const
selectorRegexp
=
/
\{[^
}
]
*
?\}
/
;
const
labelRegexp
=
/
\b(\w
+
)(
!
?
=~
?)(
"
[^
"
\n]
*
?
"
)
/g
;
...
...
public/app/
features/explore/slate-plugins/prism
/promql.ts
→
public/app/
plugins/datasource/prometheus
/promql.ts
View file @
6f2315d5
File moved
public/app/plugins/datasource/prometheus/specs/language_provider.test.ts
0 → 100644
View file @
6f2315d5
import
Plain
from
'slate-plain-serializer'
;
import
LanguageProvider
from
'../language_provider'
;
describe
(
'Language completion provider'
,
()
=>
{
const
datasource
=
{
metadataRequest
:
()
=>
({
data
:
{
data
:
[]
}
}),
};
it
(
'returns default suggestions on emtpty context'
,
()
=>
{
const
instance
=
new
LanguageProvider
(
datasource
);
const
result
=
instance
.
provideCompletionItems
({
text
:
''
,
prefix
:
''
,
wrapperClasses
:
[]
});
expect
(
result
.
context
).
toBeUndefined
();
expect
(
result
.
refresher
).
toBeUndefined
();
expect
(
result
.
suggestions
.
length
).
toEqual
(
2
);
});
describe
(
'range suggestions'
,
()
=>
{
it
(
'returns range suggestions in range context'
,
()
=>
{
const
instance
=
new
LanguageProvider
(
datasource
);
const
result
=
instance
.
provideCompletionItems
({
text
:
'1'
,
prefix
:
'1'
,
wrapperClasses
:
[
'context-range'
]
});
expect
(
result
.
context
).
toBe
(
'context-range'
);
expect
(
result
.
refresher
).
toBeUndefined
();
expect
(
result
.
suggestions
).
toEqual
([
{
items
:
[{
label
:
'1m'
},
{
label
:
'5m'
},
{
label
:
'10m'
},
{
label
:
'30m'
},
{
label
:
'1h'
}],
label
:
'Range vector'
,
},
]);
});
});
describe
(
'metric suggestions'
,
()
=>
{
it
(
'returns metrics suggestions by default'
,
()
=>
{
const
instance
=
new
LanguageProvider
(
datasource
,
{
metrics
:
[
'foo'
,
'bar'
]
});
const
result
=
instance
.
provideCompletionItems
({
text
:
'a'
,
prefix
:
'a'
,
wrapperClasses
:
[]
});
expect
(
result
.
context
).
toBeUndefined
();
expect
(
result
.
refresher
).
toBeUndefined
();
expect
(
result
.
suggestions
.
length
).
toEqual
(
2
);
});
it
(
'returns default suggestions after a binary operator'
,
()
=>
{
const
instance
=
new
LanguageProvider
(
datasource
,
{
metrics
:
[
'foo'
,
'bar'
]
});
const
result
=
instance
.
provideCompletionItems
({
text
:
'*'
,
prefix
:
''
,
wrapperClasses
:
[]
});
expect
(
result
.
context
).
toBeUndefined
();
expect
(
result
.
refresher
).
toBeUndefined
();
expect
(
result
.
suggestions
.
length
).
toEqual
(
2
);
});
});
describe
(
'label suggestions'
,
()
=>
{
it
(
'returns default label suggestions on label context and no metric'
,
()
=>
{
const
instance
=
new
LanguageProvider
(
datasource
);
const
value
=
Plain
.
deserialize
(
'{}'
);
const
range
=
value
.
selection
.
merge
({
anchorOffset
:
1
,
});
const
valueWithSelection
=
value
.
change
().
select
(
range
).
value
;
const
result
=
instance
.
provideCompletionItems
({
text
:
''
,
prefix
:
''
,
wrapperClasses
:
[
'context-labels'
],
value
:
valueWithSelection
,
});
expect
(
result
.
context
).
toBe
(
'context-labels'
);
expect
(
result
.
suggestions
).
toEqual
([{
items
:
[{
label
:
'job'
},
{
label
:
'instance'
}],
label
:
'Labels'
}]);
});
it
(
'returns label suggestions on label context and metric'
,
()
=>
{
const
instance
=
new
LanguageProvider
(
datasource
,
{
labelKeys
:
{
'{__name__="metric"}'
:
[
'bar'
]
}
});
const
value
=
Plain
.
deserialize
(
'metric{}'
);
const
range
=
value
.
selection
.
merge
({
anchorOffset
:
7
,
});
const
valueWithSelection
=
value
.
change
().
select
(
range
).
value
;
const
result
=
instance
.
provideCompletionItems
({
text
:
''
,
prefix
:
''
,
wrapperClasses
:
[
'context-labels'
],
value
:
valueWithSelection
,
});
expect
(
result
.
context
).
toBe
(
'context-labels'
);
expect
(
result
.
suggestions
).
toEqual
([{
items
:
[{
label
:
'bar'
}],
label
:
'Labels'
}]);
});
it
(
'returns label suggestions on label context but leaves out labels that already exist'
,
()
=>
{
const
instance
=
new
LanguageProvider
(
datasource
,
{
labelKeys
:
{
'{job1="foo",job2!="foo",job3=~"foo"}'
:
[
'bar'
,
'job1'
,
'job2'
,
'job3'
]
},
});
const
value
=
Plain
.
deserialize
(
'{job1="foo",job2!="foo",job3=~"foo",}'
);
const
range
=
value
.
selection
.
merge
({
anchorOffset
:
36
,
});
const
valueWithSelection
=
value
.
change
().
select
(
range
).
value
;
const
result
=
instance
.
provideCompletionItems
({
text
:
''
,
prefix
:
''
,
wrapperClasses
:
[
'context-labels'
],
value
:
valueWithSelection
,
});
expect
(
result
.
context
).
toBe
(
'context-labels'
);
expect
(
result
.
suggestions
).
toEqual
([{
items
:
[{
label
:
'bar'
}],
label
:
'Labels'
}]);
});
it
(
'returns label value suggestions inside a label value context after a negated matching operator'
,
()
=>
{
const
instance
=
new
LanguageProvider
(
datasource
,
{
labelKeys
:
{
'{}'
:
[
'label'
]
},
labelValues
:
{
'{}'
:
{
label
:
[
'a'
,
'b'
,
'c'
]
}
},
});
const
value
=
Plain
.
deserialize
(
'{label!=}'
);
const
range
=
value
.
selection
.
merge
({
anchorOffset
:
8
});
const
valueWithSelection
=
value
.
change
().
select
(
range
).
value
;
const
result
=
instance
.
provideCompletionItems
({
text
:
'!='
,
prefix
:
''
,
wrapperClasses
:
[
'context-labels'
],
labelKey
:
'label'
,
value
:
valueWithSelection
,
});
expect
(
result
.
context
).
toBe
(
'context-label-values'
);
expect
(
result
.
suggestions
).
toEqual
([
{
items
:
[{
label
:
'a'
},
{
label
:
'b'
},
{
label
:
'c'
}],
label
:
'Label values for "label"'
,
},
]);
});
it
(
'returns a refresher on label context and unavailable metric'
,
()
=>
{
const
instance
=
new
LanguageProvider
(
datasource
,
{
labelKeys
:
{
'{__name__="foo"}'
:
[
'bar'
]
}
});
const
value
=
Plain
.
deserialize
(
'metric{}'
);
const
range
=
value
.
selection
.
merge
({
anchorOffset
:
7
,
});
const
valueWithSelection
=
value
.
change
().
select
(
range
).
value
;
const
result
=
instance
.
provideCompletionItems
({
text
:
''
,
prefix
:
''
,
wrapperClasses
:
[
'context-labels'
],
value
:
valueWithSelection
,
});
expect
(
result
.
context
).
toBeUndefined
();
expect
(
result
.
refresher
).
toBeInstanceOf
(
Promise
);
expect
(
result
.
suggestions
).
toEqual
([]);
});
it
(
'returns label values on label context when given a metric and a label key'
,
()
=>
{
const
instance
=
new
LanguageProvider
(
datasource
,
{
labelKeys
:
{
'{__name__="metric"}'
:
[
'bar'
]
},
labelValues
:
{
'{__name__="metric"}'
:
{
bar
:
[
'baz'
]
}
},
});
const
value
=
Plain
.
deserialize
(
'metric{bar=ba}'
);
const
range
=
value
.
selection
.
merge
({
anchorOffset
:
13
,
});
const
valueWithSelection
=
value
.
change
().
select
(
range
).
value
;
const
result
=
instance
.
provideCompletionItems
({
text
:
'=ba'
,
prefix
:
'ba'
,
wrapperClasses
:
[
'context-labels'
],
labelKey
:
'bar'
,
value
:
valueWithSelection
,
});
expect
(
result
.
context
).
toBe
(
'context-label-values'
);
expect
(
result
.
suggestions
).
toEqual
([{
items
:
[{
label
:
'baz'
}],
label
:
'Label values for "bar"'
}]);
});
it
(
'returns label suggestions on aggregation context and metric w/ selector'
,
()
=>
{
const
instance
=
new
LanguageProvider
(
datasource
,
{
labelKeys
:
{
'{__name__="metric",foo="xx"}'
:
[
'bar'
]
}
});
const
value
=
Plain
.
deserialize
(
'sum(metric{foo="xx"}) by ()'
);
const
range
=
value
.
selection
.
merge
({
anchorOffset
:
26
,
});
const
valueWithSelection
=
value
.
change
().
select
(
range
).
value
;
const
result
=
instance
.
provideCompletionItems
({
text
:
''
,
prefix
:
''
,
wrapperClasses
:
[
'context-aggregation'
],
value
:
valueWithSelection
,
});
expect
(
result
.
context
).
toBe
(
'context-aggregation'
);
expect
(
result
.
suggestions
).
toEqual
([{
items
:
[{
label
:
'bar'
}],
label
:
'Labels'
}]);
});
it
(
'returns label suggestions on aggregation context and metric w/o selector'
,
()
=>
{
const
instance
=
new
LanguageProvider
(
datasource
,
{
labelKeys
:
{
'{__name__="metric"}'
:
[
'bar'
]
}
});
const
value
=
Plain
.
deserialize
(
'sum(metric) by ()'
);
const
range
=
value
.
selection
.
merge
({
anchorOffset
:
16
,
});
const
valueWithSelection
=
value
.
change
().
select
(
range
).
value
;
const
result
=
instance
.
provideCompletionItems
({
text
:
''
,
prefix
:
''
,
wrapperClasses
:
[
'context-aggregation'
],
value
:
valueWithSelection
,
});
expect
(
result
.
context
).
toBe
(
'context-aggregation'
);
expect
(
result
.
suggestions
).
toEqual
([{
items
:
[{
label
:
'bar'
}],
label
:
'Labels'
}]);
});
});
});
public/app/
features/explore/utils/prometheu
s.test.ts
→
public/app/
plugins/datasource/prometheus/specs/language_util
s.test.ts
View file @
6f2315d5
import
{
parseSelector
}
from
'.
/prometheu
s'
;
import
{
parseSelector
}
from
'.
./language_util
s'
;
describe
(
'parseSelector()'
,
()
=>
{
let
parsed
;
...
...
public/app/types/explore.ts
View file @
6f2315d5
import
{
Value
}
from
'slate'
;
export
interface
CompletionItem
{
/**
* The label of this completion item. By default
* this is also the text that is inserted when selecting
* this completion.
*/
label
:
string
;
/**
* The kind of this completion item. Based on the kind
* an icon is chosen by the editor.
*/
kind
?:
string
;
/**
* A human-readable string with additional information
* about this item, like type or symbol information.
*/
detail
?:
string
;
/**
* A human-readable string, can be Markdown, that represents a doc-comment.
*/
documentation
?:
string
;
/**
* A string that should be used when comparing this item
* with other items. When `falsy` the `label` is used.
*/
sortText
?:
string
;
/**
* A string that should be used when filtering a set of
* completion items. When `falsy` the `label` is used.
*/
filterText
?:
string
;
/**
* A string or snippet that should be inserted in a document when selecting
* this completion. When `falsy` the `label` is used.
*/
insertText
?:
string
;
/**
* Delete number of characters before the caret position,
* by default the letters from the beginning of the word.
*/
deleteBackwards
?:
number
;
/**
* Number of steps to move after the insertion, can be negative.
*/
move
?:
number
;
}
export
interface
CompletionItemGroup
{
/**
* Label that will be displayed for all entries of this group.
*/
label
:
string
;
/**
* List of suggestions of this group.
*/
items
:
CompletionItem
[];
/**
* If true, match only by prefix (and not mid-word).
*/
prefixMatch
?:
boolean
;
/**
* If true, do not filter items in this group based on the search.
*/
skipFilter
?:
boolean
;
/**
* If true, do not sort items.
*/
skipSort
?:
boolean
;
}
interface
ExploreDatasource
{
value
:
string
;
label
:
string
;
...
...
@@ -8,6 +80,26 @@ export interface HistoryItem {
query
:
string
;
}
export
abstract
class
LanguageProvider
{
datasource
:
any
;
request
:
(
url
)
=>
Promise
<
any
>
;
start
:
()
=>
Promise
<
any
>
;
}
export
interface
TypeaheadInput
{
text
:
string
;
prefix
:
string
;
wrapperClasses
:
string
[];
labelKey
?:
string
;
value
?:
Value
;
}
export
interface
TypeaheadOutput
{
context
?:
string
;
refresher
?:
Promise
<
{}
>
;
suggestions
:
CompletionItemGroup
[];
}
export
interface
Range
{
from
:
string
;
to
:
string
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment