Validate struct field if it exists - go

I'm POSTing a JSON user object to my Golang application where I decode the 'req.body' into a 'User' struct.
err := json.NewDecoder(req.Body).Decode(user)
//handle err if there is one
and the 'User' struct:
type User struct {
Name string `json:"name,omitempty"`
Username string `json:"username,omitempty"`
Email string `json:"email,omitempty"`
Town string `json:"town,omitempty"`
//more fields here
}
While I don't need help with the actual validation, I would like know how to validate usernames only if it is included as part of the JSON object. At the moment, if a username isn't included then User.Username will still exist but be empty i.e. ""
How can I check to see if '"username"' was included as part of the POSTed object?

You can use a pointer to a string:
type User struct {
Name string `json:"name,omitempty"`
Username *string `json:"username,omitempty"`
Email string `json:"email,omitempty"`
Town string `json:"town,omitempty"`
//more fields here
}
func main() {
var u, u2 User
json.Unmarshal([]byte(`{"username":"hi"}`), &u)
fmt.Println("username set:", u.Username != nil, *u.Username)
json.Unmarshal([]byte(`{}`), &u2)
fmt.Println("username set:", u2.Username != nil)
fmt.Println("Hello, playground")
}
playground

To add to the above answer. Note that the order of the validate functions is important. I was getting the error because I was placing the UUIDV4 validation tag before the omitempty:
ParentID *string `json:"parent_id" validate:"uuid4,omitempty"`
Correct way is:
ParentID *string `json:"parent_id" validate:"omitempty,uuid4"`

Related

How can I skip a specific field from struct while inserting with gorm

I was trying to make a rest API for user registration and on that api there is a field named "gender", so I'm receiving that field as Struct but on user table there is no field for "gender". Is it possible to skip that "gender" field from struct while inserting with gorm?
This is my DataModel
package DataModels
type User struct {
Id uint `json:"id"`
Otp int `json:"otp"`
UserId string `json:"user_id"`
UserType string `json:"user_type"`
FullName string `json:"full_name"`
MobileNo string `json:"mobile"`
Email string `json:"email"`
Gender string `json:"gender"` // I want to skip this filed while inserting to users table
Password string `json:"password"`
}
func (b *User) TableName() string {
return "users"
}
This my Controller Function
func CreateUser(c *gin.Context) {
var user Models.User
_ = c.BindJSON(&user)
err := Models.CreateUser(&user) // want to skip that gender filed while inserting
if err != nil {
fmt.Println(err.Error())
c.AbortWithStatus(http.StatusNotFound)
} else {
c.JSON(http.StatusOK, user)
}
}
This is my model function for inserting
func CreateUser(user *User) (err error) {
if err = Config.DB.Create(user).Error; err != nil {
return err
}
return nil
}
GORM allows you to ignore the field with tag, use gorm:"-" to ignore the field
type User struct {
...
Gender string `json:"gender" gorm:"-"` // ignore this field when write and read
}
Offical doc details about Field-Level Permission
Eklavyas Answer is omitting gender always, not just on Create.
If I am correct you want to Skip the Gender field within the Registration. You can use Omit for that.
db.Omit("Gender").Create(&user)

How return null value to empty string field

