Skip to content

Commit 7be52d5

Browse files
authored
Add chasm.Context.ExecutionKey() method (#8533)
## What changed? - Add chasm.Context.ExecutionKey() method - Refactor `context.go` to use better struct names and unexport concepts that should not be exported - Add some docstrings
1 parent 259a991 commit 7be52d5

15 files changed

+508
-793
lines changed

chasm/context.go

Lines changed: 47 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
//go:generate mockgen -package $GOPACKAGE -source $GOFILE -destination context_mock.go
2-
31
package chasm
42

53
import (
@@ -14,7 +12,11 @@ type Context interface {
1412
// NOTE: component created in the current transaction won't have a ref
1513
// this is a Ref to the component state at the start of the transition
1614
Ref(Component) ([]byte, error)
15+
// Now returns the current time in the context of the given component.
16+
// In a context of a transaction, this time must be used to allow for framework support of pause and time skipping.
1717
Now(Component) time.Time
18+
// ExecutionKey returns the execution key for the execution the context is operating on.
19+
ExecutionKey() EntityKey
1820

1921
// Intent() OperationIntent
2022
// ComponentOptions(Component) []ComponentOption
@@ -25,6 +27,9 @@ type Context interface {
2527
type MutableContext interface {
2628
Context
2729

30+
// AddTask adds a task to be emitted as part of the current transaction.
31+
// The task is associated with the given component and will be invoked via the registered executor for the given task
32+
// referencing the component.
2833
AddTask(Component, TaskAttributes, any)
2934

3035
// Add more methods here for other storage commands/primitives.
@@ -40,53 +45,81 @@ type MutableContext interface {
4045
// NewRef(Component) (ComponentRef, bool)
4146
}
4247

43-
type ContextImpl struct {
48+
type immutableCtx struct {
4449
// The context here is not really used today.
4550
// But it will be when we support partial loading later,
4651
// and the framework potentially needs to go to persistence to load some fields.
4752
ctx context.Context
4853

54+
executionKey EntityKey
55+
4956
// Not embedding the Node here to avoid exposing AddTask() method on Node,
5057
// so that ContextImpl won't implement MutableContext interface.
5158
root *Node
5259
}
5360

54-
type MutableContextImpl struct {
55-
*ContextImpl
61+
type mutableCtx struct {
62+
*immutableCtx
5663
}
5764

65+
// NewContext creates a new Context from an existing Context and root Node.
66+
//
67+
// NOTE: Library authors should not invoke this constructor directly, and instead use [ReadComponent].
5868
func NewContext(
5969
ctx context.Context,
6070
node *Node,
61-
) *ContextImpl {
62-
return &ContextImpl{
71+
) Context {
72+
return newContext(ctx, node)
73+
}
74+
75+
// newContext creates a new immutableCtx from an existing Context and root Node.
76+
// This is similar to NewContext, but returns *immutableCtx instead of Context interface.
77+
func newContext(
78+
ctx context.Context,
79+
node *Node,
80+
) *immutableCtx {
81+
workflowKey := node.backend.GetWorkflowKey()
82+
return &immutableCtx{
6383
ctx: ctx,
6484
root: node.root(),
85+
executionKey: EntityKey{
86+
NamespaceID: workflowKey.NamespaceID,
87+
BusinessID: workflowKey.WorkflowID,
88+
EntityID: workflowKey.RunID,
89+
},
6590
}
6691
}
6792

68-
func (c *ContextImpl) Ref(component Component) ([]byte, error) {
93+
func (c *immutableCtx) Ref(component Component) ([]byte, error) {
6994
return c.root.Ref(component)
7095
}
7196

72-
func (c *ContextImpl) Now(component Component) time.Time {
97+
func (c *immutableCtx) Now(component Component) time.Time {
7398
return c.root.Now(component)
7499
}
75100

76-
func (c *ContextImpl) getContext() context.Context {
101+
func (c *immutableCtx) ExecutionKey() EntityKey {
102+
return c.executionKey
103+
}
104+
105+
func (c *immutableCtx) getContext() context.Context {
77106
return c.ctx
78107
}
79108

109+
// NewMutableContext creates a new MutableContext from an existing Context and root Node.
110+
//
111+
// NOTE: Library authors should not invoke this constructor directly, and instead use the [UpdateComponent],
112+
// [UpdateWithNewEntity], or [NewEntity] APIs.
80113
func NewMutableContext(
81114
ctx context.Context,
82115
root *Node,
83-
) *MutableContextImpl {
84-
return &MutableContextImpl{
85-
ContextImpl: NewContext(ctx, root),
116+
) MutableContext {
117+
return &mutableCtx{
118+
immutableCtx: newContext(ctx, root),
86119
}
87120
}
88121

89-
func (c *MutableContextImpl) AddTask(
122+
func (c *mutableCtx) AddTask(
90123
component Component,
91124
attributes TaskAttributes,
92125
payload any,

chasm/context_mock.go

Lines changed: 37 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -1,164 +1,60 @@
1-
// Code generated by MockGen. DO NOT EDIT.
2-
// Source: context.go
3-
//
4-
// Generated by this command:
5-
//
6-
// mockgen -package chasm -source context.go -destination context_mock.go
7-
//
8-
9-
// Package chasm is a generated GoMock package.
101
package chasm
112

123
import (
13-
context "context"
14-
reflect "reflect"
15-
time "time"
16-
17-
gomock "go.uber.org/mock/gomock"
4+
"context"
5+
"sync"
6+
"time"
187
)
198

20-
// MockContext is a mock of Context interface.
9+
// MockContext is a mock implementation of [Context].
2110
type MockContext struct {
22-
ctrl *gomock.Controller
23-
recorder *MockContextMockRecorder
24-
isgomock struct{}
25-
}
26-
27-
// MockContextMockRecorder is the mock recorder for MockContext.
28-
type MockContextMockRecorder struct {
29-
mock *MockContext
30-
}
31-
32-
// NewMockContext creates a new mock instance.
33-
func NewMockContext(ctrl *gomock.Controller) *MockContext {
34-
mock := &MockContext{ctrl: ctrl}
35-
mock.recorder = &MockContextMockRecorder{mock}
36-
return mock
37-
}
38-
39-
// EXPECT returns an object that allows the caller to indicate expected use.
40-
func (m *MockContext) EXPECT() *MockContextMockRecorder {
41-
return m.recorder
11+
HandleExecutionKey func() EntityKey
12+
HandleNow func(component Component) time.Time
13+
HandleRef func(component Component) ([]byte, error)
4214
}
4315

44-
// Now mocks base method.
45-
func (m *MockContext) Now(arg0 Component) time.Time {
46-
m.ctrl.T.Helper()
47-
ret := m.ctrl.Call(m, "Now", arg0)
48-
ret0, _ := ret[0].(time.Time)
49-
return ret0
16+
func (c *MockContext) getContext() context.Context {
17+
return nil
5018
}
5119

52-
// Now indicates an expected call of Now.
53-
func (mr *MockContextMockRecorder) Now(arg0 any) *gomock.Call {
54-
mr.mock.ctrl.T.Helper()
55-
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Now", reflect.TypeOf((*MockContext)(nil).Now), arg0)
20+
func (c *MockContext) Now(cmp Component) time.Time {
21+
if c.HandleNow != nil {
22+
return c.HandleNow(cmp)
23+
}
24+
return time.Now()
5625
}
5726

58-
// Ref mocks base method.
59-
func (m *MockContext) Ref(arg0 Component) ([]byte, error) {
60-
m.ctrl.T.Helper()
61-
ret := m.ctrl.Call(m, "Ref", arg0)
62-
ret0, _ := ret[0].([]byte)
63-
ret1, _ := ret[1].(error)
64-
return ret0, ret1
27+
func (c *MockContext) Ref(cmp Component) ([]byte, error) {
28+
if c.HandleRef != nil {
29+
return c.HandleRef(cmp)
30+
}
31+
return nil, nil
6532
}
6633

67-
// Ref indicates an expected call of Ref.
68-
func (mr *MockContextMockRecorder) Ref(arg0 any) *gomock.Call {
69-
mr.mock.ctrl.T.Helper()
70-
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Ref", reflect.TypeOf((*MockContext)(nil).Ref), arg0)
34+
func (c *MockContext) ExecutionKey() EntityKey {
35+
if c.HandleExecutionKey != nil {
36+
return c.HandleExecutionKey()
37+
}
38+
return EntityKey{}
7139
}
7240

73-
// getContext mocks base method.
74-
func (m *MockContext) getContext() context.Context {
75-
m.ctrl.T.Helper()
76-
ret := m.ctrl.Call(m, "getContext")
77-
ret0, _ := ret[0].(context.Context)
78-
return ret0
79-
}
80-
81-
// getContext indicates an expected call of getContext.
82-
func (mr *MockContextMockRecorder) getContext() *gomock.Call {
83-
mr.mock.ctrl.T.Helper()
84-
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "getContext", reflect.TypeOf((*MockContext)(nil).getContext))
85-
}
86-
87-
// MockMutableContext is a mock of MutableContext interface.
41+
// MockMutableContext is a mock implementation of [MutableContext] that records added tasks for inspection in
42+
// tests.
8843
type MockMutableContext struct {
89-
ctrl *gomock.Controller
90-
recorder *MockMutableContextMockRecorder
91-
isgomock struct{}
92-
}
93-
94-
// MockMutableContextMockRecorder is the mock recorder for MockMutableContext.
95-
type MockMutableContextMockRecorder struct {
96-
mock *MockMutableContext
97-
}
98-
99-
// NewMockMutableContext creates a new mock instance.
100-
func NewMockMutableContext(ctrl *gomock.Controller) *MockMutableContext {
101-
mock := &MockMutableContext{ctrl: ctrl}
102-
mock.recorder = &MockMutableContextMockRecorder{mock}
103-
return mock
104-
}
105-
106-
// EXPECT returns an object that allows the caller to indicate expected use.
107-
func (m *MockMutableContext) EXPECT() *MockMutableContextMockRecorder {
108-
return m.recorder
109-
}
110-
111-
// AddTask mocks base method.
112-
func (m *MockMutableContext) AddTask(arg0 Component, arg1 TaskAttributes, arg2 any) {
113-
m.ctrl.T.Helper()
114-
m.ctrl.Call(m, "AddTask", arg0, arg1, arg2)
115-
}
116-
117-
// AddTask indicates an expected call of AddTask.
118-
func (mr *MockMutableContextMockRecorder) AddTask(arg0, arg1, arg2 any) *gomock.Call {
119-
mr.mock.ctrl.T.Helper()
120-
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddTask", reflect.TypeOf((*MockMutableContext)(nil).AddTask), arg0, arg1, arg2)
121-
}
122-
123-
// Now mocks base method.
124-
func (m *MockMutableContext) Now(arg0 Component) time.Time {
125-
m.ctrl.T.Helper()
126-
ret := m.ctrl.Call(m, "Now", arg0)
127-
ret0, _ := ret[0].(time.Time)
128-
return ret0
129-
}
130-
131-
// Now indicates an expected call of Now.
132-
func (mr *MockMutableContextMockRecorder) Now(arg0 any) *gomock.Call {
133-
mr.mock.ctrl.T.Helper()
134-
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Now", reflect.TypeOf((*MockMutableContext)(nil).Now), arg0)
135-
}
136-
137-
// Ref mocks base method.
138-
func (m *MockMutableContext) Ref(arg0 Component) ([]byte, error) {
139-
m.ctrl.T.Helper()
140-
ret := m.ctrl.Call(m, "Ref", arg0)
141-
ret0, _ := ret[0].([]byte)
142-
ret1, _ := ret[1].(error)
143-
return ret0, ret1
144-
}
44+
MockContext
14545

146-
// Ref indicates an expected call of Ref.
147-
func (mr *MockMutableContextMockRecorder) Ref(arg0 any) *gomock.Call {
148-
mr.mock.ctrl.T.Helper()
149-
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Ref", reflect.TypeOf((*MockMutableContext)(nil).Ref), arg0)
46+
mu sync.Mutex
47+
Tasks []MockTask
15048
}
15149

152-
// getContext mocks base method.
153-
func (m *MockMutableContext) getContext() context.Context {
154-
m.ctrl.T.Helper()
155-
ret := m.ctrl.Call(m, "getContext")
156-
ret0, _ := ret[0].(context.Context)
157-
return ret0
50+
func (c *MockMutableContext) AddTask(component Component, attributes TaskAttributes, payload any) {
51+
c.mu.Lock()
52+
defer c.mu.Unlock()
53+
c.Tasks = append(c.Tasks, MockTask{component, attributes, payload})
15854
}
15955

160-
// getContext indicates an expected call of getContext.
161-
func (mr *MockMutableContextMockRecorder) getContext() *gomock.Call {
162-
mr.mock.ctrl.T.Helper()
163-
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "getContext", reflect.TypeOf((*MockMutableContext)(nil).getContext))
56+
type MockTask struct {
57+
Component Component
58+
Attributes TaskAttributes
59+
Payload any
16460
}

chasm/field_test.go

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,7 @@ func TestFieldSuite(t *testing.T) {
3737
func (s *fieldSuite) SetupTest() {
3838
s.initAssertions()
3939
s.controller = gomock.NewController(s.T())
40-
s.nodeBackend = NewMockNodeBackend(s.controller)
41-
s.nodeBackend.EXPECT().IsWorkflow().Return(false).AnyTimes()
40+
s.nodeBackend = &MockNodeBackend{}
4241

4342
s.logger = testlogger.NewTestLogger(s.T(), testlogger.FailOnAnyUnexpectedError)
4443
s.registry = NewRegistry(s.logger)
@@ -167,11 +166,11 @@ func (s *fieldSuite) setupComponentWithTree(rootComponent *TestComponent) (*Node
167166

168167
func (s *fieldSuite) TestDeferredPointerResolution() {
169168
tv := testvars.New(s.T())
170-
s.nodeBackend.EXPECT().NextTransitionCount().Return(int64(1)).AnyTimes()
171-
s.nodeBackend.EXPECT().GetCurrentVersion().Return(int64(1)).AnyTimes()
172-
s.nodeBackend.EXPECT().UpdateWorkflowStateStatus(gomock.Any(), gomock.Any()).AnyTimes()
173-
s.nodeBackend.EXPECT().GetWorkflowKey().Return(tv.Any().WorkflowKey()).AnyTimes()
174-
s.nodeBackend.EXPECT().AddTasks(gomock.Any()).AnyTimes()
169+
s.nodeBackend = &MockNodeBackend{
170+
HandleNextTransitionCount: func() int64 { return 1 },
171+
HandleGetCurrentVersion: func() int64 { return 1 },
172+
HandleGetWorkflowKey: tv.Any().WorkflowKey,
173+
}
175174

176175
// Create component structure that will simulate NewEntity scenario.
177176
sc2 := &TestSubComponent2{
@@ -241,11 +240,11 @@ func (s *fieldSuite) TestDeferredPointerResolution() {
241240

242241
func (s *fieldSuite) TestMixedPointerScenario() {
243242
tv := testvars.New(s.T())
244-
s.nodeBackend.EXPECT().NextTransitionCount().Return(int64(1)).AnyTimes()
245-
s.nodeBackend.EXPECT().GetCurrentVersion().Return(int64(1)).AnyTimes()
246-
s.nodeBackend.EXPECT().UpdateWorkflowStateStatus(gomock.Any(), gomock.Any()).AnyTimes()
247-
s.nodeBackend.EXPECT().GetWorkflowKey().Return(tv.Any().WorkflowKey()).AnyTimes()
248-
s.nodeBackend.EXPECT().AddTasks(gomock.Any()).AnyTimes()
243+
s.nodeBackend = &MockNodeBackend{
244+
HandleNextTransitionCount: func() int64 { return 1 },
245+
HandleGetCurrentVersion: func() int64 { return 1 },
246+
HandleGetWorkflowKey: tv.Any().WorkflowKey,
247+
}
249248

250249
existingComponent := &TestSubComponent11{
251250
SubComponent11Data: &protoMessageType{CreateRequestId: "existing-component"},
@@ -313,11 +312,11 @@ func (s *fieldSuite) TestMixedPointerScenario() {
313312

314313
func (s *fieldSuite) TestUnresolvableDeferredPointerError() {
315314
tv := testvars.New(s.T())
316-
s.nodeBackend.EXPECT().NextTransitionCount().Return(int64(1)).AnyTimes()
317-
s.nodeBackend.EXPECT().GetCurrentVersion().Return(int64(1)).AnyTimes()
318-
s.nodeBackend.EXPECT().UpdateWorkflowStateStatus(gomock.Any(), gomock.Any()).AnyTimes()
319-
s.nodeBackend.EXPECT().GetWorkflowKey().Return(tv.Any().WorkflowKey()).AnyTimes()
320-
s.nodeBackend.EXPECT().AddTasks(gomock.Any()).AnyTimes()
315+
s.nodeBackend = &MockNodeBackend{
316+
HandleNextTransitionCount: func() int64 { return 1 },
317+
HandleGetCurrentVersion: func() int64 { return 1 },
318+
HandleGetWorkflowKey: tv.Any().WorkflowKey,
319+
}
321320

322321
s.logger.(*testlogger.TestLogger).
323322
Expect(testlogger.Error, "failed to resolve deferred pointer during transaction close")

0 commit comments

Comments
 (0)