How to use Context when getting a direction with GO? - go

I'm having the following code to get the direction from Google Cloud:
import (
"google.golang.org/appengine"
"google.golang.org/appengine/urlfetch"
"fmt"
"io/ioutil"
"net/http"
)
const directionAPIKey = "APIKey"
const directionURL = "https://maps.googleapis.com/maps/api/directions/json?origin=%s&destination=%s&mode=%s&key=%s"
func main() {
http.HandleFunc("/", handler)
}
func handler(w http.ResponseWriter, r *http.Request) {
ctx := appengine.NewContext(r)
direction, err := fetchDirection(ctx, r.FormValue("origin"), r.FormValue("destination"), r.FormValue("mode"))
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Add("Content-Type", "application/json; charset=utf-8")
w.Write(direction)
}
func fetchDirection(ctx appengine.Context, origin string, destination string, mode string) ([]byte, error) {
client := urlfetch.Client(ctx)
resp, err := client.Get(fmt.Sprintf(directionURL, origin, destination, mode, directionAPIKey))
if err != nil {
return nil, err
}
defer resp.Body.Close()
return ioutil.ReadAll(resp.Body)
}
But I get an error:
undefined: appengine.Context
When trying to deploy the app. What I have tried is to change:
ctx := appengine.NewContext(r)
into
ctx := r.Context()
And
func fetchDirection(ctx appengine.Context, origin string...)
into
func fetchDirection(ctx Context, origin string...)
But I get:
undefined: Context
I'm completely lost. I'm new to Go and GCP, so please be patient with me. Thanks

If you check the godoc for urlfetch you'll see it links to where the Context type is defined. That in turn tells you that "As of Go 1.7 this package is available in the standard library under the name context. https://golang.org/pkg/context."
So add an import:
import "context"
and refer to it as:
func fetchDirection(ctx context.Context, origin string...)

Related

cannot encode json.decoded request body

I have a server implementation. Now I am writing unit test to check it's functionalities.
I cannot prepare request, that would unmarshall on the server side well. Code below results with InvalidUnmarshallError. I don't know, how to debug it further.
Client side code:
body := PatchCatRequest{Adopted: true}
bodyBuf := &bytes.Buffer{}
err := json.NewEncoder(bodyBuf).Encode(body)
assert.NoError(t, err)
req, err := http.NewRequest("PATCH", URL+"/"+catId, bodyBuf)
recorder := httptest.NewRecorder()
handler.PatchCat(recorder, req.WithContext(ctx))
Server side code:
type PatchCatRequest struct {
Adopted bool `json:"adopted"`
}
func (h *Handler) PatchCat (rw http.ResponseWriter, req *http.Request) {
var patchRequest *PatchCatRequest
if err := json.NewDecoder(req.Body).Decode(patchRequest); err != nil {
rw.WriteHeader(http.StatusBadRequest)
logger.WithField("error", err.Error()).Error(ErrDocodeRequest.Error())
return
}
...
}
You are unmarshaling into a nil pointer, as the error message says:
package main
import (
"encoding/json"
"fmt"
)
type PatchCatRequest struct {
Adopted bool
}
func main() {
var patchRequest *PatchCatRequest // nil pointer
err := json.Unmarshal([]byte(`{"Adopted":true}`), patchRequest)
fmt.Println(err) // json: Unmarshal(nil *main.PatchCatRequest)
}
https://play.golang.org/p/vt7t5BgT3lA
Initialize the pointer before unmarshaling:
func main() {
patchRequest := new(PatchCatRequest) // non-nil pointer
err := json.Unmarshal([]byte(`{"Adopted":true}`), patchRequest)
fmt.Println(err) // <nil>
}
https://play.golang.org/p/BqliguktWmr

Passing Data from Datastore to http.Writer

