-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathstatic_execute.go
208 lines (173 loc) · 6.01 KB
/
static_execute.go
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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
package core
import (
"context"
"fmt"
"reflect"
"strings"
mgcSchemaPkg "github.com/MagaluCloud/magalu/mgc/core/schema"
"github.com/MagaluCloud/magalu/mgc/core/utils"
)
// Given a base spec and types to be reflected, populate with the schemas
func ReflectExecutorSpecSchemas[ParamsT any, ConfigsT any, ResultT any](baseSpec ExecutorSpec) (spec ExecutorSpec, err error) {
spec = baseSpec
spec.ParametersSchema, err = mgcSchemaPkg.SchemaFromType[ParamsT]()
if err != nil {
err = &ChainedError{Name: "ParamsT", Err: err}
return
}
spec.ConfigsSchema, err = mgcSchemaPkg.SchemaFromType[ConfigsT]()
if err != nil {
err = &ChainedError{Name: "ConfigsT", Err: err}
return
}
spec.ResultSchema, err = mgcSchemaPkg.SchemaFromType[ResultT]()
if err != nil {
err = &ChainedError{Name: "ResultT", Err: err}
return
}
spec.PositionalArgs = getPositionals(reflect.TypeOf(new(ParamsT)))
spec.HiddenFlags = getHiddens(reflect.TypeOf(new(ParamsT)))
return spec, nil
}
func getHiddens(t reflect.Type) []string {
if t.Kind() == reflect.Pointer {
t = t.Elem()
}
if t.Kind() != reflect.Struct {
return nil
}
var hidden []string
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
if field.Anonymous {
hidden = append(hidden, getHiddens(field.Type)...)
continue
}
if !GetMgcTagBool(field.Tag, "hidden") {
continue
}
name := field.Name
if jsonName := strings.Split(field.Tag.Get("json"), ",")[0]; jsonName != "" {
name = jsonName
}
hidden = append(hidden, name)
}
return hidden
}
func getPositionals(t reflect.Type) []string {
if t.Kind() == reflect.Pointer {
t = t.Elem()
}
if t.Kind() != reflect.Struct {
return nil
}
var positionals []string
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
if field.Anonymous {
positionals = append(positionals, getPositionals(field.Type)...)
continue
}
if !GetMgcTagBool(field.Tag, "positional") {
continue
}
name := field.Name
if jsonName := strings.Split(field.Tag.Get("json"), ",")[0]; jsonName != "" {
name = jsonName
}
positionals = append(positionals, name)
}
return positionals
}
func ReflectExecutorSpecFn[ParamsT any, ConfigsT any, ResultT any](
typedExecute func(context context.Context, params ParamsT, configs ConfigsT) (result ResultT, err error),
) ExecutorSpecFn {
return func(executor Executor, ctx context.Context, parameters Parameters, configs Configs) (Result, error) {
typedParams, err := utils.DecodeNewValue[ParamsT](parameters)
if err != nil {
return nil, &ChainedError{Name: "parameters", Err: fmt.Errorf("decoding error. Did you forget to set 'json' struct flags for struct %T?: %w", typedParams, err)}
}
typedConfigs, err := utils.DecodeNewValue[ConfigsT](configs)
if err != nil {
return nil, &ChainedError{Name: "configs", Err: fmt.Errorf("decoding error. Did you forget to set 'json' struct flags for struct %T?: %w", typedConfigs, err)}
}
typedResult, err := typedExecute(ctx, *typedParams, *typedConfigs)
if err != nil {
return nil, err
}
value, err := utils.SimplifyAny(typedResult)
if err != nil {
return nil, &ChainedError{Name: "result", Err: fmt.Errorf("error simplifying %T: %w", typedResult, err)}
}
source := ResultSource{
Executor: executor,
Context: ctx,
Parameters: parameters,
Configs: configs,
}
return NewSimpleResult(source, executor.ResultSchema(), value), nil
}
}
func ReflectExecutorSpec[ParamsT any, ConfigsT any, ResultT any](
baseSpec ExecutorSpec,
typedExecute func(context context.Context, params ParamsT, configs ConfigsT) (result ResultT, err error),
) (spec ExecutorSpec, err error) {
spec, err = ReflectExecutorSpecSchemas[ParamsT, ConfigsT, ResultT](baseSpec)
if err != nil {
return
}
spec.Execute = ReflectExecutorSpecFn[ParamsT, ConfigsT, ResultT](typedExecute)
return
}
// Go Parameter and Config structs
// Note: we use both 'jsonschema' and 'mapstructure' for this helper. Be careful
// when using struct tags in your Params and Configs structs, as the tags from those
// libraries can't be out of sync when it comes to field names/json names
// See:
// - https://pkg.go.dev/github.com/invopop/jsonschema
// - https://pkg.go.dev/github.com/mitchellh/mapstructure
func NewReflectedSimpleExecutor[ParamsT any, ConfigsT any, ResultT any](
baseSpec ExecutorSpec,
execute func(context context.Context, params ParamsT, configs ConfigsT) (result ResultT, err error),
) *SimpleExecutor {
execSpec, err := ReflectExecutorSpec[ParamsT, ConfigsT, ResultT](baseSpec, execute)
if err != nil {
logger().Fatalw("cannot reflect static executor", "err", err)
}
return NewSimpleExecutor(execSpec)
}
// Version that generates the schema, but uses baseSpec.Execute as is
func NewReflectedSimpleExecutorSchemas[ParamsT any, ConfigsT any, ResultT any](
baseSpec ExecutorSpec,
) *SimpleExecutor {
execSpec, err := ReflectExecutorSpecSchemas[ParamsT, ConfigsT, ResultT](baseSpec)
if err != nil {
logger().Fatalw("cannot reflect static executor", "err", err)
}
return NewSimpleExecutor(execSpec)
}
// Go Parameter and Config structs
// Note: we use both 'jsonschema' and 'mapstructure' for this helper. Be careful
// when using struct tags in your Params and Configs structs, as the tags from those
// libraries can't be out of sync when it comes to field names/json names
// See:
// - https://pkg.go.dev/github.com/invopop/jsonschema
// - https://pkg.go.dev/github.com/mitchellh/mapstructure
func NewStaticExecute[ParamsT any, ConfigsT any, ResultT any](
spec DescriptorSpec,
execute func(context context.Context, params ParamsT, configs ConfigsT) (result ResultT, err error),
) *SimpleExecutor {
return NewReflectedSimpleExecutor(ExecutorSpec{DescriptorSpec: spec}, execute)
}
// No parameters or configs
func NewStaticExecuteSimple[ResultT any](
spec DescriptorSpec,
execute func(context context.Context) (result ResultT, err error),
) *SimpleExecutor {
return NewReflectedSimpleExecutor(
ExecutorSpec{DescriptorSpec: spec},
func(context context.Context, _ struct{}, _ struct{}) (result ResultT, err error) {
return execute(context)
},
)
}