How can I check if a user already exists in the database using gorm? I cannot seem to find a proper way to do so without an error logging to the console if no user was found.
This is my code so far
result := models.User{}
err := connection.DB.First(&result, "username = ?", user.Username).Error
if err == gorm.ErrRecordNotFound {
if err := connection.DB.Create(&user).Error; err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"error": "internal server error",
})
}
return c.Status(fiber.StatusCreated).JSON(fiber.Map{
"message": "user created",
})
}
if result.Username != "" {
return c.Status(fiber.StatusConflict).JSON(fiber.Map{
"error": "username already exists",
})
}
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "internal server error",
})
but if a new user is created, an error is printed to the terminal saying the record was not found.
You can also use FirstOrCreate
From Doc:
Get first matched record or create a new one with given conditions (only works with struct, map conditions)
u := connection.DB.FirstOrCreate(user)
if u.Error != nil {
return c.Status(fiber.StatusInternalServerError).JSON(
fiber.Map{
"error": "Internal server error",
}
)
}
if u.RowsAffected == 1 {
return c.Status(fiber.StatusCreated).JSON(
fiber.Map{
"message": "User created successfully",
}
)
}
return c.Status(fiber.StatusBadRequest).JSON(
fiber.Map{
"error": "Username already exists",
}
)
Managed to find a way with raw MySQL.
var exists bool = false
if err := connection.DB.Raw(
"SELECT EXISTS(SELECT 1 FROM users WHERE username = ?)",
user.Username).
Scan(&exists).Error; err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"error": "internal server error",
})
}
if exists {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "Username already exists",
})
}
if err := connection.DB.Create(&user).Error; err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"error": "internal server error",
})
}
return c.Status(fiber.StatusCreated).JSON(fiber.Map{
"message": "User created successfully",
})
Related
I am trying to make a function where it hosts your file on the anonfiles.com website using the anonfiles API. Even thought I am correctly using the api, it always returns nil. Response is missing message.
func host(file string) {
fileBytes, err := ioutil.ReadFile(file)
if err != nil {
fmt.Println("\033[1;31mCommand > Host: Could not read file,", err, "\033[0m")
return
}
url := "https://api.anonfiles.com/upload"
request, err := http.NewRequest("POST", url, bytes.NewBuffer(fileBytes))
if err != nil {
fmt.Println("\033[1;31mCommand > Host: Could not post request,", err, "\033[0m")
return
}
request.Header.Set("Content-Type", "application/octet-stream")
client := &http.Client{}
response, err := client.Do(request)
if err != nil {
fmt.Println("\033[1;31mCommand > Host: Could not send request,", err, "\033[0m")
return
}
defer response.Body.Close()
body, err := ioutil.ReadAll(response.Body)
if err != nil {
fmt.Println("\033[1;31mCommand > Host: Could not read response,", err, "\033[0m")
return
}
var result map[string]interface{}
err = json.Unmarshal(body, &result)
if err != nil {
fmt.Println("\033[1;31mCommand > Host: Could not parse response,", err, "\033[0m")
return
}
if response.StatusCode == 200 {
if result["url"] == nil {
fmt.Println("\033[1;31mCommand > Host: Response is missing URL\033[0m")
return
}
fmt.Println("File hosted successfully:", result["url"].(string))
} else {
if result["message"] == nil {
fmt.Println("\033[1;31mCommand > Host: Response is missing message\033[0m")
return
}
fmt.Println("\033[1;31mCommand > Host:\033[0m", result["message"].(string))
}
}
I'd thought I'd take a moment to expand those comments into an answer.
First, as we we've already discussed, you're not using the correct API to upload files. If we modify your code to show the complete response body, like this:
client := &http.Client{}
response, err := client.Do(request)
if err != nil {
fmt.Println("\033[1;31mCommand > Host: Could not send request,", err, "\033[0m")
return
}
defer response.Body.Close()
body, err := ioutil.ReadAll(response.Body)
if err != nil {
fmt.Println("\033[1;31mCommand > Host: Could not read response,", err, "\033[0m")
return
}
fmt.Printf("BODY:\n%s\n", body)
We see the following:
{
"status": false,
"error": {
"message": "No file chosen.",
"type": "ERROR_FILE_NOT_PROVIDED",
"code": 10
}
}
We're getting this error because you're not providing the file parameter in a multipart/form-data request. The post to which I linked earlier has several examples of sending a multipart request; I've tested a couple of them and they seem to work as expected.
You're also making incorrect assumptions about the response returned by the API. If we make a successful request using curl and capture the response JSON, we find that it looks like this:
{
"status": true,
"data": {
"file": {
"url": {
"full": "https://anonfiles.com/k8cdobWey7/test_txt",
"short": "https://anonfiles.com/k8cdobWey7"
},
"metadata": {
"id": "k8cdobWey7",
"name": "test.txt",
"size": {
"bytes": 12,
"readable": "12 B"
}
}
}
}
}
Note that there is no response["url"] or response["message"]. If you want the URL for the uploaded file, you need to get response["data"]["file"]["url"]["full"] (or ["short"]).
Similarly, we can see examples of the error response above, which looks like this:
{
"status": false,
"error": {
"message": "No file chosen.",
"type": "ERROR_FILE_NOT_PROVIDED",
"code": 10
}
}
That's not result["message"]; that's result["error"]["message"].
Because you're unmarshalling into a map[string]interface, getting at these nested keys is going to be a bit of a pain. I found it easiest to create Go structs for the above responses, and just unmarshal into an appropriately typed variable.
That gets me the following types:
type (
AnonFilesUrl struct {
Full string `json:"full"`
Short string `json:"short"`
}
AnonFilesMetadata struct {
ID string `json:"id"`
Name string `json:"name"`
Size struct {
Bytes int `json:"bytes"`
Readable string `json:"readable"`
} `json:"size"`
}
AnonFilesData struct {
File struct {
URL AnonFilesUrl `json:"url"`
Metadata AnonFilesMetadata `json:"metadata"`
} `json:"file"`
}
AnonFilesError struct {
Message string
Type string
Code int
}
AnonFilesResponse struct {
Status bool `json:"status"`
Data AnonFilesData `json:"data"`
Error AnonFilesError `json:"error"`
}
)
And then unmarshalling the response looks like:
var result AnonFilesResponse
err = json.Unmarshal(body, &result)
And we can ask for fields like:
fmt.Printf("URL: %s\n", result.Data.File.URL.Full)
I am writing a user authentication system in go.First of all I prompt user to signup the form with email, username and password. Then I send a confirmation link to users email. The user must also select a title for his blog.Which is prompted after the confirmation link is clicked. How to ensure that the user don't move to the home page without a title.
My ConfirmEmail function is below:
func ConfirmEmail(w http.ResponseWriter, r *http.Request){
err := r.ParseForm()
if err != nil{
log.Fatal("Unable to parse data")
}
token := r.Form.Get("token")
db.ConnectDB()
current_time := time.Now().Unix()
user_id := 0
var date_generated int64
var date_expires int64
var date_used int64
row := db.Db.QueryRow("Select user_id, date_generated, date_expires, date_used from Token where token = ?", token)
if err := row.Scan(&user_id, &date_generated, &date_expires, &date_used); err != nil{
if err == sql.ErrNoRows{
//todo: no such token provide a link to signup..
fmt.Println("No such rows..")
} else {
log.Fatal("Something went wrong:", err)
}
}
//reuse of the token...
if (date_used != 0){
http.Redirect(w,r, "/signup", http.StatusFound)
}
// use of expired token...
if(date_expires < current_time){
//todo: inform about the expired token and prompt for re confirmation..
fmt.Println("Token expired..")
} else{
//todo: Check for blog title, if null prompt.
var title string
var username string
if err := db.Db.QueryRow("select username, blogTitle from User where user_id = ?", user_id).Scan(&username, &title); err != nil{
if err == sql.ErrNoRows{
http.Redirect(w, r, "/signup", http.StatusFound)
}
}
//want to do this until title is not provided..
if len(title) == 0{
err = templates.ExecuteTemplate(w, "chose-title.html", struct {
Username string
Msg string
}{
Username: username,
Msg: "",
})
if err != nil {
log.Fatal("Unable to render provided template:",err)
}
return
}
_, err = db.Db.Exec("Update Token set date_used = ? where token=?",current_time, token)
if err != nil {
log.Fatal("Unable to update with given data")
}
_, err = db.Db.Exec("Update User set Verified = true where user_id=?",user_id)
if err != nil {
log.Fatal("Unable to update with given data")
} else {
http.Redirect(w, r, "/login", http.StatusFound)
}
}
}
The main problematic part is:(contains snippet from previous block)
if len(title) == 0{
err = templates.ExecuteTemplate(w, "chose-title.html", struct {
Username string
Msg string
}{
Username: username,
Msg: "",
})
if err != nil {
log.Fatal("Unable to render provided template:",err)
}
return
}
_, err = db.Db.Exec("Update Token set date_used = ? where token=?",current_time, token)
if err != nil {
log.Fatal("Unable to update with given data")
}
_, err = db.Db.Exec("Update User set Verified = true where user_id=?",user_id)
if err != nil {
log.Fatal("Unable to update with given data")
} else {
http.Redirect(w, r, "/login", http.StatusFound)
}
}
I can think of a while loop in this, but don't think that would be a feasible option. Is there any other workaround or workflow to check this.
I am new to golang . and I want to get my response as multiple result. I do some method but I need to change that one
impartErrl := ph.profileService.ValidateSchema(gojsonschema.NewStringLoader(string(b)))
if impartErrl != nil {
ctx.JSON(http.StatusBadRequest, impart.ErrorResponse(impartErrl))
return
}
func (ps *profileService) ValidateSchema(document gojsonschema.JSONLoader) (errors []impart.Error) {
result, err := gojsonschema.Validate(ps.schemaValidator, document)
if err != nil {
ps.SugaredLogger.Error(err.Error())
return impart.ErrorResponse(
impart.NewError(impart.ErrBadRequest, "unable to validate schema"),
)
}
if result.Valid() {
return nil
}
// msg := fmt.Sprintf("%v validations errors.\n", len(result.Errors()))
msg := "validations errors"
for i, desc := range result.Errors() {
msg += fmt.Sprintf("%v: %s\n", i, desc)
er := impart.NewError(impart.ErrValidationError, fmt.Sprintf("%s ", desc), impart.ErrorKey(desc.Field()))
errors = append(errors, er)
}
return errors
}
func NewError(err error, msg string, args ...interface{}) Error {
key := GetErrorKey(args...)
return impartError{
err: err,
msg: msg,
key: key,
}
}
func ErrorResponse(err interface{}) []Error {
var errorResponse []Error
switch err.(type) {
case Error:
errorResponse = []Error{err.(Error)}
case []Error:
errorResponse = err.([]Error)
default:
errorResponse = []Error{
NewError(ErrUnknown, fmt.Sprintf("%v", err)),
}
}
return errorResponse
}
type Error interface {
error
HttpStatus() int
ToJson() string
Err() error
Msg() string
}
Now I am getting the output as
[
{
"error": "validation error",
"msg": "email: Does not match format 'email' ",
"key": "email"
},
{
"error": "validation error",
"msg": "screenName: String length must be greater than or equal to 4 ",
"key": "screenName"
}
]
but I require my response as
{
0 :{
"error": "validation error",
"msg": "email: Does not match format 'email' ",
"key": "email"
},
1 : {
"error": "unable to complete the request",
"msg": "invalid screen name, must be alphanumeric characters only",
"key": "screen_name"
}
}
How can I get these type of response. ? because in ios app while parsing the response [] showing error. so I need to change the output.
please help me.
The ErrorResponse func should return a map[int]Error instead of []Error. As Example:
func ErrorResponse(err interface{}) map[int]Error {
errorResponse := map[int]Error{}
switch e := err.(type) {
case Error:
errorResponse[0] = e
case []Error:
for i, k := range e {
errorResponse[i] = k
}
default:
errorResponse[0] = NewError(ErrUnknown, fmt.Sprintf("%v", err))
}
return errorResponse
}
I am trying to write a simple admission controller for pod naming (validation) but for some reason I am generating a wrong response.
Here is my code:
package main
import (
"fmt"
"encoding/json"
"io/ioutil"
"net/http"
"github.com/golang/glog"
// for Kubernetes
"k8s.io/api/admission/v1beta1"
"k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"regexp"
)
type myValidServerhandler struct {
}
// this is the handler fuction from the HTTP server
func (gs *myValidServerhandler) serve(w http.ResponseWriter, r *http.Request) {
var Body []byte
if r.Body != nil {
if data , err := ioutil.ReadAll(r.Body); err == nil {
Body = data
}
}
if len(Body) == 0 {
glog.Error("Unable to retrive Body from API")
http.Error(w,"Empty Body", http.StatusBadRequest)
return
}
glog.Info("Received Request")
// this is where I make sure the request is for the validation prefix
if r.URL.Path != "/validate" {
glog.Error("Not a Validataion String")
http.Error(w,"Not a Validataion String", http.StatusBadRequest)
return
}
// in this part the function takes the AdmissionReivew and make sure in is in the right
// JSON format
arRequest := &v1beta1.AdmissionReview{}
if err := json.Unmarshal(Body, arRequest); err != nil {
glog.Error("incorrect Body")
http.Error(w, "incorrect Body", http.StatusBadRequest)
return
}
raw := arRequest.Request.Object.Raw
pod := v1.Pod{}
if err := json.Unmarshal(raw, &pod); err != nil {
glog.Error("Error Deserializing Pod")
return
}
// this is where I make sure the pod name contains the kuku string
podnamingReg := regexp.MustCompile(`kuku`)
if podnamingReg.MatchString(string(pod.Name)) {
return
} else {
glog.Error("the pod does not contain \"kuku\"")
http.Error(w, "the pod does not contain \"kuku\"", http.StatusBadRequest)
return
}
// I think the main problem is with this part of the code because the
// error from the events I getting in the Kubernetes namespace is that
// I am sending 200 without a body response
arResponse := v1beta1.AdmissionReview{
Response: &v1beta1.AdmissionResponse{
Result: &metav1.Status{},
Allowed: true,
},
}
// generating the JSON response after the validation
resp, err := json.Marshal(arResponse)
if err != nil {
glog.Error("Can't encode response:", err)
http.Error(w, fmt.Sprintf("couldn't encode response: %v", err), http.StatusInternalServerError)
}
glog.Infof("Ready to write response ...")
if _, err := w.Write(resp); err != nil {
glog.Error("Can't write response", err)
http.Error(w, fmt.Sprintf("cloud not write response: %v", err), http.StatusInternalServerError)
}
}
The code is working as expected except for a positive output (where the pod name meets the criteria)
there is another file with a main just grabbing the TLS files and starting the HTTP service.
so after a few digging I found what was wrong with my code
first this part
if podnamingReg.MatchString(string(pod.Name)) {
return
} else {
glog.Error("the pod does not contain \"kuku\"")
http.Error(w, "the pod does not contain \"kuku\"", http.StatusBadRequest)
return
}
by writing "return" twice I discarded the rest of the code and more so I haven't attached the request UID to the response UID and because I am using the v1 and not the v1beta1 I needed to adding the APIVersion in the response
so the rest of the code looks like :
arResponse := v1beta1.AdmissionReview{
Response: &v1beta1.AdmissionResponse{
Result: &metav1.Status{},
Allowed: false,
},
}
podnamingReg := regexp.MustCompile(`kuku`)
if podnamingReg.MatchString(string(pod.Name)) {
fmt.Printf("the pod %s is up to the name standard", pod.Name)
arResponse.Response.Allowed = true
}
arResponse.APIVersion = "admission.k8s.io/v1"
arResponse.Kind = arRequest.Kind
arResponse.Response.UID = arRequest.Request.UID
so I needed to add the 2 parts and make sure that in case the pod name is not up to standard then I need to return the right response
I made a model serving server with Python Tornado library and its sole purpose is to accept http request with payload and return result in json. The request can be made with either application/json or multipart/form-data.
To authenticate and authorise users, I made another server with Golang echo library. So all user requests should reach here before reaching my resource server.
Here I have a problem, because my program requires images as input, so users will dispatch their request with FormData. When it first hit my Golang server, I need to do the following steps
Read the form file.
Save it in local disk.
Load the file and save it in a byte buffer.
Initialise a multipart writer
Make a request to my resource server
Got result, return to user
I feel like this is redundant as I imagine there is a way to propagate those request directly to my resource server (after auth is done), without having to go through the I/O parts.
My code currently looks like this, at this point authentication is done through middleware. Is there a way to optimise this flow?
func (h Handler) ProcessFormData(c echo.Context) error {
// some validation
file, err := c.FormFile("file")
if err != nil {
return c.JSON(http.StatusBadRequest, response.Exception{
Code: errcode.InvalidRequest,
Detail: "Invalid file uploaded",
Error: err,
})
}
filePath, err := fileUtil.SaveNetworkFile(file)
if err != nil {
return c.JSON(http.StatusInternalServerError, response.Exception{
Code: errcode.SystemError,
Detail: "Error when processing file",
Error: err,
})
}
f, err := os.Open(filePath)
if err != nil {
return c.JSON(http.StatusInternalServerError, response.Exception{
Code: errcode.SystemError,
Detail: "Error when processing file",
Error: err,
})
}
defer f.Close()
fi, err := f.Stat()
if err != nil {
return c.JSON(http.StatusInternalServerError, response.Exception{
Code: errcode.SystemError,
Detail: "Error when processing file",
Error: err,
})
}
var body bytes.Buffer
writer := multipart.NewWriter(&body)
part, err := writer.CreateFormFile("file", fi.Name())
if err != nil {
return c.JSON(http.StatusInternalServerError, response.Exception{
Code: errcode.SystemError,
Detail: "Error when processing file",
Error: err,
})
}
if _, err := io.Copy(part, f); err != nil {
return c.JSON(http.StatusInternalServerError, response.Exception{
Code: errcode.SystemError,
Detail: "Error when processing file",
Error: err,
})
}
writer.Close()
req, err := http.NewRequest("POST", fmt.Sprintf("%s", env.ResourceServer), &body)
req.Header.Set("Content-Type", writer.FormDataContentType())
if err != nil {
return c.JSON(http.StatusInternalServerError, response.Exception{
Code: errcode.APIRequestError,
Error: err,
})
}
client := &http.Client{}
res, err := client.Do(req)
if err != nil {
return c.JSON(http.StatusInternalServerError, response.Exception{
Code: errcode.APIRequestError,
Detail: "Error when posting request to resource server",
Error: err,
})
}
defer res.Body.Close()
data, _ := ioutil.ReadAll(res.Body)
if res.StatusCode != 200 {
errorData := &model.PanicResponse{}
err := json.Unmarshal(data, errorData)
if err != nil {
return c.JSON(http.StatusInternalServerError, response.Exception{
Code: errcode.UnmarshalError,
Error: err,
})
}
return c.JSON(res.StatusCode, errorData)
}
result := &model.SuccessResponse{}
err = json.Unmarshal(data, result)
if err != nil {
return c.JSON(http.StatusInternalServerError, response.Exception{
Code: errcode.UnmarshalError,
Error: err,
})
}
if fileUtil.IsFileExists(filePath) {
fileUtil.DeleteFile(filePath)
}
// track and update usage
userData := c.Get("USER")
user := userData.(model.User)
db.UpdateUsage(h.Db, &user.ID)
return c.JSON(200, result)
}
Found a solution thanks to the comment from #cerise-limón
Essentially, I need just 2 lines
f, err := file.Open()
if _, err := io.Copy(part, f); err != nil {
return c.JSON(http.StatusInternalServerError, response.Exception{
Code: errcode.SystemError,
Detail: "Error when processing file",
})
}