Skip to content

Commit 3a7956f

Browse files
author
Frank Martinez
committed
fix support for external property resolution
1 parent 7796f74 commit 3a7956f

9 files changed

Lines changed: 274 additions & 117 deletions

File tree

app/app.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,9 @@ func ContinueOnError(a *App) error {
8383
return nil
8484
}
8585

86-
func ExternalProperties(providerId string, overrides string, processors ...property.PostProcessor) func(*App) error {
86+
func FinalizeProperties(useExternalResolvers bool, processors ...property.PostProcessor) func(*App) error {
8787
return func(a *App) error {
88-
return a.propManager.AddExternalProperties(providerId, overrides, processors...)
88+
return a.propManager.Finalize(useExternalResolvers, processors...)
8989
}
9090
}
9191

app/propertyresolver/env.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package propertyresolver
2+
3+
import (
4+
"encoding/json"
5+
"github.com/project-flogo/core/data/property"
6+
"os"
7+
"strings"
8+
9+
"github.com/project-flogo/core/support/log"
10+
)
11+
12+
13+
const EnvAppPropertyEnvConfigKey = "FLOGO_APP_PROPS_ENV"
14+
15+
type PropertyMappings struct {
16+
Mappings map[string]string `json:"mappings"`
17+
}
18+
19+
var mapping PropertyMappings
20+
21+
func init() {
22+
23+
logger := log.RootLogger()
24+
25+
property.RegisterExternalResolver("env", &EnvVariableValueResolver{})
26+
27+
mappings := getEnvValue()
28+
if mappings != "" {
29+
e := json.Unmarshal([]byte(mappings), &mapping)
30+
if e != nil {
31+
logger.Errorf("Can not parse value set to '%s' due to error - '%v'", EnvAppPropertyEnvConfigKey, e)
32+
panic("")
33+
}
34+
}
35+
}
36+
37+
func getEnvValue() string {
38+
key := os.Getenv(EnvAppPropertyEnvConfigKey)
39+
if len(key) > 0 {
40+
return key
41+
}
42+
return ""
43+
}
44+
45+
// Resolve property value from environment variable
46+
type EnvVariableValueResolver struct {
47+
}
48+
49+
func (resolver *EnvVariableValueResolver) LookupValue(key string) (interface{}, bool) {
50+
value, exists := os.LookupEnv(key) // first try with the name of the property as is
51+
if exists {
52+
return value, exists
53+
}
54+
55+
// Lookup based on mapping defined
56+
keyMapping, ok := mapping.Mappings[key]
57+
if ok {
58+
return os.LookupEnv(keyMapping)
59+
}
60+
61+
// Replace dot with underscore e.g. a.b would be a_b
62+
key = strings.Replace(key, ".", "_", -1)
63+
value, exists = os.LookupEnv(key)
64+
if exists {
65+
return value, exists
66+
}
67+
68+
69+
// Try upper case form e.g. a.b would be A_B
70+
key = strings.ToUpper(key)
71+
value, exists = os.LookupEnv(key) // if not found try with the canonical form
72+
return value, exists
73+
}

app/propertyresolver/env_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package propertyresolver
2+
3+
import (
4+
"os"
5+
"testing"
6+
7+
"github.com/stretchr/testify/assert"
8+
)
9+
10+
func TestEnvValueResolver(t *testing.T) {
11+
os.Setenv("Test", "Test")
12+
os.Setenv("TEST_PROP", "test.Prop")
13+
14+
defer func() {
15+
os.Unsetenv("Test")
16+
os.Unsetenv("TEST_PROP")
17+
}()
18+
resolver := &EnvVariableValueResolver{}
19+
20+
resolvedVal, found := resolver.LookupValue("Test")
21+
assert.True(t, true, found)
22+
assert.Equal(t, "Test", resolvedVal)
23+
24+
resolvedVal, found = resolver.LookupValue("test.Prop")
25+
assert.True(t, true, found)
26+
assert.Equal(t, "test.Prop", resolvedVal)
27+
28+
}

app/propertyresolver/json.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package propertyresolver
2+
3+
import (
4+
"encoding/json"
5+
"github.com/project-flogo/core/data/property"
6+
"io/ioutil"
7+
"os"
8+
"strings"
9+
10+
"github.com/project-flogo/core/support/log"
11+
)
12+
13+
var preload = make(map[string]interface{})
14+
15+
//var log = logger.GetLogger("app-props-json-resolver")
16+
17+
// Comma separated list of json files overriding default application property values
18+
// e.g. FLOGO_APP_PROPS_JSON=app1.json,common.json
19+
const EnvAppPropertyFileConfigKey = "FLOGO_APP_PROPS_JSON"
20+
21+
func init() {
22+
23+
logger := log.RootLogger()
24+
25+
filePaths := getExternalFiles()
26+
if filePaths != "" {
27+
// Register value resolver
28+
property.RegisterExternalResolver("json", &JSONFileValueResolver{})
29+
30+
// preload props from files
31+
files := strings.Split(filePaths, ",")
32+
if len(files) > 0 {
33+
for _, filePath := range files {
34+
props := make(map[string]interface{})
35+
36+
file, e := ioutil.ReadFile(filePath)
37+
if e != nil {
38+
logger.Errorf("Can not read - %s due to error - %v", filePath, e)
39+
panic("")
40+
}
41+
e = json.Unmarshal(file, &props)
42+
if e != nil {
43+
logger.Errorf("Can not read - %s due to error - %v", filePath, e)
44+
panic("")
45+
}
46+
}
47+
}
48+
}
49+
}
50+
51+
func getExternalFiles() string {
52+
key := os.Getenv(EnvAppPropertyFileConfigKey)
53+
if len(key) > 0 {
54+
return key
55+
}
56+
return ""
57+
}
58+
59+
// Resolve property value from external files
60+
type JSONFileValueResolver struct {
61+
}
62+
63+
func (resolver *JSONFileValueResolver) LookupValue(toResolve string) (interface{}, bool) {
64+
val, found := preload[toResolve]
65+
return val, found
66+
}

data/property/manager.go

Lines changed: 14 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
package property
22

33
import (
4-
"encoding/json"
54
"github.com/project-flogo/core/support/log"
6-
"io/ioutil"
7-
"strings"
85
)
96

107
func init() {
@@ -36,69 +33,27 @@ func (m *Manager) GetProperty(name string) (interface{}, bool) {
3633
return val, exists
3734
}
3835

39-
func (m *Manager) AddExternalProperties(providerId string, overrides string, processors ...PostProcessor) error {
36+
func (m *Manager) Finalize(useExternalResolvers bool, processors ...PostProcessor) error {
4037

41-
provider := GetProvider(providerId)
42-
newProps, err := loadExternalProperties(provider, overrides)
43-
if err != nil {
44-
return err
45-
}
46-
47-
for _, processor := range processors {
48-
processor(newProps)
49-
}
50-
51-
for key, value := range newProps {
52-
m.properties[key] = value
53-
}
54-
55-
return nil
56-
}
57-
58-
type PostProcessor func(properties map[string]interface{}) error
38+
logger := log.RootLogger()
5939

60-
func loadExternalProperties(provider Provider, overrides string) (map[string]interface{}, error) {
40+
if useExternalResolvers {
41+
for name := range m.properties {
42+
newVal, found := ResolveExternally(name)
6143

62-
props := make(map[string]interface{})
63-
64-
if overrides != "" {
65-
if strings.HasSuffix(overrides, ".json") {
66-
// Override through file
67-
68-
propFile := overrides
69-
file, e := ioutil.ReadFile(propFile)
70-
if e != nil {
71-
return nil, e
72-
}
73-
e = json.Unmarshal(file, &props)
74-
75-
if e != nil {
76-
return nil, e
77-
}
78-
} else if strings.ContainsRune(overrides, '=') {
79-
// Override through P1=V1,P2=V2
80-
for _, pair := range strings.Split(overrides, ",") {
81-
kv := strings.Split(pair, "=")
82-
if len(kv) == 2 && kv[0] != "" {
83-
key := strings.TrimSpace(kv[0])
84-
value := strings.TrimSpace(kv[1])
85-
props[key] = value
86-
} else {
87-
log.RootLogger().Warnf("'%s' is not valid override value. It must be in PropName=PropValue format.", pair)
88-
}
44+
if !found {
45+
logger.Warnf("Property '%s' could not be resolved using external resolver(s) '%s'. Using default value.", name)
46+
} else {
47+
m.properties[name] = newVal
8948
}
9049
}
9150
}
9251

93-
//look for properties that need to go to the provider
94-
if provider != nil {
95-
for key, value := range props {
96-
if strVal, ok := value.(string); ok && strVal[0] == '$' {
97-
val := provider.GetProperty(strVal[1:])
98-
props[key] = val
99-
}
100-
}
52+
for _, processor := range processors {
53+
processor(m.properties)
10154
}
10255

103-
return props, nil
56+
return nil
10457
}
58+
59+
type PostProcessor func(properties map[string]interface{}) error

data/property/provider.go

Lines changed: 0 additions & 39 deletions
This file was deleted.

data/property/resolver.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package property
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"strings"
7+
8+
"github.com/project-flogo/core/support/log"
9+
)
10+
11+
var (
12+
externalResolverMap = make(map[string]ExternalResolver)
13+
externalResolvers []ExternalResolver
14+
)
15+
16+
// Resolver used to resolve property value from external configuration like env, file etc
17+
type ExternalResolver interface {
18+
// Should return value and true if the given key exists in the external configuration otherwise should return nil and false.
19+
LookupValue(key string) (interface{}, bool)
20+
}
21+
22+
func RegisterExternalResolver(resolverType string, resolver ExternalResolver) error {
23+
24+
logger := log.RootLogger()
25+
26+
27+
if resolverType == "" {
28+
return fmt.Errorf("'resolverType' must be specified when registering external property resolver")
29+
}
30+
31+
if resolver == nil {
32+
return fmt.Errorf("cannot register 'nil' external property resolver")
33+
}
34+
35+
if _, dup := externalResolverMap[resolverType]; dup {
36+
return fmt.Errorf("external property resolver already registered: %s", resolverType)
37+
}
38+
39+
logger.Debugf("Registering external property resolver [ %s ]", resolverType)
40+
41+
externalResolverMap[resolverType] = resolver
42+
43+
return nil
44+
}
45+
46+
func GetExternalResolver(resolverType string) ExternalResolver {
47+
return externalResolverMap[resolverType]
48+
}
49+
50+
func EnableExternalResolvers(resolverTypes string) error {
51+
52+
for _, resolverType := range strings.Split(resolverTypes, ",") {
53+
resolver := externalResolverMap[resolverType]
54+
if resolver == nil {
55+
errMag := fmt.Sprintf("Unsupported external property resolver type - %s. Resolver not registered.", resolverType)
56+
return errors.New(errMag)
57+
}
58+
externalResolvers = append(externalResolvers, resolver)
59+
}
60+
61+
return nil
62+
}
63+
64+
func ResolveExternally(propertyName string) (interface{}, bool) {
65+
66+
for _, resolver := range externalResolvers {
67+
// Use resolver
68+
value, resolved := resolver.LookupValue(propertyName)
69+
if resolved {
70+
return value, true
71+
}
72+
}
73+
74+
return nil, false
75+
}

0 commit comments

Comments
 (0)