feat(ui): support for editing pins and description (#1748)

https://user-images.githubusercontent.com/249880/152926565-2d3186d2-7989-4482-bcbc-9b20659745dd.mp4
This commit is contained in:
Jarek Kowalski
2022-02-11 20:34:15 -08:00
committed by GitHub
parent 6ae46a4d19
commit 512dcebaf4
6 changed files with 138 additions and 1 deletions

2
go.mod
View File

@@ -88,7 +88,7 @@ require (
github.com/googleapis/gax-go/v2 v2.1.1 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/kopia/htmluibuild v0.0.0-20220211164311-3c58cff936c2
github.com/kopia/htmluibuild v0.0.0-20220212024129-c944ece3b4d0
github.com/kr/fs v0.1.0 // indirect
github.com/mattn/go-ieproxy v0.0.1 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect

4
go.sum
View File

@@ -406,6 +406,10 @@ github.com/kopia/htmluibuild v0.0.0-20220208062920-a1d47d1e3096 h1:rbrPWsO4OTT2c
github.com/kopia/htmluibuild v0.0.0-20220208062920-a1d47d1e3096/go.mod h1:eWer4rx9P8lJo2eKc+Q7AZ1dE1x1hJNdkbDFPzMu1Hw=
github.com/kopia/htmluibuild v0.0.0-20220211164311-3c58cff936c2 h1:8EzPQNPLdwH3camd8l6/gHpSRS43cKqd38FKebcXbrE=
github.com/kopia/htmluibuild v0.0.0-20220211164311-3c58cff936c2/go.mod h1:eWer4rx9P8lJo2eKc+Q7AZ1dE1x1hJNdkbDFPzMu1Hw=
github.com/kopia/htmluibuild v0.0.0-20220212022637-f4a448ee180a h1:FugmgYM3pW92AjfMJsGZArtF4TROg5rDKLalXXk/50E=
github.com/kopia/htmluibuild v0.0.0-20220212022637-f4a448ee180a/go.mod h1:eWer4rx9P8lJo2eKc+Q7AZ1dE1x1hJNdkbDFPzMu1Hw=
github.com/kopia/htmluibuild v0.0.0-20220212024129-c944ece3b4d0 h1:jdyUUH53CP2G30w4endw1eXE3GmI4km2hmK1ZZ75fIg=
github.com/kopia/htmluibuild v0.0.0-20220212024129-c944ece3b4d0/go.mod h1:eWer4rx9P8lJo2eKc+Q7AZ1dE1x1hJNdkbDFPzMu1Hw=
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=

View File

@@ -128,6 +128,52 @@ func (s *Server) handleDeleteSnapshots(ctx context.Context, r *http.Request, bod
return &serverapi.Empty{}, nil
}
func (s *Server) handleEditSnapshots(ctx context.Context, r *http.Request, body []byte) (interface{}, *apiError) {
var req serverapi.EditSnapshotsRequest
if err := json.Unmarshal(body, &req); err != nil {
return nil, requestError(serverapi.ErrorMalformedRequest, "malformed request")
}
var snaps []*serverapi.Snapshot
if err := repo.WriteSession(ctx, s.rep, repo.WriteSessionOptions{
Purpose: "EditSnapshots",
}, func(ctx context.Context, w repo.RepositoryWriter) error {
for _, id := range req.Snapshots {
snap, err := snapshot.LoadSnapshot(ctx, w, id)
if err != nil {
return errors.Wrap(err, "unable to load snapshot")
}
changed := false
if snap.UpdatePins(req.AddPins, req.RemovePins) {
changed = true
}
if req.NewDescription != nil {
changed = true
snap.Description = *req.NewDescription
}
if changed {
if err := snapshot.UpdateSnapshot(ctx, w, snap); err != nil {
return errors.Wrap(err, "error updating snapshot")
}
}
snaps = append(snaps, convertSnapshotManifest(snap))
}
return nil
}); err != nil {
return nil, internalServerError(err)
}
return snaps, nil
}
func uniqueSnapshots(rows []*serverapi.Snapshot) []*serverapi.Snapshot {
result := []*serverapi.Snapshot{}
resultByRootEntry := map[string]*serverapi.Snapshot{}

View File

@@ -177,3 +177,81 @@ func TestListAndDeleteSnapshots(t *testing.T) {
require.Empty(t, sourceList.Sources)
}
func TestEditSnapshots(t *testing.T) {
ctx, env := repotesting.NewEnvironment(t, repotesting.FormatNotImportant)
si1 := localSource(env, "/dummy/path")
var id11 manifest.ID
require.NoError(t, repo.WriteSession(ctx, env.Repository, repo.WriteSessionOptions{Purpose: "Test"}, func(ctx context.Context, w repo.RepositoryWriter) error {
u := snapshotfs.NewUploader(w)
dir1 := mockfs.NewDirectory()
dir1.AddFile("file1", []byte{1, 2, 3}, 0o644)
dir1.AddFile("file2", []byte{1, 2, 4}, 0o644)
man11, err := u.Upload(ctx, dir1, nil, si1)
require.NoError(t, err)
id11, err = snapshot.SaveSnapshot(ctx, w, man11)
require.NoError(t, err)
return nil
}))
srvInfo := startServer(t, env, false)
cli, err := apiclient.NewKopiaAPIClient(apiclient.Options{
BaseURL: srvInfo.BaseURL,
TrustedServerCertificateFingerprint: srvInfo.TrustedServerCertificateFingerprint,
Username: testUIUsername,
Password: testUIPassword,
})
require.NoError(t, err)
require.NoError(t, cli.FetchCSRFTokenForTesting(ctx))
resp, err := serverapi.ListSnapshots(ctx, cli, si1, true)
require.NoError(t, err)
require.Len(t, resp.Snapshots, 1)
var (
updated []*serverapi.Snapshot
newDesc1 = "desc1"
newDesc2 = "desc2"
)
require.NoError(t, cli.Post(ctx, "snapshots/edit", &serverapi.EditSnapshotsRequest{
Snapshots: []manifest.ID{id11},
AddPins: []string{"pin1", "pin2"},
NewDescription: &newDesc1,
}, &updated))
require.Len(t, updated, 1)
require.EqualValues(t, []string{"pin1", "pin2"}, updated[0].Pins)
require.EqualValues(t, newDesc1, updated[0].Description)
require.NoError(t, cli.Post(ctx, "snapshots/edit", &serverapi.EditSnapshotsRequest{
Snapshots: []manifest.ID{updated[0].ID},
AddPins: []string{"pin3"},
RemovePins: []string{"pin1"},
NewDescription: &newDesc2,
}, &updated))
require.Len(t, updated, 1)
require.EqualValues(t, []string{"pin2", "pin3"}, updated[0].Pins)
require.EqualValues(t, newDesc2, updated[0].Description)
require.NoError(t, cli.Post(ctx, "snapshots/edit", &serverapi.EditSnapshotsRequest{
Snapshots: []manifest.ID{updated[0].ID},
RemovePins: []string{"pin3"},
}, &updated))
require.Len(t, updated, 1)
require.EqualValues(t, []string{"pin2"}, updated[0].Pins)
require.EqualValues(t, newDesc2, updated[0].Description)
}

View File

@@ -96,6 +96,7 @@ func (s *Server) SetupHTMLUIAPIHandlers(m *mux.Router) {
// snapshots
m.HandleFunc("/api/v1/snapshots", s.handleUI(s.handleListSnapshots)).Methods(http.MethodGet)
m.HandleFunc("/api/v1/snapshots/delete", s.handleUI(s.handleDeleteSnapshots)).Methods(http.MethodPost)
m.HandleFunc("/api/v1/snapshots/edit", s.handleUI(s.handleEditSnapshots)).Methods(http.MethodPost)
m.HandleFunc("/api/v1/policy", s.handleUI(s.handlePolicyGet)).Methods(http.MethodGet)
m.HandleFunc("/api/v1/policy", s.handleUI(s.handlePolicyPut)).Methods(http.MethodPut)
m.HandleFunc("/api/v1/policy", s.handleUI(s.handlePolicyDelete)).Methods(http.MethodDelete)

View File

@@ -178,6 +178,14 @@ type DeleteSnapshotsRequest struct {
DeleteSourceAndPolicy bool `json:"deleteSourceAndPolicy"`
}
// EditSnapshotsRequest contains request to edit one or more snapshots.
type EditSnapshotsRequest struct {
Snapshots []manifest.ID `json:"snapshots"`
NewDescription *string `json:"description"`
AddPins []string `json:"addPins"`
RemovePins []string `json:"removePins"`
}
// MountSnapshotRequest contains request to mount a snapshot.
type MountSnapshotRequest struct {
Root string `json:"root"`