Adding validation for list of elements in Go - validation

I am new to golang
I have created a REST service using golang. I am trying to add validaiton and i am stuck in it.
I have two structs like
type Item struct {
ProductId string `json:"prodId"`
SkuId string `json:"skuId"`
Qty string `json:"qty"`
}
type AddItemsRequest struct {
Items []Item `json:"items" validate:"required"`
}
I am trying to validate AddItemsRequest
I added below code for handling this
func (r * Item) Validate() error {
return types.ErrorMap{
"prodId": validation.Validate(&r.ProductId, validation.Required, validation.Length(1, 255)),
"skuId": validation.Validate(&r.SkuId, validation.Required, validation.Length(1, 255)),
"qty": validation.Validate(&r.Qty, validation.Required, validation.Length(1, 255)),
}
}
func (r AddItemsRequest) Validate() error {
return validation.ValidateStruct(&r,
validation.Field(&r.Items, validation.Each(is.Alpha))) // Instead of Alpha how to validate each items with my Items rule
}
But I am not bale to valdiate. How can i add validation code, so that items in request is not empty or blank and only String?

I don't know what validation is, but I can use 'github.com/go-playground/validator' to solve this problem.
func main() {
v := validator.New()
v.RegisterValidation("mySlice", func(fl validator.FieldLevel) bool {
sli, ok := fl.Field().Interface().([]string)
if !ok {
return false
}
for i := range sli {
if len(sli[i]) < 5 {
return false
}
}
return true
}, false)
sli := []string{"hello world", "hi", "congratulation"}
err := v.Var(sli, "mySlice")
if err != nil {
panic("check error: " + err.Error())
}
print("ok")
}

Related

Golang Validator : structExcept

I am trying to validate a golang struct based on some criteria. I am using this library validator.v9. I am using structExcept() to exclude certain validation. I want to exclude validation on SessionInfo for all index elements of VenueInfo when SessionInfo is nil. Same way, I want to exclude validation on TicketDetails for all elements of SessionInfo for all elements of VenueInfo.
Sample Code Snippet.
package main
import (
"fmt"
"time"
"gopkg.in/go-playground/validator.v9"
)
type (
// Event model
Event struct {
VenueInfo []VenueDetails `json:"venueInfo,omitempty" validate:"min=1,dive,required"`
}
// TicketDetails model
TicketDetails struct {
TicketTypeName string `json:"ticketTypeName,omitempty" validate:"required"`
}
// SessionDetails model
SessionDetails struct {
ShowTime time.Time `json:"showTime,omitempty" validate:"required"`
TicketInfo []TicketDetails `json:"ticketInfo,omitempty" validate:"required_with_all=ShowTime,min=1,dive"`
}
// VenueDetails model
VenueDetails struct {
VenueName string `json:"venueName,omitempty" validate:"required"`
SessionInfo []SessionDetails `json:"sessionInfo,omitempty" validate:"required_with_all=Name,min=1,dive"`
}
)
func main() {
validate := validator.New()
eventDetails := &Event{
VenueInfo: []VenueDetails{
{
VenueName: "Bengaluru International Exhibition Centre",
SessionInfo: []SessionDetails{
{
ShowTime: time.Now(),
// TicketInfo: []TicketDetails{
// {
// TicketTypeName: "Gold Class",
// },
// },
},
},
},
{
VenueName: "Bengaluru International Exhibition Centre",
SessionInfo: []SessionDetails{
{
ShowTime: time.Now(),
// TicketInfo: []TicketDetails{
// {
// TicketTypeName: "Gold Class",
// },
// },
},
},
},
},
}
// 1st use case : applying excluding validation for VenueInfo
excludeVenueInfo := []string{"VenueInfo"}
if err := validate.StructExcept(eventDetails, excludeVenueInfo...); err != nil {
fmt.Println(err.Error())
} else {
fmt.Println("Success")
}
// 2nd use case : applying excluding validation for SessionInfo & TicketInfo
excludeSessionInfo := []string{"VenueInfo[0].SessionInfo"}
if err := validate.StructExcept(eventDetails, excludeSessionInfo...); err != nil {
fmt.Println(err.Error())
} else {
fmt.Println("Success")
}
// 3rd use case : applying excluding validation for TicketInfo
excludeTicketInfo := []string{"VenueInfo[0].SessionInfo[0].TicketInfo"}
if err := validate.StructExcept(eventDetails, excludeTicketInfo...); err != nil {
fmt.Println(err.Error())
} else {
fmt.Println("Success")
}
}
Use omitempty in your validation.
package main
import (
"fmt"
"gopkg.in/go-playground/validator.v9"
)
type (
AlienObject struct {
A string `json:"a,omitempty" validate:"omitempty,min=6"`
B string `json:"b,omitempty" validate:"omitempty,min=6"`
}
)
func main() {
validate := validator.New()
h := AlienObject{A:"abcdooii"}
if err := validate.Struct(h); err != nil {
fmt.Println(err.Error())
} else {
fmt.Println("Success")
}
}

