Merge remote-tracking branch 'upstream/master' into prometheus-heatmap

This commit is contained in:
Alexander Zobnin
2018-03-12 17:16:02 +03:00
169 changed files with 3364 additions and 1521 deletions
+8 -8
View File
@@ -5,12 +5,12 @@ Read before posting:
- Checkout How to troubleshoot metric query issues: https://community.grafana.com/t/how-to-troubleshoot-metric-query-issues/50
Please include this information:
- What Grafana version are you using?
- What datasource are you using?
- What OS are you running grafana on?
- What did you do?
- What was the expected result?
- What happened instead?
- If related to metric query / data viz:
- Include raw network request & response: get by opening Chrome Dev Tools (F12, Ctrl+Shift+I on windows, Cmd+Opt+I on Mac), go the network tab.
### What Grafana version are you using?
### What datasource are you using?
### What OS are you running grafana on?
### What did you do?
### What was the expected result?
### What happened instead?
### If related to metric query / data viz:
### Include raw network request & response: get by opening Chrome Dev Tools (F12, Ctrl+Shift+I on windows, Cmd+Opt+I on Mac), go the network tab.
+24
View File
@@ -1,3 +1,27 @@
# 5.1.0 (unreleased)
* **Postgres/MySQL**: Ability to insert 0s or nulls for missing intervals [#9487](https://github.com/grafana/grafana/issues/9487), thanks [@svenklemm](https://github.com/svenklemm)
* **Graph**: Thresholds for Right Y axis [#7107](https://github.com/grafana/grafana/issues/7107), thx [@ilgizar](https://github.com/ilgizar)
* **Alerting**: Pausing/un alerts now updates new_state_date [#10942](https://github.com/grafana/grafana/pull/10942)
* **Templating**: Add comma templating format [#10632](https://github.com/grafana/grafana/issues/10632), thx [@mtanda](https://github.com/mtanda)
* **Prometheus**: Support POST for query and query_range [#9859](https://github.com/grafana/grafana/pull/9859), thx [@mtanda](https://github.com/mtanda)
### Minor
* **OpsGenie**: Add triggered alerts as description [#11046](https://github.com/grafana/grafana/pull/11046), thx [@llamashoes](https://github.com/llamashoes)
* **Cloudwatch**: Support high resolution metrics [#10925](https://github.com/grafana/grafana/pull/10925), thx [@mtanda](https://github.com/mtanda)
* **Cloudwatch**: Add dimension filtering to CloudWatch `dimension_values()` [#10029](https://github.com/grafana/grafana/issues/10029), thx [@willyhutw](https://github.com/willyhutw)
# 5.0.1 (2018-03-08)
* **Postgres**: PostgreSQL error when using ipv6 address as hostname in connection string [#11055](https://github.com/grafana/grafana/issues/11055), thanks [@svenklemm](https://github.com/svenklemm)
* **Dashboards**: Changing templated value from dropdown is causing unsaved changes [#11063](https://github.com/grafana/grafana/issues/11063)
* **Prometheus**: Fixes bundled Prometheus 2.0 dashboard [#11016](https://github.com/grafana/grafana/issues/11016), thx [@roidelapluie](https://github.com/roidelapluie)
* **Sidemenu**: Profile menu "invisible" when gravatar is disabled [#11097](https://github.com/grafana/grafana/issues/11097)
* **Dashboard**: Fixes a bug with resizeable handles for panels [#11103](https://github.com/grafana/grafana/issues/11103)
* **Alerting**: Telegram inline image mode fails when caption too long [#10975](https://github.com/grafana/grafana/issues/10975)
* **Alerting**: Fixes silent failing validation [#11145](https://github.com/grafana/grafana/pull/11145)
* **OAuth**: Only use jwt token if it contains an email address [#11127](https://github.com/grafana/grafana/pull/11127)
# 5.0.0-stable (2018-03-01)
### Fixes
+8 -2
View File
@@ -11,8 +11,14 @@ deps: deps-js
build-go:
go run build.go build
build-server:
go run build.go build-server
build-cli:
go run build.go build-cli
build-js:
npm run build
yarn run build
build: build-go build-js
@@ -20,7 +26,7 @@ test-go:
go test -v ./pkg/...
test-js:
npm test
yarn test
test: test-go test-js
+1 -1
View File
@@ -33,7 +33,7 @@ the latest master builds [here](https://grafana.com/grafana/download)
### Building the backend
```bash
go get github.com/grafana/grafana
cd ~/go/src/github.com/grafana/grafana
cd $GOPATH/src/github.com/grafana/grafana
go run build.go setup
go run build.go build
```
-1
View File
@@ -8,7 +8,6 @@ But it will give you an idea of our current vision and plan.
- v5.1
- Crossplatform builds & build speed improvements
- Enterprise LDAP
- New template interpolation syntax
- Provisioning workflow
- First login registration view
- IFQL Initial support
+4
View File
@@ -83,6 +83,10 @@ func main() {
clean()
build("grafana-cli", "./pkg/cmd/grafana-cli", []string{})
case "build-server":
clean()
build("grafana-server", "./pkg/cmd/grafana-server", []string{})
case "build":
clean()
for _, binary := range binaries {
+132 -54
View File
@@ -1,57 +1,135 @@
machine:
node:
version: 6.11.4
python:
version: 2.7.3
services:
- docker
environment:
GOPATH: "/home/ubuntu/.go_workspace"
ORG_PATH: "github.com/grafana"
REPO_PATH: "${ORG_PATH}/grafana"
GODIST: "go1.9.3.linux-amd64.tar.gz"
post:
- mkdir -p ~/download
- mkdir -p ~/docker
- test -e download/$GODIST || curl -o download/$GODIST https://storage.googleapis.com/golang/$GODIST
- sudo rm -rf /usr/local/go
- sudo tar -C /usr/local -xzf download/$GODIST
version: 2
dependencies:
cache_directories:
- "~/docker"
- "~/download"
override:
- rm -rf ${GOPATH}/src/${REPO_PATH}
- mkdir -p ${GOPATH}/src/${ORG_PATH}
- cp -r ~/grafana ${GOPATH}/src/${ORG_PATH}
pre:
- pip install awscli
- sudo apt-get update; sudo apt-get install rpm; sudo apt-get install expect
- ./scripts/build/build_container.sh
jobs:
test-frontend:
docker:
- image: circleci/node:6.11.4
steps:
- checkout
- run:
name: install yarn
command: 'sudo npm install -g yarn --quiet'
- restore_cache:
key: dependency-cache-{{ checksum "yarn.lock" }}
# Could we skip this step if the cache has been restored? `[ -d node_modules ] || yarn install ...` should be able to apply to build step as well
- run:
name: yarn install
command: 'yarn install --pure-lockfile --no-progress'
- save_cache:
key: dependency-cache-{{ checksum "yarn.lock" }}
paths:
- node_modules
- run:
name: frontend tests
command: './scripts/circle-test-frontend.sh'
test:
override:
- bash scripts/circle-test-frontend.sh
- bash scripts/circle-test-backend.sh
test-backend:
docker:
- image: circleci/golang:1.10
working_directory: /go/src/github.com/grafana/grafana
steps:
- checkout
- run:
name: build backend and run go tests
command: './scripts/circle-test-backend.sh'
deployment:
gh_branch:
branch: master
commands:
- ./scripts/build/deploy.sh
- ./scripts/build/sign_packages.sh
- go run build.go sha-dist
- aws s3 sync ./dist s3://$BUCKET_NAME/master
- ./scripts/trigger_windows_build.sh ${APPVEYOR_TOKEN} ${CIRCLE_SHA1} master
- ./scripts/trigger_docker_build.sh ${TRIGGER_GRAFANA_PACKER_CIRCLECI_TOKEN}
- go run ./scripts/build/publish.go -apiKey ${GRAFANA_COM_API_KEY}
gh_tag:
tag: /^v[0-9]+(\.[0-9]+){2}(-.+|[^-.]*)$/
commands:
- ./scripts/build/deploy.sh
- ./scripts/build/sign_packages.sh
- go run build.go sha-dist
- aws s3 sync ./dist s3://$BUCKET_NAME/release
- ./scripts/trigger_windows_build.sh ${APPVEYOR_TOKEN} ${CIRCLE_SHA1} release
- ./scripts/trigger_docker_build.sh ${TRIGGER_GRAFANA_PACKER_CIRCLECI_TOKEN} ${CIRCLE_TAG}
build:
docker:
- image: grafana/build-container:v0.1
working_directory: /go/src/github.com/grafana/grafana
steps:
- checkout
- run:
name: build and package grafana
command: './scripts/build/build.sh'
- run:
name: sign packages
command: './scripts/build/sign_packages.sh'
- run:
name: sha-sum packages
command: 'go run build.go sha-dist'
- run:
name: Build Grafana.com publisher
command: 'go build -o scripts/publish scripts/build/publish.go'
- persist_to_workspace:
root: .
paths:
- dist/grafana*
- scripts/*.sh
- scripts/publish
deploy-master:
docker:
- image: circleci/python:2.7-stretch
steps:
- attach_workspace:
at: .
- run:
name: install awscli
command: 'sudo pip install awscli'
- run:
name: deploy to s3
command: 'aws s3 sync ./dist s3://$BUCKET_NAME/master'
- run:
name: Trigger Windows build
command: './scripts/trigger_windows_build.sh ${APPVEYOR_TOKEN} ${CIRCLE_SHA1} master'
- run:
name: Trigger Docker build
command: './scripts/trigger_docker_build.sh ${TRIGGER_GRAFANA_PACKER_CIRCLECI_TOKEN}'
- run:
name: Publish to Grafana.com
command: './scripts/publish -apiKey ${GRAFANA_COM_API_KEY}'
deploy-release:
docker:
- image: circleci/python:2.7-stretch
steps:
- attach_workspace:
at: dist
- run:
name: install awscli
command: 'sudo pip install awscli'
- run:
name: deploy to s3
command: 'aws s3 sync ./dist s3://$BUCKET_NAME/release'
- run:
name: Trigger Windows build
command: './scripts/trigger_windows_build.sh ${APPVEYOR_TOKEN} ${CIRCLE_SHA1} release'
- run:
name: Trigger Docker build
command: './scripts/trigger_docker_build.sh ${TRIGGER_GRAFANA_PACKER_CIRCLECI_TOKEN} ${CIRCLE_TAG}'
workflows:
version: 2
test-and-build:
jobs:
- build:
filters:
tags:
only: /.*/
- test-frontend:
filters:
tags:
only: /.*/
- test-backend:
filters:
tags:
only: /.*/
- deploy-master:
requires:
- test-backend
- test-frontend
- build
filters:
branches:
only: master
- deploy-release:
requires:
- test-backend
- test-frontend
- build
filters:
branches:
ignore: /.*/
tags:
only: /^v[0-9]+(\.[0-9]+){2}(-.+|[^-.]*)$/
+1 -1
View File
@@ -1,3 +1,3 @@
FROM prom/prometheus:v2.0.0
FROM prom/prometheus:v2.2.0
ADD prometheus.yml /etc/prometheus/
ADD alert.rules /etc/prometheus/
+1 -1
View File
@@ -1 +1 @@
v4.3
v5.0
+39 -6
View File
@@ -65,13 +65,46 @@ Permission levels:
- **Admin**: Can edit & create dashboards and edit permissions.
- **Edit**: Can edit & create dashboards. **Cannot** edit folder/dashboard permissions.
- **View**: Can only view existing dashboards/folders.
#### Restricting Access
The highest permission always wins so if you for example want to hide a folder or dashboard from others you need to remove the **Organization Role** based permission from the Access Control List (ACL).
- You cannot override permissions for users with the **Org Admin Role**. Admins always have access to everything.
- A more specific permission with a lower permission level will not have any effect if a more general rule exists with higher permission level. You need to remove or lower the permission level of the more general rule.
#### How Grafana Resolves Multiple Permissions - Examples
##### Example 1 (`user1` has the Editor Role)
Permissions for a dashboard:
- `Everyone with Editor Role Can Edit`
- `user1 Can View`
Result: `user1` has Edit permission as the highest permission always wins.
##### Example 2 (`user1` has the Viewer Role and is a member of `team1`)
Permissions for a dashboard:
- `Everyone with Viewer Role Can View`
- `user1 Can Edit`
- `team1 Can Admin`
Result: `user1` has Admin permission as the highest permission always wins.
##### Example 3
Permissions for a dashboard:
- `user1 Can Admin (inherited from parent folder)`
- `user1 Can Edit`
Result: You cannot override to a lower permission. `user1` has Admin permission as the highest permission always wins.
- **View**: Can only view existing dashboars/folders.
#### Restricting access
The highest permission always wins so if you for example want to hide a folder or dashboard from others you need to remove the **Organization Role** based permission from the
Access Control List (ACL).
- You cannot override permissions for users with **Org Admin Role**
- A more specific permission with lower permission level will not have any effect if a more general rule exists with higher permission level. For example if "Everyone with Editor Role Can Edit" exists in the ACL list then **John Doe** will still have Edit permission even after you have specifically added a permission for this user with the permission set to **View**. You need to remove or lower the permission level of the more general rule.
+8 -1
View File
@@ -144,7 +144,7 @@ Since not all datasources have the same configuration settings we only have the
| tlsSkipVerify | boolean | *All* | Controls whether a client verifies the server's certificate chain and host name. |
| graphiteVersion | string | Graphite | Graphite version |
| timeInterval | string | Elastic, Influxdb & Prometheus | Lowest interval/step value that should be used for this data source |
| esVersion | string | Elastic | Elasticsearch version |
| esVersion | string | Elastic | Elasticsearch version as an number (2/5/56) |
| timeField | string | Elastic | Which field that should be used as timestamp |
| interval | string | Elastic | Index date time format |
| authType | string | Cloudwatch | Auth provider. keys/credentials/arn |
@@ -169,6 +169,8 @@ Secure json data is a map of settings that will be encrypted with [secret key](/
| tlsClientKey | string | *All* |TLS Client key for outgoing requests |
| password | string | Postgre | password |
| user | string | Postgre | user |
| accessKey | string | Cloudwatch | Access key for connecting to Cloudwatch |
| secretKey | string | Cloudwatch | Secret key for connecting to Cloudwatch |
### Dashboards
@@ -190,6 +192,11 @@ providers:
path: /var/lib/grafana/dashboards
```
When Grafana starts, it will update/insert all dashboards available in the configured path. Then later on poll that path and look for updated json files and insert those update/insert those into the database.
### Reuseable dashboard urls
If the dashboard in the json file contains an [uid](/reference/dashboard/#json-fields), Grafana will force insert/update on that uid. This allows you to migrate dashboards betweens Grafana instances and provisioning Grafana from configuration without breaking the urls given since the new dashboard url uses the uid as identifer.
When Grafana starts, it will update/insert all dashboards available in the configured folders. If you modify the file, the dashboard will also be updated.
By default Grafana will delete dashboards in the database if the file is removed. You can disable this behavior using the `disableDeletion` setting.
+1 -1
View File
@@ -20,7 +20,7 @@ to add and configure a `notification` channel (can be email, PagerDuty or other
## Notification Channel Setup
{{< imgbox max-width="40%" img="/img/docs/v43/alert_notifications_menu.png" caption="Alerting Notification Channels" >}}
{{< imgbox max-width="30%" img="/img/docs/v50/alerts_notifications_menu.png" caption="Alerting Notification Channels" >}}
On the Notification Channels page hit the `New Channel` button to go the page where you
can configure and setup a new Notification Channel.
+1 -1
View File
@@ -59,7 +59,7 @@ avg() OF query(A, 5m, now) IS BELOW 14
```
- `avg()` Controls how the values for **each** series should be reduced to a value that can be compared against the threshold. Click on the function to change it to another aggregation function.
- `query(A, 5m, now)` The letter defines what query to execute from the **Metrics** tab. The second two parameters define the time range, `5m, now` means 5 minutes from now to now. You can also do `10m, now-2m` to define a time range that will be 10 minutes from now to 2 minutes from now. This is useful if you want to ignore the last 2 minutes of data.
- `query(A, 5m, now)` The letter defines what query to execute from the **Metrics** tab. The second two parameters define the time range, `5m, now` means 5 minutes ago to now. You can also do `10m, now-2m` to define a time range that will be 10 minutes ago to 2 minutes ago. This is useful if you want to ignore the last 2 minutes of data.
- `IS BELOW 14` Defines the type of threshold and the threshold value. You can click on `IS BELOW` to change the type of threshold.
The query used in an alert rule cannot contain any template variables. Currently we only support `AND` and `OR` operators between conditions and they are executed serially.
@@ -87,7 +87,7 @@ Name | Description
*namespaces()* | Returns a list of namespaces CloudWatch support.
*metrics(namespace, [region])* | Returns a list of metrics in the namespace. (specify region or use "default" for custom metrics)
*dimension_keys(namespace)* | Returns a list of dimension keys in the namespace.
*dimension_values(region, namespace, metric, dimension_key)* | Returns a list of dimension values matching the specified `region`, `namespace`, `metric` and `dimension_key`.
*dimension_values(region, namespace, metric, dimension_key, [filters])* | Returns a list of dimension values matching the specified `region`, `namespace`, `metric`, `dimension_key` or you can use dimension `filters` to get more specific result as well.
*ebs_volume_ids(region, instance_id)* | Returns a list of volume ids matching the specified `region`, `instance_id`.
*ec2_instance_attribute(region, attribute_name, filters)* | Returns a list of attributes matching the specified `region`, `attribute_name`, `filters`.
@@ -104,6 +104,7 @@ Query | Service
*dimension_values(us-east-1,AWS/Redshift,CPUUtilization,ClusterIdentifier)* | RedShift
*dimension_values(us-east-1,AWS/RDS,CPUUtilization,DBInstanceIdentifier)* | RDS
*dimension_values(us-east-1,AWS/S3,BucketSizeBytes,BucketName)* | S3
*dimension_values(us-east-1,CWAgent,disk_used_percent,device,{"InstanceId":"$instance_id"})* | CloudWatch Agent
## ec2_instance_attribute examples
+4 -5
View File
@@ -8,7 +8,7 @@ weight = 7
# Keyboard shortcuts
{{< docs-imagebox img="/img/docs/v4/shortcuts.png" max-width="20rem" class="docs-image--right" >}}
{{< docs-imagebox img="/img/docs/v50/shortcuts.png" max-width="20rem" class="docs-image--right" >}}
Grafana v4 introduces a number of really powerful keyboard shortcuts. You can now focus a panel
by hovering over it with your mouse. With a panel focused you can simple hit `e` to toggle panel
@@ -34,6 +34,8 @@ Hit `?` on your keyboard to open the shortcuts help modal.
- `d` `s` Dashboard settings
- `d` `v` Toggle in-active / view mode
- `d` `k` Toggle kiosk mode (hides top nav)
- `d` `E` Expand all rows
- `d` `C` Collapse all rows
- `mod+o` Toggle shared graph crosshair
### Focused Panel
@@ -42,12 +44,9 @@ Hit `?` on your keyboard to open the shortcuts help modal.
- `p` `s` Open Panel Share Modal
- `p` `r` Remove Panel
### Focused Row
- `r` `c` Collapse Row
- `r` `r` Remove Row
### Time Range
- `t` `z` Zoom out time range
- `t` Move time range back
- `t` Move time range forward
mod = CTRL on windows or linux and CMD key on Mac
+14 -14
View File
@@ -27,36 +27,36 @@ Read the [Basic Concepts](/guides/basic_concepts) document to get a crash course
Let's start with creating a new Dashboard. You can find the new Dashboard link on the right side of the Dashboard picker. You now have a blank Dashboard.
<img class="no-shadow" src="/img/docs/v45/top_nav_annotated.png">
<img class="no-shadow" src="/img/docs/v50/top_nav_annotated.png" width="580px">
The image above shows you the top header for a Dashboard.
1. Side menubar toggle: This toggles the side menu, allowing you to focus on the data presented in the dashboard. The side menu provides access to features unrelated to a Dashboard such as Users, Organizations, and Data Sources.
2. Dashboard dropdown: This dropdown shows you which Dashboard you are currently viewing, and allows you to easily switch to a new Dashboard. From here you can also create a new Dashboard, Import existing Dashboards, and manage Dashboard playlists.
3. Star Dashboard: Star (or unstar) the current Dashboard. Starred Dashboards will show up on your own Home Dashboard by default, and are a convenient way to mark Dashboards that you're interested in.
4. Share Dashboard: Share the current dashboard by creating a link or create a static Snapshot of it. Make sure the Dashboard is saved before sharing.
5. Save dashboard: The current Dashboard will be saved with the current Dashboard name.
6. Settings: Manage Dashboard settings and features such as Templating and Annotations.
2. Dashboard dropdown: This dropdown shows you which Dashboard you are currently viewing, and allows you to easily switch to a new Dashboard. From here you can also create a new Dashboard or folder, Import existing Dashboards, and manage Dashboard playlists.
3. Add Panel: Adds a new panel to the current Dashboard
4. Star Dashboard: Star (or unstar) the current Dashboard. Starred Dashboards will show up on your own Home Dashboard by default, and are a convenient way to mark Dashboards that you're interested in.
5. Share Dashboard: Share the current dashboard by creating a link or create a static Snapshot of it. Make sure the Dashboard is saved before sharing.
6. Save dashboard: The current Dashboard will be saved with the current Dashboard name.
7. Settings: Manage Dashboard settings and features such as Templating and Annotations.
## Dashboards, Panels, Rows, the building blocks of Grafana...
## Dashboards, Panels, the building blocks of Grafana...
Dashboards are at the core of what Grafana is all about. Dashboards are composed of individual Panels arranged on a number of Rows. Grafana ships with a variety of Panels. Grafana makes it easy to construct the right queries, and customize the display properties so that you can create the perfect Dashboard for your need. Each Panel can interact with data from any configured Grafana Data Source (currently InfluxDB, Graphite, OpenTSDB, Prometheus and Cloudwatch). The [Basic Concepts](/guides/basic_concepts) guide explores these key ideas in detail.
Dashboards are at the core of what Grafana is all about. Dashboards are composed of individual Panels arranged on a grid. Grafana ships with a variety of Panels. Grafana makes it easy to construct the right queries, and customize the display properties so that you can create the perfect Dashboard for your need. Each Panel can interact with data from any configured Grafana Data Source (currently InfluxDB, Graphite, OpenTSDB, Prometheus and Cloudwatch). The [Basic Concepts](/guides/basic_concepts) guide explores these key ideas in detail.
<img src="/img/docs/v45/dashboard_annotated.png" class="no-shadow">
<img src="/img/docs/v50/dashboard_annotated.png" class="no-shadow" width="700px">
1. Zoom out time range
2. Time picker dropdown. Here you can access relative time range options, auto refresh options and set custom absolute time ranges.
3. Manual refresh button. Will cause all panels to refresh (fetch new data).
4. Row controls menu. Via this menu you can add panels to the row, set row height and more.
5. Dashboard panel. You edit panels by clicking the panel title.
6. Graph legend. You can change series colors, y-axis and series visibility directly from the legend.
4. Dashboard panel. You edit panels by clicking the panel title.
5. Graph legend. You can change series colors, y-axis and series visibility directly from the legend.
## Adding & Editing Graphs and Panels
![](/img/docs/v45/metrics_tab.png)
1. You add panels via row menu. The row menu is the icon to the left of each row.
1. You add panels by clicking the Add panel icon on the top menu.
2. To edit the graph you click on the graph title to open the panel menu, then `Edit`.
3. This should take you to the `Metrics` tab. In this tab you should see the editor for your default data source.
@@ -64,7 +64,7 @@ When you click the `Metrics` tab, you are presented with a Query Editor that is
## Drag-and-Drop panels
You can Drag-and-Drop Panels within and between Rows. Click and hold the Panel title, and drag it to its new location. You can also easily resize panels by clicking the (-) and (+) icons.
You can Drag-and-Drop Panels by simply clicking and holding the Panel title, and drag it to its new location. You can also easily resize panels by clicking the (-) and (+) icons.
![](/img/docs/animated_gifs/drag_drop.gif)
-5
View File
@@ -3,11 +3,6 @@ title = "What's New in Grafana v2.1"
description = "Feature & improvement highlights for Grafana v2.1"
keywords = ["grafana", "new", "documentation", "2.1"]
type = "docs"
[menu.docs]
name = "Version 2.1"
identifier = "v2.1"
parent = "whatsnew"
weight = 10
+++
# What's new in Grafana v2.1
-5
View File
@@ -3,11 +3,6 @@ title = "What's New in Grafana v2.5"
description = "Feature & improvement highlights for Grafana v2.5"
keywords = ["grafana", "new", "documentation", "2.5"]
type = "docs"
[menu.docs]
name = "Version 2.5"
identifier = "v2.5"
parent = "whatsnew"
weight = 9
+++
# What's new in Grafana v2.5
-5
View File
@@ -3,11 +3,6 @@ title = "What's New in Grafana v2.6"
description = "Feature & improvement highlights for Grafana v2.6"
keywords = ["grafana", "new", "documentation", "2.6"]
type = "docs"
[menu.docs]
name = "Version 2.6"
identifier = "v2.6"
parent = "whatsnew"
weight = 7
+++
# What's new in Grafana v2.6
-5
View File
@@ -3,11 +3,6 @@ title = "What's New in Grafana v2.0"
description = "Feature & improvement highlights for Grafana v2.0"
keywords = ["grafana", "new", "documentation", "2.0"]
type = "docs"
[menu.docs]
name = "Version 2.0"
identifier = "v2.0"
parent = "whatsnew"
weight = 11
+++
# What's New in Grafana v2.0
-2
View File
@@ -12,8 +12,6 @@ weight = -6
# What's New in Grafana v5.0
> Out in beta: [Download now!](https://grafana.com/grafana/download/beta)
This is the most substantial update that Grafana has ever seen. This article will detail the major new features and enhancements.
- [New Dashboard Layout Engine]({{< relref "#new-dashboard-layout-engine" >}}) enables a much easier drag, drop and resize experience and new types of layouts.
+154 -62
View File
@@ -11,6 +11,17 @@ parent = "http_api"
# Dashboard API
## Identifier (id) vs unique identifier (uid)
The identifier (id) of a dashboard is an auto-incrementing numeric value and is only unique per Grafana install.
The unique identifier (uid) of a dashboard can be used for uniquely identify a dashboard between multiple Grafana installs.
It's automatically generated if not provided when creating a dashboard. The uid allows having consistent URL's for accessing
dashboards and when syncing dashboards between multiple Grafana installs, see [dashboard provisioning](/administration/provisioning/#dashboards)
for more information. This means that changing the title of a dashboard will not break any bookmarked links to that dashboard.
The uid can have a maximum length of 40 characters.
## Create / Update dashboard
`POST /api/dashboards/db`
@@ -28,24 +39,25 @@ Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
{
"dashboard": {
"id": null,
"uid": null,
"title": "Production Overview",
"tags": [ "templated" ],
"timezone": "browser",
"rows": [
{
}
],
"schemaVersion": 6,
"schemaVersion": 16,
"version": 0
},
"folderId": 0,
"overwrite": false
}
```
JSON Body schema:
- **dashboard** The complete dashboard model, id = null to create a new dashboard
- **overwrite** Set to true if you want to overwrite existing dashboard with newer version or with same dashboard title.
- **dashboard** The complete dashboard model, id = null to create a new dashboard.
- **dashboard.id** id = null to create a new dashboard.
- **dashboard.uid** Optional [unique identifier](/http_api/dashboard/#identifier-id-vs-unique-identifier-uid) when creating a dashboard. uid = null will generate a new uid.
- **folderId** The id of the folder to save the dashboard in.
- **overwrite** Set to true if you want to overwrite existing dashboard with newer version, same dashboard title in folder or same dashboard uid.
- **message** - Set a commit message for the version history.
**Example Response**:
@@ -56,9 +68,12 @@ Content-Type: application/json; charset=UTF-8
Content-Length: 78
{
"slug": "production-overview",
"id": 1,
"uid": "cIBgcSjkk",
"url": "/d/cIBgcSjkk/production-overview",
"status": "success",
"version": 1
"version": 1,
"slug": "production-overview" //deprecated in Grafana v5.0
}
```
@@ -67,10 +82,18 @@ Status Codes:
- **200** Created
- **400** Errors (invalid json, missing or invalid fields, etc)
- **401** Unauthorized
- **403** Access denied
- **412** Precondition failed
The **412** status code is used when a newer dashboard already exists (newer, its version is greater than the version that was sent). The
same status code is also used if another dashboard exists with the same title. The response body will look like this:
The **412** status code is used for explaing that you cannot create the dashboard and why.
There can be different reasons for this:
- The dashboard has been changed by someone else, `status=version-mismatch`
- A dashboard with the same name in the folder already exists, `status=name-exists`
- A dashboard with the same uid already exists, `status=name-exists`
- The dashboard belongs to plugin `<plugin title>`, `status=plugin-dashboard`
The response body will have the following properties:
```http
HTTP/1.1 412 Precondition Failed
@@ -85,16 +108,16 @@ Content-Length: 97
In case of title already exists the `status` property will be `name-exists`.
## Get dashboard
## Get dashboard by uid
`GET /api/dashboards/db/:slug`
`GET /api/dashboards/uid/:uid`
Will return the dashboard given the dashboard slug. Slug is the url friendly version of the dashboard title.
Will return the dashboard given the dashboard unique identifier (uid).
**Example Request**:
```http
GET /api/dashboards/db/production-overview HTTP/1.1
GET /api/dashboards/uid/cIBgcSjkk HTTP/1.1
Accept: application/json
Content-Type: application/json
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
@@ -107,35 +130,40 @@ HTTP/1.1 200
Content-Type: application/json
{
"meta": {
"isStarred": false,
"slug": "production-overview"
},
"dashboard": {
"id": null,
"id": 1,
"uid": "cIBgcSjkk",
"title": "Production Overview",
"tags": [ "templated" ],
"timezone": "browser",
"rows": [
{
}
],
"schemaVersion": 6,
"schemaVersion": 16,
"version": 0
},
"meta": {
"isStarred": false,
"url": "/d/cIBgcSjkk/production-overview",
"slug": "production-overview" //deprecated in Grafana v5.0
}
}
```
## Delete dashboard
Status Codes:
`DELETE /api/dashboards/db/:slug`
- **200** Found
- **401** Unauthorized
- **403** Access denied
- **404** Not found
The above will delete the dashboard with the specified slug. The slug is the url friendly (unique) version of the dashboard title.
## Delete dashboard by uid
`DELETE /api/dashboards/uid/:uid`
Will delete the dashboard given the specified unique identifier (uid).
**Example Request**:
```http
DELETE /api/dashboards/db/test HTTP/1.1
DELETE /api/dashboards/uid/cIBgcSjkk HTTP/1.1
Accept: application/json
Content-Type: application/json
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
@@ -147,9 +175,16 @@ Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
HTTP/1.1 200
Content-Type: application/json
{"title": "Test"}
{"title": "Production Overview"}
```
Status Codes:
- **200** Deleted
- **401** Unauthorized
- **403** Access denied
- **404** Not found
## Gets the home dashboard
`GET /api/dashboards/home`
@@ -172,15 +207,6 @@ HTTP/1.1 200
Content-Type: application/json
{
"meta": {
"isHome":true,
"canSave":false,
"canEdit":false,
"canStar":false,
"slug":"",
"expires":"0001-01-01T00:00:00Z",
"created":"0001-01-01T00:00:00Z"
},
"dashboard": {
"editable":false,
"hideControls":true,
@@ -190,11 +216,6 @@ Content-Type: application/json
"type":"timepicker"
}
],
"rows": [
{
}
],
"style":"dark",
"tags":[],
"templating":{
@@ -206,13 +227,21 @@ Content-Type: application/json
"timezone":"browser",
"title":"Home",
"version":5
},
"meta": {
"isHome":true,
"canSave":false,
"canEdit":false,
"canStar":false,
"url":"",
"expires":"0001-01-01T00:00:00Z",
"created":"0001-01-01T00:00:00Z"
}
}
```
## Tags for Dashboard
`GET /api/dashboards/tags`
Get all tags of dashboards
@@ -244,21 +273,24 @@ Content-Type: application/json
]
```
## Search Dashboards
## Dashboard Search
See [Folder/Dashboard Search API](/http_api/folder_dashboard_search).
`GET /api/search/`
## Deprecated resources
Please note that these resource have been deprecated and will be removed in a future release.
Query parameters:
### Get dashboard by slug
**Deprecated starting from Grafana v5.0. Please update to use the new *Get dashboard by uid* resource instead**
- **query** Search Query
- **tag** Tag to use
- **starred** Flag indicating if only starred Dashboards should be returned
- **tagcloud** - Flag indicating if a tagcloud should be returned
`GET /api/dashboards/db/:slug`
Will return the dashboard given the dashboard slug. Slug is the url friendly version of the dashboard title.
If there exists multiple dashboards with the same slug, one of them will be returned in the response.
**Example Request**:
```http
GET /api/search?query=Production%20Overview&starred=true&tag=prod HTTP/1.1
GET /api/dashboards/db/production-overview HTTP/1.1
Accept: application/json
Content-Type: application/json
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
@@ -270,14 +302,74 @@ Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
HTTP/1.1 200
Content-Type: application/json
[
{
"id":1,
"title":"Production Overview",
"uri":"db/production-overview",
"type":"dash-db",
"tags":[prod],
"isStarred":true
{
"dashboard": {
"id": 1,
"uid": "cIBgcSjkk",
"title": "Production Overview",
"tags": [ "templated" ],
"timezone": "browser",
"schemaVersion": 16,
"version": 0
},
"meta": {
"isStarred": false,
"url": "/d/cIBgcSjkk/production-overview",
"slug": "production-overview" // deprecated in Grafana v5.0
}
]
}
```
Status Codes:
- **200** Found
- **401** Unauthorized
- **403** Access denied
- **404** Not found
### Delete dashboard by slug
**Deprecated starting from Grafana v5.0. Please update to use the *Delete dashboard by uid* resource instead.**
`DELETE /api/dashboards/db/:slug`
Will delete the dashboard given the specified slug. Slug is the url friendly version of the dashboard title.
**Example Request**:
```http
DELETE /api/dashboards/db/test HTTP/1.1
Accept: application/json
Content-Type: application/json
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
```
**Example Response**:
```http
HTTP/1.1 200
Content-Type: application/json
{"title": "Production Overview"}
```
Status Codes:
- **200** Deleted
- **401** Unauthorized
- **403** Access denied
- **404** Not found
- **412** Precondition failed
The **412** status code is used when there exists multiple dashboards with the same slug.
The response body will look like this:
```http
HTTP/1.1 412 Precondition Failed
Content-Type: application/json; charset=UTF-8
Content-Length: 97
{
"message": "Multiple dashboards with the same slug exists",
"status": "multiple-slugs-exists"
}
```
@@ -0,0 +1,149 @@
+++
title = "Dashboard Permissions HTTP API "
description = "Grafana Dashboard Permissions HTTP API"
keywords = ["grafana", "http", "documentation", "api", "dashboard", "permission", "permissions", "acl"]
aliases = ["/http_api/dashboardpermissions/"]
type = "docs"
[menu.docs]
name = "Dashboard Permissions"
parent = "http_api"
+++
# Dashboard Permissions API
This API can be used to update/get the permissions for a dashboard.
Permissions with `dashboardId=-1` are the default permissions for users with the Viewer and Editor roles. Permissions can be set for a user, a team or a role (Viewer or Editor). Permissions cannot be set for Admins - they always have access to everything.
The permission levels for the permission field:
- 1 = View
- 2 = Edit
- 4 = Admin
## Get permissions for a dashboard
`GET /api/dashboards/id/:dashboardId/permissions`
Gets all existing permissions for the dashboard with the given `dashboardId`.
**Example request**:
```http
GET /api/dashboards/id/1/permissions HTTP/1.1
Accept: application/json
Content-Type: application/json
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
```
**Example Response**
```http
HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
Content-Length: 551
[
{
"id": 1,
"dashboardId": -1,
"created": "2017-06-20T02:00:00+02:00",
"updated": "2017-06-20T02:00:00+02:00",
"userId": 0,
"userLogin": "",
"userEmail": "",
"teamId": 0,
"team": "",
"role": "Viewer",
"permission": 1,
"permissionName": "View",
"uid": "",
"title": "",
"slug": "",
"isFolder": false,
"url": ""
},
{
"id": 2,
"dashboardId": -1,
"created": "2017-06-20T02:00:00+02:00",
"updated": "2017-06-20T02:00:00+02:00",
"userId": 0,
"userLogin": "",
"userEmail": "",
"teamId": 0,
"team": "",
"role": "Editor",
"permission": 2,
"permissionName": "Edit",
"uid": "",
"title": "",
"slug": "",
"isFolder": false,
"url": ""
}
]
```
Status Codes:
- **200** - Ok
- **401** - Unauthorized
- **403** - Access denied
- **404** - Dashboard not found
## Update permissions for a dashboard
`POST /api/dashboards/id/:dashboardId/permissions`
Updates permissions for a dashboard. This operation will remove existing permissions if they're not included in the request.
**Example request**:
```http
POST /api/dashboards/id/1/permissions
Accept: application/json
Content-Type: application/json
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
"items": [
{
"role": "Viewer",
"permission": 1
},
{
"role": "Editor",
"permission": 2
},
{
"teamId": 1,
"permission": 1
},
{
"userId": 11,
"permission": 4
}
]
}
```
JSON body schema:
- **items** - The permission items to add/update. Items that are omitted from the list will be removed.
**Example response**:
```http
HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
Content-Length: 35
{"message":"Dashboard permissions updated"}
```
Status Codes:
- **200** - Ok
- **401** - Unauthorized
- **403** - Access denied
- **404** - Dashboard not found
+317
View File
@@ -0,0 +1,317 @@
+++
title = "Folder HTTP API "
description = "Grafana Folder HTTP API"
keywords = ["grafana", "http", "documentation", "api", "folder"]
aliases = ["/http_api/folder/"]
type = "docs"
[menu.docs]
name = "Folder"
parent = "http_api"
+++
# Folder API
## Identifier (id) vs unique identifier (uid)
The identifier (id) of a folder is an auto-incrementing numeric value and is only unique per Grafana install.
The unique identifier (uid) of a folder can be used for uniquely identify folders between multiple Grafana installs. It's automatically generated if not provided when creating a folder. The uid allows having consistent URL's for accessing folders and when syncing folders between multiple Grafana installs. This means that changing the title of a folder will not break any bookmarked links to that folder.
The uid can have a maximum length of 40 characters.
## Get all folders
`GET /api/folders`
Returns all folders that the authenticated user has permission to view.
**Example Request**:
```http
GET /api/folders?limit=10 HTTP/1.1
Accept: application/json
Content-Type: application/json
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
```
**Example Response**:
```http
HTTP/1.1 200
Content-Type: application/json
[
{
"id":1,
"uid": "nErXDvCkzz",
"title": "Departmenet ABC",
"url": "/dashboards/f/nErXDvCkzz/department-abc",
"hasAcl": false,
"canSave": true,
"canEdit": true,
"canAdmin": true,
"createdBy": "admin",
"created": "2018-01-31T17:43:12+01:00",
"updatedBy": "admin",
"updated": "2018-01-31T17:43:12+01:00",
"version": 1
}
]
```
## Get folder by uid
`GET /api/folders/:uid`
Will return the folder given the folder uid.
**Example Request**:
```http
GET /api/folders/nErXDvCkzzh HTTP/1.1
Accept: application/json
Content-Type: application/json
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
```
**Example Response**:
```http
HTTP/1.1 200
Content-Type: application/json
{
"id":1,
"uid": "nErXDvCkzz",
"title": "Departmenet ABC",
"url": "/dashboards/f/nErXDvCkzz/department-abc",
"hasAcl": false,
"canSave": true,
"canEdit": true,
"canAdmin": true,
"createdBy": "admin",
"created": "2018-01-31T17:43:12+01:00",
"updatedBy": "admin",
"updated": "2018-01-31T17:43:12+01:00",
"version": 1
}
```
Status Codes:
- **200** Found
- **401** Unauthorized
- **403** Access Denied
- **404** Folder not found
## Create folder
`POST /api/folders`
Creates a new folder.
**Example Request**:
```http
POST /api/folders HTTP/1.1
Accept: application/json
Content-Type: application/json
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
{
"uid": "nErXDvCkzz",
"title": "Department ABC"
}
```
JSON Body schema:
- **uid** Optional [unique identifier](/http_api/folder/#identifier-id-vs-unique-identifier-uid).
- **title** The title of the folder.
**Example Response**:
```http
HTTP/1.1 200
Content-Type: application/json
{
"id":1,
"uid": "nErXDvCkzz",
"title": "Departmenet ABC",
"url": "/dashboards/f/nErXDvCkzz/department-abc",
"hasAcl": false,
"canSave": true,
"canEdit": true,
"canAdmin": true,
"createdBy": "admin",
"created": "2018-01-31T17:43:12+01:00",
"updatedBy": "admin",
"updated": "2018-01-31T17:43:12+01:00",
"version": 1
}
```
Status Codes:
- **200** Created
- **400** Errors (invalid json, missing or invalid fields, etc)
- **401** Unauthorized
- **403** Access Denied
## Update folder
`PUT /api/folders/:uid`
Updates an existing folder identified by uid.
**Example Request**:
```http
PUT /api/folders/nErXDvCkzz HTTP/1.1
Accept: application/json
Content-Type: application/json
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
{
"title":"Department DEF",
"version": 1
}
```
JSON Body schema:
- **uid** Provide another [unique identifier](/http_api/folder/#identifier-id-vs-unique-identifier-uid) than stored to change the unique identifier.
- **title** The title of the folder.
- **version** Provide the current version to be able to update the folder. Not needed if `overwrite=true`.
- **overwrite** Set to true if you want to overwrite existing folder with newer version.
**Example Response**:
```http
HTTP/1.1 200
Content-Type: application/json
{
"id":1,
"uid": "nErXDvCkzz",
"title": "Departmenet DEF",
"url": "/dashboards/f/nErXDvCkzz/department-def",
"hasAcl": false,
"canSave": true,
"canEdit": true,
"canAdmin": true,
"createdBy": "admin",
"created": "2018-01-31T17:43:12+01:00",
"updatedBy": "admin",
"updated": "2018-01-31T17:43:12+01:00",
"version": 1
}
```
Status Codes:
- **200** Updated
- **400** Errors (invalid json, missing or invalid fields, etc)
- **401** Unauthorized
- **403** Access Denied
- **404** Folder not found
- **412** Precondition failed
The **412** status code is used for explaing that you cannot update the folder and why.
There can be different reasons for this:
- The folder has been changed by someone else, `status=version-mismatch`
The response body will have the following properties:
```http
HTTP/1.1 412 Precondition Failed
Content-Type: application/json; charset=UTF-8
Content-Length: 97
{
"message": "The folder has been changed by someone else",
"status": "version-mismatch"
}
```
## Delete folder
`DELETE /api/folders/:uid`
Deletes an existing folder identified by uid together with all dashboards stored in the folder, if any. This operation cannot be reverted.
**Example Request**:
```http
DELETE /api/folders/nErXDvCkzz HTTP/1.1
Accept: application/json
Content-Type: application/json
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
```
**Example Response**:
```http
HTTP/1.1 200
Content-Type: application/json
{
"message":"Folder deleted"
}
```
Status Codes:
- **200** Deleted
- **401** Unauthorized
- **403** Access Denied
- **404** Folder not found
## Get folder by id
`GET /api/folders/:id`
Will return the folder identified by id.
**Example Request**:
```http
GET /api/folders/1 HTTP/1.1
Accept: application/json
Content-Type: application/json
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
```
**Example Response**:
```http
HTTP/1.1 200
Content-Type: application/json
{
"id":1,
"uid": "nErXDvCkzz",
"title": "Departmenet ABC",
"url": "/dashboards/f/nErXDvCkzz/department-abc",
"hasAcl": false,
"canSave": true,
"canEdit": true,
"canAdmin": true,
"createdBy": "admin",
"created": "2018-01-31T17:43:12+01:00",
"updatedBy": "admin",
"updated": "2018-01-31T17:43:12+01:00",
"version": 1
}
```
Status Codes:
- **200** Found
- **401** Unauthorized
- **403** Access Denied
- **404** Folder not found
@@ -0,0 +1,98 @@
+++
title = "Folder/Dashboard Search HTTP API "
description = "Grafana Folder/Dashboard Search HTTP API"
keywords = ["grafana", "http", "documentation", "api", "search", "folder", "dashboard"]
aliases = ["/http_api/folder_dashboard_search/"]
type = "docs"
[menu.docs]
name = "Folder/dashboard search"
parent = "http_api"
+++
# Folder/Dashboard Search API
## Search folders and dashboards
`GET /api/search/`
Query parameters:
- **query** Search Query
- **tag** List of tags to search for
- **type** Type to search for, `dash-folder` or `dash-db`
- **dashboardIds** List of dashboard id's to search for
- **folderIds** List of folder id's to search in for dashboards
- **starred** Flag indicating if only starred Dashboards should be returned
- **limit** Limit the number of returned results
**Example request for retrieving folders and dashboards of the general folder**:
```http
GET /api/search?folderIds=0&query=&starred=false HTTP/1.1
Accept: application/json
Content-Type: application/json
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
```
**Example response for retrieving folders and dashboards of the general folder**:
```http
HTTP/1.1 200
Content-Type: application/json
[
{
"id": 163,
"uid": "000000163",
"title": "Folder",
"url": "/dashboards/f/000000163/folder",
"type": "dash-folder",
"tags": [],
"isStarred": false,
"uri":"db/folder" // deprecated in Grafana v5.0
},
{
"id":1,
"uid": "cIBgcSjkk",
"title":"Production Overview",
"url": "/d/cIBgcSjkk/production-overview",
"type":"dash-db",
"tags":[prod],
"isStarred":true,
"uri":"db/production-overview" // deprecated in Grafana v5.0
}
]
```
**Example request searching for dashboards**:
```http
GET /api/search?query=Production%20Overview&starred=true&tag=prod HTTP/1.1
Accept: application/json
Content-Type: application/json
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
```
**Example response searching for dashboards**:
```http
HTTP/1.1 200
Content-Type: application/json
[
{
"id":1,
"uid": "cIBgcSjkk",
"title":"Production Overview",
"url": "/d/cIBgcSjkk/production-overview",
"type":"dash-db",
"tags":[prod],
"isStarred":true,
"folderId": 2,
"folderUid": "000000163",
"folderTitle": "Folder",
"folderUrl": "/dashboards/f/000000163/folder",
"uri":"db/production-overview" // deprecated in Grafana v5.0
}
]
```
+149
View File
@@ -0,0 +1,149 @@
+++
title = "Folder Permissions HTTP API "
description = "Grafana Folder Permissions HTTP API"
keywords = ["grafana", "http", "documentation", "api", "folder", "permission", "permissions", "acl"]
aliases = ["/http_api/dashboardpermissions/"]
type = "docs"
[menu.docs]
name = "Folder Permissions"
parent = "http_api"
+++
# Folder Permissions API
This API can be used to update/get the permissions for a folder.
Permissions with `folderId=-1` are the default permissions for users with the Viewer and Editor roles. Permissions can be set for a user, a team or a role (Viewer or Editor). Permissions cannot be set for Admins - they always have access to everything.
The permission levels for the permission field:
- 1 = View
- 2 = Edit
- 4 = Admin
## Get permissions for a folder
`GET /api/folders/:uid/permissions`
Gets all existing permissions for the folder with the given `uid`.
**Example request**:
```http
GET /api/folders/nErXDvCkzz/permissions HTTP/1.1
Accept: application/json
Content-Type: application/json
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
```
**Example Response**
```http
HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
Content-Length: 551
[
{
"id": 1,
"folderId": -1,
"created": "2017-06-20T02:00:00+02:00",
"updated": "2017-06-20T02:00:00+02:00",
"userId": 0,
"userLogin": "",
"userEmail": "",
"teamId": 0,
"team": "",
"role": "Viewer",
"permission": 1,
"permissionName": "View",
"uid": "nErXDvCkzz",
"title": "",
"slug": "",
"isFolder": false,
"url": ""
},
{
"id": 2,
"dashboardId": -1,
"created": "2017-06-20T02:00:00+02:00",
"updated": "2017-06-20T02:00:00+02:00",
"userId": 0,
"userLogin": "",
"userEmail": "",
"teamId": 0,
"team": "",
"role": "Editor",
"permission": 2,
"permissionName": "Edit",
"uid": "",
"title": "",
"slug": "",
"isFolder": false,
"url": ""
}
]
```
Status Codes:
- **200** - Ok
- **401** - Unauthorized
- **403** - Access denied
- **404** - Folder not found
## Update permissions for a folder
`POST /api/folders/:uid/permissions`
Updates permissions for a folder. This operation will remove existing permissions if they're not included in the request.
**Example request**:
```http
POST /api/folders/nErXDvCkzz/permissions
Accept: application/json
Content-Type: application/json
Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk
"items": [
{
"role": "Viewer",
"permission": 1
},
{
"role": "Editor",
"permission": 2
},
{
"teamId": 1,
"permission": 1
},
{
"userId": 11,
"permission": 4
}
]
}
```
JSON body schema:
- **items** - The permission items to add/update. Items that are omitted from the list will be removed.
**Example response**:
```http
HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
Content-Length: 35
{"message":"Folder permissions updated"}
```
Status Codes:
- **200** - Ok
- **401** - Unauthorized
- **403** - Access denied
- **404** - Dashboard not found
+5
View File
@@ -21,12 +21,17 @@ dashboards, creating users and updating data sources.
* [Authentication API]({{< relref "/http_api/auth.md" >}})
* [Dashboard API]({{< relref "/http_api/dashboard.md" >}})
* [Dashboard Versions API]({{< relref "http_api/dashboard_versions.md" >}})
* [Dashboard Permissions API]({{< relref "http_api/dashboard_permissions.md" >}})
* [Folder API]({{< relref "/http_api/folder.md" >}})
* [Folder Permissions API]({{< relref "http_api/folder_permissions.md" >}})
* [Folder/dashboard search API]({{< relref "/http_api/folder_dashboard_search.md" >}})
* [Data Source API]({{< relref "http_api/data_source.md" >}})
* [Organisation API]({{< relref "http_api/org.md" >}})
* [Snapshot API]({{< relref "http_api/snapshot.md" >}})
* [Annotations API]({{< relref "http_api/annotations.md" >}})
* [Alerting API]({{< relref "http_api/alerting.md" >}})
* [User API]({{< relref "http_api/user.md" >}})
* [Team API]({{< relref "http_api/team.md" >}})
* [Admin API]({{< relref "http_api/admin.md" >}})
* [Preferences API]({{< relref "http_api/preferences.md" >}})
* [Other API]({{< relref "http_api/other.md" >}})
+316
View File
@@ -0,0 +1,316 @@
+++
title = "Team HTTP API "
description = "Grafana Team HTTP API"
keywords = ["grafana", "http", "documentation", "api", "team", "teams", "group"]
aliases = ["/http_api/team/"]
type = "docs"
[menu.docs]
name = "Teams"
parent = "http_api"
+++
# Team API
This API can be used to create/update/delete Teams and to add/remove users to Teams. All actions require that the user has the Admin role for the organization.
## Team Search With Paging
`GET /api/teams/search?perpage=50&page=1&query=mytea`
or
`GET /api/teams/search?name=myteam`
```http
GET /api/teams/search?perpage=10&page=1&query=myteam HTTP/1.1
Accept: application/json
Content-Type: application/json
Authorization: Basic YWRtaW46YWRtaW4=
```
### Using the query parameter
Default value for the `perpage` parameter is `1000` and for the `page` parameter is `1`.
The `totalCount` field in the response can be used for pagination of the teams list E.g. if `totalCount` is equal to 100 teams and the `perpage` parameter is set to 10 then there are 10 pages of teams.
The `query` parameter is optional and it will return results where the query value is contained in the `name` field. Query values with spaces need to be url encoded e.g. `query=my%20team`.
### Using the name parameter
The `name` parameter returns a single team if the parameter matches the `name` field.
**Example Response**:
```http
HTTP/1.1 200
Content-Type: application/json
"totalCount": 1,
"teams": [
{
"id": 1,
"orgId": 1,
"name": "MyTestTeam",
"email": "",
"avatarUrl": "\/avatar\/3f49c15916554246daa714b9bd0ee398",
"memberCount": 1
}
],
"page": 1,
"perPage": 1000
}
```
Status Codes:
- **200** - Ok
- **401** - Unauthorized
- **403** - Permission denied
- **404** - Team not found (if searching by name)
## Get Team By Id
`GET /api/teams/:id`
**Example Request**:
```http
GET /api/teams/1 HTTP/1.1
Accept: application/json
Content-Type: application/json
Authorization: Basic YWRtaW46YWRtaW4=
```
**Example Response**:
```http
HTTP/1.1 200
Content-Type: application/json
{
"id": 1,
"orgId": 1,
"name": "MyTestTeam",
"email": "",
"created": "2017-12-15T10:40:45+01:00",
"updated": "2017-12-15T10:40:45+01:00"
}
```
Status Codes:
- **200** - Ok
- **401** - Unauthorized
- **403** - Permission denied
- **404** - Team not found
## Add Team
The Team `name` needs to be unique. `name` is required and `email` is optional.
`POST /api/teams`
**Example Request**:
```http
POST /api/teams HTTP/1.1
Accept: application/json
Content-Type: application/json
Authorization: Basic YWRtaW46YWRtaW4=
{
"name": "MyTestTeam",
"email": "email@test.com"
}
```
**Example Response**:
```http
HTTP/1.1 200
Content-Type: application/json
{"message":"Team created","teamId":2}
```
Status Codes:
- **200** - Ok
- **401** - Unauthorized
- **403** - Permission denied
- **409** - Team name is taken
## Update Team
There are two fields that can be updated for a team: `name` and `email`.
`PUT /api/teams/:id`
**Example Request**:
```http
PUT /api/teams/2 HTTP/1.1
Accept: application/json
Content-Type: application/json
Authorization: Basic YWRtaW46YWRtaW4=
{
"name": "MyTestTeam",
"email": "email@test.com"
}
```
**Example Response**:
```http
HTTP/1.1 200
Content-Type: application/json
{"message":"Team updated"}
```
Status Codes:
- **200** - Ok
- **401** - Unauthorized
- **403** - Permission denied
- **404** - Team not found
- **409** - Team name is taken
## Delete Team By Id
`DELETE /api/teams/:id`
**Example Request**:
```http
DELETE /api/teams/2 HTTP/1.1
Accept: application/json
Content-Type: application/json
Authorization: Basic YWRtaW46YWRtaW4=
```
**Example Response**:
```http
HTTP/1.1 200
Content-Type: application/json
{"message":"Team deleted"}
```
Status Codes:
- **200** - Ok
- **401** - Unauthorized
- **403** - Permission denied
- **404** - Failed to delete Team. ID not found
## Get Team Members
`GET /api/teams/:teamId/members`
**Example Request**:
```http
GET /api/teams/1/members HTTP/1.1
Accept: application/json
Content-Type: application/json
Authorization: Basic YWRtaW46YWRtaW4=
```
**Example Response**:
```http
HTTP/1.1 200
Content-Type: application/json
[
{
"orgId": 1,
"teamId": 1,
"userId": 3,
"email": "user1@email.com",
"login": "user1",
"avatarUrl": "\/avatar\/1b3c32f6386b0185c40d359cdc733a79"
},
{
"orgId": 1,
"teamId": 1,
"userId": 2,
"email": "user2@email.com",
"login": "user2",
"avatarUrl": "\/avatar\/cad3c68da76e45d10269e8ef02f8e73e"
}
]
```
Status Codes:
- **200** - Ok
- **401** - Unauthorized
- **403** - Permission denied
## Add Team Member
`POST /api/teams/:teamId/members`
**Example Request**:
```http
POST /api/teams/1/members HTTP/1.1
Accept: application/json
Content-Type: application/json
Authorization: Basic YWRtaW46YWRtaW4=
{
"userId": 2
}
```
**Example Response**:
```http
HTTP/1.1 200
Content-Type: application/json
{"message":"Member added to Team"}
```
Status Codes:
- **200** - Ok
- **400** - User is already added to this team
- **401** - Unauthorized
- **403** - Permission denied
- **404** - Team not found
## Remove Member From Team
`DELETE /api/teams/:teamId/members/:userId`
**Example Request**:
```http
DELETE /api/teams/2/members/3 HTTP/1.1
Accept: application/json
Content-Type: application/json
Authorization: Basic YWRtaW46YWRtaW4=
```
**Example Response**:
```http
HTTP/1.1 200
Content-Type: application/json
{"message":"Team Member removed"}
```
Status Codes:
- **200** - Ok
- **401** - Unauthorized
- **403** - Permission denied
- **404** - Team not found/Team member not found
+1 -1
View File
@@ -296,7 +296,7 @@ options are `Admin` and `Editor`. e.g. :
`auto_assign_org_role = Viewer`
### viewers can edit
### viewers_can_edit
Viewers can edit/inspect dashboard settings in the browser. But not save the dashboard.
Defaults to `false`.
+3 -12
View File
@@ -15,8 +15,7 @@ weight = 1
Description | Download
------------ | -------------
Stable for Debian-based Linux | [grafana_4.6.3_amd64.deb](https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana_4.6.3_amd64.deb)
Beta for Debian-based Linux | [grafana_5.0.0-beta5_amd64.deb](https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana_5.0.0-beta5_amd64.deb)
Stable for Debian-based Linux | [grafana_5.0.1_amd64.deb](https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana_5.0.1_amd64.deb)
Read [Upgrading Grafana]({{< relref "installation/upgrading.md" >}}) for tips and guidance on updating an existing
installation.
@@ -25,19 +24,11 @@ installation.
```bash
wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana_4.6.3_amd64.deb
wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana_5.0.1_amd64.deb
sudo apt-get install -y adduser libfontconfig
sudo dpkg -i grafana_4.6.3_amd64.deb
sudo dpkg -i grafana_5.0.1_amd64.deb
```
## Install Latest Beta
```bash
wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana_5.0.0-beta5_amd64.deb
sudo apt-get install -y adduser libfontconfig
sudo dpkg -i grafana_5.0.0-beta5_amd64.deb
```
## APT Repository
Add the following line to your `/etc/apt/sources.list` file.
+6 -12
View File
@@ -15,8 +15,8 @@ weight = 2
Description | Download
------------ | -------------
Stable for CentOS / Fedora / OpenSuse / Redhat Linux | [4.6.3 (x86-64 rpm)](https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-4.6.3-1.x86_64.rpm)
Latest Beta for CentOS / Fedora / OpenSuse / Redhat Linux | [5.0.0-beta5 (x86-64 rpm)](https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-5.0.0-beta5.x86_64.rpm)
Stable for CentOS / Fedora / OpenSuse / Redhat Linux | [5.0.1 (x86-64 rpm)](https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-5.0.1-1.x86_64.rpm)
Read [Upgrading Grafana]({{< relref "installation/upgrading.md" >}}) for tips and guidance on updating an existing
installation.
@@ -26,13 +26,7 @@ installation.
You can install Grafana using Yum directly.
```bash
$ sudo yum install https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-4.6.3-1.x86_64.rpm
```
## Install Beta
```bash
$ sudo yum install https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-5.0.0-beta5.x86_64.rpm
$ sudo yum install https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-5.0.1-1.x86_64.rpm
```
Or install manually using `rpm`.
@@ -40,15 +34,15 @@ Or install manually using `rpm`.
#### On CentOS / Fedora / Redhat:
```bash
$ wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-4.6.3-1.x86_64.rpm
$ wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-5.0.1-1.x86_64.rpm
$ sudo yum install initscripts fontconfig
$ sudo rpm -Uvh grafana-4.6.3-1.x86_64.rpm
$ sudo rpm -Uvh grafana-5.0.1-1.x86_64.rpm
```
#### On OpenSuse:
```bash
$ sudo rpm -i --nodeps grafana-4.6.3-1.x86_64.rpm
$ sudo rpm -i --nodeps grafana-5.0.1-1.x86_64.rpm
```
## Install via YUM Repository
+4 -1
View File
@@ -105,4 +105,7 @@ We are not aware of any issues upgrading directly from 2.x to 4.x but to be on t
## Upgrading to v5.0
The dashboard grid layout engine has changed. All dashboards will be automatically upgraded to new
positioning system when you load them in v5. Dashboards saved in v5 will not work in older versions of Grafana.
positioning system when you load them in v5. Dashboards saved in v5 will not work in older versions of Grafana. Some
external panel plugins might need to be updated to work properly.
For more details on the new panel positioning system, [click here]({{< relref "reference/dashboard.md#panel-size-position" >}})
+1 -2
View File
@@ -13,8 +13,7 @@ weight = 3
Description | Download
------------ | -------------
Latest stable package for Windows | [grafana.4.6.3.windows-x64.zip](https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-4.6.3.windows-x64.zip)
Latest beta package for Windows | [grafana.5.0.0-beta5.windows-x64.zip](https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-5.0.0-beta5.windows-x64.zip)
Latest stable package for Windows | [grafana-5.0.1.windows-x64.zip](https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-5.0.1.windows-x64.zip)
Read [Upgrading Grafana]({{< relref "installation/upgrading.md" >}}) for tips and guidance on updating an existing
installation.
+2 -1
View File
@@ -54,7 +54,8 @@ Annotation events are fetched via annotation queries. To add a new annotation qu
open the dashboard settings menu, then select `Annotations`. This will open the dashboard annotations
settings view. To create a new annotation query hit the `New` button.
![](/img/docs/annotations/new_query.png)
<!--![](/img/docs/v50/annotation_new_query.png)-->
{{< docs-imagebox img="/img/docs/v50/annotation_new_query.png" max-width="600px" >}}
Specify a name for the annotation query. This name is given to the toggle (checkbox) that will allow
you to enable/disable showing annotation events from this query. For example you might have two
+28 -199
View File
@@ -10,7 +10,7 @@ weight = 100
# Dashboard JSON
A dashboard in Grafana is represented by a JSON object, which stores metadata of its dashboard. Dashboard metadata includes dashboard properties, metadata from rows, panels, template variables, panel queries, etc.
A dashboard in Grafana is represented by a JSON object, which stores metadata of its dashboard. Dashboard metadata includes dashboard properties, metadata from panels, template variables, panel queries, etc.
To view the JSON of a dashboard, follow the steps mentioned below:
@@ -27,6 +27,7 @@ When a user creates a new dashboard, a new dashboard JSON object is initialized
```json
{
"id": null,
"uid": "cLV5GDCkz",
"title": "New dashboard",
"tags": [],
"style": "dark",
@@ -34,7 +35,7 @@ When a user creates a new dashboard, a new dashboard JSON object is initialized
"editable": true,
"hideControls": false,
"graphTooltip": 1,
"rows": [],
"panels": [],
"time": {
"from": "now-6h",
"to": "now"
@@ -49,7 +50,7 @@ When a user creates a new dashboard, a new dashboard JSON object is initialized
"annotations": {
"list": []
},
"schemaVersion": 7,
"schemaVersion": 16,
"version": 0,
"links": []
}
@@ -58,224 +59,56 @@ Each field in the dashboard JSON is explained below with its usage:
| Name | Usage |
| ---- | ----- |
| **id** | unique dashboard id, an integer |
| **id** | unique numeric identifier for the dashboard. (generated by the db) |
| **uid** | unique dashboard identifier that can be generated by anyone. string (8-40) |
| **title** | current title of dashboard |
| **tags** | tags associated with dashboard, an array of strings |
| **style** | theme of dashboard, i.e. `dark` or `light` |
| **timezone** | timezone of dashboard, i.e. `utc` or `browser` |
| **editable** | whether a dashboard is editable or not |
| **hideControls** | whether row controls on the left in green are hidden or not |
| **graphTooltip** | 0 for no shared crosshair or tooltip (default), 1 for shared crosshair, 2 for shared crosshair AND shared tooltip |
| **rows** | row metadata, see [rows section](#rows) for details |
| **time** | time range for dashboard, i.e. last 6 hours, last 7 days, etc |
| **timepicker** | timepicker metadata, see [timepicker section](#timepicker) for details |
| **templating** | templating metadata, see [templating section](#templating) for details |
| **annotations** | annotations metadata, see [annotations section](#annotations) for details |
| **schemaVersion** | version of the JSON schema (integer), incremented each time a Grafana update brings changes to the said schema |
| **version** | version of the dashboard (integer), incremented each time the dashboard is updated |
| **links** | TODO |
| **panels** | panels array, see below for detail. |
### rows
## Panels
`rows` field consists of an array of JSON object representing each row in a dashboard, such as shown below:
```json
"rows": [
{
"collapse": false,
"editable": true,
"height": "200px",
"panels": [],
"title": "New row"
},
{
"collapse": true,
"editable": true,
"height": "300px",
"panels": [],
"title": "New row"
}
]
```
Usage of the fields is explained below:
| Name | Usage |
| ---- | ----- |
| **collapse** | whether row is collapsed or not |
| **editable** | whether a row is editable or not |
| **height** | height of the row in pixels |
| **panels** | panels metadata, see [panels section](#panels) for details |
| **title** | title of row |
#### panels
Panels are the building blocks a dashboard. It consists of datasource queries, type of graphs, aliases, etc. Panel JSON consists of an array of JSON objects, each representing a different panel in a row. Most of the fields are common for all panels but some fields depends on the panel type. Following is an example of panel JSON representing a `graph` panel type:
Panels are the building blocks a dashboard. It consists of datasource queries, type of graphs, aliases, etc. Panel JSON consists of an array of JSON objects, each representing a different panel. Most of the fields are common for all panels but some fields depends on the panel type. Following is an example of panel JSON of a text panel.
```json
"panels": [
{
"aliasColors": {},
"bars": false,
"datasource": null,
"editable": true,
"error": false,
"fill": 0,
"grid": {
"leftLogBase": 1,
"leftMax": null,
"leftMin": null,
"rightLogBase": 1,
"rightMax": null,
"rightMin": null,
"threshold1": null,
"threshold1Color": "rgba(216, 200, 27, 0.27)",
"threshold2": null,
"threshold2Color": "rgba(234, 112, 112, 0.22)"
"type": "text",
"title": "Panel Title",
"gridPos": {
"x": 0,
"y": 0,
"w": 12,
"h": 9
},
"id": 1,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"links": [],
"nullPointMode": "connected",
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"span": 4,
"stack": false,
"steppedLine": false,
"targets": [
{
"aggregator": "max",
"alias": "$tag_instance_id",
"currentTagKey": "",
"currentTagValue": "",
"downsampleAggregator": "avg",
"downsampleInterval": "",
"errors": {},
"metric": "memory.percent-used",
"refId": "A",
"shouldComputeRate": false,
"tags": {
"app": "$app",
"env": "stage",
"instance_id": "*"
"id": 4,
"mode": "markdown",
"content": "# title"
}
}
],
"timeFrom": null,
"timeShift": null,
"title": "Memory Utilization",
"tooltip": {
"shared": true,
"value_type": "cumulative"
},
"type": "graph",
"x-axis": true,
"y-axis": true,
"y_formats": [
"percent",
"short"
]
},
{
"aliasColors": {},
"bars": false,
"datasource": null,
"editable": true,
"error": false,
"fill": 0,
"grid": {
"leftLogBase": 1,
"leftMax": null,
"leftMin": null,
"rightLogBase": 1,
"rightMax": null,
"rightMin": null,
"threshold1": null,
"threshold1Color": "rgba(216, 200, 27, 0.27)",
"threshold2": null,
"threshold2Color": "rgba(234, 112, 112, 0.22)"
},
"id": 2,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"links": [],
"nullPointMode": "connected",
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"span": 4,
"stack": false,
"steppedLine": false,
"targets": [
{
"aggregator": "avg",
"alias": "$tag_instance_id",
"currentTagKey": "",
"currentTagValue": "",
"downsampleAggregator": "avg",
"downsampleInterval": "",
"errors": {},
"metric": "memory.percent-cached",
"refId": "A",
"shouldComputeRate": false,
"tags": {
"app": "$app",
"env": "prod",
"instance_id": "*"
}
}
],
"timeFrom": null,
"timeShift": null,
"title": "Memory Cached",
"tooltip": {
"shared": true,
"value_type": "cumulative"
},
"type": "graph",
"x-axis": true,
"y-axis": true,
"y_formats": [
"short",
"short"
]
},
```
Usage of each field is explained below:
### Panel size & position
| Name | Usage |
| ---- | ----- |
| TODO | TODO |
The gridPos property describes the panel size and position in grid coordinates.
- `w` 1-24 (the width of the dashboard is divided into 24 columns)
- `h` In grid height units, each represents 30 pixels.
- `x` The x position, in same unit as `w`.
- `y` The y position, in same unit as `h`.
The grid has a negative gravity that moves panels up if there i empty space above a panel.
### timepicker
Description: TODO
```json
"timepicker": {
"collapse": false,
@@ -416,7 +249,3 @@ Usage of the above mentioned fields in the templating section is explained below
| **refresh** | TODO |
| **regex** | TODO |
| **type** | type of variable, i.e. `custom`, `query` or `interval` |
### annotations
TODO
@@ -0,0 +1,52 @@
+++
title = "Dashboard Folders"
keywords = ["grafana", "dashboard", "dashboard folders", "folder", "folders", "documentation", "guide"]
type = "docs"
[menu.docs]
name = "Folders"
parent = "dashboard_features"
weight = 3
+++
# Dashboard Folders
Folders are a way to organize and group dashboards - very useful if you have a lot of dashboards or multiple teams using the same Grafana instance.
## How To Create A Folder
- Create a folder by using the Create Folder link in the side menu (under the create menu (+ icon))
- Use the create Folder button on the Manage Dashboards page.
- When saving a dashboard, you can either choose a folder for the dashboard to be saved in or create a new folder
On the Create Folder page, fill in a unique name for the folder and press Create.
## Manage Dashboards
{{< docs-imagebox img="/img/docs/v50/manage_dashboard_menu.png" max-width="300px" class="docs-image--right" >}}
There is a new Manage Dashboards page where you can carry out a variety of tasks:
- create a folder
- create a dashboard
- move dashboards into folders
- delete multiple dashboards
- navigate to a folder page (where you can set permissions for a folder and/or its dashboards)
## Dashboard Folder Page
You reach the dashboard folder page by clicking on the cog icon that appears when you hover
over a folder in the dashboard list in the search result or on the Manage dashboards page.
The Dashboard Folder Page is similar to the Manage Dashboards page and is where you can carry out the following tasks:
- Allows you to move or delete dashboards in a folder.
- Rename a folder (under the Settings tab).
- Set permissions for the folder (inherited by dashboards in the folder).
## Permissions
Permissions can assigned to a folder and inherited by the containing dashboards. An Access Control List (ACL) is used where
**Organization Role**, **Team** and Individual **User** can be assigned permissions. Read the
[Dashboard & Folder Permissions]({{< relref "administration/permissions.md#dashboard-folder-permissions" >}}) docs for more detail
on the permission system.
+5 -5
View File
@@ -15,9 +15,9 @@ Grafana Dashboards can easily be exported and imported, either from the UI or fr
Dashboards are exported in Grafana JSON format, and contain everything you need (layout, variables, styles, data sources, queries, etc)to import the dashboard at a later time.
The export feature is accessed from the share menu.
The export feature is accessed in the share window which you open by clicking the share button in the dashboard menu.
<img src="/img/docs/v31/export_menu.png">
{{< docs-imagebox img="/img/docs/v50/export_modal.png" max-width="700px" >}}
### Making a dashboard portable
@@ -31,12 +31,12 @@ the dashboard, and will also be added as an required input when the dashboard is
To import a dashboard open dashboard search and then hit the import button.
<img src="/img/docs/v31/import_step1.png">
{{< docs-imagebox img="/img/docs/v50/import_step1.png" max-width="700px" >}}
From here you can upload a dashboard json file, paste a [Grafana.com](https://grafana.com) dashboard
url or paste dashboard json text directly into the text area.
<img src="/img/docs/v31/import_step2.png">
{{< docs-imagebox img="/img/docs/v50/import_step2.png" max-width="700px" >}}
In step 2 of the import process Grafana will let you change the name of the dashboard, pick what
data source you want the dashboard to use and specify any metric prefixes (if the dashboard use any).
@@ -45,7 +45,7 @@ data source you want the dashboard to use and specify any metric prefixes (if th
Find dashboards for common server applications at [Grafana.com/dashboards](https://grafana.com/dashboards).
<img src="/img/docs/v31/gnet_dashboards_list.png">
{{< docs-imagebox img="/img/docs/v50/gcom_dashboard_list.png" max-width="700px" >}}
## Import & Sharing with Grafana 2.x or 3.0
+1 -1
View File
@@ -16,7 +16,7 @@ Since Grafana automatically scales Dashboards to any resolution they're perfect
## Creating a Playlist
{{< docs-imagebox img="/img/docs/v3/playlist.png" max-width="25rem" class="docs-image--right">}}
{{< docs-imagebox img="/img/docs/v50/playlist.png" max-width="25rem" class="docs-image--right">}}
The Playlist feature can be accessed from Grafana's sidemenu, in the Dashboard submenu.
+10 -23
View File
@@ -10,22 +10,22 @@ weight = 5
# Dashboard Search
Dashboards can be searched by the dashboard name, filtered by one (or many) tags or filtered by starred status. The dashboard search is accessed through the dashboard picker, available in the dashboard top nav area.
Dashboards can be searched by the dashboard name, filtered by one (or many) tags or filtered by starred status. The dashboard search is accessed through the dashboard picker, available in the dashboard top nav area. The dashboard search can also be opened by using the shortcut `F`.
<img class="no-shadow" src="/img/docs/v2/dashboard_search.png">
<img class="no-shadow" src="/img/docs/v50/dashboard_search_annotated.png" width="700px">
1. `Dashboard Picker`: The Dashboard Picker is your primary navigation tool to move between dashboards. It is present on all dashboards, and open the Dashboard Search. The dashboard picker also doubles as the title of the current dashboard.
2. `Search Bar`: The search bar allows you to enter any string and search both database and file based dashboards in real-time.
3. `Starred`: The starred link allows you to filter the list to display only starred dashboards.
4. `Tags`: The tags filter allows you to filter the list by dashboard tags.
1. `Search Bar`: The search bar allows you to enter any string and search both database and file based dashboards in real-time.
2. `Starred`: Here you find all your starred dashboards.
3. `Recent`: Here you find the latest created dashboards.
4. `Folders`: The tags filter allows you to filter the list by dashboard tags.
5. `Root`: The root contains all dashboards that are not placed in a folder.
6. `Tags`: The tags filter allows you to filter the list by dashboard tags.
When using only a keyboard, you can use your keyboard arrow keys to navigate the results, hit enter to open the selected dashboard.
## Find by dashboard name
<img class="no-shadow" src="/img/docs/v2/dashboard_search_text.gif">
To search and load dashboards click the open folder icon in the header or use the shortcut `CTRL`+`F`. Begin typing any part of the desired dashboard names. Search will return results for for any partial string match in real-time, as you type.
Begin typing any part of the desired dashboard names in the search bar. Search will return results for for any partial string match in real-time, as you type.
Dashboard search is:
- Real-time
@@ -38,21 +38,8 @@ Tags are a great way to organize your dashboards, especially as the number of da
To filter the dashboard list by tag, click on any tag appearing in the right column. The list may be further filtered by clicking on additional tags:
<img class="no-shadow" src="/img/docs/v2/dashboard_search_tag_filtering.gif">
Alternately, to see a list of all available tags, click the tags link in the search bar. All tags will be shown, and when a tag is selected, the dashboard search will be instantly filtered:
<img class="no-shadow" src="/img/docs/v2/dashboard_search_tags_all_filtering.gif">
Alternately, to see a list of all available tags, click the tags dropdown menu. All tags will be shown, and when a tag is selected, the dashboard search will be instantly filtered:
When using only a keyboard: `tab` to focus on the *tags* link, `▼` down arrow key to find a tag and select with the `Enter` key.
**Note**: When multiple tags are selected, Grafana will show dashboards that include **all**.
## Filter by Starred
Starring is a great way to organize and find commonly used dashboards. To show only starred dashboards in the list, click the *starred* link in the search bar:
<img class="no-shadow" src="/img/docs/v2/dashboard_search_starred_filtering.gif">
When using only a keyboard: `tab` to focus on the *stars* link, `▼` down arrow key to find a tag and select with the `Enter` key.
+3 -3
View File
@@ -24,7 +24,7 @@ A dashboard snapshot is an instant way to share an interactive dashboard publicl
(metric, template and annotation) and panel links, leaving only the visible metric data and series names embedded into your dashboard. Dashboard
snapshots can be accessed by anyone who has the link and can reach the URL.
![](/img/docs/v4/share_panel_modal.png)
{{< docs-imagebox img="/img/docs/v50/share_panel_modal.png" max-width="700px" >}}
### Publish snapshots
@@ -70,9 +70,9 @@ Below there should be an interactive Grafana graph embedded in an iframe:
### Export Panel Data
![](/img/docs/v4/export_panel_data.png)
{{< docs-imagebox img="/img/docs/v50/export_panel_data.png" max-width="500px" >}}
The submenu for a panel can be found by clicking on the title of a panel and then on the hamburger (three horizontal lines) submenu on the left of the context menu.
The submenu for a panel can be found by clicking on the title of a panel and then on the More submenu.
This menu contains two options for exporting data:
+7 -7
View File
@@ -1,20 +1,20 @@
+++
title = "Templating"
title = "Variables"
keywords = ["grafana", "templating", "documentation", "guide"]
type = "docs"
[menu.docs]
name = "Templating"
name = "Variables"
parent = "dashboard_features"
weight = 1
+++
# Templating
# Variables
Templating allows for more interactive and dynamic dashboards. Instead of hard-coding things like server, application
Variables allows for more interactive and dynamic dashboards. Instead of hard-coding things like server, application
and sensor name in you metric queries you can use variables in their place. Variables are shown as dropdown select boxes at the top of
the dashboard. These dropdowns make it easy to change the data being displayed in your dashboard.
<img class="no-shadow" src="/img/docs/v4/templated_dash.png">
{{< docs-imagebox img="/img/docs/v50/variables_dashboard.png" >}}
## What is a variable?
@@ -43,7 +43,7 @@ is the set of values you can choose from.
## Adding a variable
<img class="no-shadow" src="/img/docs/v4/templating_var_list.png">
{{< docs-imagebox img="/img/docs/v50/variables_var_list.png" max-width="800px" >}}
You add variables via Dashboard cogs menu > Templating. This opens up a list of variables and a `New` button to create a new variable.
@@ -133,7 +133,7 @@ Option | Description
*Tags query* | Data source query that should return a list of tags
*Tag values query* | Data source query that should return a list of values for a specified tag key. Use `$tag` in the query to refer the currently selected tag.
![](/img/docs/v4/variable_dropdown_tags.png)
{{< docs-imagebox img="/img/docs/v50/variable_dropdown_tags.png" max-width="300px" >}}
### Interval variables
+6 -6
View File
@@ -13,7 +13,7 @@ weight = 7
Grafana provides numerous ways to manage the time ranges of the data being visualized, both at the Dashboard-level and the Panel-level.
<img class="no-shadow" src="/img/docs/whatsnew_2_5/timepicker.png">
<img class="no-shadow" src="/img/docs/v50/timepicker.png" width="700px">
In the top right, you have the master Dashboard time picker (it's in between the 'Zoom out' and the 'Refresh' links).
@@ -39,11 +39,11 @@ Week to date | `now/w` | `now`
Previous Month | `now-1M/M` | `now-1M/M`
## Dashboard-Level Time Picker Settings
## Dashboard Time Options
There are two settings available from the Dashboard Settings area, allowing customization of the auto-refresh intervals and the definition of `now`.
There are two settings available in the Dashboard Settings General tab, allowing customization of the auto-refresh intervals and the definition of `now`.
<img class="no-shadow" src="/img/docs/v2/TimePicker-TimeOptions.png">
<img class="no-shadow" src="/img/docs/v50/time_options.png" width="500px">
### Auto-Refresh Options
@@ -59,11 +59,11 @@ Users often ask, [when will then be now](https://www.youtube.com/watch?v=VeZ9HhH
You can override the relative time range for individual panels, causing them to be different than what is selected in the Dashboard time picker in the upper right. This allows you to show metrics from different time periods or days at the same time.
<img class="no-shadow" src="/img/docs/v2/panel_time_override.jpg">
{{< docs-imagebox img="/img/docs/v50/panel_time_override.png" max-width="500px" >}}
You control these overrides in panel editor mode and the tab `Time Range`.
<img class="no-shadow" src="/img/docs/v2/time_range_tab.jpg">
{{< docs-imagebox img="/img/docs/v50/time_range_tab.png" max-width="500px" >}}
When you zoom or change the Dashboard time to a custom absolute time range, all panel overrides will be disabled. The panel relative time override is only active when the dashboard time is also relative. The panel timeshift override is always active, even when the dashboard time is absolute.
+3 -2
View File
@@ -1,6 +1,7 @@
[
{ "version": "v5.0", "path": "/v5.0", "archived": false },
{ "version": "v4.6", "path": "/", "archived": false, "current": true },
{ "version": "v5.1", "path": "/v5.1", "archived": false },
{ "version": "v5.0", "path": "/", "archived": false, "current": true },
{ "version": "v4.6", "path": "/v4.6", "archived": true },
{ "version": "v4.5", "path": "/v4.5", "archived": true },
{ "version": "v4.4", "path": "/v4.4", "archived": true },
{ "version": "v4.3", "path": "/v4.3", "archived": true },
+2 -2
View File
@@ -1,4 +1,4 @@
{
"stable": "4.6.2",
"testing": "4.6.2"
"stable": "5.0.0",
"testing": "5.0.0"
}
+1 -1
View File
@@ -4,7 +4,7 @@
"company": "Grafana Labs"
},
"name": "grafana",
"version": "5.0.1-pre1",
"version": "5.1.0-pre1",
"repository": {
"type": "git",
"url": "http://github.com/grafana/grafana.git"
+1 -1
View File
@@ -1,5 +1,5 @@
#! /usr/bin/env bash
version=4.6.3
version=5.0.1
wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana_${version}_amd64.deb
+2 -3
View File
@@ -4,12 +4,11 @@ import (
"strings"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/middleware"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting"
)
func AdminGetSettings(c *middleware.Context) {
func AdminGetSettings(c *m.ReqContext) {
settings := make(map[string]interface{})
for _, section := range setting.Cfg.Sections() {
@@ -30,7 +29,7 @@ func AdminGetSettings(c *middleware.Context) {
c.JSON(200, settings)
}
func AdminGetStats(c *middleware.Context) {
func AdminGetStats(c *m.ReqContext) {
statsQuery := m.GetAdminStatsQuery{}
+4 -5
View File
@@ -4,12 +4,11 @@ import (
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/metrics"
"github.com/grafana/grafana/pkg/middleware"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/util"
)
func AdminCreateUser(c *middleware.Context, form dtos.AdminCreateUserForm) {
func AdminCreateUser(c *m.ReqContext, form dtos.AdminCreateUserForm) {
cmd := m.CreateUserCommand{
Login: form.Login,
Email: form.Email,
@@ -47,7 +46,7 @@ func AdminCreateUser(c *middleware.Context, form dtos.AdminCreateUserForm) {
c.JSON(200, result)
}
func AdminUpdateUserPassword(c *middleware.Context, form dtos.AdminUpdateUserPasswordForm) {
func AdminUpdateUserPassword(c *m.ReqContext, form dtos.AdminUpdateUserPasswordForm) {
userId := c.ParamsInt64(":id")
if len(form.Password) < 4 {
@@ -77,7 +76,7 @@ func AdminUpdateUserPassword(c *middleware.Context, form dtos.AdminUpdateUserPas
c.JsonOK("User password updated")
}
func AdminUpdateUserPermissions(c *middleware.Context, form dtos.AdminUpdateUserPermissionsForm) {
func AdminUpdateUserPermissions(c *m.ReqContext, form dtos.AdminUpdateUserPermissionsForm) {
userId := c.ParamsInt64(":id")
cmd := m.UpdateUserPermissionsCommand{
@@ -93,7 +92,7 @@ func AdminUpdateUserPermissions(c *middleware.Context, form dtos.AdminUpdateUser
c.JsonOK("User permissions updated")
}
func AdminDeleteUser(c *middleware.Context) {
func AdminDeleteUser(c *m.ReqContext) {
userId := c.ParamsInt64(":id")
cmd := m.DeleteUserCommand{UserId: userId}
+31 -32
View File
@@ -5,15 +5,14 @@ import (
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/middleware"
"github.com/grafana/grafana/pkg/models"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/alerting"
"github.com/grafana/grafana/pkg/services/guardian"
)
func ValidateOrgAlert(c *middleware.Context) {
func ValidateOrgAlert(c *m.ReqContext) {
id := c.ParamsInt64(":alertId")
query := models.GetAlertByIdQuery{Id: id}
query := m.GetAlertByIdQuery{Id: id}
if err := bus.Dispatch(&query); err != nil {
c.JsonApiErr(404, "Alert not found", nil)
@@ -26,14 +25,14 @@ func ValidateOrgAlert(c *middleware.Context) {
}
}
func GetAlertStatesForDashboard(c *middleware.Context) Response {
func GetAlertStatesForDashboard(c *m.ReqContext) Response {
dashboardId := c.QueryInt64("dashboardId")
if dashboardId == 0 {
return ApiError(400, "Missing query parameter dashboardId", nil)
}
query := models.GetAlertStatesForDashboardQuery{
query := m.GetAlertStatesForDashboardQuery{
OrgId: c.OrgId,
DashboardId: c.QueryInt64("dashboardId"),
}
@@ -46,8 +45,8 @@ func GetAlertStatesForDashboard(c *middleware.Context) Response {
}
// GET /api/alerts
func GetAlerts(c *middleware.Context) Response {
query := models.GetAlertsQuery{
func GetAlerts(c *m.ReqContext) Response {
query := m.GetAlertsQuery{
OrgId: c.OrgId,
DashboardId: c.QueryInt64("dashboardId"),
PanelId: c.QueryInt64("panelId"),
@@ -65,14 +64,14 @@ func GetAlerts(c *middleware.Context) Response {
}
for _, alert := range query.Result {
alert.Url = models.GetDashboardUrl(alert.DashboardUid, alert.DashboardSlug)
alert.Url = m.GetDashboardUrl(alert.DashboardUid, alert.DashboardSlug)
}
return Json(200, query.Result)
}
// POST /api/alerts/test
func AlertTest(c *middleware.Context, dto dtos.AlertTestCommand) Response {
func AlertTest(c *m.ReqContext, dto dtos.AlertTestCommand) Response {
if _, idErr := dto.Dashboard.Get("id").Int64(); idErr != nil {
return ApiError(400, "The dashboard needs to be saved at least once before you can test an alert rule", nil)
}
@@ -114,9 +113,9 @@ func AlertTest(c *middleware.Context, dto dtos.AlertTestCommand) Response {
}
// GET /api/alerts/:id
func GetAlert(c *middleware.Context) Response {
func GetAlert(c *m.ReqContext) Response {
id := c.ParamsInt64(":alertId")
query := models.GetAlertByIdQuery{Id: id}
query := m.GetAlertByIdQuery{Id: id}
if err := bus.Dispatch(&query); err != nil {
return ApiError(500, "List alerts failed", err)
@@ -125,12 +124,12 @@ func GetAlert(c *middleware.Context) Response {
return Json(200, &query.Result)
}
func GetAlertNotifiers(c *middleware.Context) Response {
func GetAlertNotifiers(c *m.ReqContext) Response {
return Json(200, alerting.GetNotifiers())
}
func GetAlertNotifications(c *middleware.Context) Response {
query := &models.GetAllAlertNotificationsQuery{OrgId: c.OrgId}
func GetAlertNotifications(c *m.ReqContext) Response {
query := &m.GetAllAlertNotificationsQuery{OrgId: c.OrgId}
if err := bus.Dispatch(query); err != nil {
return ApiError(500, "Failed to get alert notifications", err)
@@ -152,8 +151,8 @@ func GetAlertNotifications(c *middleware.Context) Response {
return Json(200, result)
}
func GetAlertNotificationById(c *middleware.Context) Response {
query := &models.GetAlertNotificationsQuery{
func GetAlertNotificationById(c *m.ReqContext) Response {
query := &m.GetAlertNotificationsQuery{
OrgId: c.OrgId,
Id: c.ParamsInt64("notificationId"),
}
@@ -165,7 +164,7 @@ func GetAlertNotificationById(c *middleware.Context) Response {
return Json(200, query.Result)
}
func CreateAlertNotification(c *middleware.Context, cmd models.CreateAlertNotificationCommand) Response {
func CreateAlertNotification(c *m.ReqContext, cmd m.CreateAlertNotificationCommand) Response {
cmd.OrgId = c.OrgId
if err := bus.Dispatch(&cmd); err != nil {
@@ -175,7 +174,7 @@ func CreateAlertNotification(c *middleware.Context, cmd models.CreateAlertNotifi
return Json(200, cmd.Result)
}
func UpdateAlertNotification(c *middleware.Context, cmd models.UpdateAlertNotificationCommand) Response {
func UpdateAlertNotification(c *m.ReqContext, cmd m.UpdateAlertNotificationCommand) Response {
cmd.OrgId = c.OrgId
if err := bus.Dispatch(&cmd); err != nil {
@@ -185,8 +184,8 @@ func UpdateAlertNotification(c *middleware.Context, cmd models.UpdateAlertNotifi
return Json(200, cmd.Result)
}
func DeleteAlertNotification(c *middleware.Context) Response {
cmd := models.DeleteAlertNotificationCommand{
func DeleteAlertNotification(c *m.ReqContext) Response {
cmd := m.DeleteAlertNotificationCommand{
OrgId: c.OrgId,
Id: c.ParamsInt64("notificationId"),
}
@@ -199,7 +198,7 @@ func DeleteAlertNotification(c *middleware.Context) Response {
}
//POST /api/alert-notifications/test
func NotificationTest(c *middleware.Context, dto dtos.NotificationTestCommand) Response {
func NotificationTest(c *m.ReqContext, dto dtos.NotificationTestCommand) Response {
cmd := &alerting.NotificationTestCommand{
Name: dto.Name,
Type: dto.Type,
@@ -207,7 +206,7 @@ func NotificationTest(c *middleware.Context, dto dtos.NotificationTestCommand) R
}
if err := bus.Dispatch(cmd); err != nil {
if err == models.ErrSmtpNotEnabled {
if err == m.ErrSmtpNotEnabled {
return ApiError(412, err.Error(), err)
}
return ApiError(500, "Failed to send alert notifications", err)
@@ -217,10 +216,10 @@ func NotificationTest(c *middleware.Context, dto dtos.NotificationTestCommand) R
}
//POST /api/alerts/:alertId/pause
func PauseAlert(c *middleware.Context, dto dtos.PauseAlertCommand) Response {
func PauseAlert(c *m.ReqContext, dto dtos.PauseAlertCommand) Response {
alertId := c.ParamsInt64("alertId")
query := models.GetAlertByIdQuery{Id: alertId}
query := m.GetAlertByIdQuery{Id: alertId}
if err := bus.Dispatch(&query); err != nil {
return ApiError(500, "Get Alert failed", err)
@@ -235,7 +234,7 @@ func PauseAlert(c *middleware.Context, dto dtos.PauseAlertCommand) Response {
return ApiError(403, "Access denied to this dashboard and alert", nil)
}
cmd := models.PauseAlertCommand{
cmd := m.PauseAlertCommand{
OrgId: c.OrgId,
AlertIds: []int64{alertId},
Paused: dto.Paused,
@@ -245,10 +244,10 @@ func PauseAlert(c *middleware.Context, dto dtos.PauseAlertCommand) Response {
return ApiError(500, "", err)
}
var response models.AlertStateType = models.AlertStatePending
var response m.AlertStateType = m.AlertStatePending
pausedState := "un-paused"
if cmd.Paused {
response = models.AlertStatePaused
response = m.AlertStatePaused
pausedState = "paused"
}
@@ -262,8 +261,8 @@ func PauseAlert(c *middleware.Context, dto dtos.PauseAlertCommand) Response {
}
//POST /api/admin/pause-all-alerts
func PauseAllAlerts(c *middleware.Context, dto dtos.PauseAllAlertsCommand) Response {
updateCmd := models.PauseAllAlertCommand{
func PauseAllAlerts(c *m.ReqContext, dto dtos.PauseAllAlertsCommand) Response {
updateCmd := m.PauseAllAlertCommand{
Paused: dto.Paused,
}
@@ -271,10 +270,10 @@ func PauseAllAlerts(c *middleware.Context, dto dtos.PauseAllAlertsCommand) Respo
return ApiError(500, "Failed to pause alerts", err)
}
var response models.AlertStateType = models.AlertStatePending
var response m.AlertStateType = m.AlertStatePending
pausedState := "un paused"
if updateCmd.Paused {
response = models.AlertStatePaused
response = m.AlertStatePaused
pausedState = "paused"
}
+1 -2
View File
@@ -5,7 +5,6 @@ import (
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/middleware"
m "github.com/grafana/grafana/pkg/models"
. "github.com/smartystreets/goconvey/convey"
@@ -81,7 +80,7 @@ func postAlertScenario(desc string, url string, routePattern string, role m.Role
defer bus.ClearBusHandlers()
sc := setupScenarioContext(url)
sc.defaultHandler = wrap(func(c *middleware.Context) Response {
sc.defaultHandler = wrap(func(c *m.ReqContext) Response {
sc.context = c
sc.context.UserId = TestUserID
sc.context.OrgId = TestOrgID
+10 -11
View File
@@ -6,14 +6,13 @@ import (
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/middleware"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/annotations"
"github.com/grafana/grafana/pkg/services/guardian"
"github.com/grafana/grafana/pkg/util"
)
func GetAnnotations(c *middleware.Context) Response {
func GetAnnotations(c *m.ReqContext) Response {
query := &annotations.ItemQuery{
From: c.QueryInt64("from") / 1000,
@@ -52,7 +51,7 @@ func (e *CreateAnnotationError) Error() string {
return e.message
}
func PostAnnotation(c *middleware.Context, cmd dtos.PostAnnotationsCmd) Response {
func PostAnnotation(c *m.ReqContext, cmd dtos.PostAnnotationsCmd) Response {
if canSave, err := canSaveByDashboardId(c, cmd.DashboardId); err != nil || !canSave {
return dashboardGuardianResponse(err)
}
@@ -125,7 +124,7 @@ func formatGraphiteAnnotation(what string, data string) string {
return text
}
func PostGraphiteAnnotation(c *middleware.Context, cmd dtos.PostGraphiteAnnotationsCmd) Response {
func PostGraphiteAnnotation(c *m.ReqContext, cmd dtos.PostGraphiteAnnotationsCmd) Response {
repo := annotations.GetRepository()
if cmd.What == "" {
@@ -179,7 +178,7 @@ func PostGraphiteAnnotation(c *middleware.Context, cmd dtos.PostGraphiteAnnotati
})
}
func UpdateAnnotation(c *middleware.Context, cmd dtos.UpdateAnnotationsCmd) Response {
func UpdateAnnotation(c *m.ReqContext, cmd dtos.UpdateAnnotationsCmd) Response {
annotationId := c.ParamsInt64(":annotationId")
repo := annotations.GetRepository()
@@ -218,7 +217,7 @@ func UpdateAnnotation(c *middleware.Context, cmd dtos.UpdateAnnotationsCmd) Resp
return ApiSuccess("Annotation updated")
}
func DeleteAnnotations(c *middleware.Context, cmd dtos.DeleteAnnotationsCmd) Response {
func DeleteAnnotations(c *m.ReqContext, cmd dtos.DeleteAnnotationsCmd) Response {
repo := annotations.GetRepository()
err := repo.Delete(&annotations.DeleteParams{
@@ -234,7 +233,7 @@ func DeleteAnnotations(c *middleware.Context, cmd dtos.DeleteAnnotationsCmd) Res
return ApiSuccess("Annotations deleted")
}
func DeleteAnnotationById(c *middleware.Context) Response {
func DeleteAnnotationById(c *m.ReqContext) Response {
repo := annotations.GetRepository()
annotationId := c.ParamsInt64(":annotationId")
@@ -253,7 +252,7 @@ func DeleteAnnotationById(c *middleware.Context) Response {
return ApiSuccess("Annotation deleted")
}
func DeleteAnnotationRegion(c *middleware.Context) Response {
func DeleteAnnotationRegion(c *m.ReqContext) Response {
repo := annotations.GetRepository()
regionId := c.ParamsInt64(":regionId")
@@ -272,7 +271,7 @@ func DeleteAnnotationRegion(c *middleware.Context) Response {
return ApiSuccess("Annotation region deleted")
}
func canSaveByDashboardId(c *middleware.Context, dashboardId int64) (bool, error) {
func canSaveByDashboardId(c *m.ReqContext, dashboardId int64) (bool, error) {
if dashboardId == 0 && !c.SignedInUser.HasRole(m.ROLE_EDITOR) {
return false, nil
}
@@ -287,7 +286,7 @@ func canSaveByDashboardId(c *middleware.Context, dashboardId int64) (bool, error
return true, nil
}
func canSave(c *middleware.Context, repo annotations.Repository, annotationId int64) Response {
func canSave(c *m.ReqContext, repo annotations.Repository, annotationId int64) Response {
items, err := repo.Find(&annotations.ItemQuery{AnnotationId: annotationId, OrgId: c.OrgId})
if err != nil || len(items) == 0 {
@@ -303,7 +302,7 @@ func canSave(c *middleware.Context, repo annotations.Repository, annotationId in
return nil
}
func canSaveByRegionId(c *middleware.Context, repo annotations.Repository, regionId int64) Response {
func canSaveByRegionId(c *m.ReqContext, repo annotations.Repository, regionId int64) Response {
items, err := repo.Find(&annotations.ItemQuery{RegionId: regionId, OrgId: c.OrgId})
if err != nil || len(items) == 0 {
+2 -3
View File
@@ -5,7 +5,6 @@ import (
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/middleware"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/annotations"
@@ -200,7 +199,7 @@ func postAnnotationScenario(desc string, url string, routePattern string, role m
defer bus.ClearBusHandlers()
sc := setupScenarioContext(url)
sc.defaultHandler = wrap(func(c *middleware.Context) Response {
sc.defaultHandler = wrap(func(c *m.ReqContext) Response {
sc.context = c
sc.context.UserId = TestUserID
sc.context.OrgId = TestOrgID
@@ -223,7 +222,7 @@ func putAnnotationScenario(desc string, url string, routePattern string, role m.
defer bus.ClearBusHandlers()
sc := setupScenarioContext(url)
sc.defaultHandler = wrap(func(c *middleware.Context) Response {
sc.defaultHandler = wrap(func(c *m.ReqContext) Response {
sc.context = c
sc.context.UserId = TestUserID
sc.context.OrgId = TestOrgID
+3 -4
View File
@@ -4,11 +4,10 @@ import (
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/apikeygen"
"github.com/grafana/grafana/pkg/middleware"
m "github.com/grafana/grafana/pkg/models"
)
func GetApiKeys(c *middleware.Context) Response {
func GetApiKeys(c *m.ReqContext) Response {
query := m.GetApiKeysQuery{OrgId: c.OrgId}
if err := bus.Dispatch(&query); err != nil {
@@ -27,7 +26,7 @@ func GetApiKeys(c *middleware.Context) Response {
return Json(200, result)
}
func DeleteApiKey(c *middleware.Context) Response {
func DeleteApiKey(c *m.ReqContext) Response {
id := c.ParamsInt64(":id")
cmd := &m.DeleteApiKeyCommand{Id: id, OrgId: c.OrgId}
@@ -40,7 +39,7 @@ func DeleteApiKey(c *middleware.Context) Response {
return ApiSuccess("API key deleted")
}
func AddApiKey(c *middleware.Context, cmd m.AddApiKeyCommand) Response {
func AddApiKey(c *m.ReqContext, cmd m.AddApiKeyCommand) Response {
if !cmd.Role.IsValid() {
return ApiError(400, "Invalid role specified", nil)
}
+1 -1
View File
@@ -56,7 +56,7 @@ func InitAppPluginRoutes(r *macaron.Macaron) {
}
func AppPluginRoute(route *plugins.AppPluginRoute, appId string) macaron.Handler {
return func(c *middleware.Context) {
return func(c *m.ReqContext) {
path := c.Params("*")
proxy := pluginproxy.NewApiPluginProxy(c, path, route, appId)
+4 -4
View File
@@ -4,7 +4,7 @@ import (
"encoding/json"
"net/http"
"github.com/grafana/grafana/pkg/middleware"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting"
"gopkg.in/macaron.v1"
)
@@ -19,7 +19,7 @@ var (
)
type Response interface {
WriteTo(ctx *middleware.Context)
WriteTo(ctx *m.ReqContext)
}
type NormalResponse struct {
@@ -32,7 +32,7 @@ type NormalResponse struct {
func wrap(action interface{}) macaron.Handler {
return func(c *middleware.Context) {
return func(c *m.ReqContext) {
var res Response
val, err := c.Invoke(action)
if err == nil && val != nil && len(val) > 0 {
@@ -45,7 +45,7 @@ func wrap(action interface{}) macaron.Handler {
}
}
func (r *NormalResponse) WriteTo(ctx *middleware.Context) {
func (r *NormalResponse) WriteTo(ctx *m.ReqContext) {
if r.err != nil {
ctx.Logger.Error(r.errMessage, "error", r.err)
}
+7 -7
View File
@@ -8,22 +8,22 @@ import (
"github.com/go-macaron/session"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/middleware"
"github.com/grafana/grafana/pkg/models"
macaron "gopkg.in/macaron.v1"
m "github.com/grafana/grafana/pkg/models"
"gopkg.in/macaron.v1"
. "github.com/smartystreets/goconvey/convey"
)
func loggedInUserScenario(desc string, url string, fn scenarioFunc) {
loggedInUserScenarioWithRole(desc, "GET", url, url, models.ROLE_EDITOR, fn)
loggedInUserScenarioWithRole(desc, "GET", url, url, m.ROLE_EDITOR, fn)
}
func loggedInUserScenarioWithRole(desc string, method string, url string, routePattern string, role models.RoleType, fn scenarioFunc) {
func loggedInUserScenarioWithRole(desc string, method string, url string, routePattern string, role m.RoleType, fn scenarioFunc) {
Convey(desc+" "+url, func() {
defer bus.ClearBusHandlers()
sc := setupScenarioContext(url)
sc.defaultHandler = wrap(func(c *middleware.Context) Response {
sc.defaultHandler = wrap(func(c *m.ReqContext) Response {
sc.context = c
sc.context.UserId = TestUserID
sc.context.OrgId = TestOrgID
@@ -71,7 +71,7 @@ func (sc *scenarioContext) fakeReqWithParams(method, url string, queryParams map
type scenarioContext struct {
m *macaron.Macaron
context *middleware.Context
context *m.ReqContext
resp *httptest.ResponseRecorder
handlerFunc handlerFunc
defaultHandler macaron.Handler
@@ -84,7 +84,7 @@ func (sc *scenarioContext) exec() {
}
type scenarioFunc func(c *scenarioContext)
type handlerFunc func(c *middleware.Context) Response
type handlerFunc func(c *m.ReqContext) Response
func setupScenarioContext(url string) *scenarioContext {
sc := &scenarioContext{
+13 -13
View File
@@ -14,15 +14,15 @@ import (
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/log"
"github.com/grafana/grafana/pkg/metrics"
"github.com/grafana/grafana/pkg/middleware"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/services/guardian"
"github.com/grafana/grafana/pkg/services/quota"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util"
)
func isDashboardStarredByUser(c *middleware.Context, dashId int64) (bool, error) {
func isDashboardStarredByUser(c *m.ReqContext, dashId int64) (bool, error) {
if !c.IsSignedIn {
return false, nil
}
@@ -43,7 +43,7 @@ func dashboardGuardianResponse(err error) Response {
return ApiError(403, "Access denied to this dashboard", nil)
}
func GetDashboard(c *middleware.Context) Response {
func GetDashboard(c *m.ReqContext) Response {
dash, rsp := getDashboardHelper(c.OrgId, c.Params(":slug"), 0, c.Params(":uid"))
if rsp != nil {
return rsp
@@ -141,7 +141,7 @@ func getDashboardHelper(orgId int64, slug string, id int64, uid string) (*m.Dash
return query.Result, nil
}
func DeleteDashboard(c *middleware.Context) Response {
func DeleteDashboard(c *m.ReqContext) Response {
query := m.GetDashboardsBySlugQuery{OrgId: c.OrgId, Slug: c.Params(":slug")}
if err := bus.Dispatch(&query); err != nil {
@@ -173,7 +173,7 @@ func DeleteDashboard(c *middleware.Context) Response {
})
}
func DeleteDashboardByUid(c *middleware.Context) Response {
func DeleteDashboardByUid(c *m.ReqContext) Response {
dash, rsp := getDashboardHelper(c.OrgId, "", 0, c.Params(":uid"))
if rsp != nil {
return rsp
@@ -195,14 +195,14 @@ func DeleteDashboardByUid(c *middleware.Context) Response {
})
}
func PostDashboard(c *middleware.Context, cmd m.SaveDashboardCommand) Response {
func PostDashboard(c *m.ReqContext, cmd m.SaveDashboardCommand) Response {
cmd.OrgId = c.OrgId
cmd.UserId = c.UserId
dash := cmd.GetDashboardModel()
if dash.Id == 0 && dash.Uid == "" {
limitReached, err := middleware.QuotaReached(c, "dashboard")
limitReached, err := quota.QuotaReached(c, "dashboard")
if err != nil {
return ApiError(500, "failed to get quota", err)
}
@@ -278,7 +278,7 @@ func PostDashboard(c *middleware.Context, cmd m.SaveDashboardCommand) Response {
})
}
func GetHomeDashboard(c *middleware.Context) Response {
func GetHomeDashboard(c *m.ReqContext) Response {
prefsQuery := m.GetPreferencesWithDefaultsQuery{OrgId: c.OrgId, UserId: c.UserId}
if err := bus.Dispatch(&prefsQuery); err != nil {
return ApiError(500, "Failed to get preferences", err)
@@ -338,7 +338,7 @@ func addGettingStartedPanelToHomeDashboard(dash *simplejson.Json) {
}
// GetDashboardVersions returns all dashboard versions as JSON
func GetDashboardVersions(c *middleware.Context) Response {
func GetDashboardVersions(c *m.ReqContext) Response {
dashId := c.ParamsInt64(":dashboardId")
guardian := guardian.New(dashId, c.OrgId, c.SignedInUser)
@@ -377,7 +377,7 @@ func GetDashboardVersions(c *middleware.Context) Response {
}
// GetDashboardVersion returns the dashboard version with the given ID.
func GetDashboardVersion(c *middleware.Context) Response {
func GetDashboardVersion(c *m.ReqContext) Response {
dashId := c.ParamsInt64(":dashboardId")
guardian := guardian.New(dashId, c.OrgId, c.SignedInUser)
@@ -409,7 +409,7 @@ func GetDashboardVersion(c *middleware.Context) Response {
}
// POST /api/dashboards/calculate-diff performs diffs on two dashboards
func CalculateDashboardDiff(c *middleware.Context, apiOptions dtos.CalculateDiffOptions) Response {
func CalculateDashboardDiff(c *m.ReqContext, apiOptions dtos.CalculateDiffOptions) Response {
guardianBase := guardian.New(apiOptions.Base.DashboardId, c.OrgId, c.SignedInUser)
if canSave, err := guardianBase.CanSave(); err != nil || !canSave {
@@ -454,7 +454,7 @@ func CalculateDashboardDiff(c *middleware.Context, apiOptions dtos.CalculateDiff
}
// RestoreDashboardVersion restores a dashboard to the given version.
func RestoreDashboardVersion(c *middleware.Context, apiCmd dtos.RestoreDashboardVersionCommand) Response {
func RestoreDashboardVersion(c *m.ReqContext, apiCmd dtos.RestoreDashboardVersionCommand) Response {
dash, rsp := getDashboardHelper(c.OrgId, "", c.ParamsInt64(":dashboardId"), "")
if rsp != nil {
return rsp
@@ -484,7 +484,7 @@ func RestoreDashboardVersion(c *middleware.Context, apiCmd dtos.RestoreDashboard
return PostDashboard(c, saveCmd)
}
func GetDashboardTags(c *middleware.Context) {
func GetDashboardTags(c *m.ReqContext) {
query := m.GetDashboardTagsQuery{OrgId: c.OrgId}
err := bus.Dispatch(&query)
if err != nil {
+2 -3
View File
@@ -5,12 +5,11 @@ import (
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/middleware"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/guardian"
)
func GetDashboardPermissionList(c *middleware.Context) Response {
func GetDashboardPermissionList(c *m.ReqContext) Response {
dashId := c.ParamsInt64(":dashboardId")
_, rsp := getDashboardHelper(c.OrgId, "", dashId, "")
@@ -38,7 +37,7 @@ func GetDashboardPermissionList(c *middleware.Context) Response {
return Json(200, acl)
}
func UpdateDashboardPermissions(c *middleware.Context, apiCmd dtos.UpdateDashboardAclCommand) Response {
func UpdateDashboardPermissions(c *m.ReqContext, apiCmd dtos.UpdateDashboardAclCommand) Response {
dashId := c.ParamsInt64(":dashboardId")
_, rsp := getDashboardHelper(c.OrgId, "", dashId, "")
+1 -2
View File
@@ -6,7 +6,6 @@ import (
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/middleware"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/guardian"
@@ -195,7 +194,7 @@ func updateDashboardPermissionScenario(desc string, url string, routePattern str
sc := setupScenarioContext(url)
sc.defaultHandler = wrap(func(c *middleware.Context) Response {
sc.defaultHandler = wrap(func(c *m.ReqContext) Response {
sc.context = c
sc.context.OrgId = TestOrgID
sc.context.UserId = TestUserID
+5 -6
View File
@@ -6,14 +6,13 @@ import (
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/metrics"
"github.com/grafana/grafana/pkg/middleware"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/guardian"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util"
)
func GetSharingOptions(c *middleware.Context) {
func GetSharingOptions(c *m.ReqContext) {
c.JSON(200, util.DynMap{
"externalSnapshotURL": setting.ExternalSnapshotUrl,
"externalSnapshotName": setting.ExternalSnapshotName,
@@ -21,7 +20,7 @@ func GetSharingOptions(c *middleware.Context) {
})
}
func CreateDashboardSnapshot(c *middleware.Context, cmd m.CreateDashboardSnapshotCommand) {
func CreateDashboardSnapshot(c *m.ReqContext, cmd m.CreateDashboardSnapshotCommand) {
if cmd.Name == "" {
cmd.Name = "Unnamed snapshot"
}
@@ -58,7 +57,7 @@ func CreateDashboardSnapshot(c *middleware.Context, cmd m.CreateDashboardSnapsho
}
// GET /api/snapshots/:key
func GetDashboardSnapshot(c *middleware.Context) {
func GetDashboardSnapshot(c *m.ReqContext) {
key := c.Params(":key")
query := &m.GetDashboardSnapshotQuery{Key: key}
@@ -93,7 +92,7 @@ func GetDashboardSnapshot(c *middleware.Context) {
}
// GET /api/snapshots-delete/:key
func DeleteDashboardSnapshot(c *middleware.Context) Response {
func DeleteDashboardSnapshot(c *m.ReqContext) Response {
key := c.Params(":key")
query := &m.GetDashboardSnapshotQuery{DeleteKey: key}
@@ -129,7 +128,7 @@ func DeleteDashboardSnapshot(c *middleware.Context) Response {
}
// GET /api/dashboard/snapshots
func SearchDashboardSnapshots(c *middleware.Context) Response {
func SearchDashboardSnapshots(c *m.ReqContext) Response {
query := c.Query("query")
limit := c.QueryInt("limit")
+2 -3
View File
@@ -8,7 +8,6 @@ import (
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/middleware"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/setting"
@@ -862,7 +861,7 @@ func postDashboardScenario(desc string, url string, routePattern string, mock *d
defer bus.ClearBusHandlers()
sc := setupScenarioContext(url)
sc.defaultHandler = wrap(func(c *middleware.Context) Response {
sc.defaultHandler = wrap(func(c *m.ReqContext) Response {
sc.context = c
sc.context.SignedInUser = &m.SignedInUser{OrgId: cmd.OrgId, UserId: cmd.UserId}
@@ -887,7 +886,7 @@ func postDiffScenario(desc string, url string, routePattern string, cmd dtos.Cal
defer bus.ClearBusHandlers()
sc := setupScenarioContext(url)
sc.defaultHandler = wrap(func(c *middleware.Context) Response {
sc.defaultHandler = wrap(func(c *m.ReqContext) Response {
sc.context = c
sc.context.SignedInUser = &m.SignedInUser{
OrgId: TestOrgID,
+1 -2
View File
@@ -7,7 +7,6 @@ import (
"github.com/grafana/grafana/pkg/api/pluginproxy"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/metrics"
"github.com/grafana/grafana/pkg/middleware"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
)
@@ -35,7 +34,7 @@ func (hs *HttpServer) getDatasourceById(id int64, orgId int64, nocache bool) (*m
return query.Result, nil
}
func (hs *HttpServer) ProxyDataSourceRequest(c *middleware.Context) {
func (hs *HttpServer) ProxyDataSourceRequest(c *m.ReqContext) {
c.TimeRequest(metrics.M_DataSource_ProxyReq_Timer)
nocache := c.Req.Header.Get(HeaderNameNoBackendCache) == "true"
+8 -9
View File
@@ -5,13 +5,12 @@ import (
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/middleware"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/util"
)
func GetDataSources(c *middleware.Context) Response {
func GetDataSources(c *m.ReqContext) Response {
query := m.GetDataSourcesQuery{OrgId: c.OrgId}
if err := bus.Dispatch(&query); err != nil {
@@ -50,7 +49,7 @@ func GetDataSources(c *middleware.Context) Response {
return Json(200, &result)
}
func GetDataSourceById(c *middleware.Context) Response {
func GetDataSourceById(c *m.ReqContext) Response {
query := m.GetDataSourceByIdQuery{
Id: c.ParamsInt64(":id"),
OrgId: c.OrgId,
@@ -69,7 +68,7 @@ func GetDataSourceById(c *middleware.Context) Response {
return Json(200, &dtos)
}
func DeleteDataSourceById(c *middleware.Context) Response {
func DeleteDataSourceById(c *m.ReqContext) Response {
id := c.ParamsInt64(":id")
if id <= 0 {
@@ -95,7 +94,7 @@ func DeleteDataSourceById(c *middleware.Context) Response {
return ApiSuccess("Data source deleted")
}
func DeleteDataSourceByName(c *middleware.Context) Response {
func DeleteDataSourceByName(c *m.ReqContext) Response {
name := c.Params(":name")
if name == "" {
@@ -120,7 +119,7 @@ func DeleteDataSourceByName(c *middleware.Context) Response {
return ApiSuccess("Data source deleted")
}
func AddDataSource(c *middleware.Context, cmd m.AddDataSourceCommand) Response {
func AddDataSource(c *m.ReqContext, cmd m.AddDataSourceCommand) Response {
cmd.OrgId = c.OrgId
if err := bus.Dispatch(&cmd); err != nil {
@@ -140,7 +139,7 @@ func AddDataSource(c *middleware.Context, cmd m.AddDataSourceCommand) Response {
})
}
func UpdateDataSource(c *middleware.Context, cmd m.UpdateDataSourceCommand) Response {
func UpdateDataSource(c *m.ReqContext, cmd m.UpdateDataSourceCommand) Response {
cmd.OrgId = c.OrgId
cmd.Id = c.ParamsInt64(":id")
@@ -205,7 +204,7 @@ func getRawDataSourceById(id int64, orgId int64) (*m.DataSource, error) {
}
// Get /api/datasources/name/:name
func GetDataSourceByName(c *middleware.Context) Response {
func GetDataSourceByName(c *m.ReqContext) Response {
query := m.GetDataSourceByNameQuery{Name: c.Params(":name"), OrgId: c.OrgId}
if err := bus.Dispatch(&query); err != nil {
@@ -221,7 +220,7 @@ func GetDataSourceByName(c *middleware.Context) Response {
}
// Get /api/datasources/id/:name
func GetDataSourceIdByName(c *middleware.Context) Response {
func GetDataSourceIdByName(c *m.ReqContext) Response {
query := m.GetDataSourceByNameQuery{Name: c.Params(":name"), OrgId: c.OrgId}
if err := bus.Dispatch(&query); err != nil {
+6 -7
View File
@@ -4,14 +4,13 @@ import (
"fmt"
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/middleware"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/guardian"
"github.com/grafana/grafana/pkg/util"
)
func GetFolders(c *middleware.Context) Response {
func GetFolders(c *m.ReqContext) Response {
s := dashboards.NewFolderService(c.OrgId, c.SignedInUser)
folders, err := s.GetFolders(c.QueryInt("limit"))
@@ -32,7 +31,7 @@ func GetFolders(c *middleware.Context) Response {
return Json(200, result)
}
func GetFolderByUid(c *middleware.Context) Response {
func GetFolderByUid(c *m.ReqContext) Response {
s := dashboards.NewFolderService(c.OrgId, c.SignedInUser)
folder, err := s.GetFolderByUid(c.Params(":uid"))
@@ -44,7 +43,7 @@ func GetFolderByUid(c *middleware.Context) Response {
return Json(200, toFolderDto(g, folder))
}
func GetFolderById(c *middleware.Context) Response {
func GetFolderById(c *m.ReqContext) Response {
s := dashboards.NewFolderService(c.OrgId, c.SignedInUser)
folder, err := s.GetFolderById(c.ParamsInt64(":id"))
if err != nil {
@@ -55,7 +54,7 @@ func GetFolderById(c *middleware.Context) Response {
return Json(200, toFolderDto(g, folder))
}
func CreateFolder(c *middleware.Context, cmd m.CreateFolderCommand) Response {
func CreateFolder(c *m.ReqContext, cmd m.CreateFolderCommand) Response {
s := dashboards.NewFolderService(c.OrgId, c.SignedInUser)
err := s.CreateFolder(&cmd)
if err != nil {
@@ -66,7 +65,7 @@ func CreateFolder(c *middleware.Context, cmd m.CreateFolderCommand) Response {
return Json(200, toFolderDto(g, cmd.Result))
}
func UpdateFolder(c *middleware.Context, cmd m.UpdateFolderCommand) Response {
func UpdateFolder(c *m.ReqContext, cmd m.UpdateFolderCommand) Response {
s := dashboards.NewFolderService(c.OrgId, c.SignedInUser)
err := s.UpdateFolder(c.Params(":uid"), &cmd)
if err != nil {
@@ -77,7 +76,7 @@ func UpdateFolder(c *middleware.Context, cmd m.UpdateFolderCommand) Response {
return Json(200, toFolderDto(g, cmd.Result))
}
func DeleteFolder(c *middleware.Context) Response {
func DeleteFolder(c *m.ReqContext) Response {
s := dashboards.NewFolderService(c.OrgId, c.SignedInUser)
f, err := s.DeleteFolder(c.Params(":uid"))
if err != nil {
+2 -3
View File
@@ -5,13 +5,12 @@ import (
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/middleware"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/guardian"
)
func GetFolderPermissionList(c *middleware.Context) Response {
func GetFolderPermissionList(c *m.ReqContext) Response {
s := dashboards.NewFolderService(c.OrgId, c.SignedInUser)
folder, err := s.GetFolderByUid(c.Params(":uid"))
@@ -42,7 +41,7 @@ func GetFolderPermissionList(c *middleware.Context) Response {
return Json(200, acl)
}
func UpdateFolderPermissions(c *middleware.Context, apiCmd dtos.UpdateDashboardAclCommand) Response {
func UpdateFolderPermissions(c *m.ReqContext, apiCmd dtos.UpdateDashboardAclCommand) Response {
s := dashboards.NewFolderService(c.OrgId, c.SignedInUser)
folder, err := s.GetFolderByUid(c.Params(":uid"))
+1 -2
View File
@@ -6,7 +6,6 @@ import (
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/middleware"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/guardian"
@@ -227,7 +226,7 @@ func updateFolderPermissionScenario(desc string, url string, routePattern string
sc := setupScenarioContext(url)
sc.defaultHandler = wrap(func(c *middleware.Context) Response {
sc.defaultHandler = wrap(func(c *m.ReqContext) Response {
sc.context = c
sc.context.OrgId = TestOrgID
sc.context.UserId = TestUserID
+16 -19
View File
@@ -7,11 +7,8 @@ import (
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/middleware"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/dashboards"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/dashboards"
. "github.com/smartystreets/goconvey/convey"
)
@@ -155,7 +152,7 @@ func createFolderScenario(desc string, url string, routePattern string, mock *fa
defer bus.ClearBusHandlers()
sc := setupScenarioContext(url)
sc.defaultHandler = wrap(func(c *middleware.Context) Response {
sc.defaultHandler = wrap(func(c *m.ReqContext) Response {
sc.context = c
sc.context.SignedInUser = &m.SignedInUser{OrgId: TestOrgID, UserId: TestUserID}
@@ -184,7 +181,7 @@ func updateFolderScenario(desc string, url string, routePattern string, mock *fa
defer bus.ClearBusHandlers()
sc := setupScenarioContext(url)
sc.defaultHandler = wrap(func(c *middleware.Context) Response {
sc.defaultHandler = wrap(func(c *m.ReqContext) Response {
sc.context = c
sc.context.SignedInUser = &m.SignedInUser{OrgId: TestOrgID, UserId: TestUserID}
@@ -205,50 +202,50 @@ func updateFolderScenario(desc string, url string, routePattern string, mock *fa
}
type fakeFolderService struct {
GetFoldersResult []*models.Folder
GetFoldersResult []*m.Folder
GetFoldersError error
GetFolderByUidResult *models.Folder
GetFolderByUidResult *m.Folder
GetFolderByUidError error
GetFolderByIdResult *models.Folder
GetFolderByIdResult *m.Folder
GetFolderByIdError error
CreateFolderResult *models.Folder
CreateFolderResult *m.Folder
CreateFolderError error
UpdateFolderResult *models.Folder
UpdateFolderResult *m.Folder
UpdateFolderError error
DeleteFolderResult *models.Folder
DeleteFolderResult *m.Folder
DeleteFolderError error
DeletedFolderUids []string
}
func (s *fakeFolderService) GetFolders(limit int) ([]*models.Folder, error) {
func (s *fakeFolderService) GetFolders(limit int) ([]*m.Folder, error) {
return s.GetFoldersResult, s.GetFoldersError
}
func (s *fakeFolderService) GetFolderById(id int64) (*models.Folder, error) {
func (s *fakeFolderService) GetFolderById(id int64) (*m.Folder, error) {
return s.GetFolderByIdResult, s.GetFolderByIdError
}
func (s *fakeFolderService) GetFolderByUid(uid string) (*models.Folder, error) {
func (s *fakeFolderService) GetFolderByUid(uid string) (*m.Folder, error) {
return s.GetFolderByUidResult, s.GetFolderByUidError
}
func (s *fakeFolderService) CreateFolder(cmd *models.CreateFolderCommand) error {
func (s *fakeFolderService) CreateFolder(cmd *m.CreateFolderCommand) error {
cmd.Result = s.CreateFolderResult
return s.CreateFolderError
}
func (s *fakeFolderService) UpdateFolder(existingUid string, cmd *models.UpdateFolderCommand) error {
func (s *fakeFolderService) UpdateFolder(existingUid string, cmd *m.UpdateFolderCommand) error {
cmd.Result = s.UpdateFolderResult
return s.UpdateFolderError
}
func (s *fakeFolderService) DeleteFolder(uid string) (*models.Folder, error) {
func (s *fakeFolderService) DeleteFolder(uid string) (*m.Folder, error) {
s.DeletedFolderUids = append(s.DeletedFolderUids, uid)
return s.DeleteFolderResult, s.DeleteFolderError
}
func mockFolderService(mock *fakeFolderService) {
dashboards.NewFolderService = func(orgId int64, user *models.SignedInUser) dashboards.FolderService {
dashboards.NewFolderService = func(orgId int64, user *m.SignedInUser) dashboards.FolderService {
return mock
}
}
+2 -3
View File
@@ -5,14 +5,13 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/log"
"github.com/grafana/grafana/pkg/middleware"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util"
)
func getFrontendSettingsMap(c *middleware.Context) (map[string]interface{}, error) {
func getFrontendSettingsMap(c *m.ReqContext) (map[string]interface{}, error) {
orgDataSources := make([]*m.DataSource, 0)
if c.OrgId != 0 {
@@ -180,7 +179,7 @@ func getPanelSort(id string) int {
return sort
}
func GetFrontendSettings(c *middleware.Context) {
func GetFrontendSettings(c *m.ReqContext) {
settings, err := getFrontendSettingsMap(c)
if err != nil {
c.JsonApiErr(400, "Failed to get frontend settings", err)
+2 -2
View File
@@ -7,7 +7,7 @@ import (
"net/url"
"time"
"github.com/grafana/grafana/pkg/middleware"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util"
)
@@ -41,7 +41,7 @@ func ReverseProxyGnetReq(proxyPath string) *httputil.ReverseProxy {
return &httputil.ReverseProxy{Director: director}
}
func ProxyGnetRequest(c *middleware.Context) {
func ProxyGnetRequest(c *m.ReqContext) {
proxyPath := c.Params("*")
proxy := ReverseProxyGnetReq(proxyPath)
proxy.Transport = grafanaComProxyTransport
+4 -5
View File
@@ -6,13 +6,12 @@ import (
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/middleware"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/setting"
)
func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
func setIndexViewData(c *m.ReqContext) (*dtos.IndexViewData, error) {
settings, err := getFrontendSettingsMap(c)
if err != nil {
return nil, err
@@ -74,7 +73,7 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
}
if setting.DisableGravatar {
data.User.GravatarUrl = setting.AppSubUrl + "/public/img/transparent.png"
data.User.GravatarUrl = setting.AppSubUrl + "/public/img/user_profile.png"
}
if len(data.User.Name) == 0 {
@@ -299,7 +298,7 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
return &data, nil
}
func Index(c *middleware.Context) {
func Index(c *m.ReqContext) {
if data, err := setIndexViewData(c); err != nil {
c.Handle(500, "Failed to get settings", err)
return
@@ -308,7 +307,7 @@ func Index(c *middleware.Context) {
}
}
func NotFoundHandler(c *middleware.Context) {
func NotFoundHandler(c *m.ReqContext) {
if c.IsApiRequest() {
c.JsonApiErr(404, "Not found", nil)
return
+10 -10
View File
@@ -8,8 +8,8 @@ import (
"github.com/grafana/grafana/pkg/log"
"github.com/grafana/grafana/pkg/login"
"github.com/grafana/grafana/pkg/metrics"
"github.com/grafana/grafana/pkg/middleware"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/session"
"github.com/grafana/grafana/pkg/setting"
)
@@ -17,7 +17,7 @@ const (
VIEW_INDEX = "index"
)
func LoginView(c *middleware.Context) {
func LoginView(c *m.ReqContext) {
viewData, err := setIndexViewData(c)
if err != nil {
c.Handle(500, "Failed to get settings", err)
@@ -53,7 +53,7 @@ func LoginView(c *middleware.Context) {
c.Redirect(setting.AppSubUrl + "/")
}
func tryLoginUsingRememberCookie(c *middleware.Context) bool {
func tryLoginUsingRememberCookie(c *m.ReqContext) bool {
// Check auto-login.
uname := c.GetCookie(setting.CookieUserName)
if len(uname) == 0 {
@@ -87,7 +87,7 @@ func tryLoginUsingRememberCookie(c *middleware.Context) bool {
return true
}
func LoginApiPing(c *middleware.Context) {
func LoginApiPing(c *m.ReqContext) {
if !tryLoginUsingRememberCookie(c) {
c.JsonApiErr(401, "Unauthorized", nil)
return
@@ -96,7 +96,7 @@ func LoginApiPing(c *middleware.Context) {
c.JsonOK("Logged in")
}
func LoginPost(c *middleware.Context, cmd dtos.LoginCommand) Response {
func LoginPost(c *m.ReqContext, cmd dtos.LoginCommand) Response {
if setting.DisableLoginForm {
return ApiError(401, "Login is disabled", nil)
}
@@ -133,7 +133,7 @@ func LoginPost(c *middleware.Context, cmd dtos.LoginCommand) Response {
return Json(200, result)
}
func loginUserWithUser(user *m.User, c *middleware.Context) {
func loginUserWithUser(user *m.User, c *m.ReqContext) {
if user == nil {
log.Error(3, "User login with nil user")
}
@@ -146,13 +146,13 @@ func loginUserWithUser(user *m.User, c *middleware.Context) {
c.SetSuperSecureCookie(user.Rands+user.Password, setting.CookieRememberName, user.Login, days, setting.AppSubUrl+"/")
}
c.Session.RegenerateId(c)
c.Session.Set(middleware.SESS_KEY_USERID, user.Id)
c.Session.RegenerateId(c.Context)
c.Session.Set(session.SESS_KEY_USERID, user.Id)
}
func Logout(c *middleware.Context) {
func Logout(c *m.ReqContext) {
c.SetCookie(setting.CookieUserName, "", -1, setting.AppSubUrl+"/")
c.SetCookie(setting.CookieRememberName, "", -1, setting.AppSubUrl+"/")
c.Session.Destory(c)
c.Session.Destory(c.Context)
c.Redirect(setting.AppSubUrl + "/login")
}
+7 -6
View File
@@ -17,8 +17,9 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/log"
"github.com/grafana/grafana/pkg/metrics"
"github.com/grafana/grafana/pkg/middleware"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/quota"
"github.com/grafana/grafana/pkg/services/session"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/social"
)
@@ -38,7 +39,7 @@ func GenStateString() string {
return base64.URLEncoding.EncodeToString(rnd)
}
func OAuthLogin(ctx *middleware.Context) {
func OAuthLogin(ctx *m.ReqContext) {
if setting.OAuthService == nil {
ctx.Handle(404, "OAuth not enabled", nil)
return
@@ -62,7 +63,7 @@ func OAuthLogin(ctx *middleware.Context) {
code := ctx.Query("code")
if code == "" {
state := GenStateString()
ctx.Session.Set(middleware.SESS_KEY_OAUTH_STATE, state)
ctx.Session.Set(session.SESS_KEY_OAUTH_STATE, state)
if setting.OAuthService.OAuthInfos[name].HostedDomain == "" {
ctx.Redirect(connect.AuthCodeURL(state, oauth2.AccessTypeOnline))
} else {
@@ -71,7 +72,7 @@ func OAuthLogin(ctx *middleware.Context) {
return
}
savedState, ok := ctx.Session.Get(middleware.SESS_KEY_OAUTH_STATE).(string)
savedState, ok := ctx.Session.Get(session.SESS_KEY_OAUTH_STATE).(string)
if !ok {
ctx.Handle(500, "login.OAuthLogin(missing saved state)", nil)
return
@@ -167,7 +168,7 @@ func OAuthLogin(ctx *middleware.Context) {
redirectWithError(ctx, ErrSignUpNotAllowed)
return
}
limitReached, err := middleware.QuotaReached(ctx, "user")
limitReached, err := quota.QuotaReached(ctx, "user")
if err != nil {
ctx.Handle(500, "Failed to get user quota", err)
return
@@ -208,7 +209,7 @@ func OAuthLogin(ctx *middleware.Context) {
ctx.Redirect(setting.AppSubUrl + "/")
}
func redirectWithError(ctx *middleware.Context, err error, v ...interface{}) {
func redirectWithError(ctx *m.ReqContext, err error, v ...interface{}) {
ctx.Logger.Error(err.Error(), v...)
ctx.Session.Set("loginError", err.Error())
ctx.Redirect(setting.AppSubUrl + "/login")
+9 -10
View File
@@ -6,15 +6,14 @@ import (
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/middleware"
"github.com/grafana/grafana/pkg/models"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/tsdb"
"github.com/grafana/grafana/pkg/tsdb/testdata"
"github.com/grafana/grafana/pkg/util"
)
// POST /api/tsdb/query
func QueryMetrics(c *middleware.Context, reqDto dtos.MetricRequest) Response {
func QueryMetrics(c *m.ReqContext, reqDto dtos.MetricRequest) Response {
timeRange := tsdb.NewTimeRange(reqDto.From, reqDto.To)
if len(reqDto.Queries) == 0 {
@@ -26,7 +25,7 @@ func QueryMetrics(c *middleware.Context, reqDto dtos.MetricRequest) Response {
return ApiError(400, "Query missing datasourceId", nil)
}
dsQuery := models.GetDataSourceByIdQuery{Id: dsId, OrgId: c.OrgId}
dsQuery := m.GetDataSourceByIdQuery{Id: dsId, OrgId: c.OrgId}
if err := bus.Dispatch(&dsQuery); err != nil {
return ApiError(500, "failed to fetch data source", err)
}
@@ -61,7 +60,7 @@ func QueryMetrics(c *middleware.Context, reqDto dtos.MetricRequest) Response {
}
// GET /api/tsdb/testdata/scenarios
func GetTestDataScenarios(c *middleware.Context) Response {
func GetTestDataScenarios(c *m.ReqContext) Response {
result := make([]interface{}, 0)
for _, scenario := range testdata.ScenarioRegistry {
@@ -77,14 +76,14 @@ func GetTestDataScenarios(c *middleware.Context) Response {
}
// Genereates a index out of range error
func GenerateError(c *middleware.Context) Response {
func GenerateError(c *m.ReqContext) Response {
var array []string
return Json(200, array[20])
}
// GET /api/tsdb/testdata/gensql
func GenerateSqlTestData(c *middleware.Context) Response {
if err := bus.Dispatch(&models.InsertSqlTestDataCommand{}); err != nil {
func GenerateSqlTestData(c *m.ReqContext) Response {
if err := bus.Dispatch(&m.InsertSqlTestDataCommand{}); err != nil {
return ApiError(500, "Failed to insert test data", err)
}
@@ -92,7 +91,7 @@ func GenerateSqlTestData(c *middleware.Context) Response {
}
// GET /api/tsdb/testdata/random-walk
func GetTestDataRandomWalk(c *middleware.Context) Response {
func GetTestDataRandomWalk(c *m.ReqContext) Response {
from := c.Query("from")
to := c.Query("to")
intervalMs := c.QueryInt64("intervalMs")
@@ -100,7 +99,7 @@ func GetTestDataRandomWalk(c *middleware.Context) Response {
timeRange := tsdb.NewTimeRange(from, to)
request := &tsdb.TsdbQuery{TimeRange: timeRange}
dsInfo := &models.DataSource{Type: "grafana-testdata-datasource"}
dsInfo := &m.DataSource{Type: "grafana-testdata-datasource"}
request.Queries = append(request.Queries, &tsdb.Query{
RefId: "A",
IntervalMs: intervalMs,
+10 -11
View File
@@ -4,24 +4,23 @@ import (
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/metrics"
"github.com/grafana/grafana/pkg/middleware"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util"
)
// GET /api/org
func GetOrgCurrent(c *middleware.Context) Response {
func GetOrgCurrent(c *m.ReqContext) Response {
return getOrgHelper(c.OrgId)
}
// GET /api/orgs/:orgId
func GetOrgById(c *middleware.Context) Response {
func GetOrgById(c *m.ReqContext) Response {
return getOrgHelper(c.ParamsInt64(":orgId"))
}
// Get /api/orgs/name/:name
func GetOrgByName(c *middleware.Context) Response {
func GetOrgByName(c *m.ReqContext) Response {
query := m.GetOrgByNameQuery{Name: c.Params(":name")}
if err := bus.Dispatch(&query); err != nil {
if err == m.ErrOrgNotFound {
@@ -76,7 +75,7 @@ func getOrgHelper(orgId int64) Response {
}
// POST /api/orgs
func CreateOrg(c *middleware.Context, cmd m.CreateOrgCommand) Response {
func CreateOrg(c *m.ReqContext, cmd m.CreateOrgCommand) Response {
if !c.IsSignedIn || (!setting.AllowUserOrgCreate && !c.IsGrafanaAdmin) {
return ApiError(403, "Access denied", nil)
}
@@ -98,12 +97,12 @@ func CreateOrg(c *middleware.Context, cmd m.CreateOrgCommand) Response {
}
// PUT /api/org
func UpdateOrgCurrent(c *middleware.Context, form dtos.UpdateOrgForm) Response {
func UpdateOrgCurrent(c *m.ReqContext, form dtos.UpdateOrgForm) Response {
return updateOrgHelper(form, c.OrgId)
}
// PUT /api/orgs/:orgId
func UpdateOrg(c *middleware.Context, form dtos.UpdateOrgForm) Response {
func UpdateOrg(c *m.ReqContext, form dtos.UpdateOrgForm) Response {
return updateOrgHelper(form, c.ParamsInt64(":orgId"))
}
@@ -120,12 +119,12 @@ func updateOrgHelper(form dtos.UpdateOrgForm, orgId int64) Response {
}
// PUT /api/org/address
func UpdateOrgAddressCurrent(c *middleware.Context, form dtos.UpdateOrgAddressForm) Response {
func UpdateOrgAddressCurrent(c *m.ReqContext, form dtos.UpdateOrgAddressForm) Response {
return updateOrgAddressHelper(form, c.OrgId)
}
// PUT /api/orgs/:orgId/address
func UpdateOrgAddress(c *middleware.Context, form dtos.UpdateOrgAddressForm) Response {
func UpdateOrgAddress(c *m.ReqContext, form dtos.UpdateOrgAddressForm) Response {
return updateOrgAddressHelper(form, c.ParamsInt64(":orgId"))
}
@@ -150,7 +149,7 @@ func updateOrgAddressHelper(form dtos.UpdateOrgAddressForm, orgId int64) Respons
}
// GET /api/orgs/:orgId
func DeleteOrgById(c *middleware.Context) Response {
func DeleteOrgById(c *m.ReqContext) Response {
if err := bus.Dispatch(&m.DeleteOrgCommand{Id: c.ParamsInt64(":orgId")}); err != nil {
if err == m.ErrOrgNotFound {
return ApiError(404, "Failed to delete organization. ID not found", nil)
@@ -160,7 +159,7 @@ func DeleteOrgById(c *middleware.Context) Response {
return ApiSuccess("Organization deleted")
}
func SearchOrgs(c *middleware.Context) Response {
func SearchOrgs(c *m.ReqContext) Response {
query := m.SearchOrgsQuery{
Query: c.Query("query"),
Name: c.Query("name"),
+6 -7
View File
@@ -7,13 +7,12 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/events"
"github.com/grafana/grafana/pkg/metrics"
"github.com/grafana/grafana/pkg/middleware"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util"
)
func GetPendingOrgInvites(c *middleware.Context) Response {
func GetPendingOrgInvites(c *m.ReqContext) Response {
query := m.GetTempUsersQuery{OrgId: c.OrgId, Status: m.TmpUserInvitePending}
if err := bus.Dispatch(&query); err != nil {
@@ -27,7 +26,7 @@ func GetPendingOrgInvites(c *middleware.Context) Response {
return Json(200, query.Result)
}
func AddOrgInvite(c *middleware.Context, inviteDto dtos.AddInviteForm) Response {
func AddOrgInvite(c *m.ReqContext, inviteDto dtos.AddInviteForm) Response {
if !inviteDto.Role.IsValid() {
return ApiError(400, "Invalid role specified", nil)
}
@@ -89,7 +88,7 @@ func AddOrgInvite(c *middleware.Context, inviteDto dtos.AddInviteForm) Response
return ApiSuccess(fmt.Sprintf("Created invite for %s", inviteDto.LoginOrEmail))
}
func inviteExistingUserToOrg(c *middleware.Context, user *m.User, inviteDto *dtos.AddInviteForm) Response {
func inviteExistingUserToOrg(c *m.ReqContext, user *m.User, inviteDto *dtos.AddInviteForm) Response {
// user exists, add org role
createOrgUserCmd := m.AddOrgUserCommand{OrgId: c.OrgId, UserId: user.Id, Role: inviteDto.Role}
if err := bus.Dispatch(&createOrgUserCmd); err != nil {
@@ -119,7 +118,7 @@ func inviteExistingUserToOrg(c *middleware.Context, user *m.User, inviteDto *dto
}
}
func RevokeInvite(c *middleware.Context) Response {
func RevokeInvite(c *m.ReqContext) Response {
if ok, rsp := updateTempUserStatus(c.Params(":code"), m.TmpUserRevoked); !ok {
return rsp
}
@@ -127,7 +126,7 @@ func RevokeInvite(c *middleware.Context) Response {
return ApiSuccess("Invite revoked")
}
func GetInviteInfoByCode(c *middleware.Context) Response {
func GetInviteInfoByCode(c *m.ReqContext) Response {
query := m.GetTempUserByCodeQuery{Code: c.Params(":code")}
if err := bus.Dispatch(&query); err != nil {
@@ -147,7 +146,7 @@ func GetInviteInfoByCode(c *middleware.Context) Response {
})
}
func CompleteInvite(c *middleware.Context, completeInvite dtos.CompleteInviteForm) Response {
func CompleteInvite(c *m.ReqContext, completeInvite dtos.CompleteInviteForm) Response {
query := m.GetTempUserByCodeQuery{Code: completeInvite.InviteCode}
if err := bus.Dispatch(&query); err != nil {
+8 -9
View File
@@ -3,18 +3,17 @@ package api
import (
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/middleware"
m "github.com/grafana/grafana/pkg/models"
)
// POST /api/org/users
func AddOrgUserToCurrentOrg(c *middleware.Context, cmd m.AddOrgUserCommand) Response {
func AddOrgUserToCurrentOrg(c *m.ReqContext, cmd m.AddOrgUserCommand) Response {
cmd.OrgId = c.OrgId
return addOrgUserHelper(cmd)
}
// POST /api/orgs/:orgId/users
func AddOrgUser(c *middleware.Context, cmd m.AddOrgUserCommand) Response {
func AddOrgUser(c *m.ReqContext, cmd m.AddOrgUserCommand) Response {
cmd.OrgId = c.ParamsInt64(":orgId")
return addOrgUserHelper(cmd)
}
@@ -45,12 +44,12 @@ func addOrgUserHelper(cmd m.AddOrgUserCommand) Response {
}
// GET /api/org/users
func GetOrgUsersForCurrentOrg(c *middleware.Context) Response {
func GetOrgUsersForCurrentOrg(c *m.ReqContext) Response {
return getOrgUsersHelper(c.OrgId, c.Params("query"), c.ParamsInt("limit"))
}
// GET /api/orgs/:orgId/users
func GetOrgUsers(c *middleware.Context) Response {
func GetOrgUsers(c *m.ReqContext) Response {
return getOrgUsersHelper(c.ParamsInt64(":orgId"), "", 0)
}
@@ -73,14 +72,14 @@ func getOrgUsersHelper(orgId int64, query string, limit int) Response {
}
// PATCH /api/org/users/:userId
func UpdateOrgUserForCurrentOrg(c *middleware.Context, cmd m.UpdateOrgUserCommand) Response {
func UpdateOrgUserForCurrentOrg(c *m.ReqContext, cmd m.UpdateOrgUserCommand) Response {
cmd.OrgId = c.OrgId
cmd.UserId = c.ParamsInt64(":userId")
return updateOrgUserHelper(cmd)
}
// PATCH /api/orgs/:orgId/users/:userId
func UpdateOrgUser(c *middleware.Context, cmd m.UpdateOrgUserCommand) Response {
func UpdateOrgUser(c *m.ReqContext, cmd m.UpdateOrgUserCommand) Response {
cmd.OrgId = c.ParamsInt64(":orgId")
cmd.UserId = c.ParamsInt64(":userId")
return updateOrgUserHelper(cmd)
@@ -102,13 +101,13 @@ func updateOrgUserHelper(cmd m.UpdateOrgUserCommand) Response {
}
// DELETE /api/org/users/:userId
func RemoveOrgUserForCurrentOrg(c *middleware.Context) Response {
func RemoveOrgUserForCurrentOrg(c *m.ReqContext) Response {
userId := c.ParamsInt64(":userId")
return removeOrgUserHelper(c.OrgId, userId)
}
// DELETE /api/orgs/:orgId/users/:userId
func RemoveOrgUser(c *middleware.Context) Response {
func RemoveOrgUser(c *m.ReqContext) Response {
userId := c.ParamsInt64(":userId")
orgId := c.ParamsInt64(":orgId")
return removeOrgUserHelper(orgId, userId)
+2 -3
View File
@@ -3,12 +3,11 @@ package api
import (
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/middleware"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/util"
)
func SendResetPasswordEmail(c *middleware.Context, form dtos.SendResetPasswordEmailForm) Response {
func SendResetPasswordEmail(c *m.ReqContext, form dtos.SendResetPasswordEmailForm) Response {
userQuery := m.GetUserByLoginQuery{LoginOrEmail: form.UserOrEmail}
if err := bus.Dispatch(&userQuery); err != nil {
@@ -24,7 +23,7 @@ func SendResetPasswordEmail(c *middleware.Context, form dtos.SendResetPasswordEm
return ApiSuccess("Email sent")
}
func ResetPassword(c *middleware.Context, form dtos.ResetUserPasswordForm) Response {
func ResetPassword(c *m.ReqContext, form dtos.ResetUserPasswordForm) Response {
query := m.ValidateResetPasswordCodeQuery{Code: form.Code}
if err := bus.Dispatch(&query); err != nil {
+8 -9
View File
@@ -3,11 +3,10 @@ package api
import (
"github.com/grafana/grafana/pkg/bus"
_ "github.com/grafana/grafana/pkg/log"
"github.com/grafana/grafana/pkg/middleware"
m "github.com/grafana/grafana/pkg/models"
)
func ValidateOrgPlaylist(c *middleware.Context) {
func ValidateOrgPlaylist(c *m.ReqContext) {
id := c.ParamsInt64(":id")
query := m.GetPlaylistByIdQuery{Id: id}
err := bus.Dispatch(&query)
@@ -40,7 +39,7 @@ func ValidateOrgPlaylist(c *middleware.Context) {
}
}
func SearchPlaylists(c *middleware.Context) Response {
func SearchPlaylists(c *m.ReqContext) Response {
query := c.Query("query")
limit := c.QueryInt("limit")
@@ -62,7 +61,7 @@ func SearchPlaylists(c *middleware.Context) Response {
return Json(200, searchQuery.Result)
}
func GetPlaylist(c *middleware.Context) Response {
func GetPlaylist(c *m.ReqContext) Response {
id := c.ParamsInt64(":id")
cmd := m.GetPlaylistByIdQuery{Id: id}
@@ -115,7 +114,7 @@ func LoadPlaylistItems(id int64) ([]m.PlaylistItem, error) {
return *itemQuery.Result, nil
}
func GetPlaylistItems(c *middleware.Context) Response {
func GetPlaylistItems(c *m.ReqContext) Response {
id := c.ParamsInt64(":id")
playlistDTOs, err := LoadPlaylistItemDTOs(id)
@@ -127,7 +126,7 @@ func GetPlaylistItems(c *middleware.Context) Response {
return Json(200, playlistDTOs)
}
func GetPlaylistDashboards(c *middleware.Context) Response {
func GetPlaylistDashboards(c *m.ReqContext) Response {
playlistId := c.ParamsInt64(":id")
playlists, err := LoadPlaylistDashboards(c.OrgId, c.SignedInUser, playlistId)
@@ -138,7 +137,7 @@ func GetPlaylistDashboards(c *middleware.Context) Response {
return Json(200, playlists)
}
func DeletePlaylist(c *middleware.Context) Response {
func DeletePlaylist(c *m.ReqContext) Response {
id := c.ParamsInt64(":id")
cmd := m.DeletePlaylistCommand{Id: id, OrgId: c.OrgId}
@@ -149,7 +148,7 @@ func DeletePlaylist(c *middleware.Context) Response {
return Json(200, "")
}
func CreatePlaylist(c *middleware.Context, cmd m.CreatePlaylistCommand) Response {
func CreatePlaylist(c *m.ReqContext, cmd m.CreatePlaylistCommand) Response {
cmd.OrgId = c.OrgId
if err := bus.Dispatch(&cmd); err != nil {
@@ -159,7 +158,7 @@ func CreatePlaylist(c *middleware.Context, cmd m.CreatePlaylistCommand) Response
return Json(200, cmd.Result)
}
func UpdatePlaylist(c *middleware.Context, cmd m.UpdatePlaylistCommand) Response {
func UpdatePlaylist(c *m.ReqContext, cmd m.UpdatePlaylistCommand) Response {
cmd.OrgId = c.OrgId
if err := bus.Dispatch(&cmd); err != nil {
+11 -6
View File
@@ -18,7 +18,6 @@ import (
"github.com/opentracing/opentracing-go"
"github.com/grafana/grafana/pkg/log"
"github.com/grafana/grafana/pkg/middleware"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/setting"
@@ -42,14 +41,14 @@ type jwtToken struct {
type DataSourceProxy struct {
ds *m.DataSource
ctx *middleware.Context
ctx *m.ReqContext
targetUrl *url.URL
proxyPath string
route *plugins.AppPluginRoute
plugin *plugins.DataSourcePlugin
}
func NewDataSourceProxy(ds *m.DataSource, plugin *plugins.DataSourcePlugin, ctx *middleware.Context, proxyPath string) *DataSourceProxy {
func NewDataSourceProxy(ds *m.DataSource, plugin *plugins.DataSourcePlugin, ctx *m.ReqContext, proxyPath string) *DataSourceProxy {
targetUrl, _ := url.Parse(ds.Url)
return &DataSourceProxy{
@@ -190,8 +189,14 @@ func (proxy *DataSourceProxy) validateRequest() error {
}
if proxy.ds.Type == m.DS_PROMETHEUS {
if proxy.ctx.Req.Request.Method != http.MethodGet || !strings.HasPrefix(proxy.proxyPath, "api/") {
return errors.New("GET is only allowed on proxied Prometheus datasource")
if proxy.ctx.Req.Request.Method == "DELETE" {
return errors.New("Deletes not allowed on proxied Prometheus datasource")
}
if proxy.ctx.Req.Request.Method == "PUT" {
return errors.New("Puts not allowed on proxied Prometheus datasource")
}
if proxy.ctx.Req.Request.Method == "POST" && !(proxy.proxyPath == "api/v1/query" || proxy.proxyPath == "api/v1/query_range") {
return errors.New("Posts not allowed on proxied Prometheus datasource except on /query and /query_range")
}
}
@@ -255,7 +260,7 @@ func (proxy *DataSourceProxy) logRequest() {
"body", body)
}
func checkWhiteList(c *middleware.Context, host string) bool {
func checkWhiteList(c *m.ReqContext, host string) bool {
if host != "" && len(setting.DataProxyWhiteList) > 0 {
if _, exists := setting.DataProxyWhiteList[host]; !exists {
c.JsonApiErr(403, "Data proxy hostname and ip are not included in whitelist", nil)
+5 -6
View File
@@ -8,7 +8,6 @@ import (
macaron "gopkg.in/macaron.v1"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/middleware"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/setting"
@@ -61,7 +60,7 @@ func TestDSRouteRule(t *testing.T) {
}
req, _ := http.NewRequest("GET", "http://localhost/asd", nil)
ctx := &middleware.Context{
ctx := &m.ReqContext{
Context: &macaron.Context{
Req: macaron.Request{Request: req},
},
@@ -104,7 +103,7 @@ func TestDSRouteRule(t *testing.T) {
Convey("When proxying graphite", func() {
plugin := &plugins.DataSourcePlugin{}
ds := &m.DataSource{Url: "htttp://graphite:8080", Type: m.DS_GRAPHITE}
ctx := &middleware.Context{}
ctx := &m.ReqContext{}
proxy := NewDataSourceProxy(ds, plugin, ctx, "/render")
@@ -130,7 +129,7 @@ func TestDSRouteRule(t *testing.T) {
Password: "password",
}
ctx := &middleware.Context{}
ctx := &m.ReqContext{}
proxy := NewDataSourceProxy(ds, plugin, ctx, "")
requestUrl, _ := url.Parse("http://grafana.com/sub")
@@ -160,7 +159,7 @@ func TestDSRouteRule(t *testing.T) {
JsonData: json,
}
ctx := &middleware.Context{}
ctx := &m.ReqContext{}
proxy := NewDataSourceProxy(ds, plugin, ctx, "")
requestUrl, _ := url.Parse("http://grafana.com/sub")
@@ -186,7 +185,7 @@ func TestDSRouteRule(t *testing.T) {
JsonData: json,
}
ctx := &middleware.Context{}
ctx := &m.ReqContext{}
proxy := NewDataSourceProxy(ds, plugin, ctx, "")
requestUrl, _ := url.Parse("http://grafana.com/sub")
+1 -2
View File
@@ -9,7 +9,6 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/log"
"github.com/grafana/grafana/pkg/middleware"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/util"
@@ -38,7 +37,7 @@ func getHeaders(route *plugins.AppPluginRoute, orgId int64, appId string) (http.
return result, err
}
func NewApiPluginProxy(ctx *middleware.Context, proxyPath string, route *plugins.AppPluginRoute, appId string) *httputil.ReverseProxy {
func NewApiPluginProxy(ctx *m.ReqContext, proxyPath string, route *plugins.AppPluginRoute, appId string) *httputil.ReverseProxy {
targetUrl, _ := url.Parse(route.Url)
director := func(req *http.Request) {
+6 -7
View File
@@ -5,13 +5,12 @@ import (
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/middleware"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/setting"
)
func GetPluginList(c *middleware.Context) Response {
func GetPluginList(c *m.ReqContext) Response {
typeFilter := c.Query("type")
enabledFilter := c.Query("enabled")
embeddedFilter := c.Query("embedded")
@@ -79,7 +78,7 @@ func GetPluginList(c *middleware.Context) Response {
return Json(200, result)
}
func GetPluginSettingById(c *middleware.Context) Response {
func GetPluginSettingById(c *m.ReqContext) Response {
pluginId := c.Params(":pluginId")
if def, exists := plugins.Plugins[pluginId]; !exists {
@@ -116,7 +115,7 @@ func GetPluginSettingById(c *middleware.Context) Response {
}
}
func UpdatePluginSetting(c *middleware.Context, cmd m.UpdatePluginSettingCmd) Response {
func UpdatePluginSetting(c *m.ReqContext, cmd m.UpdatePluginSettingCmd) Response {
pluginId := c.Params(":pluginId")
cmd.OrgId = c.OrgId
@@ -133,7 +132,7 @@ func UpdatePluginSetting(c *middleware.Context, cmd m.UpdatePluginSettingCmd) Re
return ApiSuccess("Plugin settings updated")
}
func GetPluginDashboards(c *middleware.Context) Response {
func GetPluginDashboards(c *m.ReqContext) Response {
pluginId := c.Params(":pluginId")
if list, err := plugins.GetPluginDashboards(c.OrgId, pluginId); err != nil {
@@ -147,7 +146,7 @@ func GetPluginDashboards(c *middleware.Context) Response {
}
}
func GetPluginMarkdown(c *middleware.Context) Response {
func GetPluginMarkdown(c *m.ReqContext) Response {
pluginId := c.Params(":pluginId")
name := c.Params(":name")
@@ -164,7 +163,7 @@ func GetPluginMarkdown(c *middleware.Context) Response {
}
}
func ImportDashboard(c *middleware.Context, apiCmd dtos.ImportDashboardCommand) Response {
func ImportDashboard(c *m.ReqContext, apiCmd dtos.ImportDashboardCommand) Response {
cmd := plugins.ImportDashboardCommand{
OrgId: c.OrgId,
+5 -6
View File
@@ -3,12 +3,11 @@ package api
import (
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/middleware"
m "github.com/grafana/grafana/pkg/models"
)
// POST /api/preferences/set-home-dash
func SetHomeDashboard(c *middleware.Context, cmd m.SavePreferencesCommand) Response {
func SetHomeDashboard(c *m.ReqContext, cmd m.SavePreferencesCommand) Response {
cmd.UserId = c.UserId
cmd.OrgId = c.OrgId
@@ -21,7 +20,7 @@ func SetHomeDashboard(c *middleware.Context, cmd m.SavePreferencesCommand) Respo
}
// GET /api/user/preferences
func GetUserPreferences(c *middleware.Context) Response {
func GetUserPreferences(c *m.ReqContext) Response {
return getPreferencesFor(c.OrgId, c.UserId)
}
@@ -42,7 +41,7 @@ func getPreferencesFor(orgId int64, userId int64) Response {
}
// PUT /api/user/preferences
func UpdateUserPreferences(c *middleware.Context, dtoCmd dtos.UpdatePrefsCmd) Response {
func UpdateUserPreferences(c *m.ReqContext, dtoCmd dtos.UpdatePrefsCmd) Response {
return updatePreferencesFor(c.OrgId, c.UserId, &dtoCmd)
}
@@ -63,11 +62,11 @@ func updatePreferencesFor(orgId int64, userId int64, dtoCmd *dtos.UpdatePrefsCmd
}
// GET /api/org/preferences
func GetOrgPreferences(c *middleware.Context) Response {
func GetOrgPreferences(c *m.ReqContext) Response {
return getPreferencesFor(c.OrgId, 0)
}
// PUT /api/org/preferences
func UpdateOrgPreferences(c *middleware.Context, dtoCmd dtos.UpdatePrefsCmd) Response {
func UpdateOrgPreferences(c *m.ReqContext, dtoCmd dtos.UpdatePrefsCmd) Response {
return updatePreferencesFor(c.OrgId, 0, &dtoCmd)
}
+4 -5
View File
@@ -2,12 +2,11 @@ package api
import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/middleware"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting"
)
func GetOrgQuotas(c *middleware.Context) Response {
func GetOrgQuotas(c *m.ReqContext) Response {
if !setting.Quota.Enabled {
return ApiError(404, "Quotas not enabled", nil)
}
@@ -20,7 +19,7 @@ func GetOrgQuotas(c *middleware.Context) Response {
return Json(200, query.Result)
}
func UpdateOrgQuota(c *middleware.Context, cmd m.UpdateOrgQuotaCmd) Response {
func UpdateOrgQuota(c *m.ReqContext, cmd m.UpdateOrgQuotaCmd) Response {
if !setting.Quota.Enabled {
return ApiError(404, "Quotas not enabled", nil)
}
@@ -37,7 +36,7 @@ func UpdateOrgQuota(c *middleware.Context, cmd m.UpdateOrgQuotaCmd) Response {
return ApiSuccess("Organization quota updated")
}
func GetUserQuotas(c *middleware.Context) Response {
func GetUserQuotas(c *m.ReqContext) Response {
if !setting.Quota.Enabled {
return ApiError(404, "Quotas not enabled", nil)
}
@@ -50,7 +49,7 @@ func GetUserQuotas(c *middleware.Context) Response {
return Json(200, query.Result)
}
func UpdateUserQuota(c *middleware.Context, cmd m.UpdateUserQuotaCmd) Response {
func UpdateUserQuota(c *m.ReqContext, cmd m.UpdateUserQuotaCmd) Response {
if !setting.Quota.Enabled {
return ApiError(404, "Quotas not enabled", nil)
}
+2 -2
View File
@@ -5,11 +5,11 @@ import (
"net/http"
"github.com/grafana/grafana/pkg/components/renderer"
"github.com/grafana/grafana/pkg/middleware"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/util"
)
func RenderToPng(c *middleware.Context) {
func RenderToPng(c *m.ReqContext) {
queryReader, err := util.NewUrlQueryReader(c.Req.URL)
if err != nil {
c.Handle(400, "Render parameters error", err)
+4 -5
View File
@@ -5,25 +5,24 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/metrics"
"github.com/grafana/grafana/pkg/middleware"
"github.com/grafana/grafana/pkg/models"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/search"
)
func Search(c *middleware.Context) {
func Search(c *m.ReqContext) {
query := c.Query("query")
tags := c.QueryStrings("tag")
starred := c.Query("starred")
limit := c.QueryInt("limit")
dashboardType := c.Query("type")
permission := models.PERMISSION_VIEW
permission := m.PERMISSION_VIEW
if limit == 0 {
limit = 1000
}
if c.Query("permission") == "Edit" {
permission = models.PERMISSION_EDIT
permission = m.PERMISSION_EDIT
}
dbids := make([]int64, 0)
+3 -4
View File
@@ -5,14 +5,13 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/events"
"github.com/grafana/grafana/pkg/metrics"
"github.com/grafana/grafana/pkg/middleware"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util"
)
// GET /api/user/signup/options
func GetSignUpOptions(c *middleware.Context) Response {
func GetSignUpOptions(c *m.ReqContext) Response {
return Json(200, util.DynMap{
"verifyEmailEnabled": setting.VerifyEmailEnabled,
"autoAssignOrg": setting.AutoAssignOrg,
@@ -20,7 +19,7 @@ func GetSignUpOptions(c *middleware.Context) Response {
}
// POST /api/user/signup
func SignUp(c *middleware.Context, form dtos.SignUpForm) Response {
func SignUp(c *m.ReqContext, form dtos.SignUpForm) Response {
if !setting.AllowUserSignUp {
return ApiError(401, "User signup is disabled", nil)
}
@@ -52,7 +51,7 @@ func SignUp(c *middleware.Context, form dtos.SignUpForm) Response {
return Json(200, util.DynMap{"status": "SignUpCreated"})
}
func SignUpStep2(c *middleware.Context, form dtos.SignUpStep2Form) Response {
func SignUpStep2(c *m.ReqContext, form dtos.SignUpStep2Form) Response {
if !setting.AllowUserSignUp {
return ApiError(401, "User signup is disabled", nil)
}
+2 -3
View File
@@ -2,11 +2,10 @@ package api
import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/middleware"
m "github.com/grafana/grafana/pkg/models"
)
func StarDashboard(c *middleware.Context) Response {
func StarDashboard(c *m.ReqContext) Response {
if !c.IsSignedIn {
return ApiError(412, "You need to sign in to star dashboards", nil)
}
@@ -24,7 +23,7 @@ func StarDashboard(c *middleware.Context) Response {
return ApiSuccess("Dashboard starred!")
}
func UnstarDashboard(c *middleware.Context) Response {
func UnstarDashboard(c *m.ReqContext) Response {
cmd := m.UnstarDashboardCommand{UserId: c.UserId, DashboardId: c.ParamsInt64(":id")}
+5 -6
View File
@@ -3,13 +3,12 @@ package api
import (
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/middleware"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/util"
)
// POST /api/teams
func CreateTeam(c *middleware.Context, cmd m.CreateTeamCommand) Response {
func CreateTeam(c *m.ReqContext, cmd m.CreateTeamCommand) Response {
cmd.OrgId = c.OrgId
if err := bus.Dispatch(&cmd); err != nil {
if err == m.ErrTeamNameTaken {
@@ -25,7 +24,7 @@ func CreateTeam(c *middleware.Context, cmd m.CreateTeamCommand) Response {
}
// PUT /api/teams/:teamId
func UpdateTeam(c *middleware.Context, cmd m.UpdateTeamCommand) Response {
func UpdateTeam(c *m.ReqContext, cmd m.UpdateTeamCommand) Response {
cmd.OrgId = c.OrgId
cmd.Id = c.ParamsInt64(":teamId")
if err := bus.Dispatch(&cmd); err != nil {
@@ -39,7 +38,7 @@ func UpdateTeam(c *middleware.Context, cmd m.UpdateTeamCommand) Response {
}
// DELETE /api/teams/:teamId
func DeleteTeamById(c *middleware.Context) Response {
func DeleteTeamById(c *m.ReqContext) Response {
if err := bus.Dispatch(&m.DeleteTeamCommand{OrgId: c.OrgId, Id: c.ParamsInt64(":teamId")}); err != nil {
if err == m.ErrTeamNotFound {
return ApiError(404, "Failed to delete Team. ID not found", nil)
@@ -50,7 +49,7 @@ func DeleteTeamById(c *middleware.Context) Response {
}
// GET /api/teams/search
func SearchTeams(c *middleware.Context) Response {
func SearchTeams(c *m.ReqContext) Response {
perPage := c.QueryInt("perpage")
if perPage <= 0 {
perPage = 1000
@@ -83,7 +82,7 @@ func SearchTeams(c *middleware.Context) Response {
}
// GET /api/teams/:teamId
func GetTeamById(c *middleware.Context) Response {
func GetTeamById(c *m.ReqContext) Response {
query := m.GetTeamByIdQuery{OrgId: c.OrgId, Id: c.ParamsInt64(":teamId")}
if err := bus.Dispatch(&query); err != nil {
+3 -4
View File
@@ -3,13 +3,12 @@ package api
import (
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/middleware"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/util"
)
// GET /api/teams/:teamId/members
func GetTeamMembers(c *middleware.Context) Response {
func GetTeamMembers(c *m.ReqContext) Response {
query := m.GetTeamMembersQuery{OrgId: c.OrgId, TeamId: c.ParamsInt64(":teamId")}
if err := bus.Dispatch(&query); err != nil {
@@ -24,7 +23,7 @@ func GetTeamMembers(c *middleware.Context) Response {
}
// POST /api/teams/:teamId/members
func AddTeamMember(c *middleware.Context, cmd m.AddTeamMemberCommand) Response {
func AddTeamMember(c *m.ReqContext, cmd m.AddTeamMemberCommand) Response {
cmd.TeamId = c.ParamsInt64(":teamId")
cmd.OrgId = c.OrgId
@@ -46,7 +45,7 @@ func AddTeamMember(c *middleware.Context, cmd m.AddTeamMemberCommand) Response {
}
// DELETE /api/teams/:teamId/members/:userId
func RemoveTeamMember(c *middleware.Context) Response {
func RemoveTeamMember(c *m.ReqContext) Response {
if err := bus.Dispatch(&m.RemoveTeamMemberCommand{OrgId: c.OrgId, TeamId: c.ParamsInt64(":teamId"), UserId: c.ParamsInt64(":userId")}); err != nil {
if err == m.ErrTeamNotFound {
return ApiError(404, "Team not found", nil)
+16 -17
View File
@@ -3,19 +3,18 @@ package api
import (
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/middleware"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util"
)
// GET /api/user (current authenticated user)
func GetSignedInUser(c *middleware.Context) Response {
func GetSignedInUser(c *m.ReqContext) Response {
return getUserUserProfile(c.UserId)
}
// GET /api/users/:id
func GetUserById(c *middleware.Context) Response {
func GetUserById(c *m.ReqContext) Response {
return getUserUserProfile(c.ParamsInt64(":id"))
}
@@ -33,7 +32,7 @@ func getUserUserProfile(userId int64) Response {
}
// GET /api/users/lookup
func GetUserByLoginOrEmail(c *middleware.Context) Response {
func GetUserByLoginOrEmail(c *m.ReqContext) Response {
query := m.GetUserByLoginQuery{LoginOrEmail: c.Query("loginOrEmail")}
if err := bus.Dispatch(&query); err != nil {
if err == m.ErrUserNotFound {
@@ -55,7 +54,7 @@ func GetUserByLoginOrEmail(c *middleware.Context) Response {
}
// POST /api/user
func UpdateSignedInUser(c *middleware.Context, cmd m.UpdateUserCommand) Response {
func UpdateSignedInUser(c *m.ReqContext, cmd m.UpdateUserCommand) Response {
if setting.AuthProxyEnabled {
if setting.AuthProxyHeaderProperty == "email" && cmd.Email != c.Email {
return ApiError(400, "Not allowed to change email when auth proxy is using email property", nil)
@@ -69,13 +68,13 @@ func UpdateSignedInUser(c *middleware.Context, cmd m.UpdateUserCommand) Response
}
// POST /api/users/:id
func UpdateUser(c *middleware.Context, cmd m.UpdateUserCommand) Response {
func UpdateUser(c *m.ReqContext, cmd m.UpdateUserCommand) Response {
cmd.UserId = c.ParamsInt64(":id")
return handleUpdateUser(cmd)
}
//POST /api/users/:id/using/:orgId
func UpdateUserActiveOrg(c *middleware.Context) Response {
func UpdateUserActiveOrg(c *m.ReqContext) Response {
userId := c.ParamsInt64(":id")
orgId := c.ParamsInt64(":orgId")
@@ -108,12 +107,12 @@ func handleUpdateUser(cmd m.UpdateUserCommand) Response {
}
// GET /api/user/orgs
func GetSignedInUserOrgList(c *middleware.Context) Response {
func GetSignedInUserOrgList(c *m.ReqContext) Response {
return getUserOrgList(c.UserId)
}
// GET /api/user/:id/orgs
func GetUserOrgList(c *middleware.Context) Response {
func GetUserOrgList(c *m.ReqContext) Response {
return getUserOrgList(c.ParamsInt64(":id"))
}
@@ -146,7 +145,7 @@ func validateUsingOrg(userId int64, orgId int64) bool {
}
// POST /api/user/using/:id
func UserSetUsingOrg(c *middleware.Context) Response {
func UserSetUsingOrg(c *m.ReqContext) Response {
orgId := c.ParamsInt64(":id")
if !validateUsingOrg(c.UserId, orgId) {
@@ -163,7 +162,7 @@ func UserSetUsingOrg(c *middleware.Context) Response {
}
// GET /profile/switch-org/:id
func ChangeActiveOrgAndRedirectToHome(c *middleware.Context) {
func ChangeActiveOrgAndRedirectToHome(c *m.ReqContext) {
orgId := c.ParamsInt64(":id")
if !validateUsingOrg(c.UserId, orgId) {
@@ -179,7 +178,7 @@ func ChangeActiveOrgAndRedirectToHome(c *middleware.Context) {
c.Redirect(setting.AppSubUrl + "/")
}
func ChangeUserPassword(c *middleware.Context, cmd m.ChangeUserPasswordCommand) Response {
func ChangeUserPassword(c *m.ReqContext, cmd m.ChangeUserPasswordCommand) Response {
if setting.LdapEnabled || setting.AuthProxyEnabled {
return ApiError(400, "Not allowed to change password when LDAP or Auth Proxy is enabled", nil)
}
@@ -211,7 +210,7 @@ func ChangeUserPassword(c *middleware.Context, cmd m.ChangeUserPasswordCommand)
}
// GET /api/users
func SearchUsers(c *middleware.Context) Response {
func SearchUsers(c *m.ReqContext) Response {
query, err := searchUser(c)
if err != nil {
return ApiError(500, "Failed to fetch users", err)
@@ -221,7 +220,7 @@ func SearchUsers(c *middleware.Context) Response {
}
// GET /api/users/search
func SearchUsersWithPaging(c *middleware.Context) Response {
func SearchUsersWithPaging(c *m.ReqContext) Response {
query, err := searchUser(c)
if err != nil {
return ApiError(500, "Failed to fetch users", err)
@@ -230,7 +229,7 @@ func SearchUsersWithPaging(c *middleware.Context) Response {
return Json(200, query.Result)
}
func searchUser(c *middleware.Context) (*m.SearchUsersQuery, error) {
func searchUser(c *m.ReqContext) (*m.SearchUsersQuery, error) {
perPage := c.QueryInt("perpage")
if perPage <= 0 {
perPage = 1000
@@ -258,7 +257,7 @@ func searchUser(c *middleware.Context) (*m.SearchUsersQuery, error) {
return query, nil
}
func SetHelpFlag(c *middleware.Context) Response {
func SetHelpFlag(c *m.ReqContext) Response {
flag := c.ParamsInt64(":id")
bitmask := &c.HelpFlags1
@@ -276,7 +275,7 @@ func SetHelpFlag(c *middleware.Context) Response {
return Json(200, &util.DynMap{"message": "Help flag set", "helpFlags1": cmd.HelpFlags1})
}
func ClearHelpFlags(c *middleware.Context) Response {
func ClearHelpFlags(c *m.ReqContext) Response {
cmd := m.SetUserHelpFlagCommand{
UserId: c.UserId,
HelpFlags1: m.HelpFlags1(0),
+8 -7
View File
@@ -7,6 +7,7 @@ import (
"gopkg.in/macaron.v1"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/session"
"github.com/grafana/grafana/pkg/setting"
)
@@ -15,8 +16,8 @@ type AuthOptions struct {
ReqSignedIn bool
}
func getRequestUserId(c *Context) int64 {
userId := c.Session.Get(SESS_KEY_USERID)
func getRequestUserId(c *m.ReqContext) int64 {
userId := c.Session.Get(session.SESS_KEY_USERID)
if userId != nil {
return userId.(int64)
@@ -25,7 +26,7 @@ func getRequestUserId(c *Context) int64 {
return 0
}
func getApiKey(c *Context) string {
func getApiKey(c *m.ReqContext) string {
header := c.Req.Header.Get("Authorization")
parts := strings.SplitN(header, " ", 2)
if len(parts) == 2 && parts[0] == "Bearer" {
@@ -36,7 +37,7 @@ func getApiKey(c *Context) string {
return ""
}
func accessForbidden(c *Context) {
func accessForbidden(c *m.ReqContext) {
if c.IsApiRequest() {
c.JsonApiErr(403, "Permission denied", nil)
return
@@ -45,7 +46,7 @@ func accessForbidden(c *Context) {
c.Redirect(setting.AppSubUrl + "/")
}
func notAuthorized(c *Context) {
func notAuthorized(c *m.ReqContext) {
if c.IsApiRequest() {
c.JsonApiErr(401, "Unauthorized", nil)
return
@@ -57,7 +58,7 @@ func notAuthorized(c *Context) {
}
func RoleAuth(roles ...m.RoleType) macaron.Handler {
return func(c *Context) {
return func(c *m.ReqContext) {
ok := false
for _, role := range roles {
if role == c.OrgRole {
@@ -72,7 +73,7 @@ func RoleAuth(roles ...m.RoleType) macaron.Handler {
}
func Auth(options *AuthOptions) macaron.Handler {
return func(c *Context) {
return func(c *m.ReqContext) {
if !c.IsSignedIn && options.ReqSignedIn && !c.AllowAnonymous {
notAuthorized(c)
return
+10 -9
View File
@@ -10,10 +10,11 @@ import (
"github.com/grafana/grafana/pkg/log"
"github.com/grafana/grafana/pkg/login"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/session"
"github.com/grafana/grafana/pkg/setting"
)
func initContextWithAuthProxy(ctx *Context, orgId int64) bool {
func initContextWithAuthProxy(ctx *m.ReqContext, orgId int64) bool {
if !setting.AuthProxyEnabled {
return false
}
@@ -58,7 +59,7 @@ func initContextWithAuthProxy(ctx *Context, orgId int64) bool {
}
// initialize session
if err := ctx.Session.Start(ctx); err != nil {
if err := ctx.Session.Start(ctx.Context); err != nil {
log.Error(3, "Failed to start session", err)
return false
}
@@ -66,12 +67,12 @@ func initContextWithAuthProxy(ctx *Context, orgId int64) bool {
// Make sure that we cannot share a session between different users!
if getRequestUserId(ctx) > 0 && getRequestUserId(ctx) != query.Result.UserId {
// remove session
if err := ctx.Session.Destory(ctx); err != nil {
if err := ctx.Session.Destory(ctx.Context); err != nil {
log.Error(3, "Failed to destroy session, err")
}
// initialize a new session
if err := ctx.Session.Start(ctx); err != nil {
if err := ctx.Session.Start(ctx.Context); err != nil {
log.Error(3, "Failed to start session", err)
}
}
@@ -89,17 +90,17 @@ func initContextWithAuthProxy(ctx *Context, orgId int64) bool {
ctx.SignedInUser = query.Result
ctx.IsSignedIn = true
ctx.Session.Set(SESS_KEY_USERID, ctx.UserId)
ctx.Session.Set(session.SESS_KEY_USERID, ctx.UserId)
return true
}
var syncGrafanaUserWithLdapUser = func(ctx *Context, query *m.GetSignedInUserQuery) error {
var syncGrafanaUserWithLdapUser = func(ctx *m.ReqContext, query *m.GetSignedInUserQuery) error {
if setting.LdapEnabled {
expireEpoch := time.Now().Add(time.Duration(-setting.AuthProxyLdapSyncTtl) * time.Minute).Unix()
var lastLdapSync int64
if lastLdapSyncInSession := ctx.Session.Get(SESS_KEY_LASTLDAPSYNC); lastLdapSyncInSession != nil {
if lastLdapSyncInSession := ctx.Session.Get(session.SESS_KEY_LASTLDAPSYNC); lastLdapSyncInSession != nil {
lastLdapSync = lastLdapSyncInSession.(int64)
}
@@ -113,14 +114,14 @@ var syncGrafanaUserWithLdapUser = func(ctx *Context, query *m.GetSignedInUserQue
}
}
ctx.Session.Set(SESS_KEY_LASTLDAPSYNC, time.Now().Unix())
ctx.Session.Set(session.SESS_KEY_LASTLDAPSYNC, time.Now().Unix())
}
}
return nil
}
func checkAuthenticationProxy(ctx *Context, proxyHeaderValue string) error {
func checkAuthenticationProxy(ctx *m.ReqContext, proxyHeaderValue string) error {
if len(strings.TrimSpace(setting.AuthProxyWhitelist)) > 0 {
proxies := strings.Split(setting.AuthProxyWhitelist, ",")
remoteAddrSplit := strings.Split(ctx.Req.RemoteAddr, ":")
+17 -15
View File
@@ -6,8 +6,10 @@ import (
"github.com/grafana/grafana/pkg/login"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/session"
"github.com/grafana/grafana/pkg/setting"
. "github.com/smartystreets/goconvey/convey"
"gopkg.in/macaron.v1"
)
func TestAuthProxyWithLdapEnabled(t *testing.T) {
@@ -29,45 +31,45 @@ func TestAuthProxyWithLdapEnabled(t *testing.T) {
Convey("When session variable lastLdapSync not set, call syncSignedInUser and set lastLdapSync", func() {
// arrange
session := mockSession{}
ctx := Context{Session: &session}
So(session.Get(SESS_KEY_LASTLDAPSYNC), ShouldBeNil)
sess := mockSession{}
ctx := m.ReqContext{Session: &sess}
So(sess.Get(session.SESS_KEY_LASTLDAPSYNC), ShouldBeNil)
// act
syncGrafanaUserWithLdapUser(&ctx, &query)
// assert
So(mockLdapAuther.syncSignedInUserCalled, ShouldBeTrue)
So(session.Get(SESS_KEY_LASTLDAPSYNC), ShouldBeGreaterThan, 0)
So(sess.Get(session.SESS_KEY_LASTLDAPSYNC), ShouldBeGreaterThan, 0)
})
Convey("When session variable not expired, don't sync and don't change session var", func() {
// arrange
session := mockSession{}
ctx := Context{Session: &session}
sess := mockSession{}
ctx := m.ReqContext{Session: &sess}
now := time.Now().Unix()
session.Set(SESS_KEY_LASTLDAPSYNC, now)
sess.Set(session.SESS_KEY_LASTLDAPSYNC, now)
// act
syncGrafanaUserWithLdapUser(&ctx, &query)
// assert
So(session.Get(SESS_KEY_LASTLDAPSYNC), ShouldEqual, now)
So(sess.Get(session.SESS_KEY_LASTLDAPSYNC), ShouldEqual, now)
So(mockLdapAuther.syncSignedInUserCalled, ShouldBeFalse)
})
Convey("When lastldapsync is expired, session variable should be updated", func() {
// arrange
session := mockSession{}
ctx := Context{Session: &session}
sess := mockSession{}
ctx := m.ReqContext{Session: &sess}
expiredTime := time.Now().Add(time.Duration(-120) * time.Minute).Unix()
session.Set(SESS_KEY_LASTLDAPSYNC, expiredTime)
sess.Set(session.SESS_KEY_LASTLDAPSYNC, expiredTime)
// act
syncGrafanaUserWithLdapUser(&ctx, &query)
// assert
So(session.Get(SESS_KEY_LASTLDAPSYNC), ShouldBeGreaterThan, expiredTime)
So(sess.Get(session.SESS_KEY_LASTLDAPSYNC), ShouldBeGreaterThan, expiredTime)
So(mockLdapAuther.syncSignedInUserCalled, ShouldBeTrue)
})
})
@@ -77,7 +79,7 @@ type mockSession struct {
value interface{}
}
func (s *mockSession) Start(c *Context) error {
func (s *mockSession) Start(c *macaron.Context) error {
return nil
}
@@ -102,11 +104,11 @@ func (s *mockSession) Release() error {
return nil
}
func (s *mockSession) Destory(c *Context) error {
func (s *mockSession) Destory(c *macaron.Context) error {
return nil
}
func (s *mockSession) RegenerateId(c *Context) error {
func (s *mockSession) RegenerateId(c *macaron.Context) error {
return nil
}
+2 -2
View File
@@ -20,7 +20,7 @@ func getDashboardUrlBySlug(orgId int64, slug string) (string, error) {
}
func RedirectFromLegacyDashboardUrl() macaron.Handler {
return func(c *Context) {
return func(c *m.ReqContext) {
slug := c.Params("slug")
if slug != "" {
@@ -34,7 +34,7 @@ func RedirectFromLegacyDashboardUrl() macaron.Handler {
}
func RedirectFromLegacyDashboardSoloUrl() macaron.Handler {
return func(c *Context) {
return func(c *m.ReqContext) {
slug := c.Params("slug")
if slug != "" {
+2 -1
View File
@@ -19,6 +19,7 @@ import (
"net/http"
"time"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting"
"github.com/prometheus/client_golang/prometheus"
"gopkg.in/macaron.v1"
@@ -47,7 +48,7 @@ func Logger() macaron.Handler {
}
if ctx, ok := c.Data["ctx"]; ok {
ctxTyped := ctx.(*Context)
ctxTyped := ctx.(*m.ReqContext)
if status == 500 {
ctxTyped.Logger.Error("Request Completed", "method", req.Method, "path", req.URL.Path, "status", status, "remote_addr", c.RemoteAddr(), "time_ms", int64(timeTakenMs), "size", rw.Size(), "referer", req.Referer())
} else {
+9 -84
View File
@@ -2,7 +2,6 @@ package middleware
import (
"strconv"
"strings"
"gopkg.in/macaron.v1"
@@ -11,29 +10,17 @@ import (
"github.com/grafana/grafana/pkg/log"
l "github.com/grafana/grafana/pkg/login"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/session"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util"
"github.com/prometheus/client_golang/prometheus"
)
type Context struct {
*macaron.Context
*m.SignedInUser
Session SessionStore
IsSignedIn bool
IsRenderCall bool
AllowAnonymous bool
Logger log.Logger
}
func GetContextHandler() macaron.Handler {
return func(c *macaron.Context) {
ctx := &Context{
ctx := &m.ReqContext{
Context: c,
SignedInUser: &m.SignedInUser{},
Session: GetSession(),
Session: session.GetSession(),
IsSignedIn: false,
AllowAnonymous: false,
Logger: log.New("context"),
@@ -74,7 +61,7 @@ func GetContextHandler() macaron.Handler {
}
}
func initContextWithAnonymousUser(ctx *Context) bool {
func initContextWithAnonymousUser(ctx *m.ReqContext) bool {
if !setting.AnonymousEnabled {
return false
}
@@ -94,9 +81,9 @@ func initContextWithAnonymousUser(ctx *Context) bool {
return true
}
func initContextWithUserSessionCookie(ctx *Context, orgId int64) bool {
func initContextWithUserSessionCookie(ctx *m.ReqContext, orgId int64) bool {
// initialize session
if err := ctx.Session.Start(ctx); err != nil {
if err := ctx.Session.Start(ctx.Context); err != nil {
ctx.Logger.Error("Failed to start session", "error", err)
return false
}
@@ -117,7 +104,7 @@ func initContextWithUserSessionCookie(ctx *Context, orgId int64) bool {
return true
}
func initContextWithApiKey(ctx *Context) bool {
func initContextWithApiKey(ctx *m.ReqContext) bool {
var keyString string
if keyString = getApiKey(ctx); keyString == "" {
return false
@@ -153,7 +140,7 @@ func initContextWithApiKey(ctx *Context) bool {
return true
}
func initContextWithBasicAuth(ctx *Context, orgId int64) bool {
func initContextWithBasicAuth(ctx *m.ReqContext, orgId int64) bool {
if !setting.BasicAuthEnabled {
return false
@@ -195,70 +182,8 @@ func initContextWithBasicAuth(ctx *Context, orgId int64) bool {
return true
}
// Handle handles and logs error by given status.
func (ctx *Context) Handle(status int, title string, err error) {
if err != nil {
ctx.Logger.Error(title, "error", err)
if setting.Env != setting.PROD {
ctx.Data["ErrorMsg"] = err
}
}
ctx.Data["Title"] = title
ctx.Data["AppSubUrl"] = setting.AppSubUrl
ctx.Data["Theme"] = "dark"
ctx.HTML(status, "error")
}
func (ctx *Context) JsonOK(message string) {
resp := make(map[string]interface{})
resp["message"] = message
ctx.JSON(200, resp)
}
func (ctx *Context) IsApiRequest() bool {
return strings.HasPrefix(ctx.Req.URL.Path, "/api")
}
func (ctx *Context) JsonApiErr(status int, message string, err error) {
resp := make(map[string]interface{})
if err != nil {
ctx.Logger.Error(message, "error", err)
if setting.Env != setting.PROD {
resp["error"] = err.Error()
}
}
switch status {
case 404:
resp["message"] = "Not Found"
case 500:
resp["message"] = "Internal Server Error"
}
if message != "" {
resp["message"] = message
}
ctx.JSON(status, resp)
}
func (ctx *Context) HasUserRole(role m.RoleType) bool {
return ctx.OrgRole.Includes(role)
}
func (ctx *Context) HasHelpFlag(flag m.HelpFlags1) bool {
return ctx.HelpFlags1.HasFlag(flag)
}
func (ctx *Context) TimeRequest(timer prometheus.Summary) {
ctx.Data["perfmon.timer"] = timer
}
func AddDefaultResponseHeaders() macaron.Handler {
return func(ctx *Context) {
return func(ctx *m.ReqContext) {
if ctx.IsApiRequest() && ctx.Req.Method == "GET" {
ctx.Resp.Header().Add("Cache-Control", "no-cache")
ctx.Resp.Header().Add("Pragma", "no-cache")
+12 -11
View File
@@ -7,10 +7,11 @@ import (
"path/filepath"
"testing"
"github.com/go-macaron/session"
ms "github.com/go-macaron/session"
"github.com/grafana/grafana/pkg/bus"
l "github.com/grafana/grafana/pkg/login"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/session"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util"
. "github.com/smartystreets/goconvey/convey"
@@ -130,8 +131,8 @@ func TestMiddlewareContext(t *testing.T) {
middlewareScenario("UserId in session", func(sc *scenarioContext) {
sc.fakeReq("GET", "/").handler(func(c *Context) {
c.Session.Set(SESS_KEY_USERID, int64(12))
sc.fakeReq("GET", "/").handler(func(c *m.ReqContext) {
c.Session.Set(session.SESS_KEY_USERID, int64(12))
}).exec()
bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
@@ -276,8 +277,8 @@ func TestMiddlewareContext(t *testing.T) {
})
// create session
sc.fakeReq("GET", "/").handler(func(c *Context) {
c.Session.Set(SESS_KEY_USERID, int64(33))
sc.fakeReq("GET", "/").handler(func(c *m.ReqContext) {
c.Session.Set(session.SESS_KEY_USERID, int64(33))
}).exec()
oldSessionID := sc.context.Session.ID()
@@ -300,7 +301,7 @@ func TestMiddlewareContext(t *testing.T) {
setting.LdapEnabled = true
called := false
syncGrafanaUserWithLdapUser = func(ctx *Context, query *m.GetSignedInUserQuery) error {
syncGrafanaUserWithLdapUser = func(ctx *m.ReqContext, query *m.GetSignedInUserQuery) error {
called = true
return nil
}
@@ -336,12 +337,12 @@ func middlewareScenario(desc string, fn scenarioFunc) {
sc.m.Use(GetContextHandler())
// mock out gc goroutine
startSessionGC = func() {}
sc.m.Use(Sessioner(&session.Options{}))
session.StartSessionGC = func() {}
sc.m.Use(Sessioner(&ms.Options{}))
sc.m.Use(OrgRedirect())
sc.m.Use(AddDefaultResponseHeaders())
sc.defaultHandler = func(c *Context) {
sc.defaultHandler = func(c *m.ReqContext) {
sc.context = c
if sc.handlerFunc != nil {
sc.handlerFunc(sc.context)
@@ -356,7 +357,7 @@ func middlewareScenario(desc string, fn scenarioFunc) {
type scenarioContext struct {
m *macaron.Macaron
context *Context
context *m.ReqContext
resp *httptest.ResponseRecorder
apiKey string
authHeader string
@@ -436,4 +437,4 @@ func (sc *scenarioContext) exec() {
}
type scenarioFunc func(c *scenarioContext)
type handlerFunc func(c *Context)
type handlerFunc func(c *m.ReqContext)
+3 -3
View File
@@ -7,7 +7,7 @@ import (
"strings"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/models"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting"
"gopkg.in/macaron.v1"
@@ -22,7 +22,7 @@ func OrgRedirect() macaron.Handler {
return
}
ctx, ok := c.Data["ctx"].(*Context)
ctx, ok := c.Data["ctx"].(*m.ReqContext)
if !ok || !ctx.IsSignedIn {
return
}
@@ -31,7 +31,7 @@ func OrgRedirect() macaron.Handler {
return
}
cmd := models.SetUsingOrgCommand{UserId: ctx.UserId, OrgId: orgId}
cmd := m.SetUsingOrgCommand{UserId: ctx.UserId, OrgId: orgId}
if err := bus.Dispatch(&cmd); err != nil {
if ctx.IsApiRequest() {
ctx.JsonApiErr(404, "Not found", nil)
+12 -11
View File
@@ -6,7 +6,8 @@ import (
"fmt"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/models"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/session"
. "github.com/smartystreets/goconvey/convey"
)
@@ -14,16 +15,16 @@ func TestOrgRedirectMiddleware(t *testing.T) {
Convey("Can redirect to correct org", t, func() {
middlewareScenario("when setting a correct org for the user", func(sc *scenarioContext) {
sc.fakeReq("GET", "/").handler(func(c *Context) {
c.Session.Set(SESS_KEY_USERID, int64(12))
sc.fakeReq("GET", "/").handler(func(c *m.ReqContext) {
c.Session.Set(session.SESS_KEY_USERID, int64(12))
}).exec()
bus.AddHandler("test", func(query *models.SetUsingOrgCommand) error {
bus.AddHandler("test", func(query *m.SetUsingOrgCommand) error {
return nil
})
bus.AddHandler("test", func(query *models.GetSignedInUserQuery) error {
query.Result = &models.SignedInUser{OrgId: 1, UserId: 12}
bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
query.Result = &m.SignedInUser{OrgId: 1, UserId: 12}
return nil
})
@@ -36,16 +37,16 @@ func TestOrgRedirectMiddleware(t *testing.T) {
})
middlewareScenario("when setting an invalid org for user", func(sc *scenarioContext) {
sc.fakeReq("GET", "/").handler(func(c *Context) {
c.Session.Set(SESS_KEY_USERID, int64(12))
sc.fakeReq("GET", "/").handler(func(c *m.ReqContext) {
c.Session.Set(session.SESS_KEY_USERID, int64(12))
}).exec()
bus.AddHandler("test", func(query *models.SetUsingOrgCommand) error {
bus.AddHandler("test", func(query *m.SetUsingOrgCommand) error {
return fmt.Errorf("")
})
bus.AddHandler("test", func(query *models.GetSignedInUserQuery) error {
query.Result = &models.SignedInUser{OrgId: 1, UserId: 12}
bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
query.Result = &m.SignedInUser{OrgId: 1, UserId: 12}
return nil
})

Some files were not shown because too many files have changed in this diff Show More