From 9dcfbb02589841b223ef5aaa8d89d72bf124f56b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20K=C3=B6ken?= Date: Thu, 17 Jun 2021 21:42:26 +0200 Subject: [PATCH] =?UTF-8?q?Added=20AppFilePropertySource=20and=20AppFilePa?= =?UTF-8?q?rse=20to=20hold=20and=20parse=20the=20pr=E2=80=A6=20(#3)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add file property file support. --- .travis.yml | 4 - app_file.go | 145 +++++++++++++++++++++++++++++++ app_file_test.go | 69 +++++++++++++++ args.go | 4 + cmdline.go | 64 +++++++++++++- cmdline_test.go | 35 ++++++++ codecov.yml | 4 + component.go | 49 +++++++++++ conversion.go | 2 + environment.go | 13 +++ go.mod | 15 ++++ go.test.sh | 12 --- parser.go | 49 ----------- parser_test.go | 41 --------- property.go | 10 +++ resolver.go | 8 +- test-resources/procyon.db.yaml | 5 ++ test-resources/procyon.dev.yaml | 9 ++ test-resources/procyon.prod.yaml | 9 ++ test-resources/procyon.test.yaml | 9 ++ test-resources/procyon.yaml | 9 ++ util.go | 45 +++++----- uuid.go | 10 +++ 23 files changed, 492 insertions(+), 128 deletions(-) create mode 100644 app_file.go create mode 100644 app_file_test.go create mode 100644 codecov.yml delete mode 100644 go.test.sh delete mode 100644 parser.go delete mode 100644 parser_test.go create mode 100644 test-resources/procyon.db.yaml create mode 100644 test-resources/procyon.dev.yaml create mode 100644 test-resources/procyon.prod.yaml create mode 100644 test-resources/procyon.test.yaml create mode 100644 test-resources/procyon.yaml diff --git a/.travis.yml b/.travis.yml index 12a3d3d..4144fb8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,10 +5,6 @@ go: - 1.14.x - 1.15.x -branches: - only: - - master - before_install: - go get -t -v ./... diff --git a/app_file.go b/app_file.go new file mode 100644 index 0000000..c6f86fe --- /dev/null +++ b/app_file.go @@ -0,0 +1,145 @@ +package core + +import ( + "fmt" + "gopkg.in/yaml.v3" + "io/ioutil" + "os" + "strings" +) + +const ProcyonAppFilePropertySource = "ProcyonAppFilePropertySource" + +const ProcyonAppFilePrefix = "procyon" +const ProcyonAppFilePath = "test-resources/" +const ProcyonAppFileSuffix = ".yaml" +const ProcyonDefaultProfile = "default" + +type AppFilePropertySource struct { + propertyMap map[string]interface{} +} + +func NewAppFilePropertySource(profiles string) *AppFilePropertySource { + if profiles == "" { + profiles = ProcyonDefaultProfile + } + + profileArr := strings.FieldsFunc(profiles, func(r rune) bool { + return r == ',' + }) + + filePaths := make([]string, 0) + + for _, profile := range profileArr { + path := ProcyonAppFilePath + ProcyonAppFilePrefix + + if profile == ProcyonDefaultProfile { + path = path + ProcyonAppFileSuffix + } else { + path = path + "." + strings.Trim(profile, " ") + ProcyonAppFileSuffix + } + + filePaths = append(filePaths, path) + } + + propertyMap, err := NewAppFileParser().Parse(filePaths) + + if err != nil { + panic(err) + } + + propertySource := &AppFilePropertySource{ + propertyMap: propertyMap, + } + + return propertySource +} + +func (propertySource *AppFilePropertySource) GetName() string { + return ProcyonAppFilePropertySource +} + +func (propertySource *AppFilePropertySource) GetSource() interface{} { + return propertySource.propertyMap +} + +func (propertySource *AppFilePropertySource) GetProperty(name string) interface{} { + if propertySource.ContainsProperty(name) { + return propertySource.propertyMap[name] + } + + return nil +} + +func (propertySource *AppFilePropertySource) ContainsProperty(name string) bool { + if _, ok := propertySource.propertyMap[name]; ok { + return true + } + + return false +} + +func (propertySource *AppFilePropertySource) GetPropertyNames() []string { + keys := make([]string, 0, len(propertySource.propertyMap)) + + for key, _ := range propertySource.propertyMap { + keys = append(keys, key) + } + + return keys +} + +type AppFileParser struct { +} + +func NewAppFileParser() *AppFileParser { + return &AppFileParser{} +} + +func (parser *AppFileParser) Parse(filePaths []string) (map[string]interface{}, error) { + propertyMap := make(map[string]interface{}) + + for _, filePath := range filePaths { + resultMap, err := parser.parseFile(filePath) + + if err != nil { + return nil, err + } + + propertyMap = parser.mergeFlattenMap(propertyMap, resultMap) + } + + return propertyMap, nil +} + +func (parser *AppFileParser) parseFile(filePath string) (map[string]interface{}, error) { + if _, err := os.Stat(filePath); err != nil { + return nil, fmt.Errorf("app file does not exist : %s", filePath) + } + + data, err := ioutil.ReadFile(filePath) + + if err != nil { + return nil, fmt.Errorf("could not open app file '%s', %s", filePath, err.Error()) + } + + propertyMap := make(map[string]interface{}) + + err = yaml.Unmarshal(data, propertyMap) + + if err != nil { + return nil, fmt.Errorf("could not read app file '%s', %s", filePath, err.Error()) + } + + flattenMap := FlatMap(propertyMap) + + return flattenMap, nil +} + +func (parser *AppFileParser) mergeFlattenMap(map1, map2 map[string]interface{}) map[string]interface{} { + for key, value := range map2 { + map1[key] = value + } + + return map1 +} diff --git a/app_file_test.go b/app_file_test.go new file mode 100644 index 0000000..cbb0b7d --- /dev/null +++ b/app_file_test.go @@ -0,0 +1,69 @@ +package core + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func TestAppFilePropertySource(t *testing.T) { + propertySource := NewAppFilePropertySource("dev, db") + value := propertySource.GetProperty("procyon.application.name") + if value == "" { + + } +} + +func TestAppFileParser_Parse(t *testing.T) { + parser := NewAppFileParser() + + filePaths := []string{"test-resources/procyon.test.yaml", "test-resources/procyon.db.yaml"} + + propertyMap, err := parser.Parse(filePaths) + assert.NoError(t, err) + assert.NotNil(t, propertyMap) + + assert.Contains(t, propertyMap, "procyon.application.name") + assert.Equal(t, "Procyon Test Application", propertyMap["procyon.application.name"]) + + assert.Contains(t, propertyMap, "logging.level") + assert.Equal(t, "INFO", propertyMap["logging.level"]) + + assert.Contains(t, propertyMap, "server.port") + assert.Equal(t, 8095, propertyMap["server.port"]) + + assert.Contains(t, propertyMap, "procyon.datasource.url") + assert.Equal(t, "test-url", propertyMap["procyon.datasource.url"]) + + assert.Contains(t, propertyMap, "procyon.datasource.username") + assert.Equal(t, "test-username", propertyMap["procyon.datasource.username"]) + + assert.Contains(t, propertyMap, "procyon.datasource.password") + assert.Equal(t, "test-password", propertyMap["procyon.datasource.password"]) +} + +func TestAppFileParser_ContainsProperty(t *testing.T) { + propertySource := NewAppFilePropertySource("dev, db") + assert.True(t, propertySource.ContainsProperty("procyon.application.name")) + assert.False(t, propertySource.ContainsProperty("procyon.server.timeout")) +} + +func TestAppFileParser_GetProperty(t *testing.T) { + propertySource := NewAppFilePropertySource("dev") + assert.NotNil(t, propertySource.GetProperty("procyon.application.name")) + assert.Equal(t, "Procyon Dev Application", propertySource.GetProperty("procyon.application.name")) +} + +func TestAppFileParser_GetPropertyNames(t *testing.T) { + propertySource := NewAppFilePropertySource("dev") + assert.NotNil(t, propertySource.GetPropertyNames()) +} + +func TestAppFileParser_GetName(t *testing.T) { + propertySource := NewAppFilePropertySource("dev") + assert.Equal(t, ProcyonAppFilePropertySource, propertySource.GetName()) +} + +func TestAppFileParser_GetSource(t *testing.T) { + propertySource := NewAppFilePropertySource("dev") + assert.NotNil(t, propertySource.GetSource()) +} diff --git a/args.go b/args.go index 01d5348..206b298 100644 --- a/args.go +++ b/args.go @@ -18,15 +18,19 @@ func (args CommandLineArgs) addOptionArgs(name string, value string) { if args.optionArgs[name] == nil { args.optionArgs[name] = make([]string, 0) } + args.optionArgs[name] = append(args.optionArgs[name], value) } func (args CommandLineArgs) getOptionNames() []string { argMapKeys := goo.GetType(args.optionArgs).GetGoValue().MapKeys() + mapKeys := make([]string, len(argMapKeys)) + for i := 0; i < len(argMapKeys); i++ { mapKeys[i] = argMapKeys[i].String() } + return mapKeys } diff --git a/cmdline.go b/cmdline.go index 1cdc116..9866aaa 100644 --- a/cmdline.go +++ b/cmdline.go @@ -1,6 +1,10 @@ package core -import "strings" +import ( + "errors" + "flag" + "strings" +) const ProcyonApplicationCommandLinePropertySource = "ProcyonApplicationCommandLinePropertySource" const NonOptionArgsPropertyName = "nonOptionArgs" @@ -18,12 +22,15 @@ type SimpleCommandLinePropertySource struct { func NewSimpleCommandLinePropertySource(args []string) SimpleCommandLinePropertySource { cmdLineArgs, err := NewCommandLineArgsParser().Parse(args) + if err != nil { panic(err) } + cmdlinePropertySource := SimpleCommandLinePropertySource{ source: cmdLineArgs, } + return cmdlinePropertySource } @@ -38,15 +45,20 @@ func (cmdLineSource SimpleCommandLinePropertySource) GetSource() interface{} { func (cmdLineSource SimpleCommandLinePropertySource) GetProperty(name string) interface{} { if NonOptionArgsPropertyName == name { nonOptValues := cmdLineSource.GetNonOptionArgs() + if nonOptValues != nil { return strings.Join(nonOptValues, ",") } + return nil } + optValues := cmdLineSource.GetOptionValues(name) + if optValues != nil { return strings.Join(optValues, ",") } + return nil } @@ -54,6 +66,7 @@ func (cmdLineSource SimpleCommandLinePropertySource) ContainsProperty(name strin if NonOptionArgsPropertyName == name { return len(cmdLineSource.GetNonOptionArgs()) != 0 } + return cmdLineSource.ContainsOption(name) } @@ -72,3 +85,52 @@ func (cmdLineSource SimpleCommandLinePropertySource) GetNonOptionArgs() []string func (cmdLineSource SimpleCommandLinePropertySource) GetPropertyNames() []string { return cmdLineSource.source.getOptionNames() } + +type SimpleCommandLineArgsParser struct { +} + +func NewCommandLineArgsParser() SimpleCommandLineArgsParser { + return SimpleCommandLineArgsParser{} +} + +func (parser SimpleCommandLineArgsParser) Parse(args []string) (CommandLineArgs, error) { + cmdLineArgs := NewCommandLineArgs() + appArgumentFlagSet := flag.NewFlagSet("ProcyonApplicationArguments", flag.ContinueOnError) + + err := appArgumentFlagSet.Parse(args) + + if err != nil { + return cmdLineArgs, err + } + + for _, arg := range appArgumentFlagSet.Args() { + + if strings.HasPrefix(arg, "--") { + optionText := arg[2:] + indexOfEqualSign := strings.Index(optionText, "=") + optionName := "" + optionValue := "" + + if indexOfEqualSign > -1 { + optionName = optionText[0:indexOfEqualSign] + optionValue = optionText[indexOfEqualSign+1:] + } else { + optionName = optionText + } + + optionName = strings.TrimSpace(optionName) + optionValue = strings.TrimSpace(optionValue) + + if optionName == "" { + return cmdLineArgs, errors.New("Invalid argument syntax : " + arg) + } + + cmdLineArgs.addOptionArgs(optionName, optionValue) + } else { + cmdLineArgs.addNonOptionArgs(arg) + } + + } + + return cmdLineArgs, nil +} diff --git a/cmdline_test.go b/cmdline_test.go index 2ce1f46..d96fb47 100644 --- a/cmdline_test.go +++ b/cmdline_test.go @@ -2,6 +2,7 @@ package core import ( "github.com/stretchr/testify/assert" + "os" "testing" ) @@ -55,3 +56,37 @@ func TestSimpleCommandLinePropertySource_GetSource(t *testing.T) { commandLinePropertySource := NewSimpleCommandLinePropertySource(getTestApplicationArguments()) assert.NotNil(t, commandLinePropertySource.GetSource()) } + +func getTestApplicationArguments() []string { + var args = make([]string, 0) + args = append(args, os.Args...) + args = append(args, "--procyon.application.name=\"Test Application\"") + args = append(args, "--procyon.server.port=8080") + args = append(args, "--procyon.server.port=8090") + args = append(args, "-debug") + return args +} + +func TestSimpleCommandLineArgsParser_Parse(t *testing.T) { + commandLineParser := NewCommandLineArgsParser() + + args, err := commandLineParser.Parse(getTestApplicationArguments()) + + assert.Nil(t, err) + assert.NotNil(t, args) + + assert.Equal(t, 2, len(args.optionArgs)) + + assert.Equal(t, 1, len(args.getOptionValues("procyon.application.name"))) + assert.Equal(t, 2, len(args.getOptionValues("procyon.server.port"))) + + assert.Contains(t, args.getOptionNames(), "procyon.application.name") + assert.Contains(t, args.getOptionNames(), "procyon.server.port") + + assert.True(t, args.containsOption("procyon.application.name")) + assert.True(t, args.containsOption("procyon.server.port")) + + assert.Contains(t, args.getOptionValues("procyon.application.name"), "\"Test Application\"") + assert.Contains(t, args.getOptionValues("procyon.server.port"), "8080") + assert.Contains(t, args.getOptionValues("procyon.server.port"), "8090") +} diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..7d122f2 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,4 @@ +coverage: + precision: 2 + round: down + range: 70...100 diff --git a/component.go b/component.go index 7ced923..767a60b 100644 --- a/component.go +++ b/component.go @@ -20,18 +20,23 @@ var ( func Register(components ...Component) { for _, component := range components { typ := goo.GetType(component) + if isSupportComponent(typ) { + fun := typ.ToFunctionType() retType := fun.GetFunctionReturnTypes()[0].ToStructType() compressorType := goo.GetType((*ComponentProcessor)(nil)).ToInterfaceType() + if retType.Implements(compressorType) { registerComponentProcessor(retType.GetFullName(), typ) } else { registerComponentType(retType.GetFullName(), typ) } + } else { panic("It supports only constructor functions") } + } } @@ -39,6 +44,7 @@ func registerComponentType(name string, typ goo.Type) { if _, ok := componentTypes[name]; ok { panic("You have already registered the same component : " + name) } + componentTypes[name] = typ } @@ -46,21 +52,26 @@ func registerComponentProcessor(name string, typ goo.Type) { if _, ok := componentProcessor[name]; ok { panic("You have already registered the same component processor : " + name) } + componentProcessor[name] = typ } func isSupportComponent(typ goo.Type) bool { if typ.IsFunction() { fun := typ.ToFunctionType() + if fun.GetFunctionReturnTypeCount() != 1 { panic("Constructor functions are only supported, that why's your function must have only one return type") } retType := fun.GetFunctionReturnTypes()[0] + if !retType.IsStruct() { panic("Constructor functions must only return struct instances : " + retType.GetPackageFullName()) } + return true } + return false } @@ -72,25 +83,33 @@ func GetComponentTypesWithParam(requestedType goo.Type, paramTypes []goo.Type) ( if requestedType == nil { return nil, errors.New("type must not be null") } + if !requestedType.IsStruct() && !requestedType.IsInterface() { panic("Requested type must be only interface or struct") } + result := make([]goo.Type, 0) + for _, componentType := range componentTypes { fun := componentType.ToFunctionType() returnType := fun.GetFunctionReturnTypes()[0].ToStructType() match := false + if requestedType.IsInterface() && returnType.Implements(requestedType.ToInterfaceType()) { match = true } else if requestedType.IsStruct() { + if requestedType.GetGoType() == returnType.GetGoType() || requestedType.ToStructType().EmbeddedStruct(returnType) { match = true } + } + if match && hasFunctionSameParametersWithGivenParameters(componentType, paramTypes) { result = append(result, componentType) } } + return result, nil } @@ -98,10 +117,13 @@ func ForEachComponentType(callback func(string, goo.Type) error) (err error) { for componentName := range componentTypes { component := componentTypes[componentName] err = callback(componentName, component) + if err != nil { break } + } + return nil } @@ -109,9 +131,36 @@ func ForEachComponentProcessor(callback func(string, goo.Type) error) (err error for componentProcessorName := range componentProcessor { componentProcessor := componentProcessor[componentProcessorName] err = callback(componentProcessorName, componentProcessor) + if err != nil { break } + } + return } + +func hasFunctionSameParametersWithGivenParameters(componentType goo.Type, parameterTypes []goo.Type) bool { + if !componentType.IsFunction() { + panic("Component type must be function") + } + + fun := componentType.ToFunctionType() + functionParameterCount := fun.GetFunctionParameterCount() + + if parameterTypes == nil && functionParameterCount == 0 { + return true + } else if len(parameterTypes) != functionParameterCount || parameterTypes == nil && functionParameterCount != 0 { + return false + } + + inputParameterTypes := fun.GetFunctionParameterTypes() + for index, inputParameterType := range inputParameterTypes { + if !inputParameterType.Equals(parameterTypes[index]) { + return false + } + } + + return true +} diff --git a/conversion.go b/conversion.go index 6d64631..24e6e5b 100644 --- a/conversion.go +++ b/conversion.go @@ -151,6 +151,7 @@ func (cs *DefaultTypeConverterService) Convert(source interface{}, sourceTyp goo } } cs.mu.Unlock() + if typConverter != nil { result, err = typConverter.Convert(source, sourceTyp, targetTyp) } @@ -161,6 +162,7 @@ func (cs *DefaultTypeConverterService) RegisterConverter(converter TypeConverter if converter == nil { panic("converter must not be nil") } + cs.mu.Lock() cs.converters[goo.GetType(converter).GetFullName()] = converter cs.mu.Unlock() diff --git a/environment.go b/environment.go index 1169ab5..1ee0745 100644 --- a/environment.go +++ b/environment.go @@ -27,6 +27,7 @@ func NewStandardEnvironment() StandardEnvironment { propertySources: NewPropertySources(), converterService: NewDefaultTypeConverterService(), } + env.propertyResolver = NewSimplePropertyResolver(env.propertySources) return env } @@ -63,12 +64,15 @@ func NewSystemEnvironmentPropertySource() SystemEnvironmentPropertySource { } environmentProperties := os.Environ() + for _, property := range environmentProperties { index := strings.Index(property, "=") + if index != -1 { propertySource.environmentProperties[property[:index]] = property[index+1:] } } + return propertySource } @@ -82,14 +86,17 @@ func (propertySource SystemEnvironmentPropertySource) GetSource() interface{} { func (propertySource SystemEnvironmentPropertySource) GetProperty(name string) interface{} { actualPropertyName := propertySource.checkPropertyName(strings.ToLower(name)) + if actualPropertyName != nil { return propertySource.environmentProperties[actualPropertyName.(string)] } actualPropertyName = propertySource.checkPropertyName(strings.ToUpper(name)) + if actualPropertyName != nil { return propertySource.environmentProperties[actualPropertyName.(string)] } + return nil } @@ -99,9 +106,11 @@ func (propertySource SystemEnvironmentPropertySource) ContainsProperty(name stri func (propertySource SystemEnvironmentPropertySource) GetPropertyNames() []string { keys := make([]string, 0, len(propertySource.environmentProperties)) + for key, _ := range propertySource.environmentProperties { keys = append(keys, key) } + return keys } @@ -109,6 +118,7 @@ func (propertySource SystemEnvironmentPropertySource) checkIfPresent(propertyNam if _, ok := propertySource.environmentProperties[propertyName]; ok { return true } + return false } @@ -118,16 +128,19 @@ func (propertySource SystemEnvironmentPropertySource) checkPropertyName(property } noHyphenPropertyName := strings.ReplaceAll(propertyName, "-", "_") + if propertyName != noHyphenPropertyName && propertySource.checkIfPresent(noHyphenPropertyName) { return noHyphenPropertyName } noDotPropertyName := strings.ReplaceAll(propertyName, ".", "_") + if propertyName != noDotPropertyName && propertySource.checkIfPresent(noDotPropertyName) { return noDotPropertyName } noHyphenAndNoDotName := strings.ReplaceAll(noDotPropertyName, "-", "_") + if noDotPropertyName != noHyphenAndNoDotName && propertySource.checkIfPresent(noHyphenAndNoDotName) { return noHyphenAndNoDotName } diff --git a/go.mod b/go.mod index fcc0902..0242782 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,21 @@ module github.com/procyon-projects/procyon-core go 1.13 require ( + github.com/andybalholm/brotli v1.0.3 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/google/uuid v1.2.0 // indirect + github.com/json-iterator/go v1.1.11 // indirect + github.com/klauspost/compress v1.13.0 // indirect + github.com/mattn/go-isatty v0.0.13 // indirect github.com/procyon-projects/goo v1.0.4 + github.com/procyon-projects/procyon v0.0.0-20210603213002-0f2f7bfaa990 // indirect + github.com/procyon-projects/procyon-configure v0.0.0-20210122231728-2eefc549f2b2 // indirect + github.com/procyon-projects/procyon-context v0.0.0-20210131113519-986cb7898bbf // indirect + github.com/procyon-projects/procyon-web v0.0.0-20210209213658-0a5dc6376778 // indirect + github.com/sirupsen/logrus v1.8.1 // indirect + github.com/stretchr/objx v0.3.0 // indirect github.com/stretchr/testify v1.6.1 + github.com/valyala/fasthttp v1.26.0 // indirect + golang.org/x/sys v0.0.0-20210603125802-9665404d3644 // indirect + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b ) diff --git a/go.test.sh b/go.test.sh deleted file mode 100644 index e255303..0000000 --- a/go.test.sh +++ /dev/null @@ -1,12 +0,0 @@ - #!/usr/bin/env bash - -set -e -echo "" > coverage.txt - -for d in $(go list ./... | grep -v vendor); do - go test -race -coverprofile=profile.out -covermode=atomic "$d" - if [ -f profile.out ]; then - cat profile.out >> coverage.txt - rm profile.out - fi -done \ No newline at end of file diff --git a/parser.go b/parser.go deleted file mode 100644 index 5d76868..0000000 --- a/parser.go +++ /dev/null @@ -1,49 +0,0 @@ -package core - -import ( - "errors" - "flag" - "strings" -) - -type SimpleCommandLineArgsParser struct { -} - -func NewCommandLineArgsParser() SimpleCommandLineArgsParser { - return SimpleCommandLineArgsParser{} -} - -func (parser SimpleCommandLineArgsParser) Parse(args []string) (CommandLineArgs, error) { - cmdLineArgs := NewCommandLineArgs() - - appArgumentFlagSet := flag.NewFlagSet("ProcyonApplicationArguments", flag.ContinueOnError) - err := appArgumentFlagSet.Parse(args) - if err != nil { - return cmdLineArgs, err - } - - for _, arg := range appArgumentFlagSet.Args() { - if strings.HasPrefix(arg, "--") { - optionText := arg[2:] - indexOfEqualSign := strings.Index(optionText, "=") - optionName := "" - optionValue := "" - if indexOfEqualSign > -1 { - optionName = optionText[0:indexOfEqualSign] - optionValue = optionText[indexOfEqualSign+1:] - } else { - optionName = optionText - } - optionName = strings.TrimSpace(optionName) - optionValue = strings.TrimSpace(optionValue) - if optionName == "" { - return cmdLineArgs, errors.New("Invalid argument syntax : " + arg) - } - cmdLineArgs.addOptionArgs(optionName, optionValue) - } else { - cmdLineArgs.addNonOptionArgs(arg) - } - } - - return cmdLineArgs, nil -} diff --git a/parser_test.go b/parser_test.go deleted file mode 100644 index 285bb3c..0000000 --- a/parser_test.go +++ /dev/null @@ -1,41 +0,0 @@ -package core - -import ( - "github.com/stretchr/testify/assert" - "os" - "testing" -) - -func getTestApplicationArguments() []string { - var args = make([]string, 0) - args = append(args, os.Args...) - args = append(args, "--procyon.application.name=\"Test Application\"") - args = append(args, "--procyon.server.port=8080") - args = append(args, "--procyon.server.port=8090") - args = append(args, "-debug") - return args -} - -func TestSimpleCommandLineArgsParser_Parse(t *testing.T) { - commandLineParser := NewCommandLineArgsParser() - - args, err := commandLineParser.Parse(getTestApplicationArguments()) - - assert.Nil(t, err) - assert.NotNil(t, args) - - assert.Equal(t, 2, len(args.optionArgs)) - - assert.Equal(t, 1, len(args.getOptionValues("procyon.application.name"))) - assert.Equal(t, 2, len(args.getOptionValues("procyon.server.port"))) - - assert.Contains(t, args.getOptionNames(), "procyon.application.name") - assert.Contains(t, args.getOptionNames(), "procyon.server.port") - - assert.True(t, args.containsOption("procyon.application.name")) - assert.True(t, args.containsOption("procyon.server.port")) - - assert.Contains(t, args.getOptionValues("procyon.application.name"), "\"Test Application\"") - assert.Contains(t, args.getOptionValues("procyon.server.port"), "8080") - assert.Contains(t, args.getOptionValues("procyon.server.port"), "8090") -} diff --git a/property.go b/property.go index e9ec917..0f08daf 100644 --- a/property.go +++ b/property.go @@ -20,9 +20,11 @@ func NewPropertySources() *PropertySources { func (o *PropertySources) Get(name string) (PropertySource, bool) { for _, source := range o.sources { + if source != nil && source.GetName() == name { return source, true } + } return nil, false } @@ -34,14 +36,17 @@ func (o *PropertySources) Add(propertySource PropertySource) { func (o *PropertySources) Remove(name string) PropertySource { source, index := o.findPropertySourceByName(name) + if index != -1 { o.sources = append(o.sources[:index], o.sources[index+1:]...) } + return source } func (o *PropertySources) Replace(name string, propertySource PropertySource) { _, index := o.findPropertySourceByName(name) + if index != -1 { o.sources[index] = propertySource } @@ -51,7 +56,9 @@ func (o *PropertySources) RemoveIfPresent(propertySource PropertySource) { if propertySource == nil { return } + _, index := o.findPropertySourceByName(propertySource.GetName()) + if index != -1 { o.sources = append(o.sources[:index], o.sources[index+1:]...) } @@ -59,10 +66,13 @@ func (o *PropertySources) RemoveIfPresent(propertySource PropertySource) { func (o *PropertySources) findPropertySourceByName(name string) (PropertySource, int) { for index, source := range o.sources { + if source.GetName() == name { return source, index } + } + return nil, -1 } diff --git a/resolver.go b/resolver.go index 10f838b..5716658 100644 --- a/resolver.go +++ b/resolver.go @@ -17,18 +17,24 @@ func NewSimplePropertyResolver(sources *PropertySources) *SimplePropertyResolver func (resolver *SimplePropertyResolver) ContainsProperty(name string) bool { for _, propertySource := range resolver.sources.GetPropertyResources() { + if propertySource.ContainsProperty(name) { return true } + } + return false } func (resolver *SimplePropertyResolver) GetProperty(name string, defaultValue string) interface{} { for _, propertySource := range resolver.sources.GetPropertyResources() { + if propertySource.ContainsProperty(name) { - return propertySource.GetProperty(name).(string) + return propertySource.GetProperty(name) } + } + return defaultValue } diff --git a/test-resources/procyon.db.yaml b/test-resources/procyon.db.yaml new file mode 100644 index 0000000..f0dc664 --- /dev/null +++ b/test-resources/procyon.db.yaml @@ -0,0 +1,5 @@ +procyon: + datasource: + url: test-url + username: test-username + password: test-password diff --git a/test-resources/procyon.dev.yaml b/test-resources/procyon.dev.yaml new file mode 100644 index 0000000..e1bcc07 --- /dev/null +++ b/test-resources/procyon.dev.yaml @@ -0,0 +1,9 @@ +procyon: + application: + name: "Procyon Dev Application" + +server: + port: 8097 + +logging: + level: INFO \ No newline at end of file diff --git a/test-resources/procyon.prod.yaml b/test-resources/procyon.prod.yaml new file mode 100644 index 0000000..065c69b --- /dev/null +++ b/test-resources/procyon.prod.yaml @@ -0,0 +1,9 @@ +procyon: + application: + name: "Procyon Prod Application" + +server: + port: 8080 + +logging: + level: DEBUG \ No newline at end of file diff --git a/test-resources/procyon.test.yaml b/test-resources/procyon.test.yaml new file mode 100644 index 0000000..f31965f --- /dev/null +++ b/test-resources/procyon.test.yaml @@ -0,0 +1,9 @@ +procyon: + application: + name: "Procyon Test Application" + +server: + port: 8095 + +logging: + level: INFO \ No newline at end of file diff --git a/test-resources/procyon.yaml b/test-resources/procyon.yaml new file mode 100644 index 0000000..961ae9b --- /dev/null +++ b/test-resources/procyon.yaml @@ -0,0 +1,9 @@ +procyon: + application: + name: "Procyon Application" + +server: + port: 8091 + +logging: + level: DEBUG \ No newline at end of file diff --git a/util.go b/util.go index 7d9fb18..5ae2c99 100644 --- a/util.go +++ b/util.go @@ -2,7 +2,7 @@ package core import ( "errors" - "github.com/procyon-projects/goo" + "strconv" "time" "unsafe" ) @@ -30,6 +30,7 @@ func (watch *TaskWatch) Start() error { if watch.isRunning { return errors.New("TaskWatch is already running") } + watch.startTime = time.Now() watch.isRunning = true return nil @@ -39,6 +40,7 @@ func (watch *TaskWatch) Stop() error { if !watch.isRunning { return errors.New("TaskWatch is not running") } + watch.isRunning = false watch.totalTime = time.Since(watch.startTime) watch.taskName = "" @@ -53,26 +55,29 @@ func (watch *TaskWatch) GetTotalTime() int64 { return watch.totalTime.Nanoseconds() } -func hasFunctionSameParametersWithGivenParameters(componentType goo.Type, parameterTypes []goo.Type) bool { - if !componentType.IsFunction() { - panic("Component type must be function") - } - fun := componentType.ToFunctionType() - functionParameterCount := fun.GetFunctionParameterCount() - if parameterTypes == nil && functionParameterCount == 0 { - return true - } else if len(parameterTypes) != functionParameterCount || parameterTypes == nil && functionParameterCount != 0 { - return false - } - inputParameterTypes := fun.GetFunctionParameterTypes() - for index, inputParameterType := range inputParameterTypes { - if !inputParameterType.Equals(parameterTypes[index]) { - return false +func BytesToStr(bytes []byte) string { + return *(*string)(unsafe.Pointer(&bytes)) +} + +func FlatMap(m map[string]interface{}) map[string]interface{} { + flattenMap := map[string]interface{}{} + + for key, value := range m { + switch child := value.(type) { + case map[string]interface{}: + nm := FlatMap(child) + + for nk, nv := range nm { + flattenMap[key+"."+nk] = nv + } + case []interface{}: + for i := 0; i < len(child); i++ { + flattenMap[key+"."+strconv.Itoa(i)] = child[i] + } + default: + flattenMap[key] = value } } - return true -} -func BytesToStr(bytes []byte) string { - return *(*string)(unsafe.Pointer(&bytes)) + return flattenMap } diff --git a/uuid.go b/uuid.go index 02cbb84..00003f5 100644 --- a/uuid.go +++ b/uuid.go @@ -31,6 +31,7 @@ var ( func init() { iname, addr := getHardwareInterface() + if iname != "" && addr != nil { copy(nodeID[:], addr) } else { @@ -40,12 +41,15 @@ func init() { if clockSeq == 0 { var b [2]byte randomBits(b[:]) + seq := int(b[0])<<8 | int(b[1]) oldSeq := clockSeq clockSeq = uint16(seq&0x3fff) | 0x8000 + if oldSeq != clockSeq { lastTime = 0 } + } } @@ -54,10 +58,12 @@ const hexTable = "0123456789abcdef" func GenerateUUID(uuidBuffer []byte) { timeMu.Lock() now := uint64(time.Now().UnixNano()/100) + g1582ns100 + if now <= lastTime { clockSeq = ((clockSeq + 1) & 0x3fff) | 0x8000 } + lastTime = now timeMu.Unlock() @@ -138,13 +144,17 @@ func GenerateUUID(uuidBuffer []byte) { func getHardwareInterface() (string, []byte) { interfaces, err := net.Interfaces() + if err != nil { return "", nil } + for _, ifs := range interfaces { + if len(ifs.HardwareAddr) >= 6 { return ifs.Name, ifs.HardwareAddr } + } return "", nil }