Sorting not working in golang chaincode hyperledger fabric

I'm trying to sort the result in golang chaincode, but the result is random, below is my chaincode sample:
package main
import (
"bytes"
"encoding/json"
"fmt"
"time"
"github.com/hyperledger/fabric/core/chaincode/shim"
"github.com/hyperledger/fabric/protos/peer"
)
type itemStruct struct {
ID string `json:"id"`
Status string `json:"status"`
CreatedAt time.Time `json:"created_at"`
}
func createItem(stub shim.ChaincodeStubInterface, args []string) peer.Response {
if len(args) != 3 {
return shim.Error(fmt.Sprintf("Expecting %v arguments {id, status, created_at}, but got %v", 3, len(args)))
}
itemID := args[0]
if len(itemID) == 0 {
return shim.Error("id field is required")
}
status := args[1]
if len(status) == 0 {
return shim.Error("status field is required")
}
createdAt, err := time.Parse(time.RFC3339, args[2])
if err != nil {
return shim.Error("created_at is not a valid datetime string")
}
item := itemStruct{
ID: itemID,
CreatedAt: createdAt,
}
itemAsJSONBytes, err := json.Marshal(item)
if err != nil {
return shim.Error(err.Error())
}
return shim.Success(itemAsJSONBytes)
}
func getPendingItems(stub shim.ChaincodeStubInterface, args []string) peer.Response {
var bookmark string
if len(args) > 0 && len(args[0]) > 0 {
bookmark = args[0]
}
queryString := `{
"selector": {
"status": "pending"
},
"sort": [
{"created_at": "desc"}
]
}`
result, pagination, err := queryWithPagination(stub, queryString, 20, bookmark)
if err != nil {
return shim.Error(err.Error())
}
return shim.Success(constructResponse(result, pagination).Bytes())
}
func queryWithPagination(stub shim.ChaincodeStubInterface, queryString string, pageSize int32, bookmark string) (map[string]string, string, error) {
var pagination string
iterator, meta, err := stub.GetQueryResultWithPagination(queryString, pageSize, bookmark)
if err != nil {
return nil, pagination, err
}
defer iterator.Close()
result, err := iterateResult(iterator)
if err != nil {
return nil, pagination, err
}
pagination = fmt.Sprintf(`{"count": %v, "next_page_token": "%v"}`, meta.FetchedRecordsCount, meta.Bookmark)
return result, pagination, nil
}
func constructResponse(items map[string]string, pagination string) *bytes.Buffer {
// buffer is a JSON array containing QueryResults
var buffer bytes.Buffer
if len(pagination) > 0 {
buffer.WriteString(`{"data":`)
}
buffer.WriteString(`[`)
bArrayMemberAlreadyWritten := false
for _, val := range items {
// Add a comma before array members, suppress it for the first array member
if bArrayMemberAlreadyWritten == true {
buffer.WriteString(",")
}
buffer.WriteString(val)
bArrayMemberAlreadyWritten = true
}
buffer.WriteString("]")
if len(pagination) > 0 {
buffer.WriteString(`,"pagination":`)
buffer.WriteString(pagination)
buffer.WriteString("}")
}
return &buffer
}
func iterateResult(iterator shim.StateQueryIteratorInterface) (map[string]string, error) {
result := map[string]string{}
for iterator.HasNext() {
queryResponse, err := iterator.Next()
if err != nil {
return nil, err
}
result[queryResponse.Key] = string(queryResponse.Value)
}
return result, nil
}
// SmartContract : Smart contract struct
type SmartContract struct {
}
// Init : This method is called when chaincode is initialized or updated.
func (s *SmartContract) Init(stub shim.ChaincodeStubInterface) peer.Response {
return shim.Success(nil)
}
// Invoke : This method is called when any transaction or query fired
func (s *SmartContract) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
// Retrieve the requested Smart Contract function and arguments
function, args := stub.GetFunctionAndParameters()
// Route to the appropriate handler function to interact with the ledger appropriately
// Transactions
if function == "createItem" {
return createItem(stub, args)
}
// Queries
if function == "getItems" {
return getItems(stub, args)
}
return shim.Error("Invalid function")
}
func main() {
// Create a new Smart Contract
err := shim.Start(new(SmartContract))
if err != nil {
fmt.Printf("Error creating new Smart Contract: %s", err)
}
}
It creates and asset which can have different status based on what's passed, and I have defined one query function which fetches only pending items.
I have applied sort, but the result is still random, can anyone help me here and guide me where I'm going wrong in this?
the sort field has to be present in the selector !
something like:
queryString := `{
"selector": {
"created_at": "$gt": null
"status": "pending"
},
"sort": [
{"created_at": "desc"}
]
}`
or (range)
queryString := `{
"selector": {
"created_at": {
"$gt": "2015-01-01T00:00:00Z",
"$lt": "2019-01-01T00:00:00Z"
},
"status": "pending"
},
"sort": [
{"created_at": "desc"}
]
}`
Per the docs - to use sorting, ensure that:
At least one of the sort fields is included in the selector.
There is an index already defined, with all the sort fields in the same
order.
Each object in the sort array has a single key.
If an object in the sort array does not have a single key, the resulting sort order is implementation specific and might change.
Also, as an fyi Find does not support multiple fields with different sort orders, so the directions must be either all ascending or all descending.
See also CouchDB sort doesn't work