I was recently introduced to the wonders of the language known as go. I set myself a task of writing a RESTful API using GoLang and Google's Datastore. I am able to retrieve data from Datastore and Print it to the console using fmt.Println however the issue comes into play when I try to use the data from Datastore and pass it to the http.Handler.
I was wondering if someone could inform me of where I am going wrong or even point me in the right direction.
Here is what I have done so far
package main
import (
"log"
"fmt"
"context"
"net/http"
// "encoding/json"
"cloud.google.com/go/datastore"
)
type Item struct {
Id string `datastore:"id"`
Name string `datastore:"title"`
View int `datastore:"views"`
Brand string `datastore:"brand"`
id int64 // interger from "Name/ID" fild in datastore entities list
}
func main() {
http.HandleFunc("/", ListTasks)
http.ListenAndServe(":8080", nil)
}
//func ListTasks(w http.ResponseWriter, r *http.Request) ([]*Item, error) {
func ListTasks(w http.ResponseWriter, r *http.Request) {
ctx := context.Background()
client, err := datastore.NewClient(ctx, "my-client")
if err != nil {
log.Fatalln(err)
}
var tasks []*Item
query := datastore.NewQuery("my-query")
keys, err := client.GetAll(ctx, query, &tasks)
if err != nil {
return nil, err
}
for i, key := range keys {
tasks[i].id = key.ID
}
return tasks, nil
}
I've also looked into http Wrappers, but I'm unaware if using a wrapper is 100% necessary or if I'm just adding more to my plate.
I've removed the return tasks, nil as it appeared to be unnecessary, modified the return, nil err to log.Fatalln(nil, err) and also encoded tasks as instructed by #ThunderCat and #tkausl. My issue has been resolved, thank you.
Here is my working code
package main
import (
"log"
"context"
"net/http"
"encoding/json"
"cloud.google.com/go/datastore"
)
type Item struct {
Id string `datastore:"id"`
Name string `datastore:"title"`
View int `datastore:"views"`
Brand string `datastore:"brand"`
id int64 // interger from "Name/ID" fild in datastore entities list
}
func main() {
http.HandleFunc("/", ListTasks)
http.ListenAndServe(":8080", nil)
}
func ListTasks(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
ctx := context.Background()
client, err := datastore.NewClient(ctx, "my-client")
if err != nil {
log.Fatalln(err)
}
var tasks []*Item
query := datastore.NewQuery("my-query")
keys, err := client.GetAll(ctx, query, &tasks)
if err != nil {
log.Fatalln(nil, err)
}
for i, key := range keys {
tasks[i].id = key.ID
}
json.NewEncoder(w).Encode(tasks)
// return tasks, nil
}
It now returns [{"Id":"24X660","Name":"Fiesta","View":129,"Brand":"Ford"}]
Also thank you to #static_cast for correcting my formatting errors.

golang httputil.NewSingleHostReverseProxy how to read response and modify the response?

I've a reverse proxy like this:
Iam using RoundTrip but this proxy server don't work correctly.
How to correctly read and modify response?
and somebody create proxy server via NewSingleHostReverseProxy.
Please Help.
package main
import (
"bytes"
"fmt"
"io/ioutil"
"net/http"
"net/http/httputil"
"net/url"
)
type transport struct {
http.RoundTripper
}
func (t *transport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
resp, err = t.RoundTripper.RoundTrip(req)
if err != nil {
return nil, err
}
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
err = resp.Body.Close()
if err != nil {
return nil, err
}
b = bytes.Replace(b, []byte("Google"), []byte("GOOGLE"), -1)
body := ioutil.NopCloser(bytes.NewReader(b))
resp.Body = body
return resp, nil
}
func sameHost(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
r.Host = r.URL.Host
handler.ServeHTTP(w, r)
})
}
func main() {
u, _ := url.Parse("http://habrahabr.ru")
reverseProxy := httputil.NewSingleHostReverseProxy(u)
reverseProxy.Transport = &transport{http.DefaultTransport}
// wrap that proxy with our sameHost function
singleHosted := sameHost(reverseProxy)
http.ListenAndServe(":3000", singleHosted)
}
When you are going to http:// for most good sites (for example your habrahabr.ru) there is a redirect to https://, so request to http will return something like 301 Moved Permanently and you will not find content that you seek for. Also, after correct to https, make sure that site does not use javascript to load content, you can easily check this by curl:
curl localhost:3000
Also use some logging to determine what's wrong.

Missing symbols when importing `github.com/influxdb/influxdb/client/v2` package

