Skip to content

Commit b09a2eb

Browse files
committed
fix(prove): Prove command updates local logbook as needed
1 parent 384fdb2 commit b09a2eb

File tree

3 files changed

+102
-8
lines changed

3 files changed

+102
-8
lines changed

lib/registry.go

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@ import (
44
"context"
55
"encoding/base64"
66
"fmt"
7+
"path/filepath"
78

89
"github.com/qri-io/qri/base"
910
"github.com/qri-io/qri/config"
11+
"github.com/qri-io/qri/logbook"
12+
"github.com/qri-io/qri/logbook/oplog"
1013
"github.com/qri-io/qri/profile"
1114
"github.com/qri-io/qri/registry"
1215
)
@@ -58,6 +61,19 @@ func (m RegistryClientMethods) ProveProfileKey(p *RegistryProfile, ok *bool) err
5861

5962
ctx := context.Background()
6063

64+
// Check if the repository has any saved datasets. If so, calling prove is
65+
// not allowed, because doing so would essentially throw away the old profile,
66+
// making those references unreachable. In the future, this can be changed
67+
// such that the old identity is given a different username, and is merged
68+
// into the client's collection.
69+
numRefs, err := m.inst.repo.RefCount()
70+
if err != nil {
71+
return err
72+
}
73+
if numRefs > 0 {
74+
return fmt.Errorf("cannot prove with a non-empty repository")
75+
}
76+
6177
// For signing the outgoing message
6278
// TODO(arqu): this should take the profile PK instead of active PK once multi tenancy is supported
6379
privKey := m.inst.repo.PrivateKey(ctx)
@@ -92,7 +108,33 @@ func (m RegistryClientMethods) ProveProfileKey(p *RegistryProfile, ok *bool) err
92108
} else {
93109
return fmt.Errorf("prove: server response invalid, did not have profileID")
94110
}
95-
// TODO(dustmop): Also get logbook
111+
112+
// If an existing user of this profileID pushed something, get the previous logbook data
113+
// and write it to our repository. This enables push and pull to continue to work
114+
if logbookBin, ok := res["logbookInit"]; ok && len(logbookBin) > 0 {
115+
logbookBytes, err := base64.StdEncoding.DecodeString(logbookBin)
116+
if err != nil {
117+
return err
118+
}
119+
lg := &oplog.Log{}
120+
if err := lg.UnmarshalFlatbufferBytes(logbookBytes); err != nil {
121+
return err
122+
}
123+
err = m.inst.repo.Logbook().ReplaceAll(ctx, lg)
124+
if err != nil {
125+
return err
126+
}
127+
} else {
128+
// Otherwise, nothing was ever pushed. Create new logbook data using the
129+
// profileID we got back.
130+
logbookPath := filepath.Join(m.inst.repoPath, "logbook.qfb")
131+
logbook, err := logbook.NewJournalOverwriteWithProfileID(privKey, p.Username, m.inst.bus,
132+
m.inst.qfs, logbookPath, cfg.Profile.ID)
133+
if err != nil {
134+
return err
135+
}
136+
m.inst.logbook = logbook
137+
}
96138

97139
// Save the modified config
98140
return m.inst.ChangeConfig(cfg)

