diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d8ccbe7618..bfff6ea811a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index fd91a28693a..f44b92645a6 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -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'] diff --git a/docs/sources/datasources/influxdb.md b/docs/sources/datasources/influxdb.md index 0c96d2b733c..b48b2b0097a 100644 --- a/docs/sources/datasources/influxdb.md +++ b/docs/sources/datasources/influxdb.md @@ -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. diff --git a/pkg/api/org_invite.go b/pkg/api/org_invite.go index b7d8d213853..8d916677ed2 100644 --- a/pkg/api/org_invite.go +++ b/pkg/api/org_invite.go @@ -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 { diff --git a/pkg/api/signup.go b/pkg/api/signup.go index fa450b93df2..767b9801154 100644 --- a/pkg/api/signup.go +++ b/pkg/api/signup.go @@ -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) } diff --git a/pkg/models/user.go b/pkg/models/user.go index 30ab34db9a6..64067d59654 100644 --- a/pkg/models/user.go +++ b/pkg/models/user.go @@ -52,6 +52,7 @@ type CreateUserCommand struct { Password string EmailVerified bool IsAdmin bool + SkipOrgSetup bool Result User } diff --git a/pkg/services/notifications/notifications.go b/pkg/services/notifications/notifications.go index 576fe828331..5857ee5247d 100644 --- a/pkg/services/notifications/notifications.go +++ b/pkg/services/notifications/notifications.go @@ -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, }, diff --git a/pkg/services/sqlstore/user.go b/pkg/services/sqlstore/user.go index b26000673d6..6c6f581dcc4 100644 --- a/pkg/services/sqlstore/user.go +++ b/pkg/services/sqlstore/user.go @@ -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 }) } diff --git a/public/app/features/dashboard/partials/import.html b/public/app/features/dashboard/partials/import.html index a942297ac59..4b44544dc7b 100644 --- a/public/app/features/dashboard/partials/import.html +++ b/public/app/features/dashboard/partials/import.html @@ -1,6 +1,6 @@ diff --git a/public/app/features/templating/partials/editor.html b/public/app/features/templating/partials/editor.html index ed93e5e111e..a76c83580d5 100644 --- a/public/app/features/templating/partials/editor.html +++ b/public/app/features/templating/partials/editor.html @@ -200,7 +200,7 @@ Multi format
  • - +
  • diff --git a/public/app/plugins/datasource/influxdb/queryBuilder.js b/public/app/plugins/datasource/influxdb/queryBuilder.js index c6306f20c66..e3e4d6a27ed 100644 --- a/public/app/plugins/datasource/influxdb/queryBuilder.js +++ b/public/app/plugins/datasource/influxdb/queryBuilder.js @@ -102,7 +102,7 @@ function (_) { if (i > 0) { query += ', '; } - query += field.func + '(' + field.name + ')'; + query += field.func + '("' + field.name + '")'; } var measurement = target.measurement; diff --git a/public/app/plugins/datasource/influxdb/queryCtrl.js b/public/app/plugins/datasource/influxdb/queryCtrl.js index 0d1a9354b65..186ffaf1a11 100644 --- a/public/app/plugins/datasource/influxdb/queryCtrl.js +++ b/public/app/plugins/datasource/influxdb/queryCtrl.js @@ -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: '', type: 'plus-button' }); }; diff --git a/public/app/plugins/datasource/kairosdb/datasource.js b/public/app/plugins/datasource/kairosdb/datasource.js index a0e9ac4e949..6db99f5a244 100644 --- a/public/app/plugins/datasource/kairosdb/datasource.js +++ b/public/app/plugins/datasource/kairosdb/datasource.js @@ -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 []; } diff --git a/public/test/specs/influx09-querybuilder-specs.js b/public/test/specs/influx09-querybuilder-specs.js index 9a9ddd15fe5..098ceeb6ca4 100644 --- a/public/test/specs/influx09-querybuilder-specs.js +++ b/public/test/specs/influx09-querybuilder-specs.js @@ -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"'); }); });