Create function that receives any function with specific amount of parameters - go

Say I have several different gRPC servers, for example x.Server, y.Server and z.Server, and in order to spin them up, I have a lot of repeated code inside their main function, e.g.:
func main() {
if err := config.EnsureArgLength(1); err != nil {
log.Fatalln(err.Error())
}
srv := &x.Server{}
if err := srv.ReadServerConfig(os.Args[1]); err != nil {
log.Fatalln(err.Error())
}
if err := srv.RegisterListener(); err != nil {
log.Fatalln(err.Error())
}
if err := srv.RegisterClients(); err != nil {
log.Fatalln(err.Error())
}
s := grpc.NewServer()
proto.RegisterXServer(s, srv)
if err := srv.Serve(s); err != nil {
log.Fatalf("failed to serve: %s", err.Error())
}
}
I would love to refactor this main function to make it one or two lines long, something like the following:
func main() {
srv := x.Server{}
if err := srv.RegisterAndServe(); err != nil {
log.Fatal("failed to serve: %s", err.Error())
}
}
But each server will have an auto-generated function proto.RegisterXServer which is not part of x.Server struct, and I'm also not able to modify the file which contains it, since it is auto generated. How should I proceed?

in regards to op changes, which was radical,
I can suggest using a reducer pattern like this.
package main
import (
"fmt"
)
func main() {
fail(reduce(sayHello(), sayGoodbye))
}
func sayHello() func() error {
return func() error { fmt.Println("Hello, playground"); return nil }
}
func sayGoodbye() error {
fmt.Println("Goodbye from the playground")
return nil
}
func reduce(h ...func() error) error {
for _, hh := range h {
if err := hh(); err != nil {
return err
}
}
return nil
}
func fail(err error) {
if err != nil {
panic(err)
}
}

Related

Passing an CSVExport function to a handler Gin

I have created a CSV export function for my to-do list application. The function is working, the handler is returning a written file but I get a strange panic in the console from the Gin framework:
http: wrote more than the declared Content-Length
Is that something crucial and how can I fix this panic.
This is my function:
func (r *Repository) CSVExport() (*os.File, error) {
tasks, err := r.getAllTasks()
if err != nil {
return nil, err
}
file, err := os.Create("tasks.csv")
if err != nil {
return nil, err
}
defer file.Close()
writer := csv.NewWriter(file)
defer writer.Flush()
var taskNameList []string
for _, task := range tasks {
taskNameList = append(taskNameList, task.Text)
}
err = writer.Write(taskNameList)
if err != nil {
return nil, err
}
return file, nil
}
And this is the handler:
func CSVExport(data model.ListOperations) gin.HandlerFunc {
return func(c *gin.Context) {
tasks, err := data.CSVExport()
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid Task"})
}
c.FileAttachment("./tasks.csv", "tasks.csv")
c.Writer.Header().Set("attachment", "filename=tasks.csv")
c.JSON(200, tasks)
}
}
Your code has some error:
You need to return on error
You can't return JSON after returning your file with fileAttachment (it already does this stuff)
func CSVExport(data model.ListOperations) gin.HandlerFunc {
return func(c *gin.Context) {
tasks, err := data.CSVExport()
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid Task"})
return //stop it on error
}
c.FileAttachment("./tasks.csv", "tasks.csv")
c.Writer.Header().Set("attachment", "filename=tasks.csv")
//c.JSON(200, tasks) not need the fileAttachement func do it
}
}

Ethereum error {"code":-32000,"message":"unknown account"}

I am trying to send a raw transaction with eth.sendTransaction but I am getting an error that says {"code":-32000,"message":"unknown account"}. I am not sure what is causing this and I cant seem to find an answer on the internet. Can anyone help me figure it out? Here is my code:
func ExecuteSignedTransaction(rawTransaction string) {
var hash web3.Hash
data := make(map[string]interface{})
data["data"] = rawTransaction
err := Web3HTTPClient.Call("eth_sendTransaction", &hash, data)
if err != nil{
fmt.Println(err)
Os.Exit(1)
}
fmt.Println("Sent tx hash:", hash)
}
So, what I might do here:
import (
"strings"
"crypto/ecdsa"
"math/big"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
)
var chainId = big.NewInt(1) // chain id for the ethereum mainnet, change according to needs
func ecdsaPrivateKeyFromHex(privKeyHex string) *ecdsa.PrivateKey {
ecdsaKey, err := crypto.HexToECDSA(privKeyHex)
if err != nil { panic(err) }
return ecdsaKey
}
func newTransactOpts(privKey *ecdsa.PrivateKey) *bind.TransactOpts {
transactOpts, err := bind.NewKeyedTransactorWithChainID(privKey, chainId)
if err != nil { panic(err) }
return transactOpts
}
func newRpcClient() *ethclient.Client {
c, err := ethclient.Dial("insert rpc url here")
if err != nil { panic(err) }
return c
}
// note: constructing the *types.Transaction object left as
// an exercise to the reader
func ExecuteTransaction(rawTransaction *types.Transaction) {
privKeyHex := "0xblahblahblahblahblah" // use your own account's private key
transactOpts := newTransactOpts(ecdsaPrivateKeyFromHex(privKeyHex))
signedTxn, err := transactOpts.Signer(transactOpts.From, rawTransaction)
if err != nil { panic(err) }
rpcClient := newRpcClient()
if err := rpcClient.SendTransaction(context.Background(), signedTxn); err != nil { panic(err) }
// do whatever
}

