Skip to content

Commit b1fe325

Browse files
committed
Merge branch 'master' into support-request-custom-field-not-set-err
2 parents 32cb9d5 + b8206fb commit b1fe325

File tree

100 files changed

+2573
-1322
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

100 files changed

+2573
-1322
lines changed

.github/workflows/go.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
- name: Set up Go 1.x
1818
uses: actions/setup-go@v5
1919
with:
20-
go-version: '1.20'
20+
go-version-file: go.mod
2121
check-latest: true
2222
cache: true
2323
id: go
@@ -40,7 +40,7 @@ jobs:
4040
run: go test -race -coverprofile=coverage.txt -covermode=atomic ./...
4141

4242
- name: Codecov
43-
uses: codecov/codecov-action@v4
43+
uses: codecov/codecov-action@v5
4444

4545
test-win:
4646
name: Windows
@@ -52,8 +52,8 @@ jobs:
5252
- name: Set up Go 1.x
5353
uses: actions/setup-go@v5
5454
with:
55-
# use 1.20 to guarantee Go 1.20 compatibility
56-
go-version: '1.20'
55+
# make sure Go version compatible with go-zero
56+
go-version-file: go.mod
5757
check-latest: true
5858
cache: true
5959

.github/workflows/release.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ jobs:
2222
github_token: ${{ secrets.GITHUB_TOKEN }}
2323
goos: ${{ matrix.goos }}
2424
goarch: ${{ matrix.goarch }}
25-
goversion: "https://dl.google.com/go/go1.19.13.linux-amd64.tar.gz"
25+
goversion: "https://dl.google.com/go/go1.20.14.linux-amd64.tar.gz"
2626
project_path: "tools/goctl"
2727
binary_name: "goctl"
2828
extra_files: tools/goctl/readme.md tools/goctl/readme-cn.md

