Skip to content

Commit 46e7f04

Browse files
committed
fix null reference error in patching OpenAPI responses
improve JavaScript event handler tracking when called console.log add JavaScript shared memory feature update NPM types
1 parent 0537d2c commit 46e7f04

File tree

13 files changed

+591
-27
lines changed

13 files changed

+591
-27
lines changed

engine/common/host.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ type Host interface {
4545

4646
Lock()
4747
Unlock()
48+
49+
Store() Store
4850
}
4951

5052
type Logger interface {
@@ -168,3 +170,8 @@ type FakerNode interface {
168170
Name() string
169171
Fake(r *generator.Request) (interface{}, error)
170172
}
173+
174+
type Store interface {
175+
Get(string) any
176+
Set(string, any)
177+
}

engine/engine.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package engine
22

33
import (
4-
"github.com/pkg/errors"
54
"mokapi/config/dynamic"
65
"mokapi/config/static"
76
"mokapi/engine/common"
@@ -10,6 +9,8 @@ import (
109
"mokapi/runtime/metrics"
1110
"sync"
1211

12+
"github.com/pkg/errors"
13+
1314
log "github.com/sirupsen/logrus"
1415
)
1516

@@ -27,6 +28,12 @@ type Engine struct {
2728
cfgEvent static.Event
2829
jobCounter *metrics.Counter
2930
sm *events.StoreManager
31+
store *Store
32+
}
33+
34+
type Store struct {
35+
data map[string]any
36+
mu sync.RWMutex
3037
}
3138

3239
func New(reader dynamic.Reader, app *runtime.App, config *static.Config, parallel bool) *Engine {
@@ -41,6 +48,7 @@ func New(reader dynamic.Reader, app *runtime.App, config *static.Config, paralle
4148
cfgEvent: config.Event,
4249
jobCounter: app.Monitor.JobCounter,
4350
sm: app.Events,
51+
store: &Store{data: make(map[string]any)},
4452
}
4553
}
4654

@@ -153,3 +161,15 @@ func (e *Engine) Scripts() int {
153161
func (e *Engine) IsLevelEnabled(level string) bool {
154162
return e.logger.IsLevelEnabled(level)
155163
}
164+
165+
func (s *Store) Get(key string) any {
166+
s.mu.RLock()
167+
defer s.mu.RUnlock()
168+
return s.data[key]
169+
}
170+
171+
func (s *Store) Set(key string, value any) {
172+
s.mu.Lock()
173+
defer s.mu.Unlock()
174+
s.data[key] = value
175+
}

engine/enginetest/host.go

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ type Host struct {
2727
OnFunc func(event string, do func(args ...interface{}) (bool, error), tags map[string]string)
2828
FindFakerNodeFunc func(name string) *generator.Node
2929
m sync.Mutex
30+
StoreTest *Store
31+
}
32+
33+
type Store struct {
34+
data map[string]any
35+
m sync.RWMutex
3036
}
3137

3238
type HttpClient struct {
@@ -103,7 +109,7 @@ func (h *Host) On(event string, do func(args ...interface{}) (bool, error), tags
103109
}
104110
}
105111

106-
func (h *Host) Cancel(jobId int) error {
112+
func (h *Host) Cancel(_ int) error {
107113
return nil
108114
}
109115

@@ -126,14 +132,18 @@ func (h *Host) Unlock() {
126132
h.m.Unlock()
127133
}
128134

129-
func (h *Host) HttpClient(opts common.HttpClientOptions) common.HttpClient {
135+
func (h *Host) HttpClient(_ common.HttpClientOptions) common.HttpClient {
130136
return h.HttpClientTest
131137
}
132138

133139
func (h *Host) KafkaClient() common.KafkaClient {
134140
return h.KafkaClientTest
135141
}
136142

143+
func (h *Host) Store() common.Store {
144+
return h.StoreTest
145+
}
146+
137147
func (c *HttpClient) Do(request *http.Request) (*http.Response, error) {
138148
c.LastRequest = request
139149
if c.DoFunc != nil {
@@ -160,3 +170,21 @@ func mustParse(s string) *url.URL {
160170
}
161171
return u
162172
}
173+
174+
func (s *Store) Get(name string) any {
175+
s.m.RLock()
176+
defer s.m.RUnlock()
177+
if s.data == nil {
178+
return nil
179+
}
180+
return s.data[name]
181+
}
182+
183+
func (s *Store) Set(name string, value any) {
184+
s.m.Lock()
185+
defer s.m.Unlock()
186+
if s.data == nil {
187+
s.data = map[string]any{name: value}
188+
}
189+
s.data[name] = value
190+
}

engine/eventhandler_test.go

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
package engine_test
22

33
import (
4-
"github.com/stretchr/testify/require"
54
"mokapi/engine"
65
"mokapi/engine/common"
76
"mokapi/engine/enginetest"
87
"testing"
8+
9+
"github.com/stretchr/testify/require"
910
)
1011

1112
func TestEventHandler(t *testing.T) {
@@ -42,6 +43,26 @@ export default () => {
4243
console.log('a log message from event handler')
4344
}, { track: true })
4445
}
46+
`,
47+
run: func(evt common.EventEmitter) []*common.Action {
48+
return evt.Emit("http")
49+
},
50+
test: func(t *testing.T, actions []*common.Action, err error) {
51+
require.NoError(t, err)
52+
require.Len(t, actions, 1)
53+
require.Len(t, actions[0].Logs, 1)
54+
require.Equal(t, "a log message from event handler", actions[0].Logs[0].Message)
55+
require.Equal(t, "log", actions[0].Logs[0].Level)
56+
},
57+
},
58+
{
59+
name: "calling console.log tracks the event handler",
60+
script: `import { on } from 'mokapi'
61+
export default () => {
62+
on('http', () => {
63+
console.log('a log message from event handler')
64+
})
65+
}
4566
`,
4667
run: func(evt common.EventEmitter) []*common.Action {
4768
return evt.Emit("http")

engine/host.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,12 @@ func (sh *scriptHost) RunEvent(event string, args ...interface{}) []*common.Acti
7979
}
8080
sh.startEventHandler(action.AppendLog)
8181
start := time.Now()
82+
logs := len(action.Logs)
8283

8384
if b, err := eh.handler(args...); err != nil {
8485
log.Errorf("unable to execute event handler: %v", err)
8586
action.Error = &common.Error{Message: err.Error()}
86-
} else if !b {
87+
} else if !b && logs == len(action.Logs) {
8788
sh.Unlock()
8889
continue
8990
} else {
@@ -336,6 +337,10 @@ func (sh *scriptHost) Unlock() {
336337
sh.m.Unlock()
337338
}
338339

340+
func (sh *scriptHost) Store() common.Store {
341+
return sh.engine.store
342+
}
343+
339344
func getScriptPath(u *url.URL) string {
340345
if len(u.Path) > 0 {
341346
return u.Path

js/mokapi/mokapi.go

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@ package mokapi
22

33
import (
44
"fmt"
5-
"github.com/dop251/goja"
65
"mokapi/engine/common"
76
"mokapi/js/eventloop"
87
"mokapi/js/faker"
98
"mokapi/media"
109
"mokapi/providers/openapi/schema"
1110
"os"
1211
"time"
12+
13+
"github.com/dop251/goja"
1314
)
1415

1516
type Module struct {
@@ -28,16 +29,17 @@ func Require(vm *goja.Runtime, module *goja.Object) {
2829
loop: loop,
2930
}
3031
obj := module.Get("exports").(*goja.Object)
31-
obj.Set("sleep", f.Sleep)
32-
obj.Set("every", f.Every)
33-
obj.Set("cron", f.Cron)
34-
obj.Set("on", f.On)
35-
obj.Set("env", f.Env)
36-
obj.Set("encoding", f.Marshal)
37-
obj.Set("date", f.Date)
38-
obj.Set("marshal", f.Marshal)
39-
obj.Set("patch", patch)
40-
obj.Set("Delete", Delete)
32+
_ = obj.Set("sleep", f.Sleep)
33+
_ = obj.Set("every", f.Every)
34+
_ = obj.Set("cron", f.Cron)
35+
_ = obj.Set("on", f.On)
36+
_ = obj.Set("env", f.Env)
37+
_ = obj.Set("encoding", f.Marshal)
38+
_ = obj.Set("date", f.Date)
39+
_ = obj.Set("marshal", f.Marshal)
40+
_ = obj.Set("patch", patch)
41+
_ = obj.Set("Delete", Delete)
42+
_ = obj.Set("shared", NewSharedMemory(host.Store()))
4143
}
4244

4345
func (m *Module) Sleep(i interface{}) {

js/mokapi/shared.go

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package mokapi
2+
3+
import (
4+
"mokapi/engine/common"
5+
"sync"
6+
)
7+
8+
type SharedMemory struct {
9+
data map[string]any
10+
clear func()
11+
m sync.RWMutex
12+
}
13+
14+
func NewSharedMemory(store common.Store) *SharedMemory {
15+
v := store.Get("shared-memory")
16+
var data map[string]any
17+
if v != nil {
18+
data = v.(map[string]any)
19+
} else {
20+
data = make(map[string]any)
21+
store.Set("shared-memory", data)
22+
}
23+
24+
return &SharedMemory{data: data}
25+
}
26+
27+
func (m *SharedMemory) Get(key string) any {
28+
m.m.RLock()
29+
defer m.m.RUnlock()
30+
31+
return m.data[key]
32+
}
33+
34+
func (m *SharedMemory) Has(key string) bool {
35+
m.m.RLock()
36+
defer m.m.RUnlock()
37+
38+
_, b := m.data[key]
39+
return b
40+
}
41+
42+
func (m *SharedMemory) Set(key string, value any) {
43+
m.m.Lock()
44+
defer m.m.Unlock()
45+
m.data[key] = value
46+
}
47+
48+
func (m *SharedMemory) Delete(key string) {
49+
m.m.Lock()
50+
defer m.m.Unlock()
51+
delete(m.data, key)
52+
}
53+
54+
func (m *SharedMemory) Clear() {
55+
m.m.Lock()
56+
defer m.m.Unlock()
57+
for k := range m.data {
58+
delete(m.data, k)
59+
}
60+
}
61+
62+
func (m *SharedMemory) Update(key string, fn func(v any) any) any {
63+
m.m.Lock()
64+
defer m.m.Unlock()
65+
v := fn(m.data[key])
66+
m.data[key] = v
67+
return v
68+
}
69+
70+
func (m *SharedMemory) Keys() []string {
71+
m.m.RLock()
72+
defer m.m.RUnlock()
73+
var keys []string
74+
for k := range m.data {
75+
keys = append(keys, k)
76+
}
77+
return keys
78+
}
79+
80+
func (m *SharedMemory) Namespace(name string) *SharedMemory {
81+
m.m.Lock()
82+
v, ok := m.data[name]
83+
m.m.Unlock()
84+
if ok {
85+
ns, ok := v.(*SharedMemory)
86+
87+
if !ok {
88+
return nil
89+
}
90+
return ns
91+
}
92+
93+
m.m.Lock()
94+
defer m.m.Unlock()
95+
ns := &SharedMemory{data: make(map[string]any)}
96+
m.data[name] = ns
97+
return ns
98+
}

0 commit comments

Comments
 (0)