diff --git a/lib.go b/lib.go index 0320295..e63f24d 100644 --- a/lib.go +++ b/lib.go @@ -30,7 +30,7 @@ func ConfigureSetup( } /* -ConfigureSmart - configuring by analys tags for add prefer strategy for configuring +ConfigureSmart - configuring by analysing tags for add prefer strategy for configuring */ func ConfigureSmart( structure interface{}, diff --git a/lib_test.go b/lib_test.go index a186c18..bf3387b 100644 --- a/lib_test.go +++ b/lib_test.go @@ -2,6 +2,7 @@ package gostructor import ( "fmt" + "os" "testing" "github.com/goreflect/gostructor/infra" @@ -34,6 +35,14 @@ type ( Field1 string } `cf_hocon:"node=planC.tururu.tratatat.planZ"` } + + EnvStruct struct { + Field1 int16 `cf_env:"myField1"` + Field2 string `cf_env:"myField2"` + Field3 bool `cf_env:"myField3"` + Field4 float32 `cf_env:"myField4"` + Field5 []bool `cf_env:"myField5"` + } ) func Test_parseHocon1(t *testing.T) { @@ -103,3 +112,30 @@ func Test_smartConfigure(t *testing.T) { }, }, myStruct.(*MyStruct4)) } + +func Test_getValueFromEnvironment(t *testing.T) { + os.Setenv("myField1", "12") + os.Setenv("myField2", "test") + os.Setenv("myField3", "true") + os.Setenv("myField4", "12.2") + os.Setenv("myField5", "true,false,true") + defer func() { + os.Remove("myField1") + os.Remove("myField2") + os.Remove("myField3") + os.Remove("myField4") + os.Remove("myField5") + }() + myStruct, err := ConfigureSmart(&EnvStruct{}, "") + if err != nil { + t.Error(err) + } + + assert.Equal(t, &EnvStruct{ + Field1: 12, + Field2: "test", + Field3: true, + Field4: 12.2, + Field5: []bool{true, false, true}, + }, myStruct.(*EnvStruct)) +} diff --git a/pipeline/environment_function.go b/pipeline/environment_function.go index 9fe5bcf..9d4f0f0 100644 --- a/pipeline/environment_function.go +++ b/pipeline/environment_function.go @@ -3,21 +3,65 @@ package pipeline import ( "errors" "fmt" + "os" + "reflect" + "strings" + "github.com/goreflect/gostructor/converters" "github.com/goreflect/gostructor/infra" + "github.com/goreflect/gostructor/tags" ) +/*EnvironmentConfig - configuring structures from environment*/ type EnvironmentConfig struct { } +const ( + separator = "," +) + +/* +GetComplexType - getting complex types like slices from environment variable +*/ func (config EnvironmentConfig) GetComplexType(context *structContext) infra.GoStructorValue { - fmt.Println("Level: Debug. Message: environment values sources start") + valueIndirect := reflect.Indirect(context.Value) + switch valueIndirect.Kind() { + case reflect.Slice: + valueTag := context.StructField.Tag.Get(tags.TagEnvironment) + if valueTag != "" { + value := os.Getenv(valueTag) + // add here additional logic for middlewares and other + array := config.convertStringIntoArray(value) + return converters.ConvertBetweenComplexTypes(reflect.ValueOf(array), valueIndirect) + } + return infra.NewGoStructorNoValue(context.Value.Interface(), errors.New("complex type "+valueIndirect.Kind().String()+" not implemented in environment parsing function")) + default: + return infra.NewGoStructorNoValue(context.Value.Interface(), errors.New("complex type "+valueIndirect.Kind().String()+" not implemented in environment parsing function")) + } +} - return infra.NewGoStructorNoValue(context.Value.Interface(), errors.New("getComplexType not implemented for environment configuring")) +// TODO: Add variant for change separator. Currently it is comma +func (config EnvironmentConfig) convertStringIntoArray(value string) []string { + return strings.Split(value, separator) } +/* +GetBaseType - getting base type values +*/ func (config EnvironmentConfig) GetBaseType(context *structContext) infra.GoStructorValue { - fmt.Println("Level: Debug. Message: environment values sources start") + fmt.Println("Level: Debug. Environment values sources start") + valueIndirect := reflect.Indirect(context.Value) + valueTag := context.StructField.Tag.Get(tags.TagEnvironment) - return infra.NewGoStructorNoValue(context.Value.Interface(), errors.New("getBaseType not implemented for environment configuring")) + if valueTag != "" { + value := os.Getenv(valueTag) + return converters.ConvertBetweenPrimitiveTypes(reflect.ValueOf(value), valueIndirect) + } + return infra.NewGoStructorNoValue(context.Value, errors.New("getBaseType can not get field by empty tag value of tag: "+tags.TagEnvironment)) } + +// TODO: using in future for run middlewares +// func (config EnvironmentConfig) checksByMiddlewares(tagvalue string) bool { +// // in the future in this case will be added a call middlewares functions +// return tagvalue == "" +// } diff --git a/pipeline/environment_function_test.go b/pipeline/environment_function_test.go new file mode 100644 index 0000000..8c1cd11 --- /dev/null +++ b/pipeline/environment_function_test.go @@ -0,0 +1,132 @@ +package pipeline + +import ( + "errors" + "os" + "reflect" + "testing" + + "github.com/goreflect/gostructor/infra" + "github.com/goreflect/gostructor/tags" +) + +func TestEnvironmentConfig_GetBaseType(t *testing.T) { + myStruct := struct { + field1 string `cf_env:"testBaseType"` + }{} + os.Setenv("testBaseType", "tururu") + myStruct1 := reflect.Indirect(reflect.ValueOf(myStruct)) + field1 := myStruct1.Type().Field(0) + t.Log("type of field: ", field1.Name+", "+field1.Type.Name()) + type args struct { + context *structContext + } + tests := []struct { + name string + config EnvironmentConfig + args args + want infra.GoStructorValue + }{ + { + name: "get success base type string from environment", + config: EnvironmentConfig{}, + args: args{ + context: &structContext{ + StructField: field1, + Value: myStruct1.Field(0), + }, + }, + want: infra.NewGoStructorTrueValue(reflect.ValueOf("tururu")), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + config := EnvironmentConfig{} + got := config.GetBaseType(tt.args.context) + os.Remove("testBaseType") + + if got.Value.String() != tt.want.Value.String() { + t.Errorf("EnvironmentConfig.GetComplexType() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestEnvironmentConfig_GetBaseTypeFaield(t *testing.T) { + myStruct := struct { + field1 string `cf_env:""` + }{} + myStruct1 := reflect.Indirect(reflect.ValueOf(myStruct)) + field1 := myStruct1.Type().Field(0) + t.Log("type of field: ", field1.Name+", "+field1.Type.Name()) + type args struct { + context *structContext + } + tests := []struct { + name string + config EnvironmentConfig + args args + want infra.GoStructorValue + }{ + { + name: "get faield base type string from environment because not set name of this value", + config: EnvironmentConfig{}, + args: args{ + context: &structContext{ + StructField: field1, + Value: myStruct1.Field(0), + }, + }, + want: infra.NewGoStructorNoValue(reflect.ValueOf(myStruct1.Field(0)), errors.New("getBaseType can not get field by empty tag value of tag: "+tags.TagEnvironment)), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + config := EnvironmentConfig{} + got := config.GetBaseType(tt.args.context) + if got.GetNotAValue().Error.Error() != tt.want.GetNotAValue().Error.Error() { + t.Errorf("EnvironmentConfig.GetComplexType() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestEnvironmentConfig_GetComplexType(t *testing.T) { + myStruct := struct { + fieldhard []float32 `cf_env:"mySlice"` + }{} + os.Setenv("mySlice", "1.12,1.24,1.67") + myStruct1 := reflect.Indirect(reflect.ValueOf(myStruct)) + field1 := myStruct1.Type().Field(0) + t.Log("type of field: ", field1.Name+", "+field1.Type.Name()) + type args struct { + context *structContext + } + tests := []struct { + name string + config EnvironmentConfig + args args + want infra.GoStructorValue + }{ + { + name: "get slice from environment into float32 slice structure field", + config: EnvironmentConfig{}, + args: args{ + context: &structContext{ + StructField: field1, + Value: myStruct1.Field(0), + }, + }, + want: infra.NewGoStructorTrueValue(reflect.ValueOf([]float32{1.12, 1.24, 1.67})), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + config := EnvironmentConfig{} + got := config.GetComplexType(tt.args.context) + if !reflect.DeepEqual(got.Value.Interface(), tt.want.Value.Interface()) { + t.Errorf("EnvironmentConfig.GetComplexType() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/pipeline/pipeline.go b/pipeline/pipeline.go index 83842f4..edb7173 100644 --- a/pipeline/pipeline.go +++ b/pipeline/pipeline.go @@ -43,7 +43,6 @@ const ( notSupportedTypeError = "not supported type " - /*EmptyAdditionalPrefix - prefix for setup before all values can be empty*/ EmptyAdditionalPrefix = "" ) @@ -141,7 +140,7 @@ func Configure( // filename for file configuring fileName string, // functions will be configure structure - pipelineChaines []infra.FuncType, + pipelineChains []infra.FuncType, // prefix by getting data from source placed in entry prefix string, // smartConfigure - analys structure by tags for find methods which should use for configuration @@ -160,7 +159,7 @@ func Configure( analysedChains := tags.GetFunctionTypes(structure) pipeline = getFunctionChain(fileName, analysedChains) } else { - pipeline = getFunctionChain(fileName, pipelineChaines) + pipeline = getFunctionChain(fileName, pipelineChains) } // currentChain := pipeline.chains @@ -271,7 +270,7 @@ func (pipeline *Pipeline) configuringValues(context *structContext) error { return errors.New("can not set " + valueIndirect.Kind().String() + " into struct field.") } } else { - return errors.New("Loglevel: Debug Message: value get not implementedable value: ") + return errors.New("Loglevel: Debug Message: value get not implemented value: ") } case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: return errors.New("not supported types of unsigned integer") @@ -286,7 +285,7 @@ func (pipeline *Pipeline) configuringValues(context *structContext) error { return errors.New("can not set " + valueIndirect.Kind().String() + " into struct field.") } } else { - return errors.New("value get not implementedable value: ") + return errors.New("value get not implemented value: ") } default: return errors.New("not supported type for hocon parsing") diff --git a/tags/analys_func.go b/tags/analys_func.go index a9736bf..359c108 100644 --- a/tags/analys_func.go +++ b/tags/analys_func.go @@ -7,17 +7,17 @@ import ( ) /* -GetFunctionTypes - return slice of functions which should configuring sourceStruct structure +GetFunctionTypes - return slice of functions which should configure sourceStruct structure */ func GetFunctionTypes(sourceStruct interface{}) []infra.FuncType { - summirize := []int{} + summarize := []int{} value := reflect.Indirect(reflect.ValueOf(sourceStruct)) for i := 0; i < value.NumField(); i++ { summirizeLevel := recurseStructField(value.Type().Field(i)) - summirize = combineFields(summirize, summirizeLevel) + summarize = combineFields(summarize, summirizeLevel) } result := []infra.FuncType{} - for funcType, value := range summirize { + for funcType, value := range summarize { if value > 0 { result = append(result, infra.FuncType(funcType)) } @@ -26,16 +26,16 @@ func GetFunctionTypes(sourceStruct interface{}) []infra.FuncType { } func recurseStructField(structField reflect.StructField) []int { - summirize := checkFuncsByTags(structField) + summarize := checkFuncsByTags(structField) switch structField.Type.Kind() { case reflect.Struct: for i := 0; i < structField.Type.NumField(); i++ { - summirizeLevel := recurseStructField(structField.Type.Field(i)) - summirize = combineFields(summirize, summirizeLevel) + summarizeLevel := recurseStructField(structField.Type.Field(i)) + summarize = combineFields(summarize, summarizeLevel) } } - return summirize + return summarize } //TODO: decomposition @@ -57,7 +57,7 @@ func combineFields(summCurrent []int, newSumm []int) []int { } func checkFuncsByTags(structField reflect.StructField) []int { - summirize := make([]int, AmountTags) // amount repeats tags + summarize := make([]int, AmountTags) // amount repeats tags for _, value := range []string{ TagYaml, TagJson, @@ -72,11 +72,12 @@ func checkFuncsByTags(structField reflect.StructField) []int { continue } else { // TODO: add additional anaylys tag values for middlewares functions and others - summirize[getFuncTypeByTag(value)]++ + + summarize[getFuncTypeByTag(value)]++ } } - return summirize + return summarize } func getFuncTypeByTag(tagName string) infra.FuncType {