mirror of
https://github.com/grafana/grafana.git
synced 2025-08-06 03:09:26 +08:00
Merge branch 'master' into cloudwatch
This commit is contained in:
@ -24,6 +24,7 @@ it allows you to add queries of differnet data source types & instances to the s
|
||||
- [Issue #2563](https://github.com/grafana/grafana/issues/2563). Annotations: Fixed issue when html sanitizer failes for title to annotation body, now fallbacks to html escaping title and text
|
||||
- [Issue #2564](https://github.com/grafana/grafana/issues/2564). Templating: Another atempt at fixing #2534 (Init multi value template var used in repeat panel from url)
|
||||
- [Issue #2620](https://github.com/grafana/grafana/issues/2620). Graph: multi series tooltip did no highlight correct point when stacking was enabled and series were of different resolution
|
||||
- [Issue #2636](https://github.com/grafana/grafana/issues/2636). InfluxDB: Do no show template vars in dropdown for tag keys and group by keys
|
||||
|
||||
**Breaking Changes**
|
||||
- Notice to makers/users of custom data sources, there is a minor breaking change in 2.2 that
|
||||
|
@ -49,7 +49,6 @@ pages:
|
||||
- ['reference/graph.md', 'Reference', 'Graph Panel']
|
||||
- ['reference/singlestat.md', 'Reference', 'Singlestat Panel']
|
||||
- ['reference/dashlist.md', 'Reference', 'Dashboard List Panel']
|
||||
- ['reference/text.md', 'Reference', 'Text Panel']
|
||||
- ['reference/sharing.md', 'Reference', 'Sharing']
|
||||
- ['reference/annotations.md', 'Reference', 'Annotations']
|
||||
- ['reference/timerange.md', 'Reference', 'Time Range Controls']
|
||||
|
@ -51,7 +51,7 @@ the tag key and select `--remove tag filter--`.
|
||||
|
||||
### Regex matching
|
||||
You can type in regex patterns for metric names or tag filter values, be sure to wrap the regex pattern in forward slashes (`/`). Grafana
|
||||
will automaticallay adjust the filter tag condition to use the InfluxDB regex match condition operator (`=~`).
|
||||
will automatically adjust the filter tag condition to use the InfluxDB regex match condition operator (`=~`).
|
||||
|
||||
### Editor group by
|
||||
To group by a tag click the plus icon after the `GROUP BY ($interval)` text. Pick a tag from the dropdown that appears.
|
||||
|
@ -154,10 +154,11 @@ func CompleteInvite(c *middleware.Context, completeInvite dtos.CompleteInviteFor
|
||||
}
|
||||
|
||||
cmd := m.CreateUserCommand{
|
||||
Email: completeInvite.Email,
|
||||
Name: completeInvite.Name,
|
||||
Login: completeInvite.Username,
|
||||
Password: completeInvite.Password,
|
||||
Email: completeInvite.Email,
|
||||
Name: completeInvite.Name,
|
||||
Login: completeInvite.Username,
|
||||
Password: completeInvite.Password,
|
||||
SkipOrgSetup: true,
|
||||
}
|
||||
|
||||
if err := bus.Dispatch(&cmd); err != nil {
|
||||
|
@ -65,6 +65,7 @@ func SignUpStep2(c *middleware.Context, form dtos.SignUpStep2Form) Response {
|
||||
OrgName: form.OrgName,
|
||||
}
|
||||
|
||||
// verify email
|
||||
if setting.VerifyEmailEnabled {
|
||||
if ok, rsp := verifyUserSignUpEmail(form.Email, form.Code); !ok {
|
||||
return rsp
|
||||
@ -72,11 +73,13 @@ func SignUpStep2(c *middleware.Context, form dtos.SignUpStep2Form) Response {
|
||||
createUserCmd.EmailVerified = true
|
||||
}
|
||||
|
||||
// check if user exists
|
||||
existing := m.GetUserByLoginQuery{LoginOrEmail: form.Email}
|
||||
if err := bus.Dispatch(&existing); err == nil {
|
||||
return ApiError(401, "User with same email address already exists", nil)
|
||||
}
|
||||
|
||||
// dispatch create command
|
||||
if err := bus.Dispatch(&createUserCmd); err != nil {
|
||||
return ApiError(500, "Failed to create user", err)
|
||||
}
|
||||
|
@ -52,6 +52,7 @@ type CreateUserCommand struct {
|
||||
Password string
|
||||
EmailVerified bool
|
||||
IsAdmin bool
|
||||
SkipOrgSetup bool
|
||||
|
||||
Result User
|
||||
}
|
||||
|
@ -153,7 +153,7 @@ func signUpCompletedHandler(evt *events.SignUpCompleted) error {
|
||||
|
||||
return sendEmailCommandHandler(&m.SendEmailCommand{
|
||||
To: []string{evt.Email},
|
||||
Template: tmplSignUpStarted,
|
||||
Template: tmplWelcomeOnSignUp,
|
||||
Data: map[string]interface{}{
|
||||
"Name": evt.Name,
|
||||
},
|
||||
|
@ -30,6 +30,10 @@ func init() {
|
||||
}
|
||||
|
||||
func getOrgIdForNewUser(cmd *m.CreateUserCommand, sess *session) (int64, error) {
|
||||
if cmd.SkipOrgSetup {
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
var org m.Org
|
||||
|
||||
if setting.AutoAssignOrg {
|
||||
@ -103,23 +107,6 @@ func CreateUser(cmd *m.CreateUserCommand) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// create org user link
|
||||
orgUser := m.OrgUser{
|
||||
OrgId: orgId,
|
||||
UserId: user.Id,
|
||||
Role: m.ROLE_ADMIN,
|
||||
Created: time.Now(),
|
||||
Updated: time.Now(),
|
||||
}
|
||||
|
||||
if setting.AutoAssignOrg && !user.IsAdmin {
|
||||
orgUser.Role = m.RoleType(setting.AutoAssignOrgRole)
|
||||
}
|
||||
|
||||
if _, err = sess.Insert(&orgUser); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sess.publishAfterCommit(&events.UserCreated{
|
||||
Timestamp: user.Created,
|
||||
Id: user.Id,
|
||||
@ -129,6 +116,26 @@ func CreateUser(cmd *m.CreateUserCommand) error {
|
||||
})
|
||||
|
||||
cmd.Result = user
|
||||
|
||||
// create org user link
|
||||
if !cmd.SkipOrgSetup {
|
||||
orgUser := m.OrgUser{
|
||||
OrgId: orgId,
|
||||
UserId: user.Id,
|
||||
Role: m.ROLE_ADMIN,
|
||||
Created: time.Now(),
|
||||
Updated: time.Now(),
|
||||
}
|
||||
|
||||
if setting.AutoAssignOrg && !user.IsAdmin {
|
||||
orgUser.Role = m.RoleType(setting.AutoAssignOrgRole)
|
||||
}
|
||||
|
||||
if _, err = sess.Insert(&orgUser); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
<topnav icon="fa fa-th-large" title="Dashboards" subnav="true">
|
||||
<ul class="nav">
|
||||
<li class="active"><a href="import">Import</a></li>
|
||||
<li class="active"><a href="import/dashboard">Import</a></li>
|
||||
</ul>
|
||||
</topnav>
|
||||
|
||||
|
@ -200,7 +200,7 @@
|
||||
Multi format
|
||||
</li>
|
||||
<li ng-show="current.multi">
|
||||
<select class="input-small tight-form-input last" ng-model="current.multiFormat" ng-change="runQuery()" ng-options="f for f in ['glob', 'regex values']"></select>
|
||||
<select class="input-medium tight-form-input last" ng-model="current.multiFormat" ng-change="runQuery()" ng-options="f for f in ['glob', 'regex values']"></select>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
|
@ -102,7 +102,7 @@ function (_) {
|
||||
if (i > 0) {
|
||||
query += ', ';
|
||||
}
|
||||
query += field.func + '(' + field.name + ')';
|
||||
query += field.func + '("' + field.name + '")';
|
||||
}
|
||||
|
||||
var measurement = target.measurement;
|
||||
|
@ -119,8 +119,7 @@ function (angular, _, InfluxQueryBuilder) {
|
||||
$scope.getMeasurements = function () {
|
||||
var query = $scope.queryBuilder.buildExploreQuery('MEASUREMENTS');
|
||||
return $scope.datasource.metricFindQuery(query)
|
||||
.then($scope.transformToSegments)
|
||||
.then($scope.addTemplateVariableSegments)
|
||||
.then($scope.transformToSegments(true))
|
||||
.then(null, $scope.handleQueryError);
|
||||
};
|
||||
|
||||
@ -129,42 +128,46 @@ function (angular, _, InfluxQueryBuilder) {
|
||||
return [];
|
||||
};
|
||||
|
||||
$scope.transformToSegments = function(results) {
|
||||
return _.map(results, function(segment) {
|
||||
return new MetricSegment({ value: segment.text, expandable: segment.expandable });
|
||||
});
|
||||
};
|
||||
$scope.transformToSegments = function(addTemplateVars) {
|
||||
return function(results) {
|
||||
var segments = _.map(results, function(segment) {
|
||||
return new MetricSegment({ value: segment.text, expandable: segment.expandable });
|
||||
});
|
||||
|
||||
$scope.addTemplateVariableSegments = function(segments) {
|
||||
_.each(templateSrv.variables, function(variable) {
|
||||
segments.unshift(new MetricSegment({ type: 'template', value: '/$' + variable.name + '/', expandable: true }));
|
||||
});
|
||||
return segments;
|
||||
if (addTemplateVars) {
|
||||
_.each(templateSrv.variables, function(variable) {
|
||||
segments.unshift(new MetricSegment({ type: 'template', value: '/$' + variable.name + '/', expandable: true }));
|
||||
});
|
||||
}
|
||||
|
||||
return segments;
|
||||
};
|
||||
};
|
||||
|
||||
$scope.getTagsOrValues = function(segment, index) {
|
||||
var query;
|
||||
if (segment.type === 'condition') {
|
||||
return $q.when([new MetricSegment('AND'), new MetricSegment('OR')]);
|
||||
}
|
||||
if (segment.type === 'operator') {
|
||||
var nextValue = $scope.tagSegments[index+1].value;
|
||||
if (/^\/.*\/$/.test(nextValue)) {
|
||||
return $q.when(MetricSegment.newOperators(['=~', '!~']));
|
||||
} else {
|
||||
return $q.when(MetricSegment.newOperators(['=', '<>', '<', '>']));
|
||||
}
|
||||
}
|
||||
|
||||
var query, addTemplateVars;
|
||||
if (segment.type === 'key' || segment.type === 'plus-button') {
|
||||
query = $scope.queryBuilder.buildExploreQuery('TAG_KEYS');
|
||||
addTemplateVars = false;
|
||||
} else if (segment.type === 'value') {
|
||||
query = $scope.queryBuilder.buildExploreQuery('TAG_VALUES', $scope.tagSegments[index-2].value);
|
||||
} else if (segment.type === 'condition') {
|
||||
return $q.when([new MetricSegment('AND'), new MetricSegment('OR')]);
|
||||
} else if (segment.type === 'operator' && /^(?!\/.*\/$)/.test($scope.tagSegments[index+1].value)) {
|
||||
return $q.when([MetricSegment.newOperator('='), MetricSegment.newOperator('<>'),
|
||||
MetricSegment.newOperator('<'), MetricSegment.newOperator('>')]);
|
||||
|
||||
} else if (segment.type === 'operator' && /^\/.*\/$/.test($scope.tagSegments[index+1].value)) {
|
||||
return $q.when([MetricSegment.newOperator('=~'), MetricSegment.newOperator('!~')]);
|
||||
}
|
||||
else {
|
||||
return $q.when([]);
|
||||
addTemplateVars = true;
|
||||
}
|
||||
|
||||
return $scope.datasource.metricFindQuery(query)
|
||||
.then($scope.transformToSegments)
|
||||
.then($scope.addTemplateVariableSegments)
|
||||
.then($scope.transformToSegments(addTemplateVars))
|
||||
.then(function(results) {
|
||||
if (segment.type === 'key') {
|
||||
results.splice(0, 0, angular.copy($scope.removeTagFilterSegment));
|
||||
@ -177,8 +180,8 @@ function (angular, _, InfluxQueryBuilder) {
|
||||
$scope.getFieldSegments = function() {
|
||||
var fieldsQuery = $scope.queryBuilder.buildExploreQuery('FIELDS');
|
||||
return $scope.datasource.metricFindQuery(fieldsQuery)
|
||||
.then($scope.transformToSegments)
|
||||
.then(null, $scope.handleQueryError);
|
||||
.then($scope.transformToSegments(false))
|
||||
.then(null, $scope.handleQueryError);
|
||||
};
|
||||
|
||||
$scope.addField = function() {
|
||||
@ -197,8 +200,7 @@ function (angular, _, InfluxQueryBuilder) {
|
||||
var query = $scope.queryBuilder.buildExploreQuery('TAG_KEYS');
|
||||
|
||||
return $scope.datasource.metricFindQuery(query)
|
||||
.then($scope.transformToSegments)
|
||||
.then($scope.addTemplateVariableSegments)
|
||||
.then($scope.transformToSegments(false))
|
||||
.then(function(results) {
|
||||
if (segment.type !== 'plus-button') {
|
||||
results.splice(0, 0, angular.copy($scope.removeGroupBySegment));
|
||||
@ -322,6 +324,12 @@ function (angular, _, InfluxQueryBuilder) {
|
||||
return new MetricSegment({value: op, type: 'operator', cssClass: 'query-segment-operator' });
|
||||
};
|
||||
|
||||
MetricSegment.newOperators = function(ops) {
|
||||
return _.map(ops, function(op) {
|
||||
return new MetricSegment({value: op, type: 'operator', cssClass: 'query-segment-operator' });
|
||||
});
|
||||
};
|
||||
|
||||
MetricSegment.newPlusButton = function() {
|
||||
return new MetricSegment({fake: true, html: '<i class="fa fa-plus "></i>', type: 'plus-button' });
|
||||
};
|
||||
|
@ -10,7 +10,7 @@ function (angular, _, kbn) {
|
||||
|
||||
var module = angular.module('grafana.services');
|
||||
|
||||
module.factory('KairosDBDatasource', function($q, $http, templateSrv) {
|
||||
module.factory('KairosDBDatasource', function($q, backendSrv, templateSrv) {
|
||||
|
||||
function KairosDBDatasource(datasource) {
|
||||
this.type = datasource.type;
|
||||
@ -51,10 +51,6 @@ function (angular, _, kbn) {
|
||||
return this.performTimeSeriesQuery(queries, start, end).then(handleKairosDBQueryResponseAlias, handleQueryError);
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
/// Query methods
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
KairosDBDatasource.prototype.performTimeSeriesQuery = function(queries, start, end) {
|
||||
var reqBody = {
|
||||
metrics: queries,
|
||||
@ -70,7 +66,7 @@ function (angular, _, kbn) {
|
||||
data: reqBody
|
||||
};
|
||||
|
||||
return $http(options);
|
||||
return backendSrv.datasourceRequest(options);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -83,7 +79,7 @@ function (angular, _, kbn) {
|
||||
method: 'GET'
|
||||
};
|
||||
|
||||
return $http(options).then(function(response) {
|
||||
return backendSrv.datasourceRequest(options).then(function(response) {
|
||||
if (!response.data) {
|
||||
return $q.when([]);
|
||||
}
|
||||
@ -110,7 +106,7 @@ function (angular, _, kbn) {
|
||||
}
|
||||
};
|
||||
|
||||
return $http(options).then(function(result) {
|
||||
return backendSrv.datasourceRequest(options).then(function(result) {
|
||||
if (!result.data) {
|
||||
return $q.when([]);
|
||||
}
|
||||
@ -139,7 +135,7 @@ function (angular, _, kbn) {
|
||||
}
|
||||
};
|
||||
|
||||
return $http(options).then(function(result) {
|
||||
return backendSrv.datasourceRequest(options).then(function(result) {
|
||||
if (!result.data) {
|
||||
return $q.when([]);
|
||||
}
|
||||
@ -158,7 +154,7 @@ function (angular, _, kbn) {
|
||||
}
|
||||
};
|
||||
|
||||
return $http(options).then(function(response) {
|
||||
return backendSrv.datasourceRequest(options).then(function(response) {
|
||||
if (!response.data) {
|
||||
return [];
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ define([
|
||||
var query = builder.build();
|
||||
|
||||
it('should generate correct query', function() {
|
||||
expect(query).to.be('SELECT mean(value) FROM "cpu" WHERE $timeFilter GROUP BY time($interval)');
|
||||
expect(query).to.be('SELECT mean("value") FROM "cpu" WHERE $timeFilter GROUP BY time($interval)');
|
||||
});
|
||||
|
||||
});
|
||||
@ -27,14 +27,14 @@ define([
|
||||
var query = builder.build();
|
||||
|
||||
it('should generate correct query', function() {
|
||||
expect(query).to.be('SELECT mean(value) FROM "cpu" WHERE "hostname" = \'server1\' AND $timeFilter'
|
||||
expect(query).to.be('SELECT mean("value") FROM "cpu" WHERE "hostname" = \'server1\' AND $timeFilter'
|
||||
+ ' GROUP BY time($interval)');
|
||||
});
|
||||
|
||||
it('should switch regex operator with tag value is regex', function() {
|
||||
var builder = new InfluxQueryBuilder({measurement: 'cpu', tags: [{key: 'app', value: '/e.*/'}]});
|
||||
var query = builder.build();
|
||||
expect(query).to.be('SELECT mean(value) FROM "cpu" WHERE "app" =~ /e.*/ AND $timeFilter GROUP BY time($interval)');
|
||||
expect(query).to.be('SELECT mean("value") FROM "cpu" WHERE "app" =~ /e.*/ AND $timeFilter GROUP BY time($interval)');
|
||||
});
|
||||
});
|
||||
|
||||
@ -48,7 +48,7 @@ define([
|
||||
var query = builder.build();
|
||||
|
||||
it('should generate correct query', function() {
|
||||
expect(query).to.be('SELECT sum(tx_in), mean(tx_out) FROM "cpu" WHERE $timeFilter GROUP BY time($interval)');
|
||||
expect(query).to.be('SELECT sum("tx_in"), mean("tx_out") FROM "cpu" WHERE $timeFilter GROUP BY time($interval)');
|
||||
});
|
||||
});
|
||||
|
||||
@ -61,7 +61,7 @@ define([
|
||||
var query = builder.build();
|
||||
|
||||
it('should generate correct query', function() {
|
||||
expect(query).to.be('SELECT mean(value) FROM "cpu" WHERE "hostname" = \'server1\' AND "app" = \'email\' AND ' +
|
||||
expect(query).to.be('SELECT mean("value") FROM "cpu" WHERE "hostname" = \'server1\' AND "app" = \'email\' AND ' +
|
||||
'$timeFilter GROUP BY time($interval)');
|
||||
});
|
||||
});
|
||||
@ -75,7 +75,7 @@ define([
|
||||
var query = builder.build();
|
||||
|
||||
it('should generate correct query', function() {
|
||||
expect(query).to.be('SELECT mean(value) FROM "cpu" WHERE "hostname" = \'server1\' OR "hostname" = \'server2\' AND ' +
|
||||
expect(query).to.be('SELECT mean("value") FROM "cpu" WHERE "hostname" = \'server1\' OR "hostname" = \'server2\' AND ' +
|
||||
'$timeFilter GROUP BY time($interval)');
|
||||
});
|
||||
});
|
||||
@ -89,7 +89,7 @@ define([
|
||||
});
|
||||
|
||||
var query = builder.build();
|
||||
expect(query).to.be('SELECT mean(value) FROM "cpu" WHERE $timeFilter ' +
|
||||
expect(query).to.be('SELECT mean("value") FROM "cpu" WHERE $timeFilter ' +
|
||||
'GROUP BY time($interval), "host"');
|
||||
});
|
||||
});
|
||||
|
Reference in New Issue
Block a user