Go chi renderer having difficult processing bound lists - go

Go here. Trying to get the chi renderer to return a list of Order struct instances and getting a compiler error that I don't understand:
package myapp
import (
"net/http"
"github.com/go-chi/render"
)
type Order struct {
OrderId string
Status string
}
func (*Order) Bind(r *http.Request) error {
return nil
}
func GetAllOrderByCustomerId(dbClient DbClient, customerId string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// fetch all customer orders from the DB
orders,err := dbClient.FetchAllOrdersByCustomerId(customerId)
if err != nil {
log.Error("unable to fetch orders for customer", err)
render.Render(w, r, NewInternalServerError(err))
return
}
render.Bind(r, &orders)
return
}
}
When I go to compile this code I get:
fizz/buzz/myapp/order_fetcher.go:136:20: cannot use &orders (type *[]Order) as type render.Binder in argument to render.Bind:
*[]Order does not implement render.Binder (missing Bind method)
So even though I've defined a Bind for Order, it doesn't seem to automatically apply that Bind to a collection/list of Orders.
Can anyone see what I'm missing? Some endpoints will only be returning a single Order, whereas others (like this one) need to be able to return a collection/list of Orders.

As in the example in chi repository, you have to create a helper method to render the list of something, in your case, list of orders.
First, you have to implement the render.Renderer method then create a helper method to build a list of render.Renderer.
I've adapted your code from the example here:
type Order struct {
OrderId string
Status string
}
// Render implement render.Renderer
func (*Order) Render(w http.ResponseWriter, r *http.Request) error {
// do something
return nil
}
// newOrderList is a helper method to make list of render.Renderer
func newOrderList(orders []*Order) []render.Renderer {
list := []render.Renderer{}
for _, order := range orders {
list = append(list, order)
}
return list
}
func GetAllOrderByCustomerId(dbClient DbClient, customerId string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// fetch all customer orders from the DB
orders, err := dbClient.FetchAllOrdersByCustomerId(customerId)
if err != nil {
log.Error("unable to fetch orders for customer", err)
render.Render(w, r, NewInternalServerError(err))
return
}
// render list of orders
render.RenderList(w, r, newOrderList(orders))
return
}
}

Related

gocql bind not working in the case of concurrent calls

I have been trying to reuse a the prepared query in concurrent calls.
type product struct {
query *gocql.Query
}
type resp struct {
Pk string
Product Product
Err error
}
func (p *product) A() {
...
respFromDB := make(chan resp, len(pks))
for _, pk := range pks {
go p.getAll(pk, resp)
}
}
func (p *product) getAll(pk string, product chan resp) {
var (
err error
prodResp resp
)
prod := Product{}
prodResp = resp{
Pk: pk,
}
err = p.preparedStatement.Bind(gtin13).Scan(&prod.Pk,...)
if err != nil {
prodResp.Err = err
product <- prodResp
return
}
prodResp.Product = prod
product <- prodResp
}
The pks are being passed to getAll function but the Bind function seems to not be bind every pk to the corresponding call.
Instead the some pk is being repeatedly binded.
Is there something wrong in this approach? Not sure why the pks are being binded to the corresponding calls.
The Query is created once in the main and injected for all further calls.

Trouble scanning data to exported structs

I'm working on my first Go application, and I'm trying to keep my files well organized, DRY, etc. I have a struct that represents the data I expect to receive from my SQL queries, defined in a closure environment that returns my request handlerFunc. Pared down version:
func (s *server) getComponents() http.HandlerFunc {
type component struct {
ID int `json:"_id,omitempty"`
Name string `json:"name"`
// ... many more fields
}
return func(res http.ResponseWriter, req *http.Request) {
rows, err := s.db.GetComponents()
defer rows.Close()
if err != nil {
s.testQueryError(err, "etc, etc")
return
}
var components []component
for rows.Next() {
var c component
rows.Scan(&c)
components = append(components, c)
}
data, _ := json.Marshal(components)
res.Write(data)
}
}
This implementation responds with the expected data. However, I expected that I may need this struct in other routes, and thought that it would be good practice to define this struct elsewhere and export it from a "models" directory/package. New attempt:
// app/models/models.go
package "models"
type Component struct {
ID int `json:"_id,omitempty"`
// ...
}
// app/routes.go
import "app/models"
func (s *server) getComponents() http.HandlerFunc {
return func(res http.ResponseWriter, req *http.Request) {
rows, err := s.db.GetComponents()
// ...
var components []models.Component
for rows.Next() {
var c models.Component
rows.Scan(&c)
components = append(components, c)
}
data, _ := json.Marshal(components)
res.Write(data)
}
}
This version does not properly Scan, and Postman receives all empty fields. I'm new to strongly typed languages like Go, so could anybody point me in the right direction on where I am going wrong? The package is being properly exported/imported, so why should it work any differently?

