Skip to content

Commit eba76ea

Browse files
committed
feat(ChangeRequest): add support for change requests to query repositories
In order go give users the capacity to either reject or allow a change based on shifts to a specified url, we need the concept of a "request" that can be either accepted or rejected. Accepting a request means adding the change to a requested history. Rejecting it should mark the change rejected in some way.
1 parent f9a3938 commit eba76ea

File tree

9 files changed

+348
-26
lines changed

9 files changed

+348
-26
lines changed

api/handlers/datasets.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,6 @@ func (h *DatasetHandlers) deleteDatasetHandler(w http.ResponseWriter, r *http.Re
221221

222222
func (h *DatasetHandlers) getStructuredDataHandler(w http.ResponseWriter, r *http.Request) {
223223
listParams := core.ListParamsFromRequest(r)
224-
page := listParams.Page()
225224
all, err := util.ReqParamBool("all", r)
226225
if err != nil {
227226
all = false

repo/change_requests.go

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
package repo
2+
3+
import (
4+
"time"
5+
6+
"github.com/ipfs/go-datastore"
7+
"github.com/qri-io/dataset"
8+
)
9+
10+
// ChangeRequestStore is the interface for storying & manipulating Change Requests
11+
// Qri repos should embed a ChangeRequestStore
12+
type ChangeRequestStore interface {
13+
// Put a change request into the store
14+
PutChangeRequest(path datastore.Key, cr *ChangeRequest) error
15+
// Get a change request by it's path
16+
GetChangeRequest(path datastore.Key) (*ChangeRequest, error)
17+
// accept an open change request
18+
// AcceptChangeRequest(path datastore.Key) error
19+
// decline an open change request
20+
// DeclineChangeRequest(path datastore.Key) error
21+
// get change requests for a given target
22+
ChangeRequestsForTarget(target datastore.Key, limit, offset int) ([]*ChangeRequest, error)
23+
// list change requests in this store
24+
ListChangeRequests(limit, offset int) ([]*ChangeRequest, error)
25+
}
26+
27+
const (
28+
// open change requests haven't been addressed yet
29+
ChangeRequestStatusOpen = "open"
30+
// accepted change requests have been merged into
31+
// the target history
32+
ChangeRequestStatusAccepted = "accepted"
33+
// declined change requests will not be merged into
34+
// the target history
35+
ChangeRequestStatusDeclined = "declined"
36+
)
37+
38+
// ChangeRequests are proposed additions to the history of a given dataset
39+
type ChangeRequest struct {
40+
// status of this request. one of: open,accepted,declined
41+
Status string `json:"status"`
42+
// created marks the time this change request was created
43+
Created time.Time `json:"created"`
44+
// the dataset this change is aimed at. The
45+
// history of target must match the history
46+
// of change up until new history entries
47+
// TODO - should changes be targeting a mutable history?
48+
Target datastore.Key `json:"target"`
49+
// path to HEAD of the change history
50+
Path datastore.Key `json:"path"`
51+
// The actual change history. All relevant details must be stored
52+
// in the dataset itself. Title & description of the change goes
53+
// into this dataset's commit.
54+
Change *dataset.Dataset `json:"change"`
55+
}
56+
57+
// accept an open change request, advancing the name of the dataset
58+
// that refer to the target path to the newly-added history
59+
func AcceptChangeRequest(r Repo, path datastore.Key) (err error) {
60+
cr, err := r.GetChangeRequest(path)
61+
if err != nil {
62+
return err
63+
}
64+
65+
cr.Status = ChangeRequestStatusAccepted
66+
if err := r.PutChangeRequest(path, cr); err != nil {
67+
return err
68+
}
69+
70+
// TODO - place all datasets related to this history chain in the store
71+
ds := &dataset.Dataset{Previous: path}
72+
for {
73+
if ds.Previous.Equal(cr.Target) {
74+
break
75+
}
76+
// datasets can sometimes resolve over the netowork, so this get / put
77+
// combination is required
78+
ds, err = r.GetDataset(ds.Previous)
79+
if err != nil {
80+
return
81+
}
82+
83+
if err = r.PutDataset(ds.Previous, ds); err != nil {
84+
return
85+
}
86+
}
87+
88+
name, err := r.GetName(cr.Target)
89+
if err != nil {
90+
return err
91+
}
92+
93+
if err := r.PutName(name, cr.Path); err != nil {
94+
return err
95+
}
96+
97+
return nil
98+
}
99+
100+
// decline an open change request
101+
func DeclineChangeRequest(r Repo, path datastore.Key) error {
102+
cr, err := r.GetChangeRequest(path)
103+
if err != nil {
104+
return err
105+
}
106+
cr.Status = ChangeRequestStatusDeclined
107+
return r.PutChangeRequest(path, cr)
108+
}

repo/fs/change_requests.go

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
package fs_repo
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"io/ioutil"
7+
"os"
8+
9+
"github.com/ipfs/go-datastore"
10+
"github.com/qri-io/qri/repo"
11+
)
12+
13+
type ChangeRequests struct {
14+
basepath
15+
file File
16+
}
17+
18+
func NewChangeRequests(base string, file File) ChangeRequests {
19+
return ChangeRequests{basepath: basepath(base), file: file}
20+
}
21+
22+
// Put a change request into the store
23+
func (r ChangeRequests) PutChangeRequest(path datastore.Key, cr *repo.ChangeRequest) error {
24+
crs, err := r.changeRequests()
25+
if err != nil {
26+
return err
27+
}
28+
crs[path.String()] = cr
29+
return r.saveFile(cr, r.file)
30+
}
31+
32+
func (r ChangeRequests) DeleteChangeRequest(path datastore.Key) error {
33+
cr, err := r.changeRequests()
34+
if err != nil {
35+
return err
36+
}
37+
delete(cr, path.String())
38+
return r.saveFile(cr, r.file)
39+
}
40+
41+
// Get a change request by it's path
42+
func (r ChangeRequests) GetChangeRequest(path datastore.Key) (*repo.ChangeRequest, error) {
43+
crs, err := r.changeRequests()
44+
if err != nil {
45+
return nil, err
46+
}
47+
48+
cr := crs[path.String()]
49+
if cr == nil {
50+
return nil, datastore.ErrNotFound
51+
}
52+
return cr, nil
53+
}
54+
55+
// get change requests for a given target
56+
func (r ChangeRequests) ChangeRequestsForTarget(target datastore.Key, limit, offset int) ([]*repo.ChangeRequest, error) {
57+
crs, err := r.changeRequests()
58+
if err != nil {
59+
return nil, err
60+
}
61+
62+
results := []*repo.ChangeRequest{}
63+
skipped := 0
64+
for _, cr := range crs {
65+
if cr.Target == target {
66+
if skipped < offset {
67+
skipped++
68+
continue
69+
}
70+
results = append(results, cr)
71+
}
72+
if len(results) >= limit {
73+
break
74+
}
75+
}
76+
77+
return results, nil
78+
}
79+
80+
// list change requests in this store
81+
func (r ChangeRequests) ListChangeRequests(limit, offset int) ([]*repo.ChangeRequest, error) {
82+
crs, err := r.changeRequests()
83+
if err != nil {
84+
return nil, err
85+
}
86+
87+
if limit == -1 && len(crs) <= 0 {
88+
// default to limit of 100 entries
89+
limit = 100
90+
} else if limit == -1 {
91+
limit = len(crs)
92+
}
93+
94+
i := 0
95+
added := 0
96+
res := make([]*repo.ChangeRequest, limit)
97+
for _, cr := range crs {
98+
if i < offset {
99+
continue
100+
}
101+
102+
if limit > 0 && added < limit {
103+
res[i] = cr
104+
added++
105+
} else if added == limit {
106+
break
107+
}
108+
109+
i++
110+
}
111+
return res[:added], nil
112+
}
113+
114+
func (r ChangeRequests) changeRequests() (map[string]*repo.ChangeRequest, error) {
115+
ds := map[string]*repo.ChangeRequest{}
116+
data, err := ioutil.ReadFile(r.filepath(r.file))
117+
if err != nil {
118+
if os.IsNotExist(err) {
119+
return ds, nil
120+
}
121+
return ds, fmt.Errorf("error loading changeRequests: %s", err.Error())
122+
}
123+
124+
if err := json.Unmarshal(data, &ds); err != nil {
125+
return ds, fmt.Errorf("error unmarshaling changeRequests: %s", err.Error())
126+
}
127+
return ds, nil
128+
}

repo/fs/datasets.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@ package fs_repo
33
import (
44
"encoding/json"
55
"fmt"
6-
"github.com/qri-io/dataset/dsfs"
76
"io/ioutil"
87
"os"
98

109
"github.com/ipfs/go-datastore"
1110
"github.com/ipfs/go-datastore/query"
1211
"github.com/qri-io/cafs"
1312
"github.com/qri-io/dataset"
13+
"github.com/qri-io/dataset/dsfs"
1414
"github.com/qri-io/qri/repo"
1515
)
1616

repo/fs/files.go

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -56,21 +56,24 @@ const (
5656
FileAnalytics
5757
// SearchIndex
5858
FileSearchIndex
59+
// ChangeRequests
60+
FileChangeRequests
5961
)
6062

6163
var paths = map[File]string{
62-
FileUnknown: "",
63-
FileLockfile: "/repo.lock",
64-
FileInfo: "/info.json",
65-
FileProfile: "/profile.json",
66-
FileConfig: "/config.json",
67-
FileDatasets: "/datasets.json",
68-
FileQueryLogs: "/queries.json",
69-
FileNamestore: "/namespace.json",
70-
FilePeers: "/peers.json",
71-
FileCache: "/cache.json",
72-
FileAnalytics: "/analytics.json",
73-
FileSearchIndex: "/index.bleve",
64+
FileUnknown: "",
65+
FileLockfile: "/repo.lock",
66+
FileInfo: "/info.json",
67+
FileProfile: "/profile.json",
68+
FileConfig: "/config.json",
69+
FileDatasets: "/datasets.json",
70+
FileQueryLogs: "/queries.json",
71+
FileNamestore: "/namespace.json",
72+
FilePeers: "/peers.json",
73+
FileCache: "/cache.json",
74+
FileAnalytics: "/analytics.json",
75+
FileSearchIndex: "/index.bleve",
76+
FileChangeRequests: "/change_requests.json",
7477
}
7578

7679
// Filepath gives the relative filepath to a repofile

repo/fs/fs.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ type Repo struct {
2020
Datasets
2121
Namestore
2222
QueryLog
23+
ChangeRequests
2324

2425
analytics Analytics
2526
peers PeerStore
@@ -42,10 +43,13 @@ func NewRepo(store cafs.Filestore, base, id string) (repo.Repo, error) {
4243
}
4344

4445
return &Repo{
45-
basepath: bp,
46-
Datasets: NewDatasets(base, FileDatasets, store),
47-
Namestore: Namestore{bp, index, store},
48-
QueryLog: NewQueryLog(base, FileQueryLogs, store),
46+
basepath: bp,
47+
48+
Datasets: NewDatasets(base, FileDatasets, store),
49+
Namestore: Namestore{bp, index, store},
50+
QueryLog: NewQueryLog(base, FileQueryLogs, store),
51+
ChangeRequests: NewChangeRequests(base, FileChangeRequests),
52+
4953
analytics: NewAnalytics(base),
5054
peers: PeerStore{bp},
5155
cache: NewDatasets(base, FileCache, nil),

0 commit comments

Comments
 (0)