How to gently defer execution of a function that might return an error?

Most cleanup functions, especially those related to the IO operations, return an error, and normally we'd prefer to defer their execution in case if we'd not forget to call them when we're done with acquired resources. For example, at some point in the code we might write something like this:
var r *SomeResource
var err error
if r, err = Open(/* parameters */); err != nil {
return nil, err
}
defer r.Close() // This might return an error
It seems that if Close function returns an error, it'll be ignored. How can we gently process the returned error from such a function?
Using defer with a func() {}() like so.
var r *SomeResource
var err error
if r, err = Open(/* parameters */); err != nil {
return nil, err
}
defer func() {
if err = r.Close(); err != nil {
fmt.Printf("ERROR: %v", err)
}
}()
Fail gracefully with an error. Report the first error. Don't overwrite earlier errors. For example,
package main
import (
"fmt"
"os"
)
func demo() (name string, err error) {
filename := `test.file`
f, err := os.Open(filename)
if err != nil {
return "", err
}
defer func() {
e := f.Close()
if e != nil {
if err == nil {
err = e
}
}
}()
// do someting with the file
name = f.Name()
fi, err := f.Stat()
if err != nil {
return name, err
}
if fi.Size() == 0 {
err = fmt.Errorf("%s: empty file", filename)
return name, err
}
return name, err
}
func main() {
name, err := demo()
fmt.Println(name, err)
}
We can handle this in ways like:
way-1:
func myFn() error {
var err error
if r, err = Open(/* parameters */); err != nil {
return nil, err
}
defer func() {
if cErr = r.Close(); cErr != nil {
err = cErr
}
}()
return err
}
way-2:
func myFn() error {
var err error
if r, err = Open(/* parameters */); err != nil {
return nil, err
}
defer func() {
if cErr = r.Close(); cErr != nil {
// we can log the error
// or
// whatever we want to do
}
}()
return err
}
I have also find a nice blog on this topic, i mean handling error when defer func returns an error. Check here https://blog.learngoprogramming.com/5-gotchas-of-defer-in-go-golang-part-iii-36a1ab3d6ef1.

Golang import struct and share over all app

I would like to import package and create new struct in main() func.
// main.go
import "testapp/app"
a := app.GetApp()
db, err := a.ConnectDatabase()
if err != nil {
panic(err.Error())
}
// testapp/app.go
func (a *App) ConnectDatabase() {
db, err := sql.Open()
if err != nil {
panic(err.Error())
}
a.db = db
}
I've got error:
app.ConnectDatabase() used as value
How can I fix that?
you might want to solve this like:
// main.go
import "testapp/app"
func main(){
a := app.GetApp()
err := a.ConnectDatabase()
if err != nil {
panic(err.Error())
}
a.db. //interesting db code here
}
// testapp/app.go
func (a *App) ConnectDatabase() error{
db, err := sql.Open()
if err != nil {
return err
}
a.db = db
return nil
}

Trying to test write file from goroutines in Go

Well, part of my code was working without a method approach, I'm trying to test
append text to a file and reading from goroutines, but I'm stuck here trying to
write it.
What is wrong? the file is created, but I can't append text to it, maybe something obvious, but seems I'm blind, maybe I'm failing understanding some language concepts...
package main
import (
"bufio"
"fmt"
"os"
"sync"
"time"
)
var w sync.WaitGroup
type Buffer struct {
F *os.File
}
func (buff *Buffer) Open(pathName string) (err error) {
buff.F, err = os.OpenFile(pathName, os.O_APPEND|os.O_CREATE, 0666)
if err != nil {
return
}
fmt.Println("Open() ok")
return nil
}
func (buff *Buffer) Close() (err error) {
err = buff.F.Close()
if err != nil {
return
}
fmt.Println("Close() ok")
return nil
}
func (buff *Buffer) Push(data string) (err error) {
w := bufio.NewWriter(buff.F)
_, err = fmt.Fprintf(w, "data=%s", data)
if err != nil {
return
}
w.Flush()
fmt.Println("Push() ok")
return nil
}
func checkErr(err error) {
if err != nil {
fmt.Println(err.Error())
}
}
func worker() {
var err error
buffer := new(Buffer)
err = buffer.Open("test")
checkErr(err)
err = buffer.Push("data\n")
checkErr(err)
time.Sleep(5 * time.Second)
err = buffer.Close()
checkErr(err)
w.Done()
}
func main() {
w.Add(2)
go worker()
go worker()
w.Wait()
}
Thanks
Open the file like this:
buff.F, err = os.OpenFile(pathName, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666)
The write flag is required to write to the file.
You missed the write error because the return from bufio Flush is ignored. Change Push to:
func (buff *Buffer) Push(data string) (err error) {
w := bufio.NewWriter(buff.F)
_, err = fmt.Fprintf(w, "data=%s", data)
if err != nil {
return
}
err = w.Flush()
if err != nil {
return err
}
fmt.Println("Push() ok")
return nil
}
To cleanly append data without intermixing with other pushes, the data must be written with a single call to the file Write method. Use a bytes.Buffer instead of a bufio.Writer to ensure a single call to the file Write method:
func (buff *Buffer) Push(data string) (err error) {
var b bytes.Buffer
_, err = fmt.Fprintf(&b, "data=%s", data)
if err != nil {
return
}
_, err := buff.F.Write(b.Bytes())
if err != nil {
return err
}
fmt.Println("Push() ok")
return nil
}

Resources