I want to make an array optional in struct and use it with an if else in a func.
type TestValues struct {
Test1 string `json:"test1"`
DefaultTests []string `json:"__tests"`
//DefaultTests *array `json:"__tests,omitempty" validate:"option"`
Test2 string `json:"__Test2"`
}
func (x *Controller) createTest(context *gin.Context, uniqueId string, testBody *TestValues) (*http.Response, error) {
if testBody.DefaultTags {
postBody, err := json.Marshal(map[string]string{
"Test2": testBody.Test2,
"Test1": testBody.Test1,
"defaultTests": testBody.DefaultTests,
"uniqueId": uniqueId,
})
} else {
postBody, err := json.Marshal(map[string]string{
"Test2": testBody.Test2,
"Test1": testBody.Test1,
"uniqueId": uniqueId,
})
}
...
}
When I run the code it tells me that DefaultTests is undefined array but I don't want this error to pop because DefaultTests can existe and sometimes it won't be present in the json that's why I want to make it optional. The if else part is not working too.
It's better to use len() when checking if an array is empty here.
if len(testBody.DefaultTests) > 0 {
...
}
Check the Zero value of the DefaultTests in struct below for more clarity on this behaviour
package main
import "fmt"
type TestValues struct {
Test1 string `json:"test1"`
DefaultTests []string `json:"__tests"`
//DefaultTests *array `json:"__tests,omitempty" validate:"option"`
Test2 string `json:"__Test2"`
}
func main() {
var tv = TestValues{Test1: "test"}
if len(tv.DefaultTests) > 0 {
fmt.Printf("Default Tests: %v\n", tv.DefaultTests)
} else {
fmt.Printf("Default Tests empty value: %v\n", tv.DefaultTests)
}
}
Here's my final code with Marc's answer
func (x *Controller) createTest(context *gin.Context, uniqueId string, testBody *TestValues) (*http.Response, error) {
if len(testBody.DefaultTags) > 0 {
postBody, err := json.Marshal(testBody.DefaultTags)
} else {
postBody, err := json.Marshal(map[string]string{
"Test2": testBody.Test2,
"Test1": testBody.Test1,
"uniqueId": uniqueId,
})
}
...
}
Related
Community,
The mission
basic
Implement a func that patches all string fields on an objects
details
[done] fields shall only be patched if they match a matcher func
[done] value shall be processed via process func
patching shall be done recursive
it shall also work for []string, []*string and recursive for structs and []struct, []*struct
// update - removed old code
Solution
structs
updated the structs to use (though this does not affect the actual program, i use this for completeness
type Tag struct {
Name string `process:"yes,TagName"`
NamePtr *string `process:"no,TagNamePtr"`
}
type User struct {
ID int
Nick string
Name string `process:"yes,UserName"`
NamePtr *string `process:"yes,UserNamePtr"`
Slice []string `process:"yes,Slice"`
SlicePtr []*string `process:"yes,SlicePtr"`
SubStruct []Tag `process:"yes,SubStruct"`
SubStructPtr []*Tag `process:"yes,SubStructPtr"`
}
helper func
Further we need two helper funcs to check if a struct has a tag and to print to console
func Stringify(i interface{}) string {
s, _ := json.MarshalIndent(i, "", " ")
return string(s)
}
func HasTag(structFiled reflect.StructField, tagName string, tagValue string) bool {
tag := structFiled.Tag
if value, ok := tag.Lookup(tagName); ok {
parts := strings.Split(value, ",")
if len(parts) > 0 {
return parts[0] == tagValue
}
}
return false
}
patcher - the actual solution
type Patcher struct {
Matcher func(structFiled *reflect.StructField, v reflect.Value) bool
Process func(in string) string
}
func (p *Patcher) value(idx int, v reflect.Value, structFiled *reflect.StructField) {
if !v.IsValid() {
return
}
switch v.Kind() {
case reflect.Ptr:
p.value(idx, v.Elem(), structFiled)
case reflect.Struct:
for i := 0; i < v.NumField(); i++ {
var sf = v.Type().Field(i)
structFiled = &sf
p.value(i, v.Field(i), structFiled)
}
case reflect.Slice:
for i := 0; i < v.Len(); i++ {
p.value(i, v.Index(i), structFiled)
}
case reflect.String:
if p.Matcher(structFiled, v) {
v.SetString(p.Process(v.String()))
}
}
}
func (p *Patcher) Apply(in interface{}) {
p.value(-1, reflect.ValueOf(in).Elem(), nil)
}
how to use
func main() {
var NamePtr string = "golang"
var SubNamePtr string = "*secure"
testUser := User{
ID: 1,
Name: "lumo",
NamePtr: &NamePtr,
SubStruct: []Tag{{
Name: "go",
},
},
SubStructPtr: []*Tag{&Tag{
Name: "*go",
NamePtr: &SubNamePtr,
},
},
}
var p = Patcher{
// filter - return true if the field in struct has a tag process=true
Matcher: func(structFiled *reflect.StructField, v reflect.Value) bool {
return HasTag(*structFiled, "process", "yes")
},
// process
Process: func(in string) string {
if in != "" {
return fmt.Sprintf("!%s!", strings.ToUpper(in))
} else {
return "!empty!"
}
return in
},
}
p.Apply(&testUser)
fmt.Println("Output:")
fmt.Println(Stringify(testUser))
}
goplay
https://goplay.tools/snippet/-0MHDfKr7ax
I have a case where I'm I have to support multiple versions. Each one has different data so I create 2 structs. Based on the version I will return 1 of the structs. Once I identify which struct, I then would request the data and Unmarshal into the struct. However, Since that struct satisfies an interface, I dont think the unmarshal is working correctly. I always get the zero value for the sctruct
package main
import (
"encoding/json"
"fmt"
)
// Ten300 ...
type Ten300 struct {
Map string `json:"map"`
Enabled string `json:"enabled"`
}
// Ten400 ...
type Ten400 struct {
Block1 int `json:"block_1"`
Block2 int `json:"block_2"`
}
// NET ...
type NET struct {
CMD Commands
S TenIft
}
// Commands ...
type Commands struct {
Get string
}
// TenIft ...
type TenIft interface {
Get(string) error
}
// Get ...
func (n *Ten300) Get(module string) error {
fmt.Println("Ten300", "Get()")
return nil
}
// Get ...
func (n *Ten400) Get(module string) error {
fmt.Println("Ten400", "Get()")
n.Block2 = 100
return nil
}
// TenGen easy to read fun type
type TenGen func() NET
// VersionSetup is a map of possible version
var VersionSetup = map[string]TenGen{
"3.0.0": func() NET {
return NET{
CMD: Commands{
Get: "getConfig",
},
S: &Ten300{},
}
},
"4.0.0": func() NET {
return NET{
CMD: Commands{
Get: "getSettings",
},
S: &Ten400{},
}
},
}
const input300 = `{
"map": "one",
"enabled": "two"
}`
const input400 = `{
"block_1": 1,
"block_2": 2
}`
// Setup ...
func (n *NET) Setup() error {
// This Switch is just to use hard coded data
var input string
switch n.S.(type) {
case *Ten400:
input = input400
case *Ten300:
input = input300
}
err := json.Unmarshal([]byte(input), n.S)
if err != nil {
fmt.Println("returning err:", err)
return err
}
fmt.Printf("n.s type: %T\nn.s value: %+v\n", n.S, n.S)
n.S.Get("xxx")
return nil
}
func main() {
version := "3.0.0"
if f, ok := VersionSetup[version]; ok {
net := f()
err := net.Setup()
if err != nil {
panic(err)
}
}
version = "4.0.0"
if f, ok := VersionSetup[version]; ok {
net := f()
err := net.Setup()
if err != nil {
panic(err)
}
}
}
Added go-playground ... this seems to work, but is this the best solution?
I want to mock function using interface and I was able to mock the first function callsomething
With great help from icza , Now it’s a bit more tricky . I want to test function vl1
With mock for function function1 , how it can be done.
https://play.golang.org/p/w367IOjADFV
package main
import (
"fmt"
"time"
"testing"
)
type vInterface interface {
function1() bool
}
type mStruct struct {
info string
time time.Time
}
func (s *mStruct) function1() bool {
return true
}
func callSomething(si vInterface) bool {
return si.function1()
}
func (s *mStruct) vl1() bool {
return callSomething(s)
}
var currentVt1 mStruct
func main() {
vl1 := currentVt1.vl1()
fmt.Println(vl1)
}
//——————————————————TESTS——————————————————
// This test is working as expected (as suggested by icza) for the function "callSomething"
// here we use mock interface and mock function which helps to mock function1
type mockedVInterface struct {
value bool
}
func (m mockedVInterface) fn1() bool {
return m.value
}
func Test_callSomething(t *testing.T) {
type args struct {
si vInterface
}
tests := []struct {
name string
args args
want bool
}{
{
name: "first value",
args: args{
si: mockedVInterface{value: false},
},
want: false,
},
{
name: "second value",
args: args{
si: mockedVInterface{value: true},
},
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := callSomething(tt.args.si); got != tt.want {
t.Errorf("callSomething() = %v, want %v", got, tt.want)
}
})
}
}
//----Here is the test which a bit tricky
//I want to call to "s.vl1()" and test it with mock for "function1"
func Test_mStruct_vl1(t *testing.T) {
type fields struct {
info string
time time.Time
}
tests := []struct {
name string
fields fields
want bool
}{
{
name: "test 2",
fields: struct {
info string
time time.Time
}{info: "myinfo", time: time.Now() },
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s := &mStruct{
info: tt.fields.info,
time: tt.fields.time,
}
//here the test is starting
if got := s.vl1(); got != tt.want {
t.Errorf("mStruct.vl1() = %v, want %v", got, tt.want)
}
})
}
}
If you want to mock a function in Go you'll have to declare a variable that will hold the function value, have all callers of the function use the variable instead and then during tests you can switch up the function value for a mock implementation.
func callSomethingFunc(si vInterface) bool {
return si.function1()
}
var callSomething = callSomethingFunc
func (s *mStruct) vl1() bool {
return callSomething(s)
}
// ...
func Test_mStruct_vl1(t *testing.T) {
callSomething = func(si vInterface) bool { // set mock
// do mock stuff
}
defer func(){
callSomething = callSomethingFunc // set back original func at end of test
}()
// ...
Im using interface which I want to mock one method in it function1 in test and I wasn't able to figure it out how is the best to do it that for prod code it will provide 1 value and for test provide some mock value , can someone please give example ? (edited)
this is the code:
https://play.golang.org/p/w367IOjADFV
package main
import (
"fmt"
"time"
)
type vInterface interface {
function1() bool
}
type mStruct struct {
info string
time time.Time
}
func (s *mStruct) function1() bool {
return true
}
func callSomething(si vInterface) bool {
return si.function1()
}
func (s *mStruct) vl1() bool {
s.time = time.Now()
s.info = "vl1->info"
return callSomething(s)
}
var currentVt1 mStruct
func main() {
vl1 := currentVt1.vl1()
fmt.Println(vl1)
}
The test is like this
func Test_callSomething(t *testing.T) {
type args struct {
si vInterface
}
tests := []struct {
name string
args args
want bool
}{
{
name: "my tests",
args: args{
},
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := callSomething(tt.args.si); got != tt.want {
t.Errorf("callSomething() = %v, want %v", got, tt.want)
}
})
}
}
But not sure how to mock it right ...
update
func Test_mStruct_vl1(t *testing.T) {
type fields struct {
info string
time time.Time
}
tests := []struct {
name string
fields fields
want bool
}{
{
name: "some test",
fields: struct {
info string
time time.Time
}{info: "myinfo", time: time.Now() },
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s := &mStruct{
info: tt.fields.info,
time: tt.fields.time,
}
if got := s.vl1(); got != tt.want {
t.Errorf("vl1() = %v, want %v", got, tt.want)
}
})
}
}
First you need a type (any type) that implements the vInterface interface. Here's a simple example:
type mockedVInterface struct {
value bool
}
func (m mockedVInterface) function1() bool {
return m.value
}
This is a simple enough implementation which we can control: we can tell what its function1() function should return by simply setting that value to its value field.
This mockedVInterface type is created solely for testing purposes, the production code does not need it. Put it in the same file where you have the test code (put it before Test_callSomething()).
And here's the testing code:
func Test_callSomething(t *testing.T) {
type args struct {
si vInterface
}
tests := []struct {
name string
args args
want bool
}{
{
name: "testing false",
args: args{
si: mockedVInterface{value: false},
},
want: false,
},
{
name: "testing true",
args: args{
si: mockedVInterface{value: true},
},
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := callSomething(tt.args.si); got != tt.want {
t.Errorf("callSomething() = %v, want %v", got, tt.want)
}
})
}
}
Note that in this simple case we could also use a simple non-struct type that has bool as its underlying type like this:
type mockedVInterface bool
func (m mockedVInterface) function1() bool {
return bool(m)
}
And it works and testing code is also simpler:
tests := []struct {
name string
args args
want bool
}{
{
name: "testing false",
args: args{
si: mockedVInterface(false),
},
want: false,
},
{
name: "testing true",
args: args{
si: mockedVInterface(true),
},
want: true,
},
}
But this only works if the mockable interface has a single function with a single return value. In the general case a struct is needed.
I am new to Golang and I have been unable to find a solution to this problem using flag.
How can I use flag so my program can handle calls like these, where the -term flag may be present a variable number of times, including 0 times:
./myprogram -f flag1
./myprogram -f flag1 -term t1 -term t2 -term t3
You need to declare your own type which implements the Value interface. Here is an example.
// Created so that multiple inputs can be accecpted
type arrayFlags []string
func (i *arrayFlags) String() string {
// change this, this is just can example to satisfy the interface
return "my string representation"
}
func (i *arrayFlags) Set(value string) error {
*i = append(*i, strings.TrimSpace(value))
return nil
}
then in the main function where you are parsing the flags
var myFlags arrayFlags
flag.Var(&myFlags, "term", "my terms")
flag.Parse()
Now all the terms are contained in the slice myFlags
This question is an interesting one and can play in many variations.
Array
Map
Struct
The core content is the same as #reticentroot answered,
Complete the definition of this interface: Flag.Value
The following are examples to share and provide relevant links as much as possible
Example
expected usage:
type Books []string
func (*Books) String() string { return "" }
func (*Books) Set(string) error { return nil }
type Dict map[string]string
func (*Dict) String() string { return "" }
func (*Dict) Set(string) error { return nil }
type Person struct {
Name string
Age int
}
func (*Person) String() string { return "" }
func (*Person) Set(string) error { return nil }
func pseudocode() {
flagSetTest := flag.NewFlagSet("test", flag.ContinueOnError)
books := Books{}
flagSetTest.Var(&books, "book", "-book C++ -book Go -book javascript")
// expected output: books: []string{C++,Go,javascript}
dict := Dict{}
flagSetTest.Var(&dict, "dict", "-dict A:65|B:66")
// expected output: dict: map[string]string{"A":"65", "B":"66"}
// map
person := Person{}
flagSetTest.Var(&person, "person", "-person Name:foo|Age:18")
// output: {Name:foo Age:18}
flagSetTest.Parse(os.Args[1:])
fmt.Println(person, books, dict)
}
Full code
package main
import (
"bufio"
"errors"
"flag"
"fmt"
"os"
"reflect"
"strconv"
"strings"
)
type BooksValue []string
// https://github.com/golang/go/blob/2580d0e/src/flag/flag.go#L298
func (arr *BooksValue) String() string {
/*
value.String(): https://github.com/golang/go/blob/2580d0e/src/flag/flag.go#L870
DefValue string:
- https://github.com/golang/go/blob/2580d0e/src/flag/flag.go#L348
- https://github.com/golang/go/blob/2580d0e/src/flag/flag.go#L914-L920
- https://github.com/golang/go/blob/2580d0e/src/flag/flag.go#L529-L536
- https://github.com/golang/go/blob/2580d0e/src/flag/flag.go#L464
*/
return ""
}
// https://github.com/golang/go/blob/2580d0e/src/flag/flag.go#L299
func (arr *BooksValue) Set(value string) error {
/*
value: https://github.com/golang/go/blob/2580d0e/src/flag/flag.go#L947
bool: Set(value): https://github.com/golang/go/blob/2580d0e/src/flag/flag.go#L966-L975
else: Set(value): https://github.com/golang/go/blob/2580d0e/src/flag/flag.go#L986-L988
*/
*arr = append(*arr, strings.TrimSpace(value))
return nil
}
type DictValue map[string]string
func (m *DictValue) String() string {
return ""
}
func (m *DictValue) Set(value string) error {
arr := strings.Split(value, "|") // "key1:val1|key2:val2|..."
for _, curPairStr := range arr {
itemArr := strings.Split(curPairStr, ":")
key := itemArr[0]
val := itemArr[1]
(*m)[key] = val
}
return nil
}
type PersonValue struct {
Name string
Age int
Msg string
IsActive bool
}
func (s *PersonValue) String() string {
return ""
}
func (s *PersonValue) Set(value string) error {
arr := strings.Split(value, "|") // "Field1:Value1|F2:V2|...|FN:VN"
for _, curPairStr := range arr {
itemArr := strings.Split(curPairStr, ":")
key := itemArr[0]
val := itemArr[1]
// [Access struct property by name](https://stackoverflow.com/a/66470232/9935654)
pointToStruct := reflect.ValueOf(s)
curStruct := pointToStruct.Elem()
curField := curStruct.FieldByName(key)
if !curField.IsValid() {
return errors.New("not found")
}
// CanSet one of conditions: Name starts with a capital
if !curField.CanSet() {
return errors.New("can't set")
}
t := reflect.TypeOf(*s)
structFieldXXX, isFound := t.FieldByName(key)
if !isFound {
return errors.New("not found")
}
switch structFieldXXX.Type.Name() {
case "int":
// https://github.com/golang/go/blob/2580d0e/src/flag/flag.go#L146-L153
intValue, err := strconv.ParseInt(val, 0, strconv.IntSize)
if err != nil {
return errors.New("parse error: [int]")
}
curField.SetInt(intValue)
case "bool":
// https://github.com/golang/go/blob/2580d0e/src/flag/flag.go#L117-L121
boolValue, err := strconv.ParseBool(val)
if err != nil {
return errors.New("parse error: [bool]")
}
curField.SetBool(boolValue)
case "string":
curField.SetString(val)
default:
return errors.New("not support type=" + structFieldXXX.Type.Name())
}
}
return nil
}
func main() {
flagSetTest := flag.NewFlagSet("test", flag.ContinueOnError)
// array
books := BooksValue{}
flagSetTest.Var(&books, "book", "-book Go -book javascript ...")
// map
myMap := DictValue{}
flagSetTest.Var(&myMap, "map", "-dict A:65|B:66")
// struct
person := PersonValue{Msg: "Hello world"}
flagSetTest.Var(&person, "person", "-person Name:string|Age:int|Msg:string|IsActive:bool")
testArgs := []string{"test",
"-book", "Go", "-book", "javascript", // testArray
"-map", "A:65|B:66|Name:Carson", // testMap
"-person", "Name:Carson|Age:30|IsActive:true", // testStruct
}
testFunc := func(args []string, reset bool) {
if reset {
books = BooksValue{}
myMap = DictValue{}
person = PersonValue{}
}
if err := flagSetTest.Parse(args); err != nil {
fmt.Printf(err.Error())
}
fmt.Printf("%+v\n", books)
fmt.Printf("%+v\n", myMap)
fmt.Printf("%+v\n", person)
}
testFunc(testArgs[1:], false)
// ↓ play by yourself
scanner := bufio.NewScanner(os.Stdin)
for {
fmt.Println("Enter CMD: ") // example: test -book item1 -book item2 -map key1:value1|key2:v2 -person Age:18|Name:Neil|IsActive:true
scanner.Scan() // Scans a line from Stdin(Console)
text := scanner.Text() // Holds the string that scanned
args := strings.Split(text, " ")
switch args[0] {
case "quit":
return
case "test":
testFunc(args[1:], true)
}
}
}
go playground