mirror of
https://github.com/grafana/grafana.git
synced 2026-07-30 08:18:10 -05:00
Merge branch 'master' of github.com:grafana/grafana
This commit is contained in:
+15
-2
@@ -1,15 +1,28 @@
|
||||
# 4.3.0 (unreleased)
|
||||
|
||||
## Minor Enchancements
|
||||
* **Threema**: Add emoji to Threema alert notifications [#7676](https://github.com/grafana/grafana/pull/7676) thx [@dbrgn](https://github.com/dbrgn)
|
||||
* **Panels**: Support dm3 unit [#7695](https://github.com/grafana/grafana/issues/7695) thx [@mitjaziv](https://github.com/mitjaziv)
|
||||
|
||||
# 4.2.0-beta2 (unreleased)
|
||||
## Minor Enhancements
|
||||
* **Templates**: Prevent use of the prefix `__` for templates in web UI [#7678](https://github.com/grafana/grafana/issues/7678)
|
||||
* **Threema**: Add emoji to Threema alert notifications [#7676](https://github.com/grafana/grafana/pull/7676) thx [@dbrgn](https://github.com/dbrgn)
|
||||
* **Panels**: Support dm3 unit [#7695](https://github.com/grafana/grafana/issues/7695) thx [@mitjaziv](https://github.com/mitjaziv)
|
||||
* **Docs**: Added some details about Sessions in Postgres [#7694](https://github.com/grafana/grafana/pull/7694) thx [@rickard-von-essen](https://github.com/rickard-von-essen)
|
||||
* **Influxdb**: Allow commas in template variables [#7681](https://github.com/grafana/grafana/issues/7681) thx [@thuck](https://github.com/thuck)
|
||||
* **Cloudwatch**: stop using deprecated session.New() [#7736](https://github.com/grafana/grafana/issues/7736) thx [@mtanda](https://github.com/mtanda)
|
||||
* **OpenTSDB**: Pass dropcounter rate option if no max counter and no reset value or reset value as 0 is specified [#7743](https://github.com/grafana/grafana/pull/7743) thx [@r4um](https://github.com/r4um)
|
||||
* **Templating**: support full resolution for $interval variable [#7696](https://github.com/grafana/grafana/pull/7696) thx [@mtanda](https://github.com/mtanda)
|
||||
* **Elasticsearch**: Unique Count on string fields in ElasticSearch [#3536](https://github.com/grafana/grafana/issues/3536), thx [@pyro2927](https://github.com/pyro2927)
|
||||
* **Templating**: Data source template variable that refers to other variable in regex filter [#6365](https://github.com/grafana/grafana/issues/6365) thx [@rlodge](https://github.com/rlodge)
|
||||
* **Admin**: Global User List: add search and pagination [#7469](https://github.com/grafana/grafana/issues/7469)
|
||||
|
||||
## Bugfixes
|
||||
* **Webhook**: Use proxy settings from environment variables [#7710](https://github.com/grafana/grafana/issues/7710)
|
||||
* **Panels**: Deleting a dashboard with unsaved changes raises an error message [#7591](https://github.com/grafana/grafana/issues/7591) thx [@thuck](https://github.com/thuck)
|
||||
* **Influxdb**: Query builder detects regex to easily for measurement [#7276](https://github.com/grafana/grafana/issues/7276) thx [@thuck](https://github.com/thuck)
|
||||
* **Docs**: router_logging not documented [#7723](https://github.com/grafana/grafana/issues/7723)
|
||||
* **Alerting**: Spelling mistake [#7739](https://github.com/grafana/grafana/pull/7739) thx [@woutersmit](https://github.com/woutersmit)
|
||||
* **Alerting**: Graph legend scrolls to top when an alias is toggled/clicked [#7680](https://github.com/grafana/grafana/issues/7680) thx [@p4ddy1](https://github.com/p4ddy1)
|
||||
|
||||
# 4.2.0-beta1 (2017-02-27)
|
||||
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
"company": "Coding Instinct AB"
|
||||
},
|
||||
"name": "grafana",
|
||||
"version": "4.2.0-pre1",
|
||||
"version": "4.3.0-pre1",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "http://github.com/grafana/grafana.git"
|
||||
|
||||
+3
-1
@@ -239,7 +239,9 @@ func searchUser(c *middleware.Context) (*m.SearchUsersQuery, error) {
|
||||
page = 1
|
||||
}
|
||||
|
||||
query := &m.SearchUsersQuery{Query: "", Page: page, Limit: perPage}
|
||||
searchQuery := c.Query("query")
|
||||
|
||||
query := &m.SearchUsersQuery{Query: searchQuery, Page: page, Limit: perPage}
|
||||
if err := bus.Dispatch(query); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -363,8 +363,12 @@ func SearchUsers(query *m.SearchUsersQuery) error {
|
||||
query.Result = m.SearchUserQueryResult{
|
||||
Users: make([]*m.UserSearchHitDTO, 0),
|
||||
}
|
||||
queryWithWildcards := "%" + query.Query + "%"
|
||||
|
||||
sess := x.Table("user")
|
||||
sess.Where("email LIKE ?", query.Query+"%")
|
||||
if query.Query != "" {
|
||||
sess.Where("email LIKE ? OR name LIKE ? OR login like ?", queryWithWildcards, queryWithWildcards, queryWithWildcards)
|
||||
}
|
||||
offset := query.Limit * (query.Page - 1)
|
||||
sess.Limit(query.Limit, offset)
|
||||
sess.Cols("id", "email", "name", "login", "is_admin")
|
||||
@@ -373,7 +377,12 @@ func SearchUsers(query *m.SearchUsersQuery) error {
|
||||
}
|
||||
|
||||
user := m.User{}
|
||||
count, err := x.Count(&user)
|
||||
|
||||
countSess := x.Table("user")
|
||||
if query.Query != "" {
|
||||
countSess.Where("email LIKE ? OR name LIKE ? OR login like ?", queryWithWildcards, queryWithWildcards, queryWithWildcards)
|
||||
}
|
||||
count, err := countSess.Count(&user)
|
||||
query.Result.TotalCount = count
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ func TestUserDataAccess(t *testing.T) {
|
||||
err = CreateUser(&models.CreateUserCommand{
|
||||
Email: fmt.Sprint("user", i, "@test.com"),
|
||||
Name: fmt.Sprint("user", i),
|
||||
Login: fmt.Sprint("user", i),
|
||||
Login: fmt.Sprint("loginuser", i),
|
||||
})
|
||||
So(err, ShouldBeNil)
|
||||
}
|
||||
@@ -41,5 +41,53 @@ func TestUserDataAccess(t *testing.T) {
|
||||
So(len(query.Result.Users), ShouldEqual, 2)
|
||||
So(query.Result.TotalCount, ShouldEqual, 5)
|
||||
})
|
||||
|
||||
Convey("Can return list of users matching query on user name", func() {
|
||||
query := models.SearchUsersQuery{Query: "use", Page: 1, Limit: 3}
|
||||
err = SearchUsers(&query)
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(len(query.Result.Users), ShouldEqual, 3)
|
||||
So(query.Result.TotalCount, ShouldEqual, 5)
|
||||
|
||||
query = models.SearchUsersQuery{Query: "ser1", Page: 1, Limit: 3}
|
||||
err = SearchUsers(&query)
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(len(query.Result.Users), ShouldEqual, 1)
|
||||
So(query.Result.TotalCount, ShouldEqual, 1)
|
||||
|
||||
query = models.SearchUsersQuery{Query: "USER1", Page: 1, Limit: 3}
|
||||
err = SearchUsers(&query)
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(len(query.Result.Users), ShouldEqual, 1)
|
||||
So(query.Result.TotalCount, ShouldEqual, 1)
|
||||
|
||||
query = models.SearchUsersQuery{Query: "idontexist", Page: 1, Limit: 3}
|
||||
err = SearchUsers(&query)
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(len(query.Result.Users), ShouldEqual, 0)
|
||||
So(query.Result.TotalCount, ShouldEqual, 0)
|
||||
})
|
||||
|
||||
Convey("Can return list of users matching query on email", func() {
|
||||
query := models.SearchUsersQuery{Query: "ser1@test.com", Page: 1, Limit: 3}
|
||||
err = SearchUsers(&query)
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(len(query.Result.Users), ShouldEqual, 1)
|
||||
So(query.Result.TotalCount, ShouldEqual, 1)
|
||||
})
|
||||
|
||||
Convey("Can return list of users matching query on login name", func() {
|
||||
query := models.SearchUsersQuery{Query: "loginuser1", Page: 1, Limit: 3}
|
||||
err = SearchUsers(&query)
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(len(query.Result.Users), ShouldEqual, 1)
|
||||
So(query.Result.TotalCount, ShouldEqual, 1)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -3,18 +3,20 @@
|
||||
export default class AdminListUsersCtrl {
|
||||
users: any;
|
||||
pages = [];
|
||||
perPage = 1000;
|
||||
perPage = 50;
|
||||
page = 1;
|
||||
totalPages: number;
|
||||
showPaging = false;
|
||||
query: any;
|
||||
|
||||
/** @ngInject */
|
||||
constructor(private $scope, private backendSrv) {
|
||||
this.query = '';
|
||||
this.getUsers();
|
||||
}
|
||||
|
||||
getUsers() {
|
||||
this.backendSrv.get(`/api/users/search?perpage=${this.perPage}&page=${this.page}`).then((result) => {
|
||||
this.backendSrv.get(`/api/users/search?perpage=${this.perPage}&page=${this.page}&query=${this.query}`).then((result) => {
|
||||
this.users = result.users;
|
||||
this.page = result.page;
|
||||
this.perPage = result.perPage;
|
||||
|
||||
@@ -14,6 +14,12 @@
|
||||
Add new user
|
||||
</a>
|
||||
</div>
|
||||
<div class="search-field-wrapper pull-right width-18">
|
||||
<span style="position: relative;">
|
||||
<input type="text" placeholder="Find user by name/login/email" tabindex="1" give-focus="true"
|
||||
ng-model="ctrl.query" ng-model-options="{ debounce: 500 }" spellcheck='false' ng-change="ctrl.getUsers()" />
|
||||
</span>
|
||||
</div>
|
||||
<div class="admin-list-table">
|
||||
<table class="filter-table form-inline">
|
||||
<thead>
|
||||
|
||||
@@ -12,6 +12,7 @@ import * as dateMath from 'app/core/utils/datemath';
|
||||
import {Subject} from 'vendor/npm/rxjs/Subject';
|
||||
|
||||
class MetricsPanelCtrl extends PanelCtrl {
|
||||
scope: any;
|
||||
loading: boolean;
|
||||
datasource: any;
|
||||
datasourceName: any;
|
||||
@@ -40,6 +41,7 @@ class MetricsPanelCtrl extends PanelCtrl {
|
||||
this.datasourceSrv = $injector.get('datasourceSrv');
|
||||
this.timeSrv = $injector.get('timeSrv');
|
||||
this.templateSrv = $injector.get('templateSrv');
|
||||
this.scope = $scope;
|
||||
|
||||
if (!this.panel.targets) {
|
||||
this.panel.targets = [{}];
|
||||
|
||||
@@ -35,6 +35,8 @@ export class PanelCtrl {
|
||||
containerHeight: any;
|
||||
events: Emitter;
|
||||
timing: any;
|
||||
skippedLastRefresh: boolean;
|
||||
isPanelVisible: any;
|
||||
|
||||
constructor($scope, $injector) {
|
||||
this.$injector = $injector;
|
||||
@@ -74,7 +76,18 @@ export class PanelCtrl {
|
||||
profiler.renderingCompleted(this.panel.id, this.timing);
|
||||
}
|
||||
|
||||
private isRenderingPng () {
|
||||
return window.location.href.indexOf("/dashboard-solo/db") >= 0;
|
||||
}
|
||||
|
||||
refresh() {
|
||||
if (!this.isPanelVisible() && !this.isRenderingPng() && !this.dashboard.snapshot) {
|
||||
this.skippedLastRefresh = true;
|
||||
return;
|
||||
}
|
||||
|
||||
this.skippedLastRefresh = false;
|
||||
|
||||
this.events.emit('refresh', null);
|
||||
}
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ var panelTemplate = `
|
||||
</div>
|
||||
`;
|
||||
|
||||
module.directive('grafanaPanel', function($rootScope) {
|
||||
module.directive('grafanaPanel', function($rootScope, $document) {
|
||||
return {
|
||||
restrict: 'E',
|
||||
template: panelTemplate,
|
||||
@@ -175,9 +175,23 @@ module.directive('grafanaPanel', function($rootScope) {
|
||||
elem.on('mouseenter', mouseEnter);
|
||||
elem.on('mouseleave', mouseLeave);
|
||||
|
||||
ctrl.isPanelVisible = function () {
|
||||
var position = panelContainer[0].getBoundingClientRect();
|
||||
return (0 < position.top) && (position.top < window.innerHeight);
|
||||
};
|
||||
|
||||
const refreshOnScroll = _.debounce(function () {
|
||||
if (ctrl.skippedLastRefresh) {
|
||||
ctrl.refresh();
|
||||
}
|
||||
}, 250);
|
||||
|
||||
$document.on('scroll', refreshOnScroll);
|
||||
|
||||
scope.$on('$destroy', function() {
|
||||
elem.off();
|
||||
cornerInfoElem.off();
|
||||
$document.off('scroll', refreshOnScroll);
|
||||
|
||||
if (infoDrop) {
|
||||
infoDrop.destroy();
|
||||
|
||||
@@ -4,6 +4,7 @@ import angular from 'angular';
|
||||
import _ from 'lodash';
|
||||
import moment from 'moment';
|
||||
|
||||
import kbn from 'app/core/utils/kbn';
|
||||
import * as dateMath from 'app/core/utils/datemath';
|
||||
import PrometheusMetricFindQuery from './metric_find_query';
|
||||
|
||||
@@ -88,12 +89,7 @@ export function PrometheusDatasource(instanceSettings, $q, backendSrv, templateS
|
||||
var intervalFactor = target.intervalFactor || 1;
|
||||
target.step = query.step = this.calculateInterval(interval, intervalFactor);
|
||||
var range = Math.ceil(end - start);
|
||||
// Prometheus drop query if range/step > 11000
|
||||
// calibrate step if it is too big
|
||||
if (query.step !== 0 && range / query.step > 11000) {
|
||||
target.step = query.step = Math.ceil(range / 11000);
|
||||
}
|
||||
|
||||
target.step = query.step = this.adjustStep(query.step, range);
|
||||
queries.push(query);
|
||||
});
|
||||
|
||||
@@ -126,6 +122,15 @@ export function PrometheusDatasource(instanceSettings, $q, backendSrv, templateS
|
||||
});
|
||||
};
|
||||
|
||||
this.adjustStep = function(step, range) {
|
||||
// Prometheus drop query if range/step > 11000
|
||||
// calibrate step if it is too big
|
||||
if (step !== 0 && range / step > 11000) {
|
||||
return Math.ceil(range / 11000);
|
||||
}
|
||||
return step;
|
||||
};
|
||||
|
||||
this.performTimeSeriesQuery = function(query, start, end) {
|
||||
if (start > end) {
|
||||
throw { message: 'Invalid time range' };
|
||||
@@ -175,15 +180,19 @@ export function PrometheusDatasource(instanceSettings, $q, backendSrv, templateS
|
||||
return $q.reject(err);
|
||||
}
|
||||
|
||||
var query = {
|
||||
expr: interpolated,
|
||||
step: '60s'
|
||||
};
|
||||
var step = '60s';
|
||||
if (annotation.step) {
|
||||
step = templateSrv.replace(annotation.step);
|
||||
}
|
||||
|
||||
var start = this.getPrometheusTime(options.range.from, false);
|
||||
var end = this.getPrometheusTime(options.range.to, true);
|
||||
var self = this;
|
||||
var query = {
|
||||
expr: interpolated,
|
||||
step: this.adjustStep(kbn.interval_to_seconds(step), Math.ceil(end - start)) + 's'
|
||||
};
|
||||
|
||||
var self = this;
|
||||
return this.performTimeSeriesQuery(query, start, end).then(function(results) {
|
||||
var eventList = [];
|
||||
tagKeys = tagKeys.split(',');
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
|
||||
<h5 class="section-heading">Search expression</h6>
|
||||
<div class="gf-form-group">
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label width-10">Search expression</span>
|
||||
<input type="text" class="gf-form-input" ng-model='ctrl.annotation.expr' placeholder="ALERTS"></input>
|
||||
</div>
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label width-10">step</span>
|
||||
<input type="text" class="gf-form-input max-width-6" ng-model='ctrl.annotation.step' placeholder="60s"></input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gf-form-group">
|
||||
|
||||
Reference in New Issue
Block a user