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
ea1859a3
Commit
ea1859a3
authored
Sep 27, 2018
by
Daniel Lee
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
stackdriver: extract out filter, metric type directive
parent
92f50ca3
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
420 additions
and
321 deletions
+420
-321
public/app/plugins/datasource/stackdriver/datasource.ts
+70
-7
public/app/plugins/datasource/stackdriver/partials/query.editor.html
+2
-44
public/app/plugins/datasource/stackdriver/partials/query.filter.html
+42
-0
public/app/plugins/datasource/stackdriver/query_aggregation_ctrl.ts
+1
-0
public/app/plugins/datasource/stackdriver/query_ctrl.ts
+4
-259
public/app/plugins/datasource/stackdriver/query_filter_ctrl.ts
+278
-0
public/app/plugins/datasource/stackdriver/specs/query_filter_ctrl.test.ts
+23
-11
No files found.
public/app/plugins/datasource/stackdriver/datasource.ts
View file @
ea1859a3
import
{
stackdriverUnitMappings
}
from
'./constants'
;
import
{
stackdriverUnitMappings
}
from
'./constants'
;
/** @ngInject */
import
appEvents
from
'app/core/app_events'
;
export
default
class
StackdriverDatasource
{
export
default
class
StackdriverDatasource
{
id
:
number
;
id
:
number
;
url
:
string
;
url
:
string
;
baseUrl
:
string
;
baseUrl
:
string
;
projectName
:
string
;
projectName
:
string
;
/** @ngInject */
constructor
(
instanceSettings
,
private
backendSrv
,
private
templateSrv
,
private
timeSrv
)
{
constructor
(
instanceSettings
,
private
backendSrv
,
private
templateSrv
,
private
timeSrv
)
{
this
.
baseUrl
=
`/stackdriver/`
;
this
.
baseUrl
=
`/stackdriver/`
;
this
.
url
=
instanceSettings
.
url
;
this
.
url
=
instanceSettings
.
url
;
...
@@ -121,6 +123,49 @@ export default class StackdriverDatasource {
...
@@ -121,6 +123,49 @@ export default class StackdriverDatasource {
return
{
data
:
result
};
return
{
data
:
result
};
}
}
async
annotationQuery
(
options
)
{
const
annotation
=
options
.
annotation
;
const
queries
=
[
{
refId
:
'annotationQuery'
,
datasourceId
:
this
.
id
,
metricType
:
this
.
templateSrv
.
replace
(
annotation
.
target
.
metricType
,
options
.
scopedVars
||
{}),
primaryAggregation
:
'REDUCE_NONE'
,
perSeriesAligner
:
'ALIGN_NONE'
,
title
:
this
.
templateSrv
.
replace
(
annotation
.
target
.
title
,
options
.
scopedVars
||
{}),
text
:
this
.
templateSrv
.
replace
(
annotation
.
target
.
text
,
options
.
scopedVars
||
{}),
tags
:
this
.
templateSrv
.
replace
(
annotation
.
target
.
tags
,
options
.
scopedVars
||
{}),
view
:
'FULL'
,
filters
:
(
annotation
.
target
.
filters
||
[]).
map
(
f
=>
{
return
this
.
templateSrv
.
replace
(
f
,
options
.
scopedVars
||
{});
}),
type
:
'annotationQuery'
,
},
];
const
{
data
}
=
await
this
.
backendSrv
.
datasourceRequest
({
url
:
'/api/tsdb/query'
,
method
:
'POST'
,
data
:
{
from
:
options
.
range
.
from
.
valueOf
().
toString
(),
to
:
options
.
range
.
to
.
valueOf
().
toString
(),
queries
,
},
});
const
results
=
data
.
results
[
'annotationQuery'
].
tables
[
0
].
rows
.
map
(
v
=>
{
return
{
annotation
:
annotation
,
time
:
Date
.
parse
(
v
[
0
]),
title
:
v
[
1
],
tags
:
[
v
[
2
]],
text
:
v
[
3
],
};
});
return
results
;
}
testDatasource
()
{
testDatasource
()
{
const
path
=
`v3/projects/
${
this
.
projectName
}
/metricDescriptors`
;
const
path
=
`v3/projects/
${
this
.
projectName
}
/metricDescriptors`
;
return
this
.
doRequest
(
`
${
this
.
baseUrl
}${
path
}
`
)
return
this
.
doRequest
(
`
${
this
.
baseUrl
}${
path
}
`
)
...
@@ -161,12 +206,30 @@ export default class StackdriverDatasource {
...
@@ -161,12 +206,30 @@ export default class StackdriverDatasource {
}
}
async
getDefaultProject
()
{
async
getDefaultProject
()
{
const
projects
=
await
this
.
getProjects
();
try
{
if
(
projects
&&
projects
.
length
>
0
)
{
const
projects
=
await
this
.
getProjects
();
const
test
=
projects
.
filter
(
p
=>
p
.
id
===
this
.
projectName
)[
0
];
if
(
projects
&&
projects
.
length
>
0
)
{
return
test
;
const
test
=
projects
.
filter
(
p
=>
p
.
id
===
this
.
projectName
)[
0
];
}
else
{
return
test
;
throw
new
Error
(
'No projects found'
);
}
else
{
throw
new
Error
(
'No projects found'
);
}
}
catch
(
error
)
{
let
message
=
'Projects cannot be fetched: '
;
message
+=
error
.
statusText
?
error
.
statusText
+
': '
:
''
;
if
(
error
&&
error
.
data
&&
error
.
data
.
error
&&
error
.
data
.
error
.
message
)
{
if
(
error
.
data
.
error
.
code
===
403
)
{
message
+=
`
A list of projects could not be fetched from the Google Cloud Resource Manager API.
You might need to enable it first:
https://console.developers.google.com/apis/library/cloudresourcemanager.googleapis.com`
;
}
else
{
message
+=
error
.
data
.
error
.
code
+
'. '
+
error
.
data
.
error
.
message
;
}
}
else
{
message
+=
'Cannot connect to Stackdriver API'
;
}
appEvents
.
emit
(
'ds-request-error'
,
message
);
}
}
}
}
...
...
public/app/plugins/datasource/stackdriver/partials/query.editor.html
View file @
ea1859a3
<query-editor-row
query-ctrl=
"ctrl"
has-text-edit-mode=
"false"
>
<query-editor-row
query-ctrl=
"ctrl"
has-text-edit-mode=
"false"
>
<div
class=
"gf-form-inline"
>
<stackdriver-filter
target=
"ctrl.target"
refresh=
"ctrl.refresh()"
datasource=
"ctrl.datasource"
default-dropdown-value=
"ctrl.defaultDropdownValue"
default-service-value=
"ctrl.defaultServiceValue"
></stackdriver-filter>
<div
class=
"gf-form"
>
<span
class=
"gf-form-label width-9"
>
Service
</span>
<gf-form-dropdown
model=
"ctrl.service"
get-options=
"ctrl.services"
class=
"min-width-20"
disabled
type=
"text"
allow-custom=
"true"
lookup-text=
"true"
css-class=
"min-width-12"
on-change=
"ctrl.onServiceChange(ctrl.service)"
></gf-form-dropdown>
</div>
<div
class=
"gf-form gf-form--grow"
>
<div
class=
"gf-form-label gf-form-label--grow"
></div>
</div>
</div>
<div
class=
"gf-form-inline"
>
<div
class=
"gf-form"
>
<span
class=
"gf-form-label width-9"
>
Metric
</span>
<gf-form-dropdown
model=
"ctrl.metricType"
get-options=
"ctrl.metrics"
class=
"min-width-20"
disabled
type=
"text"
allow-custom=
"true"
lookup-text=
"true"
css-class=
"min-width-12"
on-change=
"ctrl.onMetricTypeChange()"
></gf-form-dropdown>
</div>
<div
class=
"gf-form gf-form--grow"
>
<div
class=
"gf-form-label gf-form-label--grow"
></div>
</div>
</div>
<div
class=
"gf-form-inline"
>
<div
class=
"gf-form"
>
<span
class=
"gf-form-label query-keyword width-9"
>
Filter
</span>
<div
class=
"gf-form"
ng-repeat=
"segment in ctrl.filterSegments.filterSegments"
>
<metric-segment
segment=
"segment"
get-options=
"ctrl.getFilters(segment, $index)"
on-change=
"ctrl.filterSegmentUpdated(segment, $index)"
></metric-segment>
</div>
</div>
<div
class=
"gf-form gf-form--grow"
>
<div
class=
"gf-form-label gf-form-label--grow"
></div>
</div>
</div>
<div
class=
"gf-form-inline"
>
<div
class=
"gf-form"
>
<span
class=
"gf-form-label query-keyword width-9"
>
Group By
</span>
<div
class=
"gf-form"
ng-repeat=
"segment in ctrl.groupBySegments"
>
<metric-segment
segment=
"segment"
get-options=
"ctrl.getGroupBys(segment, $index)"
on-change=
"ctrl.groupByChanged(segment, $index)"
></metric-segment>
</div>
</div>
<div
class=
"gf-form gf-form--grow"
>
<div
class=
"gf-form-label gf-form-label--grow"
></div>
</div>
</div>
<stackdriver-aggregation
target=
"ctrl.target"
alignment-period=
"ctrl.lastQueryMeta.alignmentPeriod"
refresh=
"ctrl.refresh()"
></stackdriver-aggregation>
<stackdriver-aggregation
target=
"ctrl.target"
alignment-period=
"ctrl.lastQueryMeta.alignmentPeriod"
refresh=
"ctrl.refresh()"
></stackdriver-aggregation>
<div
class=
"gf-form-inline"
>
<div
class=
"gf-form-inline"
>
<div
class=
"gf-form"
>
<div
class=
"gf-form"
>
...
@@ -100,4 +59,4 @@
...
@@ -100,4 +59,4 @@
<div
class=
"gf-form"
ng-show=
"ctrl.lastQueryError"
>
<div
class=
"gf-form"
ng-show=
"ctrl.lastQueryError"
>
<pre
class=
"gf-form-pre alert alert-error"
>
{{ctrl.lastQueryError}}
</pre>
<pre
class=
"gf-form-pre alert alert-error"
>
{{ctrl.lastQueryError}}
</pre>
</div>
</div>
</query-editor-row>
</query-editor-row>
\ No newline at end of file
public/app/plugins/datasource/stackdriver/partials/query.filter.html
0 → 100644
View file @
ea1859a3
<div
class=
"gf-form-inline"
>
<div
class=
"gf-form"
>
<span
class=
"gf-form-label width-9"
>
Service
</span>
<gf-form-dropdown
model=
"ctrl.service"
get-options=
"ctrl.services"
class=
"min-width-20"
disabled
type=
"text"
allow-custom=
"true"
lookup-text=
"true"
css-class=
"min-width-12"
on-change=
"ctrl.onServiceChange(ctrl.service)"
></gf-form-dropdown>
</div>
<div
class=
"gf-form gf-form--grow"
>
<div
class=
"gf-form-label gf-form-label--grow"
></div>
</div>
</div>
<div
class=
"gf-form-inline"
>
<div
class=
"gf-form"
>
<span
class=
"gf-form-label width-9"
>
Metric
</span>
<gf-form-dropdown
model=
"ctrl.metricType"
get-options=
"ctrl.metrics"
class=
"min-width-20"
disabled
type=
"text"
allow-custom=
"true"
lookup-text=
"true"
css-class=
"min-width-12"
on-change=
"ctrl.onMetricTypeChange()"
></gf-form-dropdown>
</div>
<div
class=
"gf-form gf-form--grow"
>
<div
class=
"gf-form-label gf-form-label--grow"
></div>
</div>
</div>
<div
class=
"gf-form-inline"
>
<div
class=
"gf-form"
>
<span
class=
"gf-form-label query-keyword width-9"
>
Filter
</span>
<div
class=
"gf-form"
ng-repeat=
"segment in ctrl.filterSegments.filterSegments"
>
<metric-segment
segment=
"segment"
get-options=
"ctrl.getFilters(segment, $index)"
on-change=
"ctrl.filterSegmentUpdated(segment, $index)"
></metric-segment>
</div>
</div>
<div
class=
"gf-form gf-form--grow"
>
<div
class=
"gf-form-label gf-form-label--grow"
></div>
</div>
</div>
<div
class=
"gf-form-inline"
>
<div
class=
"gf-form"
>
<span
class=
"gf-form-label query-keyword width-9"
>
Group By
</span>
<div
class=
"gf-form"
ng-repeat=
"segment in ctrl.groupBySegments"
>
<metric-segment
segment=
"segment"
get-options=
"ctrl.getGroupBys(segment, $index)"
on-change=
"ctrl.groupByChanged(segment, $index)"
></metric-segment>
</div>
</div>
<div
class=
"gf-form gf-form--grow"
>
<div
class=
"gf-form-label gf-form-label--grow"
></div>
</div>
</div>
public/app/plugins/datasource/stackdriver/query_aggregation_ctrl.ts
View file @
ea1859a3
...
@@ -19,6 +19,7 @@ export class StackdriverAggregation {
...
@@ -19,6 +19,7 @@ export class StackdriverAggregation {
}
}
export
class
StackdriverAggregationCtrl
{
export
class
StackdriverAggregationCtrl
{
/** @ngInject */
constructor
(
private
$scope
)
{
constructor
(
private
$scope
)
{
$scope
.
aggOptions
=
options
.
aggOptions
;
$scope
.
aggOptions
=
options
.
aggOptions
;
this
.
setAggOptions
();
this
.
setAggOptions
();
...
...
public/app/plugins/datasource/stackdriver/query_ctrl.ts
View file @
ea1859a3
import
_
from
'lodash'
;
import
_
from
'lodash'
;
import
{
QueryCtrl
}
from
'app/plugins/sdk'
;
import
{
QueryCtrl
}
from
'app/plugins/sdk'
;
import
appEvents
from
'app/core/app_events'
;
import
{
FilterSegments
,
DefaultRemoveFilterValue
}
from
'./filter_segments'
;
import
'./query_aggregation_ctrl'
;
import
'./query_aggregation_ctrl'
;
import
'./query_filter_ctrl'
;
export
interface
QueryMeta
{
export
interface
QueryMeta
{
alignmentPeriod
:
string
;
alignmentPeriod
:
string
;
...
@@ -34,11 +33,9 @@ export class StackdriverQueryCtrl extends QueryCtrl {
...
@@ -34,11 +33,9 @@ export class StackdriverQueryCtrl extends QueryCtrl {
metricKind
:
any
;
metricKind
:
any
;
valueType
:
any
;
valueType
:
any
;
};
};
defaultDropdownValue
=
'Select Metric'
;
defaultDropdownValue
=
'Select Metric'
;
defaultServiceValue
=
'All Services'
;
defaultServiceValue
=
'All Services'
;
defaultRemoveGroupByValue
=
'-- remove group by --'
;
loadLabelsPromise
:
Promise
<
any
>
;
stackdriverConstants
;
defaults
=
{
defaults
=
{
project
:
{
project
:
{
...
@@ -62,270 +59,18 @@ export class StackdriverQueryCtrl extends QueryCtrl {
...
@@ -62,270 +59,18 @@ export class StackdriverQueryCtrl extends QueryCtrl {
valueType
:
''
,
valueType
:
''
,
};
};
service
:
string
;
metricType
:
string
;
metricDescriptors
:
any
[];
metrics
:
any
[];
services
:
any
[];
groupBySegments
:
any
[];
removeSegment
:
any
;
showHelp
:
boolean
;
showHelp
:
boolean
;
showLastQuery
:
boolean
;
showLastQuery
:
boolean
;
lastQueryMeta
:
QueryMeta
;
lastQueryMeta
:
QueryMeta
;
lastQueryError
?:
string
;
lastQueryError
?:
string
;
metricLabels
:
{
[
key
:
string
]:
string
[]
};
resourceLabels
:
{
[
key
:
string
]:
string
[]
};
filterSegments
:
any
;
/** @ngInject */
/** @ngInject */
constructor
(
$scope
,
$injector
,
private
uiSegmentSrv
,
private
templateSrv
)
{
constructor
(
$scope
,
$injector
)
{
super
(
$scope
,
$injector
);
super
(
$scope
,
$injector
);
_
.
defaultsDeep
(
this
.
target
,
this
.
defaults
);
_
.
defaultsDeep
(
this
.
target
,
this
.
defaults
);
this
.
metricDescriptors
=
[];
this
.
metrics
=
[];
this
.
services
=
[];
this
.
metricType
=
this
.
defaultDropdownValue
;
this
.
service
=
this
.
defaultServiceValue
;
this
.
panelCtrl
.
events
.
on
(
'data-received'
,
this
.
onDataReceived
.
bind
(
this
),
$scope
);
this
.
panelCtrl
.
events
.
on
(
'data-received'
,
this
.
onDataReceived
.
bind
(
this
),
$scope
);
this
.
panelCtrl
.
events
.
on
(
'data-error'
,
this
.
onDataError
.
bind
(
this
),
$scope
);
this
.
panelCtrl
.
events
.
on
(
'data-error'
,
this
.
onDataError
.
bind
(
this
),
$scope
);
this
.
getCurrentProject
()
.
then
(
this
.
loadMetricDescriptors
.
bind
(
this
))
.
then
(
this
.
getLabels
.
bind
(
this
));
this
.
initSegments
();
}
initSegments
()
{
this
.
groupBySegments
=
this
.
target
.
aggregation
.
groupBys
.
map
(
groupBy
=>
{
return
this
.
uiSegmentSrv
.
getSegmentForValue
(
groupBy
);
});
this
.
removeSegment
=
this
.
uiSegmentSrv
.
newSegment
({
fake
:
true
,
value
:
'-- remove group by --'
});
this
.
ensurePlusButton
(
this
.
groupBySegments
);
this
.
filterSegments
=
new
FilterSegments
(
this
.
uiSegmentSrv
,
this
.
target
,
this
.
getGroupBys
.
bind
(
this
,
null
,
null
,
DefaultRemoveFilterValue
,
false
),
this
.
getFilterValues
.
bind
(
this
)
);
this
.
filterSegments
.
buildSegmentModel
();
}
async
getCurrentProject
()
{
try
{
this
.
target
.
project
=
await
this
.
datasource
.
getDefaultProject
();
}
catch
(
error
)
{
let
message
=
'Projects cannot be fetched: '
;
message
+=
error
.
statusText
?
error
.
statusText
+
': '
:
''
;
if
(
error
&&
error
.
data
&&
error
.
data
.
error
&&
error
.
data
.
error
.
message
)
{
if
(
error
.
data
.
error
.
code
===
403
)
{
message
+=
`
A list of projects could not be fetched from the Google Cloud Resource Manager API.
You might need to enable it first:
https://console.developers.google.com/apis/library/cloudresourcemanager.googleapis.com`
;
}
else
{
message
+=
error
.
data
.
error
.
code
+
'. '
+
error
.
data
.
error
.
message
;
}
}
else
{
message
+=
'Cannot connect to Stackdriver API'
;
}
appEvents
.
emit
(
'ds-request-error'
,
message
);
}
}
async
loadMetricDescriptors
()
{
if
(
this
.
target
.
project
.
id
!==
'default'
)
{
this
.
metricDescriptors
=
await
this
.
datasource
.
getMetricTypes
(
this
.
target
.
project
.
id
);
this
.
services
=
this
.
getServicesList
();
this
.
metrics
=
this
.
getMetricsList
();
return
this
.
metricDescriptors
;
}
else
{
return
[];
}
}
getServicesList
()
{
const
defaultValue
=
{
value
:
this
.
defaultServiceValue
,
text
:
this
.
defaultServiceValue
};
const
services
=
this
.
metricDescriptors
.
map
(
m
=>
{
const
[
service
]
=
m
.
type
.
split
(
'/'
);
const
[
serviceShortName
]
=
service
.
split
(
'.'
);
return
{
value
:
service
,
text
:
serviceShortName
,
};
});
if
(
services
.
find
(
m
=>
m
.
value
===
this
.
target
.
service
))
{
this
.
service
=
this
.
target
.
service
;
}
return
services
.
length
>
0
?
[
defaultValue
,
...
_
.
uniqBy
(
services
,
'value'
)]
:
[];
}
getMetricsList
()
{
const
metrics
=
this
.
metricDescriptors
.
map
(
m
=>
{
const
[
service
]
=
m
.
type
.
split
(
'/'
);
const
[
serviceShortName
]
=
service
.
split
(
'.'
);
return
{
service
,
value
:
m
.
type
,
serviceShortName
,
text
:
m
.
displayName
,
title
:
m
.
description
,
};
});
let
result
;
if
(
this
.
target
.
service
===
this
.
defaultServiceValue
)
{
result
=
metrics
.
map
(
m
=>
({
...
m
,
text
:
`
${
m
.
service
}
-
${
m
.
text
}
`
}));
}
else
{
result
=
metrics
.
filter
(
m
=>
m
.
service
===
this
.
target
.
service
);
}
if
(
result
.
find
(
m
=>
m
.
value
===
this
.
target
.
metricType
))
{
this
.
metricType
=
this
.
target
.
metricType
;
}
else
if
(
result
.
length
>
0
)
{
this
.
metricType
=
this
.
target
.
metricType
=
result
[
0
].
value
;
}
return
result
;
}
async
getLabels
()
{
this
.
loadLabelsPromise
=
new
Promise
(
async
resolve
=>
{
try
{
const
data
=
await
this
.
datasource
.
getLabels
(
this
.
target
.
metricType
,
this
.
target
.
refId
);
this
.
metricLabels
=
data
.
results
[
this
.
target
.
refId
].
meta
.
metricLabels
;
this
.
resourceLabels
=
data
.
results
[
this
.
target
.
refId
].
meta
.
resourceLabels
;
resolve
();
}
catch
(
error
)
{
console
.
log
(
error
.
data
.
message
);
appEvents
.
emit
(
'alert-error'
,
[
'Error'
,
'Error loading metric labels for '
+
this
.
target
.
metricType
]);
resolve
();
}
});
}
onServiceChange
()
{
this
.
target
.
service
=
this
.
service
;
this
.
metrics
=
this
.
getMetricsList
();
this
.
setMetricType
();
if
(
!
this
.
metrics
.
find
(
m
=>
m
.
value
===
this
.
target
.
metricType
))
{
this
.
target
.
metricType
=
this
.
defaultDropdownValue
;
}
else
{
this
.
refresh
();
}
}
async
onMetricTypeChange
()
{
this
.
setMetricType
();
this
.
refresh
();
this
.
getLabels
();
}
setMetricType
()
{
this
.
target
.
metricType
=
this
.
metricType
;
const
{
valueType
,
metricKind
,
unit
}
=
this
.
metricDescriptors
.
find
(
m
=>
m
.
type
===
this
.
target
.
metricType
);
this
.
target
.
unit
=
unit
;
this
.
target
.
valueType
=
valueType
;
this
.
target
.
metricKind
=
metricKind
;
this
.
$scope
.
$broadcast
(
'metricTypeChanged'
);
}
async
getGroupBys
(
segment
,
index
,
removeText
?:
string
,
removeUsed
=
true
)
{
await
this
.
loadLabelsPromise
;
const
metricLabels
=
Object
.
keys
(
this
.
metricLabels
||
{})
.
filter
(
ml
=>
{
if
(
!
removeUsed
)
{
return
true
;
}
return
this
.
target
.
aggregation
.
groupBys
.
indexOf
(
'metric.label.'
+
ml
)
===
-
1
;
})
.
map
(
l
=>
{
return
this
.
uiSegmentSrv
.
newSegment
({
value
:
`metric.label.
${
l
}
`
,
expandable
:
false
,
});
});
const
resourceLabels
=
Object
.
keys
(
this
.
resourceLabels
||
{})
.
filter
(
ml
=>
{
if
(
!
removeUsed
)
{
return
true
;
}
return
this
.
target
.
aggregation
.
groupBys
.
indexOf
(
'resource.label.'
+
ml
)
===
-
1
;
})
.
map
(
l
=>
{
return
this
.
uiSegmentSrv
.
newSegment
({
value
:
`resource.label.
${
l
}
`
,
expandable
:
false
,
});
});
const
noValueOrPlusButton
=
!
segment
||
segment
.
type
===
'plus-button'
;
if
(
noValueOrPlusButton
&&
metricLabels
.
length
===
0
&&
resourceLabels
.
length
===
0
)
{
return
Promise
.
resolve
([]);
}
this
.
removeSegment
.
value
=
removeText
||
this
.
defaultRemoveGroupByValue
;
return
Promise
.
resolve
([...
metricLabels
,
...
resourceLabels
,
this
.
removeSegment
]);
}
groupByChanged
(
segment
,
index
)
{
if
(
segment
.
value
===
this
.
removeSegment
.
value
)
{
this
.
groupBySegments
.
splice
(
index
,
1
);
}
else
{
segment
.
type
=
'value'
;
}
const
reducer
=
(
memo
,
seg
)
=>
{
if
(
!
seg
.
fake
)
{
memo
.
push
(
seg
.
value
);
}
return
memo
;
};
this
.
target
.
aggregation
.
groupBys
=
this
.
groupBySegments
.
reduce
(
reducer
,
[]);
this
.
ensurePlusButton
(
this
.
groupBySegments
);
this
.
refresh
();
}
async
getFilters
(
segment
,
index
)
{
const
hasNoFilterKeys
=
this
.
metricLabels
&&
Object
.
keys
(
this
.
metricLabels
).
length
===
0
;
return
this
.
filterSegments
.
getFilters
(
segment
,
index
,
hasNoFilterKeys
);
}
getFilterValues
(
index
)
{
const
filterKey
=
this
.
templateSrv
.
replace
(
this
.
filterSegments
.
filterSegments
[
index
-
2
].
value
);
if
(
!
filterKey
||
!
this
.
metricLabels
||
Object
.
keys
(
this
.
metricLabels
).
length
===
0
)
{
return
[];
}
const
shortKey
=
filterKey
.
substring
(
filterKey
.
indexOf
(
'.label.'
)
+
7
);
if
(
filterKey
.
startsWith
(
'metric.label.'
)
&&
this
.
metricLabels
.
hasOwnProperty
(
shortKey
))
{
return
this
.
metricLabels
[
shortKey
];
}
if
(
filterKey
.
startsWith
(
'resource.label.'
)
&&
this
.
resourceLabels
.
hasOwnProperty
(
shortKey
))
{
return
this
.
resourceLabels
[
shortKey
];
}
return
[];
}
filterSegmentUpdated
(
segment
,
index
)
{
this
.
target
.
filters
=
this
.
filterSegments
.
filterSegmentUpdated
(
segment
,
index
);
this
.
refresh
();
}
ensurePlusButton
(
segments
)
{
const
count
=
segments
.
length
;
const
lastSegment
=
segments
[
Math
.
max
(
count
-
1
,
0
)];
if
(
!
lastSegment
||
lastSegment
.
type
!==
'plus-button'
)
{
segments
.
push
(
this
.
uiSegmentSrv
.
newPlusButton
());
}
}
}
onDataReceived
(
dataList
)
{
onDataReceived
(
dataList
)
{
...
...
public/app/plugins/datasource/stackdriver/query_filter_ctrl.ts
0 → 100644
View file @
ea1859a3
import
angular
from
'angular'
;
import
_
from
'lodash'
;
import
{
FilterSegments
,
DefaultRemoveFilterValue
}
from
'./filter_segments'
;
import
appEvents
from
'app/core/app_events'
;
export
class
StackdriverFilter
{
constructor
()
{
return
{
templateUrl
:
'public/app/plugins/datasource/stackdriver/partials/query.filter.html'
,
controller
:
'StackdriverFilterCtrl'
,
controllerAs
:
'ctrl'
,
restrict
:
'E'
,
scope
:
{
target
:
'='
,
datasource
:
'='
,
refresh
:
'&'
,
defaultDropdownValue
:
'<'
,
defaultServiceValue
:
'<'
,
},
};
}
}
export
class
StackdriverFilterCtrl
{
metricLabels
:
{
[
key
:
string
]:
string
[]
};
resourceLabels
:
{
[
key
:
string
]:
string
[]
};
defaultRemoveGroupByValue
=
'-- remove group by --'
;
loadLabelsPromise
:
Promise
<
any
>
;
service
:
string
;
metricType
:
string
;
metricDescriptors
:
any
[];
metrics
:
any
[];
services
:
any
[];
groupBySegments
:
any
[];
filterSegments
:
FilterSegments
;
removeSegment
:
any
;
target
:
any
;
datasource
:
any
;
/** @ngInject */
constructor
(
private
$scope
,
private
uiSegmentSrv
,
private
templateSrv
)
{
this
.
datasource
=
$scope
.
datasource
;
this
.
target
=
$scope
.
target
;
this
.
metricType
=
$scope
.
defaultDropdownValue
;
this
.
service
=
$scope
.
defaultServiceValue
;
this
.
metricDescriptors
=
[];
this
.
metrics
=
[];
this
.
services
=
[];
this
.
getCurrentProject
()
.
then
(
this
.
loadMetricDescriptors
.
bind
(
this
))
.
then
(
this
.
getLabels
.
bind
(
this
));
this
.
initSegments
();
}
initSegments
()
{
this
.
groupBySegments
=
this
.
target
.
aggregation
.
groupBys
.
map
(
groupBy
=>
{
return
this
.
uiSegmentSrv
.
getSegmentForValue
(
groupBy
);
});
this
.
removeSegment
=
this
.
uiSegmentSrv
.
newSegment
({
fake
:
true
,
value
:
'-- remove group by --'
});
this
.
ensurePlusButton
(
this
.
groupBySegments
);
this
.
filterSegments
=
new
FilterSegments
(
this
.
uiSegmentSrv
,
this
.
target
,
this
.
getGroupBys
.
bind
(
this
,
null
,
null
,
DefaultRemoveFilterValue
,
false
),
this
.
getFilterValues
.
bind
(
this
)
);
this
.
filterSegments
.
buildSegmentModel
();
}
async
getCurrentProject
()
{
this
.
target
.
project
=
await
this
.
datasource
.
getDefaultProject
();
}
async
loadMetricDescriptors
()
{
if
(
this
.
target
.
project
.
id
!==
'default'
)
{
this
.
metricDescriptors
=
await
this
.
datasource
.
getMetricTypes
(
this
.
target
.
project
.
id
);
this
.
services
=
this
.
getServicesList
();
this
.
metrics
=
this
.
getMetricsList
();
return
this
.
metricDescriptors
;
}
else
{
return
[];
}
}
getServicesList
()
{
const
defaultValue
=
{
value
:
this
.
$scope
.
defaultServiceValue
,
text
:
this
.
$scope
.
defaultServiceValue
};
const
services
=
this
.
metricDescriptors
.
map
(
m
=>
{
const
[
service
]
=
m
.
type
.
split
(
'/'
);
const
[
serviceShortName
]
=
service
.
split
(
'.'
);
return
{
value
:
service
,
text
:
serviceShortName
,
};
});
if
(
services
.
find
(
m
=>
m
.
value
===
this
.
target
.
service
))
{
this
.
service
=
this
.
target
.
service
;
}
return
services
.
length
>
0
?
[
defaultValue
,
...
_
.
uniqBy
(
services
,
'value'
)]
:
[];
}
getMetricsList
()
{
const
metrics
=
this
.
metricDescriptors
.
map
(
m
=>
{
const
[
service
]
=
m
.
type
.
split
(
'/'
);
const
[
serviceShortName
]
=
service
.
split
(
'.'
);
return
{
service
,
value
:
m
.
type
,
serviceShortName
,
text
:
m
.
displayName
,
title
:
m
.
description
,
};
});
let
result
;
if
(
this
.
target
.
service
===
this
.
$scope
.
defaultServiceValue
)
{
result
=
metrics
.
map
(
m
=>
({
...
m
,
text
:
`
${
m
.
service
}
-
${
m
.
text
}
`
}));
}
else
{
result
=
metrics
.
filter
(
m
=>
m
.
service
===
this
.
target
.
service
);
}
if
(
result
.
find
(
m
=>
m
.
value
===
this
.
target
.
metricType
))
{
this
.
metricType
=
this
.
target
.
metricType
;
}
else
if
(
result
.
length
>
0
)
{
this
.
metricType
=
this
.
target
.
metricType
=
result
[
0
].
value
;
}
return
result
;
}
async
getLabels
()
{
this
.
loadLabelsPromise
=
new
Promise
(
async
resolve
=>
{
try
{
const
data
=
await
this
.
datasource
.
getLabels
(
this
.
target
.
metricType
,
this
.
target
.
refId
);
this
.
metricLabels
=
data
.
results
[
this
.
target
.
refId
].
meta
.
metricLabels
;
this
.
resourceLabels
=
data
.
results
[
this
.
target
.
refId
].
meta
.
resourceLabels
;
resolve
();
}
catch
(
error
)
{
console
.
log
(
error
.
data
.
message
);
appEvents
.
emit
(
'alert-error'
,
[
'Error'
,
'Error loading metric labels for '
+
this
.
target
.
metricType
]);
resolve
();
}
});
}
onServiceChange
()
{
this
.
target
.
service
=
this
.
service
;
this
.
metrics
=
this
.
getMetricsList
();
this
.
setMetricType
();
if
(
!
this
.
metrics
.
find
(
m
=>
m
.
value
===
this
.
target
.
metricType
))
{
this
.
target
.
metricType
=
this
.
$scope
.
defaultDropdownValue
;
}
else
{
this
.
$scope
.
refresh
();
}
}
async
onMetricTypeChange
()
{
this
.
setMetricType
();
this
.
$scope
.
refresh
();
this
.
getLabels
();
}
setMetricType
()
{
this
.
target
.
metricType
=
this
.
metricType
;
const
{
valueType
,
metricKind
,
unit
}
=
this
.
metricDescriptors
.
find
(
m
=>
m
.
type
===
this
.
target
.
metricType
);
this
.
target
.
unit
=
unit
;
this
.
target
.
valueType
=
valueType
;
this
.
target
.
metricKind
=
metricKind
;
this
.
$scope
.
$broadcast
(
'metricTypeChanged'
);
}
async
getGroupBys
(
segment
,
index
,
removeText
?:
string
,
removeUsed
=
true
)
{
await
this
.
loadLabelsPromise
;
const
metricLabels
=
Object
.
keys
(
this
.
metricLabels
||
{})
.
filter
(
ml
=>
{
if
(
!
removeUsed
)
{
return
true
;
}
return
this
.
target
.
aggregation
.
groupBys
.
indexOf
(
'metric.label.'
+
ml
)
===
-
1
;
})
.
map
(
l
=>
{
return
this
.
uiSegmentSrv
.
newSegment
({
value
:
`metric.label.
${
l
}
`
,
expandable
:
false
,
});
});
const
resourceLabels
=
Object
.
keys
(
this
.
resourceLabels
||
{})
.
filter
(
ml
=>
{
if
(
!
removeUsed
)
{
return
true
;
}
return
this
.
target
.
aggregation
.
groupBys
.
indexOf
(
'resource.label.'
+
ml
)
===
-
1
;
})
.
map
(
l
=>
{
return
this
.
uiSegmentSrv
.
newSegment
({
value
:
`resource.label.
${
l
}
`
,
expandable
:
false
,
});
});
const
noValueOrPlusButton
=
!
segment
||
segment
.
type
===
'plus-button'
;
if
(
noValueOrPlusButton
&&
metricLabels
.
length
===
0
&&
resourceLabels
.
length
===
0
)
{
return
Promise
.
resolve
([]);
}
this
.
removeSegment
.
value
=
removeText
||
this
.
defaultRemoveGroupByValue
;
return
Promise
.
resolve
([...
metricLabels
,
...
resourceLabels
,
this
.
removeSegment
]);
}
groupByChanged
(
segment
,
index
)
{
if
(
segment
.
value
===
this
.
removeSegment
.
value
)
{
this
.
groupBySegments
.
splice
(
index
,
1
);
}
else
{
segment
.
type
=
'value'
;
}
const
reducer
=
(
memo
,
seg
)
=>
{
if
(
!
seg
.
fake
)
{
memo
.
push
(
seg
.
value
);
}
return
memo
;
};
this
.
target
.
aggregation
.
groupBys
=
this
.
groupBySegments
.
reduce
(
reducer
,
[]);
this
.
ensurePlusButton
(
this
.
groupBySegments
);
this
.
$scope
.
refresh
();
}
async
getFilters
(
segment
,
index
)
{
const
hasNoFilterKeys
=
this
.
metricLabels
&&
Object
.
keys
(
this
.
metricLabels
).
length
===
0
;
return
this
.
filterSegments
.
getFilters
(
segment
,
index
,
hasNoFilterKeys
);
}
getFilterValues
(
index
)
{
const
filterKey
=
this
.
templateSrv
.
replace
(
this
.
filterSegments
.
filterSegments
[
index
-
2
].
value
);
if
(
!
filterKey
||
!
this
.
metricLabels
||
Object
.
keys
(
this
.
metricLabels
).
length
===
0
)
{
return
[];
}
const
shortKey
=
filterKey
.
substring
(
filterKey
.
indexOf
(
'.label.'
)
+
7
);
if
(
filterKey
.
startsWith
(
'metric.label.'
)
&&
this
.
metricLabels
.
hasOwnProperty
(
shortKey
))
{
return
this
.
metricLabels
[
shortKey
];
}
if
(
filterKey
.
startsWith
(
'resource.label.'
)
&&
this
.
resourceLabels
.
hasOwnProperty
(
shortKey
))
{
return
this
.
resourceLabels
[
shortKey
];
}
return
[];
}
filterSegmentUpdated
(
segment
,
index
)
{
this
.
target
.
filters
=
this
.
filterSegments
.
filterSegmentUpdated
(
segment
,
index
);
this
.
$scope
.
refresh
();
}
ensurePlusButton
(
segments
)
{
const
count
=
segments
.
length
;
const
lastSegment
=
segments
[
Math
.
max
(
count
-
1
,
0
)];
if
(
!
lastSegment
||
lastSegment
.
type
!==
'plus-button'
)
{
segments
.
push
(
this
.
uiSegmentSrv
.
newPlusButton
());
}
}
}
angular
.
module
(
'grafana.controllers'
).
directive
(
'stackdriverFilter'
,
StackdriverFilter
);
angular
.
module
(
'grafana.controllers'
).
controller
(
'StackdriverFilterCtrl'
,
StackdriverFilterCtrl
);
public/app/plugins/datasource/stackdriver/specs/query_ctrl.test.ts
→
public/app/plugins/datasource/stackdriver/specs/query_
filter_
ctrl.test.ts
View file @
ea1859a3
import
{
Stackdriver
QueryCtrl
}
from
'../query
_ctrl'
;
import
{
Stackdriver
FilterCtrl
}
from
'../query_filter
_ctrl'
;
import
{
TemplateSrvStub
}
from
'test/specs/helpers'
;
import
{
TemplateSrvStub
}
from
'test/specs/helpers'
;
import
{
DefaultRemoveFilterValue
,
DefaultFilterValue
}
from
'../filter_segments'
;
import
{
DefaultRemoveFilterValue
,
DefaultFilterValue
}
from
'../filter_segments'
;
describe
(
'StackdriverQueryCtrl'
,
()
=>
{
describe
(
'StackdriverQuery
Filter
Ctrl'
,
()
=>
{
let
ctrl
;
let
ctrl
;
let
result
;
let
result
;
...
@@ -367,16 +367,16 @@ describe('StackdriverQueryCtrl', () => {
...
@@ -367,16 +367,16 @@ describe('StackdriverQueryCtrl', () => {
});
});
function
createCtrlWithFakes
(
existingFilters
?:
string
[])
{
function
createCtrlWithFakes
(
existingFilters
?:
string
[])
{
StackdriverQuery
Ctrl
.
prototype
.
panelCtrl
=
{
// StackdriverFilter
Ctrl.prototype.panelCtrl = {
events
:
{
on
:
()
=>
{}
},
//
events: { on: () => {} },
panel
:
{
scopedVars
:
[],
targets
:
[]
},
//
panel: { scopedVars: [], targets: [] },
refresh
:
()
=>
{},
//
refresh: () => {},
};
//
};
StackdriverQueryCtrl
.
prototype
.
target
=
createTarget
(
existingFilters
);
// StackdriverFilterCtrl.prototype.target =
Stackdriver
Query
Ctrl
.
prototype
.
loadMetricDescriptors
=
()
=>
{
Stackdriver
Filter
Ctrl
.
prototype
.
loadMetricDescriptors
=
()
=>
{
return
Promise
.
resolve
([]);
return
Promise
.
resolve
([]);
};
};
Stackdriver
Query
Ctrl
.
prototype
.
getLabels
=
()
=>
{
Stackdriver
Filter
Ctrl
.
prototype
.
getLabels
=
()
=>
{
return
Promise
.
resolve
();
return
Promise
.
resolve
();
};
};
...
@@ -408,7 +408,19 @@ function createCtrlWithFakes(existingFilters?: string[]) {
...
@@ -408,7 +408,19 @@ function createCtrlWithFakes(existingFilters?: string[]) {
return
{
type
:
'condition'
,
value
:
val
};
return
{
type
:
'condition'
,
value
:
val
};
},
},
};
};
return
new
StackdriverQueryCtrl
(
null
,
null
,
fakeSegmentServer
,
new
TemplateSrvStub
());
const
scope
=
{
target
:
createTarget
(
existingFilters
),
datasource
:
{
getDefaultProject
:
()
=>
{
return
'project'
;
},
},
defaultDropdownValue
:
'Select Metric'
,
defaultServiceValue
:
'All Services'
,
refresh
:
()
=>
{},
};
return
new
StackdriverFilterCtrl
(
scope
,
fakeSegmentServer
,
new
TemplateSrvStub
());
}
}
function
createTarget
(
existingFilters
?:
string
[])
{
function
createTarget
(
existingFilters
?:
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