I have such struct in Golang application:
type Employee struct {
ID int `json:"employee_id"`
Email *string `json:"employee_email"`
LastName *string `json:"employee_last_name"`
FirstName *string `json:"employee_first_name"`
Sex *string `json:"employee_sex"`
}
Some string fields of this struct can be empty. If I use *string application return me "". If use sql.NullString it return me for example such result:
"employee_last_name": {
String: "",
Valid: true
}
I want to show null is string field is empty.
In documentation I found such code:
type NullString struct {
String string
Valid bool
}
func (ns *NullString) Scan(value interface{}) error {
if value == nil {
ns.String, ns.Valid = "", false
return nil
}
ns.Valid = true
return convertAssign(&ns.String, value)
}
func (ns NullString) Value() (driver.Value, error) {
if !ns.Valid {
return nil, nil
}
return ns.String, nil
}
As I understand this code could help me to solve my problem, right?! What I need to import in my application to use convertAssign function?
In the Go language specification nil is not a valid value for type string. the default, zero value is "".
The top answer for this question goes into more detail.
Edit:
What you want to do is not possible according to the oracle go driver docs:
sql.NullString
sql.NullString is not supported: Oracle DB does not
differentiate between an empty string ("") and a NULL
Original Answer:
Need more details on the SQL database type you are connecting to. I know the go SQLite3 pkg - github.com/mattn/go-sqlite3 - supports setting nil for values of type *string.
Check the details of the driver you are using to connect to the database. For instance with MySQL, getting SQL date fields into go's native time.Time struct type is not set by default - it must be turned on at the driver level.

Using struct to update values

I'm stuck when I'm updating an empty string values in a struct for updating dynamodb table.
Currently I have this struct
type Client struct {
ClientID *string `json:"client_key,omitempty"`
Name *string `json:"client_name,omitempty"`
Address *string `json:"address,omitempty"`
Country *string `json:"country,omitempty"`
City *string `json:"city,omitempty"`
State *string `json:"state,omitempty"`
PostCode *string `json:"post_code,omitempty"`
CreatedAt *time.Time `json:"created_at,omitempty"`
}
And this code when updating an item
keyAttr, err := dynamodbattribute.MarshalMap(key)
if err != nil {
return nil, err
}
valAttr, err := dynamodbattribute.MarshalMap(attributes)
if err != nil {
return nil, err
}
keyAttr will be used for the Key field and valAttr will be used in ExpressionAttributeValues field. Note that I didn't include the complete updating fields function to save space. But I will do that if you ask for it.
Currently the function is running fine except when I updated one of the field with empty string. E.g. client.Address = aws.String(""). While I'm fine with dynamodb converting my empty string to null, I can't seem to find a way to update it because of the omitempty tag.
I need the omitempty tag to ignore all nil values. However, I just researched that the omitempty tag also omits empty string values. Currently I have to make a struct in my function like this.
type client struct {
Name *string `json:"client_name"`
Address *string `json:"address"`
Country *string `json:"country"`
City *string `json:"city"`
State *string `json:"state"`
PostCode *string `json:"post_code"`
}
But i'm not a fan of repeating things. So, the question is: is there any better way of doing this? How do you guys use structs with dynamodb?
EDIT
Based on #Peter's comment, it seems that json.Encode() does print the empty string if it's not nil.
{"client_key":"test","username":"test","email":"","first_name":"test","last_name":"","phone":"","title":"","email_verified":false,"phone_verified":false,"updated_at":"2018-12-06T14:04:56.2743841+11:00"}
The problem seems to be in dynamodbattribute.MarshalMap function
After several trials, I finally got it. I didn't test it so I don't know whether it's buggy or not. But it seems to work for me right now.
So what I did was encode the struct with json.Marshal first then use json.Unmarshal with a map[string]interface{}. Then, I use dynamodbattribute.Marshal to convert it to map[string]*AttributeValue
Here's the code:
var temp map[string]interface{}
json.Unmarshal(tempStr, &temp)
valAttr, err := dynamodbattribute.MarshalMap(temp)
if err != nil {
return nil, err
}

Reuse or cast structs for validation, API results, and DB saves in Go

