Files
temporal/common/util/proto.go
Sean Kane f95c865cc0 Implement Pause/Unpause/Reset/UpdateOptions for standalone activities (#10106)
## What changed?
This branch implements the activity operator commands feature — a set of
server-initiated control APIs (PauseActivityExecution,
UnpauseActivityExecution, ResetActivityExecution,
UpdateActivityExecutionOptions) for both workflow-embedded and
standalone activities.

- Pause, Unpause, Reset and UpdateOptions for standalone activities plus
idempotency via RequestId
- common/activityoptions package (common/activityoptions/merge.go):
extracted mergeActivityOptions from the update-options handler into a
shared package (now also used by CHASM activity component).
- Metric renames: ActivityPauseRequests → ActivityPause,
ActivityResetRequests → ActivityReset, ActivityUnpauseRequests →
ActivityUnpause, ActivityUpdateOptionsRequests → ActivityUpdateOptions.
- RPC boilerplate, proto generation, and matching/frontend wiring for
the new APIs.

## Why?
Activity operator APIs existed for workflow-embedded activities but were
not wired up for standalone activities.

## How did you test it?
- [X] built
- [X] run locally and tested manually
- [X] covered by existing tests
- [X] added new unit test(s)
- [X] added new functional test(s)

## Potential risks
Minimal, this is a new feature so it won't break users.

---------

Co-authored-by: Dan Davison <dan.davison@temporal.io>
Co-authored-by: Fred Tzeng <fred.tzeng@temporal.io>
2026-07-20 15:55:26 +00:00

66 lines
1.6 KiB
Go

package util
import (
"strings"
"golang.org/x/text/cases"
"golang.org/x/text/language"
"google.golang.org/protobuf/types/known/fieldmaskpb"
)
func ConvertPathToCamel(input string) []string {
var pathParts []string
for path := range strings.SplitSeq(input, ".") {
// Split by "_" and convert each word to Title Case (CamelCase)
var b strings.Builder
j := 0
for word := range strings.SplitSeq(path, "_") {
if j > 0 {
b.WriteString(cases.Title(language.Und).String(strings.ToLower(word)))
} else {
// lowercase the first letter
if len(word) > 0 {
b.WriteString(strings.ToLower(word[:1]))
if len(word) > 1 {
b.WriteString(word[1:])
}
}
}
j++
}
// Join the words into a CamelCase substring
pathParts = append(pathParts, b.String())
}
// Join all CamelCase substrings back with "."
return pathParts
}
func ParseFieldMask(mask *fieldmaskpb.FieldMask) map[string]struct{} {
fieldMaskPaths := make(map[string]struct{})
for _, path := range mask.Paths {
pathParts := ConvertPathToCamel(path)
jsonPath := strings.Join(pathParts, ".")
fieldMaskPaths[jsonPath] = struct{}{}
}
return fieldMaskPaths
}
func FieldMaskHasSubPath(fieldMaskPaths map[string]struct{}, path string) bool {
prefix := strings.TrimSuffix(path, ".") + "."
for field := range fieldMaskPaths {
if strings.HasPrefix(field, prefix) {
return true
}
}
return false
}
func FieldMaskHasPathOrSubPath(fieldMaskPaths map[string]struct{}, path string) bool {
if _, ok := fieldMaskPaths[path]; ok {
return true
}
return FieldMaskHasSubPath(fieldMaskPaths, path)
}