How to access struct with a variable?

I'm new to Go and I'm facing issues in accessing a struct with a variable
I have this function decodeUser. My task is to check whether the keys are present in the request. So this is what I did. I've added a comment where I got the error.
func decodeUser(r *http.Request) (root.User, []string, error) {
var u root.User
if r.Body == nil {
return u, []string{}, errors.New("no request body")
}
decoder := json.NewDecoder(r.Body)
checks := []string{
"UserName",
"FirstName",
"LastName",
"Email",
}
emptyFields := []string{}
for _, check := range checks {
// i'm having problem over here `u[check]` it's showing (invalid
operation: u[check] (type root.User does not support
indexing))
if u[check] == nil {
emptyFields = append(emptyFields, check)
}
}
err := decoder.Decode(&u)
return u, emptyFields, err
}
Just in case I added root.User here's structure for it
type User struct {
ID string
Username string
Password string
FirstName string
LastName string
Email string
PhoneNumber string
PhoneNumberExtension string
DOB time.Time
AboutMe string
}
The problem occurs as it doesn't allow me to access struct by a variable and I can't use this method which is u.check. So basically how should I make u[check] work?
I would suggest you manually check for zero values since it seems that you already know the fields that needs to be non-zero at compile time. However, if that is not the case, here is a simple function (using reflection) that will check for zero values in a struct.
func zeroFields(v interface{}, fields ...string) []string {
val := reflect.Indirect(reflect.ValueOf(v))
if val.Kind() != reflect.Struct {
return nil
}
var zeroes []string
for _, name := range fields {
field := val.FieldByName(name)
if !field.IsValid() {
continue
}
zero := reflect.Zero(field.Type())
// check for zero value
if reflect.DeepEqual(zero.Interface(), field.Interface()) {
zeroes = append(zeroes, name)
}
}
return zeroes
}
func main() {
x := User{
Email: "not#nil",
}
fmt.Println(zeroFields(&x, "ID", "Username", "Email", "Something", "DOB"))
}
Which would output:
[ID Username DOB]
Playground
This is what worked for me
for _, check := range checks {
temp := reflect.Indirect(reflect.ValueOf(&u))
fieldValue := temp.FieldByName(string(check))
if (fieldValue.Type().String() == "string" && fieldValue.Len() == 0) || (fieldValue.Type().String() != "string" && fieldValue.IsNil()) {
fmt.Println("EMPTY->", check)
emptyFields = append(emptyFields, check)
}
}

