Create one-to-one association in transaction - go

UserService.go
func (service UserService) Create(model *models.User) (*models.User, error) {
db, err := database.GetConnection()
if err != nil {
return nil, err
}
tx := db.Begin()
if tx.Error != nil {
fmt.Errorf("%v", err)
return nil, err
}
if err := tx.Create(model).Error; err != nil {
fmt.Errorf("%v", err)
tx.Rollback()
return nil, err
}
if err := tx.Save(model).Error; err != nil {
fmt.Errorf("%v", err)
tx.Rollback()
return nil, err
}
if err := service.UserProfileService.Create(tx, model); err != nil {
fmt.Errorf("%v", err)
tx.Rollback()
return nil, err
}
if err := tx.Commit().Error; err != nil {
fmt.Errorf("%v", err)
return nil, err
}
return model, nil
}
UserProfileService.go
func (UserProfileService) Create(tx *gorm.DB, user *models.User) error {
if err := tx.Create(models.UserProfile{User: user}).Error; err != nil {
return err
}
return nil
}
User.go
type User struct {
Id *uuid.UUID `json:"id" gorm:"not null;primary_key;type:uuid;default:uuid_generate_v4();"`
Name *string `json:"name" gorm:"not null;type:varchar(255);"`
CreatedAt time.Time `json:"createdAt" gorm:"type:timestamp;default:now()"`
UpdatedAt time.Time `json:"updatedAt" gorm:"type:timestamp;default:now()"`
DeletedAt *time.Time `json:"deleteAt,omitempty" gorm:"type:timestamp;"`
}
func (User) TableName() string {
return "users"
}
UserProfile.go
type UserProfile struct {
User *User `json:"id" gorm:"not null;primary_key;foreignkey:Id;type:uuid;default:uuid_generate_v4();"`
Id *uuid.UUID `json:"id" gorm:"not null;type:uuid"`
Description string `json:"description" gorm:"not null;type:varchar(255)"`
UpdatedAt time.Time `json:"updatedAt" gorm:"type:timestamp;default:now()"`
}
func (UserProfile) TableName() string {
return "users_profile"
}
So, every User must have just one UserProfile, that is why UserProfile's primary key is also a foreign key (User (id)). But when I try to make a request, I got this error: (using unaddressable value)
Any ideas on how to get this working?

The model that you are inserting in UserProfileService.go is not a pointer, which is why you are getting the unaddressable error.
func (UserProfileService) Create(tx *gorm.DB, user *models.User) error {
if err := tx.Create(*models.UserProfile{User: user}).Error; err != nil {
return err
}
return nil
}
That referencing in the tx.Create call will most likely fix your error. If not please post the full error.

Related

ValidateCreate function not called on ValidateAndCreate

