diff --git a/converter/value.go b/converter/value.go index a8ddc939a..893409845 100644 --- a/converter/value.go +++ b/converter/value.go @@ -57,3 +57,22 @@ func (v RawValue) MarshalJSON() ([]byte, error) { func (v *RawValue) UnmarshalJSON(b []byte) error { return fmt.Errorf("RawValue is not JSON serializable") } + +// ValuesPayloads is an optional interface that EncodedValue and EncodedValues may implement +// to expose the underlying commonpb.Payloads without decoding. +type ValuesPayloads interface { + // Payloads gets the underlying commonpb.Payloads + Payloads() *commonpb.Payloads +} + +// GetPayloads acts as a helper to extract the raw *commonpb.Payloads from an EncodedValue +// or EncodedValues, if the underlying implementation supports it via the ValuesPayloads interface. +// If the underlying implementation does not support it, this returns nil. +// +// Exposed as: [go.temporal.io/sdk/converter.GetPayloads] +func GetPayloads(encoded interface{}) *commonpb.Payloads { + if vp, ok := encoded.(ValuesPayloads); ok { + return vp.Payloads() + } + return nil +} diff --git a/internal/error.go b/internal/error.go index bfabb811d..b052c1151 100644 --- a/internal/error.go +++ b/internal/error.go @@ -566,6 +566,10 @@ func (e *temporalError) failure() *failurepb.Failure { return e.originalFailure } +func (e *temporalError) Failure() *failurepb.Failure { + return e.originalFailure +} + // IsCanceledError returns whether error in CanceledError. func IsCanceledError(err error) bool { var canceledErr *CanceledError diff --git a/internal/workflow.go b/internal/workflow.go index e29ece31a..0256e743d 100644 --- a/internal/workflow.go +++ b/internal/workflow.go @@ -2132,6 +2132,11 @@ func (b EncodedValue) HasValue() bool { return b.value != nil } +// Payloads gets the underlying commonpb.Payloads +func (b EncodedValue) Payloads() *commonpb.Payloads { + return b.value +} + // SideEffect executes the provided function once, records its result into the workflow history. The recorded result on // history will be returned without executing the provided function during replay. This guarantees the deterministic // requirement for workflow as the exact same result will be returned in replay. diff --git a/internal/workflow_testsuite.go b/internal/workflow_testsuite.go index 3d15a2c59..faeb63da8 100644 --- a/internal/workflow_testsuite.go +++ b/internal/workflow_testsuite.go @@ -98,11 +98,16 @@ func (b EncodedValues) Get(valuePtr ...interface{}) error { return b.dataConverter.FromPayloads(b.values, valuePtr...) } -// HasValues return whether there are values +// HasValues return whether there are values encoded. func (b EncodedValues) HasValues() bool { return b.values != nil } +// Payloads gets the underlying commonpb.Payloads +func (b EncodedValues) Payloads() *commonpb.Payloads { + return b.values +} + // Get extract data from encoded data to desired value type. valuePtr is pointer to the actual value type. func (b ErrorDetailsValues) Get(valuePtr ...interface{}) error { if !b.HasValues() { @@ -117,11 +122,16 @@ func (b ErrorDetailsValues) Get(valuePtr ...interface{}) error { return nil } -// HasValues return whether there are values. +// HasValues return whether there are values encoded. func (b ErrorDetailsValues) HasValues() bool { return len(b) != 0 } +// Payloads gets the underlying commonpb.Payloads +func (b ErrorDetailsValues) Payloads() *commonpb.Payloads { + return nil +} + // NewTestWorkflowEnvironment creates a new instance of TestWorkflowEnvironment. Use the returned TestWorkflowEnvironment // to run your workflow in the test environment. func (s *WorkflowTestSuite) NewTestWorkflowEnvironment() *TestWorkflowEnvironment { diff --git a/mocks/Value.go b/mocks/Value.go index 7b3c9dd91..95e9faba7 100644 --- a/mocks/Value.go +++ b/mocks/Value.go @@ -4,7 +4,10 @@ package mocks -import "github.com/stretchr/testify/mock" +import ( + "github.com/stretchr/testify/mock" + commonpb "go.temporal.io/api/common/v1" +) // Value is an autogenerated mock type for the Value type type Value struct { @@ -47,6 +50,26 @@ func (_m *Value) HasValue() bool { return r0 } +// Payloads provides a mock function with given fields: +func (_m *Value) Payloads() *commonpb.Payloads { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Payloads") + } + + var r0 *commonpb.Payloads + if rf, ok := ret.Get(0).(func() *commonpb.Payloads); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*commonpb.Payloads) + } + } + + return r0 +} + // NewEncodedValue creates a new instance of Value. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. func NewEncodedValue(t interface {