Example #1
0
// this method only exists for compatibility with PouchDB and should not be used elsewhere.
func BulkGet(w http.ResponseWriter, r *http.Request) {
	ctx := getContext(r)
	var ireqs interface{}

	/* options */
	revs := flag(r, "revs")

	var ok bool
	if ireqs, ok = ctx.jsonBody["docs"]; !ok {
		res := responses.BadRequest("You were supposed to request some docs specified by their ids, and you didn't.")
		w.WriteHeader(res.Code)
		json.NewEncoder(w).Encode(res)
		return
	}

	reqs := ireqs.([]interface{})
	res := responses.BulkGet{
		Results: make([]responses.BulkGetResult, len(reqs)),
	}
	for i, ireq := range reqs {
		req := ireq.(map[string]interface{})
		res.Results[i] = responses.BulkGetResult{
			Docs: make([]responses.DocOrError, 1),
		}

		iid, ok := req["id"]
		if !ok {
			err := responses.BadRequest("missing id")
			res.Results[i].Docs[0].Error = &err
			continue
		}
		id := iid.(string)
		res.Results[i].Id = id

		path := db.CleanPath(ctx.path) + "/" + db.EscapeKey(id)
		rev := string(db.GetRev(path))
		doc, err := db.GetTreeAt(path)
		if err != nil {
			err := responses.NotFound()
			res.Results[i].Docs[0].Error = &err
			continue
		}

		doc["_id"] = id
		doc["_rev"] = rev
		delete(doc, "_val") // pouchdb doesn't like top-level keys starting with _

		if revs {
			// magic method to fetch _revisions
			// docs["_revisions"] = ...
		}

		res.Results[i].Docs[0].Ok = &doc
	}

	w.Header().Add("Content-Type", "application/json")
	w.WriteHeader(200)
	json.NewEncoder(w).Encode(res)
}
Example #2
0
func Delete(w http.ResponseWriter, r *http.Request) {
	ctx := getContext(r)
	var err error
	var rev string

	if !ctx.exists {
		res := responses.NotFound()
		w.WriteHeader(res.Code)
		json.NewEncoder(w).Encode(res)
		return
	}

	if ctx.lastKey[0] == '_' {
		res := responses.BadRequest("you can't delete special keys")
		w.WriteHeader(res.Code)
		json.NewEncoder(w).Encode(res)
		return
	}

	if ctx.currentRev != "" && 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
	}

	rev, err = db.DeleteAt(ctx.path)
	if err != nil {
		log.Error("couldn't delete key at ", ctx.path, ": ", err)
		res := responses.NotFound()
		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})
}
Example #3
0
/*
   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})
}
Example #4
0
/*
   Currently _all_docs does not guarantee key order and should not be used for
   querying or anything. It is here just to provide PouchDB replication support.
*/
func AllDocs(w http.ResponseWriter, r *http.Request) {
	ctx := getContext(r)

	/* options */
	include_docs := flag(r, "include_docs")
	jsonKeys := param(r, "keys")
	// startkey := param(r, "startkey")
	// endkey := param(r, "endkey")
	// descending := flag(r, "descending")
	// skip := param(r, "skip")
	// limit := param(r, "limit")

	path := db.CleanPath(ctx.path)
	tree, err := db.GetTreeAt(path)
	if err != nil {
		res := responses.UnknownError(err.Error())
		w.WriteHeader(res.Code)
		json.NewEncoder(w).Encode(res)
		return
	}

	res := responses.AllDocs{
		TotalRows: 0,
		Offset:    0, // temporary
		Rows:      make([]responses.Row, 0),
	}

	if jsonKeys != "" {
		// in case "keys" was provided we can just pick the requested keys from our tree
		var keys []string
		err = json.Unmarshal([]byte(jsonKeys), &keys)
		if err != nil {
			res := responses.BadRequest()
			w.WriteHeader(res.Code)
			json.NewEncoder(w).Encode(res)
			return
		}

		for _, id := range keys {
			var doc interface{}
			var ok bool
			if doc, ok = tree[id]; !ok || strings.HasPrefix(id, "_") {
				res.Rows = append(res.Rows, responses.Row{
					Key:   id,
					Error: (responses.NotFound()).Reason,
				})
			}
			rev := string(db.GetRev(path + "/" + db.EscapeKey(id)))
			row := responses.Row{
				Id:    id,
				Key:   id,
				Value: map[string]interface{}{"rev": rev},
			}
			if include_docs {
				row.Doc = doc.(map[string]interface{})
				row.Doc["_id"] = id
				row.Doc["_rev"] = rev
				delete(row.Doc, "_val") // pouchdb doesn't like top-level keys starting with _
			}
			res.Rows = append(res.Rows, row)
			res.TotalRows += 1
		}
	} else {
		// otherwise iterate through the entire tree
		for id, doc := range tree {
			if strings.HasPrefix(id, "_") {
				continue
			}

			rev := string(db.GetRev(path + "/" + db.EscapeKey(id)))
			row := responses.Row{
				Id:    id,
				Key:   id,
				Value: map[string]interface{}{"rev": rev},
			}
			if include_docs {
				row.Doc = doc.(map[string]interface{})
				row.Doc["_id"] = id
				row.Doc["_rev"] = rev
				delete(row.Doc, "_val") // pouchdb doesn't like top-level keys starting with _
			}
			res.Rows = append(res.Rows, row)
			res.TotalRows += 1
		}
	}

	w.Header().Add("Content-Type", "application/json")
	w.WriteHeader(200)
	json.NewEncoder(w).Encode(res)
}
Example #5
0
func Get(w http.ResponseWriter, r *http.Request) {
	ctx := getContext(r)

	/* here we handle special endpoints that, in CouchDB, refer to a database,
	   but here are treated as documents (or better, subtrees) methods */
	if ctx.wantsDatabaseInfo {
		DatabaseInfo(w, r)
		return
	} else if ctx.lastKey == "_changes" {
		Changes(w, r)
		return
	} else if ctx.lastKey == "_all_docs" {
		AllDocs(w, r)
		return
	} else if ctx.lastKey == "_security" {
		ReadSecurity(w, r)
		return
	} else if ctx.lastKey == "_missing_revs" {
		return
	}

	var response []byte
	var err error

	if !ctx.exists {
		res := responses.NotFound()
		w.WriteHeader(res.Code)
		json.NewEncoder(w).Encode(res)
		return
	}

	if ctx.localDoc {
		jsondoc, err := db.GetLocalDocJsonAt(ctx.path)
		if err != nil {
			// ctx.exists doesn't work for _local docs
			res := responses.NotFound()
			w.WriteHeader(res.Code)
			json.NewEncoder(w).Encode(res)
			return
		}

		w.WriteHeader(200)
		w.Header().Add("Content-Type", "application/json")
		w.Write(jsondoc)
		return
	}

	if ctx.wantsTree {
		var tree map[string]interface{}
		tree, err = db.GetTreeAt(ctx.path)
		if err == nil {
			tree["_id"] = ctx.lastKey
			tree["_rev"] = ctx.currentRev

			if flag(r, "revs") {
				var revs []string
				revs, err = db.RevsAt(ctx.path)
				if err == nil {
					var start int
					start, err = strconv.Atoi(strings.Split(revs[0], "-")[0])
					revids := make([]string, len(revs))
					for i, rev := range revs {
						revids[i] = strings.Split(rev, "-")[1]
					}
					tree["_revisions"] = responses.Revisions{
						Start: start,
						Ids:   revids,
					}
				}
			}

			if flag(r, "revs_info") {
				var revs []string
				revs, err = db.RevsAt(ctx.path)
				if err == nil {
					revsInfo := make([]responses.RevInfo, len(revs))
					i := 0
					for r := len(revs) - 1; r >= 0; r-- {
						revsInfo[i] = responses.RevInfo{
							Rev:    revs[r],
							Status: "missing",
						}
						i++
					}
					revsInfo[0].Status = "available"
					tree["_revs_info"] = revsInfo
				}
			}

			response, err = json.Marshal(tree)
		}
	} else {
		if ctx.lastKey == "_rev" {
			response = []byte(ctx.currentRev)
		} else {
			response, err = db.GetValueAt(ctx.path)
			if err != nil {
				res := responses.NotFound()
				w.WriteHeader(res.Code)
				json.NewEncoder(w).Encode(res)
				return
			}
		}
	}

	if err != nil {
		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")
	w.Write(response)
}