-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Expand file tree
/
Copy pathconfig.go
More file actions
170 lines (142 loc) · 5.31 KB
/
Copy pathconfig.go
File metadata and controls
170 lines (142 loc) · 5.31 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
package appconfig
import (
"fmt"
"reflect"
"strings"
"github.com/cosmos/cosmos-proto/anyutil"
gogoproto "github.com/cosmos/gogoproto/proto"
"google.golang.org/protobuf/encoding/protojson"
protov2 "google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/types/dynamicpb"
"google.golang.org/protobuf/types/known/anypb"
"sigs.k8s.io/yaml"
"cosmossdk.io/depinject"
"cosmossdk.io/depinject/appconfig/v1alpha1"
internal "cosmossdk.io/depinject/internal/appconfig"
)
// LoadJSON loads an app config in JSON format.
func LoadJSON(bz []byte) depinject.Config {
// in order to avoid a direct dependency on api types, but in order to also be able to support
// either gogo or google.golang.org/protobuf types, we use protojson and dynamicpb to unmarshal
// from JSON
resolver := gogoproto.HybridResolver
desc, err := resolver.FindDescriptorByName(protoreflect.FullName(gogoproto.MessageName(&v1alpha1.Config{})))
if err != nil {
return depinject.Error(err)
}
config := dynamicpb.NewMessage(desc.(protoreflect.MessageDescriptor))
err = protojson.UnmarshalOptions{
Resolver: dynamicTypeResolver{resolver: gogoproto.HybridResolver},
}.Unmarshal(bz, config)
if err != nil {
return depinject.Error(err)
}
return Compose(config)
}
// LoadYAML loads an app config in YAML format.
func LoadYAML(bz []byte) depinject.Config {
j, err := yaml.YAMLToJSON(bz)
if err != nil {
return depinject.Error(err)
}
return LoadJSON(j)
}
// WrapAny marshals a proto message into a proto Any instance
func WrapAny(config protoreflect.ProtoMessage) *anypb.Any {
cfg, err := anyutil.New(config)
if err != nil {
panic(err)
}
return cfg
}
// Compose composes an app config into a container option by resolving
// the required modules and composing their options. appConfig should be an instance
// of cosmos.app.v1alpha1.Config (it doesn't matter whether you use gogo proto or
// google.golang.org/protobuf types).
func Compose(appConfig gogoproto.Message) depinject.Config {
appConfigConcrete, ok := appConfig.(*v1alpha1.Config)
if !ok {
// we convert any other proto type that was passed (such as an api module type) to the concrete
// type we're using here
appConfigConcrete = &v1alpha1.Config{}
bz, err := gogoproto.Marshal(appConfig)
if err != nil {
return depinject.Error(err)
}
err = gogoproto.Unmarshal(bz, appConfigConcrete)
if err != nil {
return depinject.Error(err)
}
}
opts := []depinject.Config{
depinject.Supply(appConfig),
}
modules, err := internal.ModulesByModuleTypeName()
if err != nil {
return depinject.Error(err)
}
for _, module := range appConfigConcrete.Modules {
if module.Name == "" {
return depinject.Error(fmt.Errorf("module is missing name"))
}
if module.Config == nil {
return depinject.Error(fmt.Errorf("module %q is missing a config object", module.Name))
}
msgName := module.Config.TypeUrl
// strip type URL prefix
if slashIdx := strings.LastIndex(msgName, "/"); slashIdx >= 0 {
msgName = msgName[slashIdx+1:]
}
if msgName == "" {
return depinject.Error(fmt.Errorf("module %q is missing a type URL", module.Name))
}
init, ok := modules[msgName]
if !ok {
if msgDesc, err := gogoproto.HybridResolver.FindDescriptorByName(protoreflect.FullName(msgName)); err == nil {
modDesc, err := internal.GetModuleDescriptor(msgDesc)
if err != nil {
return depinject.Error(err)
}
if modDesc == nil {
return depinject.Error(fmt.Errorf("no module registered for type URL %s and that protobuf type does not have the option %s\n\n%s",
module.Config.TypeUrl, v1alpha1.E_Module.Name, dumpRegisteredModules(modules)))
}
return depinject.Error(fmt.Errorf("no module registered for type URL %s, did you forget to import %s: find more information on how to make a module ready for app wiring: https://docs.cosmos.network/main/building-modules/depinject\n\n%s",
module.Config.TypeUrl, modDesc.GoImport, dumpRegisteredModules(modules)))
}
}
var config gogoproto.Message
if configInit, ok := init.ConfigProtoMessage.(protov2.Message); ok {
config = configInit.ProtoReflect().Type().New().Interface().(gogoproto.Message)
} else {
config = reflect.New(init.ConfigGoType.Elem()).Interface().(gogoproto.Message)
}
// as of gogo v1.5.0 this should work with either gogoproto or golang v2 proto
err = gogoproto.Unmarshal(module.Config.Value, config)
if err != nil {
return depinject.Error(err)
}
opts = append(opts, depinject.Supply(config))
for _, provider := range init.Providers {
opts = append(opts, depinject.ProvideInModule(module.Name, provider))
}
for _, invoker := range init.Invokers {
opts = append(opts, depinject.InvokeInModule(module.Name, invoker))
}
for _, binding := range module.GolangBindings {
opts = append(opts, depinject.BindInterfaceInModule(module.Name, binding.InterfaceType, binding.Implementation))
}
}
for _, binding := range appConfigConcrete.GolangBindings {
opts = append(opts, depinject.BindInterface(binding.InterfaceType, binding.Implementation))
}
return depinject.Configs(opts...)
}
func dumpRegisteredModules(modules map[string]*internal.ModuleInitializer) string {
var mods []string
for name := range modules {
mods = append(mods, " "+name)
}
return fmt.Sprintf("registered modules are:\n%s", strings.Join(mods, "\n"))
}