diff --git a/public/app/core/directives/ng_model_on_blur.js b/public/app/core/directives/ng_model_on_blur.js index 6f4a55b53f0..09c87f5fabe 100644 --- a/public/app/core/directives/ng_model_on_blur.js +++ b/public/app/core/directives/ng_model_on_blur.js @@ -47,7 +47,7 @@ function (coreModule, kbn, rangeUtil) { if (ctrl.$isEmpty(modelValue)) { return true; } - if (viewValue.indexOf('$') === 0) { + if (viewValue.indexOf('$') === 0 || viewValue.indexOf('+$') === 0) { return true; // allow template variable } var info = rangeUtil.describeTextRange(viewValue); diff --git a/public/app/features/dashboard/dashboard_ctrl.ts b/public/app/features/dashboard/dashboard_ctrl.ts index cf72bb87f61..c07f84e820c 100644 --- a/public/app/features/dashboard/dashboard_ctrl.ts +++ b/public/app/features/dashboard/dashboard_ctrl.ts @@ -52,7 +52,7 @@ export class DashboardCtrl { .catch($scope.onInitFailed.bind(this, 'Templating init failed', false)) // continue .finally(function() { - dynamicDashboardSrv.init(dashboard, variableSrv); + dynamicDashboardSrv.init(dashboard); dynamicDashboardSrv.process(); unsavedChangesSrv.init(dashboard, $scope); diff --git a/public/app/features/dashboard/dynamic_dashboard_srv.ts b/public/app/features/dashboard/dynamic_dashboard_srv.ts index e5f1c6a9fa1..629098e290b 100644 --- a/public/app/features/dashboard/dynamic_dashboard_srv.ts +++ b/public/app/features/dashboard/dynamic_dashboard_srv.ts @@ -12,12 +12,12 @@ export class DynamicDashboardSrv { dashboard: any; variables: any; - init(dashboard, variableSrv) { + init(dashboard) { this.dashboard = dashboard; - this.variables = variableSrv.variables; + this.variables = dashboard.templating.list; } - process(options) { + process(options?) { if (this.dashboard.snapshot || this.variables.length === 0) { return; } @@ -31,6 +31,8 @@ export class DynamicDashboardSrv { // cleanup scopedVars for (i = 0; i < this.dashboard.rows.length; i++) { row = this.dashboard.rows[i]; + delete row.scopedVars; + for (j = 0; j < row.panels.length; j++) { delete row.panels[j].scopedVars; } diff --git a/public/app/features/dashboard/export/export_modal.ts b/public/app/features/dashboard/export/export_modal.ts index 57af9d9caf8..c334818b3e7 100644 --- a/public/app/features/dashboard/export/export_modal.ts +++ b/public/app/features/dashboard/export/export_modal.ts @@ -17,9 +17,7 @@ export class DashExportCtrl { constructor(private backendSrv, dashboardSrv, datasourceSrv, $scope) { this.exporter = new DashboardExporter(datasourceSrv); - var current = dashboardSrv.getCurrent().getSaveModelClone(); - - this.exporter.makeExportable(current).then(dash => { + this.exporter.makeExportable(dashboardSrv.getCurrent()).then(dash => { $scope.$apply(() => { this.dash = dash; }); diff --git a/public/app/features/dashboard/export/exporter.ts b/public/app/features/dashboard/export/exporter.ts index 9f9e326e848..5efcd498f67 100644 --- a/public/app/features/dashboard/export/exporter.ts +++ b/public/app/features/dashboard/export/exporter.ts @@ -11,19 +11,40 @@ export class DashboardExporter { constructor(private datasourceSrv) { } - makeExportable(dash) { + makeExportable(dashboard) { var dynSrv = new DynamicDashboardSrv(); - dynSrv.init(dash, {variables: dash.templating.list}); + + // clean up repeated rows and panels, + // this is done on the live real dashboard instance, not on a clone + // so we need to undo this + // this is pretty hacky and needs to be changed + dynSrv.init(dashboard); dynSrv.process({cleanUpOnly: true}); - dash.id = null; + var saveModel = dashboard.getSaveModelClone(); + saveModel.id = null; + + // undo repeat cleanup + dynSrv.process(); var inputs = []; var requires = {}; var datasources = {}; var promises = []; + var variableLookup: any = {}; + + for (let variable of saveModel.templating.list) { + variableLookup[variable.name] = variable; + } var templateizeDatasourceUsage = obj => { + // ignore data source properties that contain a variable + if (obj.datasource && obj.datasource.indexOf('$') === 0) { + if (variableLookup[obj.datasource.substring(1)]){ + return; + } + } + promises.push(this.datasourceSrv.get(obj.datasource).then(ds => { if (ds.meta.builtIn) { return; @@ -50,7 +71,7 @@ export class DashboardExporter { }; // check up panel data sources - for (let row of dash.rows) { + for (let row of saveModel.rows) { for (let panel of row.panels) { if (panel.datasource !== undefined) { templateizeDatasourceUsage(panel); @@ -77,7 +98,7 @@ export class DashboardExporter { } // templatize template vars - for (let variable of dash.templating.list) { + for (let variable of saveModel.templating.list) { if (variable.type === 'query') { templateizeDatasourceUsage(variable); variable.options = []; @@ -87,7 +108,7 @@ export class DashboardExporter { } // templatize annotations vars - for (let annotationDef of dash.annotations.list) { + for (let annotationDef of saveModel.annotations.list) { templateizeDatasourceUsage(annotationDef); } @@ -105,7 +126,7 @@ export class DashboardExporter { }); // templatize constants - for (let variable of dash.templating.list) { + for (let variable of saveModel.templating.list) { if (variable.type === 'constant') { var refName = 'VAR_' + variable.name.replace(' ', '_').toUpperCase(); inputs.push({ @@ -133,7 +154,7 @@ export class DashboardExporter { newObj["__inputs"] = inputs; newObj["__requires"] = requires; - _.defaults(newObj, dash); + _.defaults(newObj, saveModel); return newObj; }).catch(err => { diff --git a/public/app/features/dashboard/model.ts b/public/app/features/dashboard/model.ts index 6b32004cfd5..f0a32fd58fc 100644 --- a/public/app/features/dashboard/model.ts +++ b/public/app/features/dashboard/model.ts @@ -105,7 +105,7 @@ export class DashboardModel { // prepare save model this.rows = _.map(rows, row => row.getSaveModel()); - this.templating.list = _.map(variables, variable => variable.getSaveModel()); + this.templating.list = _.map(variables, variable => variable.getSaveModel ? variable.getSaveModel() : variable); var copy = $.extend(true, {}, this); diff --git a/public/app/features/dashboard/row/row_model.ts b/public/app/features/dashboard/row/row_model.ts index 9e443feec10..d99e75b3621 100644 --- a/public/app/features/dashboard/row/row_model.ts +++ b/public/app/features/dashboard/row/row_model.ts @@ -33,11 +33,11 @@ export class DashboardRow { } getSaveModel() { + this.model = {}; assignModelProperties(this.model, this, this.defaults); // remove properties that dont server persisted purpose delete this.model.isNew; - return this.model; } diff --git a/public/app/features/dashboard/specs/dynamic_dashboard_srv_specs.ts b/public/app/features/dashboard/specs/dynamic_dashboard_srv_specs.ts index 95de65b2459..93feddc0654 100644 --- a/public/app/features/dashboard/specs/dynamic_dashboard_srv_specs.ts +++ b/public/app/features/dashboard/specs/dynamic_dashboard_srv_specs.ts @@ -20,7 +20,6 @@ function dynamicDashScenario(desc, func) { beforeEach(angularMocks.inject(function(dashboardSrv) { ctx.dashboardSrv = dashboardSrv; - ctx.variableSrv = {}; var model = { rows: [], @@ -29,9 +28,8 @@ function dynamicDashScenario(desc, func) { setupFunc(model); ctx.dash = ctx.dashboardSrv.create(model); - ctx.variableSrv.variables = ctx.dash.templating.list; ctx.dynamicDashboardSrv = new DynamicDashboardSrv(); - ctx.dynamicDashboardSrv.init(ctx.dash, ctx.variableSrv); + ctx.dynamicDashboardSrv.init(ctx.dash); ctx.dynamicDashboardSrv.process(); ctx.rows = ctx.dash.rows; })); diff --git a/public/app/features/dashboard/specs/exporter_specs.ts b/public/app/features/dashboard/specs/exporter_specs.ts index fd3973206d1..0aaadae2b63 100644 --- a/public/app/features/dashboard/specs/exporter_specs.ts +++ b/public/app/features/dashboard/specs/exporter_specs.ts @@ -34,6 +34,14 @@ describe('given dashboard with repeated panels', function() { options: [] }); + dash.templating.list.push({ + name: 'ds', + type: 'datasource', + query: 'testdb', + current: {value: 'prod', text: 'prod'}, + options: [] + }); + dash.annotations.list.push({ name: 'logs', datasource: 'gfdb', @@ -49,6 +57,7 @@ describe('given dashboard with repeated panels', function() { datasource: '-- Mixed --', targets: [{datasource: 'other'}], }, + {id: 5, datasource: '$ds'}, ] }); @@ -87,7 +96,7 @@ describe('given dashboard with repeated panels', function() { }); it('exported dashboard should not contain repeated panels', function() { - expect(exported.rows[0].panels.length).to.be(2); + expect(exported.rows[0].panels.length).to.be(3); }); it('exported dashboard should not contain repeated rows', function() { diff --git a/public/app/features/templating/datasource_variable.ts b/public/app/features/templating/datasource_variable.ts index 17ba3522d00..bfd4d965029 100644 --- a/public/app/features/templating/datasource_variable.ts +++ b/public/app/features/templating/datasource_variable.ts @@ -34,10 +34,7 @@ export class DatasourceVariable implements Variable { assignModelProperties(this.model, this, this.defaults); // dont persist options - if (this.refresh !== 0) { - this.model.options = []; - } - + this.model.options = []; return this.model; } diff --git a/public/app/features/templating/specs/query_variable_specs.ts b/public/app/features/templating/specs/query_variable_specs.ts index 7f695081ba0..591362e0d84 100644 --- a/public/app/features/templating/specs/query_variable_specs.ts +++ b/public/app/features/templating/specs/query_variable_specs.ts @@ -41,7 +41,6 @@ describe('QueryVariable', function() { var model = variable.getSaveModel(); expect(model.options.length).to.be(0); }); - }); });