I'm usign buffalo to build a simple backend to store values.
I have a Target struct defined as follow:
type Target struct {
ID uuid.UUID `json:"id" db:"id"`
Name string `json:"name" db:"name"`
Description string `json:"description" db:"description"`
Observations []Observation `json:"values,omitempty" has_many:"observations" order_by:"updated_at desc"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
}
// ...
func (t *Target) Validate(tx *pop.Connection) (*validate.Errors, error) {
return validate.Validate(
&validators.StringIsPresent{Field: t.Name, Name: "Name"},
&validators.StringLengthInRange{Field: t.Name, Name: "Name", Min: 2, Max: 15},
), nil
}
func (t *Target) ValidateCreate(tx *pop.Connection) (*validate.Errors, error) {
return t.Validate(tx)
}
func (t *Target) ValidateUpdate(tx *pop.Connection) (*validate.Errors, error) {
return t.Validate(tx)
}
A new Target is created using the following action:
func TargetAdd(c buffalo.Context) error {
body, err := io.ReadAll(c.Request().Body)
if err != nil {
log.Get().Error("Error reading body: %v", err)
return err
}
target := &models.Target{}
json.Unmarshal([]byte(body), target)
vErr, err := models.DB.ValidateAndCreate(target)
if err != nil {
log.Get().Error("entity not valid: %s", err)
return response.SendGeneralError(c, err)
}
if vErr.HasAny() {
log.Get().Error("entity not valid: %s", vErr)
return response.SendGeneralError(c, err)
}
return response.SendOKResponse(c, target)
}
The problem is that the ValidateAndCreate function does not call any of the validation functions defined for the Target model, even if it should be so (link to documentation).
Debugging the issue, I found that in the validation.go file, in this function
func (m *Model) validateCreate(c *Connection) (*validate.Errors, error) {
return m.iterateAndValidate(func(model *Model) (*validate.Errors, error) {
verrs, err := model.validate(c)
if err != nil {
return verrs, err
}
if x, ok := model.Value.(validateCreateable); ok {
vs, err := x.ValidateCreate(c)
if vs != nil {
verrs.Append(vs)
}
if err != nil {
return verrs, err
}
}
return verrs, err
})
}
the call to model.Value.(validateCreateable) seems to return ok to false.
Can someone please explain me where's the problem and how it's possibile to validate a model? Thanks.
EDIT:
Changing the import from
"github.com/gobuffalo/validate"
"github.com/gobuffalo/validate/validators"
to
"github.com/gobuffalo/validate/v3"
"github.com/gobuffalo/validate/v3/validators"
seems to fix the problem I have

I want to create Access Rights by using MSPID for different organisations

My idea to do this was to use the MSPID. So I would get the MSPID of the organisation, if it is the wrong MSPID then it will return an error message and if it is the right MSPID then continue. In my code I only did it in the DeleteAsset function however I would like to do it in the CreateAsset, UpdateAsset, TransferAsset and DeleteAsset functions.
The problem is that when I go to use the CreateAsset function on the command line it gives me this error:
peer chaincode invoke "${TARGET_TLS_OPTIONS[#]}" -C mychannel -n basic -c '{"function":"CreateAsset","Args":["Asset1","Andrew Faure","20","None","O+", "Penicilin", "None", "Family Doctor", "Fractured Leg", "10/05/2022"]}'
Error: endorsement failure during invoke. response: status:500 message:"error in simulation: failed to execute transaction 9446e0c320832b92f06ea56f577a29e9a5ec94c276329cb1d31bb8a85581138d: could not launch chaincode basic_1.0:605aa3a69a8107c9c7b5fc22072554235d3ea827b0eefbffae29f0c9998bf0e6: chaincode registration failed: container exited with 2"
Without the MSPID the system works fine. However, I need the MSPID in order to create access rights to organisations.
I'm new to Hyperledger and Go lang. Would really appreciate it if anyone can help
package chaincode
import (
"encoding/json"
"fmt"
"github.com/hyperledger/fabric-chaincode-go/pkg/cid"
"github.com/hyperledger/fabric-chaincode-go/shim"
"github.com/hyperledger/fabric-contract-api-go/contractapi"
)
// SmartContract provides functions for managing an Asset
type SmartContract struct {
contractapi.Contract
}
type Asset struct {
ID string `json:"ID"`
Owner string `json:"Owner"`
Age int `json:"Age"`
MedicalFamilyHistory string `json:"MedicalFamilyHistory"`
BloodType string `json:"BloodType"`
Allergies string `json:"Allergies"`
Medication string `json:"Medication"`
DataSharedWith string `json:"DataSharedWith"`
CurrentIssue string `json:"CurrentIssue"`
Date string `json:"Date"`
}
func (s *SmartContract) CreateAsset(ctx contractapi.TransactionContextInterface, id
string, owner string, age int, medicalFamilyHistory string, bloodType string,
allergies string, medication string, dataSharedWith string, currentIssue
string, date string) error {
exists, err := s.AssetExists(ctx, id)
if err != nil {
return err
}
if exists {
return fmt.Errorf("the asset %s already exists", id)
}
asset := Asset{
ID: id,
Owner: owner,
Age: age,
MedicalFamilyHistory: medicalFamilyHistory,
BloodType: bloodType,
Allergies: allergies,
Medication: medication,
DataSharedWith: dataSharedWith,
CurrentIssue: currentIssue,
Date: date,
}
assetJSON, err := json.Marshal(asset)
if err != nil {
return err
}
return ctx.GetStub().PutState(id, assetJSON)
}
func (s *SmartContract) ReadAsset(ctx contractapi.TransactionContextInterface, id
string) (*Asset, error) {
assetJSON, err := ctx.GetStub().GetState(id)
if err != nil {
return nil, fmt.Errorf("failed to read from world state: %v", err)
}
if assetJSON == nil {
return nil, fmt.Errorf("the asset %s does not exist", id)
}
var asset Asset
err = json.Unmarshal(assetJSON, &asset)
if err != nil {
return nil, err
}
return &asset, nil
}
func (s *SmartContract) UpdateAsset(ctx contractapi.TransactionContextInterface, id
string, owner string, age int, medicalFamilyHistory string, bloodType string,
allergies string, medication string, dataSharedWith string, currentIssue string, date
string) error {
exists, err := s.AssetExists(ctx, id)
if err != nil {
return err
}
if !exists {
return fmt.Errorf("the asset %s does not exist", id)
}
// overwriting original asset with new asset
asset := Asset{
ID: id,
Owner: owner,
Age: age,
MedicalFamilyHistory: medicalFamilyHistory,
BloodType: bloodType,
Allergies: allergies,
Medication: medication,
DataSharedWith: dataSharedWith,
CurrentIssue: currentIssue,
Date: date,
}
assetJSON, err := json.Marshal(asset)
if err != nil {
return err
}
return ctx.GetStub().PutState(id, assetJSON)
}
func (s *SmartContract) DeleteAsset(ctx contractapi.TransactionContextInterface, stub
shim.ChaincodeStubInterface, id string) error {
msp, err := s.GetMSPID(stub)
if err != nil {
return err
}
// The error is with "Org1MSP" becuase I found out that mspid is Org1MSP
if msp != "org1MSP" {
return fmt.Errorf("Wrong Organisation, this organisation does not have access
to this function")
}
exists, err := s.AssetExists(ctx, id)
if err != nil {
return err
}
if !exists {
return fmt.Errorf("the asset %s does not exist", id)
}
return ctx.GetStub().DelState(id)
}
func (s *SmartContract) AssetExists(ctx contractapi.TransactionContextInterface, id
string) (bool, error) {
assetJSON, err := ctx.GetStub().GetState(id)
if err != nil {
return false, fmt.Errorf("failed to read from world state: %v", err)
}
return assetJSON != nil, nil
}
func (s *SmartContract) TransferAsset(ctx contractapi.TransactionContextInterface, id
string, newOwner string) (string, error) {
asset, err := s.ReadAsset(ctx, id)
if err != nil {
return "", err
}
oldOwner := asset.Owner
asset.Owner = newOwner
assetJSON, err := json.Marshal(asset)
if err != nil {
return "", err
}
err = ctx.GetStub().PutState(id, assetJSON)
if err != nil {
return "", err
}
return oldOwner, nil
}
func (s *SmartContract) GetAllAssets(ctx contractapi.TransactionContextInterface)
([]*Asset, error) {
// range query with empty string for startKey and endKey does an
// open-ended query of all assets in the chaincode namespace.
resultsIterator, err := ctx.GetStub().GetStateByRange("", "")
if err != nil {
return nil, err
}
defer resultsIterator.Close()
var assets []*Asset
for resultsIterator.HasNext() {
queryResponse, err := resultsIterator.Next()
if err != nil {
return nil, err
}
var asset Asset
err = json.Unmarshal(queryResponse.Value, &asset)
if err != nil {
return nil, err
}
assets = append(assets, &asset)
}
return assets, nil
}
func (s *SmartContract) GetMSPID(stub shim.ChaincodeStubInterface) (string, error) {
// Get the Client ID object
clientId, err := cid.New(stub)
if err != nil {
// Handle error
}
mspid, err := clientId.GetMSPID()
if err != nil {
// Handle error
}
// mspId, err := cid.GetMSPID(stub)
// if err != nil {
// return err
// }
// The error is with "Org1MSP" becuase I found out that mspid is Org1MSP
// if mspId != "Org1MSP" {
// return fmt.Errorf(mspId)
// }
return mspid, nil
}
Please look into below fabric sample chaincode. In this smart contract , they are validating MSPID.
https://github.com/hyperledger/fabric-samples/blob/main/asset-transfer-private-data/chaincode-go/chaincode/asset_transfer.go[![enter image description here]1]1

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.

Golang MySQL Scan only populate results if all fields passed

Golang mysql row.Scan(&pointerAddress) not populating fields.
Showing me this when i send request so omitempty does work.
If I fill all User struct fields to row.Scan(&user.Email, etc...) only then show values in result.
Code :
type User struct{
Id int `json:"id"`
Email string `json:"email"`
Password string `json:"password"`
Firstname string `json:"firstname"`
Lastname string `json:"lastname"`
RememberToken string `json:"remember_token"`
Phone int64 `json:"phone"`
AgreeTerms int8 `json:"agree_terms"`
AllowPromotionalOffers int8 `json:"allow_promotional_offers"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
LandlordId int64 `json:"landlord_id"`
RenterId int64 `json:"renter_id"`
}
My Login Function :
func Login(w http.ResponseWriter, r *http.Request) {
user := models.User{}
err := json.NewDecoder(r.Body).Decode(&user)
if err != nil {
fmt.Println(err)
}
row, err := database.DB.Query("SELECT * FROM users WHERE email=?", user.Email)
if err != nil {
fmt.Errorf("%w", err)
}
defer row.Close()
for row.Next() {
err = row.Scan(&user.Id, &user.Email, &user.Password)
if err != nil {
fmt.Errorf("%w", err)
}
}
err = json.NewEncoder(w).Encode(user)
if err != nil {
fmt.Errorf("%w", err)
}
}
Try to initialize your struct inside the for statement, if your resultset is a query rows (maybe a slices of structs):
var users []*models.User
for row.Next() {
user := new(models.User)
err = row.Scan(&user.Id, &user.Email, &user.Password)
users = append(users, user)
if err != nil {
fmt.Errorf("%w", err)
}
}
......
You don't need to loop if you're using a QueryRow resultSet
row, err := database.DB.QueryRow("SELECT id, email, password FROM users WHERE email=?",
user.Email).Scan(&user.Id, &user.Email, &user.Password)
As the best practice, you should avoid using "*" if not essential. ;)
It's a little verbose but it works.

