diff --git a/db/attachment.go b/db/attachment.go index c4acd77507..7e16f5ca22 100644 --- a/db/attachment.go +++ b/db/attachment.go @@ -286,7 +286,7 @@ func (db *Database) WriteMultipartDocument(body Body, writer *multipart.Writer, info.contentType, _ = meta["content_type"].(string) info.data, err = decodeAttachment(meta["data"]) if info.data == nil { - base.Warnf(base.KeyAll, "Couldn't decode attachment %q of doc %q: %v", base.UD(name), base.UD(body["_id"]), err) + base.Warnf(base.KeyAll, "Couldn't decode attachment %q of doc %q: %v", base.UD(name), base.UD(body[BodyId]), err) meta["stub"] = true delete(meta, "data") } else if len(info.data) > kMaxInlineAttachmentSize { @@ -317,8 +317,8 @@ func (db *Database) WriteMultipartDocument(body Body, writer *multipart.Writer, // The revision will be written as a nested multipart body if it has attachments. func (db *Database) WriteRevisionAsPart(revBody Body, isError bool, compressPart bool, writer *multipart.Writer) error { partHeaders := textproto.MIMEHeader{} - docID, _ := revBody["_id"].(string) - revID, _ := revBody["_rev"].(string) + docID, _ := revBody[BodyId].(string) + revID, _ := revBody[BodyRev].(string) if len(docID) > 0 { partHeaders.Set("X-Doc-ID", docID) partHeaders.Set("X-Rev-ID", revID) diff --git a/db/attachment_test.go b/db/attachment_test.go index bc033f80a4..36da3c2267 100644 --- a/db/attachment_test.go +++ b/db/attachment_test.go @@ -66,7 +66,7 @@ func TestAttachments(t *testing.T) { rev2str := `{"_attachments": {"hello.txt": {"stub":true, "revpos":1}, "bye.txt": {"data": "YnllLXlh"}}}` var body2 Body json.Unmarshal([]byte(rev2str), &body2) - body2["_rev"] = revid + body2[BodyRev] = revid revid, err = db.Put("doc1", body2) assertNoError(t, err, "Couldn't update document") assert.Equals(t, revid, "2-08b42c51334c0469bd060e6d9e6d797b") @@ -87,7 +87,7 @@ func TestAttachments(t *testing.T) { rev3str := `{"_attachments": {"bye.txt": {"stub":true,"revpos":2}}}` var body3 Body json.Unmarshal([]byte(rev3str), &body3) - body3["_rev"] = revid + body3[BodyRev] = revid revid, err = db.Put("doc1", body3) assertNoError(t, err, "Couldn't update document") assert.Equals(t, revid, "3-252b9fa1f306930bffc07e7d75b77faf") diff --git a/db/changes.go b/db/changes.go index e88ee0577e..a25eccb689 100644 --- a/db/changes.go +++ b/db/changes.go @@ -849,14 +849,14 @@ func (db *Database) DocIDChangesFeed(userChannels base.Set, explicitDocIds []str } changes := make([]ChangeRev, 1) - changes[0] = ChangeRev{"rev": body["_rev"].(string)} + changes[0] = ChangeRev{"rev": body[BodyRev].(string)} row.Changes = changes row.Seq = SequenceID{Seq: populatedDoc.Sequence} row.SetBranched((populatedDoc.Flags & channels.Branched) != 0) var removedChannels []string - if deleted, _ := body["_deleted"].(bool); deleted { + if deleted, _ := body[BodyDeleted].(bool); deleted { row.Deleted = true } diff --git a/db/crud.go b/db/crud.go index 0b97a95c37..8bd4231bd7 100644 --- a/db/crud.go +++ b/db/crud.go @@ -200,7 +200,7 @@ func (context *DatabaseContext) revCacheLoaderForDocument(doc *document, revid s } } if doc.History[revid].Deleted { - body["_deleted"] = true + body[BodyDeleted] = true } validatedHistory, getHistoryErr := doc.History.getHistory(revid) @@ -272,10 +272,10 @@ func (db *Database) GetRevWithHistory(docid, revid string, maxHistory int, histo // On access failure, return (only) the doc history and deletion/removal // status instead of returning an error. For justification see the comment in // the getRevFromDoc method, below - deleted, _ := body["_deleted"].(bool) - redactedBody := Body{"_id": docid, "_rev": revid} + deleted, _ := body[BodyDeleted].(bool) + redactedBody := Body{BodyId: docid, BodyRev: revid} if deleted { - redactedBody["_deleted"] = true + redactedBody[BodyDeleted] = true } else { redactedBody["_removed"] = true } @@ -287,7 +287,7 @@ func (db *Database) GetRevWithHistory(docid, revid string, maxHistory int, histo } if !revIDGiven { - if deleted, _ := body["_deleted"].(bool); deleted { + if deleted, _ := body[BodyDeleted].(bool); deleted { return nil, base.HTTPErrorf(404, "deleted") } } @@ -415,8 +415,8 @@ func (db *DatabaseContext) getRevision(doc *document, revid string) (Body, error } } body.FixJSONNumbers() // Make sure big ints won't get output in scientific notation - body["_id"] = doc.ID - body["_rev"] = revid + body[BodyId] = doc.ID + body[BodyRev] = revid if doc.Attachments != nil { body["_attachments"] = doc.Attachments @@ -465,7 +465,7 @@ func (db *Database) getRevFromDoc(doc *document, revid string, listRevisions boo if revid == "" || doc.History[revid] == nil /*|| !doc.History[revid].Deleted*/ { return nil, err } - body = Body{"_id": doc.ID, "_rev": revid} + body = Body{BodyId: doc.ID, BodyRev: revid} if !doc.History[revid].Deleted { body["_removed"] = true } @@ -482,7 +482,7 @@ func (db *Database) getRevFromDoc(doc *document, revid string, listRevisions boo } } if doc.History[revid].Deleted { - body["_deleted"] = true + body[BodyDeleted] = true } if listRevisions { validatedHistory, getHistoryErr := doc.History.getHistory(revid) @@ -541,7 +541,7 @@ func (db *Database) backupAncestorRevs(doc *document, revid string) { func (db *Database) initializeSyncData(doc *document) (err error) { body := doc.Body() doc.CurrentRev = createRevID(1, "", body) - body["_rev"] = doc.CurrentRev + body[BodyRev] = doc.CurrentRev doc.setFlag(channels.Deleted, false) doc.History = make(RevTree) if err = doc.History.addRevision(doc.ID, RevInfo{ID: doc.CurrentRev, Parent: "", Deleted: false}); err != nil { @@ -560,7 +560,7 @@ func (db *Database) OnDemandImportForWrite(docid string, doc *document, body Bod if doc.Body() == nil { isDelete = true } else { - deletedInBody, ok := body["_deleted"].(bool) + deletedInBody, ok := body[BodyDeleted].(bool) if ok { isDelete = deletedInBody } @@ -582,16 +582,16 @@ func (db *Database) OnDemandImportForWrite(docid string, doc *document, body Bod } // Updates or creates a document. -// The new body's "_rev" property must match the current revision's, if any. +// The new body's BodyRev property must match the current revision's, if any. func (db *Database) Put(docid string, body Body) (newRevID string, err error) { // Get the revision ID to match, and the new generation number: - matchRev, _ := body["_rev"].(string) + matchRev, _ := body[BodyRev].(string) generation, _ := ParseRevID(matchRev) if generation < 0 { return "", base.HTTPErrorf(http.StatusBadRequest, "Invalid revision ID") } generation++ - deleted, _ := body["_deleted"].(bool) + deleted, _ := body[BodyDeleted].(bool) expiry, err := body.extractExpiry() if err != nil { @@ -636,7 +636,7 @@ func (db *Database) Put(docid string, body Body) (newRevID string, err error) { // Make up a new _rev, and add it to the history: newRev := createRevID(generation, matchRev, body) - body["_rev"] = newRev + body[BodyRev] = newRev if err := doc.History.addRevision(docid, RevInfo{ID: newRev, Parent: matchRev, Deleted: deleted}); err != nil { base.Infof(base.KeyCRUD, "Failed to add revision ID: %s, error: %v", newRev, err) return nil, nil, nil, base.ErrRevTreeAddRevFailure @@ -668,7 +668,7 @@ func (db *Database) PutExistingRev(docid string, body Body, docHistory []string, if generation < 0 { return base.HTTPErrorf(http.StatusBadRequest, "Invalid revision ID") } - deleted, _ := body["_deleted"].(bool) + deleted, _ := body[BodyDeleted].(bool) expiry, err := body.extractExpiry() if err != nil { @@ -699,7 +699,7 @@ func (db *Database) PutExistingRev(docid string, body Body, docHistory []string, } if currentRevIndex == 0 { base.Debugf(base.KeyCRUD, "PutExistingRev(%q): No new revisions to add", base.UD(docid)) - body["_rev"] = newRev // The _rev field is expected by some callers. If missing, may cause problems for callers. + body[BodyRev] = newRev // The _rev field is expected by some callers. If missing, may cause problems for callers. return nil, nil, nil, base.ErrUpdateCancel // No new revisions to add } @@ -728,7 +728,7 @@ func (db *Database) PutExistingRev(docid string, body Body, docHistory []string, if err != nil { return nil, nil, nil, err } - body["_rev"] = newRev + body[BodyRev] = newRev return body, newAttachments, nil, nil }) return err @@ -843,7 +843,7 @@ func (db *Database) updateAndReturnDoc( } // Determine which is the current "winning" revision (it's not necessarily the new one): - newRevID = body["_rev"].(string) + newRevID = body[BodyRev].(string) prevCurrentRev := doc.CurrentRev var branched, inConflict bool doc.CurrentRev, branched, inConflict = doc.History.winningRevision() @@ -882,7 +882,7 @@ func (db *Database) updateAndReturnDoc( } // Run the sync function, to validate the update and compute its channels/access: - body["_id"] = doc.ID + body[BodyId] = doc.ID channelSet, access, roles, syncExpiry, oldBody, err := db.getChannelsAndAccess(doc, body, newRevID) if err != nil { return @@ -1148,7 +1148,7 @@ func (db *Database) updateAndReturnDoc( } if doc.History[newRevID].Deleted { - body["_deleted"] = true + body[BodyDeleted] = true } revChannels := doc.History[newRevID].Channels db.revisionCache.Put(doc.ID, newRevID, storedBody, encodeRevisions(history), revChannels) @@ -1233,12 +1233,12 @@ func (db *Database) MarkPrincipalsChanged(docid string, newRevID string, changed // Creates a new document, assigning it a random doc ID. func (db *Database) Post(body Body) (string, string, error) { - if body["_rev"] != nil { + if body[BodyRev] != nil { return "", "", base.HTTPErrorf(http.StatusNotFound, "No previous revision to replace") } // If there's an incoming _id property, use that as the doc ID. - docid, idFound := body["_id"].(string) + docid, idFound := body[BodyId].(string) if !idFound { docid = base.CreateUUID() } @@ -1250,9 +1250,9 @@ func (db *Database) Post(body Body) (string, string, error) { return docid, rev, err } -// Deletes a document, by adding a new revision whose "_deleted" property is true. +// Deletes a document, by adding a new revision whose _deleted property is true. func (db *Database) DeleteDoc(docid string, revid string) (string, error) { - body := Body{"_deleted": true, "_rev": revid} + body := Body{BodyDeleted: true, BodyRev: revid} return db.Put(docid, body) } @@ -1277,7 +1277,7 @@ func (db *Database) getChannelsAndAccess(doc *document, body Body, revID string) expiry *uint32, oldJson string, err error) { - base.Debugf(base.KeyCRUD, "Invoking sync on doc %q rev %s", base.UD(doc.ID), body["_rev"]) + base.Debugf(base.KeyCRUD, "Invoking sync on doc %q rev %s", base.UD(doc.ID), body[BodyRev]) // Get the parent revision, to pass to the sync function: var oldJsonBytes []byte diff --git a/db/crud_test.go b/db/crud_test.go index 6a3779dbb6..d0578dc5fb 100644 --- a/db/crud_test.go +++ b/db/crud_test.go @@ -128,7 +128,7 @@ func TestRevisionStorageConflictAndTombstones(t *testing.T) { log.Printf("Create tombstone 3-b") rev3b_body := Body{} rev3b_body["version"] = "3b" - rev3b_body["_deleted"] = true + rev3b_body[BodyDeleted] = true assertNoError(t, db.PutExistingRev("doc1", rev3b_body, []string{"3-b", "2-b"}, false), "add 3-b (tombstone)") // Retrieve tombstone @@ -180,7 +180,7 @@ func TestRevisionStorageConflictAndTombstones(t *testing.T) { rev3c_body := Body{} rev3c_body["version"] = "3c" rev3c_body["key1"] = prop_1000_bytes - rev3c_body["_deleted"] = true + rev3c_body[BodyDeleted] = true assertNoError(t, db.PutExistingRev("doc1", rev3c_body, []string{"3-c", "2-c"}, false), "add 3-c (large tombstone)") // Validate the tombstone is not stored inline (due to small size) @@ -295,7 +295,7 @@ func TestRevisionStoragePruneTombstone(t *testing.T) { rev3b_body := Body{} rev3b_body["version"] = "3b" rev3b_body["key1"] = prop_1000_bytes - rev3b_body["_deleted"] = true + rev3b_body[BodyDeleted] = true assertNoError(t, db.PutExistingRev("doc1", rev3b_body, []string{"3-b", "2-b"}, false), "add 3-b (tombstone)") // Retrieve tombstone diff --git a/db/database_test.go b/db/database_test.go index 04e6493a6d..2d1d82ddbd 100644 --- a/db/database_test.go +++ b/db/database_test.go @@ -177,16 +177,16 @@ func TestDatabase(t *testing.T) { body := Body{"key1": "value1", "key2": 1234} rev1id, err := db.Put("doc1", body) assertNoError(t, err, "Couldn't create document") - assert.Equals(t, rev1id, body["_rev"]) + assert.Equals(t, rev1id, body[BodyRev]) assert.Equals(t, rev1id, "1-cb0c9a22be0e5a1b01084ec019defa81") log.Printf("Create rev 2...") body["key1"] = "new value" body["key2"] = int64(4321) rev2id, err := db.Put("doc1", body) - body["_id"] = "doc1" + body[BodyId] = "doc1" assertNoError(t, err, "Couldn't update document") - assert.Equals(t, rev2id, body["_rev"]) + assert.Equals(t, rev2id, body[BodyRev]) assert.Equals(t, rev2id, "2-488724414d0ed6b398d6d2aeb228d797") // Retrieve the document: @@ -198,7 +198,7 @@ func TestDatabase(t *testing.T) { log.Printf("Retrieve rev 1...") gotbody, err = db.GetRev("doc1", rev1id, false, nil) assertNoError(t, err, "Couldn't get document with rev 1") - assert.DeepEquals(t, gotbody, Body{"key1": "value1", "key2": 1234, "_id": "doc1", "_rev": rev1id}) + assert.DeepEquals(t, gotbody, Body{"key1": "value1", "key2": 1234, BodyId: "doc1", BodyRev: rev1id}) log.Printf("Retrieve rev 2...") gotbody, err = db.GetRev("doc1", rev2id, false, nil) @@ -241,7 +241,7 @@ func TestDatabase(t *testing.T) { // Test PutExistingRev: log.Printf("Check PutExistingRev...") - body["_rev"] = "4-four" + body[BodyRev] = "4-four" body["key1"] = "fourth value" body["key2"] = int64(4444) history := []string{"4-four", "3-three", "2-488724414d0ed6b398d6d2aeb228d797", @@ -274,9 +274,9 @@ func TestGetDeleted(t *testing.T) { body, err = db.GetRev("doc1", rev2id, true, nil) assertNoError(t, err, "GetRev") expectedResult := Body{ - "_id": "doc1", - "_rev": rev2id, - "_deleted": true, + BodyId: "doc1", + BodyRev: rev2id, + BodyDeleted: true, "_revisions": map[string]interface{}{"start": 2, "ids": []string{"bc6d97f6e97c0d034a34f8aac2bf8b44", "dfd5e19813767eeddd08270fc5f385cd"}}, } assert.DeepEquals(t, body, expectedResult) @@ -314,7 +314,7 @@ func TestGetRemovedAsUser(t *testing.T) { rev2body := Body{ "key1": 1234, "channels": []string{"NBC"}, - "_rev": rev1id, + BodyRev: rev1id, } rev2id, err := db.Put("doc1", rev2body) assertNoError(t, err, "Put Rev 2") @@ -323,7 +323,7 @@ func TestGetRemovedAsUser(t *testing.T) { rev3body := Body{ "key1": 12345, "channels": []string{"NBC"}, - "_rev": rev2id, + BodyRev: rev2id, } _, err = db.Put("doc1", rev3body) assertNoError(t, err, "Put Rev 3") @@ -339,8 +339,8 @@ func TestGetRemovedAsUser(t *testing.T) { "_revisions": map[string]interface{}{ "start": 2, "ids": []string{rev2digest, rev1digest}}, - "_id": "doc1", - "_rev": rev2id, + BodyId: "doc1", + BodyRev: rev2id, } assert.DeepEquals(t, body, expectedResult) @@ -364,8 +364,8 @@ func TestGetRemovedAsUser(t *testing.T) { body, err = db.GetRev("doc1", rev2id, true, nil) assertNoError(t, err, "GetRev") expectedResult = Body{ - "_id": "doc1", - "_rev": rev2id, + BodyId: "doc1", + BodyRev: rev2id, "_removed": true, "_revisions": map[string]interface{}{ "start": 2, @@ -398,7 +398,7 @@ func TestGetRemoved(t *testing.T) { rev2body := Body{ "key1": 1234, "channels": []string{"NBC"}, - "_rev": rev1id, + BodyRev: rev1id, } rev2id, err := db.Put("doc1", rev2body) assertNoError(t, err, "Put Rev 2") @@ -407,7 +407,7 @@ func TestGetRemoved(t *testing.T) { rev3body := Body{ "key1": 12345, "channels": []string{"NBC"}, - "_rev": rev2id, + BodyRev: rev2id, } _, err = db.Put("doc1", rev3body) assertNoError(t, err, "Put Rev 3") @@ -423,8 +423,8 @@ func TestGetRemoved(t *testing.T) { "_revisions": map[string]interface{}{ "start": 2, "ids": []string{rev2digest, rev1digest}}, - "_id": "doc1", - "_rev": rev2id, + BodyId: "doc1", + BodyRev: rev2id, } assert.DeepEquals(t, body, expectedResult) @@ -439,8 +439,8 @@ func TestGetRemoved(t *testing.T) { body, err = db.GetRev("doc1", rev2id, true, nil) assertNoError(t, err, "GetRev") expectedResult = Body{ - "_id": "doc1", - "_rev": rev2id, + BodyId: "doc1", + BodyRev: rev2id, "_removed": true, "_revisions": map[string]interface{}{ "start": 2, @@ -471,9 +471,9 @@ func TestGetRemovedAndDeleted(t *testing.T) { assertNoError(t, err, "Put") rev2body := Body{ - "key1": 1234, - "_deleted": true, - "_rev": rev1id, + "key1": 1234, + BodyDeleted: true, + BodyRev: rev1id, } rev2id, err := db.Put("doc1", rev2body) assertNoError(t, err, "Put Rev 2") @@ -482,7 +482,7 @@ func TestGetRemovedAndDeleted(t *testing.T) { rev3body := Body{ "key1": 12345, "channels": []string{"NBC"}, - "_rev": rev2id, + BodyRev: rev2id, } _, err = db.Put("doc1", rev3body) assertNoError(t, err, "Put Rev 3") @@ -493,13 +493,13 @@ func TestGetRemovedAndDeleted(t *testing.T) { rev2digest := rev2id[2:] rev1digest := rev1id[2:] expectedResult := Body{ - "key1": 1234, - "_deleted": true, + "key1": 1234, + BodyDeleted: true, "_revisions": map[string]interface{}{ "start": 2, "ids": []string{rev2digest, rev1digest}}, - "_id": "doc1", - "_rev": rev2id, + BodyId: "doc1", + BodyRev: rev2id, } assert.DeepEquals(t, body, expectedResult) @@ -514,10 +514,10 @@ func TestGetRemovedAndDeleted(t *testing.T) { body, err = db.GetRev("doc1", rev2id, true, nil) assertNoError(t, err, "GetRev") expectedResult = Body{ - "_id": "doc1", - "_rev": rev2id, - "_removed": true, - "_deleted": true, + BodyId: "doc1", + BodyRev: rev2id, + "_removed": true, + BodyDeleted: true, "_revisions": map[string]interface{}{ "start": 2, "ids": []string{rev2digest, rev1digest}}, @@ -703,17 +703,17 @@ func TestRepeatedConflict(t *testing.T) { assertNoError(t, db.PutExistingRev("doc", body, []string{"2-a", "1-a"}, false), "add 2-a") // Get the _rev that was set in the body by PutExistingRev() and make assertions on it - rev, ok := body["_rev"] + rev, ok := body[BodyRev] assert.True(t, ok) revGen, _ := ParseRevID(rev.(string)) assert.Equals(t, revGen, 2) // Remove the _rev key from the body, and call PutExistingRev() again, which should re-add it - delete(body, "_rev") + delete(body, BodyRev) db.PutExistingRev("doc", body, []string{"2-a", "1-a"}, false) // The _rev should pass the same assertions as before, since PutExistingRev() should re-add it - rev, ok = body["_rev"] + rev, ok = body[BodyRev] assert.True(t, ok) revGen, _ = ParseRevID(rev.(string)) assert.Equals(t, revGen, 2) @@ -757,15 +757,15 @@ func TestConflicts(t *testing.T) { // Verify the change with the higher revid won: gotBody, err := db.Get("doc") - assert.DeepEquals(t, gotBody, Body{"_id": "doc", "_rev": "2-b", "n": 2, + assert.DeepEquals(t, gotBody, Body{BodyId: "doc", BodyRev: "2-b", "n": 2, "channels": []string{"all", "2b"}}) // Verify we can still get the other two revisions: gotBody, err = db.GetRev("doc", "1-a", false, nil) - assert.DeepEquals(t, gotBody, Body{"_id": "doc", "_rev": "1-a", "n": 1, + assert.DeepEquals(t, gotBody, Body{BodyId: "doc", BodyRev: "1-a", "n": 1, "channels": []string{"all", "1"}}) gotBody, err = db.GetRev("doc", "2-a", false, nil) - assert.DeepEquals(t, gotBody, Body{"_id": "doc", "_rev": "2-a", "n": 3, + assert.DeepEquals(t, gotBody, Body{BodyId: "doc", BodyRev: "2-a", "n": 3, "channels": []string{"all", "2a"}}) // Verify the change-log of the "all" channel: @@ -798,7 +798,7 @@ func TestConflicts(t *testing.T) { log.Printf("post-delete, got raw body: %s", rawBody) gotBody, err = db.Get("doc") - assert.DeepEquals(t, gotBody, Body{"_id": "doc", "_rev": "2-a", "n": 3, + assert.DeepEquals(t, gotBody, Body{BodyId: "doc", BodyRev: "2-a", "n": 3, "channels": []string{"all", "2a"}}) // Verify channel assignments are correct for channels defined by 2-a: @@ -852,9 +852,9 @@ func TestNoConflictsMode(t *testing.T) { assertHTTPError(t, err, 409) // Create a non-conflict with a longer history, ending in a deletion: - body["_deleted"] = true + body[BodyDeleted] = true assertNoError(t, db.PutExistingRev("doc", body, []string{"4-a", "3-a", "2-a", "1-a"}, false), "add 4-a") - delete(body, "_deleted") + delete(body, BodyDeleted) // Create a non-conflict with no history (re-creating the document, but with an invalid rev): err = db.PutExistingRev("doc", body, []string{"1-f"}, false) @@ -862,20 +862,20 @@ func TestNoConflictsMode(t *testing.T) { // Resurrect the tombstoned document with a valid history assertNoError(t, db.PutExistingRev("doc", body, []string{"5-f", "4-a"}, false), "add 5-f") - delete(body, "_deleted") + delete(body, BodyDeleted) // Create a new document with a longer history: assertNoError(t, db.PutExistingRev("COD", body, []string{"4-a", "3-a", "2-a", "1-a"}, false), "add COD") - delete(body, "_deleted") + delete(body, BodyDeleted) // Now use Put instead of PutExistingRev: // Successfully add a new revision: - _, err = db.Put("doc", Body{"_rev": "5-f", "foo": -1}) + _, err = db.Put("doc", Body{BodyRev: "5-f", "foo": -1}) assertNoError(t, err, "Put rev after 1-f") // Try to create a conflict: - _, err = db.Put("doc", Body{"_rev": "3-a", "foo": 7}) + _, err = db.Put("doc", Body{BodyRev: "3-a", "foo": 7}) assertHTTPError(t, err, 409) // Conflict with no ancestry: @@ -909,14 +909,14 @@ func TestAllowConflictsFalseTombstoneExistingConflict(t *testing.T) { // Set AllowConflicts to false db.Options.AllowConflicts = base.BooleanPointer(false) delete(body, "n") - body["_deleted"] = true + body[BodyDeleted] = true // Attempt to tombstone a non-leaf node of a conflicted document err := db.PutExistingRev("doc1", body, []string{"2-c", "1-a"}, false) assertTrue(t, err != nil, "expected error tombstoning non-leaf") // Tombstone the non-winning branch of a conflicted document - body["_rev"] = "2-a" + body[BodyRev] = "2-a" tombstoneRev, putErr := db.Put("doc1", body) assertNoError(t, putErr, "tombstone 2-a") doc, err := db.GetDocument("doc1", DocUnmarshalAll) @@ -924,12 +924,12 @@ func TestAllowConflictsFalseTombstoneExistingConflict(t *testing.T) { assert.Equals(t, doc.CurrentRev, "2-b") // Attempt to add a tombstone rev w/ the previous tombstone as parent - body["_rev"] = tombstoneRev + body[BodyRev] = tombstoneRev _, putErr = db.Put("doc1", body) assertTrue(t, putErr != nil, "Expect error tombstoning a tombstone") // Tombstone the winning branch of a conflicted document - body["_rev"] = "2-b" + body[BodyRev] = "2-b" _, putErr = db.Put("doc2", body) assertNoError(t, putErr, "tombstone 2-b") doc, err = db.GetDocument("doc2", DocUnmarshalAll) @@ -938,7 +938,7 @@ func TestAllowConflictsFalseTombstoneExistingConflict(t *testing.T) { // Set revs_limit=1, then tombstone non-winning branch of a conflicted document. Validate retrieval still works. db.RevsLimit = uint32(1) - body["_rev"] = "2-a" + body[BodyRev] = "2-a" _, putErr = db.Put("doc3", body) assertNoError(t, putErr, "tombstone 2-a w/ revslimit=1") doc, err = db.GetDocument("doc3", DocUnmarshalAll) @@ -975,7 +975,7 @@ func TestAllowConflictsFalseTombstoneExistingConflictNewEditsFalse(t *testing.T) // Set AllowConflicts to false db.Options.AllowConflicts = base.BooleanPointer(false) delete(body, "n") - body["_deleted"] = true + body[BodyDeleted] = true // Attempt to tombstone a non-leaf node of a conflicted document err := db.PutExistingRev("doc1", body, []string{"2-c", "1-a"}, false) @@ -1023,7 +1023,7 @@ func TestSyncFnOnPush(t *testing.T) { // Add several revisions at once to a doc, as on a push: log.Printf("Check PutExistingRev...") - body["_rev"] = "4-four" + body[BodyRev] = "4-four" body["key1"] = "fourth value" body["key2"] = int64(4444) body["channels"] = "clibup" @@ -1236,7 +1236,7 @@ func TestPostWithExistingId(t *testing.T) { // Test creating a document with existing id property: customDocId := "customIdValue" log.Printf("Create document with existing id...") - body := Body{"_id": customDocId, "key1": "value1", "key2": "existing"} + body := Body{BodyId: customDocId, "key1": "value1", "key2": "existing"} docid, rev1id, err := db.Post(body) assert.True(t, rev1id != "") assert.True(t, docid == customDocId) @@ -1272,7 +1272,7 @@ func TestPutWithUserSpecialProperty(t *testing.T) { // Test creating a document with existing id property: customDocId := "customIdValue" log.Printf("Create document with existing id...") - body := Body{"_id": customDocId, "key1": "value1", "_key2": "existing"} + body := Body{BodyId: customDocId, "key1": "value1", "_key2": "existing"} docid, rev1id, err := db.Post(body) assert.True(t, rev1id == "") assert.True(t, docid == "") @@ -1289,7 +1289,7 @@ func TestWithNullPropertyKey(t *testing.T) { // Test creating a document with null property key customDocId := "customIdValue" log.Printf("Create document with empty property key") - body := Body{"_id": customDocId, "": "value1"} + body := Body{BodyId: customDocId, "": "value1"} docid, rev1id, _ := db.Post(body) assert.True(t, rev1id != "") assert.True(t, docid != "") @@ -1305,7 +1305,7 @@ func TestPostWithUserSpecialProperty(t *testing.T) { // Test creating a document with existing id property: customDocId := "customIdValue" log.Printf("Create document with existing id...") - body := Body{"_id": customDocId, "key1": "value1", "key2": "existing"} + body := Body{BodyId: customDocId, "key1": "value1", "key2": "existing"} docid, rev1id, err := db.Post(body) assert.True(t, rev1id != "") assert.True(t, docid == customDocId) @@ -1319,7 +1319,7 @@ func TestPostWithUserSpecialProperty(t *testing.T) { // Test that posting an update with a user special property does not update the //document log.Printf("Update document with existing id...") - body = Body{"_id": customDocId, "_rev": rev1id, "_special": "value", "key1": "value1", "key2": "existing"} + body = Body{BodyId: customDocId, BodyRev: rev1id, "_special": "value", "key1": "value1", "key2": "existing"} _, err = db.Put(docid, body) assert.True(t, err.Error() == "400 user defined top level properties beginning with '_' are not allowed in document body") @@ -1430,7 +1430,7 @@ func TestChannelView(t *testing.T) { body := Body{"key1": "value1", "key2": 1234} rev1id, err := db.Put("doc1", body) assertNoError(t, err, "Couldn't create document") - assert.Equals(t, rev1id, body["_rev"]) + assert.Equals(t, rev1id, body[BodyRev]) assert.Equals(t, rev1id, "1-cb0c9a22be0e5a1b01084ec019defa81") var entries LogEntries diff --git a/db/document.go b/db/document.go index fc1496d004..6b11a443bf 100644 --- a/db/document.go +++ b/db/document.go @@ -627,12 +627,12 @@ func (doc *document) IsChannelRemoval(revID string) (body Body, history Body, ch // Construct removal body body = Body{ - "_id": doc.ID, - "_rev": revID, + BodyId: doc.ID, + BodyRev: revID, "_removed": true, } if isDelete { - body["_deleted"] = true + body[BodyDeleted] = true } // Build revision history for revID @@ -785,7 +785,7 @@ func (doc *document) UnmarshalWithXattr(data []byte, xdata []byte, unmarshalLeve // If there's no body, but there is an xattr, set body as {"_deleted":true} to align with non-xattr handling if len(data) == 0 && len(xdata) > 0 { doc._body = Body{} - doc._body["_deleted"] = true + doc._body[BodyDeleted] = true } return nil } @@ -795,7 +795,7 @@ func (doc *document) MarshalWithXattr() (data []byte, xdata []byte, err error) { body := doc._body // If body is non-empty and non-deleted, unmarshal and return if body != nil { - deleted, _ := body["_deleted"].(bool) + deleted, _ := body[BodyDeleted].(bool) if !deleted { data, err = json.Marshal(body) if err != nil { diff --git a/db/event.go b/db/event.go index 50e7344fd6..a2a9f2c681 100644 --- a/db/event.go +++ b/db/event.go @@ -46,7 +46,7 @@ type DocumentChangeEvent struct { } func (dce *DocumentChangeEvent) String() string { - return fmt.Sprintf("Document change event for doc id: %s", dce.Doc["_id"]) + return fmt.Sprintf("Document change event for doc id: %s", dce.Doc[BodyId]) } func (dce *DocumentChangeEvent) EventType() EventType { diff --git a/db/event_manager_test.go b/db/event_manager_test.go index 7074c5ca72..e8dc25186d 100644 --- a/db/event_manager_test.go +++ b/db/event_manager_test.go @@ -88,7 +88,7 @@ func TestDocumentChangeEvent(t *testing.T) { } eventForTest := func(i int) (Body, base.Set) { testBody := Body{ - "_id": ids[i], + BodyId: ids[i], "value": i, } var channelSet base.Set @@ -165,7 +165,7 @@ func TestSlowExecutionProcessing(t *testing.T) { eventForTest := func(i int) (Body, base.Set) { testBody := Body{ - "_id": ids[i], + BodyId: ids[i], "value": i, } var channelSet base.Set @@ -203,7 +203,7 @@ func TestCustomHandler(t *testing.T) { eventForTest := func(i int) (Body, base.Set) { testBody := Body{ - "_id": ids[i], + BodyId: ids[i], "value": i, } var channelSet base.Set @@ -242,7 +242,7 @@ func TestUnhandledEvent(t *testing.T) { eventForTest := func(i int) (Body, base.Set) { testBody := Body{ - "_id": ids[i], + BodyId: ids[i], "value": i, } var channelSet base.Set @@ -346,7 +346,7 @@ func TestWebhookBasic(t *testing.T) { time.Sleep(1 * time.Second) eventForTest := func(i int) (Body, base.Set) { testBody := Body{ - "_id": ids[i], + BodyId: ids[i], "value": i, } var channelSet base.Set @@ -475,7 +475,7 @@ func TestWebhookOldDoc(t *testing.T) { time.Sleep(1 * time.Second) eventForTest := func(i int) (Body, base.Set) { testBody := Body{ - "_id": ids[i], + BodyId: ids[i], "value": i, } var channelSet base.Set @@ -592,7 +592,7 @@ func TestWebhookTimeout(t *testing.T) { time.Sleep(1 * time.Second) eventForTest := func(i int) (Body, base.Set) { testBody := Body{ - "_id": ids[i], + BodyId: ids[i], "value": i, } var channelSet base.Set @@ -693,7 +693,7 @@ func TestUnavailableWebhook(t *testing.T) { eventForTest := func(i int) (Body, base.Set) { testBody := Body{ - "_id": ids[i], + BodyId: ids[i], "value": i, } var channelSet base.Set diff --git a/db/import.go b/db/import.go index 2e5226ed8a..65bcd66a2a 100644 --- a/db/import.go +++ b/db/import.go @@ -30,7 +30,7 @@ func (db *Database) ImportDocRaw(docid string, value []byte, xattrValue []byte, var body Body if isDelete { - body = Body{"_deleted": true} + body = Body{BodyDeleted: true} } else { err := body.Unmarshal(value) if err != nil { @@ -200,7 +200,7 @@ func (db *Database) importDoc(docid string, body Body, isDelete bool, existingDo generation++ newRev = createRevID(generation, parentRev, body) base.Infof(base.KeyImport, "Created new rev ID %v", newRev) - body["_rev"] = newRev + body[BodyRev] = newRev doc.History.addRevision(docid, RevInfo{ID: newRev, Parent: parentRev, Deleted: isDelete}) // During import, oldDoc (doc.Body) is nil (since it's no longer available) diff --git a/db/revision.go b/db/revision.go index 865616b2ee..eef45d4f39 100644 --- a/db/revision.go +++ b/db/revision.go @@ -21,6 +21,12 @@ import ( // The body of a CouchDB document/revision as decoded from JSON. type Body map[string]interface{} +const ( + BodyDeleted = "_deleted" + BodyRev = "_rev" + BodyId = "_id" +) + func (b *Body) Unmarshal(data []byte) error { if err := json.Unmarshal(data, &b); err != nil { return err @@ -172,7 +178,7 @@ func compareRevIDs(id1, id2 string) int { func stripSpecialProperties(body Body) Body { stripped := Body{} for key, value := range body { - if key == "" || key[0] != '_' || key == "_attachments" || key == "_deleted" { + if key == "" || key[0] != '_' || key == "_attachments" || key == BodyDeleted { stripped[key] = value } } @@ -181,7 +187,7 @@ func stripSpecialProperties(body Body) Body { func containsUserSpecialProperties(body Body) bool { for key := range body { - if key != "" && key[0] == '_' && key != "_id" && key != "_rev" && key != "_deleted" && key != "_attachments" && key != "_revisions" { + if key != "" && key[0] == '_' && key != BodyId && key != BodyRev && key != BodyDeleted && key != "_attachments" && key != "_revisions" { return true } } diff --git a/db/revision_cache.go b/db/revision_cache.go index bd76dec971..7dc81b2f86 100644 --- a/db/revision_cache.go +++ b/db/revision_cache.go @@ -175,9 +175,9 @@ func (value *revCacheValue) loadForDoc(doc *document, context *DatabaseContext) func (value *revCacheValue) store(body Body, history Body, channels base.Set) { value.lock.Lock() if value.body == nil { - value.body = body.ShallowCopy() // Don't store a body the caller might later mutate - value.body["_id"] = value.key.DocID // Rev cache includes id and rev in the body. Ensure they are set in case callers aren't passing - value.body["_rev"] = value.key.RevID + value.body = body.ShallowCopy() // Don't store a body the caller might later mutate + value.body[BodyId] = value.key.DocID // Rev cache includes id and rev in the body. Ensure they are set in case callers aren't passing + value.body[BodyRev] = value.key.RevID value.history = history value.channels = channels value.err = nil diff --git a/db/revision_cache_test.go b/db/revision_cache_test.go index de58781ac5..22d0d56eb5 100644 --- a/db/revision_cache_test.go +++ b/db/revision_cache_test.go @@ -16,8 +16,8 @@ func TestRevisionCache(t *testing.T) { revForTest := func(i int) (Body, Body, base.Set) { body := Body{ - "_id": ids[i], - "_rev": "x", + BodyId: ids[i], + BodyRev: "x", } history := Body{"start": i} return body, history, nil @@ -27,7 +27,7 @@ func TestRevisionCache(t *testing.T) { t.Fatalf("nil body at #%d", i) } assert.True(t, body != nil) - assert.Equals(t, body["_id"], ids[i]) + assert.Equals(t, body[BodyId], ids[i]) assert.True(t, history != nil) assert.Equals(t, history["start"], i) assert.DeepEquals(t, channels, base.Set(nil)) @@ -36,7 +36,7 @@ func TestRevisionCache(t *testing.T) { cache := NewRevisionCache(10, nil) for i := 0; i < 10; i++ { body, history, channels := revForTest(i) - cache.Put(body["_id"].(string), body["_rev"].(string), body, history, channels) + cache.Put(body[BodyId].(string), body[BodyRev].(string), body, history, channels) } for i := 0; i < 10; i++ { @@ -46,7 +46,7 @@ func TestRevisionCache(t *testing.T) { for i := 10; i < 13; i++ { body, history, channels := revForTest(i) - cache.Put(body["_id"].(string), body["_rev"].(string), body, history, channels) + cache.Put(body[BodyId].(string), body[BodyRev].(string), body, history, channels) } for i := 0; i < 3; i++ { @@ -67,8 +67,8 @@ func TestLoaderFunction(t *testing.T) { err = base.HTTPErrorf(404, "missing") } else { body = Body{ - "_id": id.DocID, - "_rev": id.RevID, + BodyId: id.DocID, + BodyRev: id.RevID, } history = Body{"start": 1} channels = base.SetOf("*") @@ -78,7 +78,7 @@ func TestLoaderFunction(t *testing.T) { cache := NewRevisionCache(10, loader) body, history, channels, err := cache.Get("Jens", "1") - assert.Equals(t, body["_id"], "Jens") + assert.Equals(t, body[BodyId], "Jens") assert.True(t, history != nil) assert.True(t, channels != nil) assert.Equals(t, err, error(nil)) @@ -90,7 +90,7 @@ func TestLoaderFunction(t *testing.T) { assert.Equals(t, callsToLoader, 2) body, history, channels, err = cache.Get("Jens", "1") - assert.Equals(t, body["_id"], "Jens") + assert.Equals(t, body[BodyId], "Jens") assert.True(t, history != nil) assert.True(t, channels != nil) assert.Equals(t, err, error(nil)) diff --git a/db/revtree.go b/db/revtree.go index 2b8eebe601..c1a2f885c3 100644 --- a/db/revtree.go +++ b/db/revtree.go @@ -708,7 +708,7 @@ func ParseRevisions(body Body) []string { // http://wiki.apache.org/couchdb/HTTP_Document_API#GET revisions, ok := body["_revisions"].(map[string]interface{}) if !ok { - revid, ok := body["_rev"].(string) + revid, ok := body[BodyRev].(string) if !ok { return nil } diff --git a/db/shadower.go b/db/shadower.go index c33639ecee..cfb69d1ed5 100644 --- a/db/shadower.go +++ b/db/shadower.go @@ -93,7 +93,7 @@ func (s *Shadower) readTapFeed() { func (s *Shadower) pullDocument(key string, value []byte, isDeletion bool, cas uint64, flags uint32) error { var body Body if isDeletion { - body = Body{"_deleted": true} + body = Body{BodyDeleted: true} } else { if err := body.Unmarshal(value); err != nil { base.Infof(base.KeyShadow, "Doc %q is not JSON; skipping", base.UD(key)) @@ -124,7 +124,7 @@ func (s *Shadower) pullDocument(key string, value []byte, isDeletion bool, cas u } doc.UpstreamRev = newRev doc.UpstreamCAS = &cas - body["_rev"] = newRev + body[BodyRev] = newRev if doc.History[newRev] == nil { // It's a new rev, so add it to the history: if parentRev != "" && !doc.History.contains(parentRev) { diff --git a/db/shadower_test.go b/db/shadower_test.go index c41bb1eb9d..7dc76d4704 100644 --- a/db/shadower_test.go +++ b/db/shadower_test.go @@ -293,9 +293,9 @@ func TestShadowerPullRevisionWithMissingParentRev(t *testing.T) { //Assert that we can get the two conflicing revisions gotBody, err := db.GetRev("foo", "1-madeup", false, nil) - assert.DeepEquals(t, gotBody, Body{"_id": "foo", "a": "b", "_rev": "1-madeup"}) + assert.DeepEquals(t, gotBody, Body{BodyId: "foo", "a": "b", BodyRev: "1-madeup"}) gotBody, err = db.GetRev("foo", "2-edce85747420ad6781bdfccdebf82180", false, nil) - assert.DeepEquals(t, gotBody, Body{"_id": "foo", "a": "c", "_rev": "2-edce85747420ad6781bdfccdebf82180"}) + assert.DeepEquals(t, gotBody, Body{BodyId: "foo", "a": "c", BodyRev: "2-edce85747420ad6781bdfccdebf82180"}) } func TestShadowerPattern(t *testing.T) { diff --git a/db/special_docs.go b/db/special_docs.go index 6c7b63dee1..c67638aa49 100644 --- a/db/special_docs.go +++ b/db/special_docs.go @@ -68,7 +68,7 @@ func (db *Database) putSpecial(doctype string, docid string, matchRev string, bo if err := json.Unmarshal(value, &prevBody); err != nil { return nil, nil, err } - if matchRev != prevBody["_rev"] { + if matchRev != prevBody[BodyRev] { return nil, nil, base.HTTPErrorf(http.StatusConflict, "Document update conflict") } } @@ -80,7 +80,7 @@ func (db *Database) putSpecial(doctype string, docid string, matchRev string, bo fmt.Sscanf(matchRev, "0-%d", &generation) } revid = fmt.Sprintf("0-%d", generation+1) - body["_rev"] = revid + body[BodyRev] = revid bodyBytes, marshalErr := json.Marshal(body) return bodyBytes, nil, marshalErr } else { @@ -93,7 +93,7 @@ func (db *Database) putSpecial(doctype string, docid string, matchRev string, bo } func (db *Database) PutSpecial(doctype string, docid string, body Body) (string, error) { - matchRev, _ := body["_rev"].(string) + matchRev, _ := body[BodyRev].(string) body = stripSpecialSpecialProperties(body) return db.putSpecial(doctype, docid, matchRev, body) } diff --git a/rest/admin_api_test.go b/rest/admin_api_test.go index 1585338d5e..21dd6aab51 100644 --- a/rest/admin_api_test.go +++ b/rest/admin_api_test.go @@ -54,7 +54,7 @@ func TestNoPanicInvalidUpdate(t *testing.T) { assert.Equals(t, revGeneration, 1) // Update doc (normal update, no conflicting revisions added) - response = rt.SendAdminRequest("PUT", fmt.Sprintf("/db/%s", docId), fmt.Sprintf(`{"value":"secondval", "_rev":"%s"}`, revId)) + response = rt.SendAdminRequest("PUT", fmt.Sprintf("/db/%s", docId), fmt.Sprintf(`{"value":"secondval", db.BodyRev:"%s"}`, revId)) response.DumpBody() // Create conflict diff --git a/rest/api_test.go b/rest/api_test.go index 7c215f9ce6..798faa7bc3 100644 --- a/rest/api_test.go +++ b/rest/api_test.go @@ -1422,7 +1422,7 @@ readerLoop: var exp int - switch partJSON["_id"] { + switch partJSON[db.BodyId] { case "doc1": exp = reqRevsLimit case "doc2": @@ -3165,7 +3165,7 @@ func TestBulkGetRevPruning(t *testing.T) { // Get latest rev id response = rt.SendRequest("GET", "/db/doc1", "") json.Unmarshal(response.Body.Bytes(), &body) - revId = body["_rev"] + revId = body[db.BodyRev] // Spin up several goroutines to all try to do a _bulk_get on the latest revision. // Since they will be pruning the same shared rev history, it will cause a data race @@ -3301,7 +3301,7 @@ func TestBulkGetBadAttachmentReproIssue2528(t *testing.T) { // Get latest rev id response = rt.SendRequest("GET", resource, "") json.Unmarshal(response.Body.Bytes(), &body) - revId := body["_rev"] + revId := body[db.BodyRev] // Do a bulk_get to get the doc -- this was causing a panic prior to the fix for #2528 bulkGetDocs := fmt.Sprintf(`{"docs": [{"id": "%v", "rev": "%v"}, {"id": "%v", "rev": "%v"}]}`, docIdDoc1, revId, docIdDoc2, revidDoc2) @@ -3379,7 +3379,7 @@ func TestBulkGetBadAttachmentReproIssue2528(t *testing.T) { } // Assert expectations for the doc with no attachment errors - rawId, ok = partJson["_id"] + rawId, ok = partJson[db.BodyId] if ok { _, hasErr := partJson["error"] assertTrue(t, !hasErr, "Did not expect error field for this doc") diff --git a/rest/blip_api_test.go b/rest/blip_api_test.go index 756092cf88..0187a1a893 100644 --- a/rest/blip_api_test.go +++ b/rest/blip_api_test.go @@ -686,7 +686,7 @@ func TestBlipSendAndGetRev(t *testing.T) { assertStatus(t, response, 200) var responseBody RestDocument assertNoError(t, json.Unmarshal(response.Body.Bytes(), &responseBody), "Error unmarshalling GET doc response") - _, ok := responseBody["_deleted"] + _, ok := responseBody[db.BodyDeleted] assert.False(t, ok) // Tombstone the document @@ -701,7 +701,7 @@ func TestBlipSendAndGetRev(t *testing.T) { assertStatus(t, response, 200) responseBody = RestDocument{} assertNoError(t, json.Unmarshal(response.Body.Bytes(), &responseBody), "Error unmarshalling GET doc response") - deletedValue, deletedOK := responseBody["_deleted"].(bool) + deletedValue, deletedOK := responseBody[db.BodyDeleted].(bool) assert.True(t, deletedOK) assert.True(t, deletedValue) } @@ -730,8 +730,48 @@ func TestMultiChannelContinousChangesSubscription(t *testing.T) { } // Test setting and getting checkpoints -func TestCheckpoints(t *testing.T) { +func TestBlipSetCheckpoint(t *testing.T) { + defer base.SetUpTestLogging(base.LevelInfo, base.KeyHTTP|base.KeySync|base.KeySyncMsg)() + + // Setup + rt := RestTester{ + noAdminParty: true, + } + btSpec := BlipTesterSpec{ + connectingUsername: "user1", + connectingPassword: "1234", + restTester: &rt, + } + bt, err := NewBlipTesterFromSpec(btSpec) + assertNoError(t, err, "Unexpected error creating BlipTester") + defer bt.Close() + + // Create new checkpoint + checkpointBody := []byte(`{"client_seq":"1000"}`) + sent, _, resp, err := bt.SetCheckpoint("testclient", "", checkpointBody) + assert.True(t, sent) + assert.Equals(t, err, nil) + assert.Equals(t, resp.Properties["Error-Code"], "") + + checkpointRev := resp.Rev() + assert.Equals(t, checkpointRev, "0-1") + + // Validate checkpoint existence in bucket (local file name "/" needs to be URL encoded as %252F) + response := rt.SendAdminRequest("GET", "/db/_local/checkpoint%252Ftestclient", "") + assertStatus(t, response, 200) + var responseBody map[string]interface{} + err = json.Unmarshal(response.Body.Bytes(), &responseBody) + assert.Equals(t, responseBody["client_seq"], "1000") + + // Attempt to update the checkpoint with previous rev + checkpointBody = []byte(`{"client_seq":"1005"}`) + sent, _, resp, err = bt.SetCheckpoint("testclient", checkpointRev, checkpointBody) + assert.True(t, sent) + assert.Equals(t, err, nil) + assert.Equals(t, resp.Properties["Error-Code"], "") + checkpointRev = resp.Rev() + assert.Equals(t, checkpointRev, "0-2") } // Test no-conflicts mode replication (proposeChanges endpoint) diff --git a/rest/blip_sync.go b/rest/blip_sync.go index f2a5151b01..c93225436b 100644 --- a/rest/blip_sync.go +++ b/rest/blip_sync.go @@ -80,13 +80,13 @@ func userBlipHandler(underlyingMethod blipHandlerMethod) blipHandlerMethod { // Maps the profile (verb) of an incoming request to the method that handles it. var kHandlersByProfile = map[string]blipHandlerMethod{ - "getCheckpoint": (*blipHandler).handleGetCheckpoint, - "setCheckpoint": (*blipHandler).handleSetCheckpoint, - "subChanges": userBlipHandler((*blipHandler).handleSubChanges), - "changes": userBlipHandler((*blipHandler).handleChanges), - "rev": userBlipHandler((*blipHandler).handleRev), - "getAttachment": userBlipHandler((*blipHandler).handleGetAttachment), - "proposeChanges": (*blipHandler).handleProposedChanges, + messageGetCheckpoint: (*blipHandler).handleGetCheckpoint, + messageSetCheckpoint: (*blipHandler).handleSetCheckpoint, + messageSubChanges: userBlipHandler((*blipHandler).handleSubChanges), + messageChanges: userBlipHandler((*blipHandler).handleChanges), + messageRev: userBlipHandler((*blipHandler).handleRev), + messageGetAttachment: userBlipHandler((*blipHandler).handleGetAttachment), + messageProposeChanges: (*blipHandler).handleProposedChanges, } // HTTP handler for incoming BLIP sync WebSocket request (/db/_blipsync) @@ -204,7 +204,7 @@ func (ctx *blipSyncContext) Logf(logLevel base.LogLevel, logKey base.LogKey, for // Received a "getCheckpoint" request func (bh *blipHandler) handleGetCheckpoint(rq *blip.Message) error { - client := rq.Properties["client"] + client := rq.Properties[blipClient] bh.logEndpointEntry(rq.Profile(), fmt.Sprintf("Client:%s", client)) docID := fmt.Sprintf("checkpoint/%s", client) @@ -220,9 +220,9 @@ func (bh *blipHandler) handleGetCheckpoint(rq *blip.Message) error { if value == nil { return base.HTTPErrorf(http.StatusNotFound, http.StatusText(http.StatusNotFound)) } - response.Properties["rev"] = value["_rev"].(string) - delete(value, "_rev") - delete(value, "_id") + response.Properties[getCheckpointResponseRev] = value[db.BodyRev].(string) + delete(value, db.BodyRev) + delete(value, db.BodyId) value.FixJSONNumbers() response.SetJSONBody(value) return nil @@ -231,23 +231,26 @@ func (bh *blipHandler) handleGetCheckpoint(rq *blip.Message) error { // Received a "setCheckpoint" request func (bh *blipHandler) handleSetCheckpoint(rq *blip.Message) error { - setCheckpointParams := newSetCheckpointParams(rq) - bh.logEndpointEntry(rq.Profile(), setCheckpointParams.String()) + checkpointMessage := SetCheckpointMessage{rq} + bh.logEndpointEntry(rq.Profile(), checkpointMessage.String()) - docID := fmt.Sprintf("checkpoint/%s", setCheckpointParams.client()) + docID := fmt.Sprintf("checkpoint/%s", checkpointMessage.client()) var checkpoint db.Body - if err := rq.ReadJSONBody(&checkpoint); err != nil { + if err := checkpointMessage.ReadJSONBody(&checkpoint); err != nil { return err } - if revID := setCheckpointParams.rev(); revID != "" { - checkpoint["_rev"] = revID + if revID := checkpointMessage.rev(); revID != "" { + checkpoint[db.BodyRev] = revID } revID, err := bh.db.PutSpecial("local", docID, checkpoint) if err != nil { return err } - rq.Response().Properties["rev"] = revID + + checkpointResponse := SetCheckpointResponse{checkpointMessage.Response()} + checkpointResponse.setRev(revID) + return nil } @@ -421,7 +424,7 @@ func (bh *blipHandler) handleChangesResponse(sender *blip.Sender, response *blip } maxHistory := 0 - if max, err := strconv.ParseUint(response.Properties["maxHistory"], 10, 64); err == nil { + if max, err := strconv.ParseUint(response.Properties[changesResponseMaxHistory], 10, 64); err == nil { maxHistory = int(max) } @@ -559,26 +562,24 @@ func (bh *blipHandler) sendRevision(sender *blip.Sender, seq db.SequenceID, docI } } - outrq := blip.NewRequest() - outrq.SetProfile("rev") - seqJSON, _ := json.Marshal(seq) - outrq.Properties["id"] = docID - delete(body, "_id") - outrq.Properties["rev"] = revID - delete(body, "_rev") - if del, _ := body["_deleted"].(bool); del { - outrq.Properties["deleted"] = "1" - delete(body, "deleted") - } - outrq.Properties["sequence"] = string(seqJSON) - if len(history) > 0 { - outrq.Properties["history"] = strings.Join(history, ",") + outrq := NewRevMessage() + outrq.setId(docID) + outrq.setRev(revID) + if del, _ := body[db.BodyDeleted].(bool); del { + outrq.setDeleted(del) } + outrq.setSequence(seq) + outrq.setHistory(history) + + delete(body, db.BodyId) + delete(body, db.BodyRev) + delete(body, db.BodyDeleted) + outrq.SetJSONBody(body) if atts := db.BodyAttachments(body); atts != nil { // Allow client to download attachments in 'atts', but only while pulling this rev bh.addAllowedAttachments(atts) - sender.Send(outrq) + sender.Send(outrq.Message) go func() { defer func() { if panicked := recover(); panicked != nil { @@ -591,15 +592,18 @@ func (bh *blipHandler) sendRevision(sender *blip.Sender, seq db.SequenceID, docI }() } else { outrq.SetNoReply(true) - sender.Send(outrq) + sender.Send(outrq.Message) } + } // Received a "rev" request, i.e. client is pushing a revision body func (bh *blipHandler) handleRev(rq *blip.Message) error { - addRevisionParams := newAddRevisionParams(rq) - bh.logEndpointEntry(rq.Profile(), addRevisionParams.String()) + //addRevisionParams := newAddRevisionParams(rq) + revMessage := revMessage{Message: rq} + + bh.logEndpointEntry(rq.Profile(), revMessage.String()) var body db.Body if err := rq.ReadJSONBody(&body); err != nil { @@ -607,20 +611,20 @@ func (bh *blipHandler) handleRev(rq *blip.Message) error { } // Doc metadata comes from the BLIP message metadata, not magic document properties: - docID, found := addRevisionParams.id() - revID, rfound := addRevisionParams.rev() + docID, found := revMessage.id() + revID, rfound := revMessage.rev() if !found || !rfound { return base.HTTPErrorf(http.StatusBadRequest, "Missing docID or revID") } - if addRevisionParams.deleted() { - body["_deleted"] = true + if revMessage.deleted() { + body[db.BodyDeleted] = true } // noconflicts flag from LiteCore // https://github.com/couchbase/couchbase-lite-core/wiki/Replication-Protocol#rev var noConflicts bool - if val, ok := rq.Properties["noconflicts"]; ok { + if val, ok := rq.Properties[revMessageNoConflicts]; ok { var err error noConflicts, err = strconv.ParseBool(val) if err != nil { @@ -629,7 +633,7 @@ func (bh *blipHandler) handleRev(rq *blip.Message) error { } history := []string{revID} - if historyStr := rq.Properties["history"]; historyStr != "" { + if historyStr := rq.Properties[revMessageHistory]; historyStr != "" { history = append(history, strings.Split(historyStr, ",")...) } @@ -671,7 +675,7 @@ func (bh *blipHandler) handleGetAttachment(rq *blip.Message) error { bh.Logf(base.LevelDebug, base.KeySync, "Sending attachment with digest=%q (%dkb) User:%s", digest, len(attachment)/1024, base.UD(bh.effectiveUsername)) response := rq.Response() response.SetBody(attachment) - response.SetCompressed(rq.Properties["compress"] == "true") + response.SetCompressed(rq.Properties[blipCompress] == "true") return nil } @@ -688,7 +692,7 @@ func (bh *blipHandler) downloadOrVerifyAttachments(body db.Body, minRevpos int, bh.Logf(base.LevelDebug, base.KeySync, " Verifying attachment %q (digest %s). User:%s", base.UD(name), digest, base.UD(bh.effectiveUsername)) nonce, proof := db.GenerateProofOfAttachment(knownData) outrq := blip.NewRequest() - outrq.Properties = map[string]string{"Profile": "proveAttachment", "digest": digest} + outrq.Properties = map[string]string{blipProfile: messageProveAttachment, proveAttachmentDigest: digest} outrq.SetBody(nonce) sender.Send(outrq) if body, err := outrq.Response().Body(); err != nil { @@ -702,9 +706,9 @@ func (bh *blipHandler) downloadOrVerifyAttachments(body db.Body, minRevpos int, // If I don't have the attachment, I will request it from the client: bh.Logf(base.LevelDebug, base.KeySync, " Asking for attachment %q (digest %s). User:%s", base.UD(name), digest, base.UD(bh.effectiveUsername)) outrq := blip.NewRequest() - outrq.Properties = map[string]string{"Profile": "getAttachment", "digest": digest} + outrq.Properties = map[string]string{blipProfile: messageGetAttachment, getAttachmentDigest: digest} if isCompressible(name, meta) { - outrq.Properties["compress"] = "true" + outrq.Properties[blipCompress] = "true" } sender.Send(outrq) return outrq.Response().Body() diff --git a/rest/blip_sync_messages.go b/rest/blip_sync_messages.go index 0b88cc9641..855a6e63e2 100644 --- a/rest/blip_sync_messages.go +++ b/rest/blip_sync_messages.go @@ -13,6 +13,57 @@ import ( "github.com/couchbase/sync_gateway/db" ) +// Message types +const ( + messageSetCheckpoint = "setCheckpoint" + messageGetCheckpoint = "getCheckpoint" + messageSubChanges = "subChanges" + messageChanges = "changes" + messageRev = "rev" + messageGetAttachment = "getAttachment" + messageProposeChanges = "proposeChanges" + messageProveAttachment = "proveAttachment" +) + +// Message properties +const ( + + // Common message properties + blipClient = "client" + blipCompress = "compress" + blipProfile = "Profile" + + // setCheckpoint message properties + setCheckpointRev = "rev" + + // getCheckpoint message properties + getCheckpointResponseRev = "rev" + + // subChanges message properties + subChangesActiveOnly = "active_only" + subChangesFilter = "filter" + subChangesChannels = "channels" + subChangesSince = "since" + subChangesContinuous = "continuous" + + // rev message properties + revMessageId = "id" + revMessageRev = "rev" + revMessageDeleted = "deleted" + revMessageSequence = "sequence" + revMessageHistory = "history" + revMessageNoConflicts = "noconflicts" + + // changes message properties + changesResponseMaxHistory = "maxHistory" + + // getAttachment message properties + getAttachmentDigest = "digest" + + // proveAttachment + proveAttachmentDigest = "digest" +) + // Function signature for something that parses a sequence id from a string type SequenceIDParser func(since string) (db.SequenceID, error) @@ -36,7 +87,7 @@ func newSubChangesParams(rq *blip.Message, logger base.SGLogger, zeroSeq db.Sequ // Determine incoming since and docIDs once, since there is some overhead associated with their calculation sinceSequenceId := zeroSeq - if sinceStr, found := rq.Properties["since"]; found { + if sinceStr, found := rq.Properties[subChangesSince]; found { var err error if sinceSequenceId, err = sequenceIDParser(base.ConvertJSONString(sinceStr)); err != nil { logger.Logf(base.LevelInfo, base.KeySync, "%s: Invalid sequence ID in 'since': %s", rq, sinceStr) @@ -91,27 +142,27 @@ func (s *subChangesParams) batchSize() int { func (s *subChangesParams) continuous() bool { continuous := false - if val, found := s.rq.Properties["continuous"]; found && val != "false" { + if val, found := s.rq.Properties[subChangesContinuous]; found && val != "false" { continuous = true } return continuous } func (s *subChangesParams) activeOnly() bool { - return (s.rq.Properties["active_only"] == "true") + return (s.rq.Properties[subChangesActiveOnly] == "true") } func (s *subChangesParams) filter() string { - return s.rq.Properties["filter"] + return s.rq.Properties[subChangesFilter] } func (s *subChangesParams) channels() (channels string, found bool) { - channels, found = s.rq.Properties["channels"] + channels, found = s.rq.Properties[subChangesChannels] return channels, found } func (s *subChangesParams) channelsExpandedSet() (resultChannels base.Set, err error) { - channelsParam, found := s.rq.Properties["channels"] + channelsParam, found := s.rq.Properties[subChangesChannels] if !found { return nil, fmt.Errorf("Missing 'channels' filter parameter") } @@ -156,31 +207,40 @@ func (s *subChangesParams) String() string { } -type setCheckpointParams struct { - rq *blip.Message // The underlying BLIP message +// setCheckpoint message +type SetCheckpointMessage struct { + *blip.Message } -func newSetCheckpointParams(rq *blip.Message) *setCheckpointParams { - return &setCheckpointParams{ - rq: rq, - } +func NewSetCheckpointMessage() *SetCheckpointMessage { + scm := &SetCheckpointMessage{blip.NewRequest()} + scm.SetProfile(messageSetCheckpoint) + return scm +} + +func (scm *SetCheckpointMessage) client() string { + return scm.Properties[blipClient] +} + +func (scm *SetCheckpointMessage) setClient(client string) { + scm.Properties[blipClient] = client } -func (s *setCheckpointParams) client() string { - return s.rq.Properties["client"] +func (scm *SetCheckpointMessage) rev() string { + return scm.Properties[setCheckpointRev] } -func (s *setCheckpointParams) rev() string { - return s.rq.Properties["rev"] +func (scm *SetCheckpointMessage) setRev(rev string) { + scm.Properties[setCheckpointRev] = rev } -func (s *setCheckpointParams) String() string { +func (scm *SetCheckpointMessage) String() string { buffer := bytes.NewBufferString("") - buffer.WriteString(fmt.Sprintf("Client:%v ", s.client())) + buffer.WriteString(fmt.Sprintf("Client:%v ", scm.client())) - rev := s.rev() + rev := scm.rev() if len(rev) > 0 { buffer.WriteString(fmt.Sprintf("Rev:%v ", rev)) } @@ -189,61 +249,107 @@ func (s *setCheckpointParams) String() string { } -type addRevisionParams struct { - rq *blip.Message // The underlying BLIP message +type SetCheckpointResponse struct { + *blip.Message } -func newAddRevisionParams(rq *blip.Message) *addRevisionParams { - return &addRevisionParams{ - rq: rq, - } +func (scr *SetCheckpointResponse) Rev() (rev string) { + return scr.Properties[setCheckpointRev] +} + +func (scr *SetCheckpointResponse) setRev(rev string) { + scr.Properties[setCheckpointRev] = rev +} + +// Rev message +type revMessage struct { + *blip.Message } -func (a *addRevisionParams) id() (id string, found bool) { - id, found = a.rq.Properties["id"] +func NewRevMessage() *revMessage { + rm := &revMessage{blip.NewRequest()} + rm.SetProfile(messageRev) + return rm +} + +func (rm *revMessage) id() (id string, found bool) { + id, found = rm.Properties[revMessageId] return id, found } -func (a *addRevisionParams) rev() (rev string, found bool) { - rev, found = a.rq.Properties["rev"] +func (rm *revMessage) rev() (rev string, found bool) { + rev, found = rm.Properties[revMessageRev] return rev, found } -func (a *addRevisionParams) deleted() bool { - deleted, found := a.rq.Properties["deleted"] +func (rm *revMessage) deleted() bool { + deleted, found := rm.Properties[revMessageDeleted] if !found { return false } return deleted != "0" && deleted != "false" } -func (a *addRevisionParams) hasDeletedPropery() bool { - _, found := a.rq.Properties["deleted"] +func (rm *revMessage) hasDeletedProperty() bool { + _, found := rm.Properties[revMessageDeleted] return found } -func (a *addRevisionParams) sequence() (sequence string, found bool) { - sequence, found = a.rq.Properties["sequence"] +func (rm *revMessage) sequence() (sequence string, found bool) { + sequence, found = rm.Properties[revMessageSequence] return sequence, found } -func (a *addRevisionParams) String() string { +func (rm *revMessage) setId(id string) { + rm.Properties[revMessageId] = id +} + +func (rm *revMessage) setRev(rev string) { + rm.Properties[revMessageRev] = rev +} + +func (rm *revMessage) setDeleted(deleted bool) { + if deleted { + rm.Properties[revMessageDeleted] = "1" + } else { + delete(rm.Properties, revMessageDeleted) + } +} + +func (rm *revMessage) setHistory(history []string) { + if len(history) > 0 { + rm.Properties[revMessageHistory] = strings.Join(history, ",") + } else { + delete(rm.Properties, revMessageHistory) + } +} + +func (rm *revMessage) setSequence(seq db.SequenceID) error { + seqJSON, marshalErr := json.Marshal(seq) + if marshalErr != nil { + return marshalErr + } + rm.Properties[revMessageSequence] = string(seqJSON) + return nil +} + +func (rm *revMessage) String() string { buffer := bytes.NewBufferString("") - if id, foundId := a.id(); foundId { + if id, foundId := rm.id(); foundId { buffer.WriteString(fmt.Sprintf("Id:%v ", id)) } - if rev, foundRev := a.rev(); foundRev { + if rev, foundRev := rm.rev(); foundRev { buffer.WriteString(fmt.Sprintf("Rev:%v ", rev)) } - if a.hasDeletedPropery() { - buffer.WriteString(fmt.Sprintf("Deleted:%v ", a.deleted())) + if rm.hasDeletedProperty() { + buffer.WriteString(fmt.Sprintf("Deleted:%v ", rm.deleted())) } - if sequence, foundSequence := a.sequence(); foundSequence == true { + if sequence, foundSequence := rm.sequence(); foundSequence == true { buffer.WriteString(fmt.Sprintf("Sequence:%v ", sequence)) } @@ -262,7 +368,7 @@ func newGetAttachmentParams(rq *blip.Message) *getAttachmentParams { } func (g *getAttachmentParams) digest() string { - return g.rq.Properties["digest"] + return g.rq.Properties[getAttachmentDigest] } func (g *getAttachmentParams) String() string { diff --git a/rest/blip_sync_messages_test.go b/rest/blip_sync_messages_test.go index e48cbc52fc..0cbd710d3f 100644 --- a/rest/blip_sync_messages_test.go +++ b/rest/blip_sync_messages_test.go @@ -50,8 +50,10 @@ func TestAddRevision(t *testing.T) { if testCase.deletedPropertyValue != "nil" { blipMessage.Properties["deleted"] = testCase.deletedPropertyValue } - addRevision := newAddRevisionParams(&blipMessage) - assert.Equals(t, addRevision.deleted(), testCase.expectedDeletedVal) + revMessage := &revMessage{ + Message: &blipMessage, + } + assert.Equals(t, revMessage.deleted(), testCase.expectedDeletedVal) } } diff --git a/rest/bulk_api.go b/rest/bulk_api.go index a5c06451b5..af99b3e9bc 100644 --- a/rest/bulk_api.go +++ b/rest/bulk_api.go @@ -154,7 +154,7 @@ func (h *handler) handleAllDocs() error { row.Status = http.StatusForbidden return row } - doc.RevID = body["_rev"].(string) + doc.RevID = body[db.BodyRev].(string) } if includeDocs { row.Doc = body @@ -482,7 +482,7 @@ func (h *handler) handleBulkDocs() error { // If ID is present, check whether local doc. (note: if _id is absent or non-string, docid will be // empty string and handled during normal doc processing) - docid, _ := doc["_id"].(string) + docid, _ := doc[db.BodyId].(string) if strings.HasPrefix(docid, "_local/") { localDocs = append(localDocs, doc) @@ -496,7 +496,7 @@ func (h *handler) handleBulkDocs() error { result := make([]db.Body, 0, len(docs)) for _, item := range docs { doc := item.(map[string]interface{}) - docid, _ := doc["_id"].(string) + docid, _ := doc[db.BodyId].(string) var err error var revid string if newEdits { @@ -540,7 +540,7 @@ func (h *handler) handleBulkDocs() error { var err error var revid string offset := len("_local/") - docid, _ := doc["_id"].(string) + docid, _ := doc[db.BodyId].(string) idslug := docid[offset:] revid, err = h.db.PutSpecial("local", idslug, doc) status := db.Body{} diff --git a/rest/changes_api_test.go b/rest/changes_api_test.go index 73d7ca4bd5..8932a2be7e 100644 --- a/rest/changes_api_test.go +++ b/rest/changes_api_test.go @@ -252,7 +252,7 @@ func TestDocDeletionFromChannel(t *testing.T) { log.Printf("Deletion looks like: %s", response.Body.Bytes()) var docBody db.Body json.Unmarshal(response.Body.Bytes(), &docBody) - assert.DeepEquals(t, docBody, db.Body{"_id": "alpha", "_rev": rev2, "_deleted": true}) + assert.DeepEquals(t, docBody, db.Body{db.BodyId: "alpha", db.BodyRev: rev2, db.BodyDeleted: true}) // Access without deletion revID shouldn't be allowed (since doc is not in Alice's channels): response = rt.Send(requestByUser("GET", "/db/alpha", "", "alice")) @@ -1062,7 +1062,7 @@ func TestOneShotChangesWithExplicitDocIds(t *testing.T) { assert.Equals(t, err, nil) assert.Equals(t, len(changes.Results), 4) assert.Equals(t, changes.Results[3].ID, "docD") - assert.Equals(t, changes.Results[3].Doc["_id"], "docD") + assert.Equals(t, changes.Results[3].Doc[db.BodyId], "docD") //test parameter style=all_docs //Create a conflict revision on docC diff --git a/rest/doc_api.go b/rest/doc_api.go index a31b8adc65..99903b7df5 100644 --- a/rest/doc_api.go +++ b/rest/doc_api.go @@ -69,7 +69,7 @@ func (h *handler) handleGetDoc() error { if value == nil { return kNotFoundError } - h.setHeader("Etag", strconv.Quote(value["_rev"].(string))) + h.setHeader("Etag", strconv.Quote(value[db.BodyRev].(string))) hasBodies := (attachmentsSince != nil && value["_attachments"] != nil) if h.requestAccepts("multipart/") && (hasBodies || !h.requestAccepts("application/json")) { @@ -214,11 +214,11 @@ func (h *handler) handlePutAttachment() error { // couchdb creates empty body on attachment PUT // for non-existant doc id body = db.Body{} - body["_rev"] = revid + body[db.BodyRev] = revid } else if err != nil { return err } else if body != nil { - body["_rev"] = revid + body[db.BodyRev] = revid } // find attachment (if it existed) @@ -262,9 +262,9 @@ func (h *handler) handlePutDoc() error { if h.getQuery("new_edits") != "false" { // Regular PUT: if oldRev := h.getQuery("rev"); oldRev != "" { - body["_rev"] = oldRev + body[db.BodyRev] = oldRev } else if ifMatch := h.rq.Header.Get("If-Match"); ifMatch != "" { - body["_rev"] = ifMatch + body[db.BodyRev] = ifMatch } newRev, err = h.db.Put(docid, body) if err != nil { @@ -282,7 +282,7 @@ func (h *handler) handlePutDoc() error { return err } - newRev, ok = body["_rev"].(string) + newRev, ok = body[db.BodyRev].(string) if !ok { return base.HTTPErrorf(http.StatusInternalServerError, "Expected revision id in body _rev field") } @@ -334,7 +334,7 @@ func (h *handler) handleGetLocalDoc() error { if value == nil { return kNotFoundError } - value["_id"] = "_local/" + docid + value[db.BodyId] = "_local/" + docid value.FixJSONNumbers() h.writeJSON(value) return nil diff --git a/rest/import_test.go b/rest/import_test.go index 8781f51387..07f344c0cf 100644 --- a/rest/import_test.go +++ b/rest/import_test.go @@ -519,7 +519,7 @@ func TestXattrImportMultipleActorOnDemandGet(t *testing.T) { // Extract rev from response for comparison with second GET below var body db.Body json.Unmarshal(response.Body.Bytes(), &body) - revId, ok := body["_rev"].(string) + revId, ok := body[db.BodyRev].(string) assertTrue(t, ok, "No rev included in response") // Go get the cas for the doc to use for update @@ -540,7 +540,7 @@ func TestXattrImportMultipleActorOnDemandGet(t *testing.T) { response = rt.SendAdminRequest("GET", "/db/"+mobileKey, "") assert.Equals(t, response.Code, 200) json.Unmarshal(response.Body.Bytes(), &body) - newRevId := body["_rev"].(string) + newRevId := body[db.BodyRev].(string) log.Printf("Retrieved via Sync Gateway after non-mobile update, revId:%v", newRevId) assert.Equals(t, newRevId, revId) } @@ -572,7 +572,7 @@ func TestXattrImportMultipleActorOnDemandPut(t *testing.T) { // Extract rev from response for comparison with second GET below var body db.Body json.Unmarshal(response.Body.Bytes(), &body) - revId, ok := body["_rev"].(string) + revId, ok := body[db.BodyRev].(string) assertTrue(t, ok, "No rev included in response") // Go get the cas for the doc to use for update @@ -631,7 +631,7 @@ func TestXattrImportMultipleActorOnDemandFeed(t *testing.T) { // Extract rev from response for comparison with second GET below var body db.Body json.Unmarshal(response.Body.Bytes(), &body) - revId, ok := body["_rev"].(string) + revId, ok := body[db.BodyRev].(string) assertTrue(t, ok, "No rev included in response") // Go get the cas for the doc to use for update @@ -672,7 +672,7 @@ func TestXattrImportMultipleActorOnDemandFeed(t *testing.T) { response = rt.SendAdminRequest("GET", "/db/"+mobileKey, "") assert.Equals(t, response.Code, 200) json.Unmarshal(response.Body.Bytes(), &body) - newRevId := body["_rev"].(string) + newRevId := body[db.BodyRev].(string) log.Printf("Retrieved via Sync Gateway after non-mobile update, revId:%v", newRevId) assert.Equals(t, newRevId, revId) diff --git a/rest/utilities_testing.go b/rest/utilities_testing.go index df043ce195..7c93e808b8 100644 --- a/rest/utilities_testing.go +++ b/rest/utilities_testing.go @@ -738,6 +738,24 @@ func NewBlipTesterFromSpec(spec BlipTesterSpec) (*BlipTester, error) { } +func (bt *BlipTester) SetCheckpoint(client string, checkpointRev string, body []byte) (sent bool, req *SetCheckpointMessage, res *SetCheckpointResponse, err error) { + + scm := NewSetCheckpointMessage() + scm.SetCompressed(true) + scm.setClient(client) + scm.setRev(checkpointRev) + scm.SetBody(body) + + sent = bt.sender.Send(scm.Message) + if !sent { + return sent, scm, nil, fmt.Errorf("Failed to send setCheckpoint for client: %v", client) + } + + scr := &SetCheckpointResponse{scm.Response()} + return true, scm, scr, nil + +} + // The docHistory should be in the same format as expected by db.PutExistingRev(), or empty if this is the first revision func (bt *BlipTester) SendRevWithHistory(docId, docRev string, revHistory []string, body []byte, properties blip.Properties) (sent bool, req, res *blip.Message, err error) { @@ -1283,7 +1301,7 @@ func NewRestDocument() *RestDocument { } func (d RestDocument) ID() string { - rawID, hasID := d["_id"] + rawID, hasID := d[db.BodyId] if !hasID { return "" } @@ -1292,11 +1310,11 @@ func (d RestDocument) ID() string { } func (d RestDocument) SetID(docId string) { - d["_id"] = docId + d[db.BodyId] = docId } func (d RestDocument) RevID() string { - rawRev, hasRev := d["_rev"] + rawRev, hasRev := d[db.BodyRev] if !hasRev { return "" } @@ -1304,7 +1322,7 @@ func (d RestDocument) RevID() string { } func (d RestDocument) SetRevID(revId string) { - d["_rev"] = revId + d[db.BodyRev] = revId } func (d RestDocument) SetAttachments(attachments db.AttachmentMap) { diff --git a/rest/utilities_testing_test.go b/rest/utilities_testing_test.go index bea63c563c..5ebf38ffb3 100644 --- a/rest/utilities_testing_test.go +++ b/rest/utilities_testing_test.go @@ -1,11 +1,12 @@ package rest import ( - "testing" - "github.com/couchbaselabs/go.assert" "encoding/json" "log" + "testing" + "github.com/couchbase/sync_gateway/db" + "github.com/couchbaselabs/go.assert" ) func TestDocumentUnmarshal(t *testing.T) { @@ -50,19 +51,17 @@ func TestDocumentUnmarshal(t *testing.T) { } - func TestAttachmentRoundTrip(t *testing.T) { - doc := RestDocument{} attachmentMap := db.AttachmentMap{ "foo": &db.DocAttachment{ ContentType: "text", - Digest: "whatever", + Digest: "whatever", }, "bar": &db.DocAttachment{ ContentType: "text", - Digest: "whatever", + Digest: "whatever", }, } @@ -79,4 +78,3 @@ func TestAttachmentRoundTrip(t *testing.T) { } } -