How do I create a child entity in Golang AppEngine? - google-app-engine

I'm attempting to create a Child entity based on the docs here: https://cloud.google.com/appengine/docs/standard/python/datastore/entities
When I intiate the user instance, I'm getting an error "issing ',' in argument list" even though there is only one agument.
When I attempt to assign the parent value to Prospect on initiation, I get this error:
expected '==', found '='
func sign(w http.ResponseWriter, r *http.Request) {
c := appengine.NewContext(r)
zip, err := strconv.Atoi(r.FormValue("zip"))
//user := user.Current(c)
user = Client(name: "Bryan")
// error thrown "missing ',' in argument list"
p = Prospect(parent=user )
// error thrown: expected '==', found '='
p := Prospect{
Name: r.FormValue("name"),
Zip: zip,
Date: time.Now(),
}
It looks just like the examples in the documentation, what am I doing wrong?

When using the App Engine docs and writing in Go, you constantly get redirected to the Python docs. It is the most annoying thing on the planet. Always check the URL you are at after navigation and clicking links. If you are in the right place, you should find "go" in the URL somewhere. If it shows python or java, replace it with go in the URL and hope it exists.
So the page you really want is this: https://cloud.google.com/appengine/docs/standard/go/datastore/entities

Related

Is there a replacement for appengine/user in Go 1.12?

Since migrating to Go 1.12 from 1.9, appengine/user.Current() returns an empty User
Originally set this up using this tutorial: https://cloud.google.com/appengine/docs/standard/go/users/
I've tried both the recommended change to use http.Request.Context() instead of appengine.NewContext() and still trying appengine.NewContext() but now calls to Current() return an empty user object (not nil, empty)
I plan on moving to a different authentication method since it's recomended to move away from the appengine package, but this was a "quick and dirty" way to secure the app for now as this only has two users (my wife and me) for the moment.
func getContext(r *http.Request) error {
ctx = r.Context()
var w http.ResponseWriter
cu, err := Current()
.
.
.
}

c.Infof undefined (type context.Context has no field or method Infof) google.golang.org/appengine/log error

In the Go Runtime i used the method c.Infof to log messages , but it fails to compile with the following error
c.Infof undefined (type context.Context has no field or method Infof) .
The Error clearly tells that the app engine context returned from c := appengine.NewContext(r) is of type context.Context and it doesnt have a method c.Infof on it. But contrary to this the documentation in https://godoc.org/google.golang.org/appengine/log suggests that this method exists . Another point to note , The method existed on the context returned by "appengine" (import "appengine" ) package , and this doesnt seem to exist on the context returned by the new package google.golang.org/appengine , what is c.Infof equivalent on the new Context of type context.Context returned by package "google.golang.org/appengine" ?
The example in the package documentation is not correct.
Use the log package functions to write to the App Engine log. Here's the corrected example:
c := appengine.NewContext(r)
query := &log.Query{
AppLogs: true,
Versions: []string{"1"},
}
for results := query.Run(c); ; {
record, err := results.Next()
if err == log.Done {
log.Infof(c, "Done processing results")
break
}
if err != nil {
log.Errorf(c, "Failed to retrieve next log: %v", err)
break
}
log.Infof(c, "Saw record %v", record)
}
The example in the package documentation was copied from the App Engine Classic package, but not updated to use the new functions. I suggest reporting this to the App Engine Team.

How to create a task for app engine task queue in Go?

Google's docs omit the most important aspect: How a Task is created. Can anybody complete the sample code:
import (
"appengine/datastore"
"appengine/taskqueue"
)
func f(c appengine.Context) {
err := datastore.RunInTransaction(c, func(c appengine.Context) error {
t := ... // WHY DOES GOOGLE NOT EXPLAIN THIS PART???
// Use the transaction's context when invoking taskqueue.Add.
_, err := taskqueue.Add(c, t, "")
// ...
})
// ...
}
I think what you need is described in the docs for datastore transactions.
So the missing code to create a task is:
t := &taskqueue.Task{Path: "/path/to/workertask"}
The reference for the Task type shows that Task is a struct with 10 or so fields, so you probably don't want to construct a Task yourself. However, it also provides the NewPOSTTask function (just below that):
NewPOSTTask creates a Task that will POST to a path with the given form data
I agree the documentation could be much better though.

Getting weird error on datastore.Query.Run(c).Next(x)

I have not run into this particular error before, and when I Google it I can't find anything at all out there that even mentions it. I am using Go language, performing a standard datastore query like I have many times before using an iterator, and I get this error: "proto: required fields not set in datastore.QueryResult". See my code below, any ideas?
k, err := datastore.NewQuery("QBConnection").
Ancestor(datastore.NewKey(c, "Company", "", x.CompanyID, nil)).
Limit(1).
Run(c).
Next(x)
if err != nil {
if _, ok := err.(*datastore.ErrFieldMismatch); ok { err = nil } //ignore it if we can't load some fields
if err == datastore.Done { err = nil } //If we don't find it in datastore, then just return nil
if err != nil {return err}
}
I figure it out for my case, at least. I'm still unsure exactly what the error message is supposed to mean, and I got a different error when I ran this in the live environment (which led to my answer). On the live site it told me "ancestor missing id/name"... long story short, my x.CompanyID variable was not set properly before running the query. Not sure why the error messages were so different on dev/live, and sure would have helped to get the missing id/name error on my dev server... but oh well, problem solved.

Upload file in GAE Go

I am trying to upload a file in my GAE app. How do I the upload of a file in Google App Engine using Go and using the r.FormValue()?
You have to go through the Blobstore Go API Overview to get an idea and there is a full example on how could you store & serve user data on Google App Engine using Go.
I would suggest you to do that example in a completely separate application, so you'll be able to experiment with it for a while before trying to integrate it to your already existing one.
I managed to solve my problem by using the middle return param, "other". These code below are inside the upload handler
blobs, other, err := blobstore.ParseUpload(r)
Then assign corresponding formkey
file := blobs["file"]
**name := other["name"]** //name is a form field
**description := other["description"]** //descriptionis a form field
And use it like this in my struct value assignment
newData := data{
Name: **string(name[0])**,
Description: **string(description[0])**,
Image: string(file[0].BlobKey),
}
datastore.Put(c, datastore.NewIncompleteKey(c, "data", nil), &newData )
Not 100% sure this is the right thing but this solves my problem and it is now uploading the image to blobstore and saving other data and blobkey to datastore.
Hope this could help others too.
I have tried the full example from here https://developers.google.com/appengine/docs/go/blobstore/overview, and it worked fine doing the upload in blobstore and serving it.
But inserting extra post values to be saved somewhere in the datastore erases the values of "r.FormValue() "? Please refer to the code below
func handleUpload(w http.ResponseWriter, r *http.Request) {
c := appengine.NewContext(r)
//tried to put the saving in the datastore here, it saves as expected with correct values but would raised a server error.
blobs, _, err := blobstore.ParseUpload(r)
if err != nil {
serveError(c, w, err)
return
}
file := blobs["file"]
if len(file) == 0 {
c.Errorf("no file uploaded")
http.Redirect(w, r, "/", http.StatusFound)
return
}
// a new row is inserted but no values in column name and description
newData:= data{
Name: r.FormValue("name"), //this is always blank
Description: r.FormValue("description"), //this is always blank
}
datastore.Put(c, datastore.NewIncompleteKey(c, "Data", nil), &newData)
//the image is displayed as expected
http.Redirect(w, r, "/serve/?blobKey="+string(file[0].BlobKey), http.StatusFound)
}
Is it not possible to combine the upload with regular data? How come the values of r.FormValue() seems to disappear except for the file (input file type)? Even if I would have to force upload first before associating the blobkey, as the result of the upload, to other data, it would not be possible since I could not pass any r.FormValue() to the upload handler(which like I said becomes empty, or would raised an error when accessed prior the blobs, _, err := blobstore.ParseUpload(r) statement). I hope someone could help me solve this problem. Thank you!
In addition to using the Blobstore API, you can just use the Request.FormFile() method to get the file upload content. Use net\http package documentation for additional help.
Using the Request directly allows you to skip setting up an blobstore.UploadUrl() before handling the upload POST message.
A simple example would be:
func uploadHandler(w http.ResponseWriter, r *http.Request) {
// Create an App Engine context.
c := appengine.NewContext(r)
// use FormFile()
f, _, err := r.FormFile("file")
if err != nil {
c.Errorf("FormFile error: %v", err)
return
}
defer f.Close()
// do something with the file here
c.Infof("Hey!!! got a file: %v", f)
}

Resources