Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create new string-enum[] type #677

Merged
merged 23 commits into from
Sep 24, 2024
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 15 additions & 10 deletions temporalcli/commands.gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,15 +180,16 @@ type WorkflowStartOptions struct {
Cron string
FailExisting bool
StartDelay Duration
IdReusePolicy string
IdReusePolicy StringEnum
}

func (v *WorkflowStartOptions) buildFlags(cctx *CommandContext, f *pflag.FlagSet) {
f.StringVar(&v.Cron, "cron", "", "Cron schedule for the Workflow. Deprecated. Use Schedules instead.")
f.BoolVar(&v.FailExisting, "fail-existing", false, "Fail if the Workflow already exists.")
v.StartDelay = 0
f.Var(&v.StartDelay, "start-delay", "Delay before starting the Workflow Execution. Can't be used with cron schedules. If the Workflow receives a signal or update prior to this time, the Workflow Execution starts immediately.")
f.StringVar(&v.IdReusePolicy, "id-reuse-policy", "", "Re-use policy for the Workflow ID in new Workflow Executions. Options: AllowDuplicate, AllowDuplicateFailedOnly, RejectDuplicate, TerminateIfRunning.")
v.IdReusePolicy = NewStringEnum([]string{"AllowDuplicate", "AllowDuplicateFailedOnly", "RejectDuplicate", "TerminateIfRunning"}, "")
f.Var(&v.IdReusePolicy, "id-reuse-policy", "Re-use policy for the Workflow ID in new Workflow Executions. Accepted values: AllowDuplicate, AllowDuplicateFailedOnly, RejectDuplicate, TerminateIfRunning.")
}

