Merge pull request #2014 from ZLBer/resolve_placeholder

feat: resolve placeholder
diff --git a/common/constant/file/suffix.go b/common/constant/file/suffix.go
index bd8876c..fe1b41e 100644
--- a/common/constant/file/suffix.go
+++ b/common/constant/file/suffix.go
@@ -26,3 +26,8 @@
 	YML        = Suffix("yml")
 	PROPERTIES = Suffix("properties")
 )
+
+const (
+	PlaceholderPrefix = "${"
+	PlaceholderSuffix = "}"
+)
diff --git a/config/config_resolver.go b/config/config_resolver.go
index 0e4eefd..517a419 100644
--- a/config/config_resolver.go
+++ b/config/config_resolver.go
@@ -18,10 +18,17 @@
 package config
 
 import (
+	"strings"
+)
+
+import (
+	log "github.com/dubbogo/gost/log/logger"
+
 	"github.com/knadh/koanf"
 	"github.com/knadh/koanf/parsers/json"
 	"github.com/knadh/koanf/parsers/toml"
 	"github.com/knadh/koanf/parsers/yaml"
+	"github.com/knadh/koanf/providers/confmap"
 	"github.com/knadh/koanf/providers/rawbytes"
 
 	"github.com/pkg/errors"
@@ -66,5 +73,46 @@
 	if err != nil {
 		panic(err)
 	}
-	return k
+	return resolvePlaceholder(k)
+}
+
+// resolvePlaceholder replace ${xx} with real value
+func resolvePlaceholder(resolver *koanf.Koanf) *koanf.Koanf {
+	m := make(map[string]interface{})
+	for k, v := range resolver.All() {
+		s, ok := v.(string)
+		if !ok {
+			continue
+		}
+		newKey, defaultValue := checkPlaceholder(s)
+		if newKey == "" {
+			continue
+		}
+		m[k] = resolver.Get(newKey)
+		if m[k] == nil {
+			m[k] = defaultValue
+		}
+	}
+	err := resolver.Load(confmap.Provider(m, resolver.Delim()), nil)
+	if err != nil {
+		log.Errorf("resolvePlaceholder error %s", err)
+	}
+	return resolver
+}
+
+func checkPlaceholder(s string) (newKey, defaultValue string) {
+	s = strings.TrimSpace(s)
+	if !strings.HasPrefix(s, file.PlaceholderPrefix) || !strings.HasSuffix(s, file.PlaceholderSuffix) {
+		return
+	}
+	s = s[len(file.PlaceholderPrefix) : len(s)-len(file.PlaceholderSuffix)]
+	indexColon := strings.Index(s, ":")
+	if indexColon == -1 {
+		newKey = strings.TrimSpace(s)
+		return
+	}
+	newKey = strings.TrimSpace(s[0:indexColon])
+	defaultValue = strings.TrimSpace(s[indexColon+1:])
+
+	return
 }
diff --git a/config/config_resolver_test.go b/config/config_resolver_test.go
new file mode 100644
index 0000000..4495447
--- /dev/null
+++ b/config/config_resolver_test.go
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package config
+
+import (
+	"testing"
+)
+
+import (
+	"github.com/knadh/koanf"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestResolvePlaceHolder(t *testing.T) {
+	t.Run("test resolver", func(t *testing.T) {
+		conf := NewLoaderConf(WithPath("./testdata/config/resolver/application.yaml"))
+		koan := GetConfigResolver(conf)
+		assert.Equal(t, koan.Get("dubbo.config-center.address"), koan.Get("dubbo.registries.nacos.address"))
+		assert.Equal(t, koan.Get("localhost"), koan.Get("dubbo.protocols.dubbo.ip"))
+		assert.Equal(t, "", koan.Get("dubbo.registries.nacos.group"))
+		assert.Equal(t, "dev", koan.Get("dubbo.registries.zk.group"))
+
+		rc := NewRootConfigBuilder().Build()
+		err := koan.UnmarshalWithConf(rc.Prefix(), rc, koanf.UnmarshalConf{Tag: "yaml"})
+		assert.Nil(t, err)
+		assert.Equal(t, rc.ConfigCenter.Address, rc.Registries["nacos"].Address)
+		//not exist, default
+		assert.Equal(t, "", rc.Registries["nacos"].Group)
+		assert.Equal(t, "dev", rc.Registries["zk"].Group)
+
+	})
+}
diff --git a/config/testdata/config/resolver/application.yaml b/config/testdata/config/resolver/application.yaml
new file mode 100644
index 0000000..92b157e
--- /dev/null
+++ b/config/testdata/config/resolver/application.yaml
@@ -0,0 +1,36 @@
+localhost: 127.0.0.1
+dubbo:
+  application:
+    name: dubbo-go
+    module: local
+    version: 1.0.0
+    owner: zhaoyunxing
+  config-center:
+    address: nacos://127.0.0.1:8848
+    cluster: dev
+    namespace: dubbo
+    log-dir: ./logs
+  protocols:
+    dubbo:
+      name: dubbo
+      ip: ${localhost}
+      port: 20000
+  registries:
+    nacos:
+      timeout: 5s
+      group: ${notexist}
+      address: ${dubbo.config-center.address:nacos://127.0.0.1:8848}
+    zk:
+      protocol: zookeeper
+      group: ${notexist:dev}
+      address: 127.0.0.1:2181
+  services:
+    helloService:
+      interface: org.dubbo.service.HelloService
+      registry-ids: nacos,zk
+    orderService:
+      interface: org.dubbo.service.OrderService
+      registry-ids: nacos
+  provider:
+    register: true
+    services:
\ No newline at end of file