So... I'm creating a RESTful API for my idea using Gin framework and I've came into the following problem -
Let's say that I've got the following endpoints:
/a/:id/*action
/b/:id/*action
/c/:id/*action
So, obviously, when I'm not giving any action then I want to return the data for the given ID. Meaning, I'm doing nothing but querying some data and returning it, this means that the functionality is basically the same and only the returned data is different.
Here's an example code of mine -
func GetBusiness(c *gin.Context) {
businessID, err := strconv.Atoi(c.Param("id"))
if businessID == 0 || err != nil {
c.JSON(http.StatusBadRequest, gin.H{"success": false, "errorMessage": "Missing ID"})
}
business := &Business{}
business, err = business.Get(businessID)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"success": false, "errorMessage": "Business not found"})
}
c.JSON(http.StatusOK, business)
}
So, obviously, business can become user or anything else. So, after this long exposition, my question to you goers, is, how can I prevent code duplication in this kind of situation? I've tried using an interface but I'm still struggling with the OO nature of Go, so I would really appriciate any help.
Thanks in advance!
There are a few things you can do to reduce code duplication, but unfortunately, you will always be writing some boilerplate in go, because of it's explicit error handling and lack of OOP-ness. (which is not necessarily a bad thing!).
So my only suggestions at the moment is to put common functionality in middleware handlers and restructure your code a litte, for example:
parseIdMiddleware := func(c *gin.Context) {
id, err := strconv.Atoi(c.Param("id"))
if businessID == 0 || err != nil {
c.AbortWithError(http.StatusBadRequest, errors.New("Missing ID"))
return
}
c.Set("id", id)
}
...
gin.Use(gin.ErrorLogger(), parseIdMiddleware)
and rewrite your handlers to
func GetBusiness(c *gin.Context) {
id := c.MustGet("id").(int)
business, err := store.GetBusiness(id)
if err != nil {
c.AbortWithError(http.StatusBadRequest, err)
return // don't forget this!
}
c.JSON(http.StatusOK, business)
}
And as always, read other people's code! I recommend https://github.com/drone/drone. That should give you a pretty good overview of how to structure your code.
Related
How do I create a proper mongo based application in Go using the official driver(go.mongodb.org/mongo-driver/mongo)? I have a MongoConnect() and MongoDisconnect(client) function to create a connection and delete it. But, it's not too efficient and starts leaking FD as the app has got around 40 functions and finding all the missed MongoDisconnect() becomes hectic.
The current MongoConnect and MongoDisconnect are as follows.
func MongoConnect() (*mongo.Client, error) {
clientOptions := options.Client().ApplyURI("mongodb://localhost:27017")
client, err := mongo.Connect(context.TODO(), clientOptions)
if err != nil {
Logger(err)
return nil, err
}
err = client.Ping(context.TODO(), nil)
if err != nil {
Logger(err)
return nil, err
}
return client, err
}
func MongoDisconnect(client *mongo.Client) {
_ = client.Disconnect(context.TODO())
}
I am looking for a method that would still use MongoConnect() to create connections and would automatically kill the client without the usage of MongoDisconnect().
PS. Other methods that are better than the above requirement are also welcome
I'm not sure that there is an 'efficient' way to fix the underlying issue, you probably will need to look at all the places where you've called MongoConnect() and ensure you have a corresponding MongoDisconnect().
In saying that, what you might want to look at is implemententing the right pattern for connecting to databases.
Generally speaking, if your database driver takes care of managing connections for you then you should create the connection once and just pass it around as needed.
You could also defer the closing of that connection to a go routine which would close it once it was no longer needed (when you're application is shutting down).
Here is a code snippet of how this is implemented:
// =========================================================================
// Start Database
log.Println("main: Initializing database support")
db, err := database.Open(database.Config{
User: cfg.DB.User,
Password: cfg.DB.Password,
Host: cfg.DB.Host,
Name: cfg.DB.Name,
DisableTLS: cfg.DB.DisableTLS,
})
if err != nil {
return errors.Wrap(err, "connecting to db")
}
defer func() {
log.Printf("main: Database Stopping : %s", cfg.DB.Host)
db.Close()
}()
I didn't write this code and its part of a larger project scaffold which has some other nice patterns for web applications.
Ultimate service
I store a double linked list in PostgreSQL. I have a Go API to manage this list.
There is a function that creates new Node (in specific position). Let's assume there is an INSERT SQL query inside of it.
Also, there is a function that deletes Node (by id). Let's assume there is a DELETE SQL query inside of it.
It is well known that if you need to move a Node to different position you should call DeleteNode() function and CreateNode() function. So there is the third function called MoveNode()
func MoveNode() error {
if err := DeleteNode(); err != nil {
return err
}
if err := CreateNode(); err != nil {
return err
}
return nil
}
But these functions (which are inside of MoveNode() should be called in one transaction.
Is there a way to "merge" functions in Go? Or what is the way to solve this problem (except copy & paste code from 2 functions to the third)?
p.s The idea is simple: you have two functions which do some SQL queries and you need to do these queries in one transaction (or call 2 functions in one transaction)
The better way to go about this here will be to move tx.Commit() outside the query execution functions (DeleteNode() and CreateNode() here)
Suggested Solution :
func MoveNode() error {
tx, err := db.Begin()
// err handling
res, err := DeleteNode(tx)
// err handling
res, err := CreateNode(tx)
// err handling
tx.Commit()
}
func DeleteNode(transactionFromDbBegin) (responseFromExec, errorFromExec) {
//...
}
func CreateNode(transactionFromDbBegin) (responseFromExec, errorFromExec) {
//...
}
This should do the trick.
I would like to handle a postgresql unique constraint error by accessing the error code. I have gone through these articles here and here and also gone through the documentation here, but still don't quite understand how to implement this and access the errorcode returned by the db. It seems only the Message field method was implemented:
func (err Error) Error() string {
return "pq: " + err.Message
}
If I want to access the SQLSTATE code, Do I implement something like:
func (err *Error) Error() string {
return err.Code
}
and assume the Error struct as defined here will be available.
I have tried something in my handler like this:
sqlInsert := INSERT INTO usrtable (usrCode, teamName, email, phone) VALUES ($1,$2,$3,$4)
_, err := db.Exec(sqlInsert, Data.UsrCode, Data.Teamname, Data.Email, Data.Phone)
if err != nil {
switch err {
case errorCodeNames["23505"]:
// Return web page identifying field and advising user what to do.
return
This returns undefined which makes sense since errorCodeNames is not exported but I am stumped regarding how to achieve this.
You could do something like this if you are using Postgres:
if pgErr, ok := err.(*pq.Error); ok {
if pgErr.Code == "23505" {
//handle duplicate insert
}
}
I writing a client that connects to a server with REST endpoints. The client needs to make a chain of 11 different requests to complete an action (it's a rococo backup system).
I'm writing my client in Go and I also want to write my mocks/tests in Go. What I'm unclear about is how a test called func TestMain would call into the client's func main(), to test completion of the chain of 11 requests.
My client's binary would be run from the shell in the following way:
$ client_id=12345 region=apac3 backup
How would I call func main() from the tests, with environment variables set? Or is there another approach? (I'm comfortable writing tests, so that's not the issue)
I'm looking at the Advanced Example in jarcoal/httpmock (but I could use another library). At the end the example says // do stuff that adds and checks articles, is that where I would call main()?
I've pasted the Advanced Example below, for future reference.
func TestFetchArticles(t *testing.T) {
httpmock.Activate()
defer httpmock.DeactivateAndReset()
// our database of articles
articles := make([]map[string]interface{}, 0)
// mock to list out the articles
httpmock.RegisterResponder("GET", "https://api.mybiz.com/articles.json",
func(req *http.Request) (*http.Response, error) {
resp, err := httpmock.NewJsonResponse(200, articles)
if err != nil {
return httpmock.NewStringResponse(500, ""), nil
}
return resp, nil
},
)
// mock to add a new article
httpmock.RegisterResponder("POST", "https://api.mybiz.com/articles.json",
func(req *http.Request) (*http.Response, error) {
article := make(map[string]interface{})
if err := json.NewDecoder(req.Body).Decode(&article); err != nil {
return httpmock.NewStringResponse(400, ""), nil
}
articles = append(articles, article)
resp, err := httpmock.NewJsonResponse(200, article)
if err != nil {
return httpmock.NewStringResponse(500, ""), nil
}
return resp, nil
},
)
// do stuff that adds and checks articles
}
Writing this out helped me answer my own question.
main() would read in environment variables and then call a function like doBackup(client_id, region). My test would mock the endpoints and then call doBackup(client_id, region).
I am using the GOB encoding for my project and i figured out (after a long fight) that empty strings are not encoded/decoded correctly. In my code i use a errormessage (string) to report any problems, this errormessage is most of the time empty. If i encode a empty string, it become nothing, and this gives me a problem with decoding. I don't want to alter the encoding/decoding because these parts are used the most.
How can i tell Go how to encode/decode empty strings?
Example:
Playground working code.
Playground not working code.
The problem isn't the encoding/gob module, but instead the custom MarshalBinary/UnmarshalBinary methods you've declared for Msg, which can't correctly round trip an empty string. There are two ways you could go here:
Get rid of the MarshalBinary/UnmarshalBinary methods and rely on GOB's default encoding for structures. This change alone wont' be enough because the fields of the structure aren't exported. If you're happy to export the fields then this is the simplest option: https://play.golang.org/p/rwzxTtaIh2
Use an encoding that can correctly round trip empty strings. One simple option would be to use GOB itself to encode the struct fields:
func (m Msg) MarshalBinary() ([]byte, error) {
var b bytes.Buffer
enc := gob.NewEncoder(&b)
if err := enc.Encode(m.x); err != nil {
return nil, err
}
if err := enc.Encode(m.y); err != nil {
return nil, err
}
if err := enc.Encode(m.z); err != nil {
return nil, err
}
return b.Bytes(), nil
}
// UnmarshalBinary modifies the receiver so it must take a pointer receiver.
func (m *Msg) UnmarshalBinary(data []byte) error {
dec := gob.NewDecoder(bytes.NewBuffer(data))
if err := dec.Decode(&m.x); err != nil {
return err
}
if err := dec.Decode(&m.y); err != nil {
return err
}
return dec.Decode(&m.z)
}
You can experiment with this example here: https://play.golang.org/p/oNXgt88FtK
The first option is obviously easier, but the second might be useful if your real example is a little more complex. Be careful with custom encoders though: GOB includes a few features that are intended to detect incompatibilities (e.g. if you add a field to a struct and try to decode old data), which are missing from this custom encoding.