Golang handlers handling different types

These are AppHandlers from a pattern I found online while researching gorilla/mux. They part of a struct that satisfies http.Handler. If you notice, the following two blocks are exactly the same. Effectively, they could be passed the 'variant' ("flow" or "process") as a string.
func CreateFlow(a *AppContext, w http.ResponseWriter, r *http.Request) (int, error) {
highest, code, err := a.Create("flow", r)
if code != 200 || err != nil {
return code, err
}
b := new(bytes.Buffer)
json.NewEncoder(b).Encode(struct {
Highest int `json:"id"`
}{highest})
w.Header().Set("Content-Type", "application/json")
w.Write(b.Bytes())
return 200, nil
}
func CreateProcess(a *AppContext, w http.ResponseWriter, r *http.Request) (int, error) {
highest, code, err := a.Create("process", r)
if code != 200 || err != nil {
return code, err
}
b := new(bytes.Buffer)
json.NewEncoder(b).Encode(struct {
Highest int `json:"id"`
}{highest})
w.Header().Set("Content-Type", "application/json")
w.Write(b.Bytes())
return 200, nil
}
However, the following two blocks not only need the string, but they need a variable of the associated type ("Flow" and "Process") to successfully Unmarshal the hit I get from ElasticSearch. Other than that, they are Identical code.
func GetFlow(a *AppContext, w http.ResponseWriter, r *http.Request) (int, error) {
hit, code, err := a.GetByID("flow", mux.Vars(r)["id"], r)
if code != 200 {
return code, err
}
var flow Flow
err = json.Unmarshal(*hit.Source, &flow)
if err != nil {
return 500, err
}
flow.ESID = hit.Id
b := new(bytes.Buffer)
json.NewEncoder(b).Encode(flow)
w.Header().Set("Content-Type", "application/json")
w.Write(b.Bytes())
return 200, nil
}
func GetProcess(a *AppContext, w http.ResponseWriter, r *http.Request) (int, error) {
hit, code, err := a.GetByID("process", mux.Vars(r)["id"], r)
if code != 200 {
return code, err
}
var process Process
err = json.Unmarshal(*hit.Source, &process)
if err != nil {
return 500, err
}
process.ESID = hit.Id
b := new(bytes.Buffer)
json.NewEncoder(b).Encode(process)
w.Header().Set("Content-Type", "application/json")
w.Write(b.Bytes())
return 200, nil
}
I am not sure how to generalize this behavior in golang when there is a declared type involved. These handlers are all in the same package too, as I think that they are all accomplishing a similar task. I am very clearly repeating myself in code but I need advice on how I can improve. I've gone past "a little copying is better than a little dependency." but I am afraid because "reflection is never clear".
Here is an example of the declaration in main using one of these functions.
api.Handle("/flow/{id:[0-9]+}", handlers.AppHandler{context, handlers.GetFlow}).Methods("GET")
You can do it by passing in an exemplar of the necessary type, the same way that Unmarshal does it:
func GetFlow(a *AppContext, w http.ResponseWriter, r *http.Request) (int, error) {
return GetThing(a,w,r,"flow",new(Flow))
}
func GetProcess(a *AppContext, w http.ResponseWriter, r *http.Request) (int, error) {
return GetThing(a,w,r,"process",new(Process))
}
func GetThing(a *AppContext, w http.ResponseWriter, r *http.Request, t string, ob Elastible{}) (int, error) {
hit, code, err := a.GetByID(t, mux.Vars(r)["id"], r)
if code != 200 {
return code, err
}
err = json.Unmarshal(*hit.Source, ob)
if err != nil {
return 500, err
}
ob.SetESID(hit.Id)
b := new(bytes.Buffer)
json.NewEncoder(b).Encode(ob)
w.Header().Set("Content-Type", "application/json")
w.Write(b.Bytes())
return 200, nil
}
type Elastible interface {
SetESID(id ESIDType) // whatever type ESID is, not clear from example
}
func (f *Flow) SetESID(id ESIDType) {
f.ESID = id
}
This code is untested (because I don't have your struct defs or other dependent code) but I hope it gets the idea across.
Alright, I propose a solution that will give you the maximum code reuse and minimum code copying. This, in my opinion, is by far the most generic solution. We will also take into account the answer given by https://stackoverflow.com/users/7426/adrian to complete the solution. You only have to define a single function which will be a higher order function CreateHandler which will return a function of the following signature:
func(*AppContext, http.ResponseWriter, http.Request) (int, error).
This signature is the actual signature of the handler that is to be used as a mux end point. The solution involves defining a Handler type which is a struct having three fields:
• handlerType: Think of it as an enum having either a value of "CREATE" or "GET". This will decide which among the two blocks of code that you pasted in your question should we use.
• handlerActionName: This will tell the "CREATE" or "GET" which Elastible to use. Value should either be "flow" or "process".
• elastible: This will the Interface type Elastible that will have the SetESID function. We will use this to send our Flow or Process types to our Handler. Thus both Flow and Process should satisfy our interface.
This will make the solution even more generic and will only calling handler.elastible.SetESID() and we will have inserted the ESID irrespective of that fact the underlying type in 'elastible' can either be 'Flow' or a 'Process'
I also define a sendResponse(response interface{}) function that we will resuse to send the response. It acquires w http.ResponseWriter using closure. response can thus be anything, a
struct {
Highest int `json:"id"`
}{highest}
or a Flow or a Process. This will make this function generic too.
The complete solution would now be.
// This is the type that will be used to build our handlers.
type Handler struct {
handlerType string // Can be "CREATE" or "GET"
handlerActionName string // Can be "flow" or "process"
elastible Elastible // Can be *Flow or *Process
}
// Your ESID Type.
type ESIDType string
// Solution proposed by https://stackoverflow.com/users/7426/adrian.
type Elastible interface {
SetESID(id ESIDType)
}
// Make the Flow and Process pointers implement the Elastible interface.
func (flow *Flow) SetESID(id ESIDType) {
flow.ESID = id
}
func (process *Process) SetESID(id ESIDType) {
process.ESID = id
}
// Create a Higher Order Function which will return the actual handler.
func CreateHandler(handler Handler) func(*AppContext, http.ResponseWriter, http.Request) (int, error) {
return func(a *AppContext, w http.ResponseWriter, r http.Request) (int, error) {
// Define a sendResponse function so that we may not need to copy paste it later.
// It captures w using closure and takes an interface argument that we use to call .Encode() with.
sendResponse := func(response interface{}) (int, error) {
b := new(bytes.Buffer)
json.NewEncoder(b).Encode(response)
w.Header().Set("Content-Type", "application/json")
w.Write(b.Bytes())
return 200, nil
}
// Define these variables beforehand since we'll be using them
// in both the if and else block. Not necessary really.
var code int
var err error
// Check the handlerType. Is it create or get?
if handler.handlerType == "CREATE" {
var highest int
// Creates the thing using handler.handlerActionName which may be "flow" or "process"
highest, code, err = a.Create(handler.handlerActionName, r)
if code != 200 || err != nil {
return code, err
}
// Send the response using the above defined function and return.
return sendResponse(struct {
Highest int `json:"id"`
}{highest})
} else {
// This is GET handlerType.
var hit HitType
// Get the hit using again the handler.handlerActionName which may be "flow" or "process"
hit, code, err = a.GetByID(handler.handlerActionName, mux.Vars(r)["id"], r)
if code != 200 || err != nil {
return code, err
}
// Do the un-marshalling.
err = json.Unmarshal(*hit.Source, ob)
if err != nil {
return 500, err
}
// We have set the handler.elastible to be an interface type
// which will have the SetESID function that will set the ESID in the
// underlying type that will be passed on runtime.
// So the ESID will be set for both the Flow and the Process types.
// This interface idea was given inside an earlier answer by
// https://stackoverflow.com/users/7426/adrian
handler.elastible.SetESID(hit.id)
return sendResponse(handler.elastible)
}
}
}
And you would setup your mux end points using the following code.
// This was your first function. "CreateFlow"
api.Handle("/createFlow/{id:[0-9]+}", handlers.AppHandler{
context, CreateHandler(Handler{
elastible: &Flow{},
handlerActionName: "flow",
handlerType: "CREATE",
}),
}).Methods("GET")
// This was your second function. "CreateProcess"
api.Handle("/createProcess/{id:[0-9]+}", handlers.AppHandler{
context, CreateHandler(Handler{
elastible: &Process{},
handlerActionName: "process",
handlerType: "CREATE",
}),
}).Methods("GET")
// This was your third function. "GetFlow"
api.Handle("/getFlow/{id:[0-9]+}", handlers.AppHandler{
context, CreateHandler(Handler{
elastible: &Flow{},
handlerActionName: "flow",
handlerType: "GET",
}),
}).Methods("GET")
// This was your fourth function. "GetProcess"
api.Handle("/getProcess/{id:[0-9]+}", handlers.AppHandler{
context, CreateHandler(Handler{
elastible: &Process{},
handlerActionName: "process",
handlerType: "GET",
}),
}).Methods("GET")
Hope it helps!

Convert URL.Query (map of slices) to struct golang

It would be awesome to have a straight forward mapping from the standard library URL.Query() to an struct.
Query() returns a map like:
map[a:[aaaa] b:[bbbb] c:[cccc]]
The struct looks like:
type Thing struct {
A string
B string
C string
}
I've no idea why URL.Query returns a map with array elements inside tough. (well.. I know why but a GET is not likely to have duplicated params)
Please find below the complete example of parsing get query params directly in a golang struct and then sending the struct back as response
package main
import (
"log"
"net/http"
"encoding/json"
"github.com/gorilla/schema"
)
var decoder = schema.NewDecoder()
type EmployeeStruct struct {
MemberId string `schema:"memberId"`
ActivityType string `schema:"activityType"`
BusinessUnitCode int `schema:"businessUnitCode"`
}
func GetEmployee(w http.ResponseWriter, r *http.Request) {
var employeeStruct EmployeeStruct
err := decoder.Decode(&employeeStruct, r.URL.Query())
if err != nil {
log.Println("Error in GET parameters : ", err)
} else {
log.Println("GET parameters : ", employeeStruct)
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(employeeStruct)
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/GetEmployee", GetEmployee)
log.Fatal(http.ListenAndServe(":8080", mux))
}
Steps to execute & Test (Assuming you are saving above code in employee.go) :
Step 1 : go run employee.go
Step 2 : Open in browser http://localhost:8080/GetEmployee?memberId=123&activityType=Call&businessUnitCode=56
Step 3 : You should get below response in browser window
{
"MemberId": "123",
"ActivityType": "Call",
"BusinessUnitCode": 56
}
Step 4 : On console you should see below
GET parameters : {123 Call 56}
example:
filters={"reference":["docker.io/library/alpine:latest"]}
need url encode to:
filters=%7B%22reference%22%3A%5B%22docker.io%2Flibrary%2Falpine%3Alatest%22%5D%7D
and could use "github.com/gorilla/schema"
query := struct {
All bool
Filters map[string][]string `schema:"filters"`
Digests bool
Filter string
}{}
decoder := schema.NewDecoder()
decoder.Decode(&query, r.URL.Query())
As pointed out by #mh-cbon gorilla schema is the ultimate solution here.
Instead for obtaining the queryParams from the URL attribute.
func handleRequest(w http.ResponseWriter, r *http.Request) {
queryString := r.URL.Query()
//...parsing the Values -> map[string][]string
}
The approach of gorilla schema is to ship r.PostForm to the decode function.
func handleRequest(w http.ResponseWriter, r *http.Request) {
err := decoder.Decode(person, r.PostForm)
//...using reflect each struct's property can be called using
// the PostForm(url string, data url.Values) signature
fmt.Print(person.GoodJobGorilla)
}
Just parse the string to URL and after you can use the lib github.com/gorilla/schema to parse it :)
// Example to parse querystring to struct
package main
import (
"log"
"net/url"
"github.com/gorilla/schema"
)
type URLParams struct {
Code string `schema:"code"`
State string `schema:"state"`
}
func main() {
var (
params URLParams
decoder = schema.NewDecoder()
)
p := "https://www.redirect-url.com?code=CODE&state=RANDOM_ID"
u, _ := url.Parse(p)
err := decoder.Decode(&params, u.Query())
if err != nil {
log.Println("Error in Decode parameters : ", err)
} else {
log.Printf("Decoded parameters : %#v\n", params)
}
}
https://go.dev/play/p/CmuPhdKh6Yg
Using ggicci/httpin
Disclaimer: I'm the creator and maintainer of this package.
httpin helps you easily decoding HTTP request data from
Query parameters, e.g. ?name=john&is_member=true
Headers, e.g. Authorization: xxx
Form data, e.g. username=john&password=******
JSON/XML Body, e.g. POST {"name":"john"}
Path variables, e.g. /users/{username}
File uploads
How to use?
type ListUsersInput struct {
Page int `in:"query=page"`
PerPage int `in:"query=per_page"`
IsMember bool `in:"query=is_member"`
}
func ListUsers(rw http.ResponseWriter, r *http.Request) {
input := r.Context().Value(httpin.Input).(*ListUsersInput)
if input.IsMember {
// Do sth.
}
// Do sth.
}
httpin is:
well documented: at https://ggicci.github.io/httpin/
well tested: coverage over 98%
open integrated: with net/http, go-chi/chi, gorilla/mux, gin-gonic/gin, etc.
extensible (advanced feature): by adding your custom directives. Read httpin - custom directives for more details.
awesome mentioned: https://github.com/avelino/awesome-go#forms
You can use the graceful package of Echo.
I write some codes as an example, with self-explanatory comments
package main
import (
"log"
"github.com/labstacks/echo"
)
// Declare your struct with form: "" tag
type Employee struct {
MemberId string `form:"memberId"`
ActivityType string `form:"activityType"`
BusinessUnitCode int `form:"businessUnitCode"`
}
// Your handlers should look like this method
// Which takes an echo.Context and returns an error
func GetEmployee(ctx echo.Context) error{
var employee Employee
// With Bind, you can get the Post Body or query params from http.Request
// that is wrapped by echo.Context here
if err := ctx.Bind(&employee);err != nil {
return err
}
// now you can use your struct , e.g
return ctx.json(200, employee.MemberId)
}
// now use the handler in your main function or anywhere you need
func main() {
e := echo.New()
e.Get("/employee", GetEmployee)
log.Fatal(e.Start(":8080"))
}
If anyone is using Echo, query struct tag will be useful for this case.
Example Struct
type struct Name {
FirstName string `query:"first_name"`
LastName string `query:"last_name"`
}
Example Query Param
?first_name="shahriar"&last_name="sazid"
Code
var name Name
err := c.Bind(&name); if err != nil {
return c.String(http.StatusBadRequest, "bad request")
}

Interface/Struct "Does not implement X, Wrong type or Method, not sure why I am getting this error

Hi guys fairly new to Golang, I understand that interfaces are kind of like contracts that guarantee that certain things will operate a certain way, thats cool and all, and if I make a local copy of it I can basically re-write how it operates (From what I understand, please correct me if I'm wrong)
Here is what I have so far
package register
import (
"log"
"net/http"
"github.com/yohcop/openid-go"
)
var nonceStore = &openid.SimpleNonceStore{
Store: make(map[string][]*openid.Nonce)}
var discoveryCache = &SimpleDiscoveryCache{}
type DiscoveredInfo interface {
OpEndpoint() string
OPLocalID() string
ClaimedID() string
}
type SimpleDiscoveredInfo struct {
opEndpoint, opLocalID, claimedID string
}
type SimpleDiscoveryCache map[string]DiscoveredInfo
func (s *SimpleDiscoveryCache) Put(id string, info DiscoveredInfo) {
db := common.ConnectDB()
rows, err := db.Query("INSERT INTO discovery_cache SET id=?, opendpoint=?, oplocalid=?, claimedid=?",
id, info.OpEndpoint(), info.OPLocalID(), info.ClaimedID())
if err != nil {
panic("Error: " + err.Error())
}
log.Println(rows)
}
func (s *SimpleDiscoveryCache) Get(id string) DiscoveredInfo {
db := common.ConnectDB()
rows, err := db.Query("SELECT FROM discovery_cache WHERE id=?", id)
if err != nil {
panic("Error: " + err.Error())
}
log.Println(rows)
var opEndpoint, opLocalID, claimedID string
for rows.Next() {
err := rows.Scan(&opEndpoint, &opLocalID, &claimedID)
if err != nil {
panic("Help!")
}
}
return &SimpleDiscoveredInfo{
opEndpoint, opLocalID, claimedID,
}
}
func DiscoverHandler(w http.ResponseWriter, r *http.Request) {
url, err := openid.RedirectURL("http://steamcommunity.com/openid", "http://localhost:1337/login/return", "http://localhost")
if err != nil {
http.Error(w, "Failed to login", 500)
}
http.Redirect(w, r, url, 303)
}
func CallbackHandler(w http.ResponseWriter, r *http.Request) {
fullUrl := "http://localhost:1337" + r.URL.String()
id, err := openid.Verify(fullUrl, discoveryCache, nonceStore)
if err != nil {
http.Error(w, "Failed", 500)
}
log.Println(id)
}
Basically I am trying to make my own DiscoveryCache so that it uses a database instead of memory for storage (as instructed to do by the Go-OpenID package located here: https://github.com/yohcop/openid-go
The part I'm trying to recreate is located here: https://github.com/yohcop/openid-go/blob/master/discovery_cache.go
Now I have done (what I assume) everything that should need doing to make this work, but I keep getting this error:
controllers/register/register.go:60: cannot use SimpleDiscoveredInfo literal (type *SimpleDiscoveredInfo) as type openid.DiscoveredInfo in return argument:
*SimpleDiscoveredInfo does not implement openid.DiscoveredInfo (missing ClaimedID method)
controllers/register/register.go:78: cannot use discoveryCache (type *SimpleDiscoveryCache) as type openid.DiscoveryCache in argument to openid.Verify:
*SimpleDiscoveryCache does not implement openid.DiscoveryCache (wrong type for Put method)
have Put(string, DiscoveredInfo)
want Put(string, openid.DiscoveredInfo)
If anybody could inform me on what I have done wrong that would be much appreciated. Thanks! If you need any more information please let me know.
SimpleDiscoveredInfo doesn't implement the interface's methods, you need something like this:
func (sdi *SimpleDiscoveredInfo) OpEndpoint() string { return sdi.opEndpoint }
func (sdi *SimpleDiscoveredInfo) OpLocalID() string { return sdi.opLocalID }
func (sdi *SimpleDiscoveredInfo) ClaimedID() string { return sdi.claimedID }
var _ openid.DiscoveredInfo = (*SimpleDiscoveredInfo)(nil)
http://play.golang.org/p/qVTTKfhNHu
For
controllers/register/register.go:78: cannot use discoveryCache (type *SimpleDiscoveryCache) as type openid.DiscoveryCache in argument to openid.Verify:
*SimpleDiscoveryCache does not implement openid.DiscoveryCache (wrong type for Put method)
have Put(string, DiscoveredInfo)
want Put(string, openid.DiscoveredInfo)
Your types need to return openid.DiscoveredInfo not DiscoveredInfo.

Resources