Using or in bind and validation? - go

I'm using go gin with validation package called ozzo-validation and when I bind json data and validation error like this:
err := c.ShouldBind(&user)
valErrors := user.LoginValidate()
if err != nil || valErrors != nil {
util.ErrorJSON(c, http.StatusBadRequest, err.Error())
return
}
I got errors in console:
/usr/local/go/src/net/http/server.go:2878 +0x43b
net/http.(*conn).serve(0xc0002cebe0, {0x17430c0, 0xc0004a6f30})
/usr/local/go/src/net/http/server.go:1929 +0xb08
created by net/http.(*Server).Serve
/usr/local/go/src/net/http/server.go:3033 +0x4e8
when I convert the above code to the following it works!
var user models.User
// Decode request data
if err := c.ShouldBind(&user); err != nil {
util.ErrorJSON(c, http.StatusBadRequest, err.Error())
return
}
// Validate Error
if err := user.LoginValidate(); err != nil {
util.ErrorJSON(c, http.StatusBadRequest, err.Error())
return
}

what is variable user? is it a model? why don't you declare validation on your model with validatin-ozzo like :
func (f *user) Validate() error {
if err := validation.Validate(f.name, validation.Required); err != nil {
return &response.ErrorResponse{
ErrorID: 422,
Msg: map[string]string{
"en": "name cannot be empty",
},
}
}
return nil
}
type ErrorResponse struct {
ErrorID int `json:"error_id"`
Msg map[string]string `json:"message"`
}
func (c *ErrorResponse) Error() string {
b, _ := json.Marshal(c)
return string(b)
}
// and validate it on your code like :
if err := user.Validate(); err != nil {
return nil, 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
}
}

How to update in BD only the fields filled in my struct?

I have a struct to get the data from the user and update the info in the database. However, if the user lets a field in a blank, the correspondent field into DB will be blank. I don't want that, I would like to edit only the fields that the user informed.
My model:
type Business struct {
Id uint64 `json:"id,omitempty"`
Company_name string `json:"company_name,omitempty"`
Trading_name string `json:"trading_name,omitempty"`
Facebook string `json:"facebook,omitempty"`
Instagram string `json:"instagram,omitempty"`
Tel string `json:"tel,omitempty"`
User_id uint64 `json:"user_id,omitempty"`
}
My controller:
func EditBusinessInfo(w http.ResponseWriter, r *http.Request) {
params := mux.Vars(r)
businessIDParams, err := strconv.ParseUint(params["businessID"], 10, 64)
if err != nil {
returns.ERROR(w, http.StatusBadRequest, err)
return
}
userIDInToken, err := auth.ExtractUserID(r)
if err != nil {
returns.ERROR(w, http.StatusInternalServerError, err)
return
}
db, err := db.ConnectToDB()
if err != nil {
returns.ERROR(w, http.StatusInternalServerError, err)
return
}
defer db.Close()
repository := repositories.NewUsersRepository(db)
businessInBD, err := repository.GetBusiness(businessIDParams)
if err != nil {
returns.ERROR(w, http.StatusInternalServerError, err)
return
}
if userIDInToken != businessInBD.User_id {
returns.ERROR(w, http.StatusUnauthorized, errors.New("você não pode editar a empresa de outra pessoa"))
return
}
if businessIDParams != businessInBD.Id {
returns.ERROR(w, http.StatusForbidden, errors.New("essa empresa não peertence a você"))
return
}
bodyRequest, err := ioutil.ReadAll(r.Body)
if err != nil {
returns.ERROR(w, http.StatusBadRequest, err)
return
}
var business models.Business
if err := json.Unmarshal(bodyRequest, &business); err != nil {
returns.ERROR(w, http.StatusUnprocessableEntity, err)
return
}
if err := repository.EditBusinessInfo(userIDInToken, business); err != nil {
returns.ERROR(w, http.StatusInternalServerError, err)
return
}
returns.JSON_RESPONSE(w, http.StatusOK, nil)
}
An int and string both have default values, so if you don't assign a value to them they will be populated with their default value (0 or ""). Since they will always have a value assigned, the omitempty tag will never come into play.
A common solution to this issue is to make your struct fields be pointers, if a pointer isn't set then it is nil. the nil value will then trigger the json marshaler to recognize the omitempty tag. And when you insert to you DB those values will be null/nil as well.
You should evaluate which fields need a value and which can be allowed to be empty in case your DB has integrity constraints. You will also have to add nil checks in your code when working with data.

Reuse a method on a struct

I have a method that takes a URL to a yaml file and unmarshals it into a struct. There's nothing unique about it so I'd like to use it on another struct.
type SWConfig struct {
Name string `yaml:"name"`
Version string `yaml:"version"`
BuildType string `yaml:"buildType"`
}
func (c *SWConfig) getConfiguration(url string) {
resp, err := http.Get(url)
if err != nil {
log.Fatalf("ERROR: GET request failed: %s\n", err.Error())
}
if resp.StatusCode != http.StatusOK {
log.Fatalf("ERROR: %v: %s\n", resp.Status, url)
}
source, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatalf("ERROR: could not read response: %s\n", err.Error())
}
err = yaml.Unmarshal(source, c)
if err != nil {
log.Fatalf("ERROR: could not read YAML: %s\n", err.Error())
}
}
var swConfig SWConfig
swConfig.getConfiguration(swURL)
fmt.Println(swConfig.Name)
The other struct would have different fields, but again that shouldn't matter for this method. Is it possible to reuse this method or do I need to convert it to a function?
Use this utility function. It works with a pointer to any type.
// fetchYAML unmarshals the YAML document at url to the value
// pointed to by v.
func fetchYAML(url string, v interface{}) error {
resp, err := http.Get(url)
if err != nil {
return err
}
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("bad status: %v: %s\n", resp.Status, url)
}
source, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
err = yaml.Unmarshal(source, v)
return err
}
Call it like this:
func (c *SWConfig) getConfiguration(url string) {
if err := fetchYAML(url, c); err != nil {
log.Fatalf("ERROR: get value failed: %s\n", err.Error())
}
}

