How to reference multiple libs with same functions and switch between them inline in Go - pointers

I am wondering how I can do something similar to this. I currently have multiple packages with that same structure and functions but they actually retrieve values from multiple APIs. I am also loading in a config that has an array with parameters to use one of those packages per array item.
I am wondering how I can create a variable that uses one of those packages based on the config value. Hopefully this is clear enough. Here is pseudo code that I have written up to explain. Thanks in advance
package main
import (
"errors"
"flag"
"os"
"project/lib"
"project/morelib"
"project/extralib"
"fmt"
"math"
"math/rand"
"time"
)
func stuff(info RunInfo) (err error) {
apiKey:= "stuff1" // actually in the config
apiSecret := "stuff2" // actually in the config
variable := lib.New(apiKey, apiSecret) //returns *Lib struct
//this is where I have to comment out the other libs and uncomment them as needed
// variable := morelib.New(apiKey, apiSecret)
// variable := extralib.New(apiKey, apiSecret)
//trying to switch between libraries like this or in a switch statement
if info.libname == "lib"{
variable = lib.New(apiKey, apiSecret) //.New("", "") returns *Lib struct
}else if info.libname == "morelib"{
variable = morelib.New(apiKey, apiSecret) //.New("", "") returns *MoreLib struct
}else if info.libname == "extralib"{
variable = extralib.New(apiKey, apiSecret) //.New("", "") returns *ExtraLib struct
}else{
err = errors.New("there was an error with the value.....")
return err
}
mystuffs, err := variable.GetBalance("USD")
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("mystuff value: %v", mystuffs.value)
return
}
type RunInfo struct{
libname string
//other stuff
}
func main() {
//load from config with array
config := config.Load()
for i:=0; i<compare; i++{
var runInfo RunInfo
runInfo.libname = config.libname
stuff(runInfo)
}
}
pseudo lib code:
func New(apiKey, apiSecret string) *Lib {
client := NewClient(apiKey, apiSecret)
return &Lib{client}
}
func NewClient(apiKey, apiSecret string) (c *client) {
return &client{apiKey, apiSecret, &http.Client{}, false}
}
type Lib struct {
client *client
}
type client struct {
apiKey string
apiSecret string
httpClient *http.Client
debug bool
}
func (b *Lib) GetBalance(currency string) (balance Balance, err error) {
payload, err := json.Marshal(BalanceParams{Currency: currency})
if err != nil {
return
}
r, err := b.client.do("POST", "GetBalance", string(payload), true)
if err != nil {
return
}
var response jsonResponse
if err = json.Unmarshal(r, &response); err != nil {
return
}
if err = handleErr(response); err != nil {
return
}
err = json.Unmarshal(response.Result, &balance)
return
}

Use and if statement as in the question, a switch statement or a map.
I assume that the type returned by the New function is the following interface:
type GetStuffer interface
GetStuff(string) (Stuff, error)
}
The switch statement is:
var variable GetStuffer
switch info.CompareVal {
case "lib":
variable = lib.New(string1, string2)
case "morelib":
variable = morelib.New(string1, string2)
case "extralib":
variable = extralib.New(string1, string2)
default:
return errors.New("there was an error with the value.....")
}
mystuffs, err := variable.GetMyStuff()
if err != nil {
fmt.Println(err)
return
}
For the map, initialize a package level variable with the map:
var m = map[string]func(string,string) GetStuffer {
"lib": lib.New,
"morelib": morelib.New,
"extralib": extralib.New,
}
and use it like this:
fn := m[info.CompareValue]
if m == nil {
return errors.New("there was an error with the value.....")
}
variable := fn(string1, string2)
mystuffs, err := variable.GetMyStuff()
if err != nil {
fmt.Println(err)
return
}
If the assumption above about the return type is not correct, then there are two options. The first and likely the simplest is to modify New functions to return type GetStuffer. If that's not possible, then write little adaptor functions:
var m = map[string]func(string,string) GetStuffer {
"lib":func(s1, s2 string) GetStuffer { return lib.New(s1, s2) }
"morelib":func(s1, s2 string) GetStuffer { return morelib.New(s1, s2) }
"extralib":func(s1, s2 string) GetStuffer { return extralib.New(s1, s2) }
}

Why don't you define an interface that's only one function? In your example would be
type Stuffer interface {
GetMyStuff(string) (Stuff, error)
}
Then you declare your variable as type Stuffer.

Related

How to validate headers and body in Gin-Gonic?

