Пример #1
0
/* requests with raw string bodies:
   - curl -X PUT http://db/path/to/key/_val -d 'some value'
 and complete JSON objects
   - curl -X PUT http://db/path -d '{"to": {"_val": "nothing here", "key": "some value"}}' -H 'content-type: application/json'

While setting the raw string body of a path will only update that path and do not change others, a full JSON request will replace all keys under the specified path. PUT is idempotent.

The "_val" key is optional when setting, but can be used to set values right to the key to which they refer. It is sometimes needed, like in this example, here "path/to" had some children values to be set, but also needed a value of its own.
Other use of the "_val" key is to set a value to null strictly, because setting the nude key to null will delete it instead of setting it.
*/
func Put(w http.ResponseWriter, r *http.Request) {
	ctx := getContext(r)

	if ctx.lastKey == "_security" {
		WriteSecurity(w, r)
		return
	}

	var err error
	var rev string

	if ctx.exists && 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
	}

	if ctx.lastKey == "_val" || ctx.jsonBody == nil {
		// updating the value at only this specific path, as raw bytes
		rev, err = db.SaveValueAt(ctx.path, db.ToLevel(ctx.body))
	} else if ctx.jsonBody != nil {
		// saving a JSON tree, as its structure demands, expanding to subpaths and so on
		if ctx.lastKey[0] == '_' {
			log.WithFields(log.Fields{
				"path":    ctx.path,
				"lastkey": ctx.lastKey,
			}).Debug("couldn't update special key.")
			res := responses.BadRequest("you can't update special keys")
			w.WriteHeader(res.Code)
			json.NewEncoder(w).Encode(res)
			return
		}

		if ctx.localDoc {
			rev, err = db.SaveLocalDocAt(ctx.path, ctx.jsonBody)
		} else {
			rev, err = db.ReplaceTreeAt(ctx.path, ctx.jsonBody, false)
		}
	} else {
		err = errors.New("value received at " + ctx.path + " is not JSON: " + string(ctx.body))
	}

	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(201)
	w.Header().Add("Content-Type", "application/json")
	json.NewEncoder(w).Encode(responses.Success{true, ctx.lastKey, rev})
}
Пример #2
0
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))
		})
	})
}
Пример #3
0
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-"))
		})
	})
}
Пример #4
0
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))
		})
	})
}