Unmarshal map[string]DynamoDBAttributeValue into a struct

I'm trying to set-up an AWS-lambda using aws-sdk-go that is triggered whenever a new user is added to a certain dynamodb table.
Everything is working just fine but I can't find a way to unmarshal a map map[string]DynamoDBAttributeValue like:
{
"name": {
"S" : "John"
},
"residence_address": {
"M": {
"address": {
"S": "some place"
}
}
}
}
To a given struct, for instance, a User struct. Here is shown an example of unsmarhaling a map[string]*dynamodb.AttributeValue into a given interface, but I can't find a way to do the same thing with map[string]DynamoDBAttributeValue even though these types seem to fit the same purposes.
map[string]DynamoDBAttributeValue is returned by a events.DynamoDBEvents from package github.com/aws/aws-lambda-go/events. This is my code:
package handler
import (
"context"
"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute"
"github.com/aws/aws-sdk-go/service/dynamodb"
)
func HandleDynamoDBRequest(ctx context.Context, e events.DynamoDBEvent) {
for _, record := range e.Records {
if record.EventName == "INSERT" {
// User Struct
var dynamoUser model.DynamoDBUser
// Of course this can't be done for incompatible types
_ := dynamodbattribute.UnmarshalMap(record.Change.NewImage, &dynamoUser)
}
}
}
Of course, I can marshal record.Change.NewImage to JSON and unmarshal it back to a given struct, but then, I would have to manually initialize dynamoUser attributes starting from the latter ones.
Or I could even write a function that parses map[string]DynamoDBAttributeValue to map[string]*dynamodb.AttributeValue like:
func getAttributeValueMapFromDynamoDBStreamRecord(e events.DynamoDBStreamRecord) map[string]*dynamodb.AttributeValue {
image := e.NewImage
m := make(map[string]*dynamodb.AttributeValue)
for k, v := range image {
if v.DataType() == events.DataTypeString {
s := v.String()
m[k] = &dynamodb.AttributeValue{
S : &s,
}
}
if v.DataType() == events.DataTypeBoolean {
b := v.Boolean()
m[k] = &dynamodb.AttributeValue{
BOOL : &b,
}
}
// . . .
if v.DataType() == events.DataTypeMap {
// ?
}
}
return m
}
And then simply use dynamodbattribute.UnmarshalMap, but on events.DataTypeMap it would be quite a tricky process.
Is there a way through which I can unmarshal a DynamoDB record coming from a events.DynamoDBEvent into a struct with a similar method shown for map[string]*dynamodb.AttributeValue?
I tried the function you provided, and I met some problems with events.DataTypeList, so I managed to write the following function that does the trick:
// UnmarshalStreamImage converts events.DynamoDBAttributeValue to struct
func UnmarshalStreamImage(attribute map[string]events.DynamoDBAttributeValue, out interface{}) error {
dbAttrMap := make(map[string]*dynamodb.AttributeValue)
for k, v := range attribute {
var dbAttr dynamodb.AttributeValue
bytes, marshalErr := v.MarshalJSON(); if marshalErr != nil {
return marshalErr
}
json.Unmarshal(bytes, &dbAttr)
dbAttrMap[k] = &dbAttr
}
return dynamodbattribute.UnmarshalMap(dbAttrMap, out)
}
I was frustrated that the type of NewImage from the record wasn't map[string]*dynamodb.AttributeValue so I could use the dynamodbattribute package.
The JSON representation of events.DynamoDBAttributeValue seems to be the same as the JSON represenation of dynamodb.AttributeValue.
So I tried creating my own DynamoDBEvent type and changed the type of OldImage and NewImage, so it would be marshalled into map[string]*dynamodb.AttributeValue instead of map[string]events.DynamoDBAttributeValue
It is a little bit ugly but it works for me.
package main
import (
"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-lambda-go/lambda"
"github.com/aws/aws-sdk-go/service/dynamodb"
"github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute"
"fmt"
)
func main() {
lambda.Start(lambdaHandler)
}
// changed type of event from: events.DynamoDBEvent to DynamoDBEvent (see below)
func lambdaHandler(event DynamoDBEvent) error {
for _, record := range event.Records {
change := record.Change
newImage := change.NewImage // now of type: map[string]*dynamodb.AttributeValue
var item IdOnly
err := dynamodbattribute.UnmarshalMap(newImage, &item)
if err != nil {
return err
}
fmt.Println(item.Id)
}
return nil
}
type IdOnly struct {
Id string `json:"id"`
}
type DynamoDBEvent struct {
Records []DynamoDBEventRecord `json:"Records"`
}
type DynamoDBEventRecord struct {
AWSRegion string `json:"awsRegion"`
Change DynamoDBStreamRecord `json:"dynamodb"`
EventID string `json:"eventID"`
EventName string `json:"eventName"`
EventSource string `json:"eventSource"`
EventVersion string `json:"eventVersion"`
EventSourceArn string `json:"eventSourceARN"`
UserIdentity *events.DynamoDBUserIdentity `json:"userIdentity,omitempty"`
}
type DynamoDBStreamRecord struct {
ApproximateCreationDateTime events.SecondsEpochTime `json:"ApproximateCreationDateTime,omitempty"`
// changed to map[string]*dynamodb.AttributeValue
Keys map[string]*dynamodb.AttributeValue `json:"Keys,omitempty"`
// changed to map[string]*dynamodb.AttributeValue
NewImage map[string]*dynamodb.AttributeValue `json:"NewImage,omitempty"`
// changed to map[string]*dynamodb.AttributeValue
OldImage map[string]*dynamodb.AttributeValue `json:"OldImage,omitempty"`
SequenceNumber string `json:"SequenceNumber"`
SizeBytes int64 `json:"SizeBytes"`
StreamViewType string `json:"StreamViewType"`
}
I have found the same problem and the solution is to perform a simple conversion of types. This is possible because in the end the type received by lambda events events.DynamoDBAttributeValue and the type used by the SDK V2 of AWS DynamoDB types.AttributeValue are the same. Next I show you the conversion code.
package aws_lambda
import (
"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue"
"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
)
func UnmarshalDynamoEventsMap(
record map[string]events.DynamoDBAttributeValue, out interface{}) error {
asTypesMap := DynamoDbEventsMapToTypesMap(record)
err := attributevalue.UnmarshalMap(asTypesMap, out)
if err != nil {
return err
}
return nil
}
func DynamoDbEventsMapToTypesMap(
record map[string]events.DynamoDBAttributeValue) map[string]types.AttributeValue {
resultMap := make(map[string]types.AttributeValue)
for key, rec := range record {
resultMap[key] = DynamoDbEventsToTypes(rec)
}
return resultMap
}
// DynamoDbEventsToTypes relates the dynamo event received by AWS Lambda with the data type that is
// used in the Amazon SDK V2 to deal with DynamoDB data.
// This function is necessary because Amazon does not provide any kind of solution to make this
// relationship between types of data.
func DynamoDbEventsToTypes(record events.DynamoDBAttributeValue) types.AttributeValue {
var val types.AttributeValue
switch record.DataType() {
case events.DataTypeBinary:
val = &types.AttributeValueMemberB{
Value: record.Binary(),
}
case events.DataTypeBinarySet:
val = &types.AttributeValueMemberBS{
Value: record.BinarySet(),
}
case events.DataTypeBoolean:
val = &types.AttributeValueMemberBOOL{
Value: record.Boolean(),
}
case events.DataTypeList:
var items []types.AttributeValue
for _, value := range record.List() {
items = append(items, DynamoDbEventsToTypes(value))
}
val = &types.AttributeValueMemberL{
Value: items,
}
case events.DataTypeMap:
items := make(map[string]types.AttributeValue)
for k, v := range record.Map() {
items[k] = DynamoDbEventsToTypes(v)
}
val = &types.AttributeValueMemberM{
Value: items,
}
case events.DataTypeNull:
val = nil
case events.DataTypeNumber:
val = &types.AttributeValueMemberN{
Value: record.Number(),
}
case events.DataTypeNumberSet:
val = &types.AttributeValueMemberNS{
Value: record.NumberSet(),
}
case events.DataTypeString:
val = &types.AttributeValueMemberS{
Value: record.String(),
}
case events.DataTypeStringSet:
val = &types.AttributeValueMemberSS{
Value: record.StringSet(),
}
}
return val
}
There is a package that allows conversion from events.DynamoDBAttributeValue to dynamodb.AttributeValue
https://pkg.go.dev/github.com/aereal/go-dynamodb-attribute-conversions/v2
From there one can unmarshal AttributeValue into struct
func Unmarshal(attribute map[string]events.DynamoDBAttributeValue, out interface{}) error {
av := ddbconversions.AttributeValueMapFrom(attribute)
return attributevalue.UnmarshalMap(av, out)
}