type PayloadInputOptions struct {
Expand Down Expand Up @@ -242,7 +243,7 @@ type TemporalCommand struct {
Env string
EnvFile string
LogLevel StringEnum
LogFormat string
LogFormat StringEnum
Output StringEnum
TimeFormat StringEnum
Color StringEnum
Expand Down Expand Up @@ -272,7 +273,8 @@ func NewTemporalCommand(cctx *CommandContext) *TemporalCommand {
s.Command.PersistentFlags().StringVar(&s.EnvFile, "env-file", "", "Path to environment settings file. (defaults to `$HOME/.config/temporalio/temporal.yaml`).")
s.LogLevel = NewStringEnum([]string{"debug", "info", "warn", "error", "never"}, "info")
s.Command.PersistentFlags().Var(&s.LogLevel, "log-level", "Log level. Default is \"info\" for most commands and \"warn\" for `server start-dev`. Accepted values: debug, info, warn, error, never.")
s.Command.PersistentFlags().StringVar(&s.LogFormat, "log-format", "text", "Log format. Options are: text, json.")
s.LogFormat = NewStringEnum([]string{"text", "json", "pretty"}, "text")
s.Command.PersistentFlags().Var(&s.LogFormat, "log-format", "Log format. \"pretty\" is an alias for \"text\". This is for compatibility reasons, please use \"text\". Accepted values: text, json, pretty.")
s.Output = NewStringEnum([]string{"text", "json", "jsonl", "none"}, "text")
s.Command.PersistentFlags().VarP(&s.Output, "output", "o", "Non-logging data output format. Accepted values: text, json, jsonl, none.")
s.TimeFormat = NewStringEnum([]string{"relative", "iso", "raw"}, "relative")
Expand Down Expand Up @@ -1263,7 +1265,7 @@ type TemporalOperatorSearchAttributeCreateCommand struct {
Parent *TemporalOperatorSearchAttributeCommand
Command cobra.Command
Name []string
Type []string
Type StringEnumArray
}

func NewTemporalOperatorSearchAttributeCreateCommand(cctx *CommandContext, parent *TemporalOperatorSearchAttributeCommand) *TemporalOperatorSearchAttributeCreateCommand {
Expand All @@ -1280,7 +1282,8 @@ func NewTemporalOperatorSearchAttributeCreateCommand(cctx *CommandContext, paren
s.Command.Args = cobra.NoArgs
s.Command.Flags().StringArrayVar(&s.Name, "name", nil, "Search Attribute name. Required.")
_ = cobra.MarkFlagRequired(s.Command.Flags(), "name")
s.Command.Flags().StringArrayVar(&s.Type, "type", nil, "Search Attribute type. Options: Text, Keyword, Int, Double, Bool, Datetime, KeywordList. Required.")
s.Type = NewStringEnumArray([]string{"Text", "Keyword", "Int", "Double", "Bool", "Datetime", "KeywordList"}, []string{})
s.Command.Flags().Var(&s.Type, "type", "Search Attribute type. Accepted values: Text, Keyword, Int, Double, Bool, Datetime, KeywordList. Required.")
_ = cobra.MarkFlagRequired(s.Command.Flags(), "type")
s.Command.Run = func(c *cobra.Command, args []string) {
if err := s.run(cctx, args); err != nil {
Expand Down Expand Up @@ -1740,7 +1743,7 @@ type TemporalTaskQueueDescribeCommand struct {
Parent *TemporalTaskQueueCommand
Command cobra.Command
TaskQueue string
TaskQueueType []string
TaskQueueType StringEnumArray
SelectBuildId []string
SelectUnversioned bool
SelectAllActive bool
Expand All @@ -1765,7 +1768,8 @@ func NewTemporalTaskQueueDescribeCommand(cctx *CommandContext, parent *TemporalT
s.Command.Args = cobra.NoArgs
s.Command.Flags().StringVarP(&s.TaskQueue, "task-queue", "t", "", "Task Queue name. Required.")
_ = cobra.MarkFlagRequired(s.Command.Flags(), "task-queue")
s.Command.Flags().StringArrayVar(&s.TaskQueueType, "task-queue-type", nil, "Task Queue type. If not specified, all types are reported. Options are: workflow, activity, nexus.")
s.TaskQueueType = NewStringEnumArray([]string{"workflow", "activity", "nexus"}, []string{})
s.Command.Flags().Var(&s.TaskQueueType, "task-queue-type", "Task Queue type. If not specified, all types are reported. Accepted values: workflow, activity, nexus.")
s.Command.Flags().StringArrayVar(&s.SelectBuildId, "select-build-id", nil, "Filter the Task Queue based on Build ID.")
s.Command.Flags().BoolVar(&s.SelectUnversioned, "select-unversioned", false, "Include the unversioned queue.")
s.Command.Flags().BoolVar(&s.SelectAllActive, "select-all-active", false, "Include all active versions. A version is active if it had new tasks or polls recently.")
Expand Down Expand Up @@ -2604,7 +2608,7 @@ type TemporalWorkflowResetCommand struct {
EventId int
Reason string
ReapplyType StringEnum
ReapplyExclude []string
ReapplyExclude StringEnumArray
Type StringEnum
BuildId string
Query string
Expand All @@ -2630,7 +2634,8 @@ func NewTemporalWorkflowResetCommand(cctx *CommandContext, parent *TemporalWorkf
_ = cobra.MarkFlagRequired(s.Command.Flags(), "reason")
s.ReapplyType = NewStringEnum([]string{"All", "Signal", "None"}, "All")
s.Command.Flags().Var(&s.ReapplyType, "reapply-type", "Types of events to re-apply after reset point. Deprecated. Use --reapply-exclude instead. Accepted values: All, Signal, None.")
s.Command.Flags().StringArrayVar(&s.ReapplyExclude, "reapply-exclude", nil, "Exclude these event types from re-application. Options: All, Signal, Update.")
s.ReapplyExclude = NewStringEnumArray([]string{"All", "Signal", "Update"}, []string{})
s.Command.Flags().Var(&s.ReapplyExclude, "reapply-exclude", "Exclude these event types from re-application. Accepted values: All, Signal, Update.")
s.Type = NewStringEnum([]string{"FirstWorkflowTask", "LastWorkflowTask", "LastContinuedAsNew", "BuildId"}, "")
s.Command.Flags().VarP(&s.Type, "type", "t", "The event type for the reset. Accepted values: FirstWorkflowTask, LastWorkflowTask, LastContinuedAsNew, BuildId.")
s.Command.Flags().StringVar(&s.BuildId, "build-id", "", "A Build ID. Use only with the BuildId `--type`. Resets the first Workflow task processed by this ID. By default, this reset may be in a prior run, earlier than a Continue as New point.")
Expand Down
6 changes: 3 additions & 3 deletions temporalcli/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -442,9 +442,9 @@ func (c *TemporalCommand) preRun(cctx *CommandContext) error {
return fmt.Errorf("invalid log level %q: %w", c.LogLevel.Value, err)
}
var handler slog.Handler
switch c.LogFormat {
switch c.LogFormat.Value {
// We have a "pretty" alias for compatibility
case "", "text", "pretty":
case "text", "pretty":
handler = slog.NewTextHandler(cctx.Options.Stderr, &slog.HandlerOptions{
Level: level,
// Remove the TZ
Expand All @@ -458,7 +458,7 @@ func (c *TemporalCommand) preRun(cctx *CommandContext) error {
case "json":
handler = slog.NewJSONHandler(cctx.Options.Stderr, &slog.HandlerOptions{Level: level})
default:
return fmt.Errorf("invalid log format %q", c.LogFormat)
return fmt.Errorf("unreachable: invalid log format %q", c.LogFormat.Value)
}
cctx.Logger = slog.New(handler)
}
Expand Down
4 changes: 2 additions & 2 deletions temporalcli/commands.operator_search_attribute.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ func (c *TemporalOperatorSearchAttributeCreateCommand) run(cctx *CommandContext,
return fmt.Errorf("unable to get existing search attributes: %w", err)
}

searchAttributes := make(map[string]enums.IndexedValueType, len(c.Type))
for i, saType := range c.Type {
searchAttributes := make(map[string]enums.IndexedValueType, len(c.Type.Values))
for i, saType := range c.Type.Values {
saName := c.Name[i]
typeInt, err := searchAttributeTypeStringToEnum(saType)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion temporalcli/commands.taskqueue.go
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ func (c *TemporalTaskQueueDescribeCommand) run(cctx *CommandContext, args []stri
}

var taskQueueTypes []client.TaskQueueType
for _, t := range c.TaskQueueType {
for _, t := range c.TaskQueueType.Values {
var taskQueueType client.TaskQueueType
switch t {
case "workflow":
Expand Down
4 changes: 2 additions & 2 deletions temporalcli/commands.workflow_exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -275,10 +275,10 @@ func buildStartOptions(sw *SharedWorkflowStartOptions, w *WorkflowStartOptions)
WorkflowExecutionErrorWhenAlreadyStarted: w.FailExisting,
StartDelay: w.StartDelay.Duration(),
}
if w.IdReusePolicy != "" {
if w.IdReusePolicy.Value != "" {
var err error
o.WorkflowIDReusePolicy, err = stringToProtoEnum[enums.WorkflowIdReusePolicy](
w.IdReusePolicy, enums.WorkflowIdReusePolicy_shorthandValue, enums.WorkflowIdReusePolicy_value)
w.IdReusePolicy.Value, enums.WorkflowIdReusePolicy_shorthandValue, enums.WorkflowIdReusePolicy_value)
if err != nil {
return o, fmt.Errorf("invalid workflow ID reuse policy: %w", err)
}
Expand Down
4 changes: 2 additions & 2 deletions temporalcli/commands.workflow_reset.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ func (c *TemporalWorkflowResetCommand) doWorkflowReset(cctx *CommandContext, cl
}

reapplyExcludes := make([]enums.ResetReapplyExcludeType, 0)
for _, exclude := range c.ReapplyExclude {
for _, exclude := range c.ReapplyExclude.Values {
if strings.ToLower(exclude) == "all" {
for _, excludeType := range enums.ResetReapplyExcludeType_value {
if excludeType == 0 {
Expand All @@ -97,7 +97,7 @@ func (c *TemporalWorkflowResetCommand) doWorkflowReset(cctx *CommandContext, cl

reapplyType := enums.RESET_REAPPLY_TYPE_SIGNAL
if c.ReapplyType.Value != "All" {
if len(c.ReapplyExclude) > 0 {
if len(c.ReapplyExclude.Values) > 0 {
return errors.New("cannot specify --reapply-type and --reapply-exclude at the same time")
}
reapplyType, err = enums.ResetReapplyTypeFromString(c.ReapplyType.Value)
Expand Down
20 changes: 20 additions & 0 deletions temporalcli/commandsgen/code.go
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,8 @@ func (o *Option) writeStructField(w *codeWriter) error {
goDataType = "[]string"
case "string-enum":
goDataType = "StringEnum"
case "string-enum[]":
goDataType = "StringEnumArray"
default:
return fmt.Errorf("unrecognized data type %v", o.Type)
}
Expand Down Expand Up @@ -368,6 +370,24 @@ func (o *Option) writeFlagBuilding(selfVar, flagVar string, w *codeWriter) error
w.writeLinef("%v.%v = NewStringEnum([]string{%v}, %q)",
selfVar, o.fieldName(), strings.Join(pieces, ", "), o.Default)
flagMeth = "Var"
case "string-enum[]":
if len(o.EnumValues) == 0 {
return fmt.Errorf("missing enum values")
}
// Create enum
pieces := make([]string, len(o.EnumValues))
for i, enumVal := range o.EnumValues {
pieces[i] = fmt.Sprintf("%q", enumVal)
}

if o.Default != "" {
w.writeLinef("%v.%v = NewStringEnumArray([]string{%v}, %q)",
selfVar, o.fieldName(), strings.Join(pieces, ", "), o.Default)
} else {
w.writeLinef("%v.%v = NewStringEnumArray([]string{%v}, []string{})",
selfVar, o.fieldName(), strings.Join(pieces, ", "))
}
flagMeth = "Var"
default:
return fmt.Errorf("unrecognized data type %v", o.Type)
}
Expand Down
59 changes: 37 additions & 22 deletions temporalcli/commandsgen/commands.yml
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,8 @@
# option-sets: A list of option sets. (string[])

# * name, summary, and descrption are required fields. All other fields are optional.
# * Available option types are `bool`, `duration`, `int`, `float`, `string`, `string[]`,
# `string-enum`, or `timestamp`.
# * Available option types are `bool`, `duration`, `int`, `float`, `string`, `string[]`,
# `string-enum`, string-enum[], or `timestamp`.
# * Include a new-line after each command entry.

# OPTION SET OVERVIEW
Expand Down Expand Up @@ -161,10 +161,14 @@ commands:
Default is "info" for most commands and "warn" for `server start-dev`.
default: info
- name: log-format
type: string
type: string-enum
description: |
Log format.
Options are: text, json.
"pretty" is an alias for "text". This is for compatibility reasons, please use "text".
enum-values:
- text
- json
- pretty
yuandrew marked this conversation as resolved.
Show resolved Hide resolved
default: text
- name: output
type: string-enum
Expand Down Expand Up @@ -1026,10 +1030,16 @@ commands:
description: Search Attribute name.
required: true
- name: type
type: string[]
description: |
Search Attribute type.
Options: Text, Keyword, Int, Double, Bool, Datetime, KeywordList.
type: string-enum[]
description: Search Attribute type.
enum-values:
- Text
- Keyword
- Int
- Double
- Bool
- Datetime
- KeywordList
required: true

yuandrew marked this conversation as resolved.
Show resolved Hide resolved
- name: temporal operator search-attribute list
Expand Down Expand Up @@ -1543,10 +1553,12 @@ commands:
description: Task Queue name.
required: true
- name: task-queue-type
type: string[]
description: |
Task Queue type. If not specified, all types are reported.
Options are: workflow, activity, nexus.
type: string-enum[]
description: Task Queue type. If not specified, all types are reported.
enum-values:
- workflow
- activity
- nexus
- name: select-build-id
type: string[]
description: Filter the Task Queue based on Build ID.
Expand Down Expand Up @@ -2375,7 +2387,6 @@ commands:
- not_open
- not_completed_cleanly


- name: temporal workflow reset
summary: Move Workflow Execution history point
description: |
Expand Down Expand Up @@ -2435,10 +2446,12 @@ commands:
- None
default: All
- name: reapply-exclude
type: string[]
description: |
Exclude these event types from re-application.
Options: All, Signal, Update.
type: string-enum[]
description: Exclude these event types from re-application.
enum-values:
- All
- Signal
- Update
- name: type
short: t
type: string-enum
Expand Down Expand Up @@ -3052,11 +3065,13 @@ option-sets:
If the Workflow receives a signal or update prior to this time, the Workflow
Execution starts immediately.
- name: id-reuse-policy
type: string
description: |
Re-use policy for the Workflow ID in new Workflow Executions.
Options: AllowDuplicate, AllowDuplicateFailedOnly, RejectDuplicate, TerminateIfRunning.

type: string-enum
description: Re-use policy for the Workflow ID in new Workflow Executions.
enum-values:
- AllowDuplicate
- AllowDuplicateFailedOnly
- RejectDuplicate
- TerminateIfRunning
- name: payload-input
options:
- name: input
Expand Down
4 changes: 2 additions & 2 deletions temporalcli/commandsgen/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,8 @@ func (o *Option) processSection() error {
}

if len(o.EnumValues) != 0 {
if o.Type != "string-enum" {
return fmt.Errorf("enum-values can only specified for string-enum type")
if o.Type != "string-enum" && o.Type != "string-enum[]" {
return fmt.Errorf("enum-values can only specified for string-enum and string-enum[] types")
}
// Check default enum values
if o.Default != "" && !slices.Contains(o.EnumValues, o.Default) {
Expand Down
34 changes: 34 additions & 0 deletions temporalcli/strings.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,40 @@ func (s *StringEnum) Set(p string) error {

func (*StringEnum) Type() string { return "string" }

type StringEnumArray struct {
// maps lower case value to original case
Allowed map[string]string
// values in original case
Values []string
}

func NewStringEnumArray(allowed []string, values []string) StringEnumArray {
// store allowed values in lower case so we can do case-insensitive comparison
var allowedMap = make(map[string]string)
for _, str := range allowed {
allowedMap[strings.ToLower(str)] = str
}

return StringEnumArray{Allowed: allowedMap, Values: values}
}

func (s *StringEnumArray) String() string { return strings.Join(s.Values, ",") }

func (s *StringEnumArray) Set(p string) error {
val, ok := s.Allowed[strings.ToLower(p)]
if !ok {
values := make([]string, 0, len(s.Allowed))
for _, v := range s.Allowed {
values = append(values, v)
}
Comment on lines +59 to +62
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just a suggestion if you like

Suggested change
values := make([]string, 0, len(s.Allowed))
for _, v := range s.Allowed {
values = append(values, v)
}
values := maps.Values(s.Allowed)

(from "golang.org/x/exp/maps")

Copy link
Member

@cretz cretz Sep 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from "golang.org/x/exp/maps"

I think it's in the standard library now w/ the iter stuff (so not a slice necessarily)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They didn't include Keys and Values from x/exp/maps in the stdlib package. I'm not sure why, they're pretty useful 🤷

return fmt.Errorf("invalid value: %s, allowed values are: %s", p, strings.Join(values, ", "))
}
s.Values = append(s.Values, val)
return nil
}

func (*StringEnumArray) Type() string { return "string" }

func stringToProtoEnum[T ~int32](s string, maps ...map[string]int32) (T, error) {
// Go over each map looking, if not there, use first map to build set of
// strings required
Expand Down
Loading