How to handle result - go

I'm working on a Go project that use a neo4j database. I don't fully understand how to handle results from my queries.
Under, this is my code that I managed to work but I would like to return user instead of return user.email as email, user.pseudo as pseudo. Here I have 2 return values, but what if I have more than 10 values to return... I'm not sure, I'm doing it properly.
data, err := session.ReadTransaction(func(tx neo4j.Transaction)(interface{}, error) {
res, err := tx.Run(
`match (user:User) where user.email = $email
return user.email as email, user.pseudo as pseudo`,
map[string]interface{}{"email": email})
if err != nil {
return nil, err
}
if res.Next() {
if pseudo, found := res.Record().Get("pseudo"); found {
userData.Pseudo = pseudo.(string)
}
if email, found := res.Record().Get("email"); found {
userData.Email = email.(string)
}
return userData, nil
}
return nil, nil
})
Thing is, when I return user := res.Record().Values()[0] instead of res.Record().Get("something"), I got an interface but I don't know how to extract the data specifying the id like user.email.
A fmt.Printf("%t", user) gives &{%!t(*types.Node=&{0 [User] map[email:testman#mail.com pseudo:testman]})}.
**UPDATE
data, err := session.ReadTransaction(func(tx neo4j.Transaction)(interface{}, error) {
res, err := tx.Run(`MATCH (user:User) WHERE user.email = $email RETURN user`, map[string]interface{}{"email": email})
if err != nil {
return nil, err
}
if res.Next() {
record, err := res.Single()
if err != nil {
fmt.Printf("%s\n", err.Error())
return nil, err
}
userRecord, found := record.Get("user")
if !found {
return nil, errors.New("User not found")
}
userAttributes := userRecord.(map[string]interface{})
userData.Email = userAttributes["email"].(string)
return userData, nil
}
return nil, nil
})

Welcome!
If you query ends with RETURN user and because it seems you want a single result, you can write:
record, err := res.Single()
// [...] check err
userRecord, err := record.Get("user")
// [...] check err
userAttributes := userRecord.(dbtype.Node).Props // 1st cast record into node and extract properties
userData.pseudo := userAttributes["pseudo"].(string) // then cast each property value to expected type
// ...

Related

the function x509.ParsePKCS8PrivateKey return rsa.privateKey. But can't use in the encryptPKCS1v15 function

const strPrivateKey = "30820b82020100300d06092a864886f70d010101050004820b6c30820b680201000282028100acfc585f43ca36ec2dddc518b5c7d1303b658faec58b634aff16ce4b7930b93a23517f8d9c8a260f4e2eb44b01da5b6588fefe63acb68c15677"
decoded, err := hex.DecodeString(strPrivateKey)
if err != nil {
return ""
}
privateKey, err := x509.ParsePKCS8PrivateKey(decoded)
if err != nil {
return ""
}
encypt, err := rsa.EncryptPKCS1v15(rand.Reader, &privateKey.PublicKey, data)
if err != nil {
fmt.Println(err)
return ""
}
privateKey.PublicKey undefined (type any has no field or method PublicKey)
According to the doc (https://pkg.go.dev/crypto/x509#go1.19.3#ParsePKCS8PrivateKey):
func ParsePKCS8PrivateKey(der []byte) (key any, err error)
...
It returns a *rsa.PrivateKey, a *ecdsa.PrivateKey, or a ed25519.PrivateKey. More types might be supported in the future.
You should use type assertion to check the type of the key:
switch privateKey := privateKey.(type) {
case *rsa.PrivateKey:
// ...
case *ecdsa.PrivateKey:
// ...
case ed25519.PrivateKey:
// ...
default:
panic("unknown key")
}
Since rsa.EncryptPKCS1v15 expects a *rsa.PublicKey, your code can be written like this:
if privateKey, ok := privateKey.(*rsa.PrivateKey); ok {
encypt, err := rsa.EncryptPKCS1v15(rand.Reader, &privateKey.PublicKey, data)
}
BTW, the provided strPrivateKey is invalid (encoding/hex: odd length hex string). You can get some valid private keys from https://github.com/golang/go/blob/1c05968c9a5d6432fc6f30196528f8f37287dd3d/src/crypto/x509/pkcs8_test.go#L52-L124
*correct answer. I resolved privateKey.(rsa.PrivateKey)
decodedString, err := hex.DecodeString(utility.StrPrivateKey)
if err != nil {
return err
}
pkcs8PrivateKey, err := x509.ParsePKCS8PrivateKey(decodedString)
if err != nil {
return err
}
privateKey := pkcs8PrivateKey.(*rsa.PrivateKey)

Atomically Execute commands across Redis Data Structures

I want to execute some redis commands atomically (HDel, SADD, HSet etc). I see the Watch feature in the go-redis to implement transactions , however since I am not going to modify the value of a key i.e use SET,GET etc , does it make sense to use Watch to execute it as transaction or just wrapping the commands in a TxPipeline would be good enough?
Approach 1 : Using Watch
func sampleTransaction() error{
transactionFunc := func(tx *redis.Tx) error {
// Get the current value or zero.
_, err := tx.TxPipelined(context.Background(), func(pipe redis.Pipeliner) error {
_, Err := tx.SAdd(context.Background(), "redis-set-key", "value1").Result()
if Err != nil {
return Err
}
_, deleteErr := tx.HDel(context.Background(), "redis-hash-key", "value1").Result()
if deleteErr != nil {
return deleteErr
}
return nil
})
return err
}
retries:=10
// Retry if the key has been changed.
for i := 0; i < retries; i++ {
fmt.Println("tries", i)
err := redisClient.Watch(context.Background(), transactionFunc())
if err == nil {
// Success.
return nil
}
if err == redis.TxFailedErr {
continue
}
return err
}
}
Approach 2: Just wrapping in TxPipelined
func sampleTransaction() error {
_, err:= tx.TxPipelined(context.Background(), func(pipe redis.Pipeliner) error {
_, Err := tx.SAdd(context.Background(), "redis-set-key", "value1").Result()
if Err != nil {
return Err
}
_, deleteErr := tx.HDel(context.Background(), "redis-hash-key", "value1").Result()
if deleteErr != nil {
return deleteErr
}
return nil
})
return err
}
As far as I know, pipelines do not guarantee atomicity. If you need atomicity, use lua.
https://pkg.go.dev/github.com/mediocregopher/radix.v3#NewEvalScript

How to return the errors in golang?

In this code, I am taking the data from the environment variable and using it to send the email to the given address. It only works if I exclude the errors from the code. I returned the errors and used them in middleware because I don't want to use log.Fatal(err) every time. But it gives me two errors.
1. cannot use "" (untyped string constant) as [4]string value in return statement
2. code can not execute unreachable return statement
func LoadEnvVariable(key string) (string, error) {
viper.SetConfigFile(".env")
err := viper.ReadInConfig()
return "", err
value, ok := viper.Get(key).(string)
if !ok {
return "", err
}
return value, nil
}
func Email(value [4]string) ([4]string, error) {
mail := gomail.NewMessage()
myEmail, err := LoadEnvVariable("EMAIL")
return "", err
appPassword, err := LoadEnvVariable("APP_PASSWORD")
return "", err
mail.SetHeader("From", myEmail)
mail.SetHeader("To", myEmail)
mail.SetHeader("Reply-To", value[1])
mail.SetHeader("subject", value[2])
mail.SetBody("text/plain", value[3])
a := gomail.NewDialer("smtp.gmail.com", 587, myEmail, appPassword)
err = a.DialAndSend(mail)
return "", err
return value, nil
}
use this to check error
if err != nil {
return "", err
}
func LoadEnvVariable(key string) (string, error) {
viper.SetConfigFile(".env")
err := viper.ReadInConfig()
if err != nil {
return "", err
}
value, ok := viper.Get(key).(string)
if !ok {
return "", err
}
return value, nil
}
func Email(value [4]string) ([4]string, error) {
mail := gomail.NewMessage()
myEmail, err := LoadEnvVariable("EMAIL")
if err != nil {
return "", err
}
appPassword, err := LoadEnvVariable("APP_PASSWORD")
if err != nil {
return "", err
}
mail.SetHeader("From", myEmail)
mail.SetHeader("To", myEmail)
mail.SetHeader("Reply-To", value[1])
mail.SetHeader("subject", value[2])
mail.SetBody("text/plain", value[3])
a := gomail.NewDialer("smtp.gmail.com", 587, myEmail, appPassword)
err = a.DialAndSend(mail)
if err != nil {
return "", err
}
return value, nil
}
Firstly, to error handle, check if err is nil before returning!
So not:
myEmail, err := LoadEnvVariable("EMAIL")
return "", err
but instead:
myEmail, err := LoadEnvVariable("EMAIL")
if err != nil {
return "", err
}
Secondly, a single string type "" is compatible to a fixed array size of [4]string. It may make your life easier to use named return variables, so you don't need to pass a explicit "zero" value in the event of an error. So define your function signature like so:
func Email(in [4]string) (out [4]string, err error) {
myEmail, err := LoadEnvVariable("EMAIL")
if err != nil {
return // implicitly returns 'out' and 'err'
}
// ...
out = in
return // implicitly returns 'out' and nil 'err'
}

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.

Update mongo db collection to create new field with unique values without impacting existing data using mongo go driver

I am new to mongo and mongo go driver. Need to add new field "uri" to my collection with existing data - using mongo go driver. New field needs to be populated with unique values so that unique index can be created on it. the collection uses _id as well, if there is a way we can populate new field based on _id field that will work as well.
I am trying below code, not sure how to populate unique values.
//Step1: update all documents to add new field with unique values
_, err := myColl.UpdateMany(
ctx,
bson.D{},// select all docs in collection
bson.D{
{"$set", bson.D{{"uri", GenerateRandomUniqueString()}}},
},
)
if err != nil {
return err
}
// then next step is to create index on this field:
key := bson.D{{"uri", 1}}
opt := options.Index().SetName("uri-index").SetUnique(true)
model := mongo.IndexModel{Keys: key, Options: opt}
_, err = myColl.Indexes().CreateOne(ctx, model)
if err != nil {
return err
}
Once the index is set up, old records will marked read only, but we can not delete those. New data will have unique 'uri' string value.
Any help is much appreciated.
Using above code fails while unique index creation, as the same value is used for backfill.
I tried this as well:
func BackFillUri(db *mongo.Database) error {
myColl := db.Collection("myColl")
ctx := context.Background()
cursor, err := myColl.Find(ctx, bson.M{})
if err != nil {
return err
}
defer cursor.Close(ctx)
for cursor.Next(ctx) {
var ds bson.M
if err = cursor.Decode(&ds); err != nil {
return err
}
_, err1 := myColl.UpdateOne(
ctx,
bson.D{"_id": ds.ObjectId},
bson.D{
{"$set", bson.D{{"uri", rand.Float64()}}},
},
)
if err1 != nil {
return err1
}
}
return nil
}
But i am getting quite a few errors and not sure if any of the above logic is correct
I finally used below code, hope it helps someone who's new like me :-)
const charset = "abcdefghijklmnopqrstuvwxyz" +
"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
func RandomUniqueString(length int) string {
return StringWithCharset(length, charset)
}
var seededRand *rand.Rand = rand.New(
rand.NewSource(time.Now().UnixNano()))
func StringWithCharset(length int, charset string) string {
b := make([]byte, length)
for i := range b {
b[i] = charset[seededRand.Intn(len(charset))]
}
return string(b)
}
// Adds index on uri
func AddUriIndex(db *mongo.Database) error {
mycoll := db.Collection("mycoll")
ctx := context.Background()
//backfill code starts
type attribute struct {
Key string `bson:"key"`
Value interface{} `bson:"value"`
}
type item struct {
ID primitive.ObjectID `bson:"_id"`
ResourceAttributes []attribute `bson:"resourceAttributes,omitempty"`
}
cursor, err := mycoll.Find(ctx, primitive.M{})
if err != nil {
return err
}
defer cursor.Close(ctx)
for cursor.Next(ctx) {
var result item
if err = cursor.Decode(&result); err != nil {
return err
}
//fmt.Println("Found() result:", result)
filter := primitive.M{"_id": result.ID}
update := primitive.M{"$set": primitive.M{"uri": RandomUniqueString(32)}}
if _, err := mycoll.UpdateOne(ctx, filter, update); err != nil {
return err
}
}
//add uri-index starts
key := bson.D{{"uri", 1}}
opt := options.Index().
SetName("uri-index").
SetUnique(true)
model := mongo.IndexModel{Keys: key, Options: opt}
_, err = mycoll.Indexes().CreateOne(ctx, model)
if err != nil {
return err
}
return nil
}

Resources