diff --git a/pkg/api/api.go b/pkg/api/api.go index 636d61e14d3..c3a3728338d 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -226,11 +226,10 @@ func (hs *HttpServer) registerRoutes() { r.Get("/id/:dashboardId/versions", wrap(GetDashboardVersions)) r.Get("/id/:dashboardId/versions/:id", wrap(GetDashboardVersion)) - r.Get("/id/:dashboardId/compare/:versions", wrap(CompareDashboardVersions)) - r.Get("/id/:dashboardId/compare/:versions/html", wrap(CompareDashboardVersionsJSON)) - r.Get("/id/:dashboardId/compare/:versions/basic", wrap(CompareDashboardVersionsBasic)) r.Post("/id/:dashboardId/restore", reqEditorRole, bind(dtos.RestoreDashboardVersionCommand{}), wrap(RestoreDashboardVersion)) + r.Post("/calculate-diff", bind(dtos.CalculateDiffOptions{}), wrap(CalculateDashboardDiff)) + r.Post("/db", reqEditorRole, bind(m.SaveDashboardCommand{}), wrap(PostDashboard)) r.Get("/file/:file", GetDashboardFromJsonFile) r.Get("/home", wrap(GetHomeDashboard)) diff --git a/pkg/api/dashboard.go b/pkg/api/dashboard.go index 4b4f6fe8597..129faa3d01e 100644 --- a/pkg/api/dashboard.go +++ b/pkg/api/dashboard.go @@ -2,11 +2,9 @@ package api import ( "encoding/json" - "errors" "fmt" "os" "path" - "strconv" "strings" "github.com/grafana/grafana/pkg/api/dtos" @@ -328,101 +326,34 @@ func GetDashboardVersion(c *middleware.Context) Response { return Json(200, dashVersionMeta) } -func getDashboardVersionDiffOptions(c *middleware.Context, diffType dashdiffs.DiffType) (*dashdiffs.Options, error) { +// POST /api/dashboards/calculate-diff performs diffs on two dashboards +func CalculateDashboardDiff(c *middleware.Context, apiOptions dtos.CalculateDiffOptions) Response { - dashId := c.ParamsInt64(":dashboardId") - if dashId == 0 { - return nil, errors.New("Missing dashboardId") + options := dashdiffs.Options{ + OrgId: c.OrgId, + DiffType: dashdiffs.ParseDiffType(apiOptions.DiffType), + Base: dashdiffs.DiffTarget{ + DashboardId: apiOptions.Base.DashboardId, + Version: apiOptions.Base.Version, + UnsavedDashboard: apiOptions.Base.UnsavedDashboard, + }, + New: dashdiffs.DiffTarget{ + DashboardId: apiOptions.New.DashboardId, + Version: apiOptions.New.Version, + UnsavedDashboard: apiOptions.New.UnsavedDashboard, + }, } - versionStrings := strings.Split(c.Params(":versions"), "...") - if len(versionStrings) != 2 { - return nil, fmt.Errorf("bad format: urls should be in the format /versions/0...1") - } - - BaseVersion, err := strconv.Atoi(versionStrings[0]) - if err != nil { - return nil, fmt.Errorf("bad format: first argument is not of type int") - } - - newVersion, err := strconv.Atoi(versionStrings[1]) - if err != nil { - return nil, fmt.Errorf("bad format: second argument is not of type int") - } - - options := &dashdiffs.Options{} - options.DashboardId = dashId - options.OrgId = c.OrgId - options.BaseVersion = BaseVersion - options.NewVersion = newVersion - options.DiffType = diffType - - return options, nil -} - -// CompareDashboardVersions compares dashboards the way the GitHub API does. -func CompareDashboardVersions(c *middleware.Context) Response { - options, err := getDashboardVersionDiffOptions(c, dashdiffs.DiffDelta) - - if err != nil { - return ApiError(500, err.Error(), err) - } - - result, err := dashdiffs.GetVersionDiff(options) + result, err := dashdiffs.CalculateDiff(&options) if err != nil { return ApiError(500, "Unable to compute diff", err) } - // here the output is already JSON, so we need to unmarshal it into a - // map before marshaling the entire response - - deltaMap := make(map[string]interface{}) - err = json.Unmarshal(result.Delta, &deltaMap) - if err != nil { - return ApiError(500, err.Error(), err) + if options.DiffType == dashdiffs.DiffDelta { + return Respond(200, result.Delta).Header("Content-Type", "application/json") + } else { + return Respond(200, result.Delta).Header("Content-Type", "text/html") } - - return Json(200, util.DynMap{ - "meta": util.DynMap{ - "baseVersion": options.BaseVersion, - "newVersion": options.NewVersion, - }, - "delta": deltaMap, - }) -} - -// CompareDashboardVersionsJSON compares dashboards the way the GitHub API does, -// returning a human-readable JSON diff. -func CompareDashboardVersionsJSON(c *middleware.Context) Response { - options, err := getDashboardVersionDiffOptions(c, dashdiffs.DiffJSON) - - if err != nil { - return ApiError(500, err.Error(), err) - } - - result, err := dashdiffs.GetVersionDiff(options) - if err != nil { - return ApiError(500, err.Error(), err) - } - - return Respond(200, result.Delta).Header("Content-Type", "text/html") -} - -// CompareDashboardVersionsBasic compares dashboards the way the GitHub API does, -// returning a human-readable diff. -func CompareDashboardVersionsBasic(c *middleware.Context) Response { - options, err := getDashboardVersionDiffOptions(c, dashdiffs.DiffBasic) - - if err != nil { - return ApiError(500, err.Error(), err) - } - - result, err := dashdiffs.GetVersionDiff(options) - if err != nil { - return ApiError(500, err.Error(), err) - } - - return Respond(200, result.Delta).Header("Content-Type", "text/html") } // RestoreDashboardVersion restores a dashboard to the given version. diff --git a/pkg/api/dtos/dashboard.go b/pkg/api/dtos/dashboard.go index fd8a7115de2..6a11199bb1c 100644 --- a/pkg/api/dtos/dashboard.go +++ b/pkg/api/dtos/dashboard.go @@ -32,6 +32,18 @@ type DashboardRedirect struct { RedirectUri string `json:"redirectUri"` } +type CalculateDiffOptions struct { + Base CalculateDiffTarget `json:"base" binding:"Required"` + New CalculateDiffTarget `json:"new" binding:"Required"` + DiffType string `json:"DiffType" binding:"Required"` +} + +type CalculateDiffTarget struct { + DashboardId int64 `json:"dashboardId"` + Version int `json:"version"` + UnsavedDashboard *simplejson.Json `json:"unsavedDashboard"` +} + type RestoreDashboardVersionCommand struct { Version int `json:"version" binding:"Required"` } diff --git a/pkg/components/dashdiffs/compare.go b/pkg/components/dashdiffs/compare.go index a559c5b16f9..51c8a947ce1 100644 --- a/pkg/components/dashdiffs/compare.go +++ b/pkg/components/dashdiffs/compare.go @@ -11,14 +11,6 @@ import ( deltaFormatter "github.com/yudai/gojsondiff/formatter" ) -type DiffType int - -const ( - DiffJSON DiffType = iota - DiffBasic - DiffDelta -) - var ( // ErrUnsupportedDiffType occurs when an invalid diff type is used. ErrUnsupportedDiffType = errors.New("dashdiff: unsupported diff type") @@ -27,24 +19,49 @@ var ( ErrNilDiff = errors.New("dashdiff: diff is nil") ) +type DiffType int + +const ( + DiffJSON DiffType = iota + DiffBasic + DiffDelta +) + type Options struct { - OrgId int64 - DashboardId int64 - BaseVersion int - NewVersion int - DiffType DiffType + OrgId int64 + Base DiffTarget + New DiffTarget + DiffType DiffType +} + +type DiffTarget struct { + DashboardId int64 + Version int + UnsavedDashboard *simplejson.Json } type Result struct { Delta []byte `json:"delta"` } +func ParseDiffType(diff string) DiffType { + switch diff { + case "json": + return DiffJSON + case "basic": + return DiffBasic + case "delta": + return DiffDelta + } + return DiffBasic +} + // CompareDashboardVersionsCommand computes the JSON diff of two versions, // assigning the delta of the diff to the `Delta` field. -func GetVersionDiff(options *Options) (*Result, error) { +func CalculateDiff(options *Options) (*Result, error) { baseVersionQuery := models.GetDashboardVersionQuery{ - DashboardId: options.DashboardId, - Version: options.BaseVersion, + DashboardId: options.Base.DashboardId, + Version: options.Base.Version, } if err := bus.Dispatch(&baseVersionQuery); err != nil { @@ -52,8 +69,8 @@ func GetVersionDiff(options *Options) (*Result, error) { } newVersionQuery := models.GetDashboardVersionQuery{ - DashboardId: options.DashboardId, - Version: options.NewVersion, + DashboardId: options.New.DashboardId, + Version: options.New.Version, } if err := bus.Dispatch(&newVersionQuery); err != nil { diff --git a/public/app/features/dashboard/history/history.html b/public/app/features/dashboard/history/history.html index 53d960fd620..ab64b48d342 100644 --- a/public/app/features/dashboard/history/history.html +++ b/public/app/features/dashboard/history/history.html @@ -5,16 +5,13 @@
- Version {{new}} updated by - {{ctrl.getMeta(new, 'createdBy')}} - {{ctrl.formatBasicDate(ctrl.getMeta(new, 'created'))}} - - {{ctrl.getMeta(new, 'message')}} + Version {{ctrl.newInfo.version}} updated by + {{ctrl.newInfo.createdBy}} + {{ctrl.newInfo.ageString}} + - {{ctrl.newInfo.message}}
- Version {{original}} updated by - {{ctrl.getMeta(original, 'createdBy')}} - {{ctrl.formatBasicDate(ctrl.getMeta(original, 'created'))}} - - {{ctrl.getMeta(original, 'message')}} + Version {{ctrl.baseInfo.version}} updated by + {{ctrl.baseInfo.createdBy}} + {{ctrl.baseInfo.ageString}} + - {{ctrl.baseInfo.message}}
');
+ expect(ctx.ctrl.delta.json).to.be('');
});
it('should set the json diff view as active', function() {
expect(ctx.ctrl.mode).to.be('compare');
- expect(ctx.ctrl.diff).to.be('html');
+ expect(ctx.ctrl.diff).to.be('json');
});
it('should indicate loading has finished', function() {
@@ -232,14 +229,15 @@ describe('HistoryListCtrl', function() {
describe('and diffs have already been fetched', function() {
beforeEach(function() {
deferred.resolve(compare('basic'));
- ctx.ctrl.selected = [3, 1];
+ ctx.ctrl.revisions[3].checked = true;
+ ctx.ctrl.revisions[1].checked = true;
ctx.ctrl.delta.basic = 'cached basic';
ctx.ctrl.getDiff('basic');
ctx.ctrl.$scope.$apply();
});
it('should use the cached diffs instead of fetching', function() {
- expect(historySrv.compareVersions.calledOnce).to.be(false);
+ expect(historySrv.calculateDiff.calledOnce).to.be(false);
expect(ctx.ctrl.delta.basic).to.be('cached basic');
});
@@ -251,13 +249,14 @@ describe('HistoryListCtrl', function() {
describe('and fetching the diff fails', function() {
beforeEach(function() {
deferred.reject(new Error('DiffError'));
- ctx.ctrl.selected = [4, 2];
+ ctx.ctrl.revisions[3].checked = true;
+ ctx.ctrl.revisions[1].checked = true;
ctx.ctrl.getDiff('basic');
ctx.ctrl.$scope.$apply();
});
it('should fetch the diff if two valid versions are selected', function() {
- expect(historySrv.compareVersions.calledOnce).to.be(true);
+ expect(historySrv.calculateDiff.calledOnce).to.be(true);
});
it('should return to the history list view', function() {
@@ -269,7 +268,7 @@ describe('HistoryListCtrl', function() {
});
it('should have an empty delta/changeset', function() {
- expect(ctx.ctrl.delta).to.eql({ basic: '', html: '' });
+ expect(ctx.ctrl.delta).to.eql({ basic: '', json: '' });
});
});
});
diff --git a/public/app/features/dashboard/specs/history_srv_specs.ts b/public/app/features/dashboard/specs/history_srv_specs.ts
index 2d01f323d35..4678759c438 100644
--- a/public/app/features/dashboard/specs/history_srv_specs.ts
+++ b/public/app/features/dashboard/specs/history_srv_specs.ts
@@ -8,7 +8,6 @@ describe('historySrv', function() {
var ctx = new helpers.ServiceTestContext();
var versionsResponse = versions();
- var compareResponse = compare();
var restoreResponse = restore;
beforeEach(angularMocks.module('grafana.core'));
@@ -16,7 +15,6 @@ describe('historySrv', function() {
beforeEach(angularMocks.inject(function($httpBackend) {
ctx.$httpBackend = $httpBackend;
$httpBackend.whenRoute('GET', 'api/dashboards/id/:id/versions').respond(versionsResponse);
- $httpBackend.whenRoute('GET', 'api/dashboards/id/:id/compare/:original...:new').respond(compareResponse);
$httpBackend.whenRoute('POST', 'api/dashboards/id/:id/restore')
.respond(function(method, url, data, headers, params) {
const parsedData = JSON.parse(data);
@@ -51,26 +49,6 @@ describe('historySrv', function() {
});
});
- describe('compareVersions', function() {
- it('should return a diff object for the given dashboard revisions', function(done) {
- var compare = { original: 6, new: 4 };
- ctx.service.compareVersions({ id: 1 }, compare).then(function(response) {
- expect(response).to.eql(compareResponse);
- done();
- });
- ctx.$httpBackend.flush();
- });
-
- it('should return an empty object when not given an id', function(done) {
- var compare = { original: 6, new: 4 };
- ctx.service.compareVersions({ }, compare).then(function(response) {
- expect(response).to.eql({});
- done();
- });
- ctx.$httpBackend.flush();
- });
- });
-
describe('restoreDashboard', function() {
it('should return a success response given valid parameters', function(done) {
var version = 6;