func populateDB() (err error) { _, err = db.SaveTreeAt("", map[string]interface{}{ "_val": "root", "vehicles": map[string]interface{}{ "_val": "things that move", "car": map[string]interface{}{ "land": true, "air": false, "water": false, }, "airplane": map[string]interface{}{ "land": true, "air": true, "water": false, }, "boat": map[string]interface{}{ "land": false, "air": false, "water": true, }, }, "animals": []map[string]interface{}{ map[string]interface{}{ "name": "bird", }, map[string]interface{}{ "name": "dog", }, map[string]interface{}{ "name": "cow", }, }, }) return err }
func Post(w http.ResponseWriter, r *http.Request) { ctx := getContext(r) var err error var rev string if ctx.jsonBody == nil { res := responses.BadRequest("You must send a JSON body for this request.") w.WriteHeader(res.Code) json.NewEncoder(w).Encode(res) return } if ctx.lastKey == "_bulk_get" { BulkGet(w, r) return } else if ctx.lastKey == "_bulk_docs" { BulkDocs(w, r) return } else if ctx.lastKey == "_revs_diff" { RevsDiff(w, r) return } if ctx.lastKey[0] == '_' { res := responses.BadRequest("you can't post to special keys.") w.WriteHeader(res.Code) json.NewEncoder(w).Encode(res) return } // POST is supposed to generate a new doc, with a new random _id: id := db.Random(7) path := ctx.path + "/" + id if ctx.jsonBody != nil { // if it is JSON we must save it as its structure demands rev, err = db.SaveTreeAt(path, ctx.jsonBody) } else { // otherwise it's an error res := responses.BadRequest("you need to send a JSON body for this request.") w.WriteHeader(res.Code) json.NewEncoder(w).Encode(res) return } if err != nil { log.Error("couldn't save value: ", err) res := responses.UnknownError(err.Error()) w.WriteHeader(res.Code) json.NewEncoder(w).Encode(res) return } w.WriteHeader(201) w.Header().Add("Content-Type", "application/json") json.NewEncoder(w).Encode(responses.Success{true, id, rev}) }
/* should accept PATCH requests with JSON objects: `curl -X PATCH http://db/path -d '{"to": {"key": "some value"}}' -H 'content-type: application/json'` this will not replace all values under /path, but only modify the values which the JSON object refers to. */ func Patch(w http.ResponseWriter, r *http.Request) { ctx := getContext(r) var err error var rev string if ctx.lastKey[0] == '_' { res := responses.BadRequest("you can't update special keys") w.WriteHeader(res.Code) json.NewEncoder(w).Encode(res) return } if !ctx.exists { res := responses.NotFound() w.WriteHeader(res.Code) json.NewEncoder(w).Encode(res) return } if ctx.currentRev != ctx.providedRev { log.WithFields(log.Fields{ "given": ctx.providedRev, "current": ctx.currentRev, }).Debug("rev mismatch.") res := responses.ConflictError() w.WriteHeader(res.Code) json.NewEncoder(w).Encode(res) return } // update the tree as the JSON structure demands rev, err = db.SaveTreeAt(ctx.path, ctx.jsonBody) if err != nil { log.Debug("couldn't save value: ", err) res := responses.UnknownError(err.Error()) w.WriteHeader(res.Code) json.NewEncoder(w).Encode(res) return } w.WriteHeader(200) w.Header().Add("Content-Type", "application/json") json.NewEncoder(w).Encode(responses.Success{true, ctx.lastKey, rev}) }
func TestBasics(t *testing.T) { g := Goblin(t) RegisterFailHandler(func(m string, _ ...int) { g.Fail(m) }) g.Describe("basics test", func() { g.Before(func() { db.Erase() db.Start() }) g.After(func() { db.End() }) g.It("should save a tree", func() { db.SaveTreeAt("/fruits/banana", map[string]interface{}{ "colour": "yellow", "hardness": "low", "_val": "a fruit.", }) Expect(db.GetValueAt("/fruits/banana")).To(BeEquivalentTo(`"a fruit."`)) Expect(db.GetValueAt("/fruits/banana/colour")).To(BeEquivalentTo(`"yellow"`)) Expect(db.GetValueAt("/fruits/banana/hardness")).To(BeEquivalentTo(`"low"`)) _, err := db.GetValueAt("/fruits") Expect(err).To(HaveOccurred()) Expect(db.GetTreeAt("/fruits")).To(Equal(map[string]interface{}{ "banana": map[string]interface{}{ "colour": value("yellow"), "hardness": value("low"), "_val": "a fruit.", }, })) }) g.It("should modify a subvalue of the tree", func() { db.SaveValueAt("/fruits/banana/colour", []byte(`"black-and-yellow"`)) Expect(db.GetValueAt("/fruits/banana/colour")).To(BeEquivalentTo(`"black-and-yellow"`)) Expect(db.GetValueAt("/fruits/banana/hardness")).To(BeEquivalentTo(`"low"`)) }) g.It("should add a value deeply nested in a tree that doesn't exists", func() { db.SaveValueAt("/fruits/mellon/season", []byte(`"spring"`)) Expect(db.GetValueAt("/fruits/mellon/season")).To(BeEquivalentTo(`"spring"`)) Expect(db.GetTreeAt("/fruits")).To(Equal(map[string]interface{}{ "banana": map[string]interface{}{ "colour": value("black-and-yellow"), "hardness": value("low"), "_val": "a fruit.", }, "mellon": map[string]interface{}{ "season": value("spring"), }, })) }) g.It("should add a tree deeply nested like the previous", func() { db.SaveTreeAt("/fruits/orange", map[string]interface{}{ "colour": "orange", "hardness": "medium", "_val": "name == colour", }) Expect(db.GetValueAt("/fruits/orange/colour")).To(BeEquivalentTo(`"orange"`)) Expect(db.GetValueAt("/fruits/orange")).To(BeEquivalentTo(`"name == colour"`)) Expect(db.GetTreeAt("/fruits/orange")).To(Equal(map[string]interface{}{ "_val": "name == colour", "colour": value("orange"), "hardness": value("medium"), })) }) g.It("should delete a key", func() { db.DeleteAt("/fruits/banana/colour") Expect(db.GetValueAt("/fruits/orange/colour")).To(BeEquivalentTo(`"orange"`)) _, err := db.GetValueAt("/fruits/banana/colour") Expect(db.GetValueAt("/fruits/banana/colour/_deleted")).To(BeEquivalentTo("")) Expect(err).To(HaveOccurred()) }) g.It("should delete a value when setting it to null with a tree", func() { db.SaveTreeAt("/fruits/mellon", map[string]interface{}{ "colour": "orange", "season": nil, }) Expect(db.GetValueAt("/fruits/mellon/colour")).To(BeEquivalentTo(`"orange"`)) _, err := db.GetValueAt("/fruits/mellon/season") Expect(err).To(HaveOccurred()) db.SaveTreeAt("/fruits", map[string]interface{}{ "mellon": nil, }) _, err = db.GetValueAt("/fruits/mellon/colour") Expect(err).To(HaveOccurred()) _, err = db.GetValueAt("/fruits/mellon") Expect(err).To(HaveOccurred()) }) g.It("should delete a tree", func() { db.DeleteAt("/fruits/banana") Expect(db.GetValueAt("/fruits/orange/colour")).To(BeEquivalentTo(`"orange"`)) _, err := db.GetValueAt("/fruits/banana/hardness") Expect(err).To(HaveOccurred()) rev, err := db.DeleteAt("/fruits") Expect(err).ToNot(HaveOccurred()) Expect(rev).To(HavePrefix("9-")) _, err = db.GetValueAt("/fruits") Expect(err).To(HaveOccurred()) Expect(db.GetValueAt("/fruits/orange/_deleted")).To(BeEquivalentTo("")) _, err = db.GetValueAt("/fruits/orange/colour") Expect(err).To(HaveOccurred()) _, err = db.GetValueAt("/fruits/banana/hardness") Expect(err).To(HaveOccurred()) }) g.It("should error when fetching an untouched tree path", func() { _, err := db.GetTreeAt("/nowhere") Expect(err).To(HaveOccurred()) }) g.It("should return when fetching a deleted tree path", func() { tree, err := db.GetTreeAt("/fruits/banana") Expect(err).ToNot(HaveOccurred()) empty := make(map[string]interface{}) Expect(tree).To(BeEquivalentTo(empty)) }) }) }
func TestRevs(t *testing.T) { g := Goblin(t) RegisterFailHandler(func(m string, _ ...int) { g.Fail(m) }) g.Describe("_rev", func() { g.Before(func() { db.Erase() db.Start() }) g.After(func() { db.End() }) g.It("should generate _rev for a single key", func() { savedrev, _ := db.SaveValueAt("/name", []byte(`"database of vehicles"`)) gottenrev, _ := db.GetValueAt("/name/_rev") Expect(savedrev).To(BeEquivalentTo(gottenrev)) Expect(gottenrev).To(HavePrefix("1-")) }) g.It("should generate _rev for parent keys", func() { db.SaveValueAt("/vehicles/car/land", []byte("true")) db.SaveValueAt("/vehicles/carriage/land", []byte("true")) db.SaveValueAt("/vehicles/carriage/air", []byte("false")) Expect(db.GetValueAt("/vehicles/car/land/_rev")).To(HavePrefix("1-")) Expect(db.GetValueAt("/vehicles/car/_rev")).To(HavePrefix("1-")) Expect(db.GetValueAt("/vehicles/_rev")).To(HavePrefix("3-")) Expect(db.GetValueAt("/vehicles/carriage/land/_rev")).To(HavePrefix("1-")) Expect(db.GetValueAt("/vehicles/carriage/_rev")).To(HavePrefix("2-")) }) g.It("should bump _rev for single keys", func() { db.SaveValueAt("/name", []byte(`"just a database of vehicles"`)) Expect(db.GetValueAt("/name/_rev")).To(HavePrefix("2-")) }) g.It("should bump _rev for parent keys", func() { db.SaveValueAt("/vehicles/car/water", []byte("false")) Expect(db.GetValueAt("/vehicles/car/land/_rev")).To(HavePrefix("1-")) Expect(db.GetValueAt("/vehicles/car/water/_rev")).To(HavePrefix("1-")) Expect(db.GetValueAt("/vehicles/car/_rev")).To(HavePrefix("2-")) Expect(db.GetValueAt("/vehicles/_rev")).To(HavePrefix("4-")) Expect(db.GetValueAt("/vehicles/carriage/land/_rev")).To(HavePrefix("1-")) Expect(db.GetValueAt("/vehicles/carriage/_rev")).To(HavePrefix("2-")) db.SaveValueAt("/vehicles/boat/water", []byte("true")) Expect(db.GetValueAt("/vehicles/car/land/_rev")).To(HavePrefix("1-")) Expect(db.GetValueAt("/vehicles/car/water/_rev")).To(HavePrefix("1-")) Expect(db.GetValueAt("/vehicles/boat/water/_rev")).To(HavePrefix("1-")) Expect(db.GetValueAt("/vehicles/car/_rev")).To(HavePrefix("2-")) Expect(db.GetValueAt("/vehicles/boat/_rev")).To(HavePrefix("1-")) Expect(db.GetValueAt("/vehicles/_rev")).To(HavePrefix("5-")) }) g.It("on delete, should bump _rev for parents and sons", func() { db.DeleteAt("/vehicles/car") Expect(db.GetValueAt("/vehicles/car/land/_rev")).To(HavePrefix("2-")) Expect(db.GetValueAt("/vehicles/car/water/_rev")).To(HavePrefix("2-")) Expect(db.GetValueAt("/vehicles/car/_rev")).To(HavePrefix("3-")) Expect(db.GetValueAt("/vehicles/boat/water/_rev")).To(HavePrefix("1-")) Expect(db.GetValueAt("/vehicles/boat/_rev")).To(HavePrefix("1-")) Expect(db.GetValueAt("/vehicles/_rev")).To(HavePrefix("6-")) Expect(db.GetValueAt("/vehicles/carriage/land/_rev")).To(HavePrefix("1-")) Expect(db.GetValueAt("/vehicles/carriage/_rev")).To(HavePrefix("2-")) }) g.It("should bump rev of all parents of affected keys", func() { db.SaveTreeAt("/vehicles/boat", map[string]interface{}{ "water": true, "land": false, "air": false, }) Expect(db.GetValueAt("/vehicles/car/land/_rev")).To(HavePrefix("2-")) Expect(db.GetValueAt("/vehicles/car/water/_rev")).To(HavePrefix("2-")) Expect(db.GetValueAt("/vehicles/car/_rev")).To(HavePrefix("3-")) Expect(db.GetValueAt("/vehicles/boat/water/_rev")).To(HavePrefix("2-")) Expect(db.GetValueAt("/vehicles/boat/land/_rev")).To(HavePrefix("1-")) Expect(db.GetValueAt("/vehicles/boat/air/_rev")).To(HavePrefix("1-")) Expect(db.GetValueAt("/vehicles/boat/_rev")).To(HavePrefix("2-")) Expect(db.GetValueAt("/vehicles/_rev")).To(HavePrefix("7-")) }) g.It("doing it again to make sure", func() { db.SaveTreeAt("/vehicles", map[string]interface{}{ "car": map[string]interface{}{ "water": true, }, "boat": map[string]interface{}{ "air": true, }, }) Expect(db.GetValueAt("/vehicles/car/land/_rev")).To(HavePrefix("2-")) Expect(db.GetValueAt("/vehicles/car/water/_rev")).To(HavePrefix("3-")) Expect(db.GetValueAt("/vehicles/car/_rev")).To(HavePrefix("4-")) Expect(db.GetValueAt("/vehicles/boat/water/_rev")).To(HavePrefix("2-")) Expect(db.GetValueAt("/vehicles/boat/land/_rev")).To(HavePrefix("1-")) Expect(db.GetValueAt("/vehicles/boat/air/_rev")).To(HavePrefix("2-")) Expect(db.GetValueAt("/vehicles/boat/_rev")).To(HavePrefix("3-")) Expect(db.GetValueAt("/vehicles/_rev")).To(HavePrefix("8-")) Expect(db.GetValueAt("/vehicles/carriage/land/_rev")).To(HavePrefix("1-")) Expect(db.GetValueAt("/vehicles/carriage/_rev")).To(HavePrefix("2-")) }) g.It("should bump the revs correctly when a tree operation involves deleting", func() { db.SaveTreeAt("/vehicles", map[string]interface{}{ "carriage": map[string]interface{}{ "space": false, "land": nil, }, "boat": nil, }) Expect(db.GetValueAt("/vehicles/car/_rev")).To(HavePrefix("4-")) Expect(db.GetValueAt("/vehicles/boat/water/_rev")).To(HavePrefix("3-")) Expect(db.GetValueAt("/vehicles/boat/land/_rev")).To(HavePrefix("2-")) Expect(db.GetValueAt("/vehicles/boat/air/_rev")).To(HavePrefix("3-")) Expect(db.GetValueAt("/vehicles/boat/_rev")).To(HavePrefix("4-")) Expect(db.GetValueAt("/vehicles/_rev")).To(HavePrefix("9-")) Expect(db.GetValueAt("/vehicles/carriage/land/_rev")).To(HavePrefix("2-")) Expect(db.GetValueAt("/vehicles/carriage/_rev")).To(HavePrefix("3-")) }) g.It("should bump revs of intermediate paths when modifying a deep field", func() { db.SaveValueAt("/vehicles/train/land/rail", []byte("true")) Expect(db.GetValueAt("/vehicles/_rev")).To(HavePrefix("10-")) Expect(db.GetValueAt("/vehicles/train/_rev")).To(HavePrefix("1-")) Expect(db.GetValueAt("/vehicles/train/land/_rev")).To(HavePrefix("1-")) Expect(db.GetValueAt("/vehicles/train/land/rail/_rev")).To(HavePrefix("1-")) db.DeleteAt("/vehicles/train") Expect(db.GetValueAt("/vehicles/_rev")).To(HavePrefix("11-")) Expect(db.GetValueAt("/vehicles/train/_rev")).To(HavePrefix("2-")) Expect(db.GetValueAt("/vehicles/train/land/_rev")).To(HavePrefix("2-")) Expect(db.GetValueAt("/vehicles/train/land/rail/_rev")).To(HavePrefix("2-")) db.SaveTreeAt("", map[string]interface{}{ "vehicles": map[string]interface{}{ "skate": map[string]interface{}{ "air": map[string]interface{}{ "carried": map[string]interface{}{ "_val": true, }, }, }, }, }) Expect(db.GetValueAt("/vehicles/_rev")).To(HavePrefix("12-")) Expect(db.GetValueAt("/vehicles/skate/_rev")).To(HavePrefix("1-")) Expect(db.GetValueAt("/vehicles/skate/air/_rev")).To(HavePrefix("1-")) Expect(db.GetValueAt("/vehicles/skate/air/carried/_rev")).To(HavePrefix("1-")) db.SaveTreeAt("", map[string]interface{}{ "vehicles": map[string]interface{}{ "skate": map[string]interface{}{ "air": map[string]interface{}{ "carried": nil, }, }, }, }) Expect(db.GetValueAt("/vehicles/_rev")).To(HavePrefix("13-")) Expect(db.GetValueAt("/vehicles/skate/_rev")).To(HavePrefix("2-")) Expect(db.GetValueAt("/vehicles/skate/air/_rev")).To(HavePrefix("2-")) Expect(db.GetValueAt("/vehicles/skate/air/carried/_rev")).To(HavePrefix("2-")) }) g.It("should return rev", func() { sk, err := db.GetSpecialKeysAt("/vehicles/skate") Expect(err).ToNot(HaveOccurred()) Expect(sk.Rev).To(HavePrefix("2-")) }) }) }
func TestArrays(t *testing.T) { g := Goblin(t) RegisterFailHandler(func(m string, _ ...int) { g.Fail(m) }) g.Describe("array values", func() { g.Before(func() { db.Erase() db.Start() }) g.After(func() { db.End() }) g.It("should save a tree with a simple array", func() { rev, err := db.SaveTreeAt("", map[string]interface{}{ "numbers": []interface{}{"zero", "one", "two", "three"}, }) Expect(err).ToNot(HaveOccurred()) Expect(rev).To(HavePrefix("1-")) Expect(db.GetValueAt("/numbers/0")).To(BeEquivalentTo(`"zero"`)) Expect(db.GetValueAt("/numbers/3")).To(BeEquivalentTo(`"three"`)) _, err = db.GetValueAt("/numbers") Expect(err).To(HaveOccurred()) Expect(db.GetTreeAt("/numbers")).To(Equal(map[string]interface{}{ "0": value("zero"), "1": value("one"), "2": value("two"), "3": value("three"), })) }) g.It("should save a tree with a complex array", func() { rev, err := db.SaveTreeAt("", map[string]interface{}{ "letters": []interface{}{ map[string]interface{}{ "name": "á", "variations": []interface{}{"a", "A"}, }, map[string]interface{}{ "name": "bê", "variations": []interface{}{"b", "B"}, }, }, }) Expect(err).ToNot(HaveOccurred()) Expect(rev).To(HavePrefix("2-")) Expect(db.GetValueAt("/letters/0/name")).To(BeEquivalentTo(`"á"`)) Expect(db.GetValueAt("/letters/1/variations/1")).To(BeEquivalentTo(`"B"`)) _, err = db.GetValueAt("/letters/0/variations") Expect(err).To(HaveOccurred()) Expect(db.GetTreeAt("/letters")).To(Equal(map[string]interface{}{ "0": map[string]interface{}{ "name": value("á"), "variations": map[string]interface{}{ "0": value("a"), "1": value("A"), }, }, "1": map[string]interface{}{ "name": value("bê"), "variations": map[string]interface{}{ "0": value("b"), "1": value("B"), }, }, })) }) }) }
func TestSeqs(t *testing.T) { g := Goblin(t) RegisterFailHandler(func(m string, _ ...int) { g.Fail(m) }) g.Describe("seqs getting bumped", func() { g.Before(func() { db.Erase() db.Start() }) g.After(func() { db.End() }) /* we currently don't care a lot to the exact seqs here, we only care if the values are being increased and there are no conflicts. later maybe we'll have to optimize all this (and conseqüently change all these tests) so we don't get new the seq bumped for operations that do not result in an actual _rev being written (in DELETE and UNDELETE operations there are temporary _revs being created and then replaced by others with the same number prefix). */ g.It("should increase seqs when adding a new tree", func() { db.SaveTreeAt("", map[string]interface{}{ "x": "oiwaeuriasburis", }) Expect(db.GlobalUpdateSeq()).To(BeEquivalentTo(2)) Expect(db.LastSeqAt("")).To(BeNumerically(">", uint64(0))) Expect(db.LastSeqAt("/x")).To(BeNumerically("==", uint64(0))) }) g.It("should increase seqs when adding a new value", func() { db.SaveValueAt("/z", []byte("ihfiuewrhewoiruh")) Expect(db.GlobalUpdateSeq()).To(BeEquivalentTo(4)) Expect(db.LastSeqAt("")).To(BeNumerically(">", uint64(2))) Expect(db.LastSeqAt("/x")).To(BeNumerically("==", uint64(0))) }) g.It("should increase seqs when deleting a value", func() { db.DeleteAt("/x") Expect(db.GlobalUpdateSeq()).To(BeEquivalentTo(7)) Expect(db.LastSeqAt("")).To(BeNumerically(">", uint64(5))) Expect(db.LastSeqAt("/x")).To(BeNumerically("==", uint64(0))) Expect(db.LastSeqAt("/z")).To(BeNumerically("==", uint64(0))) }) g.It("should increase seqs when undeleting a value", func() { db.SaveValueAt("/x/xchild", []byte("skjfbslkfbskdf")) Expect(db.GlobalUpdateSeq()).To(BeEquivalentTo(10)) Expect(db.LastSeqAt("")).To(BeNumerically(">", uint64(7))) Expect(db.LastSeqAt("/x")).To(BeNumerically(">", uint64(7))) }) g.It("should increase seqs when making bizarre things", func() { db.ReplaceTreeAt("/x", map[string]interface{}{ "xchild": nil, "other": "saldkasndlksad", "_val": "askjdasnkdjasd", }, false) db.ReplaceTreeAt("/x/xchild", map[string]interface{}{ "ham": "sadljkasndlksad", "_val": "askjdasnkdjasd", }, false) Expect(db.GlobalUpdateSeq()).To(BeEquivalentTo(19)) }) }) }