I'm trying to write an API that will do different things with the data depending on its purpose.
API results - The API should expose certain fields to the user
Validation - Validation should handle different schemas e.g. login form doesn't require Name, but register does
Database - The database should save everything, including password etc. - if I try using the same struct for the API and DB with json:"-" or un-exporting the field the database save also ignores the field
Some sample code is below with a couple of comments in capitals to show where I ideally need to type cast to change the data. As they are different structs, they cannot be type cast, so I get an error. How can I fix this?
Alternatively, what is a better way of doing different things with the data without having lots of similar structs?
// IDValidation for uuids
type IDValidation struct {
ID string `json:"id" validate:"required,uuid"`
}
// RegisterValidation for register form
type RegisterValidation struct {
Name string `json:"name" validate:"required"`
Email string `json:"email" validate:"required,email"`
Password string `json:"password" validate:"required,min=8"`
}
// UserModel to save in DB
type UserModel struct {
ID string `json:"id,omitempty"`
Name string `json:"name"`
Email string `json:"email"`
Password string `json:"password,omitempty"`
Active bool `json:"active,omitempty"`
CreatedAt int64 `json:"created_at,omitempty"`
UpdatedAt int64 `json:"updated_at,omitempty"`
jwt.StandardClaims
}
// UserAPI data to display to user
type UserAPI struct {
ID string `json:"id,omitempty"`
Name string `json:"name"`
Email string `json:"email"`
Active bool `json:"active,omitempty"`
}
// register a user
func register(c echo.Context) error {
u := new(UserModel)
if err := c.Bind(u); err != nil {
return err
}
// NEED TO CAST TO RegisterValidation HERE?
if err := c.Validate(u); err != nil {
return err
}
token, err := u.Register()
if err != nil {
return err
}
return c.JSON(http.StatusOK, lib.JSON{"token": token})
}
// retrieve a user
func retrieve(c echo.Context) error {
u := IDValidation{
ID: c.Param("id"),
}
if err := c.Validate(u); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
}
// NEED TO CAST USER TO UserAPI?
user, err := userModel.GetByID(u.ID)
if err != nil {
return err
}
return c.JSON(200, lib.JSON{"message": "User found", "user": user})
}

Is it possible to delete a field of a struct value at runtime?

I have the following struct:
type Record struct {
Id string `json:"id"`
ApiKey string `json:"apiKey"`
Body []string `json:"body"`
Type string `json:"type"`
}
Which is a Blueprint of a dynamoDB table. And I need somehow, delete the ApiKey, after using to check if the user has access to the giving record. Explaining:
I have and endpoint in my API where the user can send a id to get a item, but he needs to have access to the ID and the ApiKey (I'm using Id (uuid) + ApiKey) to create unique items.
How I'm doing:
func getExtraction(id string, apiKey string) (Record, error) {
svc := dynamodb.New(cfg)
req := svc.GetItemRequest(&dynamodb.GetItemInput{
TableName: aws.String(awsEnv.Dynamo_Table),
Key: map[string]dynamodb.AttributeValue{
"id": {
S: aws.String(id),
},
},
})
result, err := req.Send()
if err != nil {
return Record{}, err
}
record := Record{}
err = dynamodbattribute.UnmarshalMap(result.Item, &record)
if err != nil {
return Record{}, err
}
if record.ApiKey != apiKey {
return Record{}, fmt.Errorf("item %d not found", id)
}
// Delete ApiKey from record
return record, nil
}
After checking if the ApiKey is equal to the provided apiKey, I want to delete the ApiKey from record, but unfortunately that's not possible using delete.
Thank you.
There is no way to actually edit a golang type (such as a struct) at runtime. Unfortunately you haven't really explained what you are hoping to achieve by "deleting" the APIKey field.
General approaches would be:
set the APIKey field to an empty string after inspection, if you dont want to display this field when empty set the json struct tag to omitempty (e.g `json:"apiKey,omitempty"`)
set the APIKey field to never Marshal into JSON ( e.g ApiKey string `json:"-"`) and you will still be able to inspect it just wont display in JSON, you could take this further by adding a custom marshal / unmarshal function to handle this in one direction or in a context dependent way
copy the data to a new struct, e.g type RecordNoAPI struct without the APIKey field and return that after you have inspected the original Record
Created RecordShort structure without "ApiKey"
Marshalin Record
Unmarsaling Record to ShortRecord
type RecordShot struct {
Id string `json:"id"`
Body []string `json:"body"`
Type string `json:"type"`
}
record,_:=json.Marshal(Record)
json.Unmarshal([]byte(record), &RecordShot)

Resources