Get Object returns empty string as values Golang

I am trying to get users from the DB with gorm and I get a 200 response but the user object got has empty values
{
"id": "",
"firstName": "",
"lastName": "",
"email": "",
"createdAt": "0001-01-01T00:00:00Z",
"updatedAt": "0001-01-01T00:00:00Z",
"deletedAt": null
}
here is my model and my reg method
func (user *User) Get(db *gorm.DB, uid string) *errors.Error {
fmt.Println("This is user ID %s", uid)
var err error
err = db.Debug().Model(User{}).Where("id = ?", uid).Take(&user).Error
if err != nil {
return errors.NewBadRequestError(fmt.Sprintf("error when trying to get user: %s", err.Error()))
}
if gorm.IsRecordNotFoundError(err) {
return errors.NewBadRequestError(fmt.Sprintf("user not found: %s", err.Error()))
}
return nil
}
the model is
type User struct {
ID string `gorm:"primary_key;" json:"id"`
FirstName string `gorm:"size:255;not null;column:firstName" json:"firstName"`
LastName string `gorm:"size:255;not null;column:lastName" json:"lastName"`
Email string `gorm:"size:100;not null;unique;column:email" json:"email"`
CreatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP;column:createdAt" json:"createdAt"`
UpdatedAt time.Time `gorm:"default:CURRENT_TIMESTAMP;column:updatedAt" json:"updatedAt"`
DeletedAt *time.Time `sql:"index;column:deletedAt" json:"deletedAt"`
}
my services logic
func GetUser(userId string) (*users.User, *errors.Error) {
// result := &users.User{Base: {
// }, }
fmt.Println("This is user ID 2 %s", userId)
result := &users.User{}
if err := result.Get(database.DB, userId); err != nil {
return nil, err
}
return &users.User{}, nil
}
my controller
func GetUser(c *gin.Context) {
userId := c.Param("user_id")
fmt.Println(userId)
user, getErr := services.GetUser(userId)
if getErr != nil {
// TODO: handle user createing error
c.JSON(getErr.Status, getErr)
return
}
c.JSON(http.StatusOK, user)
}
In your service logic, you are returning a pointer and not a value.
func GetUser(userId string) (*users.User, *errors.Error) {
fmt.Println("This is user ID 2 %s", userId)
result := &users.User{}
if err := result.Get(database.DB, userId); err != nil {
return nil, err
}
return &users.User{}, nil
}
It should be:
func GetUser(userId string) (*users.User, *errors.Error) {
fmt.Println("This is user ID 2 %s", userId)
result := &users.User{ID: userId}
if err := result.Get(database.DB, userId); err != nil {
return nil, err
}
return result, nil
}

Resources