Compare commits

...
Author SHA1 Message Date
Dominik Prokop 962089bc2b test 2019-07-12 15:08:35 +02:00
Dominik Prokop ae61bb923f Minor copy 2019-07-12 13:43:58 +02:00
Dominik Prokop d69b54d41a Run next packages release on master but not when tagged with release 2019-07-12 13:41:43 +02:00
Dominik Prokop ceaa30d88e Rename job and run it after frontend tests and build passes 2019-07-12 13:19:29 +02:00
Dominik Prokop 5e4971ac01 Set git creds before commit 2019-07-12 13:04:00 +02:00
Dominik Prokop 266d8cab89 Publish to npm 2019-07-12 12:53:37 +02:00
Dominik Prokop 1bf2ad37f3 Merge branch 'vvmaster' into packages/circle 2019-07-12 12:45:17 +02:00
Ryan McKinley ca628832ab grafana/toolkit: improve CircleCI integration (#18071)
* don't actually install grafana in the setup step

* updat eversion changes

* add report stage

* update versions

* don't do failing test

* upate version

* print plugins

* update versions

* copy docs

* Update package.json
2019-07-12 10:25:38 +02:00
Oleg Gaidarenko 1cbec50866 Build: consistently reference go binary (#18059)
Before this applied, go binary is referenced inconsistently in makefile.
Sometimes with go modules enabled and sometimes not, sometimes through
a variable and sometimes not.

This change makes all the references in makefile consistent
2019-07-11 20:55:35 +02:00
Andrej Ocenas 743f8420bc devenv: Fix typo in nginix docker for mac (#18068) 2019-07-11 18:13:01 +02:00
Tobias Skarhed e1cec1069c noImplicitAny: 1670 errors (#18035)
* Sub 2000 errors

* Down to 1670 errors

* Minor fixes
2019-07-11 17:05:45 +02:00
Dominik Prokop e9dd84f9d3 Remove postpublish 2019-07-11 16:05:49 +02:00
Dominik Prokop 660b9a3126 Packages version bump 2019-07-11 15:58:59 +02:00
Dominik Prokop b924884240 Correct lerna version 2019-07-11 15:50:02 +02:00
Dominik Prokop 605de54852 Reset git befgore publishing package 2019-07-11 15:40:02 +02:00
Dominik Prokop 9b674b3944 update lerna publish script 2019-07-11 15:33:46 +02:00
Dominik Prokop d249335a6c Add publishing packages 2019-07-11 15:27:13 +02:00
Sofia Papagiannaki 6aa58182c7 Add missing pull requests to Changelog (#18061) 2019-07-11 16:13:23 +03:00
Dominik Prokop bcabffc25b Typo fix 2019-07-11 14:42:28 +02:00
Dominik Prokop e58e7cb4c5 Try any... 2019-07-11 14:41:05 +02:00
Dominik Prokop 74c118f1d1 Remove @types/lodas resolution 2019-07-11 14:34:16 +02:00
Dominik Prokop ac9774e7bb lerna add data package to ui 2019-07-11 14:18:07 +02:00
Dominik Prokop 31d619c7de temporarily add tsignore 2019-07-11 14:08:42 +02:00
Dominik Prokop ecac5d6931 add lerna bootstrap 2019-07-11 13:52:03 +02:00
Dominik Prokop 6e2c5eb52a Remove cache tmp 2019-07-11 13:44:50 +02:00
Kyle Brandt 76d08989f0 provisioning: escape literal '$' with '$$' to avoid interpolation (#18045)
fixes #17986
2019-07-11 07:32:07 -04:00
Dominik Prokop a28c96090c Install packages before release 2019-07-11 13:31:27 +02:00
Dominik Prokop 1292d203a8 Add packages:build 2019-07-11 13:26:59 +02:00
Dominik Prokop 2ed7ceb59d Use packages cache, run lern via npx 2019-07-11 13:23:17 +02:00
Dominik Prokop 98908f7b98 Run lerna from local bin 2019-07-11 13:16:07 +02:00
Dominik Prokop c8da0ac1c8 yarn fix 2019-07-11 13:10:14 +02:00
Dominik Prokop add6a0d00a Remove dependency on test task, fix yarn 2019-07-11 13:08:33 +02:00
Dominik Prokop c74c7e24e2 Testing lerna on circle 2019-07-11 13:00:58 +02:00
Ryan McKinley 7ec87ee76b grafana/toolkit: improve CircleCI stubs (#17995)
* validate type and id

* copy all svg and png, useful if people don't use the img folder

* update comments

* add stubs for each ci task

* use ci-work folder rather than build

* use axios for basic testing

* Packages: publish packages@6.3.0-alpha.39

* bump version

* add download task

* Packages: publish packages@6.3.0-alpha.40

* merge all dist folders into one

* fix folder paths

* Fix ts error

* Packages: publish packages@6.3.0-beta.0

* Packages: publish packages@6.3.0-beta.1

* bump next to 6.4

* Packages: publish packages@6.4.0-alpha.2

* better build and bundle tasks

* fix lint

* Packages: publish packages@6.4.0-alpha.3

* copy the file to start grafana

* Packages: publish packages@6.4.0-alpha.4

* use sudo for copy

* Packages: publish packages@6.4.0-alpha.5

* add missing service

* add service and homepath

* Packages: publish packages@6.4.0-alpha.6

* make the folder

* Update packages/grafana-toolkit/src/cli/tasks/plugin.ci.ts

* Update packages/grafana-toolkit/src/cli/tasks/plugin.ci.ts
2019-07-11 12:47:58 +02:00
Mikhail f. Shiryaev 5190949950 Docs: clarify the ttl units (#18039) 2019-07-11 12:48:24 +03:00
Barry 6f4625bb78 Update docs readme for running MySQL/Postgres tests 2019-07-11 11:33:53 +03:00
Anthony Templeton 3680b95b44 Auth: Duplicate API Key Name Handle With Useful HTTP Code (#17905)
* API: Duplicate API Key Name Handle With Useful HTTP Code

* 17447: make changes requested during review

- use dialect.IsUniqueContraintViolation
- change if statement to match others
- return error properly

* Revert "17447: make changes requested during review"

This reverts commit a4a674ea83.

* API: useful http code on duplicate api key error w/ tests

* API: API Key Duplicate Handling

fixed small typo associated with error
2019-07-11 11:20:34 +03:00
Šimon Podlipský 04e7970375 Chore: upgrade node-sass to 4.12.0 (#18052) 2019-07-11 09:18:27 +02:00
Sofia Papagiannaki f2ad3242be API: Minor fix for nil pointer when trying to log error during creating new dashboard via the API (#18003)
* Minor fix for nil pointer when trying to log error

* Do not return error if a dashboard is created

Only log the failures

* Do not return error if the folder is created

Only log the failures
2019-07-11 09:45:29 +03:00
Ryan McKinley aa89210c9d Chore: update lodash (#18055) 2019-07-10 23:37:52 -07:00
Sofia Papagiannaki a5834d3250 Update latest.json (#18043) 2019-07-10 20:24:17 +03:00
Sofia Papagiannaki f5efef1370 Update Changelog (#18042) 2019-07-10 19:42:23 +03:00
Sofia Papagiannaki 3bbc40a32f Chore: bump master version number to 6.4.0-pre 2019-07-10 16:12:28 +03:00
kay delaney bf7fb67f73 Explore/Loki: Display live tailed logs in correct order (#18031)
Closes #18027
2019-07-10 13:57:23 +01:00
70 changed files with 2562 additions and 461 deletions
+27 -1
View File
@@ -7,12 +7,17 @@ aliases:
only: /^v[0-9]+(\.[0-9]+){2}(-.+|[^-.]*)$/ only: /^v[0-9]+(\.[0-9]+){2}(-.+|[^-.]*)$/
- &filter-not-release-or-master - &filter-not-release-or-master
tags: tags:
ignore: /^v[0-9]+(\.[0-9]+){2}(-.+|[^-.]*)$/ ignore: /^v[0--9]+(\.[0-9]+){2}(-.+|[^-.]*)$/
branches: branches:
ignore: master ignore: master
- &filter-only-master - &filter-only-master
branches: branches:
only: master only: master
- &filter-only-master-but-not-release
tags:
ignore: /^v[0-9]+(\.[0-9]+){2}(-.+|[^-.]*)$/
branches:
only: master
version: 2 version: 2
@@ -623,6 +628,21 @@ jobs:
echo "-- no changes to docs files --" echo "-- no changes to docs files --"
fi fi
release-next-packages:
docker:
- image: circleci/node:10
steps:
- checkout
- run:
name: Boostrap lerna
command: 'npx lerna bootstrap'
- run:
name: npm - Prepare auth token
command: 'echo //registry.npmjs.org/:_authToken=$NPM_TOKEN >> ~/.npmrc'
- run:
name: Release next packages
command: './scripts/circle-release-next-packages.sh'
workflows: workflows:
version: 2 version: 2
build-master: build-master:
@@ -694,6 +714,11 @@ workflows:
requires: requires:
- end-to-end-test - end-to-end-test
filters: *filter-only-master filters: *filter-only-master
- release-next-packages:
requires:
- test-frontend
- build-fast-frontend
filters: *filter-only-master-but-not-release
release: release:
jobs: jobs:
- build-all: - build-all:
@@ -803,3 +828,4 @@ workflows:
- postgres-integration-test - postgres-integration-test
- cache-server-test - cache-server-test
filters: *filter-not-release-or-master filters: *filter-not-release-or-master
+70 -1
View File
@@ -1,4 +1,73 @@
# 6.3.0 (unreleased) # 6.4.0 (unreleased)
# 6.3.0-beta1
### Features / Enhancements
* **Alerting**: Add tags to alert rules. [#10989](https://github.com/grafana/grafana/pull/10989), [@Thib17](https://github.com/Thib17)
* **Alerting**: Attempt to send email notifications to all given email addresses. [#16881](https://github.com/grafana/grafana/pull/16881), [@zhulongcheng](https://github.com/zhulongcheng)
* **Alerting**: Improve alert rule testing. [#16286](https://github.com/grafana/grafana/pull/16286), [@marefr](https://github.com/marefr)
* **Alerting**: Support for configuring content field for Discord alert notifier. [#17017](https://github.com/grafana/grafana/pull/17017), [@jan25](https://github.com/jan25)
* **Alertmanager**: Replace illegal chars with underscore in label names. [#17002](https://github.com/grafana/grafana/pull/17002), [@bergquist](https://github.com/bergquist)
* **Auth**: Allow expiration of API keys. [#17678](https://github.com/grafana/grafana/pull/17678), [@papagian](https://github.com/papagian)
* **Auth**: Return device, os and browser when listing user auth tokens in HTTP API. [#17504](https://github.com/grafana/grafana/pull/17504), [@shavonn](https://github.com/shavonn)
* **Auth**: Support list and revoke of user auth tokens in UI. [#17434](https://github.com/grafana/grafana/pull/17434), [@shavonn](https://github.com/shavonn)
* **AzureMonitor**: change clashing built-in Grafana variables/macro names for Azure Logs. [#17140](https://github.com/grafana/grafana/pull/17140), [@shavonn](https://github.com/shavonn)
* **CloudWatch**: Made region visible for AWS Cloudwatch Expressions. [#17243](https://github.com/grafana/grafana/pull/17243), [@utkarshcmu](https://github.com/utkarshcmu)
* **Cloudwatch**: Add AWS DocDB metrics. [#17241](https://github.com/grafana/grafana/pull/17241), [@utkarshcmu](https://github.com/utkarshcmu)
* **Dashboard**: Use timezone dashboard setting when exporting to CSV. [#18002](https://github.com/grafana/grafana/pull/18002), [@dehrax](https://github.com/dehrax)
* **Data links**. [#17267](https://github.com/grafana/grafana/pull/17267), [@torkelo](https://github.com/torkelo)
* **Docker**: Switch base image to ubuntu:latest from debian:stretch to avoid security issues.. [#17066](https://github.com/grafana/grafana/pull/17066), [@bergquist](https://github.com/bergquist)
* **Elasticsearch**: Support for visualizing logs in Explore . [#17605](https://github.com/grafana/grafana/pull/17605), [@marefr](https://github.com/marefr)
* **Explore**: Adds Live option for supported datasources. [#17062](https://github.com/grafana/grafana/pull/17062), [@hugohaggmark](https://github.com/hugohaggmark)
* **Explore**: Adds orgId to URL for sharing purposes. [#17895](https://github.com/grafana/grafana/pull/17895), [@kaydelaney](https://github.com/kaydelaney)
* **Explore**: Adds support for new loki 'start' and 'end' params for labels endpoint. [#17512](https://github.com/grafana/grafana/pull/17512), [@kaydelaney](https://github.com/kaydelaney)
* **Explore**: Adds support for toggling raw query mode in explore. [#17870](https://github.com/grafana/grafana/pull/17870), [@kaydelaney](https://github.com/kaydelaney)
* **Explore**: Allow switching between metrics and logs . [#16959](https://github.com/grafana/grafana/pull/16959), [@marefr](https://github.com/marefr)
* **Explore**: Combines the timestamp and local time columns into one. [#17775](https://github.com/grafana/grafana/pull/17775), [@hugohaggmark](https://github.com/hugohaggmark)
* **Explore**: Display log lines context . [#17097](https://github.com/grafana/grafana/pull/17097), [@dprokop](https://github.com/dprokop)
* **Explore**: Don't parse log levels if provided by field or label. [#17180](https://github.com/grafana/grafana/pull/17180), [@marefr](https://github.com/marefr)
* **Explore**: Improves performance of Logs element by limiting re-rendering. [#17685](https://github.com/grafana/grafana/pull/17685), [@kaydelaney](https://github.com/kaydelaney)
* **Explore**: Support for new LogQL filtering syntax. [#16674](https://github.com/grafana/grafana/pull/16674), [@davkal](https://github.com/davkal)
* **Explore**: Use new TimePicker from Grafana/UI. [#17793](https://github.com/grafana/grafana/pull/17793), [@hugohaggmark](https://github.com/hugohaggmark)
* **Explore**: handle newlines in LogRow Highlighter. [#17425](https://github.com/grafana/grafana/pull/17425), [@rrfeng](https://github.com/rrfeng)
* **Graph**: Added new fill gradient option. [#17528](https://github.com/grafana/grafana/pull/17528), [@torkelo](https://github.com/torkelo)
* **GraphPanel**: Don't sort series when legend table & sort column is not visible . [#17095](https://github.com/grafana/grafana/pull/17095), [@shavonn](https://github.com/shavonn)
* **InfluxDB**: Support for visualizing logs in Explore. [#17450](https://github.com/grafana/grafana/pull/17450), [@hugohaggmark](https://github.com/hugohaggmark)
* **Logging**: Login and Logout actions (#17760). [#17883](https://github.com/grafana/grafana/pull/17883), [@ATTron](https://github.com/ATTron)
* **Logging**: Move log package to pkg/infra. [#17023](https://github.com/grafana/grafana/pull/17023), [@zhulongcheng](https://github.com/zhulongcheng)
* **Metrics**: Expose stats about roles as metrics. [#17469](https://github.com/grafana/grafana/pull/17469), [@bergquist](https://github.com/bergquist)
* **MySQL/Postgres/MSSQL**: Add parsing for day, weeks and year intervals in macros. [#13086](https://github.com/grafana/grafana/pull/13086), [@bernardd](https://github.com/bernardd)
* **MySQL**: Add support for periodically reloading client certs. [#14892](https://github.com/grafana/grafana/pull/14892), [@tpetr](https://github.com/tpetr)
* **Plugins**: replace dataFormats list with skipDataQuery flag in plugin.json. [#16984](https://github.com/grafana/grafana/pull/16984), [@ryantxu](https://github.com/ryantxu)
* **Prometheus**: Take timezone into account for step alignment. [#17477](https://github.com/grafana/grafana/pull/17477), [@fxmiii](https://github.com/fxmiii)
* **Prometheus**: Use overridden panel range for $__range instead of dashboard range. [#17352](https://github.com/grafana/grafana/pull/17352), [@patrick246](https://github.com/patrick246)
* **Prometheus**: added time range filter to series labels query. [#16851](https://github.com/grafana/grafana/pull/16851), [@FUSAKLA](https://github.com/FUSAKLA)
* **Provisioning**: Support folder that doesn't exist yet in dashboard provisioning. [#17407](https://github.com/grafana/grafana/pull/17407), [@Nexucis](https://github.com/Nexucis)
* **Refresh picker**: Handle empty intervals. [#17585](https://github.com/grafana/grafana/pull/17585), [@dehrax](https://github.com/dehrax)
* **Singlestat**: Add y min/max config to singlestat sparklines. [#17527](https://github.com/grafana/grafana/pull/17527), [@pitr](https://github.com/pitr)
* **Snapshot**: use given key and deleteKey. [#16876](https://github.com/grafana/grafana/pull/16876), [@zhulongcheng](https://github.com/zhulongcheng)
* **Templating**: Correctly display __text in multi-value variable after page reload. [#17840](https://github.com/grafana/grafana/pull/17840), [@EduardSergeev](https://github.com/EduardSergeev)
* **Templating**: Support selecting all filtered values of a multi-value variable. [#16873](https://github.com/grafana/grafana/pull/16873), [@r66ad](https://github.com/r66ad)
* **Tracing**: allow propagation with Zipkin headers. [#17009](https://github.com/grafana/grafana/pull/17009), [@jrockway](https://github.com/jrockway)
* **Users**: Disable users removed from LDAP. [#16820](https://github.com/grafana/grafana/pull/16820), [@alexanderzobnin](https://github.com/alexanderzobnin)
### Bug Fixes
* **AddPanel**: Fix issue when removing moved add panel widget . [#17659](https://github.com/grafana/grafana/pull/17659), [@dehrax](https://github.com/dehrax)
* **CLI**: Fix encrypt-datasource-passwords fails with sql error. [#18014](https://github.com/grafana/grafana/pull/18014), [@marefr](https://github.com/marefr)
* **Elasticsearch**: Fix default max concurrent shard requests. [#17770](https://github.com/grafana/grafana/pull/17770), [@marefr](https://github.com/marefr)
* **Explore**: Fix browsing back to dashboard panel. [#17061](https://github.com/grafana/grafana/pull/17061), [@jschill](https://github.com/jschill)
* **Explore**: Fix filter by series level in logs graph. [#17798](https://github.com/grafana/grafana/pull/17798), [@marefr](https://github.com/marefr)
* **Explore**: Fix issues when loading and both graph/table are collapsed. [#17113](https://github.com/grafana/grafana/pull/17113), [@marefr](https://github.com/marefr)
* **Explore**: Fix selection/copy of log lines. [#17121](https://github.com/grafana/grafana/pull/17121), [@marefr](https://github.com/marefr)
* **Fix**: Wrap value of multi variable in array when coming from URL. [#16992](https://github.com/grafana/grafana/pull/16992), [@aocenas](https://github.com/aocenas)
* **Frontend**: Fix for Json tree component not working. [#17608](https://github.com/grafana/grafana/pull/17608), [@srid12](https://github.com/srid12)
* **Graphite**: Fix for issue with alias function being moved last. [#17791](https://github.com/grafana/grafana/pull/17791), [@torkelo](https://github.com/torkelo)
* **Graphite**: Fixes issue with seriesByTag & function with variable param. [#17795](https://github.com/grafana/grafana/pull/17795), [@torkelo](https://github.com/torkelo)
* **Graphite**: use POST for /metrics/find requests. [#17814](https://github.com/grafana/grafana/pull/17814), [@papagian](https://github.com/papagian)
* **HTTP Server**: Serve Grafana with a custom URL path prefix. [#17048](https://github.com/grafana/grafana/pull/17048), [@jan25](https://github.com/jan25)
* **InfluxDB**: Fixes single quotes are not escaped in label value filters. [#17398](https://github.com/grafana/grafana/pull/17398), [@Panzki](https://github.com/Panzki)
* **Prometheus**: Correctly escape '|' literals in interpolated PromQL variables. [#16932](https://github.com/grafana/grafana/pull/16932), [@Limess](https://github.com/Limess)
* **Prometheus**: Fix when adding label for metrics which contains colons in Explore. [#16760](https://github.com/grafana/grafana/pull/16760), [@tolwi](https://github.com/tolwi)
* **SinglestatPanel**: Remove background color when value turns null. [#17552](https://github.com/grafana/grafana/pull/17552), [@druggieri](https://github.com/druggieri)
# 6.2.5 (2019-06-25) # 6.2.5 (2019-06-25)
+7 -7
View File
@@ -8,7 +8,7 @@ GO_FILES := ./pkg/...
all: deps build all: deps build
deps-go: deps-go:
go run build.go setup $(GO) run build.go setup
deps-js: node_modules deps-js: node_modules
@@ -16,15 +16,15 @@ deps: deps-js
build-go: build-go:
@echo "build go files" @echo "build go files"
GO111MODULE=on go run build.go build $(GO) run build.go build
build-server: build-server:
@echo "build server" @echo "build server"
GO111MODULE=on go run build.go build-server $(GO) run build.go build-server
build-cli: build-cli:
@echo "build in CI environment" @echo "build in CI environment"
GO111MODULE=on go run build.go build-cli $(GO) run build.go build-cli
build-js: build-js:
@echo "build frontend" @echo "build frontend"
@@ -35,7 +35,7 @@ build: build-go build-js
build-docker-dev: build-docker-dev:
@echo "build development container" @echo "build development container"
@echo "\033[92mInfo:\033[0m the frontend code is expected to be built already." @echo "\033[92mInfo:\033[0m the frontend code is expected to be built already."
GO111MODULE=on go run build.go -goos linux -pkg-arch amd64 ${OPT} build pkg-archive latest $(GO) run build.go -goos linux -pkg-arch amd64 ${OPT} build pkg-archive latest
cp dist/grafana-latest.linux-x64.tar.gz packaging/docker cp dist/grafana-latest.linux-x64.tar.gz packaging/docker
cd packaging/docker && docker build --tag grafana/grafana:dev . cd packaging/docker && docker build --tag grafana/grafana:dev .
@@ -45,7 +45,7 @@ build-docker-full:
test-go: test-go:
@echo "test backend" @echo "test backend"
GO111MODULE=on go test -v ./pkg/... $(GO) test -v ./pkg/...
test-js: test-js:
@echo "test frontend" @echo "test frontend"
@@ -107,7 +107,7 @@ golangci-lint: scripts/go/bin/golangci-lint
go-vet: go-vet:
@echo "lint via go vet" @echo "lint via go vet"
@go vet $(GO_FILES) @$(GO) vet $(GO_FILES)
lint-go: go-vet golangci-lint revive revive-alerting gosec lint-go: go-vet golangci-lint revive revive-alerting gosec
+26 -4
View File
@@ -147,12 +147,34 @@ Writing & watching frontend tests
```bash ```bash
# Run Golang tests using sqlite3 as database (default) # Run Golang tests using sqlite3 as database (default)
go test ./pkg/... go test ./pkg/...
```
# Run Golang tests using mysql as database - convenient to use /docker/blocks/mysql_tests ##### Running the MySQL or Postgres backend tests:
GRAFANA_TEST_DB=mysql go test ./pkg/...
# Run Golang tests using postgres as database - convenient to use /docker/blocks/postgres_tests Run these by setting `GRAFANA_TEST_DB` in your environment.
GRAFANA_TEST_DB=postgres go test ./pkg/...
- `GRAFANA_TEST_DB=mysql` to test MySQL
- `GRAFANA_TEST_DB=postgres` to test Postgres
Follow the instructions in `./devenv` to spin up test containers running the appropriate databases with `docker-compose`
- Use `docker/blocks/mysql_tests` or `docker/blocks/postgres_tests` as appropriate
```bash
# MySQL
# Tests can only be ran in one Go package at a time due to clashing db queries. To run MySQL tests for the "pkg/services/sqlstore" package, run:
GRAFANA_TEST_DB=mysql go test ./pkg/services/sqlstore/...
# Or run all the packages using the circle CI scripts. This method will be slower as the scripts will run all the tests, including the integration tests.
./scripts/circle-test-mysql.sh
```
```bash
# Postgres
# Tests can only be ran in one Go package at a time due to clashing db queries. To run Postgres tests for the "pkg/services/sqlstore" package, run:
GRAFANA_TEST_DB=postgres go test ./pkg/services/sqlstore/...
# Or run all the packages using the circle CI scripts. This method will be slower as the scripts will run all the tests, including the integration tests.
./scripts/circle-test-postgres.sh
``` ```
#### End-to-end #### End-to-end
@@ -5,7 +5,7 @@
# root_url = %(protocol)s://%(domain)s:10080/grafana/ # root_url = %(protocol)s://%(domain)s:10080/grafana/
nginxproxy: nginxproxy:
build: docker/blocks/nginx_proxy build: docker/blocks/nginx_proxy_mac
ports: ports:
- "10080:10080" - "10080:10080"
@@ -45,6 +45,8 @@ datasources:
password: $PASSWORD password: $PASSWORD
``` ```
If you have a literal `$` in your value and want to avoid interpolation, `$$` can be used.
<hr /> <hr />
## Configuration Management Tools ## Configuration Management Tools
+1 -1
View File
@@ -27,7 +27,7 @@ header_name = X-WEBAUTH-USER
header_property = username header_property = username
# Set to `true` to enable auto sign up of users who do not exist in Grafana DB. Defaults to `true`. # Set to `true` to enable auto sign up of users who do not exist in Grafana DB. Defaults to `true`.
auto_sign_up = true auto_sign_up = true
# If combined with Grafana LDAP integration define sync interval # If combined with Grafana LDAP integration define sync interval in minutes
ldap_sync_ttl = 60 ldap_sync_ttl = 60
# Limit where auth proxy requests come from by configuring a list of IP addresses. # Limit where auth proxy requests come from by configuring a list of IP addresses.
# This can be used to prevent users spoofing the X-WEBAUTH-USER header. # This can be used to prevent users spoofing the X-WEBAUTH-USER header.
+1 -1
View File
@@ -1,4 +1,4 @@
{ {
"stable": "6.2.5", "stable": "6.2.5",
"testing": "6.2.5" "testing": "6.3.0-beta1"
} }
+1 -1
View File
@@ -2,5 +2,5 @@
"npmClient": "yarn", "npmClient": "yarn",
"useWorkspaces": true, "useWorkspaces": true,
"packages": ["packages/*"], "packages": ["packages/*"],
"version": "6.3.0-alpha.36" "version": "6.4.0-alpha.12"
} }
+5 -4
View File
@@ -5,7 +5,7 @@
"company": "Grafana Labs" "company": "Grafana Labs"
}, },
"name": "grafana", "name": "grafana",
"version": "6.3.0-pre", "version": "6.4.0-pre",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "http://github.com/grafana/grafana.git" "url": "http://github.com/grafana/grafana.git"
@@ -89,7 +89,7 @@
"ng-annotate-loader": "0.6.1", "ng-annotate-loader": "0.6.1",
"ng-annotate-webpack-plugin": "0.3.0", "ng-annotate-webpack-plugin": "0.3.0",
"ngtemplate-loader": "2.0.1", "ngtemplate-loader": "2.0.1",
"node-sass": "4.11.0", "node-sass": "4.12.0",
"npm": "6.9.0", "npm": "6.9.0",
"optimize-css-assets-webpack-plugin": "5.0.1", "optimize-css-assets-webpack-plugin": "5.0.1",
"phantomjs-prebuilt": "2.1.16", "phantomjs-prebuilt": "2.1.16",
@@ -148,7 +148,8 @@
"themes:generate": "ts-node --project ./scripts/cli/tsconfig.json ./scripts/cli/generateSassVariableFiles.ts", "themes:generate": "ts-node --project ./scripts/cli/tsconfig.json ./scripts/cli/generateSassVariableFiles.ts",
"packages:prepare": "lerna run clean && npm run test && lerna version --tag-version-prefix=\"packages@\" -m \"Packages: publish %s\" --no-push", "packages:prepare": "lerna run clean && npm run test && lerna version --tag-version-prefix=\"packages@\" -m \"Packages: publish %s\" --no-push",
"packages:build": "lerna run clean && lerna run build", "packages:build": "lerna run clean && lerna run build",
"packages:publish": "lerna publish from-package --contents dist --tag-version-prefix=\"packages@\" --dist-tag next" "packages:publish": "lerna publish from-package --contents dist",
"packages:publishNext": "lerna publish from-package --contents dist --dist-tag next --yes"
}, },
"husky": { "husky": {
"hooks": { "hooks": {
@@ -201,7 +202,7 @@
"file-saver": "1.3.8", "file-saver": "1.3.8",
"immutable": "3.8.2", "immutable": "3.8.2",
"jquery": "3.4.1", "jquery": "3.4.1",
"lodash": "4.17.11", "lodash": "4.17.14",
"marked": "0.6.2", "marked": "0.6.2",
"moment": "2.24.0", "moment": "2.24.0",
"mousetrap": "1.6.3", "mousetrap": "1.6.3",
+1 -1
View File
@@ -1,3 +1,3 @@
# Grafana Data Library # Grafana Data Library
The core data components This package holds the root data types and functions used within Grafana.
+2 -6
View File
@@ -1,6 +1,6 @@
{ {
"name": "@grafana/data", "name": "@grafana/data",
"version": "6.3.0-alpha.36", "version": "6.4.0-alpha.12",
"description": "Grafana Data Library", "description": "Grafana Data Library",
"keywords": [ "keywords": [
"typescript" "typescript"
@@ -11,8 +11,7 @@
"typecheck": "tsc --noEmit", "typecheck": "tsc --noEmit",
"clean": "rimraf ./dist ./compiled", "clean": "rimraf ./dist ./compiled",
"bundle": "rollup -c rollup.config.ts", "bundle": "rollup -c rollup.config.ts",
"build": "grafana-toolkit package:build --scope=data", "build": "grafana-toolkit package:build --scope=data"
"postpublish": "npm run clean"
}, },
"author": "Grafana Labs", "author": "Grafana Labs",
"license": "Apache-2.0", "license": "Apache-2.0",
@@ -37,8 +36,5 @@
"rollup-plugin-visualizer": "0.9.2", "rollup-plugin-visualizer": "0.9.2",
"sinon": "1.17.6", "sinon": "1.17.6",
"typescript": "3.4.1" "typescript": "3.4.1"
},
"resolutions": {
"@types/lodash": "4.14.119"
} }
} }
+1 -1
View File
@@ -1,3 +1,3 @@
# Grafana Runtime library # Grafana Runtime library
Interfaces that let you use the runtime... This package allows access to grafana services. It requires Grafana to be running already and the functions to be imported as externals.
+3 -9
View File
@@ -1,11 +1,9 @@
{ {
"name": "@grafana/runtime", "name": "@grafana/runtime",
"version": "6.3.0-alpha.36", "version": "6.4.0-alpha.12",
"description": "Grafana Runtime Library", "description": "Grafana Runtime Library",
"keywords": [ "keywords": [
"typescript", "grafana"
"react",
"react-component"
], ],
"main": "src/index.ts", "main": "src/index.ts",
"scripts": { "scripts": {
@@ -13,8 +11,7 @@
"typecheck": "tsc --noEmit", "typecheck": "tsc --noEmit",
"clean": "rimraf ./dist ./compiled", "clean": "rimraf ./dist ./compiled",
"bundle": "rollup -c rollup.config.ts", "bundle": "rollup -c rollup.config.ts",
"build": "grafana-toolkit package:build --scope=runtime", "build": "grafana-toolkit package:build --scope=runtime"
"postpublish": "npm run clean"
}, },
"author": "Grafana Labs", "author": "Grafana Labs",
"license": "Apache-2.0", "license": "Apache-2.0",
@@ -35,8 +32,5 @@
"rollup-plugin-typescript2": "0.19.3", "rollup-plugin-typescript2": "0.19.3",
"rollup-plugin-visualizer": "0.9.2", "rollup-plugin-visualizer": "0.9.2",
"typescript": "3.4.1" "typescript": "3.4.1"
},
"resolutions": {
"@types/lodash": "4.14.119"
} }
} }
+7 -10
View File
@@ -1,11 +1,11 @@
{ {
"name": "@grafana/toolkit", "name": "@grafana/toolkit",
"version": "6.3.0-alpha.36", "version": "6.4.0-alpha.12",
"description": "Grafana Toolkit", "description": "Grafana Toolkit",
"keywords": [ "keywords": [
"typescript", "grafana",
"react", "cli",
"react-component" "plugins"
], ],
"bin": { "bin": {
"grafana-toolkit": "./bin/grafana-toolkit.js" "grafana-toolkit": "./bin/grafana-toolkit.js"
@@ -15,8 +15,7 @@
"typecheck": "tsc --noEmit", "typecheck": "tsc --noEmit",
"precommit": "npm run tslint & npm run typecheck", "precommit": "npm run tslint & npm run typecheck",
"clean": "rimraf ./dist ./compiled", "clean": "rimraf ./dist ./compiled",
"build": "grafana-toolkit toolkit:build", "build": "grafana-toolkit toolkit:build"
"postpublish": "npm run clean"
}, },
"author": "Grafana Labs", "author": "Grafana Labs",
"license": "Apache-2.0", "license": "Apache-2.0",
@@ -30,6 +29,7 @@
"@types/node": "^12.0.4", "@types/node": "^12.0.4",
"@types/react-dev-utils": "^9.0.1", "@types/react-dev-utils": "^9.0.1",
"@types/semver": "^6.0.0", "@types/semver": "^6.0.0",
"@types/tmp": "^0.1.0",
"@types/webpack": "4.4.34", "@types/webpack": "4.4.34",
"axios": "0.19.0", "axios": "0.19.0",
"babel-loader": "8.0.6", "babel-loader": "8.0.6",
@@ -47,7 +47,7 @@
"jest": "24.8.0", "jest": "24.8.0",
"jest-cli": "^24.8.0", "jest-cli": "^24.8.0",
"jest-coverage-badges": "^1.1.2", "jest-coverage-badges": "^1.1.2",
"lodash": "4.17.11", "lodash": "4.17.14",
"mini-css-extract-plugin": "^0.7.0", "mini-css-extract-plugin": "^0.7.0",
"node-sass": "^4.12.0", "node-sass": "^4.12.0",
"optimize-css-assets-webpack-plugin": "^5.0.3", "optimize-css-assets-webpack-plugin": "^5.0.3",
@@ -74,9 +74,6 @@
"url-loader": "^2.0.1", "url-loader": "^2.0.1",
"webpack": "4.35.0" "webpack": "4.35.0"
}, },
"resolutions": {
"@types/lodash": "4.14.119"
},
"devDependencies": { "devDependencies": {
"@types/glob": "^7.1.1", "@types/glob": "^7.1.1",
"@types/prettier": "^1.16.4" "@types/prettier": "^1.16.4"
+51 -6
View File
@@ -13,7 +13,14 @@ import { pluginTestTask } from './tasks/plugin.tests';
import { searchTestDataSetupTask } from './tasks/searchTestDataSetup'; import { searchTestDataSetupTask } from './tasks/searchTestDataSetup';
import { closeMilestoneTask } from './tasks/closeMilestone'; import { closeMilestoneTask } from './tasks/closeMilestone';
import { pluginDevTask } from './tasks/plugin.dev'; import { pluginDevTask } from './tasks/plugin.dev';
import { pluginCITask } from './tasks/plugin.ci'; import {
ciBuildPluginTask,
ciBuildPluginDocsTask,
ciBundlePluginTask,
ciTestPluginTask,
ciPluginReportTask,
ciDeployPluginTask,
} from './tasks/plugin.ci';
import { buildPackageTask } from './tasks/package.build'; import { buildPackageTask } from './tasks/package.build';
export const run = (includeInternalScripts = false) => { export const run = (includeInternalScripts = false) => {
@@ -141,15 +148,53 @@ export const run = (includeInternalScripts = false) => {
}); });
program program
.command('plugin:ci') .command('plugin:ci-build')
.option('--dryRun', "Dry run (don't post results)") .option('--backend <backend>', 'For backend task, which backend to run')
.description('Run Plugin CI task') .description('Build the plugin, leaving artifacts in /dist')
.action(async cmd => { .action(async cmd => {
await execTask(pluginCITask)({ await execTask(ciBuildPluginTask)({
dryRun: cmd.dryRun, backend: cmd.backend,
}); });
}); });
program
.command('plugin:ci-docs')
.description('Build the HTML docs')
.action(async cmd => {
await execTask(ciBuildPluginDocsTask)({});
});
program
.command('plugin:ci-bundle')
.description('Create a zip artifact for the plugin')
.action(async cmd => {
await execTask(ciBundlePluginTask)({});
});
program
.command('plugin:ci-test')
.option('--full', 'run all the tests (even stuff that will break)')
.description('end-to-end test using bundle in /artifacts')
.action(async cmd => {
await execTask(ciTestPluginTask)({
full: cmd.full,
});
});
program
.command('plugin:ci-report')
.description('Build a report for this whole process')
.action(async cmd => {
await execTask(ciPluginReportTask)({});
});
program
.command('plugin:ci-deploy')
.description('Publish plugin CI results')
.action(async cmd => {
await execTask(ciDeployPluginTask)({});
});
program.on('command:*', () => { program.on('command:*', () => {
console.error('Invalid command: %s\nSee --help for a list of available commands.', program.args.join(' ')); console.error('Invalid command: %s\nSee --help for a list of available commands.', program.args.join(' '));
process.exit(1); process.exit(1);
@@ -99,4 +99,4 @@ const buildTaskRunner: TaskRunner<PackageBuildOptions> = async ({ scope }) => {
await Promise.all(scopes.map(s => s())); await Promise.all(scopes.map(s => s()));
}; };
export const buildPackageTask = new Task<PackageBuildOptions>('@grafana/ui build', buildTaskRunner); export const buildPackageTask = new Task<PackageBuildOptions>('Package build', buildTaskRunner);
@@ -9,7 +9,8 @@ import path = require('path');
import fs = require('fs'); import fs = require('fs');
export interface PluginCIOptions { export interface PluginCIOptions {
dryRun?: boolean; backend?: string;
full?: boolean;
} }
const calcJavascriptSize = (base: string, files?: string[]): number => { const calcJavascriptSize = (base: string, files?: string[]): number => {
@@ -32,46 +33,353 @@ const calcJavascriptSize = (base: string, files?: string[]): number => {
return size; return size;
}; };
const pluginCIRunner: TaskRunner<PluginCIOptions> = async ({ dryRun }) => { const getJobFromProcessArgv = () => {
const arg = process.argv[2];
if (arg && arg.startsWith('plugin:ci-')) {
const task = arg.substring('plugin:ci-'.length);
if ('build' === task) {
if ('--platform' === process.argv[3] && process.argv[4]) {
return task + '_' + process.argv[4];
}
return 'build_nodejs';
}
return task;
}
return 'unknown_job';
};
const job = process.env.CIRCLE_JOB || getJobFromProcessArgv();
const getJobFolder = () => {
const dir = path.resolve(process.cwd(), 'ci', 'jobs', job);
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
return dir;
};
const getCiFolder = () => {
const dir = path.resolve(process.cwd(), 'ci');
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
return dir;
};
const writeJobStats = (startTime: number, workDir: string) => {
const stats = {
job,
startTime,
endTime: Date.now(),
};
const f = path.resolve(workDir, 'stats.json');
fs.writeFile(f, JSON.stringify(stats, null, 2), err => {
if (err) {
throw new Error('Unable to stats: ' + f);
}
});
};
/**
* 1. BUILD
*
* when platform exists it is building backend, otherwise frontend
*
* Each build writes data:
* ~/work/build_xxx/
*
* Anything that should be put into the final zip file should be put in:
* ~/work/build_xxx/dist
*/
const buildPluginRunner: TaskRunner<PluginCIOptions> = async ({ backend }) => {
const start = Date.now(); const start = Date.now();
const distDir = `${process.cwd()}/dist`; const workDir = getJobFolder();
const artifactsDir = `${process.cwd()}/artifacts`; await execa('rimraf', [workDir]);
await execa('rimraf', [`${process.cwd()}/coverage`]); fs.mkdirSync(workDir);
await execa('rimraf', [artifactsDir]);
// Do regular build process if (backend) {
await pluginBuildRunner({ coverage: true }); console.log('TODO, backend support?');
const elapsed = Date.now() - start; fs.mkdirSync(path.resolve(process.cwd(), 'dist'));
const file = path.resolve(process.cwd(), 'dist', `README_${backend}.txt`);
if (!fs.existsSync(artifactsDir)) { fs.writeFile(file, `TODO... build bakend plugin: ${backend}!`, err => {
fs.mkdirSync(artifactsDir); if (err) {
throw new Error('Unable to write: ' + file);
}
});
} else {
// Do regular build process with coverage
await pluginBuildRunner({ coverage: true });
} }
// TODO? can this typed from @grafana/ui? // Move local folders to the scoped job folder
for (const name of ['dist', 'coverage']) {
const dir = path.resolve(process.cwd(), name);
if (fs.existsSync(dir)) {
fs.renameSync(dir, path.resolve(workDir, name));
}
}
writeJobStats(start, workDir);
};
export const ciBuildPluginTask = new Task<PluginCIOptions>('Build Plugin', buildPluginRunner);
/**
* 2. Build Docs
*
* Take /docs/* and format it into /ci/docs/HTML site
*
*/
const buildPluginDocsRunner: TaskRunner<PluginCIOptions> = async () => {
const docsSrc = path.resolve(process.cwd(), 'docs');
if (!fs.existsSync(docsSrc)) {
throw new Error('Docs folder does not exist!');
}
const start = Date.now();
const workDir = getJobFolder();
await execa('rimraf', [workDir]);
fs.mkdirSync(workDir);
const docsDest = path.resolve(process.cwd(), 'ci', 'docs');
fs.mkdirSync(docsDest);
const exe = await execa('cp', ['-rv', docsSrc + '/.', docsDest]);
console.log(exe.stdout);
fs.writeFile(path.resolve(docsDest, 'index.html'), `TODO... actually build docs`, err => {
if (err) {
throw new Error('Unable to docs');
}
});
writeJobStats(start, workDir);
};
export const ciBuildPluginDocsTask = new Task<PluginCIOptions>('Build Plugin Docs', buildPluginDocsRunner);
/**
* 2. BUNDLE
*
* Take everything from `~/ci/job/{any}/dist` and
* 1. merge it into: `~/ci/dist`
* 2. zip it into artifacts in `~/ci/artifacts`
* 3. prepare grafana environment in: `~/ci/grafana-test-env`
*
*/
const bundlePluginRunner: TaskRunner<PluginCIOptions> = async () => {
const start = Date.now();
const ciDir = getCiFolder();
const artifactsDir = path.resolve(ciDir, 'artifacts');
const distDir = path.resolve(ciDir, 'dist');
const docsDir = path.resolve(ciDir, 'docs');
const grafanaEnvDir = path.resolve(ciDir, 'grafana-test-env');
await execa('rimraf', [artifactsDir, distDir, grafanaEnvDir]);
fs.mkdirSync(artifactsDir);
fs.mkdirSync(distDir);
fs.mkdirSync(grafanaEnvDir);
console.log('Build Dist Folder');
// 1. Check for a local 'dist' folder
const d = path.resolve(process.cwd(), 'dist');
if (fs.existsSync(d)) {
await execa('cp', ['-rn', d + '/.', distDir]);
}
// 2. Look for any 'dist' folders under ci/job/XXX/dist
const dirs = fs.readdirSync(path.resolve(ciDir, 'jobs'));
for (const j of dirs) {
const contents = path.resolve(ciDir, 'jobs', j, 'dist');
if (fs.existsSync(contents)) {
try {
await execa('cp', ['-rn', contents + '/.', distDir]);
} catch (er) {
throw new Error('Duplicate files found in dist folders');
}
}
}
console.log('Building ZIP');
const pluginInfo = getPluginJson(`${distDir}/plugin.json`); const pluginInfo = getPluginJson(`${distDir}/plugin.json`);
const zipName = pluginInfo.id + '-' + pluginInfo.info.version + '.zip'; let zipName = pluginInfo.id + '-' + pluginInfo.info.version + '.zip';
const zipFile = path.resolve(artifactsDir, zipName); let zipFile = path.resolve(artifactsDir, zipName);
process.chdir(distDir); process.chdir(distDir);
await execa('zip', ['-r', zipFile, '.']); await execa('zip', ['-r', zipFile, '.']);
restoreCwd(); restoreCwd();
const stats = { const zipStats = fs.statSync(zipFile);
startTime: start, if (zipStats.size < 100) {
buildTime: elapsed, throw new Error('Invalid zip file: ' + zipFile);
jsSize: calcJavascriptSize(distDir), }
zipSize: fs.statSync(zipFile).size,
endTime: Date.now(), const zipInfo: any = {
name: zipName,
size: zipStats.size,
}; };
fs.writeFile(artifactsDir + '/stats.json', JSON.stringify(stats, null, 2), err => { const info: any = {
plugin: zipInfo,
};
try {
const exe = await execa('shasum', [zipFile]);
const idx = exe.stdout.indexOf(' ');
const sha1 = exe.stdout.substring(0, idx);
fs.writeFile(zipFile + '.sha1', sha1, err => {});
zipInfo.sha1 = sha1;
} catch {
console.warn('Unable to read SHA1 Checksum');
}
// If docs exist, zip them into artifacts
if (fs.existsSync(docsDir)) {
zipName = pluginInfo.id + '-' + pluginInfo.info.version + '-docs.zip';
zipFile = path.resolve(artifactsDir, zipName);
process.chdir(docsDir);
await execa('zip', ['-r', zipFile, '.']);
restoreCwd();
const zipStats = fs.statSync(zipFile);
const zipInfo: any = {
name: zipName,
size: zipStats.size,
};
try {
const exe = await execa('shasum', [zipFile]);
const idx = exe.stdout.indexOf(' ');
const sha1 = exe.stdout.substring(0, idx);
fs.writeFile(zipFile + '.sha1', sha1, err => {});
zipInfo.sha1 = sha1;
} catch {
console.warn('Unable to read SHA1 Checksum');
}
info.docs = zipInfo;
}
let p = path.resolve(artifactsDir, 'info.json');
fs.writeFile(p, JSON.stringify(info, null, 2), err => {
if (err) { if (err) {
throw new Error('Unable to write stats'); throw new Error('Error writing artifact info: ' + p);
} }
console.log('Stats', stats);
}); });
if (!dryRun) { console.log('Setup Grafan Environment');
console.log('TODO send info to github?'); p = path.resolve(grafanaEnvDir, 'plugins', pluginInfo.id);
} fs.mkdirSync(p, { recursive: true });
await execa('unzip', [zipFile, '-d', p]);
// Write the custom settings
p = path.resolve(grafanaEnvDir, 'custom.ini');
const customIniBody =
`# Autogenerated by @grafana/toolkit \n` +
`[paths] \n` +
`plugins = ${path.resolve(grafanaEnvDir, 'plugins')}\n` +
`\n`; // empty line
fs.writeFile(p, customIniBody, err => {
if (err) {
throw new Error('Unable to write: ' + p);
}
});
writeJobStats(start, getJobFolder());
}; };
export const pluginCITask = new Task<PluginCIOptions>('Plugin CI', pluginCIRunner); export const ciBundlePluginTask = new Task<PluginCIOptions>('Bundle Plugin', bundlePluginRunner);
/**
* 3. Test (end-to-end)
*
* deploy the zip to a running grafana instance
*
*/
const testPluginRunner: TaskRunner<PluginCIOptions> = async ({ full }) => {
const start = Date.now();
const workDir = getJobFolder();
const pluginInfo = getPluginJson(`${process.cwd()}/src/plugin.json`);
const args = {
withCredentials: true,
baseURL: process.env.GRAFANA_URL || 'http://localhost:3000/',
responseType: 'json',
auth: {
username: 'admin',
password: 'admin',
},
};
const axios = require('axios');
const frontendSettings = await axios.get('api/frontend/settings', args);
console.log('Grafana Version: ' + JSON.stringify(frontendSettings.data.buildInfo, null, 2));
const allPlugins: any[] = await axios.get('api/plugins', args).data;
// for (const plugin of allPlugins) {
// if (plugin.id === pluginInfo.id) {
// console.log('------------');
// console.log(plugin);
// console.log('------------');
// } else {
// console.log('Plugin:', plugin.id, plugin.latestVersion);
// }
// }
console.log('PLUGINS:', allPlugins);
if (full) {
const pluginSettings = await axios.get(`api/plugins/${pluginInfo.id}/settings`, args);
console.log('Plugin Info: ' + JSON.stringify(pluginSettings.data, null, 2));
}
console.log('TODO puppeteer');
const elapsed = Date.now() - start;
const stats = {
job,
sha1: `${process.env.CIRCLE_SHA1}`,
startTime: start,
buildTime: elapsed,
endTime: Date.now(),
};
console.log('TODO Puppeteer Tests', stats);
writeJobStats(start, workDir);
};
export const ciTestPluginTask = new Task<PluginCIOptions>('Test Plugin (e2e)', testPluginRunner);
/**
* 4. Report
*
* Create a report from all the previous steps
*
*/
const pluginReportRunner: TaskRunner<PluginCIOptions> = async () => {
const start = Date.now();
const workDir = getJobFolder();
const reportDir = path.resolve(process.cwd(), 'ci', 'report');
await execa('rimraf', [reportDir]);
fs.mkdirSync(reportDir);
const file = path.resolve(reportDir, `report.txt`);
fs.writeFile(file, `TODO... actually make a report (csv etc)`, err => {
if (err) {
throw new Error('Unable to write: ' + file);
}
});
console.log('TODO... real report');
writeJobStats(start, workDir);
};
export const ciPluginReportTask = new Task<PluginCIOptions>('Deploy plugin', pluginReportRunner);
/**
* 5. Deploy
*
* deploy the zip to a running grafana instance
*
*/
const deployPluginRunner: TaskRunner<PluginCIOptions> = async () => {
console.log('TODO DEPLOY??');
console.log(' if PR => write a comment to github with difference ');
console.log(' if master | vXYZ ==> upload artifacts to some repo ');
};
export const ciDeployPluginTask = new Task<PluginCIOptions>('Deploy plugin', deployPluginRunner);
@@ -3,7 +3,7 @@ import { getPluginJson, validatePluginJson } from './pluginValidation';
describe('pluginValdation', () => { describe('pluginValdation', () => {
describe('plugin.json', () => { describe('plugin.json', () => {
test('missing plugin.json file', () => { test('missing plugin.json file', () => {
expect(() => getPluginJson(`${__dirname}/mocks/missing-plugin-json`)).toThrow('plugin.json file is missing!'); expect(() => getPluginJson(`${__dirname}/mocks/missing-plugin.json`)).toThrowError();
}); });
}); });
@@ -1,5 +1,3 @@
import path = require('path');
// See: packages/grafana-ui/src/types/plugin.ts // See: packages/grafana-ui/src/types/plugin.ts
interface PluginJSONSchema { interface PluginJSONSchema {
id: string; id: string;
@@ -22,15 +20,24 @@ export const validatePluginJson = (pluginJson: any) => {
if (!pluginJson.info.version) { if (!pluginJson.info.version) {
throw new Error('Plugin info.version is missing in plugin.json'); throw new Error('Plugin info.version is missing in plugin.json');
} }
const types = ['panel', 'datasource', 'app'];
const type = pluginJson.type;
if (!types.includes(type)) {
throw new Error('Invalid plugin type in plugin.json: ' + type);
}
if (!pluginJson.id.endsWith('-' + type)) {
throw new Error('[plugin.json] id should end with: -' + type);
}
}; };
export const getPluginJson = (root: string = process.cwd()): PluginJSONSchema => { export const getPluginJson = (path: string): PluginJSONSchema => {
let pluginJson; let pluginJson;
try { try {
pluginJson = require(path.resolve(root, 'src/plugin.json')); pluginJson = require(path);
} catch (e) { } catch (e) {
throw new Error('plugin.json file is missing!'); throw new Error('Unable to find: ' + path);
} }
validatePluginJson(pluginJson); validatePluginJson(pluginJson);
+5 -8
View File
@@ -1,9 +1,9 @@
{ {
"name": "@grafana/ui", "name": "@grafana/ui",
"version": "6.3.0-alpha.36", "version": "6.4.0-alpha.12",
"description": "Grafana Components Library", "description": "Grafana Components Library",
"keywords": [ "keywords": [
"typescript", "grafana",
"react", "react",
"react-component" "react-component"
], ],
@@ -15,18 +15,18 @@
"storybook:build": "build-storybook -o ./dist/storybook -c .storybook", "storybook:build": "build-storybook -o ./dist/storybook -c .storybook",
"clean": "rimraf ./dist ./compiled", "clean": "rimraf ./dist ./compiled",
"bundle": "rollup -c rollup.config.ts", "bundle": "rollup -c rollup.config.ts",
"build": "grafana-toolkit package:build --scope=ui", "build": "grafana-toolkit package:build --scope=ui"
"postpublish": "npm run clean"
}, },
"author": "Grafana Labs", "author": "Grafana Labs",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@grafana/data": "^6.4.0-alpha.8",
"@torkelo/react-select": "2.1.1", "@torkelo/react-select": "2.1.1",
"@types/react-color": "2.17.0", "@types/react-color": "2.17.0",
"classnames": "2.2.6", "classnames": "2.2.6",
"d3": "5.9.1", "d3": "5.9.1",
"jquery": "3.4.1", "jquery": "3.4.1",
"lodash": "4.17.11", "lodash": "4.17.14",
"moment": "2.24.0", "moment": "2.24.0",
"papaparse": "4.6.3", "papaparse": "4.6.3",
"react": "16.8.6", "react": "16.8.6",
@@ -77,8 +77,5 @@
"rollup-plugin-typescript2": "0.19.3", "rollup-plugin-typescript2": "0.19.3",
"rollup-plugin-visualizer": "0.9.2", "rollup-plugin-visualizer": "0.9.2",
"typescript": "3.4.1" "typescript": "3.4.1"
},
"resolutions": {
"@types/lodash": "4.14.119"
} }
} }
@@ -29,7 +29,7 @@ export class TableInputCSV extends React.PureComponent<Props, State> {
}; };
} }
readCSV = debounce(() => { readCSV: any = debounce(() => {
const { config } = this.props; const { config } = this.props;
const { text } = this.state; const { text } = this.state;
+4 -1
View File
@@ -68,7 +68,10 @@ func (hs *HTTPServer) AddAPIKey(c *models.ReqContext, cmd models.AddApiKeyComman
if err == models.ErrInvalidApiKeyExpiration { if err == models.ErrInvalidApiKeyExpiration {
return Error(400, err.Error(), nil) return Error(400, err.Error(), nil)
} }
return Error(500, "Failed to add API key", err) if err == models.ErrDuplicateApiKey {
return Error(409, err.Error(), nil)
}
return Error(500, "Failed to add API Key", err)
} }
result := &dtos.NewApiKeyResult{ result := &dtos.NewApiKeyResult{
+1 -2
View File
@@ -278,8 +278,7 @@ func (hs *HTTPServer) PostDashboard(c *m.ReqContext, cmd m.SaveDashboardCommand)
inFolder := cmd.FolderId > 0 inFolder := cmd.FolderId > 0
err := dashboards.MakeUserAdmin(hs.Bus, cmd.OrgId, cmd.UserId, dashboard.Id, !inFolder) err := dashboards.MakeUserAdmin(hs.Bus, cmd.OrgId, cmd.UserId, dashboard.Id, !inFolder)
if err != nil { if err != nil {
hs.log.Error("Could not make user admin", "dashboard", cmd.Result.Title, "user", c.SignedInUser.UserId, "error", err) hs.log.Error("Could not make user admin", "dashboard", dashboard.Title, "user", c.SignedInUser.UserId, "error", err)
return Error(500, "Failed to make user admin of dashboard", err)
} }
} }
-1
View File
@@ -64,7 +64,6 @@ func (hs *HTTPServer) CreateFolder(c *m.ReqContext, cmd m.CreateFolderCommand) R
if hs.Cfg.EditorsCanAdmin { if hs.Cfg.EditorsCanAdmin {
if err := dashboards.MakeUserAdmin(hs.Bus, c.OrgId, c.SignedInUser.UserId, cmd.Result.Id, true); err != nil { if err := dashboards.MakeUserAdmin(hs.Bus, c.OrgId, c.SignedInUser.UserId, cmd.Result.Id, true); err != nil {
hs.log.Error("Could not make user admin", "folder", cmd.Result.Title, "user", c.SignedInUser.UserId, "error", err) hs.log.Error("Could not make user admin", "folder", cmd.Result.Title, "user", c.SignedInUser.UserId, "error", err)
return Error(500, "Failed to make user admin of folder", err)
} }
} }
+1
View File
@@ -7,6 +7,7 @@ import (
var ErrInvalidApiKey = errors.New("Invalid API Key") var ErrInvalidApiKey = errors.New("Invalid API Key")
var ErrInvalidApiKeyExpiration = errors.New("Negative value for SecondsToLive") var ErrInvalidApiKeyExpiration = errors.New("Negative value for SecondsToLive")
var ErrDuplicateApiKey = errors.New("API Key Organization ID And Name Must Be Unique")
type ApiKey struct { type ApiKey struct {
Id int64 Id int64
+8 -1
View File
@@ -14,6 +14,7 @@ import (
"os" "os"
"reflect" "reflect"
"strconv" "strconv"
"strings"
"github.com/grafana/grafana/pkg/util/errutil" "github.com/grafana/grafana/pkg/util/errutil"
) )
@@ -185,8 +186,14 @@ func transformMap(i map[interface{}]interface{}) interface{} {
// interpolateValue returns final value after interpolation. At the moment only env var interpolation is done // interpolateValue returns final value after interpolation. At the moment only env var interpolation is done
// here but in the future something like interpolation from file could be also done here. // here but in the future something like interpolation from file could be also done here.
// For a literal '$', '$$' can be used to avoid interpolation.
func interpolateValue(val string) string { func interpolateValue(val string) string {
return os.ExpandEnv(val) parts := strings.Split(val, "$$")
interpolated := make([]string, len(parts))
for i, v := range parts {
interpolated[i] = os.ExpandEnv(v)
}
return strings.Join(interpolated, "$")
} }
type interpolated struct { type interpolated struct {
@@ -1,16 +1,18 @@
package values package values
import ( import (
. "github.com/smartystreets/goconvey/convey"
"gopkg.in/yaml.v2"
"os" "os"
"testing" "testing"
. "github.com/smartystreets/goconvey/convey"
"gopkg.in/yaml.v2"
) )
func TestValues(t *testing.T) { func TestValues(t *testing.T) {
Convey("Values", t, func() { Convey("Values", t, func() {
os.Setenv("INT", "1") os.Setenv("INT", "1")
os.Setenv("STRING", "test") os.Setenv("STRING", "test")
os.Setenv("EMPTYSTRING", "")
os.Setenv("BOOL", "true") os.Setenv("BOOL", "true")
Convey("IntValue", func() { Convey("IntValue", func() {
@@ -61,6 +63,24 @@ func TestValues(t *testing.T) {
So(d.Val.Value(), ShouldEqual, "") So(d.Val.Value(), ShouldEqual, "")
So(d.Val.Raw, ShouldEqual, "") So(d.Val.Raw, ShouldEqual, "")
}) })
Convey("empty var should have empty value", func() {
unmarshalingTest(`val: $EMPTYSTRING`, d)
So(d.Val.Value(), ShouldEqual, "")
So(d.Val.Raw, ShouldEqual, "$EMPTYSTRING")
})
Convey("$$ should be a literal $", func() {
unmarshalingTest(`val: $$`, d)
So(d.Val.Value(), ShouldEqual, "$")
So(d.Val.Raw, ShouldEqual, "$$")
})
Convey("$$ should be a literal $ and not expanded within a string", func() {
unmarshalingTest(`val: mY,Passwo$$rd`, d)
So(d.Val.Value(), ShouldEqual, "mY,Passwo$rd")
So(d.Val.Raw, ShouldEqual, "mY,Passwo$$rd")
})
}) })
Convey("BoolValue", func() { Convey("BoolValue", func() {
@@ -199,6 +219,7 @@ func TestValues(t *testing.T) {
Reset(func() { Reset(func() {
os.Unsetenv("INT") os.Unsetenv("INT")
os.Unsetenv("STRING") os.Unsetenv("STRING")
os.Unsetenv("EMPTYSTRING")
os.Unsetenv("BOOL") os.Unsetenv("BOOL")
}) })
}) })
+6
View File
@@ -37,6 +37,12 @@ func DeleteApiKeyCtx(ctx context.Context, cmd *models.DeleteApiKeyCommand) error
func AddApiKey(cmd *models.AddApiKeyCommand) error { func AddApiKey(cmd *models.AddApiKeyCommand) error {
return inTransaction(func(sess *DBSession) error { return inTransaction(func(sess *DBSession) error {
key := models.ApiKey{OrgId: cmd.OrgId, Name: cmd.Name}
exists, _ := sess.Get(&key)
if exists {
return models.ErrDuplicateApiKey
}
updated := timeNow() updated := timeNow()
var expires *int64 = nil var expires *int64 = nil
if cmd.SecondsToLive > 0 { if cmd.SecondsToLive > 0 {
+20
View File
@@ -115,3 +115,23 @@ func TestApiKeyDataAccess(t *testing.T) {
}) })
}) })
} }
func TestApiKeyErrors(t *testing.T) {
mockTimeNow()
defer resetTimeNow()
t.Run("Testing API Duplicate Key Errors", func(t *testing.T) {
InitTestDB(t)
t.Run("Given saved api key", func(t *testing.T) {
cmd := models.AddApiKeyCommand{OrgId: 0, Name: "duplicate", Key: "asd"}
err := AddApiKey(&cmd)
assert.Nil(t, err)
t.Run("Add API Key with existing Org ID and Name", func(t *testing.T) {
cmd := models.AddApiKeyCommand{OrgId: 0, Name: "duplicate", Key: "asd"}
err = AddApiKey(&cmd)
assert.EqualError(t, err, models.ErrDuplicateApiKey.Error())
})
})
})
}
+2 -6
View File
@@ -324,10 +324,6 @@ export function logSeriesToLogsModel(logSeries: DataFrame[]): LogsModel {
} }
} }
const sortedRows = rows.sort((a, b) => {
return a.timestamp > b.timestamp ? -1 : 1;
});
// Meta data to display in status // Meta data to display in status
const meta: LogsMetaItem[] = []; const meta: LogsMetaItem[] = [];
if (_.size(commonLabels) > 0) { if (_.size(commonLabels) > 0) {
@@ -343,7 +339,7 @@ export function logSeriesToLogsModel(logSeries: DataFrame[]): LogsModel {
if (limits.length > 0) { if (limits.length > 0) {
meta.push({ meta.push({
label: 'Limit', label: 'Limit',
value: `${limits[0].meta.limit} (${sortedRows.length} returned)`, value: `${limits[0].meta.limit} (${rows.length} returned)`,
kind: LogsMetaKind.String, kind: LogsMetaKind.String,
}); });
} }
@@ -351,7 +347,7 @@ export function logSeriesToLogsModel(logSeries: DataFrame[]): LogsModel {
return { return {
hasUniqueLabels, hasUniqueLabels,
meta, meta,
rows: sortedRows, rows,
}; };
} }
+13 -13
View File
@@ -418,13 +418,6 @@ describe('dataFrameToLogsModel', () => {
expect(logsModel.hasUniqueLabels).toBeFalsy(); expect(logsModel.hasUniqueLabels).toBeFalsy();
expect(logsModel.rows).toHaveLength(2); expect(logsModel.rows).toHaveLength(2);
expect(logsModel.rows).toMatchObject([ expect(logsModel.rows).toMatchObject([
{
timestamp: '2019-04-26T14:42:50.991981292Z',
entry: 't=2019-04-26T16:42:50+0200 lvl=eror msg="new token…t unhashed token=56d9fdc5c8b7400bd51b060eea8ca9d7',
labels: { filename: '/var/log/grafana/grafana.log', job: 'grafana' },
logLevel: 'error',
uniqueLabels: {},
},
{ {
timestamp: '2019-04-26T09:28:11.352440161Z', timestamp: '2019-04-26T09:28:11.352440161Z',
entry: 't=2019-04-26T11:05:28+0200 lvl=info msg="Initializing DatasourceCacheService" logger=server', entry: 't=2019-04-26T11:05:28+0200 lvl=info msg="Initializing DatasourceCacheService" logger=server',
@@ -432,6 +425,13 @@ describe('dataFrameToLogsModel', () => {
logLevel: 'info', logLevel: 'info',
uniqueLabels: {}, uniqueLabels: {},
}, },
{
timestamp: '2019-04-26T14:42:50.991981292Z',
entry: 't=2019-04-26T16:42:50+0200 lvl=eror msg="new token…t unhashed token=56d9fdc5c8b7400bd51b060eea8ca9d7',
labels: { filename: '/var/log/grafana/grafana.log', job: 'grafana' },
logLevel: 'error',
uniqueLabels: {},
},
]); ]);
expect(logsModel.series).toHaveLength(2); expect(logsModel.series).toHaveLength(2);
@@ -524,12 +524,6 @@ describe('dataFrameToLogsModel', () => {
expect(logsModel.hasUniqueLabels).toBeTruthy(); expect(logsModel.hasUniqueLabels).toBeTruthy();
expect(logsModel.rows).toHaveLength(3); expect(logsModel.rows).toHaveLength(3);
expect(logsModel.rows).toMatchObject([ expect(logsModel.rows).toMatchObject([
{
entry: 'INFO 2',
labels: { foo: 'bar', baz: '2' },
logLevel: LogLevel.error,
uniqueLabels: { baz: '2' },
},
{ {
entry: 'WARN boooo', entry: 'WARN boooo',
labels: { foo: 'bar', baz: '1' }, labels: { foo: 'bar', baz: '1' },
@@ -542,6 +536,12 @@ describe('dataFrameToLogsModel', () => {
logLevel: LogLevel.error, logLevel: LogLevel.error,
uniqueLabels: { baz: '2' }, uniqueLabels: { baz: '2' },
}, },
{
entry: 'INFO 2',
labels: { foo: 'bar', baz: '2' },
logLevel: LogLevel.error,
uniqueLabels: { baz: '2' },
},
]); ]);
expect(logsModel.series).toHaveLength(2); expect(logsModel.series).toHaveLength(2);
+4 -4
View File
@@ -487,11 +487,11 @@ export const getRefIds = (value: any): string[] => {
}; };
const sortInAscendingOrder = (a: LogRowModel, b: LogRowModel) => { const sortInAscendingOrder = (a: LogRowModel, b: LogRowModel) => {
if (a.timeEpochMs < b.timeEpochMs) { if (a.timestamp < b.timestamp) {
return -1; return -1;
} }
if (a.timeEpochMs > b.timeEpochMs) { if (a.timestamp > b.timestamp) {
return 1; return 1;
} }
@@ -499,11 +499,11 @@ const sortInAscendingOrder = (a: LogRowModel, b: LogRowModel) => {
}; };
const sortInDescendingOrder = (a: LogRowModel, b: LogRowModel) => { const sortInDescendingOrder = (a: LogRowModel, b: LogRowModel) => {
if (a.timeEpochMs > b.timeEpochMs) { if (a.timestamp > b.timestamp) {
return -1; return -1;
} }
if (a.timeEpochMs < b.timeEpochMs) { if (a.timestamp < b.timestamp) {
return 1; return 1;
} }
@@ -72,9 +72,10 @@ export class ResultProcessor {
const graphInterval = this.state.queryIntervals.intervalMs; const graphInterval = this.state.queryIntervals.intervalMs;
const dataFrame = this.rawData.map(result => guessFieldTypes(toDataFrame(result))); const dataFrame = this.rawData.map(result => guessFieldTypes(toDataFrame(result)));
const newResults = this.rawData ? dataFrameToLogsModel(dataFrame, graphInterval) : null; const newResults = this.rawData ? dataFrameToLogsModel(dataFrame, graphInterval) : null;
const sortedNewResults = sortLogsResult(newResults, this.state.refreshInterval);
if (this.replacePreviousResults) { if (this.replacePreviousResults) {
return newResults; return sortedNewResults;
} }
const prevLogsResult: LogsModel = this.state.logsResult || { hasUniqueLabels: false, rows: [] }; const prevLogsResult: LogsModel = this.state.logsResult || { hasUniqueLabels: false, rows: [] };
@@ -86,17 +87,17 @@ export class ResultProcessor {
for (const row of rowsInState) { for (const row of rowsInState) {
processedRows.push({ ...row, fresh: false }); processedRows.push({ ...row, fresh: false });
} }
for (const row of newResults.rows) { for (const row of sortedNewResults.rows) {
processedRows.push({ ...row, fresh: true }); processedRows.push({ ...row, fresh: true });
} }
const processedSeries = this.mergeGraphResults(newResults.series, seriesInState); const processedSeries = this.mergeGraphResults(sortedNewResults.series, seriesInState);
const slice = -1000; const slice = -1000;
const rows = processedRows.slice(slice); const rows = processedRows.slice(slice);
const series = processedSeries.slice(slice); const series = processedSeries.slice(slice);
return { ...newResults, rows, series }; return { ...sortedNewResults, rows, series };
}; };
private makeTimeSeriesList = (rawData: any[]) => { private makeTimeSeriesList = (rawData: any[]) => {
+22 -22
View File
@@ -4,7 +4,7 @@ import { variableRegex } from 'app/features/templating/variable';
import { ScopedVars } from '@grafana/ui'; import { ScopedVars } from '@grafana/ui';
import { TimeRange } from '@grafana/data'; import { TimeRange } from '@grafana/data';
function luceneEscape(value) { function luceneEscape(value: string) {
return value.replace(/([\!\*\+\-\=<>\s\&\|\(\)\[\]\{\}\^\~\?\:\\/"])/g, '\\$1'); return value.replace(/([\!\*\+\-\=<>\s\&\|\(\)\[\]\{\}\^\~\?\:\\/"])/g, '\\$1');
} }
@@ -12,8 +12,8 @@ export class TemplateSrv {
variables: any[]; variables: any[];
private regex = variableRegex; private regex = variableRegex;
private index = {}; private index: any = {};
private grafanaVariables = {}; private grafanaVariables: any = {};
private builtIns: any = {}; private builtIns: any = {};
private timeRange: TimeRange = null; private timeRange: TimeRange = null;
@@ -23,7 +23,7 @@ export class TemplateSrv {
this.variables = []; this.variables = [];
} }
init(variables, timeRange?: TimeRange) { init(variables: any, timeRange?: TimeRange) {
this.variables = variables; this.variables = variables;
this.timeRange = timeRange; this.timeRange = timeRange;
this.updateIndex(); this.updateIndex();
@@ -34,7 +34,7 @@ export class TemplateSrv {
} }
updateIndex() { updateIndex() {
const existsOrEmpty = value => value || value === ''; const existsOrEmpty = (value: any) => value || value === '';
this.index = this.variables.reduce((acc, currentValue) => { this.index = this.variables.reduce((acc, currentValue) => {
if (currentValue.current && (currentValue.current.isNone || existsOrEmpty(currentValue.current.value))) { if (currentValue.current && (currentValue.current.isNone || existsOrEmpty(currentValue.current.value))) {
@@ -64,12 +64,12 @@ export class TemplateSrv {
this.updateIndex(); this.updateIndex();
} }
variableInitialized(variable) { variableInitialized(variable: any) {
this.index[variable.name] = variable; this.index[variable.name] = variable;
} }
getAdhocFilters(datasourceName) { getAdhocFilters(datasourceName: string) {
let filters = []; let filters: any = [];
if (this.variables) { if (this.variables) {
for (let i = 0; i < this.variables.length; i++) { for (let i = 0; i < this.variables.length; i++) {
@@ -92,7 +92,7 @@ export class TemplateSrv {
return filters; return filters;
} }
luceneFormat(value) { luceneFormat(value: any) {
if (typeof value === 'string') { if (typeof value === 'string') {
return luceneEscape(value); return luceneEscape(value);
} }
@@ -108,7 +108,7 @@ export class TemplateSrv {
// encode string according to RFC 3986; in contrast to encodeURIComponent() // encode string according to RFC 3986; in contrast to encodeURIComponent()
// also the sub-delims "!", "'", "(", ")" and "*" are encoded; // also the sub-delims "!", "'", "(", ")" and "*" are encoded;
// unicode handling uses UTF-8 as in ECMA-262. // unicode handling uses UTF-8 as in ECMA-262.
encodeURIComponentStrict(str) { encodeURIComponentStrict(str: string) {
return encodeURIComponent(str).replace(/[!'()*]/g, c => { return encodeURIComponent(str).replace(/[!'()*]/g, c => {
return ( return (
'%' + '%' +
@@ -120,7 +120,7 @@ export class TemplateSrv {
}); });
} }
formatValue(value, format, variable) { formatValue(value: any, format: any, variable: any) {
// for some scopedVars there is no variable // for some scopedVars there is no variable
variable = variable || {}; variable = variable || {};
@@ -180,11 +180,11 @@ export class TemplateSrv {
} }
} }
setGrafanaVariable(name, value) { setGrafanaVariable(name: string, value: any) {
this.grafanaVariables[name] = value; this.grafanaVariables[name] = value;
} }
getVariableName(expression) { getVariableName(expression: string) {
this.regex.lastIndex = 0; this.regex.lastIndex = 0;
const match = this.regex.exec(expression); const match = this.regex.exec(expression);
if (!match) { if (!match) {
@@ -194,12 +194,12 @@ export class TemplateSrv {
return variableName; return variableName;
} }
variableExists(expression) { variableExists(expression: string) {
const name = this.getVariableName(expression); const name = this.getVariableName(expression);
return name && this.index[name] !== void 0; return name && this.index[name] !== void 0;
} }
highlightVariablesAsHtml(str) { highlightVariablesAsHtml(str: string) {
if (!str || !_.isString(str)) { if (!str || !_.isString(str)) {
return str; return str;
} }
@@ -214,7 +214,7 @@ export class TemplateSrv {
}); });
} }
getAllValue(variable) { getAllValue(variable: any) {
if (variable.allValue) { if (variable.allValue) {
return variable.allValue; return variable.allValue;
} }
@@ -225,7 +225,7 @@ export class TemplateSrv {
return values; return values;
} }
replace(target: string, scopedVars?: ScopedVars, format?: string | Function) { replace(target: string, scopedVars?: ScopedVars, format?: string | Function): any {
if (!target) { if (!target) {
return target; return target;
} }
@@ -266,11 +266,11 @@ export class TemplateSrv {
}); });
} }
isAllValue(value) { isAllValue(value: any) {
return value === '$__all' || (Array.isArray(value) && value[0] === '$__all'); return value === '$__all' || (Array.isArray(value) && value[0] === '$__all');
} }
replaceWithText(target, scopedVars) { replaceWithText(target: string, scopedVars: ScopedVars) {
if (!target) { if (!target) {
return target; return target;
} }
@@ -278,7 +278,7 @@ export class TemplateSrv {
let variable; let variable;
this.regex.lastIndex = 0; this.regex.lastIndex = 0;
return target.replace(this.regex, (match, var1, var2, fmt2, var3) => { return target.replace(this.regex, (match: any, var1: any, var2: any, fmt2: any, var3: any) => {
if (scopedVars) { if (scopedVars) {
const option = scopedVars[var1 || var2 || var3]; const option = scopedVars[var1 || var2 || var3];
if (option) { if (option) {
@@ -297,7 +297,7 @@ export class TemplateSrv {
}); });
} }
fillVariableValuesForUrl(params, scopedVars?) { fillVariableValuesForUrl(params: any, scopedVars?: ScopedVars) {
_.each(this.variables, variable => { _.each(this.variables, variable => {
if (scopedVars && scopedVars[variable.name] !== void 0) { if (scopedVars && scopedVars[variable.name] !== void 0) {
if (scopedVars[variable.name].skipUrlSync) { if (scopedVars[variable.name].skipUrlSync) {
@@ -313,7 +313,7 @@ export class TemplateSrv {
}); });
} }
distributeVariable(value, variable) { distributeVariable(value: any, variable: any) {
value = _.map(value, (val: any, index: number) => { value = _.map(value, (val: any, index: number) => {
if (index !== 0) { if (index !== 0) {
return variable + '=' + val; return variable + '=' + val;
+6 -6
View File
@@ -16,12 +16,12 @@ export const variableRegexExec = (variableString: string) => {
}; };
export interface Variable { export interface Variable {
setValue(option); setValue(option: any): any;
updateOptions(); updateOptions(): any;
dependsOn(variable); dependsOn(variable: any): any;
setValueFromUrl(urlValue); setValueFromUrl(urlValue: any): any;
getValueForUrl(); getValueForUrl(): any;
getSaveModel(); getSaveModel(): any;
} }
export let variableTypes = {}; export let variableTypes = {};
+16 -15
View File
@@ -1,5 +1,5 @@
// Libaries // Libaries
import angular from 'angular'; import angular, { IQService, ILocationService, auto, IPromise } from 'angular';
import _ from 'lodash'; import _ from 'lodash';
// Utils & Services // Utils & Services
@@ -19,9 +19,9 @@ export class VariableSrv {
/** @ngInject */ /** @ngInject */
constructor( constructor(
private $q, private $q: IQService,
private $location, private $location: ILocationService,
private $injector, private $injector: auto.IInjectorService,
private templateSrv: TemplateSrv, private templateSrv: TemplateSrv,
private timeSrv: TimeSrv private timeSrv: TimeSrv
) {} ) {}
@@ -71,7 +71,7 @@ export class VariableSrv {
}); });
} }
processVariable(variable, queryParams) { processVariable(variable: any, queryParams: any) {
const dependencies = []; const dependencies = [];
for (const otherVariable of this.variables) { for (const otherVariable of this.variables) {
@@ -100,7 +100,8 @@ export class VariableSrv {
}); });
} }
createVariableFromModel(model) { createVariableFromModel(model: any) {
// @ts-ignore
const ctor = variableTypes[model.type].ctor; const ctor = variableTypes[model.type].ctor;
if (!ctor) { if (!ctor) {
throw { throw {
@@ -112,24 +113,24 @@ export class VariableSrv {
return variable; return variable;
} }
addVariable(variable) { addVariable(variable: any) {
this.variables.push(variable); this.variables.push(variable);
this.templateSrv.updateIndex(); this.templateSrv.updateIndex();
this.dashboard.updateSubmenuVisibility(); this.dashboard.updateSubmenuVisibility();
} }
removeVariable(variable) { removeVariable(variable: any) {
const index = _.indexOf(this.variables, variable); const index = _.indexOf(this.variables, variable);
this.variables.splice(index, 1); this.variables.splice(index, 1);
this.templateSrv.updateIndex(); this.templateSrv.updateIndex();
this.dashboard.updateSubmenuVisibility(); this.dashboard.updateSubmenuVisibility();
} }
updateOptions(variable) { updateOptions(variable: any) {
return variable.updateOptions(); return variable.updateOptions();
} }
variableUpdated(variable, emitChangeEvents?) { variableUpdated(variable: any, emitChangeEvents?: any) {
// if there is a variable lock ignore cascading update because we are in a boot up scenario // if there is a variable lock ignore cascading update because we are in a boot up scenario
if (variable.initLock) { if (variable.initLock) {
return this.$q.when(); return this.$q.when();
@@ -152,7 +153,7 @@ export class VariableSrv {
}); });
} }
selectOptionsForCurrentValue(variable) { selectOptionsForCurrentValue(variable: any) {
let i, y, value, option; let i, y, value, option;
const selected: any = []; const selected: any = [];
@@ -176,7 +177,7 @@ export class VariableSrv {
return selected; return selected;
} }
validateVariableSelectionState(variable) { validateVariableSelectionState(variable: any) {
if (!variable.current) { if (!variable.current) {
variable.current = {}; variable.current = {};
} }
@@ -221,7 +222,7 @@ export class VariableSrv {
* @param variable Instance of Variable * @param variable Instance of Variable
* @param urlValue Value of the query parameter * @param urlValue Value of the query parameter
*/ */
setOptionFromUrl(variable: any, urlValue: string | string[]): Promise<any> { setOptionFromUrl(variable: any, urlValue: string | string[]): IPromise<any> {
let promise = this.$q.when(); let promise = this.$q.when();
if (variable.refresh) { if (variable.refresh) {
@@ -268,7 +269,7 @@ export class VariableSrv {
}); });
} }
setOptionAsCurrent(variable, option) { setOptionAsCurrent(variable: any, option: any) {
variable.current = _.cloneDeep(option); variable.current = _.cloneDeep(option);
if (_.isArray(variable.current.text) && variable.current.text.length > 0) { if (_.isArray(variable.current.text) && variable.current.text.length > 0) {
@@ -298,7 +299,7 @@ export class VariableSrv {
this.$location.search(params); this.$location.search(params);
} }
setAdhocFilter(options) { setAdhocFilter(options: any) {
let variable: any = _.find(this.variables, { let variable: any = _.find(this.variables, {
type: 'adhoc', type: 'adhoc',
datasource: options.datasource, datasource: options.datasource,
+1 -1
View File
@@ -78,7 +78,7 @@ export class UsersActionBar extends PureComponent<Props> {
} }
} }
function mapStateToProps(state) { function mapStateToProps(state: any) {
return { return {
searchQuery: getUsersSearchQuery(state.users), searchQuery: getUsersSearchQuery(state.users),
pendingInvitesCount: getInviteesCount(state.users), pendingInvitesCount: getInviteesCount(state.users),
+4 -4
View File
@@ -34,7 +34,7 @@ export interface State {
export class UsersListPage extends PureComponent<Props, State> { export class UsersListPage extends PureComponent<Props, State> {
externalUserMngInfoHtml: string; externalUserMngInfoHtml: string;
constructor(props) { constructor(props: Props) {
super(props); super(props);
if (this.props.externalUserMngInfo) { if (this.props.externalUserMngInfo) {
@@ -59,13 +59,13 @@ export class UsersListPage extends PureComponent<Props, State> {
return await this.props.loadInvitees(); return await this.props.loadInvitees();
} }
onRoleChange = (role, user) => { onRoleChange = (role: string, user: OrgUser) => {
const updatedUser = { ...user, role: role }; const updatedUser = { ...user, role: role };
this.props.updateUser(updatedUser); this.props.updateUser(updatedUser);
}; };
onRemoveUser = user => { onRemoveUser = (user: OrgUser) => {
appEvents.emit('confirm-modal', { appEvents.emit('confirm-modal', {
title: 'Delete', title: 'Delete',
text: 'Are you sure you want to delete user ' + user.login + '?', text: 'Are you sure you want to delete user ' + user.login + '?',
@@ -119,7 +119,7 @@ export class UsersListPage extends PureComponent<Props, State> {
} }
} }
function mapStateToProps(state) { function mapStateToProps(state: any) {
return { return {
navModel: getNavModel(state.navIndex, 'users'), navModel: getNavModel(state.navIndex, 'users'),
users: getUsers(state.users), users: getUsers(state.users),
+6 -4
View File
@@ -1,4 +1,6 @@
export const getUsers = state => { import { UsersState } from 'app/types';
export const getUsers = (state: UsersState) => {
const regex = new RegExp(state.searchQuery, 'i'); const regex = new RegExp(state.searchQuery, 'i');
return state.users.filter(user => { return state.users.filter(user => {
@@ -6,7 +8,7 @@ export const getUsers = state => {
}); });
}; };
export const getInvitees = state => { export const getInvitees = (state: UsersState) => {
const regex = new RegExp(state.searchQuery, 'i'); const regex = new RegExp(state.searchQuery, 'i');
return state.invitees.filter(invitee => { return state.invitees.filter(invitee => {
@@ -14,5 +16,5 @@ export const getInvitees = state => {
}); });
}; };
export const getInviteesCount = state => state.invitees.length; export const getInviteesCount = (state: UsersState) => state.invitees.length;
export const getUsersSearchQuery = state => state.searchQuery; export const getUsersSearchQuery = (state: UsersState) => state.searchQuery;
@@ -1,4 +1,6 @@
import _ from 'lodash'; import _ from 'lodash';
import DatasourceSrv from 'app/features/plugins/datasource_srv';
import CloudWatchDatasource from './datasource';
export class CloudWatchConfigCtrl { export class CloudWatchConfigCtrl {
static templateUrl = 'partials/config.html'; static templateUrl = 'partials/config.html';
current: any; current: any;
@@ -8,7 +10,7 @@ export class CloudWatchConfigCtrl {
secretKeyExist = false; secretKeyExist = false;
/** @ngInject */ /** @ngInject */
constructor($scope, datasourceSrv) { constructor($scope: any, datasourceSrv: DatasourceSrv) {
this.current.jsonData.timeField = this.current.jsonData.timeField || '@timestamp'; this.current.jsonData.timeField = this.current.jsonData.timeField || '@timestamp';
this.current.jsonData.authType = this.current.jsonData.authType || 'credentials'; this.current.jsonData.authType = this.current.jsonData.authType || 'credentials';
@@ -32,7 +34,7 @@ export class CloudWatchConfigCtrl {
{ name: 'ARN', value: 'arn' }, { name: 'ARN', value: 'arn' },
]; ];
indexPatternTypes = [ indexPatternTypes: any = [
{ name: 'No pattern', value: undefined }, { name: 'No pattern', value: undefined },
{ name: 'Hourly', value: 'Hourly', example: '[logstash-]YYYY.MM.DD.HH' }, { name: 'Hourly', value: 'Hourly', example: '[logstash-]YYYY.MM.DD.HH' },
{ name: 'Daily', value: 'Daily', example: '[logstash-]YYYY.MM.DD' }, { name: 'Daily', value: 'Daily', example: '[logstash-]YYYY.MM.DD' },
@@ -71,14 +73,14 @@ export class CloudWatchConfigCtrl {
getRegions() { getRegions() {
this.datasourceSrv this.datasourceSrv
.loadDatasource(this.current.name) .loadDatasource(this.current.name)
.then(ds => { .then((ds: CloudWatchDatasource) => {
return ds.getRegions(); return ds.getRegions();
}) })
.then( .then(
regions => { (regions: any) => {
this.regions = _.map(regions, 'value'); this.regions = _.map(regions, 'value');
}, },
err => { (err: any) => {
console.error('failed to get latest regions'); console.error('failed to get latest regions');
} }
); );
@@ -1,9 +1,9 @@
import angular from 'angular'; import angular, { IQService } from 'angular';
import _ from 'lodash'; import _ from 'lodash';
import { dateMath } from '@grafana/data'; import { dateMath } from '@grafana/data';
import kbn from 'app/core/utils/kbn'; import kbn from 'app/core/utils/kbn';
import { CloudWatchQuery } from './types'; import { CloudWatchQuery } from './types';
import { DataSourceApi, DataQueryRequest, DataSourceInstanceSettings } from '@grafana/ui'; import { DataSourceApi, DataQueryRequest, DataSourceInstanceSettings, ScopedVars } from '@grafana/ui';
import { BackendSrv } from 'app/core/services/backend_srv'; import { BackendSrv } from 'app/core/services/backend_srv';
import { TemplateSrv } from 'app/features/templating/template_srv'; import { TemplateSrv } from 'app/features/templating/template_srv';
import { TimeSrv } from 'app/features/dashboard/services/TimeSrv'; import { TimeSrv } from 'app/features/dashboard/services/TimeSrv';
@@ -18,7 +18,7 @@ export default class CloudWatchDatasource extends DataSourceApi<CloudWatchQuery>
/** @ngInject */ /** @ngInject */
constructor( constructor(
private instanceSettings: DataSourceInstanceSettings, private instanceSettings: DataSourceInstanceSettings,
private $q, private $q: IQService,
private backendSrv: BackendSrv, private backendSrv: BackendSrv,
private templateSrv: TemplateSrv, private templateSrv: TemplateSrv,
private timeSrv: TimeSrv private timeSrv: TimeSrv
@@ -96,7 +96,7 @@ export default class CloudWatchDatasource extends DataSourceApi<CloudWatchQuery>
return this.performTimeSeriesQuery(request); return this.performTimeSeriesQuery(request);
} }
getPeriod(target, options, now?) { getPeriod(target: any, options: any, now?: number) {
const start = this.convertToCloudWatchTime(options.range.from, false); const start = this.convertToCloudWatchTime(options.range.from, false);
const end = this.convertToCloudWatchTime(options.range.to, true); const end = this.convertToCloudWatchTime(options.range.to, true);
now = Math.round((now || Date.now()) / 1000); now = Math.round((now || Date.now()) / 1000);
@@ -142,8 +142,8 @@ export default class CloudWatchDatasource extends DataSourceApi<CloudWatchQuery>
return period; return period;
} }
performTimeSeriesQuery(request) { performTimeSeriesQuery(request: any) {
return this.awsRequest('/api/tsdb/query', request).then(res => { return this.awsRequest('/api/tsdb/query', request).then((res: any) => {
const data = []; const data = [];
if (res.results) { if (res.results) {
@@ -165,7 +165,7 @@ export default class CloudWatchDatasource extends DataSourceApi<CloudWatchQuery>
}); });
} }
transformSuggestDataFromTable(suggestData) { transformSuggestDataFromTable(suggestData: any) {
return _.map(suggestData.results['metricFindQuery'].tables[0].rows, v => { return _.map(suggestData.results['metricFindQuery'].tables[0].rows, v => {
return { return {
text: v[0], text: v[0],
@@ -174,7 +174,7 @@ export default class CloudWatchDatasource extends DataSourceApi<CloudWatchQuery>
}); });
} }
doMetricQueryRequest(subtype, parameters) { doMetricQueryRequest(subtype: any, parameters: any) {
const range = this.timeSrv.timeRange(); const range = this.timeSrv.timeRange();
return this.awsRequest('/api/tsdb/query', { return this.awsRequest('/api/tsdb/query', {
from: range.from.valueOf().toString(), from: range.from.valueOf().toString(),
@@ -192,7 +192,7 @@ export default class CloudWatchDatasource extends DataSourceApi<CloudWatchQuery>
parameters parameters
), ),
], ],
}).then(r => { }).then((r: any) => {
return this.transformSuggestDataFromTable(r); return this.transformSuggestDataFromTable(r);
}); });
} }
@@ -205,21 +205,27 @@ export default class CloudWatchDatasource extends DataSourceApi<CloudWatchQuery>
return this.doMetricQueryRequest('namespaces', null); return this.doMetricQueryRequest('namespaces', null);
} }
getMetrics(namespace, region) { getMetrics(namespace: string, region: string) {
return this.doMetricQueryRequest('metrics', { return this.doMetricQueryRequest('metrics', {
region: this.templateSrv.replace(this.getActualRegion(region)), region: this.templateSrv.replace(this.getActualRegion(region)),
namespace: this.templateSrv.replace(namespace), namespace: this.templateSrv.replace(namespace),
}); });
} }
getDimensionKeys(namespace, region) { getDimensionKeys(namespace: string, region: string) {
return this.doMetricQueryRequest('dimension_keys', { return this.doMetricQueryRequest('dimension_keys', {
region: this.templateSrv.replace(this.getActualRegion(region)), region: this.templateSrv.replace(this.getActualRegion(region)),
namespace: this.templateSrv.replace(namespace), namespace: this.templateSrv.replace(namespace),
}); });
} }
getDimensionValues(region, namespace, metricName, dimensionKey, filterDimensions) { getDimensionValues(
region: string,
namespace: string,
metricName: string,
dimensionKey: string,
filterDimensions: {}
) {
return this.doMetricQueryRequest('dimension_values', { return this.doMetricQueryRequest('dimension_values', {
region: this.templateSrv.replace(this.getActualRegion(region)), region: this.templateSrv.replace(this.getActualRegion(region)),
namespace: this.templateSrv.replace(namespace), namespace: this.templateSrv.replace(namespace),
@@ -229,14 +235,14 @@ export default class CloudWatchDatasource extends DataSourceApi<CloudWatchQuery>
}); });
} }
getEbsVolumeIds(region, instanceId) { getEbsVolumeIds(region: string, instanceId: string) {
return this.doMetricQueryRequest('ebs_volume_ids', { return this.doMetricQueryRequest('ebs_volume_ids', {
region: this.templateSrv.replace(this.getActualRegion(region)), region: this.templateSrv.replace(this.getActualRegion(region)),
instanceId: this.templateSrv.replace(instanceId), instanceId: this.templateSrv.replace(instanceId),
}); });
} }
getEc2InstanceAttribute(region, attributeName, filters) { getEc2InstanceAttribute(region: string, attributeName: string, filters: any) {
return this.doMetricQueryRequest('ec2_instance_attribute', { return this.doMetricQueryRequest('ec2_instance_attribute', {
region: this.templateSrv.replace(this.getActualRegion(region)), region: this.templateSrv.replace(this.getActualRegion(region)),
attributeName: this.templateSrv.replace(attributeName), attributeName: this.templateSrv.replace(attributeName),
@@ -244,7 +250,7 @@ export default class CloudWatchDatasource extends DataSourceApi<CloudWatchQuery>
}); });
} }
getResourceARNs(region, resourceType, tags) { getResourceARNs(region: string, resourceType: string, tags: any) {
return this.doMetricQueryRequest('resource_arns', { return this.doMetricQueryRequest('resource_arns', {
region: this.templateSrv.replace(this.getActualRegion(region)), region: this.templateSrv.replace(this.getActualRegion(region)),
resourceType: this.templateSrv.replace(resourceType), resourceType: this.templateSrv.replace(resourceType),
@@ -252,7 +258,7 @@ export default class CloudWatchDatasource extends DataSourceApi<CloudWatchQuery>
}); });
} }
metricFindQuery(query) { metricFindQuery(query: string) {
let region; let region;
let namespace; let namespace;
let metricName; let metricName;
@@ -324,7 +330,7 @@ export default class CloudWatchDatasource extends DataSourceApi<CloudWatchQuery>
return this.$q.when([]); return this.$q.when([]);
} }
annotationQuery(options) { annotationQuery(options: any) {
const annotation = options.annotation; const annotation = options.annotation;
const statistics = _.map(annotation.statistics, s => { const statistics = _.map(annotation.statistics, s => {
return this.templateSrv.replace(s); return this.templateSrv.replace(s);
@@ -359,7 +365,7 @@ export default class CloudWatchDatasource extends DataSourceApi<CloudWatchQuery>
parameters parameters
), ),
], ],
}).then(r => { }).then((r: any) => {
return _.map(r.results['annotationQuery'].tables[0].rows, v => { return _.map(r.results['annotationQuery'].tables[0].rows, v => {
return { return {
annotation: annotation, annotation: annotation,
@@ -372,7 +378,7 @@ export default class CloudWatchDatasource extends DataSourceApi<CloudWatchQuery>
}); });
} }
targetContainsTemplate(target) { targetContainsTemplate(target: any) {
return ( return (
this.templateSrv.variableExists(target.region) || this.templateSrv.variableExists(target.region) ||
this.templateSrv.variableExists(target.namespace) || this.templateSrv.variableExists(target.namespace) ||
@@ -395,14 +401,14 @@ export default class CloudWatchDatasource extends DataSourceApi<CloudWatchQuery>
}); });
} }
awsRequest(url, data) { awsRequest(url: string, data: any) {
const options = { const options = {
method: 'POST', method: 'POST',
url: url, url,
data: data, data,
}; };
return this.backendSrv.datasourceRequest(options).then(result => { return this.backendSrv.datasourceRequest(options).then((result: any) => {
return result.data; return result.data;
}); });
} }
@@ -411,14 +417,14 @@ export default class CloudWatchDatasource extends DataSourceApi<CloudWatchQuery>
return this.defaultRegion; return this.defaultRegion;
} }
getActualRegion(region) { getActualRegion(region: string) {
if (region === 'default' || _.isEmpty(region)) { if (region === 'default' || _.isEmpty(region)) {
return this.getDefaultRegion(); return this.getDefaultRegion();
} }
return region; return region;
} }
getExpandedVariables(target, dimensionKey, variable, templateSrv) { getExpandedVariables(target: any, dimensionKey: any, variable: any, templateSrv: TemplateSrv) {
/* if the all checkbox is marked we should add all values to the targets */ /* if the all checkbox is marked we should add all values to the targets */
const allSelected: any = _.find(variable.options, { selected: true, text: 'All' }); const allSelected: any = _.find(variable.options, { selected: true, text: 'All' });
const selectedVariables = _.filter(variable.options, v => { const selectedVariables = _.filter(variable.options, v => {
@@ -430,7 +436,7 @@ export default class CloudWatchDatasource extends DataSourceApi<CloudWatchQuery>
}); });
const currentVariables = !_.isArray(variable.current.value) const currentVariables = !_.isArray(variable.current.value)
? [variable.current] ? [variable.current]
: variable.current.value.map(v => { : variable.current.value.map((v: any) => {
return { return {
text: v, text: v,
value: v, value: v,
@@ -440,9 +446,9 @@ export default class CloudWatchDatasource extends DataSourceApi<CloudWatchQuery>
selectedVariables.some((s: any) => { selectedVariables.some((s: any) => {
return s.value === currentVariables[0].value; return s.value === currentVariables[0].value;
}) || currentVariables[0].value === '$__all'; }) || currentVariables[0].value === '$__all';
return (useSelectedVariables ? selectedVariables : currentVariables).map(v => { return (useSelectedVariables ? selectedVariables : currentVariables).map((v: any) => {
const t = angular.copy(target); const t = angular.copy(target);
const scopedVar = {}; const scopedVar: any = {};
scopedVar[variable.name] = v; scopedVar[variable.name] = v;
t.refId = target.refId + '_' + v.value; t.refId = target.refId + '_' + v.value;
t.dimensions[dimensionKey] = templateSrv.replace(t.dimensions[dimensionKey], scopedVar); t.dimensions[dimensionKey] = templateSrv.replace(t.dimensions[dimensionKey], scopedVar);
@@ -455,7 +461,7 @@ export default class CloudWatchDatasource extends DataSourceApi<CloudWatchQuery>
}); });
} }
expandTemplateVariable(targets, scopedVars, templateSrv) { expandTemplateVariable(targets: any, scopedVars: ScopedVars, templateSrv: TemplateSrv) {
// Datasource and template srv logic uber-complected. This should be cleaned up. // Datasource and template srv logic uber-complected. This should be cleaned up.
return _.chain(targets) return _.chain(targets)
.map(target => { .map(target => {
@@ -480,15 +486,15 @@ export default class CloudWatchDatasource extends DataSourceApi<CloudWatchQuery>
.value(); .value();
} }
convertToCloudWatchTime(date, roundUp) { convertToCloudWatchTime(date: any, roundUp: any) {
if (_.isString(date)) { if (_.isString(date)) {
date = dateMath.parse(date, roundUp); date = dateMath.parse(date, roundUp);
} }
return Math.round(date.valueOf() / 1000); return Math.round(date.valueOf() / 1000);
} }
convertDimensionFormat(dimensions, scopedVars) { convertDimensionFormat(dimensions: any, scopedVars: ScopedVars) {
const convertedDimensions = {}; const convertedDimensions: any = {};
_.each(dimensions, (value, key) => { _.each(dimensions, (value, key) => {
convertedDimensions[this.templateSrv.replace(key, scopedVars)] = this.templateSrv.replace(value, scopedVars); convertedDimensions[this.templateSrv.replace(key, scopedVars)] = this.templateSrv.replace(value, scopedVars);
}); });
@@ -1,5 +1,6 @@
import './query_parameter_ctrl'; import './query_parameter_ctrl';
import { QueryCtrl } from 'app/plugins/sdk'; import { QueryCtrl } from 'app/plugins/sdk';
import { auto } from 'angular';
export class CloudWatchQueryCtrl extends QueryCtrl { export class CloudWatchQueryCtrl extends QueryCtrl {
static templateUrl = 'partials/query.editor.html'; static templateUrl = 'partials/query.editor.html';
@@ -7,7 +8,7 @@ export class CloudWatchQueryCtrl extends QueryCtrl {
aliasSyntax: string; aliasSyntax: string;
/** @ngInject */ /** @ngInject */
constructor($scope, $injector) { constructor($scope: any, $injector: auto.IInjectorService) {
super($scope, $injector); super($scope, $injector);
this.aliasSyntax = '{{metric}} {{stat}} {{namespace}} {{region}} {{<dimension name>}}'; this.aliasSyntax = '{{metric}} {{stat}} {{namespace}} {{region}} {{<dimension name>}}';
} }
@@ -1,10 +1,12 @@
import angular from 'angular'; import angular, { IQService } from 'angular';
import coreModule from 'app/core/core_module'; import coreModule from 'app/core/core_module';
import _ from 'lodash'; import _ from 'lodash';
import { TemplateSrv } from 'app/features/templating/template_srv';
import DatasourceSrv from 'app/features/plugins/datasource_srv';
export class CloudWatchQueryParameterCtrl { export class CloudWatchQueryParameterCtrl {
/** @ngInject */ /** @ngInject */
constructor($scope, templateSrv, uiSegmentSrv, datasourceSrv, $q) { constructor($scope: any, templateSrv: TemplateSrv, uiSegmentSrv: any, datasourceSrv: DatasourceSrv, $q: IQService) {
$scope.init = () => { $scope.init = () => {
const target = $scope.target; const target = $scope.target;
target.namespace = target.namespace || ''; target.namespace = target.namespace || '';
@@ -69,7 +71,7 @@ export class CloudWatchQueryParameterCtrl {
); );
}; };
$scope.statSegmentChanged = (segment, index) => { $scope.statSegmentChanged = (segment: any, index: number) => {
if (segment.value === $scope.removeStatSegment.value) { if (segment.value === $scope.removeStatSegment.value) {
$scope.statSegments.splice(index, 1); $scope.statSegments.splice(index, 1);
} else { } else {
@@ -91,7 +93,7 @@ export class CloudWatchQueryParameterCtrl {
$scope.onChange(); $scope.onChange();
}; };
$scope.ensurePlusButton = segments => { $scope.ensurePlusButton = (segments: any) => {
const count = segments.length; const count = segments.length;
const lastSegment = segments[Math.max(count - 1, 0)]; const lastSegment = segments[Math.max(count - 1, 0)];
@@ -100,7 +102,7 @@ export class CloudWatchQueryParameterCtrl {
} }
}; };
$scope.getDimSegments = (segment, $index) => { $scope.getDimSegments = (segment: any, $index: number) => {
if (segment.type === 'operator') { if (segment.type === 'operator') {
return $q.when([]); return $q.when([]);
} }
@@ -130,7 +132,7 @@ export class CloudWatchQueryParameterCtrl {
}); });
}; };
$scope.dimSegmentChanged = (segment, index) => { $scope.dimSegmentChanged = (segment: any, index: number) => {
$scope.dimSegments[index] = segment; $scope.dimSegments[index] = segment;
if (segment.value === $scope.removeDimSegment.value) { if (segment.value === $scope.removeDimSegment.value) {
@@ -148,7 +150,7 @@ export class CloudWatchQueryParameterCtrl {
}; };
$scope.syncDimSegmentsWithModel = () => { $scope.syncDimSegmentsWithModel = () => {
const dims = {}; const dims: any = {};
const length = $scope.dimSegments.length; const length = $scope.dimSegments.length;
for (let i = 0; i < length - 2; i += 3) { for (let i = 0; i < length - 2; i += 3) {
@@ -165,7 +167,7 @@ export class CloudWatchQueryParameterCtrl {
$scope.getRegions = () => { $scope.getRegions = () => {
return $scope.datasource return $scope.datasource
.metricFindQuery('regions()') .metricFindQuery('regions()')
.then(results => { .then((results: any) => {
results.unshift({ text: 'default' }); results.unshift({ text: 'default' });
return results; return results;
}) })
@@ -197,8 +199,8 @@ export class CloudWatchQueryParameterCtrl {
$scope.onChange(); $scope.onChange();
}; };
$scope.transformToSegments = addTemplateVars => { $scope.transformToSegments = (addTemplateVars: any) => {
return results => { return (results: any) => {
const segments = _.map(results, segment => { const segments = _.map(results, segment => {
return uiSegmentSrv.newSegment({ return uiSegmentSrv.newSegment({
value: segment.text, value: segment.text,
@@ -32,7 +32,7 @@ describe('CloudWatchDatasource', () => {
} as any; } as any;
beforeEach(() => { beforeEach(() => {
ctx.ds = new CloudWatchDatasource(instanceSettings, {}, backendSrv, templateSrv, timeSrv); ctx.ds = new CloudWatchDatasource(instanceSettings, {} as any, backendSrv, templateSrv, timeSrv);
}); });
describe('When performing CloudWatch query', () => { describe('When performing CloudWatch query', () => {
@@ -56,7 +56,7 @@ describe('CloudWatchDatasource', () => {
], ],
}; };
const response = { const response: any = {
timings: [null], timings: [null],
results: { results: {
A: { A: {
@@ -156,7 +156,7 @@ describe('CloudWatchDatasource', () => {
}); });
it('should return series list', done => { it('should return series list', done => {
ctx.ds.query(query).then(result => { ctx.ds.query(query).then((result: any) => {
expect(result.data[0].target).toBe(response.results.A.series[0].name); expect(result.data[0].target).toBe(response.results.A.series[0].name);
expect(result.data[0].datapoints[0][0]).toBe(response.results.A.series[0].points[0][0]); expect(result.data[0].datapoints[0][0]).toBe(response.results.A.series[0].points[0][0]);
done(); done();
@@ -204,7 +204,7 @@ describe('CloudWatchDatasource', () => {
], ],
}; };
ctx.ds.query(query).then(result => { ctx.ds.query(query).then((result: any) => {
expect(requestParams.queries[0].region).toBe(instanceSettings.jsonData.defaultRegion); expect(requestParams.queries[0].region).toBe(instanceSettings.jsonData.defaultRegion);
done(); done();
}); });
@@ -231,7 +231,7 @@ describe('CloudWatchDatasource', () => {
], ],
}; };
const response = { const response: any = {
timings: [null], timings: [null],
results: { results: {
A: { A: {
@@ -259,7 +259,7 @@ describe('CloudWatchDatasource', () => {
}); });
it('should return series list', done => { it('should return series list', done => {
ctx.ds.query(query).then(result => { ctx.ds.query(query).then((result: any) => {
expect(result.data[0].target).toBe(response.results.A.series[0].name); expect(result.data[0].target).toBe(response.results.A.series[0].name);
expect(result.data[0].datapoints[0][0]).toBe(response.results.A.series[0].points[0][0]); expect(result.data[0].datapoints[0][0]).toBe(response.results.A.series[0].points[0][0]);
done(); done();
@@ -411,7 +411,7 @@ describe('CloudWatchDatasource', () => {
}); });
it('should generate the correct query for multilple template variables with expression', done => { it('should generate the correct query for multilple template variables with expression', done => {
const query = { const query: any = {
range: { from: 'now-1h', to: 'now' }, range: { from: 'now-1h', to: 'now' },
rangeRaw: { from: 1483228800, to: 1483232400 }, rangeRaw: { from: 1483228800, to: 1483232400 },
targets: [ targets: [
@@ -466,17 +466,17 @@ describe('CloudWatchDatasource', () => {
}); });
}); });
function describeMetricFindQuery(query, func) { function describeMetricFindQuery(query: any, func: any) {
describe('metricFindQuery ' + query, () => { describe('metricFindQuery ' + query, () => {
const scenario: any = {}; const scenario: any = {};
scenario.setup = setupCallback => { scenario.setup = (setupCallback: any) => {
beforeEach(() => { beforeEach(() => {
setupCallback(); setupCallback();
ctx.backendSrv.datasourceRequest = jest.fn(args => { ctx.backendSrv.datasourceRequest = jest.fn(args => {
scenario.request = args.data; scenario.request = args.data;
return Promise.resolve({ data: scenario.requestResponse }); return Promise.resolve({ data: scenario.requestResponse });
}); });
ctx.ds.metricFindQuery(query).then(args => { ctx.ds.metricFindQuery(query).then((args: any) => {
scenario.result = args; scenario.result = args;
}); });
}); });
@@ -486,7 +486,7 @@ describe('CloudWatchDatasource', () => {
}); });
} }
describeMetricFindQuery('regions()', scenario => { describeMetricFindQuery('regions()', (scenario: any) => {
scenario.setup(() => { scenario.setup(() => {
scenario.requestResponse = { scenario.requestResponse = {
results: { results: {
@@ -504,7 +504,7 @@ describe('CloudWatchDatasource', () => {
}); });
}); });
describeMetricFindQuery('namespaces()', scenario => { describeMetricFindQuery('namespaces()', (scenario: any) => {
scenario.setup(() => { scenario.setup(() => {
scenario.requestResponse = { scenario.requestResponse = {
results: { results: {
@@ -522,7 +522,7 @@ describe('CloudWatchDatasource', () => {
}); });
}); });
describeMetricFindQuery('metrics(AWS/EC2)', scenario => { describeMetricFindQuery('metrics(AWS/EC2)', (scenario: any) => {
scenario.setup(() => { scenario.setup(() => {
scenario.requestResponse = { scenario.requestResponse = {
results: { results: {
@@ -540,7 +540,7 @@ describe('CloudWatchDatasource', () => {
}); });
}); });
describeMetricFindQuery('dimension_keys(AWS/EC2)', scenario => { describeMetricFindQuery('dimension_keys(AWS/EC2)', (scenario: any) => {
scenario.setup(() => { scenario.setup(() => {
scenario.requestResponse = { scenario.requestResponse = {
results: { results: {
@@ -558,7 +558,7 @@ describe('CloudWatchDatasource', () => {
}); });
}); });
describeMetricFindQuery('dimension_values(us-east-1,AWS/EC2,CPUUtilization,InstanceId)', scenario => { describeMetricFindQuery('dimension_values(us-east-1,AWS/EC2,CPUUtilization,InstanceId)', (scenario: any) => {
scenario.setup(() => { scenario.setup(() => {
scenario.requestResponse = { scenario.requestResponse = {
results: { results: {
@@ -576,7 +576,7 @@ describe('CloudWatchDatasource', () => {
}); });
}); });
describeMetricFindQuery('dimension_values(default,AWS/EC2,CPUUtilization,InstanceId)', scenario => { describeMetricFindQuery('dimension_values(default,AWS/EC2,CPUUtilization,InstanceId)', (scenario: any) => {
scenario.setup(() => { scenario.setup(() => {
scenario.requestResponse = { scenario.requestResponse = {
results: { results: {
@@ -594,7 +594,7 @@ describe('CloudWatchDatasource', () => {
}); });
}); });
describeMetricFindQuery('resource_arns(default,ec2:instance,{"environment":["production"]})', scenario => { describeMetricFindQuery('resource_arns(default,ec2:instance,{"environment":["production"]})', (scenario: any) => {
scenario.setup(() => { scenario.setup(() => {
scenario.requestResponse = { scenario.requestResponse = {
results: { results: {
@@ -1,10 +1,11 @@
import coreModule from 'app/core/core_module'; import coreModule from 'app/core/core_module';
import _ from 'lodash'; import _ from 'lodash';
import * as queryDef from './query_def'; import * as queryDef from './query_def';
import { IQService } from 'angular';
export class ElasticBucketAggCtrl { export class ElasticBucketAggCtrl {
/** @ngInject */ /** @ngInject */
constructor($scope, uiSegmentSrv, $q, $rootScope) { constructor($scope: any, uiSegmentSrv: any, $q: IQService, $rootScope: any) {
const bucketAggs = $scope.target.bucketAggs; const bucketAggs = $scope.target.bucketAggs;
$scope.orderByOptions = []; $scope.orderByOptions = [];
@@ -158,7 +159,7 @@ export class ElasticBucketAggCtrl {
$scope.agg.settings.filters.push({ query: '*' }); $scope.agg.settings.filters.push({ query: '*' });
}; };
$scope.removeFiltersQuery = filter => { $scope.removeFiltersQuery = (filter: any) => {
$scope.agg.settings.filters = _.without($scope.agg.settings.filters, filter); $scope.agg.settings.filters = _.without($scope.agg.settings.filters, filter);
}; };
@@ -8,7 +8,7 @@ export class ElasticConfigCtrl {
current: DataSourceInstanceSettings<ElasticsearchOptions>; current: DataSourceInstanceSettings<ElasticsearchOptions>;
/** @ngInject */ /** @ngInject */
constructor($scope) { constructor($scope: any) {
this.current.jsonData.timeField = this.current.jsonData.timeField || '@timestamp'; this.current.jsonData.timeField = this.current.jsonData.timeField || '@timestamp';
this.current.jsonData.esVersion = this.current.jsonData.esVersion || 5; this.current.jsonData.esVersion = this.current.jsonData.esVersion || 5;
const defaultMaxConcurrentShardRequests = this.current.jsonData.esVersion >= 70 ? 5 : 256; const defaultMaxConcurrentShardRequests = this.current.jsonData.esVersion >= 70 ? 5 : 256;
@@ -18,7 +18,7 @@ export class ElasticConfigCtrl {
this.current.jsonData.logLevelField = this.current.jsonData.logLevelField || ''; this.current.jsonData.logLevelField = this.current.jsonData.logLevelField || '';
} }
indexPatternTypes = [ indexPatternTypes: any = [
{ name: 'No pattern', value: undefined }, { name: 'No pattern', value: undefined },
{ name: 'Hourly', value: 'Hourly', example: '[logstash-]YYYY.MM.DD.HH' }, { name: 'Hourly', value: 'Hourly', example: '[logstash-]YYYY.MM.DD.HH' },
{ name: 'Daily', value: 'Daily', example: '[logstash-]YYYY.MM.DD' }, { name: 'Daily', value: 'Daily', example: '[logstash-]YYYY.MM.DD' },
@@ -63,7 +63,7 @@ export class ElasticDatasource extends DataSourceApi<ElasticsearchQuery, Elastic
} }
} }
private request(method, url, data?) { private request(method: string, url: string, data?: undefined) {
const options: any = { const options: any = {
url: this.url + '/' + url, url: this.url + '/' + url,
method: method, method: method,
@@ -82,29 +82,29 @@ export class ElasticDatasource extends DataSourceApi<ElasticsearchQuery, Elastic
return this.backendSrv.datasourceRequest(options); return this.backendSrv.datasourceRequest(options);
} }
private get(url) { private get(url: string) {
const range = this.timeSrv.timeRange(); const range = this.timeSrv.timeRange();
const indexList = this.indexPattern.getIndexList(range.from.valueOf(), range.to.valueOf()); const indexList = this.indexPattern.getIndexList(range.from.valueOf(), range.to.valueOf());
if (_.isArray(indexList) && indexList.length) { if (_.isArray(indexList) && indexList.length) {
return this.request('GET', indexList[0] + url).then(results => { return this.request('GET', indexList[0] + url).then((results: any) => {
results.data.$$config = results.config; results.data.$$config = results.config;
return results.data; return results.data;
}); });
} else { } else {
return this.request('GET', this.indexPattern.getIndexForToday() + url).then(results => { return this.request('GET', this.indexPattern.getIndexForToday() + url).then((results: any) => {
results.data.$$config = results.config; results.data.$$config = results.config;
return results.data; return results.data;
}); });
} }
} }
private post(url, data) { private post(url: string, data: any) {
return this.request('POST', url, data) return this.request('POST', url, data)
.then(results => { .then((results: any) => {
results.data.$$config = results.config; results.data.$$config = results.config;
return results.data; return results.data;
}) })
.catch(err => { .catch((err: any) => {
if (err.data && err.data.error) { if (err.data && err.data.error) {
throw { throw {
message: 'Elasticsearch error: ' + err.data.error.reason, message: 'Elasticsearch error: ' + err.data.error.reason,
@@ -116,14 +116,14 @@ export class ElasticDatasource extends DataSourceApi<ElasticsearchQuery, Elastic
}); });
} }
annotationQuery(options) { annotationQuery(options: any) {
const annotation = options.annotation; const annotation = options.annotation;
const timeField = annotation.timeField || '@timestamp'; const timeField = annotation.timeField || '@timestamp';
const queryString = annotation.query || '*'; const queryString = annotation.query || '*';
const tagsField = annotation.tagsField || 'tags'; const tagsField = annotation.tagsField || 'tags';
const textField = annotation.textField || null; const textField = annotation.textField || null;
const range = {}; const range: any = {};
range[timeField] = { range[timeField] = {
from: options.range.from.valueOf(), from: options.range.from.valueOf(),
to: options.range.to.valueOf(), to: options.range.to.valueOf(),
@@ -144,8 +144,8 @@ export class ElasticDatasource extends DataSourceApi<ElasticsearchQuery, Elastic
}, },
}; };
const data = { const data: any = {
query: query, query,
size: 10000, size: 10000,
}; };
@@ -168,11 +168,11 @@ export class ElasticDatasource extends DataSourceApi<ElasticsearchQuery, Elastic
const payload = angular.toJson(header) + '\n' + angular.toJson(data) + '\n'; const payload = angular.toJson(header) + '\n' + angular.toJson(data) + '\n';
return this.post('_msearch', payload).then(res => { return this.post('_msearch', payload).then((res: any) => {
const list = []; const list = [];
const hits = res.responses[0].hits.hits; const hits = res.responses[0].hits.hits;
const getFieldFromSource = (source, fieldName) => { const getFieldFromSource = (source: any, fieldName: any) => {
if (!fieldName) { if (!fieldName) {
return; return;
} }
@@ -229,7 +229,7 @@ export class ElasticDatasource extends DataSourceApi<ElasticsearchQuery, Elastic
testDatasource() { testDatasource() {
// validate that the index exist and has date field // validate that the index exist and has date field
return this.getFields({ type: 'date' }).then( return this.getFields({ type: 'date' }).then(
dateFields => { (dateFields: any) => {
const timeField: any = _.find(dateFields, { text: this.timeField }); const timeField: any = _.find(dateFields, { text: this.timeField });
if (!timeField) { if (!timeField) {
return { return {
@@ -239,7 +239,7 @@ export class ElasticDatasource extends DataSourceApi<ElasticsearchQuery, Elastic
} }
return { status: 'success', message: 'Index OK. Time field name OK.' }; return { status: 'success', message: 'Index OK. Time field name OK.' };
}, },
err => { (err: any) => {
console.log(err); console.log(err);
if (err.data && err.data.error) { if (err.data && err.data.error) {
let message = angular.toJson(err.data.error); let message = angular.toJson(err.data.error);
@@ -254,7 +254,7 @@ export class ElasticDatasource extends DataSourceApi<ElasticsearchQuery, Elastic
); );
} }
getQueryHeader(searchType, timeFrom, timeTo) { getQueryHeader(searchType: any, timeFrom: any, timeTo: any) {
const queryHeader: any = { const queryHeader: any = {
search_type: searchType, search_type: searchType,
ignore_unavailable: true, ignore_unavailable: true,
@@ -319,7 +319,7 @@ export class ElasticDatasource extends DataSourceApi<ElasticsearchQuery, Elastic
const url = this.getMultiSearchUrl(); const url = this.getMultiSearchUrl();
return this.post(url, payload).then(res => { return this.post(url, payload).then((res: any) => {
const er = new ElasticResponse(sentTargets, res); const er = new ElasticResponse(sentTargets, res);
if (sentTargets.some(target => target.isLogsQuery)) { if (sentTargets.some(target => target.isLogsQuery)) {
return er.getLogs(this.logMessageField, this.logLevelField); return er.getLogs(this.logMessageField, this.logLevelField);
@@ -329,10 +329,10 @@ export class ElasticDatasource extends DataSourceApi<ElasticsearchQuery, Elastic
}); });
} }
getFields(query) { getFields(query: any) {
const configuredEsVersion = this.esVersion; const configuredEsVersion = this.esVersion;
return this.get('/_mapping').then(result => { return this.get('/_mapping').then((result: any) => {
const typeMap = { const typeMap: any = {
float: 'number', float: 'number',
double: 'number', double: 'number',
integer: 'number', integer: 'number',
@@ -344,7 +344,7 @@ export class ElasticDatasource extends DataSourceApi<ElasticsearchQuery, Elastic
nested: 'nested', nested: 'nested',
}; };
function shouldAddField(obj, key, query) { function shouldAddField(obj: any, key: any, query: any) {
if (key[0] === '_') { if (key[0] === '_') {
return false; return false;
} }
@@ -358,10 +358,10 @@ export class ElasticDatasource extends DataSourceApi<ElasticsearchQuery, Elastic
} }
// Store subfield names: [system, process, cpu, total] -> system.process.cpu.total // Store subfield names: [system, process, cpu, total] -> system.process.cpu.total
const fieldNameParts = []; const fieldNameParts: any = [];
const fields = {}; const fields: any = {};
function getFieldsRecursively(obj) { function getFieldsRecursively(obj: any) {
for (const key in obj) { for (const key in obj) {
const subObj = obj[key]; const subObj = obj[key];
@@ -415,7 +415,7 @@ export class ElasticDatasource extends DataSourceApi<ElasticsearchQuery, Elastic
}); });
} }
getTerms(queryDef) { getTerms(queryDef: any) {
const range = this.timeSrv.timeRange(); const range = this.timeSrv.timeRange();
const searchType = this.esVersion >= 5 ? 'query_then_fetch' : 'count'; const searchType = this.esVersion >= 5 ? 'query_then_fetch' : 'count';
const header = this.getQueryHeader(searchType, range.from, range.to); const header = this.getQueryHeader(searchType, range.from, range.to);
@@ -427,7 +427,7 @@ export class ElasticDatasource extends DataSourceApi<ElasticsearchQuery, Elastic
const url = this.getMultiSearchUrl(); const url = this.getMultiSearchUrl();
return this.post(url, esQuery).then(res => { return this.post(url, esQuery).then((res: any) => {
if (!res.responses[0].aggregations) { if (!res.responses[0].aggregations) {
return []; return [];
} }
@@ -450,7 +450,7 @@ export class ElasticDatasource extends DataSourceApi<ElasticsearchQuery, Elastic
return '_msearch'; return '_msearch';
} }
metricFindQuery(query) { metricFindQuery(query: any) {
query = angular.fromJson(query); query = angular.fromJson(query);
if (!query) { if (!query) {
return this.$q.when([]); return this.$q.when([]);
@@ -472,11 +472,11 @@ export class ElasticDatasource extends DataSourceApi<ElasticsearchQuery, Elastic
return this.getFields({}); return this.getFields({});
} }
getTagValues(options) { getTagValues(options: any) {
return this.getTerms({ field: options.key, query: '*' }); return this.getTerms({ field: options.key, query: '*' });
} }
targetContainsTemplate(target) { targetContainsTemplate(target: any) {
if (this.templateSrv.variableExists(target.query) || this.templateSrv.variableExists(target.alias)) { if (this.templateSrv.variableExists(target.query) || this.templateSrv.variableExists(target.alias)) {
return true; return true;
} }
@@ -500,7 +500,7 @@ export class ElasticDatasource extends DataSourceApi<ElasticsearchQuery, Elastic
return false; return false;
} }
private isPrimitive(obj) { private isPrimitive(obj: any) {
if (obj === null || obj === undefined) { if (obj === null || obj === undefined) {
return true; return true;
} }
@@ -511,7 +511,7 @@ export class ElasticDatasource extends DataSourceApi<ElasticsearchQuery, Elastic
return false; return false;
} }
private objectContainsTemplate(obj) { private objectContainsTemplate(obj: any) {
if (!obj) { if (!obj) {
return false; return false;
} }
@@ -4,14 +4,15 @@ import * as queryDef from './query_def';
import TableModel from 'app/core/table_model'; import TableModel from 'app/core/table_model';
import { DataFrame, toDataFrame, FieldType } from '@grafana/data'; import { DataFrame, toDataFrame, FieldType } from '@grafana/data';
import { DataQueryResponse } from '@grafana/ui'; import { DataQueryResponse } from '@grafana/ui';
import { ElasticsearchAggregation } from './types';
export class ElasticResponse { export class ElasticResponse {
constructor(private targets, private response) { constructor(private targets: any, private response: any) {
this.targets = targets; this.targets = targets;
this.response = response; this.response = response;
} }
processMetrics(esAgg, target, seriesList, props) { processMetrics(esAgg: any, target: any, seriesList: any, props: any) {
let metric, y, i, newSeries, bucket, value; let metric, y, i, newSeries, bucket, value;
for (y = 0; y < target.metrics.length; y++) { for (y = 0; y < target.metrics.length; y++) {
@@ -113,7 +114,7 @@ export class ElasticResponse {
} }
} }
processAggregationDocs(esAgg, aggDef, target, table, props) { processAggregationDocs(esAgg: any, aggDef: ElasticsearchAggregation, target: any, table: any, props: any) {
// add columns // add columns
if (table.columns.length === 0) { if (table.columns.length === 0) {
for (const propKey of _.keys(props)) { for (const propKey of _.keys(props)) {
@@ -123,7 +124,7 @@ export class ElasticResponse {
} }
// helper func to add values to value array // helper func to add values to value array
const addMetricValue = (values, metricName, value) => { const addMetricValue = (values: any[], metricName: string, value: any) => {
table.addColumn({ text: metricName }); table.addColumn({ text: metricName });
values.push(value); values.push(value);
}; };
@@ -188,8 +189,8 @@ export class ElasticResponse {
// This is quite complex // This is quite complex
// need to recurse down the nested buckets to build series // need to recurse down the nested buckets to build series
processBuckets(aggs, target, seriesList, table, props, depth) { processBuckets(aggs: any, target: any, seriesList: any, table: any, props: any, depth: any) {
let bucket, aggDef, esAgg, aggId; let bucket, aggDef: any, esAgg, aggId;
const maxDepth = target.bucketAggs.length - 1; const maxDepth = target.bucketAggs.length - 1;
for (aggId in aggs) { for (aggId in aggs) {
@@ -224,7 +225,7 @@ export class ElasticResponse {
} }
} }
private getMetricName(metric) { private getMetricName(metric: any) {
let metricDef: any = _.find(queryDef.metricAggTypes, { value: metric }); let metricDef: any = _.find(queryDef.metricAggTypes, { value: metric });
if (!metricDef) { if (!metricDef) {
metricDef = _.find(queryDef.extendedStats, { value: metric }); metricDef = _.find(queryDef.extendedStats, { value: metric });
@@ -233,13 +234,13 @@ export class ElasticResponse {
return metricDef ? metricDef.text : metric; return metricDef ? metricDef.text : metric;
} }
private getSeriesName(series, target, metricTypeCount) { private getSeriesName(series: any, target: any, metricTypeCount: any) {
let metricName = this.getMetricName(series.metric); let metricName = this.getMetricName(series.metric);
if (target.alias) { if (target.alias) {
const regex = /\{\{([\s\S]+?)\}\}/g; const regex = /\{\{([\s\S]+?)\}\}/g;
return target.alias.replace(regex, (match, g1, g2) => { return target.alias.replace(regex, (match: any, g1: any, g2: any) => {
const group = g1 || g2; const group = g1 || g2;
if (group.indexOf('term ') === 0) { if (group.indexOf('term ') === 0) {
@@ -303,7 +304,7 @@ export class ElasticResponse {
return name.trim() + ' ' + metricName; return name.trim() + ' ' + metricName;
} }
nameSeries(seriesList, target) { nameSeries(seriesList: any, target: any) {
const metricTypeCount = _.uniq(_.map(seriesList, 'metric')).length; const metricTypeCount = _.uniq(_.map(seriesList, 'metric')).length;
for (let i = 0; i < seriesList.length; i++) { for (let i = 0; i < seriesList.length; i++) {
@@ -312,17 +313,17 @@ export class ElasticResponse {
} }
} }
processHits(hits, seriesList) { processHits(hits: { total: { value: any }; hits: any[] }, seriesList: any[]) {
const hitsTotal = typeof hits.total === 'number' ? hits.total : hits.total.value; // <- Works with Elasticsearch 7.0+ const hitsTotal = typeof hits.total === 'number' ? hits.total : hits.total.value; // <- Works with Elasticsearch 7.0+
const series = { const series: any = {
target: 'docs', target: 'docs',
type: 'docs', type: 'docs',
datapoints: [], datapoints: [],
total: hitsTotal, total: hitsTotal,
filterable: true, filterable: true,
}; };
let propName, hit, doc, i; let propName, hit, doc: any, i;
for (i = 0; i < hits.hits.length; i++) { for (i = 0; i < hits.hits.length; i++) {
hit = hits.hits[i]; hit = hits.hits[i];
@@ -347,7 +348,7 @@ export class ElasticResponse {
seriesList.push(series); seriesList.push(series);
} }
trimDatapoints(aggregations, target) { trimDatapoints(aggregations: any, target: any) {
const histogram: any = _.find(target.bucketAggs, { type: 'date_histogram' }); const histogram: any = _.find(target.bucketAggs, { type: 'date_histogram' });
const shouldDropFirstAndLast = histogram && histogram.settings && histogram.settings.trimEdges; const shouldDropFirstAndLast = histogram && histogram.settings && histogram.settings.trimEdges;
@@ -362,7 +363,7 @@ export class ElasticResponse {
} }
} }
getErrorFromElasticResponse(response, err) { getErrorFromElasticResponse(response: any, err: any) {
const result: any = {}; const result: any = {};
result.data = JSON.stringify(err, null, 4); result.data = JSON.stringify(err, null, 4);
if (err.root_cause && err.root_cause.length > 0 && err.root_cause[0].reason) { if (err.root_cause && err.root_cause.length > 0 && err.root_cause[0].reason) {
@@ -394,7 +395,7 @@ export class ElasticResponse {
if (response.aggregations) { if (response.aggregations) {
const aggregations = response.aggregations; const aggregations = response.aggregations;
const target = this.targets[i]; const target = this.targets[i];
const tmpSeriesList = []; const tmpSeriesList: any[] = [];
const table = new TableModel(); const table = new TableModel();
this.processBuckets(aggregations, target, tmpSeriesList, table, {}, 0); this.processBuckets(aggregations, target, tmpSeriesList, table, {}, 0);
@@ -426,7 +427,7 @@ export class ElasticResponse {
const hits = response.hits; const hits = response.hits;
let propNames: string[] = []; let propNames: string[] = [];
let propName, hit, doc, i; let propName, hit, doc: any, i;
for (i = 0; i < hits.hits.length; i++) { for (i = 0; i < hits.hits.length; i++) {
hit = hits.hits[i]; hit = hits.hits[i];
@@ -534,7 +535,7 @@ export class ElasticResponse {
if (response.aggregations) { if (response.aggregations) {
const aggregations = response.aggregations; const aggregations = response.aggregations;
const target = this.targets[n]; const target = this.targets[n];
const tmpSeriesList = []; const tmpSeriesList: any[] = [];
const table = new TableModel(); const table = new TableModel();
this.processBuckets(aggregations, target, tmpSeriesList, table, {}, 0); this.processBuckets(aggregations, target, tmpSeriesList, table, {}, 0);
@@ -1,6 +1,6 @@
import { toUtc, dateTime } from '@grafana/data'; import { toUtc, dateTime } from '@grafana/data';
const intervalMap = { const intervalMap: any = {
Hourly: { startOf: 'hour', amount: 'hours' }, Hourly: { startOf: 'hour', amount: 'hours' },
Daily: { startOf: 'day', amount: 'days' }, Daily: { startOf: 'day', amount: 'days' },
Weekly: { startOf: 'isoWeek', amount: 'weeks' }, Weekly: { startOf: 'isoWeek', amount: 'weeks' },
@@ -9,7 +9,7 @@ const intervalMap = {
}; };
export class IndexPattern { export class IndexPattern {
constructor(private pattern, private interval: string | null) {} constructor(private pattern: any, private interval: string | null) {}
getIndexForToday() { getIndexForToday() {
if (this.interval) { if (this.interval) {
@@ -19,7 +19,7 @@ export class IndexPattern {
} }
} }
getIndexList(from, to) { getIndexList(from: any, to: any) {
if (!this.interval) { if (!this.interval) {
return this.pattern; return this.pattern;
} }
@@ -2,10 +2,11 @@ import coreModule from 'app/core/core_module';
import _ from 'lodash'; import _ from 'lodash';
import * as queryDef from './query_def'; import * as queryDef from './query_def';
import { ElasticsearchAggregation } from './types'; import { ElasticsearchAggregation } from './types';
import { IQService } from 'angular';
export class ElasticMetricAggCtrl { export class ElasticMetricAggCtrl {
/** @ngInject */ /** @ngInject */
constructor($scope, uiSegmentSrv, $q, $rootScope) { constructor($scope: any, uiSegmentSrv: any, $q: IQService, $rootScope: any) {
const metricAggs: ElasticsearchAggregation[] = $scope.target.metrics; const metricAggs: ElasticsearchAggregation[] = $scope.target.metrics;
$scope.metricAggTypes = queryDef.getMetricAggTypes($scope.esVersion); $scope.metricAggTypes = queryDef.getMetricAggTypes($scope.esVersion);
$scope.extendedStats = queryDef.extendedStats; $scope.extendedStats = queryDef.extendedStats;
@@ -14,7 +14,7 @@ export function elasticPipelineVariables() {
}; };
} }
const newVariable = index => { const newVariable = (index: any) => {
return { return {
name: 'var' + index, name: 'var' + index,
pipelineAgg: 'select metric', pipelineAgg: 'select metric',
@@ -23,7 +23,7 @@ const newVariable = index => {
export class ElasticPipelineVariablesCtrl { export class ElasticPipelineVariablesCtrl {
/** @ngInject */ /** @ngInject */
constructor($scope) { constructor($scope: any) {
$scope.variables = $scope.variables || [newVariable(1)]; $scope.variables = $scope.variables || [newVariable(1)];
$scope.onChangeInternal = () => { $scope.onChangeInternal = () => {
@@ -35,7 +35,7 @@ export class ElasticPipelineVariablesCtrl {
$scope.onChange(); $scope.onChange();
}; };
$scope.remove = index => { $scope.remove = (index: number) => {
$scope.variables.splice(index, 1); $scope.variables.splice(index, 1);
$scope.onChange(); $scope.onChange();
}; };
@@ -1,16 +1,17 @@
import * as queryDef from './query_def'; import * as queryDef from './query_def';
import { ElasticsearchAggregation } from './types';
export class ElasticQueryBuilder { export class ElasticQueryBuilder {
timeField: string; timeField: string;
esVersion: number; esVersion: number;
constructor(options) { constructor(options: any) {
this.timeField = options.timeField; this.timeField = options.timeField;
this.esVersion = options.esVersion; this.esVersion = options.esVersion;
} }
getRangeFilter() { getRangeFilter() {
const filter = {}; const filter: any = {};
filter[this.timeField] = { filter[this.timeField] = {
gte: '$timeFrom', gte: '$timeFrom',
lte: '$timeTo', lte: '$timeTo',
@@ -20,7 +21,7 @@ export class ElasticQueryBuilder {
return filter; return filter;
} }
buildTermsAgg(aggDef, queryNode, target) { buildTermsAgg(aggDef: ElasticsearchAggregation, queryNode: { terms?: any; aggs?: any }, target: { metrics: any[] }) {
let metricRef, metric, y; let metricRef, metric, y;
queryNode.terms = { field: aggDef.field }; queryNode.terms = { field: aggDef.field };
@@ -63,7 +64,7 @@ export class ElasticQueryBuilder {
return queryNode; return queryNode;
} }
getDateHistogramAgg(aggDef) { getDateHistogramAgg(aggDef: ElasticsearchAggregation) {
const esAgg: any = {}; const esAgg: any = {};
const settings = aggDef.settings || {}; const settings = aggDef.settings || {};
esAgg.interval = settings.interval; esAgg.interval = settings.interval;
@@ -87,7 +88,7 @@ export class ElasticQueryBuilder {
return esAgg; return esAgg;
} }
getHistogramAgg(aggDef) { getHistogramAgg(aggDef: ElasticsearchAggregation) {
const esAgg: any = {}; const esAgg: any = {};
const settings = aggDef.settings || {}; const settings = aggDef.settings || {};
esAgg.interval = settings.interval; esAgg.interval = settings.interval;
@@ -100,8 +101,8 @@ export class ElasticQueryBuilder {
return esAgg; return esAgg;
} }
getFiltersAgg(aggDef) { getFiltersAgg(aggDef: ElasticsearchAggregation) {
const filterObj = {}; const filterObj: any = {};
for (let i = 0; i < aggDef.settings.filters.length; i++) { for (let i = 0; i < aggDef.settings.filters.length; i++) {
const query = aggDef.settings.filters[i].query; const query = aggDef.settings.filters[i].query;
let label = aggDef.settings.filters[i].label; let label = aggDef.settings.filters[i].label;
@@ -117,7 +118,7 @@ export class ElasticQueryBuilder {
return filterObj; return filterObj;
} }
documentQuery(query, size) { documentQuery(query: any, size: number) {
query.size = size; query.size = size;
query.sort = {}; query.sort = {};
query.sort[this.timeField] = { order: 'desc', unmapped_type: 'boolean' }; query.sort[this.timeField] = { order: 'desc', unmapped_type: 'boolean' };
@@ -136,12 +137,12 @@ export class ElasticQueryBuilder {
return query; return query;
} }
addAdhocFilters(query, adhocFilters) { addAdhocFilters(query: any, adhocFilters: any) {
if (!adhocFilters) { if (!adhocFilters) {
return; return;
} }
let i, filter, condition, queryCondition; let i, filter, condition: any, queryCondition: any;
for (i = 0; i < adhocFilters.length; i++) { for (i = 0; i < adhocFilters.length; i++) {
filter = adhocFilters[i]; filter = adhocFilters[i];
@@ -183,7 +184,7 @@ export class ElasticQueryBuilder {
} }
} }
build(target, adhocFilters?, queryString?) { build(target: any, adhocFilters?: any, queryString?: string) {
// make sure query has defaults; // make sure query has defaults;
target.metrics = target.metrics || [queryDef.defaultMetricAgg()]; target.metrics = target.metrics || [queryDef.defaultMetricAgg()];
target.bucketAggs = target.bucketAggs || [queryDef.defaultBucketAgg()]; target.bucketAggs = target.bucketAggs || [queryDef.defaultBucketAgg()];
@@ -224,7 +225,7 @@ export class ElasticQueryBuilder {
for (i = 0; i < target.bucketAggs.length; i++) { for (i = 0; i < target.bucketAggs.length; i++) {
const aggDef = target.bucketAggs[i]; const aggDef = target.bucketAggs[i];
const esAgg = {}; const esAgg: any = {};
switch (aggDef.type) { switch (aggDef.type) {
case 'date_histogram': { case 'date_histogram': {
@@ -265,8 +266,8 @@ export class ElasticQueryBuilder {
continue; continue;
} }
const aggField = {}; const aggField: any = {};
let metricAgg = null; let metricAgg: any = null;
if (queryDef.isPipelineAgg(metric.type)) { if (queryDef.isPipelineAgg(metric.type)) {
if (queryDef.isPipelineAggWithMultipleBucketPaths(metric.type)) { if (queryDef.isPipelineAggWithMultipleBucketPaths(metric.type)) {
@@ -323,7 +324,7 @@ export class ElasticQueryBuilder {
return query; return query;
} }
getTermsQuery(queryDef) { getTermsQuery(queryDef: any) {
const query: any = { const query: any = {
size: 0, size: 0,
query: { query: {
@@ -368,7 +369,7 @@ export class ElasticQueryBuilder {
return query; return query;
} }
getLogsQuery(target, querystring) { getLogsQuery(target: any, querystring: string) {
let query: any = { let query: any = {
size: 0, size: 0,
query: { query: {
@@ -2,7 +2,7 @@ import './bucket_agg';
import './metric_agg'; import './metric_agg';
import './pipeline_variables'; import './pipeline_variables';
import angular from 'angular'; import angular, { auto } from 'angular';
import _ from 'lodash'; import _ from 'lodash';
import * as queryDef from './query_def'; import * as queryDef from './query_def';
import { QueryCtrl } from 'app/plugins/sdk'; import { QueryCtrl } from 'app/plugins/sdk';
@@ -15,7 +15,7 @@ export class ElasticQueryCtrl extends QueryCtrl {
rawQueryOld: string; rawQueryOld: string;
/** @ngInject */ /** @ngInject */
constructor($scope, $injector, private $rootScope, private uiSegmentSrv) { constructor($scope: any, $injector: auto.IInjectorService, private $rootScope: any, private uiSegmentSrv: any) {
super($scope, $injector); super($scope, $injector);
this.esVersion = this.datasource.esVersion; this.esVersion = this.datasource.esVersion;
@@ -35,7 +35,7 @@ export class ElasticQueryCtrl extends QueryCtrl {
this.queryUpdated(); this.queryUpdated();
} }
getFields(type) { getFields(type: any) {
const jsonStr = angular.toJson({ find: 'fields', type: type }); const jsonStr = angular.toJson({ find: 'fields', type: type });
return this.datasource return this.datasource
.metricFindQuery(jsonStr) .metricFindQuery(jsonStr)
@@ -98,7 +98,7 @@ export class ElasticQueryCtrl extends QueryCtrl {
return text; return text;
} }
handleQueryError(err) { handleQueryError(err: any): any[] {
this.error = err.message || 'Failed to issue metric query'; this.error = err.message || 'Failed to issue metric query';
return []; return [];
} }
@@ -128,7 +128,7 @@ export const movingAvgModelOptions = [
{ text: 'Holt Winters', value: 'holt_winters' }, { text: 'Holt Winters', value: 'holt_winters' },
]; ];
export const pipelineOptions = { export const pipelineOptions: any = {
moving_avg: [ moving_avg: [
{ text: 'window', default: 5 }, { text: 'window', default: 5 },
{ text: 'model', default: 'simple' }, { text: 'model', default: 'simple' },
@@ -139,7 +139,7 @@ export const pipelineOptions = {
bucket_script: [], bucket_script: [],
}; };
export const movingAvgModelSettings = { export const movingAvgModelSettings: any = {
simple: [], simple: [],
linear: [], linear: [],
ewma: [{ text: 'Alpha', value: 'alpha', default: undefined }], ewma: [{ text: 'Alpha', value: 'alpha', default: undefined }],
@@ -153,7 +153,7 @@ export const movingAvgModelSettings = {
], ],
}; };
export function getMetricAggTypes(esVersion) { export function getMetricAggTypes(esVersion: any) {
return _.filter(metricAggTypes, f => { return _.filter(metricAggTypes, f => {
if (f.minVersion) { if (f.minVersion) {
return f.minVersion <= esVersion; return f.minVersion <= esVersion;
@@ -163,7 +163,7 @@ export function getMetricAggTypes(esVersion) {
}); });
} }
export function getPipelineOptions(metric) { export function getPipelineOptions(metric: any) {
if (!isPipelineAgg(metric.type)) { if (!isPipelineAgg(metric.type)) {
return []; return [];
} }
@@ -171,7 +171,7 @@ export function getPipelineOptions(metric) {
return pipelineOptions[metric.type]; return pipelineOptions[metric.type];
} }
export function isPipelineAgg(metricType) { export function isPipelineAgg(metricType: any) {
if (metricType) { if (metricType) {
const po = pipelineOptions[metricType]; const po = pipelineOptions[metricType];
return po !== null && po !== undefined; return po !== null && po !== undefined;
@@ -180,7 +180,7 @@ export function isPipelineAgg(metricType) {
return false; return false;
} }
export function isPipelineAggWithMultipleBucketPaths(metricType) { export function isPipelineAggWithMultipleBucketPaths(metricType: any) {
if (metricType) { if (metricType) {
return metricAggTypes.find(t => t.value === metricType && t.supportsMultipleBucketPaths) !== undefined; return metricAggTypes.find(t => t.value === metricType && t.supportsMultipleBucketPaths) !== undefined;
} }
@@ -188,8 +188,8 @@ export function isPipelineAggWithMultipleBucketPaths(metricType) {
return false; return false;
} }
export function getPipelineAggOptions(targets) { export function getPipelineAggOptions(targets: any) {
const result = []; const result: any[] = [];
_.each(targets.metrics, metric => { _.each(targets.metrics, metric => {
if (!isPipelineAgg(metric.type)) { if (!isPipelineAgg(metric.type)) {
result.push({ text: describeMetric(metric), value: metric.id }); result.push({ text: describeMetric(metric), value: metric.id });
@@ -199,8 +199,8 @@ export function getPipelineAggOptions(targets) {
return result; return result;
} }
export function getMovingAvgSettings(model, filtered) { export function getMovingAvgSettings(model: any, filtered: boolean) {
const filteredResult = []; const filteredResult: any[] = [];
if (filtered) { if (filtered) {
_.each(movingAvgModelSettings[model], setting => { _.each(movingAvgModelSettings[model], setting => {
if (!setting.isCheckbox) { if (!setting.isCheckbox) {
@@ -212,8 +212,8 @@ export function getMovingAvgSettings(model, filtered) {
return movingAvgModelSettings[model]; return movingAvgModelSettings[model];
} }
export function getOrderByOptions(target) { export function getOrderByOptions(target: any) {
const metricRefs = []; const metricRefs: any[] = [];
_.each(target.metrics, metric => { _.each(target.metrics, metric => {
if (metric.type !== 'count') { if (metric.type !== 'count') {
metricRefs.push({ text: describeMetric(metric), value: metric.id }); metricRefs.push({ text: describeMetric(metric), value: metric.id });
@@ -223,12 +223,12 @@ export function getOrderByOptions(target) {
return orderByOptions.concat(metricRefs); return orderByOptions.concat(metricRefs);
} }
export function describeOrder(order) { export function describeOrder(order: string) {
const def: any = _.find(orderOptions, { value: order }); const def: any = _.find(orderOptions, { value: order });
return def.text; return def.text;
} }
export function describeMetric(metric) { export function describeMetric(metric: { type: string; field: string }) {
const def: any = _.find(metricAggTypes, { value: metric.type }); const def: any = _.find(metricAggTypes, { value: metric.type });
if (!def.requiresField && !isPipelineAgg(metric.type)) { if (!def.requiresField && !isPipelineAgg(metric.type)) {
return def.text; return def.text;
@@ -236,7 +236,7 @@ export function describeMetric(metric) {
return def.text + ' ' + metric.field; return def.text + ' ' + metric.field;
} }
export function describeOrderBy(orderBy, target) { export function describeOrderBy(orderBy: any, target: any) {
const def: any = _.find(orderByOptions, { value: orderBy }); const def: any = _.find(orderByOptions, { value: orderBy });
if (def) { if (def) {
return def.text; return def.text;
@@ -69,7 +69,7 @@ describe('ElasticDatasource', function(this: any) {
}); });
it('should translate index pattern to current day', () => { it('should translate index pattern to current day', () => {
let requestOptions; let requestOptions: any;
ctx.backendSrv.datasourceRequest = jest.fn(options => { ctx.backendSrv.datasourceRequest = jest.fn(options => {
requestOptions = options; requestOptions = options;
return Promise.resolve({ data: {} }); return Promise.resolve({ data: {} });
@@ -83,7 +83,7 @@ describe('ElasticDatasource', function(this: any) {
}); });
describe('When issuing metric query with interval pattern', () => { describe('When issuing metric query with interval pattern', () => {
let requestOptions, parts, header, query, result; let requestOptions: any, parts: any, header: any, query: any, result: any;
beforeEach(async () => { beforeEach(async () => {
createDatasource({ createDatasource({
@@ -154,7 +154,7 @@ describe('ElasticDatasource', function(this: any) {
}); });
describe('When issuing logs query with interval pattern', () => { describe('When issuing logs query with interval pattern', () => {
let query, queryBuilderSpy; let query, queryBuilderSpy: any;
beforeEach(async () => { beforeEach(async () => {
createDatasource({ createDatasource({
@@ -249,7 +249,7 @@ describe('ElasticDatasource', function(this: any) {
}); });
describe('When issuing document query', () => { describe('When issuing document query', () => {
let requestOptions, parts, header; let requestOptions: any, parts: any, header: any;
beforeEach(() => { beforeEach(() => {
createDatasource({ createDatasource({
@@ -539,7 +539,7 @@ describe('ElasticDatasource', function(this: any) {
}); });
describe('When issuing aggregation query on es5.x', () => { describe('When issuing aggregation query on es5.x', () => {
let requestOptions, parts, header; let requestOptions: any, parts: any, header: any;
beforeEach(() => { beforeEach(() => {
createDatasource({ createDatasource({
@@ -582,7 +582,7 @@ describe('ElasticDatasource', function(this: any) {
}); });
describe('When issuing metricFind query on es5.x', () => { describe('When issuing metricFind query on es5.x', () => {
let requestOptions, parts, header, body, results; let requestOptions: any, parts, header: any, body: any, results: any;
beforeEach(() => { beforeEach(() => {
createDatasource({ createDatasource({
@@ -615,7 +615,7 @@ describe('ElasticDatasource', function(this: any) {
}); });
}); });
ctx.ds.metricFindQuery('{"find": "terms", "field": "test"}').then(res => { ctx.ds.metricFindQuery('{"find": "terms", "field": "test"}').then((res: any) => {
results = res; results = res;
}); });
@@ -2,8 +2,8 @@ import { ElasticResponse } from '../elastic_response';
describe('ElasticResponse', () => { describe('ElasticResponse', () => {
let targets; let targets;
let response; let response: any;
let result; let result: any;
describe('simple query and count', () => { describe('simple query and count', () => {
beforeEach(() => { beforeEach(() => {
@@ -48,7 +48,7 @@ describe('ElasticResponse', () => {
}); });
describe('simple query count & avg aggregation', () => { describe('simple query count & avg aggregation', () => {
let result; let result: any;
beforeEach(() => { beforeEach(() => {
targets = [ targets = [
@@ -97,7 +97,7 @@ describe('ElasticResponse', () => {
}); });
describe('single group by query one metric', () => { describe('single group by query one metric', () => {
let result; let result: any;
beforeEach(() => { beforeEach(() => {
targets = [ targets = [
@@ -149,7 +149,7 @@ describe('ElasticResponse', () => {
}); });
describe('single group by query two metrics', () => { describe('single group by query two metrics', () => {
let result; let result: any;
beforeEach(() => { beforeEach(() => {
targets = [ targets = [
@@ -209,7 +209,7 @@ describe('ElasticResponse', () => {
}); });
describe('with percentiles ', () => { describe('with percentiles ', () => {
let result; let result: any;
beforeEach(() => { beforeEach(() => {
targets = [ targets = [
@@ -257,7 +257,7 @@ describe('ElasticResponse', () => {
}); });
describe('with extended_stats', () => { describe('with extended_stats', () => {
let result; let result: any;
beforeEach(() => { beforeEach(() => {
targets = [ targets = [
@@ -333,7 +333,7 @@ describe('ElasticResponse', () => {
}); });
describe('single group by with alias pattern', () => { describe('single group by with alias pattern', () => {
let result; let result: any;
beforeEach(() => { beforeEach(() => {
targets = [ targets = [
@@ -394,7 +394,7 @@ describe('ElasticResponse', () => {
}); });
describe('histogram response', () => { describe('histogram response', () => {
let result; let result: any;
beforeEach(() => { beforeEach(() => {
targets = [ targets = [
@@ -426,7 +426,7 @@ describe('ElasticResponse', () => {
}); });
describe('with two filters agg', () => { describe('with two filters agg', () => {
let result; let result: any;
beforeEach(() => { beforeEach(() => {
targets = [ targets = [
@@ -583,7 +583,7 @@ describe('ElasticResponse', () => {
}); });
describe('No group by time with percentiles ', () => { describe('No group by time with percentiles ', () => {
let result; let result: any;
beforeEach(() => { beforeEach(() => {
targets = [ targets = [
@@ -720,7 +720,7 @@ describe('ElasticResponse', () => {
}); });
describe('with bucket_script ', () => { describe('with bucket_script ', () => {
let result; let result: any;
beforeEach(() => { beforeEach(() => {
targets = [ targets = [
@@ -861,7 +861,7 @@ describe('ElasticResponse', () => {
expect(result.data[0].fields).toContainEqual({ name: '@timestamp', type: 'time' }); expect(result.data[0].fields).toContainEqual({ name: '@timestamp', type: 'time' });
expect(result.data[0].fields).toContainEqual({ name: 'host', type: 'string' }); expect(result.data[0].fields).toContainEqual({ name: 'host', type: 'string' });
expect(result.data[0].fields).toContainEqual({ name: 'message', type: 'string' }); expect(result.data[0].fields).toContainEqual({ name: 'message', type: 'string' });
result.data[0].rows.forEach((row, i) => { result.data[0].rows.forEach((row: any, i: number) => {
expect(row).toContain(response.responses[0].hits.hits[i]._id); expect(row).toContain(response.responses[0].hits.hits[i]._id);
expect(row).toContain(response.responses[0].hits.hits[i]._type); expect(row).toContain(response.responses[0].hits.hits[i]._type);
expect(row).toContain(response.responses[0].hits.hits[i]._index); expect(row).toContain(response.responses[0].hits.hits[i]._index);
@@ -869,7 +869,7 @@ describe('ElasticResponse', () => {
}); });
expect(result.data[1]).toHaveProperty('name', 'Count'); expect(result.data[1]).toHaveProperty('name', 'Count');
response.responses[0].aggregations['2'].buckets.forEach(bucket => { response.responses[0].aggregations['2'].buckets.forEach((bucket: any) => {
expect(result.data[1].rows).toContainEqual([bucket.doc_count, bucket.key]); expect(result.data[1].rows).toContainEqual([bucket.doc_count, bucket.key]);
}); });
}); });
@@ -1,7 +1,7 @@
import { ElasticQueryBuilder } from '../query_builder'; import { ElasticQueryBuilder } from '../query_builder';
describe('ElasticQueryBuilder', () => { describe('ElasticQueryBuilder', () => {
let builder; let builder: any;
beforeEach(() => { beforeEach(() => {
builder = new ElasticQueryBuilder({ timeField: '@timestamp' }); builder = new ElasticQueryBuilder({ timeField: '@timestamp' });
@@ -103,6 +103,7 @@ describe('ElasticQueryBuilder', () => {
], ],
}, },
100, 100,
// @ts-ignore
1000 1000
); );
@@ -1,3 +1,5 @@
import { auto } from 'angular';
export class QueryCtrl { export class QueryCtrl {
target: any; target: any;
datasource: any; datasource: any;
@@ -6,7 +8,7 @@ export class QueryCtrl {
hasRawMode: boolean; hasRawMode: boolean;
error: string; error: string;
constructor(public $scope, _$injector) { constructor(public $scope: any, _$injector: auto.IInjectorService) {
this.panelCtrl = this.panelCtrl || { panel: {} }; this.panelCtrl = this.panelCtrl || { panel: {} };
this.target = this.target || { target: '' }; this.target = this.target || { target: '' };
this.panel = this.panelCtrl.panel; this.panel = this.panelCtrl.panel;
@@ -1,3 +1,5 @@
import { TemplateSrv } from 'app/features/templating/template_srv';
export class AzureMonitorAnnotationsQueryCtrl { export class AzureMonitorAnnotationsQueryCtrl {
static templateUrl = 'partials/annotations.editor.html'; static templateUrl = 'partials/annotations.editor.html';
datasource: any; datasource: any;
@@ -9,7 +11,7 @@ export class AzureMonitorAnnotationsQueryCtrl {
'<your table>\n| where $__timeFilter() \n| project TimeGenerated, Text=YourTitleColumn, Tags="tag1,tag2"'; '<your table>\n| where $__timeFilter() \n| project TimeGenerated, Text=YourTitleColumn, Tags="tag1,tag2"';
/** @ngInject */ /** @ngInject */
constructor(private templateSrv) { constructor(private templateSrv: TemplateSrv) {
this.annotation.queryType = this.annotation.queryType || 'Azure Log Analytics'; this.annotation.queryType = this.annotation.queryType || 'Azure Log Analytics';
this.annotation.rawQuery = this.annotation.rawQuery || this.defaultQuery; this.annotation.rawQuery = this.annotation.rawQuery || this.defaultQuery;
this.initDropdowns(); this.initDropdowns();
@@ -25,7 +27,7 @@ export class AzureMonitorAnnotationsQueryCtrl {
return; return;
} }
return this.datasource.azureMonitorDatasource.getSubscriptions().then(subs => { return this.datasource.azureMonitorDatasource.getSubscriptions().then((subs: any[]) => {
this.subscriptions = subs; this.subscriptions = subs;
if (!this.annotation.subscription && this.annotation.queryType === 'Azure Log Analytics') { if (!this.annotation.subscription && this.annotation.queryType === 'Azure Log Analytics') {
@@ -45,7 +47,7 @@ export class AzureMonitorAnnotationsQueryCtrl {
return this.datasource return this.datasource
.getAzureLogAnalyticsWorkspaces(this.annotation.subscription) .getAzureLogAnalyticsWorkspaces(this.annotation.subscription)
.then(list => { .then((list: any[]) => {
this.workspaces = list; this.workspaces = list;
if (list.length > 0 && !this.annotation.workspace) { if (list.length > 0 && !this.annotation.workspace) {
this.annotation.workspace = list[0].value; this.annotation.workspace = list[0].value;
@@ -72,6 +74,6 @@ export class AzureMonitorAnnotationsQueryCtrl {
}; };
get templateVariables() { get templateVariables() {
return this.templateSrv.variables.map(t => '$' + t.name); return this.templateSrv.variables.map((t: any) => '$' + t.name);
} }
} }
@@ -1,4 +1,5 @@
import AzureMonitorDatasource from '../datasource'; import AzureMonitorDatasource from '../datasource';
// @ts-ignore
import Q from 'q'; import Q from 'q';
import { TemplateSrv } from 'app/features/templating/template_srv'; import { TemplateSrv } from 'app/features/templating/template_srv';
import { toUtc } from '@grafana/data'; import { toUtc } from '@grafana/data';
@@ -46,7 +47,7 @@ describe('AppInsightsDatasource', () => {
}); });
it('should return success status', () => { it('should return success status', () => {
return ctx.ds.testDatasource().then(results => { return ctx.ds.testDatasource().then((results: any) => {
expect(results.status).toEqual('success'); expect(results.status).toEqual('success');
}); });
}); });
@@ -71,7 +72,7 @@ describe('AppInsightsDatasource', () => {
}); });
it('should return error status and a detailed error message', () => { it('should return error status and a detailed error message', () => {
return ctx.ds.testDatasource().then(results => { return ctx.ds.testDatasource().then((results: any) => {
expect(results.status).toEqual('error'); expect(results.status).toEqual('error');
expect(results.message).toEqual( expect(results.message).toEqual(
'1. Application Insights: Not Found: Invalid Application Id for Application Insights service. ' '1. Application Insights: Not Found: Invalid Application Id for Application Insights service. '
@@ -99,7 +100,7 @@ describe('AppInsightsDatasource', () => {
}); });
it('should return error status and a detailed error message', () => { it('should return error status and a detailed error message', () => {
return ctx.ds.testDatasource().then(results => { return ctx.ds.testDatasource().then((results: any) => {
expect(results.status).toEqual('error'); expect(results.status).toEqual('error');
expect(results.message).toEqual('1. Application Insights: Error: SomeOtherError. An error message. '); expect(results.message).toEqual('1. Application Insights: Error: SomeOtherError. An error message. ');
}); });
@@ -149,7 +150,7 @@ describe('AppInsightsDatasource', () => {
}); });
it('should return a single datapoint', () => { it('should return a single datapoint', () => {
return ctx.ds.query(options).then(results => { return ctx.ds.query(options).then((results: any) => {
expect(results.data.length).toBe(1); expect(results.data.length).toBe(1);
expect(results.data[0].datapoints.length).toBe(1); expect(results.data[0].datapoints.length).toBe(1);
expect(results.data[0].target).toEqual('exceptions/server'); expect(results.data[0].target).toEqual('exceptions/server');
@@ -196,7 +197,7 @@ describe('AppInsightsDatasource', () => {
}); });
it('should return a list of datapoints', () => { it('should return a list of datapoints', () => {
return ctx.ds.query(options).then(results => { return ctx.ds.query(options).then((results: any) => {
expect(results.data.length).toBe(1); expect(results.data.length).toBe(1);
expect(results.data[0].datapoints.length).toBe(2); expect(results.data[0].datapoints.length).toBe(2);
expect(results.data[0].target).toEqual('exceptions/server'); expect(results.data[0].target).toEqual('exceptions/server');
@@ -267,7 +268,7 @@ describe('AppInsightsDatasource', () => {
}); });
it('should return a list of datapoints', () => { it('should return a list of datapoints', () => {
return ctx.ds.query(options).then(results => { return ctx.ds.query(options).then((results: any) => {
expect(results.data.length).toBe(3); expect(results.data.length).toBe(3);
expect(results.data[0].datapoints.length).toBe(2); expect(results.data[0].datapoints.length).toBe(2);
expect(results.data[0].target).toEqual('exceptions/server{client/city="Miami"}'); expect(results.data[0].target).toEqual('exceptions/server{client/city="Miami"}');
@@ -292,7 +293,7 @@ describe('AppInsightsDatasource', () => {
}); });
it('should return a list of datapoints', () => { it('should return a list of datapoints', () => {
return ctx.ds.query(options).then(results => { return ctx.ds.query(options).then((results: any) => {
expect(results.data.length).toBe(3); expect(results.data.length).toBe(3);
expect(results.data[0].datapoints.length).toBe(2); expect(results.data[0].datapoints.length).toBe(2);
expect(results.data[0].target).toEqual('exceptions/server + client/city + Miami'); expect(results.data[0].target).toEqual('exceptions/server + client/city + Miami');
@@ -323,7 +324,7 @@ describe('AppInsightsDatasource', () => {
}); });
it('should return a list of metric names', () => { it('should return a list of metric names', () => {
return ctx.ds.metricFindQuery('appInsightsMetricNames()').then(results => { return ctx.ds.metricFindQuery('appInsightsMetricNames()').then((results: any) => {
expect(results.length).toBe(2); expect(results.length).toBe(2);
expect(results[0].text).toBe('exceptions/server'); expect(results[0].text).toBe('exceptions/server');
expect(results[0].value).toBe('exceptions/server'); expect(results[0].value).toBe('exceptions/server');
@@ -361,7 +362,7 @@ describe('AppInsightsDatasource', () => {
}); });
it('should return a list of group bys', () => { it('should return a list of group bys', () => {
return ctx.ds.metricFindQuery('appInsightsGroupBys(requests/count)').then(results => { return ctx.ds.metricFindQuery('appInsightsGroupBys(requests/count)').then((results: any) => {
expect(results[0].text).toContain('client/os'); expect(results[0].text).toContain('client/os');
expect(results[0].value).toContain('client/os'); expect(results[0].value).toContain('client/os');
expect(results[1].text).toContain('client/city'); expect(results[1].text).toContain('client/city');
@@ -389,7 +390,7 @@ describe('AppInsightsDatasource', () => {
}); });
it('should return a list of metric names', () => { it('should return a list of metric names', () => {
return ctx.ds.getAppInsightsMetricNames().then(results => { return ctx.ds.getAppInsightsMetricNames().then((results: any) => {
expect(results.length).toBe(2); expect(results.length).toBe(2);
expect(results[0].text).toBe('exceptions/server'); expect(results[0].text).toBe('exceptions/server');
expect(results[0].value).toBe('exceptions/server'); expect(results[0].value).toBe('exceptions/server');
@@ -427,7 +428,7 @@ describe('AppInsightsDatasource', () => {
}); });
it('should return a list of group bys', () => { it('should return a list of group bys', () => {
return ctx.ds.getAppInsightsMetricMetadata('requests/count').then(results => { return ctx.ds.getAppInsightsMetricMetadata('requests/count').then((results: any) => {
expect(results.primaryAggType).toEqual('avg'); expect(results.primaryAggType).toEqual('avg');
expect(results.supportedAggTypes).toContain('avg'); expect(results.supportedAggTypes).toContain('avg');
expect(results.supportedAggTypes).toContain('sum'); expect(results.supportedAggTypes).toContain('sum');
@@ -6,6 +6,7 @@ import { DataSourceInstanceSettings } from '@grafana/ui';
import { AzureDataSourceJsonData } from '../types'; import { AzureDataSourceJsonData } from '../types';
import { BackendSrv } from 'app/core/services/backend_srv'; import { BackendSrv } from 'app/core/services/backend_srv';
import { TemplateSrv } from 'app/features/templating/template_srv'; import { TemplateSrv } from 'app/features/templating/template_srv';
import { IQService } from 'angular';
export interface LogAnalyticsColumn { export interface LogAnalyticsColumn {
text: string; text: string;
@@ -24,7 +25,7 @@ export default class AppInsightsDatasource {
instanceSettings: DataSourceInstanceSettings<AzureDataSourceJsonData>, instanceSettings: DataSourceInstanceSettings<AzureDataSourceJsonData>,
private backendSrv: BackendSrv, private backendSrv: BackendSrv,
private templateSrv: TemplateSrv, private templateSrv: TemplateSrv,
private $q private $q: IQService
) { ) {
this.id = instanceSettings.id; this.id = instanceSettings.id;
this.applicationId = instanceSettings.jsonData.appInsightsAppId; this.applicationId = instanceSettings.jsonData.appInsightsAppId;
@@ -36,7 +37,7 @@ export default class AppInsightsDatasource {
return !!this.applicationId && this.applicationId.length > 0; return !!this.applicationId && this.applicationId.length > 0;
} }
query(options) { query(options: any) {
const queries = _.filter(options.targets, item => { const queries = _.filter(options.targets, item => {
return item.hide !== true; return item.hide !== true;
}).map(target => { }).map(target => {
@@ -106,6 +107,7 @@ export default class AppInsightsDatasource {
}); });
if (!queries || queries.length === 0) { if (!queries || queries.length === 0) {
// @ts-ignore
return; return;
} }
@@ -130,16 +132,16 @@ export default class AppInsightsDatasource {
}); });
} }
doQueries(queries) { doQueries(queries: any) {
return _.map(queries, query => { return _.map(queries, query => {
return this.doRequest(query.url) return this.doRequest(query.url)
.then(result => { .then((result: any) => {
return { return {
result: result, result: result,
query: query, query: query,
}; };
}) })
.catch(err => { .catch((err: any) => {
throw { throw {
error: err, error: err,
query: query, query: query,
@@ -148,7 +150,7 @@ export default class AppInsightsDatasource {
}); });
} }
annotationQuery(options) {} annotationQuery(options: any) {}
metricFindQuery(query: string) { metricFindQuery(query: string) {
const appInsightsMetricNameQuery = query.match(/^AppInsightsMetricNames\(\)/i); const appInsightsMetricNameQuery = query.match(/^AppInsightsMetricNames\(\)/i);
@@ -168,7 +170,7 @@ export default class AppInsightsDatasource {
testDatasource() { testDatasource() {
const url = `${this.baseUrl}/metrics/metadata`; const url = `${this.baseUrl}/metrics/metadata`;
return this.doRequest(url) return this.doRequest(url)
.then(response => { .then((response: any) => {
if (response.status === 200) { if (response.status === 200) {
return { return {
status: 'success', status: 'success',
@@ -182,7 +184,7 @@ export default class AppInsightsDatasource {
message: 'Returned http status code ' + response.status, message: 'Returned http status code ' + response.status,
}; };
}) })
.catch(error => { .catch((error: any) => {
let message = 'Application Insights: '; let message = 'Application Insights: ';
message += error.statusText ? error.statusText + ': ' : ''; message += error.statusText ? error.statusText + ': ' : '';
@@ -201,13 +203,13 @@ export default class AppInsightsDatasource {
}); });
} }
doRequest(url, maxRetries = 1) { doRequest(url: any, maxRetries = 1) {
return this.backendSrv return this.backendSrv
.datasourceRequest({ .datasourceRequest({
url: this.url + url, url: this.url + url,
method: 'GET', method: 'GET',
}) })
.catch(error => { .catch((error: any) => {
if (maxRetries > 0) { if (maxRetries > 0) {
return this.doRequest(url, maxRetries - 1); return this.doRequest(url, maxRetries - 1);
} }
@@ -223,20 +225,20 @@ export default class AppInsightsDatasource {
getMetricMetadata(metricName: string) { getMetricMetadata(metricName: string) {
const url = `${this.baseUrl}/metrics/metadata`; const url = `${this.baseUrl}/metrics/metadata`;
return this.doRequest(url).then(result => { return this.doRequest(url).then((result: any) => {
return new ResponseParser(result).parseMetadata(metricName); return new ResponseParser(result).parseMetadata(metricName);
}); });
} }
getGroupBys(metricName: string) { getGroupBys(metricName: string) {
return this.getMetricMetadata(metricName).then(result => { return this.getMetricMetadata(metricName).then((result: any) => {
return new ResponseParser(result).parseGroupBys(); return new ResponseParser(result).parseGroupBys();
}); });
} }
getQuerySchema() { getQuerySchema() {
const url = `${this.baseUrl}/query/schema`; const url = `${this.baseUrl}/query/schema`;
return this.doRequest(url).then(result => { return this.doRequest(url).then((result: any) => {
const schema = new ResponseParser(result).parseQuerySchema(); const schema = new ResponseParser(result).parseQuerySchema();
// console.log(schema); // console.log(schema);
return schema; return schema;
@@ -162,7 +162,7 @@ export default class FakeSchemaData {
}; };
} }
static getlogAnalyticsFakeMetadata() { static getlogAnalyticsFakeMetadata(): any {
return { return {
tables: [ tables: [
{ {
@@ -1,5 +1,5 @@
export default class SupportedNamespaces { export default class SupportedNamespaces {
supportedMetricNamespaces = { supportedMetricNamespaces: any = {
azuremonitor: [ azuremonitor: [
'Microsoft.AnalysisServices/servers', 'Microsoft.AnalysisServices/servers',
'Microsoft.ApiManagement/service', 'Microsoft.ApiManagement/service',
@@ -43,7 +43,7 @@ const defaultSchema: any = () => ({
}, },
}); });
const cleanText = s => s.replace(/[{}[\]="(),!~+\-*/^%]/g, '').trim(); const cleanText = (s: string) => s.replace(/[{}[\]="(),!~+\-*/^%]/g, '').trim();
const wrapText = (text: string) => ({ text }); const wrapText = (text: string) => ({ text });
export default class KustoQueryField extends QueryField { export default class KustoQueryField extends QueryField {
@@ -353,11 +353,13 @@ export default class KustoQueryField extends QueryField {
} }
getTableSuggestions(db = 'Default'): SuggestionGroup[] { getTableSuggestions(db = 'Default'): SuggestionGroup[] {
// @ts-ignore
if (this.schema.Databases[db]) { if (this.schema.Databases[db]) {
return [ return [
{ {
prefixMatch: true, prefixMatch: true,
label: 'Tables', label: 'Tables',
// @ts-ignore
items: _.map(this.schema.Databases[db].Tables, (t: any) => ({ text: t.Name })), items: _.map(this.schema.Databases[db].Tables, (t: any) => ({ text: t.Name })),
}, },
]; ];
@@ -10,6 +10,7 @@ describe('Graphite query model', () => {
waitForFuncDefsLoaded: jest.fn().mockReturnValue(Promise.resolve(null)), waitForFuncDefsLoaded: jest.fn().mockReturnValue(Promise.resolve(null)),
createFuncInstance: gfunc.createFuncInstance, createFuncInstance: gfunc.createFuncInstance,
}, },
// @ts-ignore
templateSrv: new TemplateSrvStub(), templateSrv: new TemplateSrvStub(),
targets: [], targets: [],
}; };
@@ -111,7 +111,7 @@ describe('Request URL', () => {
}; };
const datasourceWithLabels = { const datasourceWithLabels = {
metadataRequest: url => { metadataRequest: (url: string) => {
if (url.slice(0, 15) === '/api/prom/label') { if (url.slice(0, 15) === '/api/prom/label') {
return { data: { data: ['other'] } }; return { data: { data: ['other'] } };
} else { } else {
@@ -154,7 +154,7 @@ describe('Query imports', () => {
it('returns empty query from selector query if label is not available', async () => { it('returns empty query from selector query if label is not available', async () => {
const datasourceWithLabels = { const datasourceWithLabels = {
metadataRequest: url => metadataRequest: (url: string) =>
url.slice(0, 15) === '/api/prom/label' url.slice(0, 15) === '/api/prom/label'
? { data: { data: ['other'] } } ? { data: { data: ['other'] } }
: { data: { data: [] as DataQueryResponseData[] } }, : { data: { data: [] as DataQueryResponseData[] } },
@@ -166,7 +166,7 @@ describe('Query imports', () => {
it('returns selector query from selector query with common labels', async () => { it('returns selector query from selector query with common labels', async () => {
const datasourceWithLabels = { const datasourceWithLabels = {
metadataRequest: url => metadataRequest: (url: string) =>
url.slice(0, 15) === '/api/prom/label' url.slice(0, 15) === '/api/prom/label'
? { data: { data: ['foo'] } } ? { data: { data: ['foo'] } }
: { data: { data: [] as DataQueryResponseData[] } }, : { data: { data: [] as DataQueryResponseData[] } },
@@ -178,7 +178,7 @@ describe('Query imports', () => {
it('returns selector query from selector query with all labels if logging label list is empty', async () => { it('returns selector query from selector query with all labels if logging label list is empty', async () => {
const datasourceWithLabels = { const datasourceWithLabels = {
metadataRequest: url => metadataRequest: (url: string) =>
url.slice(0, 15) === '/api/prom/label' url.slice(0, 15) === '/api/prom/label'
? { data: { data: [] as DataQueryResponseData[] } } ? { data: { data: [] as DataQueryResponseData[] } }
: { data: { data: [] as DataQueryResponseData[] } }, : { data: { data: [] as DataQueryResponseData[] } },
+1 -1
View File
@@ -3,7 +3,7 @@
echo -e "Collecting code stats (typescript errors & more)" echo -e "Collecting code stats (typescript errors & more)"
ERROR_COUNT_LIMIT=2350 ERROR_COUNT_LIMIT=1670
DIRECTIVES_LIMIT=172 DIRECTIVES_LIMIT=172
CONTROLLERS_LIMIT=139 CONTROLLERS_LIMIT=139
+42
View File
@@ -0,0 +1,42 @@
#!/bin/bash
function parse_git_hash() {
git rev-parse --short HEAD 2> /dev/null | sed "s/\(.*\)/\1/"
}
function prapare_version_commit () {
echo $'\nCommiting version changes. This commit will not be checked-in!'
git config --global user.email "circleci@grafana.com"
git config --global user.name "CirceCI"
git commit -am "Version commit"
}
#Get current version from lerna.json
PACKAGE_VERSION=`grep '"version"' lerna.json | cut -d '"' -f 4`
# Get short current commit's has
GIT_SHA=$(parse_git_hash)
echo "Commit: ${GIT_SHA}"
echo "Current lerna.json version: ${PACKAGE_VERSION}"
# count packages that changed
count=`npx lerna changed --loglevel silent | awk '{c++} END {print c}'`
if [ -z $count ]; then
echo "No changes in packages, skipping packages publishing"
else
echo "Changes detected in ${count} packages"
echo "Releasing packages under ${PACKAGE_VERSION}-${GIT_SHA}"
npx lerna version ${PACKAGE_VERSION}-${GIT_SHA} --no-git-tag-version --no-push --force-publish -y
echo $'\nGit status:'
git status -s
echo $'\nBuilding packages'
yarn packages:build
prapare_version_commit
echo $'\nPublishing packages'
yarn packages:publishNext
fi
+1570 -57
View File
File diff suppressed because it is too large Load Diff