Beego validation accepts invalid data

I am trying to validate some forms using Beego validation, but it is not working at all: invalid data passes without errors.
This the relevant code, I don't know what is wrong. Can you point me at the mistake?
https://github.com/dionyself/golang-cms/blob/master/models/form.go
package models
import (
"github.com/astaxie/beego"
"github.com/astaxie/beego/validation"
)
type BaseForm struct {
Errors map[string]string
}
func (form *BaseForm) Validate() bool {
valid := validation.Validation{}
b, err := valid.Valid(form)
if err != nil {
beego.Error(err)
}
if !b {
for _, err := range valid.Errors {
form.Errors[err.Key] = err.Message
beego.Debug(err.Key, err.Message)
}
}
return b
}
type RegisterForm struct {
BaseForm
Username string `form:"username" valid:"Required; AlphaNumeric; MinSize(4); MaxSize(300)"`
Password string `form:"password" valid:"Required; MinSize(4); MaxSize(30)"`
PasswordRe string `form:"passwordre" valid:"Required; MinSize(4); MaxSize(30)"`
}
func (form *RegisterForm) Valid(v *validation.Validation) {
// Check if passwords of two times are same.
if form.Password != form.PasswordRe {
v.SetError("PasswordRe", "Passwords did not match")
return
}
}
type ArticleForm struct {
BaseForm
Id int `form:"-"`
Title string `form:"title" valid:"Required;MinSize(4);MaxSize(300)"`
Category int `form:"category"`
Content string `form:"content" valid:"Required; MinSize(50); MaxSize(2000)"`
TopicTags string `form:"topic-tags" valid:"MinSize(4); MaxSize(300)"`
TaggedUsers string `form:"tagged-users" valid:"MinSize(4); MaxSize(300)"`
AllowReviews bool `form:"allow-reviews" valid:"Required"`
AllowComments bool `form:"allow-comments" valid:"Required"`
Errors map[string]string
}
func (form *ArticleForm) Valid(v *validation.Validation) {
if form.Category >= 0 {
v.SetError("Category", "Invalid category")
return
}
}
Some documentation:
http://beego.me/docs/mvc/controller/validation.md
This is the code that parses the form:
func (this *ArticleController) Post() {
form := models.ArticleForm{}
Art := new(models.Article)
if err := this.ParseForm(&form); err != nil {
this.Abort("401")
} else {
db := this.GetDB()
if !form.Validate() {
this.Data["form"] = form
var cats []*models.Category
db.QueryTable("category").All(&cats)
this.Data["Categories"] = cats
this.ConfigPage("article-editor.html")
for key, msg := range form.Errors {
fmt.Println(key, msg)
}
} else {
db.Insert(Art)
this.Data["Article"] = Art
this.ConfigPage("article.html")
}
}
}
Note: FormData is always accepted (even an empty form), form.Validate() is always returning 'true'... 0 errors on logs.
It's because your struct has a data type with map[string]interface{} which accepts any data type and converting it into a string try to be specific in data type

Resources