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
b762f56a
Commit
b762f56a
authored
Apr 29, 2015
by
Torkel Ödegaard
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'master' into panel_repeat
Conflicts: public/app/features/templating/templateValuesSrv.js
parents
8c14e565
74a8fa61
Hide whitespace changes
Inline
Side-by-side
Showing
16 changed files
with
144 additions
and
48 deletions
+144
-48
CHANGELOG.md
+7
-1
conf/defaults.ini
+3
-2
conf/sample.ini
+5
-4
docs/Makefile
+1
-1
docs/sources/guides/screencasts.md
+1
-4
docs/sources/installation/configuration.md
+16
-2
docs/sources/installation/debian.md
+1
-1
pkg/api/dtos/models.go
+2
-1
pkg/api/index.go
+1
-0
pkg/api/login_oauth.go
+7
-2
pkg/social/social.go
+60
-8
public/app/components/kbn.js
+2
-0
public/app/controllers/loginCtrl.js
+8
-1
public/app/features/templating/templateValuesSrv.js
+20
-7
public/app/plugins/datasource/influxdb/influxSeries.js
+8
-13
public/app/plugins/datasource/influxdb/plugin.json
+2
-1
No files found.
CHANGELOG.md
View file @
b762f56a
# 2.0.3 (unreleased)
# 2.1.0 (unreleased - master branch)
**Backend**
-
[
Issue #1905
](
https://github.com/grafana/grafana/issues/1905
)
. Github OAuth: You can now configure a Github team membership requirement, thx @dewski
# 2.0.3 (unreleased - 2.0.x branch)
**Fixes**
-
[
Issue #1872
](
https://github.com/grafana/grafana/issues/1872
)
. Firefox/IE issue, invisible text in dashboard search fixed
...
...
conf/defaults.ini
View file @
b762f56a
...
...
@@ -137,18 +137,20 @@ org_role = Viewer
#################################### Github Auth ##########################
[auth.github]
enabled
=
false
allow_sign_up
=
false
client_id
=
some_id
client_secret
=
some_secret
scopes
=
user:email
auth_url
=
https://github.com/login/oauth/authorize
token_url
=
https://github.com/login/oauth/access_token
api_url
=
https://api.github.com/user
team_ids
=
allowed_domains
=
allow_sign_up
=
false
#################################### Google Auth ##########################
[auth.google]
enabled
=
false
allow_sign_up
=
false
client_id
=
some_client_id
client_secret
=
some_client_secret
scopes
=
https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email
...
...
@@ -156,7 +158,6 @@ auth_url = https://accounts.google.com/o/oauth2/auth
token_url
=
https://accounts.google.com/o/oauth2/token
api_url
=
https://www.googleapis.com/oauth2/v1/userinfo
allowed_domains
=
allow_sign_up
=
false
#################################### Logging ##########################
[log]
...
...
conf/sample.ini
View file @
b762f56a
...
...
@@ -136,26 +136,27 @@
#################################### Github Auth ##########################
[auth.github]
;enabled = false
;allow_sign_up = false
;client_id = some_id
;client_secret = some_secret
;scopes = user:email
;auth_url = https://github.com/login/oauth/authorize
;token_url = https://github.com/login/oauth/access_token
;api_url = https://api.github.com/user
# Uncomment bellow to only allow specific email domains
;
allowed_domains = mycompany.com othercompany.com
;team_ids =
;
allowed_domains =
#################################### Google Auth ##########################
[auth.google]
;enabled = false
;allow_sign_up = false
;client_id = some_client_id
;client_secret = some_client_secret
;scopes = https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email
;auth_url = https://accounts.google.com/o/oauth2/auth
;token_url = https://accounts.google.com/o/oauth2/token
;api_url = https://www.googleapis.com/oauth2/v1/userinfo
# Uncomment bellow to only allow specific email domains
; allowed_domains = mycompany.com othercompany.com
;allowed_domains =
#################################### Logging ##########################
[log]
...
...
docs/Makefile
View file @
b762f56a
...
...
@@ -44,7 +44,7 @@ docs-test: docs-build
$(DOCKER_RUN_DOCS)
"
$(DOCKER_DOCS_IMAGE)
"
./test.sh
docs-build
:
git fetch https://github.com/grafana/grafana.git docs-
2.0
&&
git diff
--name-status
FETCH_HEAD...HEAD
--
.
>
changed-files
git fetch https://github.com/grafana/grafana.git docs-
1.x
&&
git diff
--name-status
FETCH_HEAD...HEAD
--
.
>
changed-files
echo
"
$(GIT_BRANCH)
"
>
GIT_BRANCH
echo
"
$(GITCOMMIT)
"
>
GITCOMMIT
docker build
-t
"
$(DOCKER_DOCS_IMAGE)
"
.
docs/sources/guides/screencasts.md
View file @
b762f56a
...
...
@@ -15,10 +15,9 @@ no_toc: true
<div
class=
"columns medium-6"
>
<h3>
Episode 2 - Templated Graphite Queries
</h3>
<div
class=
"video-container"
>
<iframe
height=
"
3
15"
src=
"//www.youtube.com/embed/FhNUrueWwOk?list=PLDGkOdUX1Ujo3wHw9-z5Vo12YLqXRjzg2"
frameborder=
"0"
allowfullscreen
></iframe>
<iframe
height=
"
2
15"
src=
"//www.youtube.com/embed/FhNUrueWwOk?list=PLDGkOdUX1Ujo3wHw9-z5Vo12YLqXRjzg2"
frameborder=
"0"
allowfullscreen
></iframe>
</div>
</div>
</div>
</div>
<div
class=
"row"
>
...
...
@@ -34,7 +33,6 @@ no_toc: true
<iframe
height=
"215"
src=
"https://www.youtube.com/embed/JY22EBOR9hQ?list=PLDGkOdUX1Ujo3wHw9-z5Vo12YLqXRjzg2"
frameborder=
"0"
allowfullscreen
></iframe>
</div>
</div>
</div>
</div>
<div
class=
"row"
>
...
...
@@ -50,7 +48,6 @@ no_toc: true
<iframe
height=
"215"
src=
"https://www.youtube.com/embed/9ZCMVNxUf6s?list=PLDGkOdUX1Ujo3wHw9-z5Vo12YLqXRjzg2"
frameborder=
"0"
allowfullscreen
></iframe>
</div>
</div>
</div>
</div>
<div
class=
"row"
>
...
...
docs/sources/installation/configuration.md
View file @
b762f56a
...
...
@@ -182,6 +182,7 @@ Client ID and a Client Secret. Specify these in the grafana config file. Example
auth_url = https://github.com/login/oauth/authorize
token_url = https://github.com/login/oauth/access_token
allow_sign_up = false
team_ids =
Restart the grafana backend. You should now see a github login button on the login page. You can
now login or signup with your github accounts.
...
...
@@ -189,6 +190,21 @@ now login or signup with your github accounts.
You may allow users to sign-up via github auth by setting allow_sign_up to true. When this option is
set to true, any user successfully authenticating via github auth will be automatically signed up.
### team_ids
Require an active team membership for at least one of the given teams on GitHub.
If the authenticated user isn't a member of at least one the teams they will not
be able to register or authenticate with your Grafana instance. Example:
[auth.github]
enabled = true
client_id = YOUR_GITHUB_APP_CLIENT_ID
client_secret = YOUR_GITHUB_APP_CLIENT_SECRET
scopes = user:email
team_ids = 150,300
auth_url = https://github.com/login/oauth/authorize
token_url = https://github.com/login/oauth/access_token
allow_sign_up = false
## [auth.google]
You need to create a google project. You can do this in the
[
Google Developer Console
](
https://console.developers.google.com/project
)
.
When you create the project you will need to specify a callback URL. Specify this as callback:
...
...
@@ -257,5 +273,3 @@ enabled. Counters are sent every 24 hours. Default value is `true`.
### google_analytics_ua_id
If you want to track Grafana usage via Google analytics specify
*your*
Univeral Analytics ID
here. By defualt this feature is disabled.
docs/sources/installation/debian.md
View file @
b762f56a
...
...
@@ -16,7 +16,7 @@ Description | Download
$ wget https://grafanarel.s3.amazonaws.com/builds/grafana_2.0.2_amd64.deb
$ sudo apt-get install -y adduser libfontconfig
$ sudo dpkg -i grafana_2.0.
1
_amd64.deb
$ sudo dpkg -i grafana_2.0.
2
_amd64.deb
## APT Repository
Add the following line to your
`/etc/apt/sources.list`
...
...
pkg/api/dtos/models.go
View file @
b762f56a
...
...
@@ -21,8 +21,9 @@ type CurrentUser struct {
Email
string
`json:"email"`
Name
string
`json:"name"`
LightTheme
bool
`json:"lightTheme"`
Org
Role
m
.
RoleType
`json:"orgRole
"`
Org
Id
int64
`json:"orgId
"`
OrgName
string
`json:"orgName"`
OrgRole
m
.
RoleType
`json:"orgRole"`
IsGrafanaAdmin
bool
`json:"isGrafanaAdmin"`
GravatarUrl
string
`json:"gravatarUrl"`
}
...
...
pkg/api/index.go
View file @
b762f56a
...
...
@@ -18,6 +18,7 @@ func setIndexViewData(c *middleware.Context) error {
Email
:
c
.
Email
,
Name
:
c
.
Name
,
LightTheme
:
c
.
Theme
==
"light"
,
OrgId
:
c
.
OrgId
,
OrgName
:
c
.
OrgName
,
OrgRole
:
c
.
OrgRole
,
GravatarUrl
:
dtos
.
GetGravatarUrl
(
c
.
Email
),
...
...
pkg/api/login_oauth.go
View file @
b762f56a
...
...
@@ -3,6 +3,7 @@ package api
import
(
"errors"
"fmt"
"net/url"
"golang.org/x/oauth2"
...
...
@@ -45,7 +46,11 @@ func OAuthLogin(ctx *middleware.Context) {
userInfo
,
err
:=
connect
.
UserInfo
(
token
)
if
err
!=
nil
{
ctx
.
Handle
(
500
,
fmt
.
Sprintf
(
"login.OAuthLogin(get info from %s)"
,
name
),
err
)
if
err
==
social
.
ErrMissingTeamMembership
{
ctx
.
Redirect
(
setting
.
AppSubUrl
+
"/login?failedMsg="
+
url
.
QueryEscape
(
"Required Github team membership not fulfilled"
))
}
else
{
ctx
.
Handle
(
500
,
fmt
.
Sprintf
(
"login.OAuthLogin(get info from %s)"
,
name
),
err
)
}
return
}
...
...
@@ -54,7 +59,7 @@ func OAuthLogin(ctx *middleware.Context) {
// validate that the email is allowed to login to grafana
if
!
connect
.
IsEmailAllowed
(
userInfo
.
Email
)
{
log
.
Info
(
"OAuth login attempt with unallowed email, %s"
,
userInfo
.
Email
)
ctx
.
Redirect
(
setting
.
AppSubUrl
+
"/login?
email_not_allowed=1"
)
ctx
.
Redirect
(
setting
.
AppSubUrl
+
"/login?
failedMsg="
+
url
.
QueryEscape
(
"Required email domain not fulfilled"
)
)
return
}
...
...
pkg/social/social.go
View file @
b762f56a
...
...
@@ -2,7 +2,9 @@ package social
import
(
"encoding/json"
"errors"
"fmt"
"net/http"
"strconv"
"strings"
...
...
@@ -75,13 +77,24 @@ func NewOAuthService() {
// GitHub.
if
name
==
"github"
{
setting
.
OAuthService
.
GitHub
=
true
SocialMap
[
"github"
]
=
&
SocialGithub
{
Config
:
&
config
,
allowedDomains
:
info
.
AllowedDomains
,
ApiUrl
:
info
.
ApiUrl
,
allowSignup
:
info
.
AllowSignup
}
teamIds
:=
sec
.
Key
(
"team_ids"
)
.
Ints
(
","
)
SocialMap
[
"github"
]
=
&
SocialGithub
{
Config
:
&
config
,
allowedDomains
:
info
.
AllowedDomains
,
apiUrl
:
info
.
ApiUrl
,
allowSignup
:
info
.
AllowSignup
,
teamIds
:
teamIds
,
}
}
// Google.
if
name
==
"google"
{
setting
.
OAuthService
.
Google
=
true
SocialMap
[
"google"
]
=
&
SocialGoogle
{
Config
:
&
config
,
allowedDomains
:
info
.
AllowedDomains
,
ApiUrl
:
info
.
ApiUrl
,
allowSignup
:
info
.
AllowSignup
}
SocialMap
[
"google"
]
=
&
SocialGoogle
{
Config
:
&
config
,
allowedDomains
:
info
.
AllowedDomains
,
apiUrl
:
info
.
ApiUrl
,
allowSignup
:
info
.
AllowSignup
,
}
}
}
}
...
...
@@ -103,10 +116,15 @@ func isEmailAllowed(email string, allowedDomains []string) bool {
type
SocialGithub
struct
{
*
oauth2
.
Config
allowedDomains
[]
string
A
piUrl
string
a
piUrl
string
allowSignup
bool
teamIds
[]
int
}
var
(
ErrMissingTeamMembership
=
errors
.
New
(
"User not a member of one of the required teams"
)
)
func
(
s
*
SocialGithub
)
Type
()
int
{
return
int
(
models
.
GITHUB
)
}
...
...
@@ -119,6 +137,28 @@ func (s *SocialGithub) IsSignupAllowed() bool {
return
s
.
allowSignup
}
func
(
s
*
SocialGithub
)
IsTeamMember
(
client
*
http
.
Client
,
username
string
,
teamId
int
)
bool
{
var
data
struct
{
Url
string
`json:"url"`
State
string
`json:"state"`
}
membershipUrl
:=
fmt
.
Sprintf
(
"https://api.github.com/teams/%d/memberships/%s"
,
teamId
,
username
)
r
,
err
:=
client
.
Get
(
membershipUrl
)
if
err
!=
nil
{
return
false
}
defer
r
.
Body
.
Close
()
if
err
=
json
.
NewDecoder
(
r
.
Body
)
.
Decode
(
&
data
);
err
!=
nil
{
return
false
}
active
:=
data
.
State
==
"active"
return
active
}
func
(
s
*
SocialGithub
)
UserInfo
(
token
*
oauth2
.
Token
)
(
*
BasicUserInfo
,
error
)
{
var
data
struct
{
Id
int
`json:"id"`
...
...
@@ -128,7 +168,7 @@ func (s *SocialGithub) UserInfo(token *oauth2.Token) (*BasicUserInfo, error) {
var
err
error
client
:=
s
.
Client
(
oauth2
.
NoContext
,
token
)
r
,
err
:=
client
.
Get
(
s
.
A
piUrl
)
r
,
err
:=
client
.
Get
(
s
.
a
piUrl
)
if
err
!=
nil
{
return
nil
,
err
}
...
...
@@ -139,11 +179,23 @@ func (s *SocialGithub) UserInfo(token *oauth2.Token) (*BasicUserInfo, error) {
return
nil
,
err
}
return
&
BasicUserInfo
{
userInfo
:=
&
BasicUserInfo
{
Identity
:
strconv
.
Itoa
(
data
.
Id
),
Name
:
data
.
Name
,
Email
:
data
.
Email
,
},
nil
}
if
len
(
s
.
teamIds
)
>
0
{
for
_
,
teamId
:=
range
s
.
teamIds
{
if
s
.
IsTeamMember
(
client
,
data
.
Name
,
teamId
)
{
return
userInfo
,
nil
}
}
return
nil
,
ErrMissingTeamMembership
}
else
{
return
userInfo
,
nil
}
}
// ________ .__
...
...
@@ -156,7 +208,7 @@ func (s *SocialGithub) UserInfo(token *oauth2.Token) (*BasicUserInfo, error) {
type
SocialGoogle
struct
{
*
oauth2
.
Config
allowedDomains
[]
string
A
piUrl
string
a
piUrl
string
allowSignup
bool
}
...
...
@@ -181,7 +233,7 @@ func (s *SocialGoogle) UserInfo(token *oauth2.Token) (*BasicUserInfo, error) {
var
err
error
client
:=
s
.
Client
(
oauth2
.
NoContext
,
token
)
r
,
err
:=
client
.
Get
(
s
.
A
piUrl
)
r
,
err
:=
client
.
Get
(
s
.
a
piUrl
)
if
err
!=
nil
{
return
nil
,
err
}
...
...
public/app/components/kbn.js
View file @
b762f56a
...
...
@@ -376,6 +376,7 @@ function($, _, moment) {
kbn
.
valueFormats
.
bytes
=
kbn
.
formatFuncCreator
(
1024
,
[
' B'
,
' KiB'
,
' MiB'
,
' GiB'
,
' TiB'
,
' PiB'
,
' EiB'
,
' ZiB'
,
' YiB'
]);
kbn
.
valueFormats
.
kbytes
=
kbn
.
formatFuncCreator
(
1024
,
[
' KiB'
,
' MiB'
,
' GiB'
,
' TiB'
,
' PiB'
,
' EiB'
,
' ZiB'
,
' YiB'
]);
kbn
.
valueFormats
.
mbytes
=
kbn
.
formatFuncCreator
(
1024
,
[
' MiB'
,
' GiB'
,
' TiB'
,
' PiB'
,
' EiB'
,
' ZiB'
,
' YiB'
]);
kbn
.
valueFormats
.
gbytes
=
kbn
.
formatFuncCreator
(
1024
,
[
' GiB'
,
' TiB'
,
' PiB'
,
' EiB'
,
' ZiB'
,
' YiB'
]);
kbn
.
valueFormats
.
bps
=
kbn
.
formatFuncCreator
(
1000
,
[
' bps'
,
' Kbps'
,
' Mbps'
,
' Gbps'
,
' Tbps'
,
' Pbps'
,
' Ebps'
,
' Zbps'
,
' Ybps'
]);
kbn
.
valueFormats
.
Bps
=
kbn
.
formatFuncCreator
(
1000
,
[
' Bps'
,
' KBps'
,
' MBps'
,
' GBps'
,
' TBps'
,
' PBps'
,
' EBps'
,
' ZBps'
,
' YBps'
]);
kbn
.
valueFormats
.
short
=
kbn
.
formatFuncCreator
(
1000
,
[
''
,
' K'
,
' Mil'
,
' Bil'
,
' Tri'
,
' Qaudr'
,
' Quint'
,
' Sext'
,
' Sept'
]);
...
...
@@ -547,6 +548,7 @@ function($, _, moment) {
{
text
:
'bytes'
,
value
:
'bytes'
},
{
text
:
'kilobytes'
,
value
:
'kbytes'
},
{
text
:
'megabytes'
,
value
:
'mbytes'
},
{
text
:
'gigabytes'
,
value
:
'gbytes'
},
]
},
{
...
...
public/app/controllers/loginCtrl.js
View file @
b762f56a
...
...
@@ -7,7 +7,7 @@ function (angular, config) {
var
module
=
angular
.
module
(
'grafana.controllers'
);
module
.
controller
(
'LoginCtrl'
,
function
(
$scope
,
backendSrv
,
contextSrv
)
{
module
.
controller
(
'LoginCtrl'
,
function
(
$scope
,
backendSrv
,
contextSrv
,
$location
)
{
$scope
.
formModel
=
{
user
:
''
,
email
:
''
,
...
...
@@ -28,6 +28,13 @@ function (angular, config) {
$scope
.
init
=
function
()
{
$scope
.
$watch
(
"loginMode"
,
$scope
.
loginModeChanged
);
$scope
.
passwordChanged
();
var
params
=
$location
.
search
();
if
(
params
.
failedMsg
)
{
$scope
.
appEvent
(
'alert-warning'
,
[
'Login Failed'
,
params
.
failedMsg
]);
delete
params
.
failedMsg
;
$location
.
search
(
params
);
}
};
// build info view model
...
...
public/app/features/templating/templateValuesSrv.js
View file @
b762f56a
...
...
@@ -29,13 +29,7 @@ function (angular, _, kbn) {
var
variable
=
this
.
variables
[
i
];
var
urlValue
=
queryParams
[
'var-'
+
variable
.
name
];
if
(
urlValue
!==
void
0
)
{
var
option
=
_
.
findWhere
(
variable
.
options
,
{
text
:
urlValue
});
option
=
option
||
{
text
:
urlValue
,
value
:
urlValue
};
var
promise
=
this
.
setVariableValue
(
variable
,
option
);
this
.
updateAutoInterval
(
variable
);
promises
.
push
(
promise
);
promises
.
push
(
this
.
setVariableFromUrl
(
variable
,
urlValue
));
}
else
if
(
variable
.
refresh
)
{
promises
.
push
(
this
.
updateOptions
(
variable
));
...
...
@@ -48,6 +42,25 @@ function (angular, _, kbn) {
return
$q
.
all
(
promises
);
};
this
.
setVariableFromUrl
=
function
(
variable
,
urlValue
)
{
if
(
variable
.
refresh
)
{
var
self
=
this
;
//refresh the list of options before setting the value
return
this
.
updateOptions
(
variable
).
then
(
function
()
{
var
option
=
_
.
findWhere
(
variable
.
options
,
{
text
:
urlValue
});
option
=
option
||
{
text
:
urlValue
,
value
:
urlValue
};
self
.
updateAutoInterval
(
variable
);
return
self
.
setVariableValue
(
variable
,
option
);
});
}
var
option
=
_
.
findWhere
(
variable
.
options
,
{
text
:
urlValue
});
option
=
option
||
{
text
:
urlValue
,
value
:
urlValue
};
this
.
updateAutoInterval
(
variable
);
return
this
.
setVariableValue
(
variable
,
option
);
};
this
.
updateAutoInterval
=
function
(
variable
)
{
if
(
!
variable
.
auto
)
{
return
;
}
...
...
public/app/plugins/datasource/influxdb/influxSeries.js
View file @
b762f56a
...
...
@@ -5,7 +5,8 @@ function (_) {
'use strict'
;
function
InfluxSeries
(
options
)
{
this
.
seriesList
=
options
.
seriesList
;
this
.
seriesList
=
options
.
seriesList
&&
options
.
seriesList
.
results
&&
options
.
seriesList
.
results
.
length
>
0
?
options
.
seriesList
.
results
[
0
].
series
||
[]
:
[];
this
.
alias
=
options
.
alias
;
this
.
annotation
=
options
.
annotation
;
}
...
...
@@ -17,12 +18,10 @@ function (_) {
var
self
=
this
;
console
.
log
(
self
.
seriesList
);
if
(
!
self
.
seriesList
||
!
self
.
seriesList
.
results
||
!
self
.
seriesList
.
results
[
0
]
)
{
if
(
self
.
seriesList
.
length
===
0
)
{
return
output
;
}
this
.
seriesList
=
self
.
seriesList
.
results
[
0
].
series
;
_
.
each
(
self
.
seriesList
,
function
(
series
)
{
var
datapoints
=
[];
for
(
var
i
=
0
;
i
<
series
.
values
.
length
;
i
++
)
{
...
...
@@ -63,19 +62,15 @@ function (_) {
if
(
column
===
self
.
annotation
.
textColumn
)
{
textCol
=
index
;
return
;
}
});
_
.
each
(
series
.
points
,
function
(
point
)
{
_
.
each
(
series
.
values
,
function
(
value
)
{
var
data
=
{
annotation
:
self
.
annotation
,
time
:
point
[
timeCol
]
,
title
:
point
[
titleCol
],
tags
:
point
[
tagsCol
],
text
:
point
[
textCol
]
time
:
+
new
Date
(
value
[
timeCol
])
,
title
:
value
[
titleCol
],
tags
:
value
[
tagsCol
],
text
:
value
[
textCol
]
};
if
(
tagsCol
)
{
data
.
tags
=
point
[
tagsCol
];
}
list
.
push
(
data
);
});
});
...
...
public/app/plugins/datasource/influxdb/plugin.json
View file @
b762f56a
...
...
@@ -13,5 +13,6 @@
"annotations"
:
"app/plugins/datasource/influxdb/partials/annotations.editor.html"
},
"metrics"
:
true
"metrics"
:
true
,
"annotations"
:
true
}
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