diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000000..cb3dd73576 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,7 @@ +language: go +go: +- 1.12.x +env: +- GO111MODULE=on # needed even for Go 1.12 +script: +- go test -v ./... diff --git a/internal/weldr/api.go b/internal/weldr/api.go index 472c38ffbc..188bbcd27b 100644 --- a/internal/weldr/api.go +++ b/internal/weldr/api.go @@ -453,13 +453,72 @@ func (api *API) blueprintsDepsolveHandler(writer http.ResponseWriter, request *h }) } -func (api *API) blueprintsDiffHandler(writer http.ResponseWriter, request *http.Request, _ httprouter.Params) { - var reply struct { - Diff []interface{} `json:"diff"` +func (api *API) blueprintsDiffHandler(writer http.ResponseWriter, request *http.Request, params httprouter.Params) { + type pack struct { + Package blueprintPackage `json:"Package"` } - reply.Diff = make([]interface{}, 0) - json.NewEncoder(writer).Encode(reply) + type diff struct { + New *pack `json:"new"` + Old *pack `json:"old"` + } + + type reply struct { + Diffs []diff `json:"diff"` + } + + name := params.ByName("blueprint") + if len(name) == 0 { + statusResponseError(writer, http.StatusNotFound, "no blueprint name given") + return + } + fromCommit := params.ByName("from") + if len(fromCommit) == 0 || fromCommit != "NEWEST" { + statusResponseError(writer, http.StatusNotFound, "invalid from commit ID given") + return + } + toCommit := params.ByName("to") + if len(toCommit) == 0 || toCommit != "WORKSPACE" { + statusResponseError(writer, http.StatusNotFound, "invalid to commit ID given") + return + } + + // Fetch old and new blueprint details from store and return error if not found + var oldBlueprint, newBlueprint blueprint + if !api.store.getBlueprintCommitted(name, &oldBlueprint) || !api.store.getBlueprint(name, &newBlueprint, nil) { + statusResponseError(writer, http.StatusNotFound) + return + } + + newSlice := newBlueprint.Packages + oldMap := make(map[string]blueprintPackage) + diffs := []diff{} + + for _, oldPackage := range oldBlueprint.Packages { + oldMap[oldPackage.Name] = oldPackage + } + + // For each package in new blueprint check if the old one contains it + for _, newPackage := range newSlice { + oldPackage, found := oldMap[newPackage.Name] + // If found remove from old packages map but otherwise create a diff with the added package + if found { + delete(oldMap, oldPackage.Name) + // Create a diff if the versions changed + if oldPackage.Version != newPackage.Version { + diffs = append(diffs, diff{Old: &pack{oldPackage}, New: &pack{newPackage}}) + } + } else { + diffs = append(diffs, diff{Old: nil, New: &pack{newPackage}}) + } + } + + // All packages remaining in the old packages map have been removed in the new blueprint so create a diff + for _, oldPackage := range oldMap { + diffs = append(diffs, diff{Old: &pack{oldPackage}, New: nil}) + } + + json.NewEncoder(writer).Encode(reply{diffs}) } func (api *API) blueprintsNewHandler(writer http.ResponseWriter, request *http.Request, _ httprouter.Params) { diff --git a/internal/weldr/api_test.go b/internal/weldr/api_test.go index 571c8666a5..858892c8a5 100644 --- a/internal/weldr/api_test.go +++ b/internal/weldr/api_test.go @@ -134,4 +134,7 @@ func TestBlueprints(t *testing.T) { testRoute(t, api, "GET", "/api/v0/blueprints/info/test", ``, http.StatusOK, `{"blueprints":[{"name":"test","description":"Test","modules":[],"packages":[{"name":"systemd","version":"123"}],"version":"0"}], "changes":[{"name":"test","changed":true}], "errors":[]}`) + + testRoute(t, api, "GET", "/api/v0/blueprints/diff/test/NEWEST/WORKSPACE", ``, + http.StatusOK, `{"diff":[{"new":{"Package":{"name":"systemd","version":"123"}},"old":null},{"new":null,"old":{"Package":{"name":"httpd","version":"2.4.*"}}}]}`) } diff --git a/internal/weldr/store.go b/internal/weldr/store.go index 9774d98f02..be0a1e0c81 100644 --- a/internal/weldr/store.go +++ b/internal/weldr/store.go @@ -108,6 +108,27 @@ func (s *store) getBlueprint(name string, bp *blueprint, changed *bool) bool { return true } +func (s *store) getBlueprintCommitted(name string, bp *blueprint) bool { + s.mu.RLock() + defer s.mu.RUnlock() + + var ok bool + *bp, ok = s.Blueprints[name] + if !ok { + return false + } + + // cockpit-composer cannot deal with missing "packages" or "modules" + if bp.Packages == nil { + bp.Packages = []blueprintPackage{} + } + if bp.Modules == nil { + bp.Modules = []blueprintPackage{} + } + + return true +} + func (s *store) pushBlueprint(bp blueprint) { s.change(func() { delete(s.Workspace, bp.Name)