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
781dd25c
Commit
781dd25c
authored
Aug 08, 2017
by
Torkel Ödegaard
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'elasticsearch-filtering'
parents
1507c02e
f979e958
Show whitespace changes
Inline
Side-by-side
Showing
17 changed files
with
253 additions
and
113 deletions
+253
-113
public/app/core/table_model.ts
+9
-0
public/app/features/dashboard/ad_hoc_filters.ts
+3
-3
public/app/features/dashboard/submenu/submenu.ts
+1
-4
public/app/features/templating/editor_ctrl.ts
+3
-7
public/app/features/templating/specs/variable_srv_specs.ts
+4
-1
public/app/features/templating/variable_srv.ts
+39
-5
public/app/plugins/datasource/elasticsearch/datasource.js
+11
-2
public/app/plugins/datasource/elasticsearch/elastic_response.d.ts
+0
-2
public/app/plugins/datasource/elasticsearch/elastic_response.ts
+68
-58
public/app/plugins/datasource/elasticsearch/specs/datasource_specs.ts
+5
-1
public/app/plugins/datasource/elasticsearch/specs/elastic_response_specs.ts
+14
-16
public/app/plugins/panel/table/editor.html
+9
-1
public/app/plugins/panel/table/editor.ts
+23
-1
public/app/plugins/panel/table/module.ts
+16
-1
public/app/plugins/panel/table/renderer.ts
+24
-9
public/app/plugins/panel/table/transformers.ts
+10
-2
public/sass/components/_panel_table.scss
+14
-0
No files found.
public/app/core/table_model.ts
View file @
781dd25c
...
...
@@ -3,9 +3,11 @@ export default class TableModel {
columns
:
any
[];
rows
:
any
[];
type
:
string
;
columnMap
:
any
;
constructor
()
{
this
.
columns
=
[];
this
.
columnMap
=
{};
this
.
rows
=
[];
this
.
type
=
'table'
;
}
...
...
@@ -36,4 +38,11 @@ export default class TableModel {
this
.
columns
[
options
.
col
].
desc
=
false
;
}
}
addColumn
(
col
)
{
if
(
!
this
.
columnMap
[
col
.
text
])
{
this
.
columns
.
push
(
col
);
this
.
columnMap
[
col
.
text
]
=
col
;
}
}
}
public/app/features/dashboard/ad_hoc_filters.ts
View file @
781dd25c
...
...
@@ -10,9 +10,10 @@ export class AdHocFiltersCtrl {
removeTagFilterSegment
:
any
;
/** @ngInject */
constructor
(
private
uiSegmentSrv
,
private
datasourceSrv
,
private
$q
,
private
templateSrv
,
private
$rootScope
)
{
constructor
(
private
uiSegmentSrv
,
private
datasourceSrv
,
private
$q
,
private
variableSrv
,
private
$scope
,
private
$rootScope
)
{
this
.
removeTagFilterSegment
=
uiSegmentSrv
.
newSegment
({
fake
:
true
,
value
:
'-- remove filter --'
});
this
.
buildSegmentModel
();
this
.
$rootScope
.
onAppEvent
(
'template-variable-value-updated'
,
this
.
buildSegmentModel
.
bind
(
this
),
$scope
);
}
buildSegmentModel
()
{
...
...
@@ -141,8 +142,7 @@ export class AdHocFiltersCtrl {
}
this
.
variable
.
setFilters
(
filters
);
this
.
$rootScope
.
$emit
(
'template-variable-value-updated'
);
this
.
$rootScope
.
$broadcast
(
'refresh'
);
this
.
variableSrv
.
variableUpdated
(
this
.
variable
,
true
);
}
}
...
...
public/app/features/dashboard/submenu/submenu.ts
View file @
781dd25c
...
...
@@ -22,10 +22,7 @@ export class SubmenuCtrl {
}
variableUpdated
(
variable
)
{
this
.
variableSrv
.
variableUpdated
(
variable
).
then
(()
=>
{
this
.
$rootScope
.
$emit
(
'template-variable-value-updated'
);
this
.
$rootScope
.
$broadcast
(
'refresh'
);
});
this
.
variableSrv
.
variableUpdated
(
variable
,
true
);
}
openEditView
(
editview
)
{
...
...
public/app/features/templating/editor_ctrl.ts
View file @
781dd25c
...
...
@@ -55,9 +55,8 @@ export class VariableEditorCtrl {
$scope
.
add
=
function
()
{
if
(
$scope
.
isValid
())
{
$scope
.
variables
.
push
(
$scope
.
current
);
variableSrv
.
addVariable
(
$scope
.
current
);
$scope
.
update
();
$scope
.
dashboard
.
updateSubmenuVisibility
();
}
};
...
...
@@ -114,9 +113,8 @@ export class VariableEditorCtrl {
$scope
.
duplicate
=
function
(
variable
)
{
var
clone
=
_
.
cloneDeep
(
variable
.
getSaveModel
());
$scope
.
current
=
variableSrv
.
createVariableFromModel
(
clone
);
$scope
.
variables
.
push
(
$scope
.
current
);
$scope
.
current
.
name
=
'copy_of_'
+
variable
.
name
;
$scope
.
dashboard
.
updateSubmenuVisibility
(
);
$scope
.
variableSrv
.
addVariable
(
$scope
.
current
);
};
$scope
.
update
=
function
()
{
...
...
@@ -150,9 +148,7 @@ export class VariableEditorCtrl {
};
$scope
.
removeVariable
=
function
(
variable
)
{
var
index
=
_
.
indexOf
(
$scope
.
variables
,
variable
);
$scope
.
variables
.
splice
(
index
,
1
);
$scope
.
dashboard
.
updateSubmenuVisibility
();
variableSrv
.
removeVariable
(
variable
);
};
}
}
...
...
public/app/features/templating/specs/variable_srv_specs.ts
View file @
781dd25c
...
...
@@ -22,6 +22,7 @@ describe('VariableSrv', function() {
ctx
.
variableSrv
.
init
({
templating
:
{
list
:
[]},
events
:
new
Emitter
(),
updateSubmenuVisibility
:
sinon
.
stub
(),
});
ctx
.
$rootScope
.
$digest
();
}));
...
...
@@ -41,7 +42,9 @@ describe('VariableSrv', function() {
ctx
.
datasourceSrv
.
getMetricSources
=
sinon
.
stub
().
returns
(
scenario
.
metricSources
);
scenario
.
variable
=
ctx
.
variableSrv
.
addVariable
(
scenario
.
variableModel
);
scenario
.
variable
=
ctx
.
variableSrv
.
createVariableFromModel
(
scenario
.
variableModel
);
ctx
.
variableSrv
.
addVariable
(
scenario
.
variable
);
ctx
.
variableSrv
.
updateOptions
(
scenario
.
variable
);
ctx
.
$rootScope
.
$digest
();
});
...
...
public/app/features/templating/variable_srv.ts
View file @
781dd25c
...
...
@@ -90,17 +90,24 @@ export class VariableSrv {
return
variable
;
}
addVariable
(
model
)
{
var
variable
=
this
.
createVariableFromModel
(
model
);
addVariable
(
variable
)
{
this
.
variables
.
push
(
variable
);
return
variable
;
this
.
templateSrv
.
updateTemplateData
();
this
.
dashboard
.
updateSubmenuVisibility
();
}
removeVariable
(
variable
)
{
var
index
=
_
.
indexOf
(
this
.
variables
,
variable
);
this
.
variables
.
splice
(
index
,
1
);
this
.
templateSrv
.
updateTemplateData
();
this
.
dashboard
.
updateSubmenuVisibility
();
}
updateOptions
(
variable
)
{
return
variable
.
updateOptions
();
}
variableUpdated
(
variable
)
{
variableUpdated
(
variable
,
emitChangeEvents
?
)
{
// if there is a variable lock ignore cascading update because we are in a boot up scenario
if
(
variable
.
initLock
)
{
return
this
.
$q
.
when
();
...
...
@@ -117,7 +124,12 @@ export class VariableSrv {
}
});
return
this
.
$q
.
all
(
promises
);
return
this
.
$q
.
all
(
promises
).
then
(()
=>
{
if
(
emitChangeEvents
)
{
this
.
$rootScope
.
$emit
(
'template-variable-value-updated'
);
this
.
$rootScope
.
$broadcast
(
'refresh'
);
}
});
}
selectOptionsForCurrentValue
(
variable
)
{
...
...
@@ -218,6 +230,28 @@ export class VariableSrv {
// update url
this
.
$location
.
search
(
params
);
}
setAdhocFilter
(
options
)
{
var
variable
=
_
.
find
(
this
.
variables
,
{
type
:
'adhoc'
,
datasource
:
options
.
datasource
});
if
(
!
variable
)
{
variable
=
this
.
createVariableFromModel
({
name
:
'Filters'
,
type
:
'adhoc'
,
datasource
:
options
.
datasource
});
this
.
addVariable
(
variable
);
}
let
filters
=
variable
.
filters
;
let
filter
=
_
.
find
(
filters
,
{
key
:
options
.
key
,
value
:
options
.
value
});
if
(
!
filter
)
{
filter
=
{
key
:
options
.
key
,
value
:
options
.
value
};
filters
.
push
(
filter
);
}
filter
.
operator
=
options
.
operator
;
variable
.
setFilters
(
filters
);
this
.
variableUpdated
(
variable
,
true
);
}
}
coreModule
.
service
(
'variableSrv'
,
VariableSrv
);
public/app/plugins/datasource/elasticsearch/datasource.js
View file @
781dd25c
...
...
@@ -11,6 +11,8 @@ define([
function
(
angular
,
_
,
moment
,
kbn
,
ElasticQueryBuilder
,
IndexPattern
,
ElasticResponse
)
{
'use strict'
;
ElasticResponse
=
ElasticResponse
.
ElasticResponse
;
/** @ngInject */
function
ElasticDatasource
(
instanceSettings
,
$q
,
backendSrv
,
templateSrv
,
timeSrv
)
{
this
.
basicAuth
=
instanceSettings
.
basicAuth
;
...
...
@@ -270,10 +272,17 @@ function (angular, _, moment, kbn, ElasticQueryBuilder, IndexPattern, ElasticRes
var
subObj
=
obj
[
key
];
// Check mapping field for nested fields
if
(
subObj
.
hasOwnProperty
(
'properties'
))
{
if
(
_
.
isObject
(
subObj
.
properties
))
{
fieldNameParts
.
push
(
key
);
getFieldsRecursively
(
subObj
.
properties
);
}
else
{
}
if
(
_
.
isObject
(
subObj
.
fields
))
{
fieldNameParts
.
push
(
key
);
getFieldsRecursively
(
subObj
.
fields
);
}
if
(
_
.
isString
(
subObj
.
type
))
{
var
fieldName
=
fieldNameParts
.
concat
(
key
).
join
(
'.'
);
// Hide meta-fields and check field type
...
...
public/app/plugins/datasource/elasticsearch/elastic_response.d.ts
deleted
100644 → 0
View file @
1507c02e
declare
var
test
:
any
;
export
default
test
;
public/app/plugins/datasource/elasticsearch/elastic_response.
j
s
→
public/app/plugins/datasource/elasticsearch/elastic_response.
t
s
View file @
781dd25c
define
([
"lodash"
,
"./query_def"
],
function
(
_
,
queryDef
)
{
'use strict'
;
function
ElasticResponse
(
targets
,
response
)
{
///<reference path="../../../headers/common.d.ts" />
import
_
from
'lodash'
;
import
queryDef
from
"./query_def"
;
import
TableModel
from
'app/core/table_model'
;
export
function
ElasticResponse
(
targets
,
response
)
{
this
.
targets
=
targets
;
this
.
response
=
response
;
}
}
ElasticResponse
.
prototype
.
processMetrics
=
function
(
esAgg
,
target
,
seriesList
,
props
)
{
ElasticResponse
.
prototype
.
processMetrics
=
function
(
esAgg
,
target
,
seriesList
,
props
)
{
var
metric
,
y
,
i
,
newSeries
,
bucket
,
value
;
for
(
y
=
0
;
y
<
target
.
metrics
.
length
;
y
++
)
{
...
...
@@ -19,7 +18,7 @@ function (_, queryDef) {
continue
;
}
switch
(
metric
.
type
)
{
switch
(
metric
.
type
)
{
case
'count'
:
{
newSeries
=
{
datapoints
:
[],
metric
:
'count'
,
props
:
props
};
for
(
i
=
0
;
i
<
esAgg
.
buckets
.
length
;
i
++
)
{
...
...
@@ -95,23 +94,37 @@ function (_, queryDef) {
}
}
}
};
ElasticResponse
.
prototype
.
processAggregationDocs
=
function
(
esAgg
,
aggDef
,
target
,
table
,
props
)
{
// add columns
if
(
table
.
columns
.
length
===
0
)
{
for
(
let
propKey
of
_
.
keys
(
props
))
{
table
.
addColumn
({
text
:
propKey
,
filterable
:
true
});
}
table
.
addColumn
({
text
:
aggDef
.
field
,
filterable
:
true
});
}
// helper func to add values to value array
let
addMetricValue
=
(
values
,
metricName
,
value
)
=>
{
table
.
addColumn
({
text
:
metricName
});
values
.
push
(
value
);
};
ElasticResponse
.
prototype
.
processAggregationDocs
=
function
(
esAgg
,
aggDef
,
target
,
docs
,
prop
s
)
{
var
metric
,
y
,
i
,
bucket
,
metricName
,
doc
;
for
(
let
bucket
of
esAgg
.
bucket
s
)
{
let
values
=
[]
;
for
(
i
=
0
;
i
<
esAgg
.
buckets
.
length
;
i
++
)
{
bucket
=
esAgg
.
buckets
[
i
];
doc
=
_
.
defaults
({},
props
);
doc
[
aggDef
.
field
]
=
bucket
.
key
;
for
(
let
propValues
of
_
.
values
(
props
))
{
values
.
push
(
propValues
);
}
for
(
y
=
0
;
y
<
target
.
metrics
.
length
;
y
++
)
{
metric
=
target
.
metrics
[
y
]
;
// add bucket key (value)
values
.
push
(
bucket
.
key
)
;
switch
(
metric
.
type
)
{
for
(
let
metric
of
target
.
metrics
)
{
switch
(
metric
.
type
)
{
case
"count"
:
{
metricName
=
this
.
_getMetricName
(
metric
.
type
);
doc
[
metricName
]
=
bucket
.
doc_count
;
addMetricValue
(
values
,
this
.
_getMetricName
(
metric
.
type
),
bucket
.
doc_count
);
break
;
}
case
'extended_stats'
:
{
...
...
@@ -125,33 +138,32 @@ function (_, queryDef) {
stats
.
std_deviation_bounds_upper
=
stats
.
std_deviation_bounds
.
upper
;
stats
.
std_deviation_bounds_lower
=
stats
.
std_deviation_bounds
.
lower
;
metricName
=
this
.
_getMetricName
(
statName
);
doc
[
metricName
]
=
stats
[
statName
];
addMetricValue
(
values
,
this
.
_getMetricName
(
statName
),
stats
[
statName
]);
}
break
;
}
default
:
{
metricName
=
this
.
_getMetricName
(
metric
.
type
);
var
otherMetrics
=
_
.
filter
(
target
.
metrics
,
{
type
:
metric
.
type
});
let
metricName
=
this
.
_getMetricName
(
metric
.
type
);
let
otherMetrics
=
_
.
filter
(
target
.
metrics
,
{
type
:
metric
.
type
});
// if more of the same metric type include field field name in property
if
(
otherMetrics
.
length
>
1
)
{
metricName
+=
' '
+
metric
.
field
;
}
doc
[
metricName
]
=
bucket
[
metric
.
id
].
value
;
addMetricValue
(
values
,
metricName
,
bucket
[
metric
.
id
].
value
)
;
break
;
}
}
}
docs
.
push
(
doc
);
table
.
rows
.
push
(
values
);
}
};
};
// This is quite complex
// neeed to recurise down the nested buckets to build series
ElasticResponse
.
prototype
.
processBuckets
=
function
(
aggs
,
target
,
seriesList
,
docs
,
props
,
depth
)
{
// This is quite complex
// neeed to recurise down the nested buckets to build series
ElasticResponse
.
prototype
.
processBuckets
=
function
(
aggs
,
target
,
seriesList
,
table
,
props
,
depth
)
{
var
bucket
,
aggDef
,
esAgg
,
aggId
;
var
maxDepth
=
target
.
bucketAggs
.
length
-
1
;
...
...
@@ -167,7 +179,7 @@ function (_, queryDef) {
if
(
aggDef
.
type
===
'date_histogram'
)
{
this
.
processMetrics
(
esAgg
,
target
,
seriesList
,
props
);
}
else
{
this
.
processAggregationDocs
(
esAgg
,
aggDef
,
target
,
docs
,
props
);
this
.
processAggregationDocs
(
esAgg
,
aggDef
,
target
,
table
,
props
);
}
}
else
{
for
(
var
nameIndex
in
esAgg
.
buckets
)
{
...
...
@@ -181,22 +193,22 @@ function (_, queryDef) {
if
(
bucket
.
key_as_string
)
{
props
[
aggDef
.
field
]
=
bucket
.
key_as_string
;
}
this
.
processBuckets
(
bucket
,
target
,
seriesList
,
docs
,
props
,
depth
+
1
);
this
.
processBuckets
(
bucket
,
target
,
seriesList
,
table
,
props
,
depth
+
1
);
}
}
}
};
};
ElasticResponse
.
prototype
.
_getMetricName
=
function
(
metric
)
{
ElasticResponse
.
prototype
.
_getMetricName
=
function
(
metric
)
{
var
metricDef
=
_
.
find
(
queryDef
.
metricAggTypes
,
{
value
:
metric
});
if
(
!
metricDef
)
{
metricDef
=
_
.
find
(
queryDef
.
extendedStats
,
{
value
:
metric
});
}
return
metricDef
?
metricDef
.
text
:
metric
;
};
};
ElasticResponse
.
prototype
.
_getSeriesName
=
function
(
series
,
target
,
metricTypeCount
)
{
ElasticResponse
.
prototype
.
_getSeriesName
=
function
(
series
,
target
,
metricTypeCount
)
{
var
metricName
=
this
.
_getMetricName
(
series
.
metric
);
if
(
target
.
alias
)
{
...
...
@@ -240,9 +252,9 @@ function (_, queryDef) {
}
return
name
.
trim
()
+
' '
+
metricName
;
};
};
ElasticResponse
.
prototype
.
nameSeries
=
function
(
seriesList
,
target
)
{
ElasticResponse
.
prototype
.
nameSeries
=
function
(
seriesList
,
target
)
{
var
metricTypeCount
=
_
.
uniq
(
_
.
map
(
seriesList
,
'metric'
)).
length
;
var
fieldNameCount
=
_
.
uniq
(
_
.
map
(
seriesList
,
'field'
)).
length
;
...
...
@@ -250,10 +262,10 @@ function (_, queryDef) {
var
series
=
seriesList
[
i
];
series
.
target
=
this
.
_getSeriesName
(
series
,
target
,
metricTypeCount
,
fieldNameCount
);
}
};
};
ElasticResponse
.
prototype
.
processHits
=
function
(
hits
,
seriesList
)
{
var
series
=
{
target
:
'docs'
,
type
:
'docs'
,
datapoints
:
[],
total
:
hits
.
total
};
ElasticResponse
.
prototype
.
processHits
=
function
(
hits
,
seriesList
)
{
var
series
=
{
target
:
'docs'
,
type
:
'docs'
,
datapoints
:
[],
total
:
hits
.
total
,
filterable
:
true
};
var
propName
,
hit
,
doc
,
i
;
for
(
i
=
0
;
i
<
hits
.
hits
.
length
;
i
++
)
{
...
...
@@ -277,25 +289,25 @@ function (_, queryDef) {
}
seriesList
.
push
(
series
);
};
};
ElasticResponse
.
prototype
.
trimDatapoints
=
function
(
aggregations
,
target
)
{
ElasticResponse
.
prototype
.
trimDatapoints
=
function
(
aggregations
,
target
)
{
var
histogram
=
_
.
find
(
target
.
bucketAggs
,
{
type
:
'date_histogram'
});
var
shouldDropFirstAndLast
=
histogram
&&
histogram
.
settings
&&
histogram
.
settings
.
trimEdges
;
if
(
shouldDropFirstAndLast
)
{
var
trim
=
histogram
.
settings
.
trimEdges
;
for
(
var
prop
in
aggregations
)
{
for
(
var
prop
in
aggregations
)
{
var
points
=
aggregations
[
prop
];
if
(
points
.
datapoints
.
length
>
trim
*
2
)
{
points
.
datapoints
=
points
.
datapoints
.
slice
(
trim
,
points
.
datapoints
.
length
-
trim
);
}
}
}
};
};
ElasticResponse
.
prototype
.
getErrorFromElasticResponse
=
function
(
response
,
err
)
{
var
result
=
{};
ElasticResponse
.
prototype
.
getErrorFromElasticResponse
=
function
(
response
,
err
)
{
var
result
:
any
=
{};
result
.
data
=
JSON
.
stringify
(
err
,
null
,
4
);
if
(
err
.
root_cause
&&
err
.
root_cause
.
length
>
0
&&
err
.
root_cause
[
0
].
reason
)
{
result
.
message
=
err
.
root_cause
[
0
].
reason
;
...
...
@@ -308,9 +320,9 @@ function (_, queryDef) {
}
return
result
;
};
};
ElasticResponse
.
prototype
.
getTimeSeries
=
function
()
{
ElasticResponse
.
prototype
.
getTimeSeries
=
function
()
{
var
seriesList
=
[];
for
(
var
i
=
0
;
i
<
this
.
response
.
responses
.
length
;
i
++
)
{
...
...
@@ -327,9 +339,9 @@ function (_, queryDef) {
var
aggregations
=
response
.
aggregations
;
var
target
=
this
.
targets
[
i
];
var
tmpSeriesList
=
[];
var
docs
=
[]
;
var
table
=
new
TableModel
()
;
this
.
processBuckets
(
aggregations
,
target
,
tmpSeriesList
,
docs
,
{},
0
);
this
.
processBuckets
(
aggregations
,
target
,
tmpSeriesList
,
table
,
{},
0
);
this
.
trimDatapoints
(
tmpSeriesList
,
target
);
this
.
nameSeries
(
tmpSeriesList
,
target
);
...
...
@@ -337,14 +349,12 @@ function (_, queryDef) {
seriesList
.
push
(
tmpSeriesList
[
y
]);
}
if
(
seriesList
.
length
===
0
&&
doc
s
.
length
>
0
)
{
seriesList
.
push
({
target
:
'docs'
,
type
:
'docs'
,
datapoints
:
docs
}
);
if
(
table
.
row
s
.
length
>
0
)
{
seriesList
.
push
(
table
);
}
}
}
return
{
data
:
seriesList
};
};
};
return
ElasticResponse
;
});
public/app/plugins/datasource/elasticsearch/specs/datasource_specs.ts
View file @
781dd25c
...
...
@@ -129,7 +129,10 @@ describe('ElasticDatasource', function() {
'@timestamp'
:
{
type
:
'date'
},
beat
:
{
properties
:
{
name
:
{
type
:
'string'
},
name
:
{
fields
:
{
raw
:
{
type
:
'keyword'
}},
type
:
'string'
},
hostname
:
{
type
:
'string'
},
}
},
...
...
@@ -169,6 +172,7 @@ describe('ElasticDatasource', function() {
var
fields
=
_
.
map
(
fieldObjects
,
'text'
);
expect
(
fields
).
to
.
eql
([
'@timestamp'
,
'beat.name.raw'
,
'beat.name'
,
'beat.hostname'
,
'system.cpu.system'
,
...
...
public/app/plugins/datasource/elasticsearch/specs/elastic_response_specs.ts
View file @
781dd25c
import
{
describe
,
beforeEach
,
it
,
expect
}
from
'test/lib/common'
;
import
ElasticResponse
from
'../elastic_response'
;
import
{
ElasticResponse
}
from
'../elastic_response'
;
describe
(
'ElasticResponse'
,
function
()
{
var
targets
;
...
...
@@ -387,10 +387,9 @@ describe('ElasticResponse', function() {
result
=
new
ElasticResponse
(
targets
,
response
).
getTimeSeries
();
});
it
(
'should return docs with byte and count'
,
function
()
{
expect
(
result
.
data
[
0
].
datapoints
.
length
).
to
.
be
(
3
);
expect
(
result
.
data
[
0
].
datapoints
[
0
].
Count
).
to
.
be
(
1
);
expect
(
result
.
data
[
0
].
datapoints
[
0
].
bytes
).
to
.
be
(
1000
);
it
(
'should return table with byte and count'
,
function
()
{
expect
(
result
.
data
[
0
].
rows
.
length
).
to
.
be
(
3
);
expect
(
result
.
data
[
0
].
columns
).
to
.
eql
([{
text
:
'bytes'
,
filterable
:
true
},
{
text
:
'Count'
}]);
});
});
...
...
@@ -530,14 +529,14 @@ describe('ElasticResponse', function() {
it
(
'should return table'
,
function
()
{
expect
(
result
.
data
.
length
).
to
.
be
(
1
);
expect
(
result
.
data
[
0
].
type
).
to
.
be
(
'
docs
'
);
expect
(
result
.
data
[
0
].
datapoint
s
.
length
).
to
.
be
(
2
);
expect
(
result
.
data
[
0
].
datapoints
[
0
].
host
).
to
.
be
(
"server-1"
);
expect
(
result
.
data
[
0
].
datapoints
[
0
].
Average
).
to
.
be
(
1000
);
expect
(
result
.
data
[
0
].
datapoints
[
0
].
Count
).
to
.
be
(
369
);
expect
(
result
.
data
[
0
].
type
).
to
.
be
(
'
table
'
);
expect
(
result
.
data
[
0
].
row
s
.
length
).
to
.
be
(
2
);
expect
(
result
.
data
[
0
].
rows
[
0
][
0
]
).
to
.
be
(
"server-1"
);
expect
(
result
.
data
[
0
].
rows
[
0
][
1
]
).
to
.
be
(
1000
);
expect
(
result
.
data
[
0
].
rows
[
0
][
2
]
).
to
.
be
(
369
);
expect
(
result
.
data
[
0
].
datapoints
[
1
].
host
).
to
.
be
(
"server-2"
);
expect
(
result
.
data
[
0
].
datapoints
[
1
].
Average
).
to
.
be
(
2000
);
expect
(
result
.
data
[
0
].
rows
[
1
][
0
]
).
to
.
be
(
"server-2"
);
expect
(
result
.
data
[
0
].
rows
[
1
][
1
]
).
to
.
be
(
2000
);
});
});
...
...
@@ -573,10 +572,9 @@ describe('ElasticResponse', function() {
});
it
(
'should include field in metric name'
,
function
()
{
expect
(
result
.
data
[
0
].
type
).
to
.
be
(
'docs'
);
expect
(
result
.
data
[
0
].
datapoints
[
0
].
Average
).
to
.
be
(
undefined
);
expect
(
result
.
data
[
0
].
datapoints
[
0
][
'Average test'
]).
to
.
be
(
1000
);
expect
(
result
.
data
[
0
].
datapoints
[
0
][
'Average test2'
]).
to
.
be
(
3000
);
expect
(
result
.
data
[
0
].
type
).
to
.
be
(
'table'
);
expect
(
result
.
data
[
0
].
rows
[
0
][
1
]).
to
.
be
(
1000
);
expect
(
result
.
data
[
0
].
rows
[
0
][
2
]).
to
.
be
(
3000
);
});
});
...
...
public/app/plugins/panel/table/editor.html
View file @
781dd25c
...
...
@@ -17,9 +17,17 @@
<span>
{{column.text}}
</span>
</label>
</div>
<div
class=
"gf-form"
>
<div
class=
"gf-form"
ng-show=
"editor.canSetColumns"
>
<metric-segment
segment=
"editor.addColumnSegment"
get-options=
"editor.getColumnOptions()"
on-change=
"editor.addColumn()"
></metric-segment>
</div>
<div
class=
"gf-form"
ng-hide=
"editor.canSetColumns"
>
<label
class=
"gf-form-label"
>
Auto
<info-popover
mode=
"right-normal"
ng-if=
"editor.columnsHelpMessage"
>
{{editor.columnsHelpMessage}}
</info-popover>
</label>
</div>
</div>
</div>
...
...
public/app/plugins/panel/table/editor.ts
View file @
781dd25c
...
...
@@ -16,6 +16,8 @@ export class TablePanelEditorCtrl {
fontSizes
:
any
;
addColumnSegment
:
any
;
getColumnNames
:
any
;
canSetColumns
:
boolean
;
columnsHelpMessage
:
string
;
/** @ngInject */
constructor
(
$scope
,
private
$q
,
private
uiSegmentSrv
)
{
...
...
@@ -24,8 +26,27 @@ export class TablePanelEditorCtrl {
this
.
panel
=
this
.
panelCtrl
.
panel
;
this
.
transformers
=
transformers
;
this
.
fontSizes
=
[
'80%'
,
'90%'
,
'100%'
,
'110%'
,
'120%'
,
'130%'
,
'150%'
,
'160%'
,
'180%'
,
'200%'
,
'220%'
,
'250%'
];
this
.
addColumnSegment
=
uiSegmentSrv
.
newPlusButton
();
this
.
updateTransformHints
();
}
updateTransformHints
()
{
this
.
canSetColumns
=
false
;
this
.
columnsHelpMessage
=
''
;
switch
(
this
.
panel
.
transform
)
{
case
"timeseries_aggregations"
:
{
this
.
canSetColumns
=
true
;
break
;
}
case
"json"
:
{
this
.
canSetColumns
=
true
;
break
;
}
case
"table"
:
{
this
.
columnsHelpMessage
=
"Columns and their order are determined by the data query"
;
}
}
}
getColumnOptions
()
{
...
...
@@ -57,6 +78,7 @@ export class TablePanelEditorCtrl {
this
.
panel
.
columns
.
push
({
text
:
'Avg'
,
value
:
'avg'
});
}
this
.
updateTransformHints
();
this
.
render
();
}
...
...
public/app/plugins/panel/table/module.ts
View file @
781dd25c
...
...
@@ -50,8 +50,9 @@ class TablePanelCtrl extends MetricsPanelCtrl {
};
/** @ngInject */
constructor
(
$scope
,
$injector
,
templateSrv
,
private
annotationsSrv
,
private
$sanitize
)
{
constructor
(
$scope
,
$injector
,
templateSrv
,
private
annotationsSrv
,
private
$sanitize
,
private
variableSrv
)
{
super
(
$scope
,
$injector
);
this
.
pageIndex
=
0
;
if
(
this
.
panel
.
styles
===
void
0
)
{
...
...
@@ -223,10 +224,24 @@ class TablePanelCtrl extends MetricsPanelCtrl {
selector
:
'[data-link-tooltip]'
});
function
addFilterClicked
(
e
)
{
let
filterData
=
$
(
e
.
currentTarget
).
data
();
var
options
=
{
datasource
:
panel
.
datasource
,
key
:
data
.
columns
[
filterData
.
column
].
text
,
value
:
data
.
rows
[
filterData
.
row
][
filterData
.
column
],
operator
:
filterData
.
operator
,
};
ctrl
.
variableSrv
.
setAdhocFilter
(
options
);
}
elem
.
on
(
'click'
,
'.table-panel-page-link'
,
switchPage
);
elem
.
on
(
'click'
,
'.table-panel-filter-link'
,
addFilterClicked
);
var
unbindDestroy
=
scope
.
$on
(
'$destroy'
,
function
()
{
elem
.
off
(
'click'
,
'.table-panel-page-link'
);
elem
.
off
(
'click'
,
'.table-panel-filter-link'
);
unbindDestroy
();
});
...
...
public/app/plugins/panel/table/renderer.ts
View file @
781dd25c
...
...
@@ -140,9 +140,12 @@ export class TableRenderer {
renderCell
(
columnIndex
,
rowIndex
,
value
,
addWidthHack
=
false
)
{
value
=
this
.
formatColumnValue
(
columnIndex
,
value
);
var
column
=
this
.
table
.
columns
[
columnIndex
];
var
style
=
''
;
var
cellClasses
=
[];
var
cellClass
=
''
;
if
(
this
.
colorState
.
cell
)
{
style
=
' style="background-color:'
+
this
.
colorState
.
cell
+
';color: white"'
;
this
.
colorState
.
cell
=
null
;
...
...
@@ -161,26 +164,25 @@ export class TableRenderer {
if
(
value
===
undefined
)
{
style
=
' style="display:none;"'
;
this
.
table
.
columns
[
columnIndex
]
.
hidden
=
true
;
column
.
hidden
=
true
;
}
else
{
this
.
table
.
columns
[
columnIndex
]
.
hidden
=
false
;
column
.
hidden
=
false
;
}
var
columnStyle
=
this
.
table
.
columns
[
columnIndex
].
style
;
if
(
columnStyle
&&
columnStyle
.
preserveFormat
)
{
if
(
column
.
style
&&
column
.
style
.
preserveFormat
)
{
cellClasses
.
push
(
"table-panel-cell-pre"
);
}
var
columnHtml
=
value
+
widthHack
;
var
columnHtml
=
widthHack
+
value
;
if
(
column
Style
&&
columnS
tyle
.
link
)
{
if
(
column
.
style
&&
column
.
s
tyle
.
link
)
{
// Render cell as link
var
scopedVars
=
this
.
renderRowVariables
(
rowIndex
);
scopedVars
[
'__cell'
]
=
{
value
:
value
};
var
cellLink
=
this
.
templateSrv
.
replace
(
column
S
tyle
.
linkUrl
,
scopedVars
);
var
cellLinkTooltip
=
this
.
templateSrv
.
replace
(
column
S
tyle
.
linkTooltip
,
scopedVars
);
var
cellTarget
=
column
S
tyle
.
linkTargetBlank
?
'_blank'
:
''
;
var
cellLink
=
this
.
templateSrv
.
replace
(
column
.
s
tyle
.
linkUrl
,
scopedVars
);
var
cellLinkTooltip
=
this
.
templateSrv
.
replace
(
column
.
s
tyle
.
linkTooltip
,
scopedVars
);
var
cellTarget
=
column
.
s
tyle
.
linkTargetBlank
?
'_blank'
:
''
;
cellClasses
.
push
(
"table-panel-cell-link"
);
columnHtml
=
`
...
...
@@ -190,6 +192,19 @@ export class TableRenderer {
`
;
}
if
(
column
.
filterable
)
{
cellClasses
.
push
(
"table-panel-cell-filterable"
);
columnHtml
+=
`
<a class="table-panel-filter-link" data-link-tooltip data-original-title="Filter out value" data-placement="bottom"
data-row="
${
rowIndex
}
" data-column="
${
columnIndex
}
" data-operator="!=">
<i class="fa fa-search-minus"></i>
</a>
<a class="table-panel-filter-link" data-link-tooltip data-original-title="Filter for value" data-placement="bottom"
data-row="
${
rowIndex
}
" data-column="
${
columnIndex
}
" data-operator="=">
<i class="fa fa-search-plus"></i>
</a>`
;
}
if
(
cellClasses
.
length
)
{
cellClass
=
' class="'
+
cellClasses
.
join
(
' '
)
+
'"'
;
}
...
...
public/app/plugins/panel/table/transformers.ts
View file @
781dd25c
...
...
@@ -185,8 +185,16 @@ transformers['json'] = {
},
transform
:
function
(
data
,
panel
,
model
)
{
var
i
,
y
,
z
;
for
(
i
=
0
;
i
<
panel
.
columns
.
length
;
i
++
)
{
model
.
columns
.
push
({
text
:
panel
.
columns
[
i
].
text
});
for
(
let
column
of
panel
.
columns
)
{
var
tableCol
:
any
=
{
text
:
column
.
text
};
// if filterable data then set columns to filterable
if
(
data
.
length
>
0
&&
data
[
0
].
filterable
)
{
tableCol
.
filterable
=
true
;
}
model
.
columns
.
push
(
tableCol
);
}
if
(
model
.
columns
.
length
===
0
)
{
...
...
public/sass/components/_panel_table.scss
View file @
781dd25c
...
...
@@ -91,9 +91,23 @@
&
.cell-highlighted
:hover
{
background-color
:
$tight-form-func-bg
;
}
&
:hover
{
.table-panel-filter-link
{
visibility
:
visible
;
}
}
}
}
.table-panel-filter-link
{
visibility
:
hidden
;
color
:
$text-color-weak
;
float
:
right
;
display
:
block
;
padding
:
0
5px
;
}
.table-panel-header-bg
{
background
:
$grafanaListAccent
;
border-top
:
2px
solid
$body-bg
;
...
...
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