I'm trying to PUT my GridDB container (a simple container for users on my website) but it's having issues.
I've confirmed that the sample code for the go_client works so it's not an issue of improper build or anything of that sort.
func getAdminUsers(c echo.Context) error {
var tmp []interface{}
col, err1 := gridstore.GetContainer("users")
if err1 != nil {
fmt.Println("get container failed")
}
col.SetAutoCommit(true)
// Create normal query
query, err := col.Query("SELECT *")
if err != nil {
fmt.Println("create query failed")
}
//Execute query
rs, err := query.Fetch(true)
if err != nil {
fmt.Println("create rs from query failed")
}
for rs.HasNext() {
// Update row
rrow, err := rs.NextRow()
if err != nil {
fmt.Println("NextRow from rs failed")
}
tmp = rrow
fmt.Println("Person: name=", rrow[0], " status=", rrow[1], " count=", rrow[2], " lob=", rrow[3])
}
col.Commit()
fmt.Println(tmp)
return c.Render(http.StatusOK, "admin", "admin")
}
My container is properly being written but for some reason the querying isn't working. This is rather basic code so I expect there's some minor detail I'm missing somewhere.
As of now, I'm getting errors here: "get container failed". My error could either be from writing or querying, though I suspect it's from querying.
Can you try initializing your container by using the CreateContainerInfo instead? It will create container if it doesn't exist, but if it does exist, it's a more robust method of calling your container.
conInfo, _ := griddb_go.CreateContainerInfo("Users",
[][]interface{}{
{"email", griddb_go.TYPE_STRING},
{"password", griddb_go.TYPE_BLOB}},
griddb_go.CONTAINER_COLLECTION,
true)
col, _ := gridstore.PutContainer(conInfo, false)
Related
i've been trying to wrap my head around unit testing, dependency injection, tdd and all that stuff and i've been stuck on testing functions that make database calls, for example.
Let's say you have a PostgresStore struct that takes in a Database interface, which has a Query() method.
type PostgresStore struct {
db Database
}
type Database interface {
Query(query string, args ...interface{}) (*sql.Rows, error)
}
And your PostgresStore has a GetPatients method, which calls database query.
func (p *PostgresStore) GetPatients() ([]Patient, error) {
rows, err := p.db.Query("SELECT id, name, age, insurance FROM patients")
if err != nil {
return nil, err
}
defer rows.Close()
items := []Patient{}
for rows.Next() {
var i Patient
if err := rows.Scan(
&i.ID,
&i.Name,
&i.Surname,
&i.Age,
&i.InsuranceCompany,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
In the real implementation, you would just pass a *sql.DB as Database argument, but how would you guys write a unit test with a fake database struct?
let me try to clarify some of your doubts. First of all, I'm gonna share a working example to better understand what's going on. Then, I'm gonna mention all of the relevant aspects.
repo/db.go
package repo
import "database/sql"
type Patient struct {
ID int
Name string
Surname string
Age int
InsuranceCompany string
}
type PostgresStore struct {
// rely on the generic DB provided by the "sql" package
db *sql.DB
}
func (p *PostgresStore) GetPatient(id int) ([]Patient, error) {
rows, err := p.db.Query("SELECT id, name, age, insurance FROM patients")
if err != nil {
return nil, err
}
defer rows.Close()
items := []Patient{}
for rows.Next() {
var i Patient
if err := rows.Scan(
&i.ID,
&i.Name,
&i.Surname,
&i.Age,
&i.InsuranceCompany,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
Here, the only relevant change is how you define the PostgresStore struct. As the db field, you should rely on the generic DB provided by the database/sql package of the Go Standard Library. Thanks to this, it's trivial to swap its implementation with a fake one, as we're gonna see later.
Please note that in the GetPatient method you're accepting an id parameter but you're not using it. Your query is more suitable to a method like GetAllPatients or something like that. Be sure to fix it accordingly.
repo/db_test.go
package repo
import (
"testing"
"github.com/DATA-DOG/go-sqlmock"
"github.com/stretchr/testify/assert"
)
func TestGetPatient(t *testing.T) {
// 1. set up fake db and mock
db, mock, err := sqlmock.New()
if err != nil {
t.Fatalf("err not expected: %v", err)
}
// 2. configure the mock. What we expect (query or command)? The outcome (error vs no error).
rows := sqlmock.NewRows([]string{"id", "name", "surname", "age", "insurance"}).AddRow(1, "john", "doe", 23, "insurance-test")
mock.ExpectQuery("SELECT id, name, age, insurance FROM patients").WillReturnRows(rows)
// 3. instantiate the PostgresStore with the fake db
sut := &PostgresStore{
db: db,
}
// 4. invoke the action we've to test
got, err := sut.GetPatient(1)
// 5. assert the result
assert.Nil(t, err)
assert.Contains(t, got, Patient{1, "john", "doe", 23, "insurance-test"})
}
Here, there are a lot to cover. First, you can check the comments within the code that give you a better idea of each step. In the code, we're relying on the package github.com/DATA-DOG/go-sqlmock that allows us to easily mock a database client.
Obviously, the purpose of this code is to give a general idea on how to implement your needs. It can be written in a better way but it can be a good starting point for writing tests in this scenario.
Let me know if this helps, thanks!
Is there a way I can set gorm to time out after a configurable period of time when running a long query? I am using mssql. I have looked through the documentation and haven't discovered a way yet.
This code seems to work for me and is pretty clean. Just use transactions I guess.
//Gorm query below
query = query.Where(whereClause)
//Set up Timeout
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
var x sql.TxOptions
db := query.BeginTx(ctx, &x)
defer db.Commit()
// Execute the query
if err := db.Find(*results).Error; err != nil {
return err
}
By using WithContext from *gorm.DB you can pass a Timeout Context to Gorm:
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
var products []Product
db.WithContext(ctx).Find(&products)
gorm at the moment seems is not implementing any query that accepts as a parameter a context.Context, as e.g. QueryRowContext is doing.
You can create a workaround and use a Context to make "expirable" your query.
type QueryResponse struct {
MyResult *MyResult
Error error
}
func queryHelper(ctx context.Context) <- chan *QueryResponse {
chResult := make(chan *QueryResponse, 1)
go func() {
//your query here
//...
//blah blah check stuff do whatever you want
//err is an error that comes from the query code
if err != nil {
chResult <- &QueryResponse{nil, err}
return
}
chResult <- &QueryResponse{queryResponse, nil}
} ()
return chResult
}
func MyQueryFunction(ctx context.Context) (*MyResult, error) {
select {
case <-ctx.Done():
return nil, fmt.Errorf("context timeout, query out of time")
case res := <-queryHelper(ctx):
return res.MyResult, res.Error
}
}
And then in your upper function, whatever is it you can create a context and pass it to the MyQueryFunction. If the query exceeds the time you have set an error is raised and you should (must) check it.
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
res, err := MyQueryFunction(ctx)
if err != nil {
fmt.Printf("err %v", err)
} else {
fmt.Printf("res %v", res)
}
Of course, it is an example, is not considering a lot of use cases and a proper implementation inside the framework must be preferred.
I am working on a multi tenant application, I need to query a particular user from a KIND and From Particular Namespace.
I am able to get the values from default Namespace.the package i am using here is "google.golang.org/appengine/datastore"
q := datastore.NewQuery(ENTITYNAME).Filter("Name =", ed.Expense.Name)
var expenses []ExpenseEntiry
return q.GetAll(ed.Ctx, &expenses)
The namespace value is not part of the query (it's not a property of the query). The namespace comes from the context which you pass when executing the query, e.g. to Query.GetAll().
If you have a context (you do as you pass it to q.GetAll()), you can create a derivative context with a given namespace using the appengine.Namespace() function.
For example:
ctx2, err := appengine.Namespace(ed.Ctx, "mynamespace")
// check err
And use this new context to pass to Query.GetAll():
return q.GetAll(ctx2, &expenses)
It is rare that you need to create a new context with a different namespace, ed.Ctx should already be a context with the right namespace. So when / where you create ed.Ctx, you should already apply the namespace there, so you can avoid "accidental" exposure of data of other tenants (which is a major security issue).
If you are using the old lib: google.golang.org/appengine/datastore, then you need to create the context with the namespace:
ctx2, err := appengine.Namespace(ed.Ctx, "mynamespace")
if err != nil {
return err
}
But you WANT to be using the latest lib: cloud.google.com/go/datastore. The Namespace can be set directly on the Query object. This is new. You must then run the query using datastoreClient.Run(ctx, query).
func deleteTestNamespace(ctx context.Context, namespaces string) error {
dsClient, err := datastore.NewClient(ctx, log, datastore.Config{...})
err := dsClient.DeleteMulti(ctx, keys[i:i+chunk])
if err != nil {
return err
}
var keys []*datastore.Key
for _, kind := range envKinds {
// Get all keys
query := datastore.NewQuery(kind).KeysOnly().Namespace(namespace)
it := dsClient.Run(ctx, query)
for {
var key datastore.Key
_, err := it.Next(&key)
if err == iterator.Done {
break
}
if err != nil {
return err
}
keys = append(keys, &key)
}
// Delete all records in chunks of 500 or less
for i := 0; i < len(keys); i += 500 {
chunk := min(len(keys)-i, 500)
err := dsClient.DeleteMulti(ctx, keys[i:i+chunk])
if err != nil {
return err
}
}
}
return nil
}
func min(num1 int, num2 int) int {
if num1 < num2 {
return num1
}
return num2
}
I followed a thread in here and came up with this
var b Button
queryErr := connection.QueryRow("SELECT id_printer, name, has_children FROM button WHERE id_parent IS NULL;").Scan(&b.ID, &b.Name, &b.Children)
if queryErr != nil {
response, err := json.MarshalIndent(b, "", " ")
fmt.Fprint(w, string(response))
if err != nil {
log.Println("Error on jsonmarshalindent Starter")
}
} else {
log.Println("Error on queryErr starter")
log.Println(queryErr)
fmt.Fprint(w, "Error getting starter button")
}
it has 2 problems:
It breaks on b.Children
I don't know how to make it dynamically. for instance, the query returns 3 rows, but it depends on the company's client, it can be 3 or any number.
the struct is
type Starter struct {
Buttons []Button `json:buttons`
}
type Button struct {
ID int `json:id`
Name string `json:name`
Children bool `json:children`
}
Can someone shed some light in this please?
For the bool, it depends on the type of the actual column. If it isn't boolean, it won't map directly.
You may need to store in a temporary variable first and then translate and assign to the field in Button to match.
As for reading all rows, here's an example adapted from the docs.
You use Query instead of QueryRow to get all rows.
rows, err := db.Query("SELECT id_printer, name, has_children FROM button WHERE id_parent IS NULL;")
if err != nil {
log.Fatal(err)
}
defer rows.Close()
var buttons []Button
for rows.Next() {
var b Button
if err := rows.Scan(&b.ID, &b.Name, &b.Children); err != nil {
log.Fatal(err)
}
buttons = append(buttons,b)
}
// At this point, you have all your rows in the "buttons" variable
Using templates with delimiters works fine when using template.New("...").Delims("[[", "]]").Parse()
However, I cannot figure out how to get to the same result with template.ParseFiles()
tmpl, err := template.ParseFiles("base.tmpl", "homepage/inner.tmpl")
if err != nil { panic(err) }
tmpl.Delims("[[", "]]")
p := new(Page)
err = tmpl.Execute(os.Stdout, p)
if err != nil { panic(err) }
I have no errors, but the Delimiters are not changed.
tmpl, err := template.ParseFiles("base.tmpl", "homepage/inner.tmpl")
t := tmpl.Lookup("base.tmpl").Delims("[[", "]]")
p := new(Page)
err = t.Execute(os.Stdout, p)
if err != nil { panic(err) }
This leads to the same result.
In case this is relevant, my need is to embed a small angular app in a particular page of my site.
Also, I have a base template with a common HTML structure that I combine with a page-specific template with ParseFiles(), leading to this layout :
/templates/base.tmpl
/templates/homepage/inner.tmpl
/templates/otherpage/inner.tmpl
Is this possible at all ? If so, what am I doing wrong ?
Create a dummy template, set the delimiters and then parse the files:
tmpl, err := template.New("").Delims("[[", "]]").ParseFiles("base.tmpl", "homepage/inner.tmpl")
This aspect of the API is quirky and not very obvious. The API made more sense in the early days when the template package had the additional Set type