Example #1
0
func nutsHandler(c appengine.Context, w http.ResponseWriter, r *http.Request) {
	d := make(ContentData)
	apiCall := r.Header.Get("Accept") == "application/json"

	// TODO: no need to load all, then render all - replace with chunking
	var nuts []gonuts.Nut
	var err error
	var title string
	vendor := r.URL.Query().Get(":vendor")
	q := r.URL.Query().Get("q")
	if vendor != "" {
		title = fmt.Sprintf("%s's Nuts", vendor)
		_, err = datastore.NewQuery("Nut").Filter("Vendor=", vendor).Order("Name").GetAll(c, &nuts)
	} else if q == "" {
		title = "All Nuts"
		_, err = datastore.NewQuery("Nut").Order("Vendor").Order("Name").GetAll(c, &nuts)
	} else {
		title = fmt.Sprintf("Search %q", q)
		res, err := gonuts.SearchIndex(c, q)
		gonuts.LogError(c, err)
		keys := make([]*datastore.Key, len(res))
		for i, pair := range res {
			keys[i] = gonuts.NutKey(c, pair[0], pair[1])
		}
		nuts = make([]gonuts.Nut, len(keys))
		err = datastore.GetMulti(c, keys, nuts)
	}
	gonuts.LogError(c, err)
	d["Nuts"] = nuts
	status := http.StatusOK
	if len(nuts) == 0 {
		status = http.StatusNotFound
	}

	if apiCall {
		d["Message"] = title
		ServeJSON(w, status, d)
		return
	}

	var content bytes.Buffer
	gonuts.PanicIfErr(Base.ExecuteTemplate(&content, "nuts.html", d))

	bd := BaseData{
		Tabtitle: title,
		Title:    title,
		Content:  template.HTML(content.String()),
	}

	w.WriteHeader(status)
	gonuts.PanicIfErr(Base.Execute(w, &bd))
}
Example #2
0
func nutCreateHandler(c appengine.Context, w http.ResponseWriter, r *http.Request) {
	d := make(ContentData)
	ct := r.Header.Get("Content-Type")
	putNut := ct == "application/zip"

	if !putNut {
		err := fmt.Errorf(`Unexpected Content-Type %q, should be "application/zip".`, ct)
		ServeJSONError(w, http.StatusNotAcceptable, err, d)
		return
	}

	vendor := r.URL.Query().Get(":vendor")
	name := r.URL.Query().Get(":name")
	ver := r.URL.Query().Get(":version")

	if vendor == "" || !nutp.VendorRegexp.MatchString(vendor) || name == "" || (ver != "" && !nutp.VersionRegexp.MatchString(ver)) {
		err := fmt.Errorf("Invalid vendor %q, name %q or version %q.", vendor, name, ver)
		ServeJSONError(w, http.StatusBadRequest, err, d)
		return
	}

	// extract token from request
	token := r.URL.Query().Get("token")
	if token == "" {
		ServeJSONError(w, http.StatusForbidden, fmt.Errorf("Can't find 'token' in get parameters."), d)
		return
	}

	// find user by token
	q := datastore.NewQuery("User").KeysOnly().Filter("Token=", token)
	userKeys, err := q.Limit(2).GetAll(c, nil)
	if err != nil || len(userKeys) != 1 {
		if err == nil || err == datastore.ErrNoSuchEntity {
			err = fmt.Errorf("Can't find user with token %q.", token)
		}
		ServeJSONError(w, http.StatusForbidden, err, d)
		return
	}
	userID := userKeys[0].StringID()

	// user should belong to vendor
	v := gonuts.Vendor{}
	err = datastore.Get(c, gonuts.VendorKey(c, vendor), &v)
	if err == datastore.ErrNoSuchEntity {
		err = fmt.Errorf("Can't find vendor %q.", vendor)
		ServeJSONError(w, http.StatusNotFound, err, d)
		return
	}
	if err != nil {
		ServeJSONError(w, http.StatusInternalServerError, err, d)
		return
	}
	found := false
	for _, id := range v.UserStringID {
		if id == userID {
			found = true
			break
		}
	}
	if !found {
		err = fmt.Errorf("You don't have publish access to vendor %q.", vendor)
		ServeJSONError(w, http.StatusForbidden, err, d)
		return
	}

	// nut version should not exist
	nutKey := gonuts.NutKey(c, vendor, name)
	nut := gonuts.Nut{Vendor: vendor, Name: name}
	versionKey := gonuts.VersionKey(c, vendor, name, ver)
	version := gonuts.Version{Vendor: vendor, Name: name, Version: ver, CreatedAt: time.Now()}
	err = datastore.Get(c, versionKey, &version)
	if err != nil && err != datastore.ErrNoSuchEntity {
		ServeJSONError(w, http.StatusInternalServerError, err, d)
		return
	}
	if err == nil {
		ServeJSONError(w, http.StatusConflict, fmt.Errorf("Nut %s/%s version %s already exists.", vendor, name, ver), d)
		return
	}

	// read nut from request body
	nf := new(nutp.NutFile)
	b, err := ioutil.ReadAll(r.Body)
	if err == nil {
		_, err = nf.ReadFrom(bytes.NewReader(b))
	}
	if err != nil {
		ServeJSONError(w, http.StatusBadRequest, err, d)
		return
	}
	nut.Doc = nf.Doc
	version.Doc = nf.Doc
	version.Homepage = nf.Homepage
	version.VersionNum = nf.Version.Major*1000000 + nf.Version.Minor*1000 + nf.Version.Patch // for sorting

	// check vendor, name and version match
	if nf.Vendor != vendor || nf.Name != name || nf.Version.String() != ver {
		err = fmt.Errorf("Nut vendor %q, name %q and version %q from URL don't match found in body: %q %q %q.",
			vendor, name, ver, nf.Vendor, nf.Name, nf.Version.String())
		ServeJSONError(w, http.StatusBadRequest, err, d)
		return
	}

	// check nut
	errors := nf.Check()
	if len(errors) != 0 {
		err = fmt.Errorf("%s", strings.Join(errors, "\n"))
		ServeJSONError(w, http.StatusBadRequest, err, d)
		return
	}

	// store nut blob
	bw, err := blobstore.Create(c, ct)
	if err == nil {
		_, err = bw.Write(b)
		if err == nil {
			err = bw.Close()
		}
	}
	if err != nil {
		ServeJSONError(w, http.StatusInternalServerError, err, d)
		return
	}

	// store nut version
	blobKey, err := bw.Key()
	if err == nil {
		version.BlobKey = blobKey
		_, err = datastore.Put(c, versionKey, &version)
	}
	if err != nil {
		ServeJSONError(w, http.StatusInternalServerError, err, d)
		return
	}

	// store nut with new doc
	_, err = datastore.Put(c, nutKey, &nut)
	if err != nil {
		ServeJSONError(w, http.StatusInternalServerError, err, d)
		return
	}

	// update search index
	err = gonuts.AddToSearchIndex(c, &nut)
	gonuts.LogError(c, err)

	// done!
	d["Message"] = fmt.Sprintf("Nut %s/%s version %s published.", vendor, name, ver)
	ServeJSON(w, http.StatusCreated, d)
	return
}