Error unmarshalling message body from amqp

I'm trying to send a JSON inside the body of the message using AMQP and receive it. The message sends without any error, however I can't unmarshall it on the other side. I get a unexpected end of JSON input error.
JSON:
var jsonStr = []byte(`
{
"name": "Test",
"phone_number": "12345"
}`)
err = files.SendMessageToCore(jsonStr, "event.saveorder")
if err != nil {
logr.Error("Error sending a message to rabbitmq:", fmt.Sprintf("%v", err))
}
SendMessageToCore function:
func SendMessageToCore(body []byte, routingKey string) error {
pb, err := GetRabbitPublisher()
if err != nil {
return err
}
var message amqp.Message
message.Exchange = "example"
message.RoutingKey = routingKey
message.Body = body
messageByte, err := json.Marshal(message)
if err != nil {
return err
}
err = (*pb).Publish(amqp.Message{Exchange: message.Exchange, RoutingKey: message.RoutingKey, Body: messageByte})
if err != nil {
return err
}
fmt.Println("Message was successfully sent")
return nil
}
Unmarshalling part:
func SaveOrderEventHandler(mes amqp.Message) *amqp.Message {
var incomingMessage amqp.Message
if err := json.Unmarshal(mes.Body, &incomingMessage); err != nil {
fmt.Println(err)
return nil
}
var user foodDomain.User
if err := json.Unmarshal(incomingMessage.Body, &user); err != nil {
fmt.Println("Error unmarshalling incomingMessage body: ", err) //Error appears here
return nil
}
fmt.Println(user.PhoneNumber)
return nil
}
User struct:
type User struct {
Name string `json:"name"`
PhoneNumber string `json:"phone_number"`
}
In your SendMessageToCore you already initialized amqb.Message. No need to re-initialize in the Publish func, just pass your initialized message. Also, your json string body is already in jsonByte, so no need to marshal it once more.
For instance -
func SendMessageToCore(body []byte, routingKey string) error {
pb, err := GetRabbitPublisher()
if err != nil {
return err
}
var message amqp.Message
message.Exchange = "example"
message.RoutingKey = routingKey
message.Body = body
err = (*pb).Publish(message)
if err != nil {
return err
}
fmt.Println("Message was successfully sent")
return nil
}
And your mes already in amqp.Message type, so no need to unmarshal mes.Body into amqp.Message type variable.
Just directly unmarshal your mes.Body into user.
func SaveOrderEventHandler(mes amqp.Message) *amqp.Message {
var user foodDomain.User
if err := json.Unmarshal(mes.Body, &user); err != nil {
fmt.Println(err)
return nil
}
fmt.Println(user.PhoneNumber)
return nil
}

I am trying to test an REST Api client in Golang using mux and httptest

Here I am trying to write a test for a REST client, by writing the apiOutput into the http.ResponseWriter but I always receive {nil nil} as apiResponse.
Can someone help me in pointing out the error ?
func Test_Do(t *testing.T) {
mux := http.NewServeMux()
server := httptest.NewServer(mux)
t.Cleanup(server.Close)
config := NewClientConfig()
config.BaseURL = server.URL
client, err := NewClient(config)
if err != nil {
t.Fatal(err)
}
apiResponse := struct {
Id string `json:"id"`
Error error `json:"error"`
Data interface{} `json:"data"`
}{}
apiOutput := []byte(`{
"id":"1",
"error":nil,
"data":[{"IP": "1.2.2.3"}]}`)
mux.HandleFunc("/api/v1/hosts", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Write(apiOutput)
})
t.Run("Get Host Details", func(t *testing.T) {
req, err := client.MakeGetRequest(http.MethodGet, "/api/v1/hosts", nil)
if err != nil {
t.Fatal(err)
}
resp, err1 := client.Do(req, &apiResponse)
if err1 != nil {
t.Fatalf("failed with statusCode: %d, error: %s", resp.StatusCode, err1)
}
if apiResponse.Id != "1" || apiResponse.Error != nil {
t.Errorf("Client.Do() problem unmarshaling problem: got %#v", apiResponse)
}
fmt.Printf("%v", apiResponse)
})
}
func (c *Client) Do(req *http.Request, v interface{}) (*http.Response, error) {
resp, err := c.HTTPClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if errorResponse := c.CheckResponse(resp); errorResponse != nil {
return resp, errorResponse
}
if v != nil {
if w, ok := v.(io.Writer); ok {
io.Copy(w, resp.Body)
} else {
decErr := json.NewDecoder(resp.Body).Decode(v)
if decErr == io.EOF {
decErr = nil // ignore EOF errors caused by empty response body
}
if decErr != nil {
err = decErr
}
}
}
return resp, err
}
Output:
Test_Do/Get_Host_Details: clients_test.go:269: Client.Do() problem unmarshaling problem: got struct { Id string "json:\"id\""; Error error "json:\"error\""; Data interface {} "json:\"data\"" }{Id:"", Error:error(nil), Data:interface {}(nil)}
{ <nil> <nil>}

Resources