logbook/logbook.go

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,11 @@ func NewJournal(pk crypto.PrivKey, username string, bus event.Bus, fs qfs.Filesy
133133

134134
if err := book.load(ctx); err != nil {
135135
if err == ErrNotFound {
136-
err = book.initialize(ctx)
136+
keyID, err := profile.KeyIDFromPriv(book.pk)
137+
if err != nil {
138+
return nil, err
139+
}
140+
err = book.initialize(ctx, keyID)
137141
return book, err
138142
}
139143
return nil, err
@@ -145,23 +149,51 @@ func NewJournal(pk crypto.PrivKey, username string, bus event.Bus, fs qfs.Filesy
145149
return book, nil
146150
}
147151

148-
func (book *Book) initialize(ctx context.Context) error {
149-
keyID, err := profile.KeyIDFromPriv(book.pk)
150-
if err != nil {
151-
return err
152+
// NewJournalOverwriteWithProfileID initializes a new logbook using the
153+
// given profileID. Any existing logbook will be overwritten.
154+
func NewJournalOverwriteWithProfileID(pk crypto.PrivKey, username string, bus event.Bus, fs qfs.Filesystem, location, profileID string) (*Book, error) {
155+
ctx := context.Background()
156+
if pk == nil {
157+
return nil, fmt.Errorf("logbook: private key is required")
158+
}
159+
if fs == nil {
160+
return nil, fmt.Errorf("logbook: filesystem is required")
161+
}
162+
if location == "" {
163+
return nil, fmt.Errorf("logbook: location is required")
164+
}
165+
if profileID == "" {
166+
return nil, fmt.Errorf("logbook: profileID is required")
167+
}
168+
if bus == nil {
169+
return nil, fmt.Errorf("logbook: event.Bus is required")
152170
}
153171

172+
book := &Book{
173+
store: &oplog.Journal{},
174+
fs: fs,
175+
pk: pk,
176+
authorName: username,
177+
fsLocation: location,
178+
publisher: bus,
179+
}
180+
181+
err := book.initialize(ctx, profileID)
182+
return book, err
183+
}
184+
185+
func (book *Book) initialize(ctx context.Context, authorID string) error {
154186
// initialize author's log of user actions
155187
userActions := oplog.InitLog(oplog.Op{
156188
Type: oplog.OpTypeInit,
157189
Model: AuthorModel,
158190
Name: book.Username(),
159-
AuthorID: keyID,
191+
AuthorID: authorID,
160192
Timestamp: NewTimestamp(),
161193
})
162194
book.authorID = userActions.ID()
163195

164-
if err = book.store.MergeLog(ctx, userActions); err != nil {
196+
if err := book.store.MergeLog(ctx, userActions); err != nil {
165197
return err
166198
}
167199
if al, ok := book.store.(oplog.AuthorLogstore); ok {
@@ -215,6 +247,16 @@ func (book *Book) DeleteAuthor() error {
215247
return fmt.Errorf("not finished")
216248
}
217249

250+
// ReplaceAll replaces the contents of the logbook with
251+
// the provided log data
252+
func (book *Book) ReplaceAll(ctx context.Context, lg *oplog.Log) error {
253+
err := book.store.ReplaceAll(ctx, lg)
254+
if err != nil {
255+
return err
256+
}
257+
return book.save(ctx)
258+
}
259+
218260
// save writes the book to book.fsLocation
219261
func (book *Book) save(ctx context.Context) (err error) {
220262
if al, ok := book.store.(oplog.AuthorLogstore); ok {

logbook/oplog/log.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ type Logstore interface {
9191
// get all generations of a log, using the given log as an outparam
9292
// Descendants MUST only mutate Logs field of the passed-in log pointer
9393
Descendants(ctx context.Context, l *Log) error
94+
95+
// ReplaceAll replaces the contents of the entire log
96+
ReplaceAll(ctx context.Context, l *Log) error
9497
}
9598

9699
// SparseAncestorsAllDescendantsLogstore is a an extension interface to
@@ -335,6 +338,13 @@ func (j *Journal) Descendants(ctx context.Context, l *Log) error {
335338
return nil
336339
}
337340

341+
// ReplaceAll replaces the entirety of the logs
342+
func (j *Journal) ReplaceAll(ctx context.Context, l *Log) error {
343+
j.logs = []*Log{l}
344+
j.id = l.Ops[0].Hash()
345+
return nil
346+
}
347+
338348
// FlatbufferCipher marshals journal to a flatbuffer and encrypts the book using
339349
// a given private key. This same private key must be retained elsewhere to read
340350
// the flatbuffer later on

0 commit comments

Comments
 (0)