Setting up a web socket on google cloud in Golang, and import code that works fine on my local machine does not work on the cloud.
I have:
import "github.com/influxdb/influxdb/client/v2"
and have run
go get "github.com/influxdb/influxdb/client/v2"
Upon running go run server.go I get:
# command-line-arguments
./pi_server.go:47: undefined: client.NewClient
./pi_server.go:47: undefined: client.Config
Full code below, excluding const declarations and html:
package main
import (
"flag"
"html/template"
"log"
"net/http"
"github.com/gorilla/websocket"
"fmt"
"net/url"
"github.com/influxdb/influxdb/client/v2"
"time"
)
var addr = flag.String("addr", "localhost:8080", "http service address")
var upgrader = websocket.Upgrader{} // use default options
func echo(w http.ResponseWriter, r *http.Request) {
//Influx init
u,err := url.Parse("http://localhost:8086")
checkError(err)
influx_c := client.NewClient(client.Config{
URL: u,
Username: username,
Password: password,
})
bp,err := client.NewBatchPoints(client.BatchPointsConfig{
Database: MyDB,
Precision: "s",
})
tags := map[string]string{"my_sensor_id": my_sensor_id}
//end influx init
c, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Print("upgrade:", err)
return
}
defer c.Close()
for {
mt, message, err := c.ReadMessage()
if err != nil {
log.Println("read:", err)
break
}
log.Printf("recv: %s", message)
/*
write to influx here
*/
fields := map[string]interface{}{
"random_int": message,
"other_stuff": 69696,
}
pt,err := client.NewPoint("test_collection", tags, fields, time.Now())
checkError(err)
bp.AddPoint(pt)
influx_c.Write(bp)
err = c.WriteMessage(mt, message)
if err != nil {
log.Println("write:", err)
break
}
}
}
func home(w http.ResponseWriter, r *http.Request) {
homeTemplate.Execute(w, "ws://"+r.Host+"/echo", )
}
func main() {
flag.Parse()
log.SetFlags(0)
http.HandleFunc("/echo", echo)
http.HandleFunc("/", home)
log.Fatal(http.ListenAndServe(*addr, nil))
}
You local machine has a version of github.com/influxdb/influxdb/client/v2 before this commit. Your cloud server is fetching a more recent version of the package.
To fix the issue, run
go get -u github.com/influxdb/influxdb/client/v2
on your local machine to get the latest version of the package. Update the application code to use the new function and type names:
influx_c := client.NewHTTPClient(client.HTTPConfig{
URL: u,
Username: username,
Password: password,
})
Nailed it, thanks! Also note from following code:
influx_c,err := client.NewHTTPClient(client.HTTPConfig{
Addr: "http://localhost:8086",
Username: username,
Password: password,
})
They changed URL field to Addr, with is a string literal instead of a net/url object

Auth0 in Go Martini

I'm trying to use Auth0 with Martini in Go. I'm using their examples but I can't seem to get it working no matter what I try.
Here is my code:
package main
import (
"flag"
"github.com/go-martini/martini"
"github.com/martini-contrib/render"
"github.com/auth0/go-jwt-middleware"
"encoding/base64"
"github.com/dgrijalva/jwt-go"
"net/http"
)
func main() {
m := martini.Classic()
port := flag.String("port", "8000", "HTTP Port")
flag.Parse()
jwtMiddleware := jwtmiddleware.New(jwtmiddleware.Options{
ValidationKeyGetter: func(token *jwt.Token) (interface{}, error) {
decoded, err := base64.URLEncoding.DecodeString("<token>")
if err != nil {
return nil, err
}
return decoded, nil
},
})
m.Use(render.Renderer(render.Options{
IndentJSON: true, // Output human readable JSON
}))
m.Get("/", jwtMiddleware.Handler, func(res http.ResponseWriter, req *http.Request) { // res and req are injected by Martini
res.WriteHeader(200) // HTTP 200
})
// Get the PORT from the environment.
m.RunOnAddr(":" + *port)
}
When I run that, I get a panic that says Value not found for type http.Handler
If I change the jwtMiddleware.Handler to jwtMiddleware.HandlerWithNext, I get a panic for Value not found for type http.HandlerFunc.
Does anyone have any ideas what I'm doing wrong?
To use the jwt-middleware with Martini, you just have to use the CheckJWT method instead of the Handler method.
Check this example: https://github.com/auth0/go-jwt-middleware/blob/master/examples/martini-example/main.go#L27
Let me know if this helps.
Cheers!

Resources