I have a Gin program. When a request comes, I want all of the fields of the variable data (type ProductCreate) to have values: UserId (from headers) and Name, Price (from JSON body). I used the below code and it works:
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
type ProductCreate struct {
UserId int `header:"user-id"`
Name string `json:"name"`
Price int `json:"price"`
}
func main() {
r := gin.Default()
r.POST("/product", func(c *gin.Context) {
var data ProductCreate
// bind the headers to data
if err := c.ShouldBindHeader(&data); err != nil {
c.JSON(400, err.Error())
return
}
// bind the body to data
if err := c.ShouldBindJSON(&data); err != nil {
c.JSON(400, err.Error())
return
}
c.JSON(200, data)
})
r.Run(":8080")
}
After that, I wanted to make sure that fields must be provided so I edited the ProductCreate struct like this:
type ProductCreate struct {
UserId int `header:"user-id" binding:"required"`
Name string `json:"name" binding:"required"`
Price int `json:"price" binding:"required"`
}
Then it raised an unexpected error when I tested it again:
Key: 'ProductCreate.Name' Error:Field validation for 'Name' failed on the 'required' tag\nKey: 'ProductCreate.Price' Error:Field validation for 'Price' failed on the 'required' tag
I realized the error happened at this:
// bind the headers to data
if err := c.ShouldBindHeader(&data); err != nil {
c.JSON(400, err.Error())
return
}
Is there any solution to resolve my problem?
Can you try this?
curl --location --request POST 'http://localhost:8080/product' \
--header 'user-id: 20' \
--data-raw '{
"name": "sr"
}'
I tried your code and it works perfectly.
{
"UserId": 20,
"name": "sr",
"price": 0
}
Gin version: github.com/gin-gonic/gin v1.8.1 // indirect
Soln:
package main
import (
"github.com/gin-gonic/gin"
)
type ProductCreate struct {
Name *string `json:"name" binding:"required"`
Price *int `json:"price" binding:"required"`
}
type Header struct {
UserId *int `header:"user-id" binding:"required"`
}
func main() {
r := gin.Default()
r.POST("/product", func(c *gin.Context) {
data := &ProductCreate{}
header := &Header{}
// bind the headers to data
if err := c.ShouldBindHeader(header); err != nil {
c.JSON(400, err.Error())
return
}
// bind the body to data
if err := c.ShouldBindJSON(data); err != nil {
c.JSON(400, err.Error())
return
}
c.JSON(200, data)
})
r.Run(":8080")
}

io.Copy fails to copy all data to http.ResponseWriter

In the following codes, s.getFile gets file from S3 and return a struct of io.ReadCloser and ContentLength.
WriteResultesponse write the file to http.ResponseWriter.
But *reader.ContentLength sometimes is different from actualContentLength.
Any idea why? Thanks
s3Ctx, closeS3 := context.WithTimeout(ctx, xxx) // 1 hour
defer closeS3()
// directly stream result from locations
for _, location := range Locations {
reader, err := s.getFile(s3Ctx, xxx)
// reader is
//struct {
// Data io.ReadCloser
// ContentLength *int64
//}
if err != nil {
return err
}
actualContentLength, err := WriteResultesponse(params.Writer, ResultResponse{
Data: reader.Data,
})
}
// WriteResultResponse streams the result data to the user.
func WriteResultResponse(w http.ResponseWriter, resultResp ResultResponse) (int64, error) {
w.Header().Set("Content-Type", "text/plain")
// resultResp.Data is io.ReadCloser
defer resultResp.Data.Close()
return io.Copy(w, resultResp.Data)
}
UPDATE
How about
if f, ok := params.Writer.(http.Flusher); ok {
f.Flush()
}
?

How to return Document Index Name Value

This is a GoLang, Firebase AdminSDK question.
This example works to iterate through all of the documents in a FireStore DB.
How can I get the Document Name?
To put another way: If the collection name is JohnyCollection, and JohnyCollection has 20 Documents called (Document1, Document2.... Document20), how do I get the document name in golang Code?
//========================================
package main
import (
"context"
"fmt"
"log"
"firebase.google.com/go"
"google.golang.org/api/iterator"
"google.golang.org/api/option"
)
func check(e error) {
if e != nil {
panic(e)
}
}
func main() {
ctx := context.Background()
sa := option.WithCredentialsFile("./scai-qit-fb-adminsdk.json")
app, err := firebase.NewApp(ctx, nil, sa)
if err != nil {
log.Fatalf("error initializing app: %v\n", err)
}
client, err := app.Firestore(ctx)
if err != nil {
log.Fatal(err)
}
defer client.Close()
iter := client.Collection("COMPLEX_NONACS").Documents(ctx)
for {
doc, err := iter.Next()
if err == iterator.Done {
break
}
if err != nil {
log.Fatalf("Failed to iterate: %v", err)
}
//This part works. WIll return a Map of each Document
fmt.Println("--------------------------/n")
fmt.Println(doc.Data())
// This is the question. How do I get the INDEX name of the Document?
// something like...
fmt.Println(doc.Index_value_or_something_that_returns_IndexName())
// for example...
// {
// "ABC":{"line1":"yabba dabba","line2":"dingo dong"},
// "DEF":{"line1":"hooty tooty","line2":"blah blah"}
// }
// How to just get the "ABC" and "DEF"
}
}
You can get the document ID from the DocumentSnapshot, by first looking up the DocumentRef:
fmt.Println(doc.Ref.ID)
See the reference docs for DocumentSnapshot and DocumentRef.

