I have learned Golang, Chi Router and I have a question
https://github.com/go-chi/chi/blob/master/_examples/rest/main.go#L83
r.Route("/{articleID}", func(r chi.Router) {
r.Use(ArticleCtx) // Load the *Article on the request context
r.Get("/", GetArticle) // GET /articles/123
r.Put("/", UpdateArticle) // PUT /articles/123
r.Delete("/", DeleteArticle) // DELETE /articles/123
})
According to document r.Route will return chi.Router
https://pkg.go.dev/github.com/go-chi/chi/v5#v5.0.1#Mux.Route
But in above code, I see that r.Route(...) does not return anything.
Is it OK?
Route (1) creates a new Mux with a fresh middleware stack and (2) mounts it along the pattern as a subrouter.
func (mx *Mux) Route(pattern string, fn func(r Router)) Router {
if fn == nil {
panic(fmt.Sprintf("chi: attempting to Route() a nil subrouter on '%s'", pattern))
}
subRouter := NewRouter()
fn(subRouter)
mx.Mount(pattern, subRouter)
return subRouter
}
In your example the value is just discarded and its totally fine. By the way, InfluxDB uses chi, so you can check a more complex case of usage when documentation is not enough.
Related
I have the following yaml file with me:
nodes: ["1.1.1.1","127.0.0.1","2.2.2.2"]
emailto: ["sample#sample.com","sample#sample.com"]
I want to open the YAML file, iterate over the IPs one by one and do some certain action. If there is an error, then it should automatically take the next ip and perform the same function. I am having trouble as to how to save the IPs to a list or how to iterate in GOLANG.
Also, I have to send an email based to the e-mail IDs present in the YAML file. Which package in GO is used for that and how to do it like SMTPLIB in Python.
It looks like there are three parts to your question:
1. See gopkg.in/yaml.v2 for YAML parsing
import (
// ...
"gopkg.in/yaml.v2"
)
type Doc struct {
Nodes []string `yaml:"nodes"`
Emails []string `yaml:"emailto"`
}
// Parse the YAML document.
doc := Doc{}
err := yaml.Unmarshal(yamlbytes, &doc)
if err != nil {
log.Fatalf("FATAL: could not parse YAML file: %s", err)
}
2. Example of iterating using range, calling a function, and handling errors
// Iterate over each node and do something, handling errors as needed.
for _, node := range doc.Nodes {
err := DoSomethingWithNode(node)
if err != nil {
log.Printf("ERROR: failed to handle node %q: %s", node, err)
} else {
log.Printf("OK: successfully handled node %q.", node)
}
}
3. See the builtin net/smtp package for sending email
See the package example for a complete illustration.
I'm trying to wrap my head around test driven development with Go and having an issue testing my CRUD functions since they are written for my production database. I'm coming from Ruby on Rails so I am used to using a test database, but Go doesn't seem to be too friendly in this regard.
So, how does one go about testing CRUD with Go?
main.go
package main
import (
"database/sql"
)
type book struct {
id int `json:"id"`
isbn string `json:"isbn"`
title string `json:"title"`
author string `json:"author"`
price float32 `json:"price"`
}
// type Books []*Book
// CRUD functions for Book
func (b *book) getBook(db *sql.DB) error {
return db.QueryRow("SELECT * FROM books WHERE id=$1", b.id).Scan(&b)
}
app.go
func (a *App) Initialize(dbname string) {
var err error
a.DB, err = sql.Open("postgres", "postgresql://localhost:5432/bookstore?sslmode=disable")
if err != nil {
log.Fatal(err)
}
}
my test
func TestGetBook(t *testing.T) {
clearTable()
addBook(1)
req, _ := http.NewRequest("GET", "/book/1", nil)
response := executeRequest(req)
checkResponseCode(t, http.StatusOK, response.Code)
}
The problem is that this keeps on looking at the books table in my DB, not the books_test table I'd like to use for testing. How can I go about making ONLY the tests use the books_test DB?
You should create a dev/test database which should be a complete copy of your production database. You will never want to run test directly against your production database since too many unexpected issues could happen.
A workaround would be starting up your app first, which creates the connection to your database, then run the test. You can use IntelliJ to achieve this.
TDD in my opinion is great for developing business logic layer code since new models and business processes can have unexpected impacts on existing ones.
#Godzilla74, there'are 2 solutions: enable SSL for test DB (try to check database settings or ask your system administrator) of have completely different setting for test:
func (a *App) Initialize(dbname string) {
var err error
pgsettings := os.Getenv("PGSETTINGS")
if pgsettins == "" {
// default options if not overridden
pgsettins := "postgresql://localhost:5432/bookstore?sslmode=disable"
}
a.DB, err = sql.Open("postgres", pgsettins)
if err != nil {
log.Fatal(err)
}
}
So you can run set environment setting to any required value and run app, like so:
export PGSETTINGS="postgresql://localhost:5432/bookstore_test?sslmode=disable"
go run main.go
Using go (golang):
Is there a way to create a logger that outputs to a database?
Or more precisely, can I implement some kind of io.Writer interface that I can pass as the first argument to log.New()?
EG: (dbLogger would receive the output of the log and write it to the database)
logger := log.New(dbLogger, "dbLog: ", log.Lshortfile)
logger.Print("This message will be stored in the database")
I would assume that I should just create my own database logging function, but I was curious to see if there is already a way of doing this using the existing tools in the language.
For some context, I'm using mgo.v2 to handle my mongodb database, but I don't see any io.Writer interfaces there other than in GridFS, which I think solves a different problem.
I'm also still getting my head around the language, so I may have used some terms above incorrecly. Any corrections are very welcome.
This is easily doable, because the log.Logger type guarantees that each log message is delivered to the destination io.Writer with a single Writer.Write() call:
Each logging operation makes a single call to the Writer's Write method. A Logger can be used simultaneously from multiple goroutines; it guarantees to serialize access to the Writer.
So basically you just need to create a type which implements io.Writer, and whose Write() method creates a new document with the contents of the byte slice, and saves it in the MongoDB.
Here's a simple implementation which does that:
type MongoWriter struct {
sess *mgo.Session
}
func (mw *MongoWriter) Write(p []byte) (n int, err error) {
c := mw.sess.DB("").C("log")
err = c.Insert(bson.M{
"created": time.Now(),
"msg": string(p),
})
if err != nil {
return
}
return len(p), nil
}
Using it:
sess := ... // Get a MongoDB session
mw := &MongoWriter{sess}
log.SetOutput(mw)
// Now the default Logger of the log package uses our MongoWriter.
// Generate a log message that will be inserted into MongoDB:
log.Println("I'm the first log message.")
log.Println("I'm multi-line,\nbut will still be in a single log message.")
Obviously if you're using another log.Logger instance, set the MongoWriter to that, e.g.:
mylogger := log.New(mw, "", 0)
mylogger.Println("Custom logger")
Note that the log messages end with newline as log.Logger appends it even if the log message itself does not end with newline. If you don't want to log the ending newline, you may simply cut it, e.g.:
func (mw *MongoWriter) Write(p []byte) (n int, err error) {
origLen := len(p)
if len(p) > 0 && p[len(p)-1] == '\n' {
p = p[:len(p)-1] // Cut terminating newline
}
c := mw.sess.DB("").C("log")
// ... the rest is the same
return origLen, nil // Must return original length (we resliced p)
}
I have the following bit of code where I'm using the RESTful framework for Go called sleepy.
I can successfully start the service at: http://localhost:3000, however when I try to access http://localhost:3000/temperature I'm expecting my SparkCore function dht to execute.
I'm using the Gobot.io Spark platform to execute this function based on this example, which I've implemented in my own code.
The problem is that the code doesn't get past the gobot.Start() method inside the Get() function so I can't actually return the result data.
I'm setting the data value hoping that I can do the:
return 200, data, http.Header{"Content-type": {"application/json"}}
But it never gets called becuase of the gobot.Start().
I'm very new to Go so any help would be greatly appreciated.
package main
import (
"net/url"
"net/http"
"fmt"
"github.com/dougblack/sleepy"
"github.com/hybridgroup/gobot"
"github.com/hybridgroup/gobot/platforms/spark"
)
var gbot = gobot.NewGobot()
var sparkCore = spark.NewSparkCoreAdaptor("spark", "device_id", "auth_token")
type Temperature struct {}
func (temperature Temperature) Get(values url.Values, headers http.Header) (int, interface{}, http.Header) {
work := func() {
if result, err := sparkCore.Function("dht", ""); err != nil {
fmt.Println(err)
} else {
data := map[string]string{"Temperature": result}
fmt.Println("result from \"dht\":", result)
}
}
robot := gobot.NewRobot("spark",
[]gobot.Connection{sparkCore},
work,
)
gbot.AddRobot(robot)
gbot.Start()
return 200, data, http.Header{"Content-type": {"application/json"}}
}
func main() {
api := sleepy.NewAPI()
temperatureResource := new(Temperature)
api.AddResource(temperatureResource, "/temperature")
fmt.Println("Listening on http://localhost:3000/")
api.Start(3000)
}
gbot.Start() is a blocking call.
In this context, you are expected to call it as:
go gbot.Start()
This will launch it in a goroutine (think thread) and then let your app continue.
When you look at the gobot example app, they don't run in the background since it is the main function. If main runs everything in the background and doesn't wait for anything, the app exits immediately with no apparent effect.
I would like to ask you if there is a way to find out if insertion was successful when inserting new object using collection.
Insert(object) with single operation.
What I mean is that, I don't want to send another query to the db to find out if there is a record or not. I need one single atomic operation (insert -> result (isSuccessful) - pseudo code).
The Insert method returns an error object that represents it success or failure. You need to set the safe mode of the session first to enable this behaviour.
session.SetSafe(&mgo.Safe{}) // <-- first set safe mode!
c := session.DB("test").C("people")
err = c.Insert(&Person{"Ale", "+55 53 8116 9639"})
if err != nil { // <-- then check error after insert!
fmt.Printf("There was an error: %v", err)
} else {
fmt.Print("Success!")
}