core/conf/config.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ func buildFieldsInfo(tp reflect.Type, fullName string) (*fieldInfo, error) {
189189
switch tp.Kind() {
190190
case reflect.Struct:
191191
return buildStructFieldsInfo(tp, fullName)
192-
case reflect.Array, reflect.Slice:
192+
case reflect.Array, reflect.Slice, reflect.Map:
193193
return buildFieldsInfo(mapping.Deref(tp.Elem()), fullName)
194194
case reflect.Chan, reflect.Func:
195195
return nil, fmt.Errorf("unsupported type: %s", tp.Kind())
@@ -332,6 +332,8 @@ func toLowerCaseKeyMap(m map[string]any, info *fieldInfo) map[string]any {
332332
res[lk] = toLowerCaseInterface(v, ti)
333333
} else if info.mapField != nil {
334334
res[k] = toLowerCaseInterface(v, info.mapField)
335+
} else if vv, ok := v.(map[string]any); ok {
336+
res[k] = toLowerCaseKeyMap(vv, info)
335337
} else {
336338
res[k] = v
337339
}

core/conf/config_test.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1192,6 +1192,29 @@ Email = "bar"`)
11921192
assert.Len(t, c.Value, 2)
11931193
}
11941194
})
1195+
1196+
t.Run("multi layer map", func(t *testing.T) {
1197+
type Value struct {
1198+
User struct {
1199+
Name string
1200+
}
1201+
}
1202+
1203+
type Config struct {
1204+
Value map[string]map[string]Value
1205+
}
1206+
1207+
var input = []byte(`
1208+
[Value.first.User1.User]
1209+
Name = "foo"
1210+
[Value.second.User2.User]
1211+
Name = "bar"
1212+
`)
1213+
var c Config
1214+
if assert.NoError(t, LoadFromTomlBytes(input, &c)) {
1215+
assert.Len(t, c.Value, 2)
1216+
}
1217+
})
11951218
}
11961219

11971220
func Test_getFullName(t *testing.T) {

core/configcenter/configurator.go

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
package configurator
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"reflect"
7+
"strings"
8+
"sync"
9+
"sync/atomic"
10+
11+
"github.com/zeromicro/go-zero/core/configcenter/subscriber"
12+
"github.com/zeromicro/go-zero/core/logx"
13+
"github.com/zeromicro/go-zero/core/mapping"
14+
"github.com/zeromicro/go-zero/core/threading"
15+
)
16+
17+
var (
18+
errEmptyConfig = errors.New("empty config value")
19+
errMissingUnmarshalerType = errors.New("missing unmarshaler type")
20+
)
21+
22+
// Configurator is the interface for configuration center.
23+
type Configurator[T any] interface {
24+
// GetConfig returns the subscription value.
25+
GetConfig() (T, error)
26+
// AddListener adds a listener to the subscriber.
27+
AddListener(listener func())
28+
}
29+
30+
type (
31+
// Config is the configuration for Configurator.
32+
Config struct {
33+
// Type is the value type, yaml, json or toml.
34+
Type string `json:",default=yaml,options=[yaml,json,toml]"`
35+
// Log is the flag to control logging.
36+
Log bool `json:",default=true"`
37+
}
38+
39+
configCenter[T any] struct {
40+
conf Config
41+
unmarshaler LoaderFn
42+
subscriber subscriber.Subscriber
43+
listeners []func()
44+
lock sync.Mutex
45+
snapshot atomic.Value
46+
}
47+
48+
value[T any] struct {
49+
data string
50+
marshalData T
51+
err error
52+
}
53+
)
54+
55+
// Configurator is the interface for configuration center.
56+
var _ Configurator[any] = (*configCenter[any])(nil)
57+
58+
// MustNewConfigCenter returns a Configurator, exits on errors.
59+
func MustNewConfigCenter[T any](c Config, subscriber subscriber.Subscriber) Configurator[T] {
60+
cc, err := NewConfigCenter[T](c, subscriber)
61+
logx.Must(err)
62+
return cc
63+
}
64+
65+
// NewConfigCenter returns a Configurator.
66+
func NewConfigCenter[T any](c Config, subscriber subscriber.Subscriber) (Configurator[T], error) {
67+
unmarshaler, ok := Unmarshaler(strings.ToLower(c.Type))
68+
if !ok {
69+
return nil, fmt.Errorf("unknown format: %s", c.Type)
70+
}
71+
72+
cc := &configCenter[T]{
73+
conf: c,
74+
unmarshaler: unmarshaler,
75+
subscriber: subscriber,
76+
}
77+
78+
if err := cc.loadConfig(); err != nil {
79+
return nil, err
80+
}
81+
82+
if err := cc.subscriber.AddListener(cc.onChange); err != nil {
83+
return nil, err
84+
}
85+
86+
if _, err := cc.GetConfig(); err != nil {
87+
return nil, err
88+
}
89+
90+
return cc, nil
91+
}
92+
93+
// AddListener adds listener to s.
94+
func (c *configCenter[T]) AddListener(listener func()) {
95+
c.lock.Lock()
96+
defer c.lock.Unlock()
97+
c.listeners = append(c.listeners, listener)
98+
}
99+
100+
// GetConfig return structured config.
101+
func (c *configCenter[T]) GetConfig() (T, error) {
102+
v := c.value()
103+
if v == nil || len(v.data) == 0 {
104+
var empty T
105+
return empty, errEmptyConfig
106+
}
107+
108+
return v.marshalData, v.err
109+
}
110+
111+
// Value returns the subscription value.
112+
func (c *configCenter[T]) Value() string {
113+
v := c.value()
114+
if v == nil {
115+
return ""
116+
}
117+
return v.data
118+
}
119+
120+
func (c *configCenter[T]) loadConfig() error {
121+
v, err := c.subscriber.Value()
122+
if err != nil {
123+
if c.conf.Log {
124+
logx.Errorf("ConfigCenter loads changed configuration, error: %v", err)
125+
}
126+
return err
127+
}
128+
129+
if c.conf.Log {
130+
logx.Infof("ConfigCenter loads changed configuration, content [%s]", v)
131+
}
132+
133+
c.snapshot.Store(c.genValue(v))
134+
return nil
135+
}
136+
137+
func (c *configCenter[T]) onChange() {
138+
if err := c.loadConfig(); err != nil {
139+
return
140+
}
141+
142+
c.lock.Lock()
143+
listeners := make([]func(), len(c.listeners))
144+
copy(listeners, c.listeners)
145+
c.lock.Unlock()
146+
147+
for _, l := range listeners {
148+
threading.GoSafe(l)
149+
}
150+
}
151+
152+
func (c *configCenter[T]) value() *value[T] {
153+
content := c.snapshot.Load()
154+
if content == nil {
155+
return nil
156+
}
157+
return content.(*value[T])
158+
}
159+
160+
func (c *configCenter[T]) genValue(data string) *value[T] {
161+
v := &value[T]{
162+
data: data,
163+
}
164+
if len(data) == 0 {
165+
return v
166+
}
167+
168+
t := reflect.TypeOf(v.marshalData)
169+
// if the type is nil, it means that the user has not set the type of the configuration.
170+
if t == nil {
171+
v.err = errMissingUnmarshalerType
172+
return v
173+
}
174+
175+
t = mapping.Deref(t)
176+
switch t.Kind() {
177+
case reflect.Struct, reflect.Array, reflect.Slice:
178+
if err := c.unmarshaler([]byte(data), &v.marshalData); err != nil {
179+
v.err = err
180+
if c.conf.Log {
181+
logx.Errorf("ConfigCenter unmarshal configuration failed, err: %+v, content [%s]",
182+
err.Error(), data)
183+
}
184+
}
185+
case reflect.String:
186+
if str, ok := any(data).(T); ok {
187+
v.marshalData = str
188+
} else {
189+
v.err = errMissingUnmarshalerType
190+
}
191+
default:
192+
if c.conf.Log {
193+
logx.Errorf("ConfigCenter unmarshal configuration missing unmarshaler for type: %s, content [%s]",
194+
t.Kind(), data)
195+
}
196+
v.err = errMissingUnmarshalerType
197+
}
198+
199+
return v
200+
}

0 commit comments

Comments
 (0)