Golang fatal error: concurrent map read and map write

I'm writing minecraft server in Go, when server is being stressed by 2000+ connections I get this crash:
fatal error: concurrent map read and map write/root/work/src/github.com/user/imoobler/limbo.go:78 +0x351
created by main.main /root/work/src/github.com/user/imoobler/limbo.go:33 +0x368
My code:
package main
import (
"log"
"net"
"bufio"
"time"
"math/rand"
"fmt"
)
var (
connCounter = 0
)
func main() {
InitConfig()
InitPackets()
port := int(config["port"].(float64))
ln, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
if err != nil {
log.Fatal(err)
}
log.Println("Server launched on port", port)
go KeepAlive()
for {
conn, err := ln.Accept()
if err != nil {
log.Print(err)
} else {
connCounter+=1
go HandleConnection(conn, connCounter)
}
}
}
func KeepAlive() {
r := rand.New(rand.NewSource(15768735131534))
keepalive := &PacketPlayKeepAlive{
id: 0,
}
for {
for _, player := range players {
if player.state == PLAY {
id := int(r.Uint32())
keepalive.id = id
player.keepalive = id
player.WritePacket(keepalive)
}
}
time.Sleep(20000000000)
}
}
func HandleConnection(conn net.Conn, id int) {
log.Printf("%s connected.", conn.RemoteAddr().String())
player := &Player {
id: id,
conn: conn,
state: HANDSHAKING,
protocol: V1_10,
io: &ConnReadWrite{
rdr: bufio.NewReader(conn),
wtr: bufio.NewWriter(conn),
},
inaddr: InAddr{
"",
0,
},
name: "",
uuid: "d979912c-bb24-4f23-a6ac-c32985a1e5d3",
keepalive: 0,
}
for {
packet, err := player.ReadPacket()
if err != nil {
break
}
CallEvent("packetReceived", packet)
}
player.unregister()
conn.Close()
log.Printf("%s disconnected.", conn.RemoteAddr().String())
}
For now server is only "limbo".
Generally speaking (without having access to the code where the error occurs) you have a few options. Here are two of them:
sync.RWMutex
Control access to the map with sync.RWMutex{}. Use this option if you have single reads and writes, not loops over the map. See RWMutex
Here a sample with access control to someMap via someMapMutex:
var (
someMap = map[string]string{}
someMapMutex = sync.RWMutex{}
)
go func() {
someMapMutex.Lock()
someMap["key"] = "value"
someMapMutex.Unlock()
}()
someMapMutex.RLock()
v, ok := someMap["key"]
someMapMutex.RUnlock()
if !ok {
fmt.Println("key missing")
return
}
fmt.Println(v)
syncmap.Map
Use a syncmap.Map{} instead of a normal map. This map is already taking care of race issues but may be slower depending on your usage. syncmap.Map{}s main advantage lies with for loops. See syncmap
var (
someMap = syncmap.Map{}
)
go func() {
someMap.Store("key", "value")
}()
v, ok := someMap.Load("key")
if !ok {
fmt.Println("key missing")
return
}
fmt.Println(v)
// with syncmap, looping over all keys is simple without locking the whole map for the entire loop
someMap.Range(func(key, value interface{}) bool {
// cast value to correct format
val, ok := value.(string)
if !ok {
// this will break iteration
return false
}
// do something with key/value
fmt.Println(key, val)
// this will continue iterating
return true
})
General Advice
You should test your server with -race option and then eliminate all the race conditions it throws. That way you can easier eliminate such errors before they occur.
go run -race server.go
See golang race detector

Golang: returning pointers and derefrencing

So I have a function getToken()
func getToken() jwt.MapClaims {
tokenString := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkYW0iLCJwYXNzd29yZCI6InRlc3QiLCJpYXQiOjE0ODcyMDY2OTIsImV4cCI6MTUxODc2NDI5Mn0.6LQo_gRwXiFBvNIJOwtf9UuxoQMZZ3XNILTnU-46-Zg"
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
}
hmacSampleSecret := []byte("supersecretkittysecret")
return hmacSampleSecret, nil
})
if err != nil {
println("error")
}
if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
return claims
} else {
return nil
}
}
Then the corresponding call:
res := getToken()
println(res["username"])
Why is res["username"] equal to two memory addresses (0x2b3c20,0xc420075420)? This should just be a string like adam. I have also tried func getToken() *jwt.MapClaims and return &claims, but this still did not help.
You should try using fmt.Println instead of println. Here's an example of printing a map using println vs fmt.Println
package main
import (
"fmt"
)
func foo() map[string]string {
return map[string]string{
"k": "value",
}
}
func main() {
res := foo()
println("Output from println:", res) // prints pointer address
fmt.Println("Output from fmt.Println: ", res) // prints the map
}
https://play.golang.org/p/gCNqng3KEE
Output:
Output from println: 0x10432200
Output from fmt.Println: map[k:value]

Resources