Getting weird error on datastore.Query.Run(c).Next(x) - google-app-engine

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.

Related

Proper error handling when no entity is found

I'm writing a Go application which has a database package. Now in database package there are a couple of methods which you can call for getting an entity based on some fields.
I was wondering what is the best practice for error handling in Go when no entity is found. Should I return errors in my own database package or return nil as the value?
I know google's datastore returns error when no entity is found.
Right now I'm using gorm and it also returns error when no entity is found.
I'm wondering you can simply return nil as the value instead of returning an error. Am I missing a point?
If you are using gorm, there is a function specifically for that; namely,
// IsRecordNotFoundError returns true if error contains a RecordNotFound error
func IsRecordNotFoundError(err error) bool {}
You can use it as following:
err = db.Find(object).Error
if err != nil {
if gorm.IsRecordNotFoundError(err) {
// handle object not found
} else {
return err
}
}
and for your question:
I'm wondering you can simply return nil as the value instead of returning an error
It really depends on your design; so if you want to separate your database layer, then you should still inform the user about this error using your own exported error type and let them handle it as they wish.
you can use this code:
user:=&User{}
if db.First(&user, "username=?", mobile).RecordNotFound() {
return nil
}
return user

How do I create a child entity in Golang AppEngine?

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

Golang: Accessing Google App Engine CloudSQL

New question: Everything I can find on Google suggests that the CloudSQL connector should be working, with the exception of the source code itself (and importing the SDK's cloudsql doesn't work). Is there some place I can find this updated library, if it exists?
---EDIT: I have my answer: it seems the problem is with CloudSQL itself rather than with the driver or something similar.---
I'm trying to access CloudSQL from a Google App Engine Go program.
I've tried both go-sql-driver/mysql and ziutek/mymysql, using Go 1.2.1 and Go 1.4.2. I've tried both the go get version of go-sql-driver and cloned it directly from Github. I've tried both the App Engine installer and the archive.
Every time I try to access a database with my application, the resulting webpage states:
cloudsql: not supported in dev yet
I've seen the other similar question here, tried everything noted there, none of it worked.
The code in question:
import (
_ "github.com/go-sql-driver/mysql"
_ "appengine/cloudsql"
"database/sql"
"net/http"
)
func adminLogin(w http.ResponseWriter, r *http.Request) {
username := formatter(r.FormValue("username"))
password := formatter(r.FormValue("password"))
db, err := sql.Open("mysql",
"root:password#cloudsql(ws-virtual-classroom:database)/logins") // And all the variations on that string I could think of...
defer db.Close()
if err != nil {
log.Print(err)
} else {
rows, err := db.Query("SELECT username FROM admin_logins WHERE username=? AND password=? LIMIT 1", username, password)
defer rows.Close()
if err != nil {
log.Print(err)
} else {
var user string
for rows.Next() {
err = rows.Scan(&user)
if err != nil {
log.Print(err)
} else {
makeCookie(w, r, user, true, true)
}
}
}
}
teachersHome(w, r)
}
The result (this is displayed in my browser after I submit the login form):
the runtime process gave a bad HTTP response: ''
2015/05/17 01:53:06 cloudsql: not supported in dev yet
2015/05/17 01:53:06 http: panic serving 127.0.0.1:56970: runtime error: invalid memory address or nil pointer dereference
goroutine 12 [running]:
net/http.func·011()
/tmp/appengine/go_appengine/goroot/src/net/http/server.go:1130 +0xbb
database/sql.(*Rows).Close(0x0, 0x0, 0x0)
/tmp/appengine/go_appengine/goroot/src/database/sql/sql.go:1659 +0x31
main57750.adminLogin(0x7f76a72ef5d8, 0xc208045860, 0xc2080c4820)
main.go:208 +0x25a
net/http.HandlerFunc.ServeHTTP(0x927c78, 0x7f76a72ef5d8, 0xc208045860, 0xc2080c4820)
/tmp/appengine/go_appengine/goroot/src/net/http/server.go:1265 +0x41
github.com/gorilla/mux.(*Router).ServeHTTP(0xc20800c730, 0x7f76a72ef5d8, 0xc208045860, 0xc2080c4820)
/home/daniel/go/src/github.com/gorilla/mux/mux.go:98 +0x297
net/http.(*ServeMux).ServeHTTP(0xc20803a6f0, 0x7f76a72ef5d8, 0xc208045860, 0xc2080c4820)
/tmp/appengine/go_appengine/goroot/src/net/http/server.go:1541 +0x17d
appengine_internal.handleFilteredHTTP(0x7f76a72ef5d8, 0xc208045860, 0xc2080c4820)
/tmp/appengine/go_appengine/goroot/src/appengine_internal/api_dev.go:98 +0x413
net/http.HandlerFunc.ServeHTTP(0x927248, 0x7f76a72ef5d8, 0xc208045860, 0xc2080c4820)
/tmp/appengine/go_appengine/goroot/src/net/http/server.go:1265 +0x41
net/http.serverHandler.ServeHTTP(0xc208042540, 0x7f76a72ef5d8, 0xc208045860, 0xc2080c4820)
/tmp/appengine/go_appengine/goroot/src/net/http/server.go:1703 +0x19a
net/http.(*conn).serve(0xc2080457c0)
/tmp/appengine/go_appengine/goroot/src/net/http/server.go:1204 +0xb57
created by net/http.(*Server).Serve
/tmp/appengine/go_appengine/goroot/src/net/http/server.go:1751 +0x35e
This is when running it with goapp serve. If I deploy it I seem to have the same problem--the resulting page is blank rather than showing that text, but the log error messages are the same.
The updated library is here: https://godoc.org/google.golang.org/appengine. It doesn't have CloudSQL though. Perhaps you should just use a regular mysql connection locally:
func dialSQL() (*sql.DB, error) {
if appengine.IsDevAppServer() {
// or sql.Open("mysql", "user-name:password#ip-address-of-google-cloud-sql-instance/dbname")
return sql.Open("mysql", "user:password#/dbname")
}
return sql.Open("mysql", "cloudsql:my-instance*dbname/user/passwd")
}
Using a local database is usually the best option because connecting to a live database can be super dangerous. For example it's all too easy to accidentally run your tests against your production database and drop everything.
Nevertheless, Google has instructions for connecting to CloudSQL here: https://cloud.google.com/sql/docs/introduction. The instructions for 3rd party tools will work for Go too.
I don't think cloudsql does anything? The source just looks like it returns that error no matter what lol
https://code.google.com/p/appengine-go/source/browse/appengine/cloudsql/cloudsql.go
// Dial connects to the named Cloud SQL instance.
func Dial(instance string) (net.Conn, error) {
return connect(instance)
}
https://code.google.com/p/appengine-go/source/browse/appengine/cloudsql/cloudsql_dev.go
func connect(instance string) (net.Conn, error) {
return nil, errors.New("cloudsql: not supported in dev yet")
}

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.

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