-
-
Notifications
You must be signed in to change notification settings - Fork 3
/
helper_test.go
151 lines (126 loc) · 3.68 KB
/
helper_test.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
package owl_test
import (
"context"
"encoding/json"
"fmt"
"os"
"strings"
"github.com/ggicci/owl"
)
func exeNoop(rtm *owl.DirectiveRuntime) error {
return nil
}
func exeEnvReader(rtm *owl.DirectiveRuntime) error {
if len(rtm.Directive.Argv) == 0 {
return nil
}
if value, ok := os.LookupEnv(rtm.Directive.Argv[0]); ok {
rtm.Value.Elem().SetString(value)
}
return nil
}
func exeConfigLoader(rtm *owl.DirectiveRuntime) error {
if len(rtm.Directive.Argv) == 0 {
return nil
}
key := rtm.Directive.Argv[0]
overrideKey := strings.ToUpper("MYAPP_" + strings.ReplaceAll(key, ".", "_"))
// Override by environment variable.
value, exists := os.LookupEnv(overrideKey)
if exists {
rtm.Value.Elem().SetString(value)
return nil
}
// Last resort, load from config file. The file will be opened and closed
// each time the directive is executed. Which is not a performance-wise
// implementation. But it's just a sample here.
configFile := rtm.Context.Value("ConfigFile").(string)
file, err := os.Open(configFile)
if err != nil {
return err
}
defer file.Close()
decoder := json.NewDecoder(file)
var config map[string]string
if err := decoder.Decode(&config); err != nil {
return err
}
rtm.Value.Elem().SetString(config[key])
return nil
}
type ExecutedData struct {
*owl.Directive
FieldValue any
}
type ExecutedDataList []ExecutedData
func (edl ExecutedDataList) ExecutedDirectives() []*owl.Directive {
dirs := make([]*owl.Directive, len(edl))
for i, d := range edl {
dirs[i] = d.Directive
}
return dirs
}
type ExecutionTracker struct {
Executed ExecutedDataList
}
func NewExecutionTracker() *ExecutionTracker {
return &ExecutionTracker{}
}
func (et *ExecutionTracker) Track(directive *owl.Directive, value any) {
et.Executed = append(et.Executed, ExecutedData{directive, value})
}
func (et *ExecutionTracker) Reset() {
et.Executed = nil
}
type ContextVerifier struct {
Key interface{}
Expected interface{}
}
func (cv *ContextVerifier) Verify(ctx context.Context) error {
if cv.Expected != ctx.Value(cv.Key) {
return fmt.Errorf("unexpected context value for key %q: %v", cv.Key, ctx.Value(cv.Key))
}
return nil
}
type EchoExecutor struct {
Name string
ThrowError error
ContextVerifier *ContextVerifier
tracker *ExecutionTracker
}
func NewEchoExecutor(tracker *ExecutionTracker, name string, throwError error, verifier *ContextVerifier) *EchoExecutor {
return &EchoExecutor{
Name: name,
ThrowError: throwError,
ContextVerifier: verifier,
tracker: tracker,
}
}
func (e *EchoExecutor) Execute(ctx *owl.DirectiveRuntime) error {
e.tracker.Track(ctx.Directive, ctx.Value.Interface())
fmt.Printf("Execute %q with args: %v, value: %v\n", ctx.Directive.Name, ctx.Directive.Argv, ctx.Value)
if e.ContextVerifier != nil {
if err := e.ContextVerifier.Verify(ctx.Context); err != nil {
return err
}
}
return e.ThrowError
}
func createNsForTracking(keys ...string) (*owl.Namespace, *ExecutionTracker) {
return createNsForTrackingCtor(nil, nil, keys...)
}
func createNsForTrackingWithError(throwError error) (*owl.Namespace, *ExecutionTracker) {
return createNsForTrackingCtor(throwError, nil)
}
func createNsForTrackingWithContextVerifier(verifier *ContextVerifier) (*owl.Namespace, *ExecutionTracker) {
return createNsForTrackingCtor(nil, verifier)
}
func createNsForTrackingCtor(throwError error, verifier *ContextVerifier, keys ...string) (*owl.Namespace, *ExecutionTracker) {
ns := owl.NewNamespace()
tracker := NewExecutionTracker()
keys = append(keys, "form", "env", "default")
for _, key := range keys {
ns.RegisterDirectiveExecutor(key, NewEchoExecutor(tracker, key, throwError, verifier), true)
}
return ns, tracker
}