Skip to content

Commit f7d16a3

Browse files
committed
Reuse existing AliasFields, use Generic chasm list return type, clean up misc code
1 parent 0a59101 commit f7d16a3

34 files changed

+1312
-868
lines changed

chasm/engine.go

Lines changed: 25 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ type Engine interface {
5757
reflect.Type,
5858
*ListChasmExecutionsRequest,
5959
...ListChasmExecutionsOption,
60-
) (*ListChasmExecutionsResponse, error)
60+
) (*ListChasmExecutionsResponse[*commonpb.Payload], error)
6161

6262
CountExecutions(
6363
context.Context,
@@ -66,7 +66,11 @@ type Engine interface {
6666
) (*CountChasmExecutionsResponse, error)
6767
}
6868

69-
type ChasmExecutionInfo struct {
69+
type ListChasmExecutionsResponse[M proto.Message] struct {
70+
Executions []*ChasmExecutionInfo[M]
71+
NextPageToken []byte
72+
}
73+
type ChasmExecutionInfo[M proto.Message] struct {
7074
BusinessID string
7175
RunID string
7276
StartTime time.Time
@@ -77,7 +81,7 @@ type ChasmExecutionInfo struct {
7781
ChasmSearchAttributes SearchAttributesMap
7882
CustomSearchAttributes map[string]*commonpb.Payload
7983
Memo *commonpb.Memo
80-
ChasmMemo *commonpb.Payload
84+
ChasmMemo M
8185
}
8286

8387
type ListChasmExecutionsRequest struct {
@@ -103,11 +107,6 @@ func WithPagination(
103107
}
104108
}
105109

106-
type ListChasmExecutionsResponse struct {
107-
Executions []*ChasmExecutionInfo
108-
NextPageToken []byte
109-
}
110-
111110
type CountChasmExecutionsRequest struct {
112111
NamespaceID string
113112
NamespaceName string
@@ -118,18 +117,6 @@ type CountChasmExecutionsResponse struct {
118117
Count int64
119118
}
120119

121-
// TypedChasmRunInfo provides type-safe access to ChasmMemo
122-
type TypedChasmExecutionInfo[M proto.Message] struct {
123-
*ChasmExecutionInfo
124-
TypedChasmMemo M
125-
}
126-
127-
// TypedListChasmRunsResponse provides type-safe response with unmarshaled memos
128-
type TypedListChasmExecutionsResponse[M proto.Message] struct {
129-
Executions []*TypedChasmExecutionInfo[M]
130-
NextPageToken []byte
131-
}
132-
133120
type BusinessIDReusePolicy int
134121

135122
const (
@@ -391,31 +378,36 @@ func ListExecutions[C Component, M proto.Message](
391378
ctx context.Context,
392379
request *ListChasmExecutionsRequest,
393380
opts ...ListChasmExecutionsOption,
394-
) (*TypedListChasmExecutionsResponse[M], error) {
381+
) (*ListChasmExecutionsResponse[M], error) {
395382
archetypeType := reflect.TypeFor[C]()
396383
response, err := engineFromContext(ctx).ListExecutions(ctx, archetypeType, request, opts...)
397384
if err != nil {
398385
return nil, err
399386
}
400387

401388
// Convert response, unmarshaling ChasmMemo to type M
402-
typedExecutions := make([]*TypedChasmExecutionInfo[M], len(response.Executions))
389+
executions := make([]*ChasmExecutionInfo[M], len(response.Executions))
403390
for i, execution := range response.Executions {
404-
var typedMemo M
405-
if len(execution.ChasmMemo.Data) > 0 {
406-
msg := reflect.New(reflect.TypeFor[M]()).Interface().(M)
407-
if err := proto.Unmarshal(execution.ChasmMemo.Data, msg); err == nil {
408-
typedMemo = msg
391+
chasmMemo := reflect.New(reflect.TypeFor[M]()).Interface().(M)
392+
if err := proto.Unmarshal(execution.ChasmMemo.Data, chasmMemo); err == nil {
393+
executions[i] = &ChasmExecutionInfo[M]{
394+
BusinessID: execution.BusinessID,
395+
RunID: execution.RunID,
396+
StartTime: execution.StartTime,
397+
CloseTime: execution.CloseTime,
398+
HistoryLength: execution.HistoryLength,
399+
HistorySizeBytes: execution.HistorySizeBytes,
400+
StateTransitionCount: execution.StateTransitionCount,
401+
ChasmSearchAttributes: execution.ChasmSearchAttributes,
402+
CustomSearchAttributes: execution.CustomSearchAttributes,
403+
Memo: execution.Memo,
404+
ChasmMemo: chasmMemo,
409405
}
410406
}
411-
typedExecutions[i] = &TypedChasmExecutionInfo[M]{
412-
ChasmExecutionInfo: execution,
413-
TypedChasmMemo: typedMemo,
414-
}
415407
}
416408

417-
return &TypedListChasmExecutionsResponse[M]{
418-
Executions: typedExecutions,
409+
return &ListChasmExecutionsResponse[M]{
410+
Executions: executions,
419411
NextPageToken: response.NextPageToken,
420412
}, nil
421413
}

chasm/engine_mock.go

Lines changed: 3 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

chasm/ref.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ func (r *ComponentRef) ArchetypeID(
7676
return r.archetypeID, nil
7777
}
7878

79-
rc, ok := registry.componentOf(r.entityGoType)
79+
rc, ok := registry.ComponentOf(r.entityGoType)
8080
if !ok {
8181
return 0, serviceerror.NewInternal("unknown chasm component type: " + r.entityGoType.String())
8282
}
@@ -85,6 +85,17 @@ func (r *ComponentRef) ArchetypeID(
8585
return r.archetypeID, nil
8686
}
8787

88+
func GetArchetypeIDFromType(
89+
registry *Registry,
90+
archetypeType reflect.Type,
91+
) (ArchetypeID, error) {
92+
rc, ok := registry.ComponentOf(archetypeType)
93+
if !ok {
94+
return UnspecifiedArchetypeID, serviceerror.NewInternal("unknown chasm component type: " + archetypeType.String())
95+
}
96+
return rc.componentID, nil
97+
}
98+
8899
// ShardingKey returns the sharding key used for determining the shardID of the run
89100
// that contains the referenced component.
90101
// TODO: remove this method and ShardingKey concept, we don't need this functionality.

chasm/search_attribute.go

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
package chasm
22

33
import (
4+
"errors"
45
"fmt"
56
"time"
67

78
enumspb "go.temporal.io/api/enums/v1"
9+
"go.temporal.io/api/serviceerror"
810
"go.temporal.io/server/common/searchattribute/defs"
911
)
1012

@@ -382,3 +384,167 @@ func (s SearchAttributeKeywordList) Value(value []string) SearchAttributeKeyValu
382384
Value: VisibilityValueStringSlice(value),
383385
}
384386
}
387+
388+
// SearchAttributeMap wraps search attribute values with type-safe access.
389+
type SearchAttributesMap struct {
390+
values map[string]VisibilityValue
391+
}
392+
393+
// NewSearchAttributeMap creates a new SearchAttributeMap from raw values.
394+
func NewSearchAttributesMap(values map[string]VisibilityValue) SearchAttributesMap {
395+
return SearchAttributesMap{values: values}
396+
}
397+
398+
// GetBool returns the boolean value for a given SearchAttributeBool. If not found or map is nil, second parameter is false.
399+
func (m SearchAttributesMap) GetBool(sa SearchAttributeBool) (bool, bool) {
400+
if m.values == nil {
401+
return false, false
402+
}
403+
404+
alias := sa.definition().alias
405+
boolValue, ok := m.values[alias].(VisibilityValueBool)
406+
if !ok {
407+
return false, false
408+
}
409+
410+
return bool(boolValue), true
411+
}
412+
413+
// GetInt returns the int value for a given SearchAttributeInt. If not found or map is nil, second parameter is false.
414+
func (m SearchAttributesMap) GetInt(sa SearchAttributeInt) (int, bool) {
415+
if m.values == nil {
416+
return 0, false
417+
}
418+
419+
alias := sa.definition().alias
420+
intValue, ok := m.values[alias].(VisibilityValueInt)
421+
if !ok {
422+
return 0, false
423+
}
424+
425+
return int(intValue), true
426+
}
427+
428+
// GetDouble returns the double value for a given SearchAttributeDouble. If not found or map is nil, second parameter is false.
429+
func (m SearchAttributesMap) GetDouble(sa SearchAttributeDouble) (float64, bool) {
430+
if m.values == nil {
431+
return 0, false
432+
}
433+
434+
alias := sa.definition().alias
435+
doubleValue, ok := m.values[alias].(VisibilityValueFloat64)
436+
if !ok {
437+
return 0, false
438+
}
439+
440+
return float64(doubleValue), true
441+
}
442+
443+
// GetKeyword returns the string value for a given SearchAttributeKeyword. If not found or map is nil, second parameter is false.
444+
func (m SearchAttributesMap) GetKeyword(sa SearchAttributeKeyword) (string, bool) {
445+
if m.values == nil {
446+
return "", false
447+
}
448+
449+
alias := sa.definition().alias
450+
stringValue, ok := m.values[alias].(VisibilityValueString)
451+
if !ok {
452+
return "", false
453+
}
454+
455+
return string(stringValue), true
456+
}
457+
458+
// GetDateTime returns the time value for a given SearchAttributeDateTime. If not found or map is nil, second parameter is false.
459+
func (m SearchAttributesMap) GetDateTime(sa SearchAttributeDateTime) (time.Time, bool) {
460+
if m.values == nil {
461+
return time.Time{}, false
462+
}
463+
464+
alias := sa.definition().alias
465+
timeValue, ok := m.values[alias].(VisibilityValueTime)
466+
if !ok {
467+
return time.Time{}, false
468+
}
469+
470+
return time.Time(timeValue), true
471+
}
472+
473+
// GetKeywordList returns the string list value for a given SearchAttributeKeywordList. If not found or map is nil, second parameter is false.
474+
func (m SearchAttributesMap) GetKeywordList(sa SearchAttributeKeywordList) ([]string, bool) {
475+
if m.values == nil {
476+
return nil, false
477+
}
478+
479+
alias := sa.definition().alias
480+
keywordListValue, ok := m.values[alias].(VisibilityValueStringSlice)
481+
if !ok {
482+
return nil, false
483+
}
484+
485+
return []string(keywordListValue), true
486+
}
487+
488+
// convertToVisibilityValue converts a value to VisibilityValue based on its runtime type.
489+
func convertToVisibilityValue(value interface{}) VisibilityValue {
490+
switch val := value.(type) {
491+
case int:
492+
return VisibilityValueInt64(int64(val))
493+
case int32:
494+
return VisibilityValueInt64(int64(val))
495+
case int64:
496+
return VisibilityValueInt64(val)
497+
case float32:
498+
return VisibilityValueFloat64(float64(val))
499+
case float64:
500+
return VisibilityValueFloat64(val)
501+
case bool:
502+
return VisibilityValueBool(val)
503+
case time.Time:
504+
return VisibilityValueTime(val)
505+
case string:
506+
// Try to parse as datetime first
507+
if parsedTime, err := time.Parse(time.RFC3339, val); err == nil {
508+
return VisibilityValueTime(parsedTime)
509+
}
510+
return VisibilityValueString(val)
511+
case []byte:
512+
return VisibilityValueByteSlice(val)
513+
case []string:
514+
return VisibilityValueStringSlice(val)
515+
default:
516+
// Return as string if type is unknown
517+
return VisibilityValueString(fmt.Sprintf("%v", val))
518+
}
519+
}
520+
521+
// AliasChasmSearchAttributes converts search attribute values to VisibilityValue and aliases field names.
522+
// It takes a map of field names to interface{} values, converts them to VisibilityValue based on their runtime type,
523+
// and then aliases the field names using the mapper.
524+
func AliasChasmSearchAttributes(
525+
chasmSearchAttributes map[string]interface{},
526+
mapper *VisibilitySearchAttributesMapper,
527+
) (map[string]VisibilityValue, error) {
528+
if len(chasmSearchAttributes) == 0 {
529+
return nil, nil
530+
}
531+
532+
chasmSAs := make(map[string]VisibilityValue, len(chasmSearchAttributes))
533+
for fieldName, value := range chasmSearchAttributes {
534+
visibilityValue := convertToVisibilityValue(value)
535+
aliasName, err := mapper.Alias(fieldName)
536+
if err != nil {
537+
// Silently ignore serviceerror.InvalidArgument because it indicates unmapped field, search attribute is not registered.
538+
// IMPORTANT: AliasChasmSearchAttributes InvalidArgument indicates a bug in the code, not a user error.
539+
// Chasm search attributes must be registered with the CHASM Registry using the WithSearchAttributes() option.
540+
var invalidArgumentErr *serviceerror.InvalidArgument
541+
if !errors.As(err, &invalidArgumentErr) {
542+
return nil, err
543+
}
544+
continue
545+
}
546+
chasmSAs[aliasName] = visibilityValue
547+
}
548+
549+
return chasmSAs, nil
550+
}

chasm/test_visibility.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ package chasm
22

33
import enumspb "go.temporal.io/api/enums/v1"
44

5-
// NewVisibilitySearchAttributesMapper creates a new VisibilitySearchAttributesMapper.
5+
// NewTestVisibilitySearchAttributesMapper creates a new VisibilitySearchAttributesMapper.
66
// For testing only.
7-
func NewVisibilitySearchAttributesMapper(
7+
func NewTestVisibilitySearchAttributesMapper(
88
fieldToAlias map[string]string,
99
saTypeMap map[string]enumspb.IndexedValueType,
1010
) *VisibilitySearchAttributesMapper {

chasm/tree_test.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -924,7 +924,6 @@ func (s *nodeSuite) TestApplyMutation() {
924924
s.Len(root.currentMemo, 1)
925925
s.Contains(root.currentSA, "TemporalDatetime01")
926926
s.True(root.currentSA["TemporalDatetime01"].(VisibilityValueTime).Equal(VisibilityValueTime(now)))
927-
s.True(root.currentMemo[TestComponentStartTimeMemoKey].(VisibilityValueTime).Equal(VisibilityValueTime(now)))
928927

929928
// Validate the "child" node got updated.
930929
nodeSC1, ok := root.children["SubComponent1"]
@@ -1059,7 +1058,6 @@ func (s *nodeSuite) TestApplySnapshot() {
10591058
s.Len(root.currentMemo, 1)
10601059
s.Contains(root.currentSA, "TemporalDatetime01")
10611060
s.True(root.currentSA["TemporalDatetime01"].(VisibilityValueTime).Equal(VisibilityValueTime(now.AsTime())))
1062-
s.True(root.currentMemo[TestComponentStartTimeMemoKey].(VisibilityValueTime).Equal(VisibilityValueTime(now.AsTime())))
10631061
}
10641062

10651063
func (s *nodeSuite) TestApplyMutation_OutOfOrder() {

0 commit comments

Comments
 (0)