diff --git a/temporalcloudcli/commands.gen.go b/temporalcloudcli/commands.gen.go index 156aa0c..e62bd87 100644 --- a/temporalcloudcli/commands.gen.go +++ b/temporalcloudcli/commands.gen.go @@ -4112,6 +4112,7 @@ type CloudNexusEndpointDeleteCommand struct { AsyncOperationOptions ResourceVersionOptions Name string + Id string } func NewCloudNexusEndpointDeleteCommand(cctx *CommandContext, parent *CloudNexusEndpointCommand) *CloudNexusEndpointDeleteCommand { @@ -4119,11 +4120,15 @@ func NewCloudNexusEndpointDeleteCommand(cctx *CommandContext, parent *CloudNexus s.Parent = parent s.Command.DisableFlagsInUseLine = true s.Command.Use = "delete [flags]" - s.Command.Short = "Delete a Nexus Endpoint" - s.Command.Long = "Delete a Nexus Endpoint on the Cloud Account." + s.Command.Short = "Delete a Nexus Endpoint by name or ID" + if hasHighlighting { + s.Command.Long = "Delete a Nexus Endpoint on the Cloud Account.\nSpecify either \x1b[1m--name\x1b[0m or \x1b[1m--id\x1b[0m (exactly one is required)." + } else { + s.Command.Long = "Delete a Nexus Endpoint on the Cloud Account.\nSpecify either `--name` or `--id` (exactly one is required)." + } s.Command.Args = cobra.NoArgs - s.Command.Flags().StringVar(&s.Name, "name", "", "The name of the Nexus Endpoint to delete. Required.") - _ = cobra.MarkFlagRequired(s.Command.Flags(), "name") + s.Command.Flags().StringVar(&s.Name, "name", "", "The name of the Nexus Endpoint to delete.") + s.Command.Flags().StringVar(&s.Id, "id", "", "The ID of the Nexus Endpoint to delete.") s.ClientOptions.BuildFlags(s.Command.Flags()) s.AsyncOperationOptions.BuildFlags(s.Command.Flags()) s.ResourceVersionOptions.BuildFlags(s.Command.Flags()) @@ -4200,6 +4205,7 @@ type CloudNexusEndpointUpdateCommand struct { AsyncOperationOptions ResourceVersionOptions Name string + Id string TargetNamespace string TargetTaskQueue string Description string @@ -4212,15 +4218,15 @@ func NewCloudNexusEndpointUpdateCommand(cctx *CommandContext, parent *CloudNexus s.Parent = parent s.Command.DisableFlagsInUseLine = true s.Command.Use = "update [flags]" - s.Command.Short = "Update an existing Nexus Endpoint" + s.Command.Short = "Update an existing Nexus Endpoint by name or ID" if hasHighlighting { - s.Command.Long = "Update an existing Nexus Endpoint on the Cloud Account.\nAn endpoint name is used in workflow code to invoke Nexus operations.\n\nThe endpoint is patched leaving any existing fields for which flags are not provided\nas they were.\n\nExample:\n\n\x1b[1mtemporal cloud nexus endpoint update --name my-endpoint --target-namespace new-ns.my-account --target-task-queue new-tq\x1b[0m" + s.Command.Long = "Update an existing Nexus Endpoint on the Cloud Account.\nAn endpoint name is used in workflow code to invoke Nexus operations.\nSpecify either \x1b[1m--name\x1b[0m or \x1b[1m--id\x1b[0m to identify the endpoint (exactly one is required).\n\nThe endpoint is patched leaving any existing fields for which flags are not provided\nas they were.\n\nExample:\n\n\x1b[1mtemporal cloud nexus endpoint update --name my-endpoint --target-namespace new-ns.my-account --target-task-queue new-tq\x1b[0m" } else { - s.Command.Long = "Update an existing Nexus Endpoint on the Cloud Account.\nAn endpoint name is used in workflow code to invoke Nexus operations.\n\nThe endpoint is patched leaving any existing fields for which flags are not provided\nas they were.\n\nExample:\n\n```\ntemporal cloud nexus endpoint update --name my-endpoint --target-namespace new-ns.my-account --target-task-queue new-tq\n```" + s.Command.Long = "Update an existing Nexus Endpoint on the Cloud Account.\nAn endpoint name is used in workflow code to invoke Nexus operations.\nSpecify either `--name` or `--id` to identify the endpoint (exactly one is required).\n\nThe endpoint is patched leaving any existing fields for which flags are not provided\nas they were.\n\nExample:\n\n```\ntemporal cloud nexus endpoint update --name my-endpoint --target-namespace new-ns.my-account --target-task-queue new-tq\n```" } s.Command.Args = cobra.NoArgs - s.Command.Flags().StringVar(&s.Name, "name", "", "The name of the Nexus Endpoint to update. Required.") - _ = cobra.MarkFlagRequired(s.Command.Flags(), "name") + s.Command.Flags().StringVar(&s.Name, "name", "", "The name of the Nexus Endpoint to update.") + s.Command.Flags().StringVar(&s.Id, "id", "", "The ID of the Nexus Endpoint to update.") s.Command.Flags().StringVar(&s.TargetNamespace, "target-namespace", "", "The namespace in which a handler worker will be polling for Nexus tasks.") s.Command.Flags().StringVar(&s.TargetTaskQueue, "target-task-queue", "", "The task queue on which a handler worker will be polling for Nexus tasks.") s.Command.Flags().StringVar(&s.Description, "description", "", "An optional endpoint description in markdown format.") diff --git a/temporalcloudcli/commands.nexus.go b/temporalcloudcli/commands.nexus.go index c18f762..1330233 100644 --- a/temporalcloudcli/commands.nexus.go +++ b/temporalcloudcli/commands.nexus.go @@ -74,32 +74,14 @@ func (c *CloudNexusEndpointListCommand) run(cctx *CommandContext, _ []string) er } func (c *CloudNexusEndpointGetCommand) run(cctx *CommandContext, _ []string) error { - if c.Name == "" && c.Id == "" { - return errors.New("either --name or --id is required") - } - if c.Name != "" && c.Id != "" { - return errors.New("--name and --id are mutually exclusive") - } - client, err := cctx.GetCloudClient(c.ClientOptions) if err != nil { return err } - var endpoint *nexusv1.Endpoint - if c.Id != "" { - res, err := client.GetNexusEndpoint(cctx, &cloudservice.GetNexusEndpointRequest{ - EndpointId: c.Id, - }) - if err != nil { - return err - } - endpoint = res.Endpoint - } else { - endpoint, err = getNexusEndpointByName(cctx, client, c.Name) - if err != nil { - return err - } + endpoint, err := resolveNexusEndpoint(cctx, client, c.Name, c.Id) + if err != nil { + return err } return cctx.Printer.PrintResource(endpoint, printer.PrintResourceOptions{}) @@ -160,7 +142,7 @@ func (c *CloudNexusEndpointDeleteCommand) run(cctx *CommandContext, _ []string) return err } - endpoint, err := getNexusEndpointByName(cctx, client, c.Name) + endpoint, err := resolveNexusEndpoint(cctx, client, c.Name, c.Id) if err != nil { return err } @@ -200,7 +182,7 @@ func (c *CloudNexusEndpointUpdateCommand) run(cctx *CommandContext, _ []string) return err } - endpoint, err := getNexusEndpointByName(cctx, client, c.Name) + endpoint, err := resolveNexusEndpoint(cctx, client, c.Name, c.Id) if err != nil { return err } @@ -240,6 +222,30 @@ func (c *CloudNexusEndpointUpdateCommand) run(cctx *CommandContext, _ []string) return cctx.GetPoller(client, c.AsyncOperationOptions).HandleUpdateOperation(cctx, resp, err) } +// resolveNexusEndpoint looks up a Nexus Endpoint by name or ID. Exactly one of name/id must be set. +func resolveNexusEndpoint( + cctx *CommandContext, + client cloudservice.CloudServiceClient, + name, id string, +) (*nexusv1.Endpoint, error) { + if name == "" && id == "" { + return nil, errors.New("either --name or --id is required") + } + if name != "" && id != "" { + return nil, errors.New("--name and --id are mutually exclusive") + } + if id != "" { + res, err := client.GetNexusEndpoint(cctx, &cloudservice.GetNexusEndpointRequest{ + EndpointId: id, + }) + if err != nil { + return nil, err + } + return res.Endpoint, nil + } + return getNexusEndpointByName(cctx, client, name) +} + // getNexusEndpointByName looks up a Nexus Endpoint by name using the list RPC with a name filter. func getNexusEndpointByName( cctx *CommandContext, diff --git a/temporalcloudcli/commands.nexus_test.go b/temporalcloudcli/commands.nexus_test.go index 28001a7..d0ea1cd 100644 --- a/temporalcloudcli/commands.nexus_test.go +++ b/temporalcloudcli/commands.nexus_test.go @@ -332,6 +332,29 @@ func TestDeleteNexusEndpoint(t *testing.T) { promptOptions: temporalcloudcli.TestPromptOptions{ExpectPromptYes: true, PromptResult: true}, pollerOptions: temporalcloudcli.TestAsyncPollerOptions{AsyncOperationID: "op-del"}, }, + { + name: "SuccessDeleteEndpointById", + cmd: temporalcloudcli.CloudNexusEndpointDeleteCommand{Id: "ep-123"}, + cloudClientExpectations: func(c *cloudmock.MockCloudServiceClient) { + c.EXPECT(). + GetNexusEndpoint(mock.Anything, &cloudservice.GetNexusEndpointRequest{ + EndpointId: "ep-123", + }, mock.Anything). + Return(&cloudservice.GetNexusEndpointResponse{ + Endpoint: testEndpoint, + }, nil) + c.EXPECT(). + DeleteNexusEndpoint(mock.Anything, &cloudservice.DeleteNexusEndpointRequest{ + EndpointId: testEndpoint.Id, + ResourceVersion: "v1", + }, mock.Anything). + Return(&cloudservice.DeleteNexusEndpointResponse{ + AsyncOperation: &operation.AsyncOperation{Id: "op-del"}, + }, nil) + }, + promptOptions: temporalcloudcli.TestPromptOptions{ExpectPromptYes: true, PromptResult: true}, + pollerOptions: temporalcloudcli.TestAsyncPollerOptions{AsyncOperationID: "op-del"}, + }, { name: "NotFound", cmd: temporalcloudcli.CloudNexusEndpointDeleteCommand{Name: "missing"}, @@ -430,6 +453,21 @@ func TestDeleteNexusEndpoint(t *testing.T) { } } +func TestDeleteNexusEndpoint_NeitherNameNorId(t *testing.T) { + temporalcloudcli.TestCommand(t, &temporalcloudcli.CloudNexusEndpointDeleteCommand{}, temporalcloudcli.TestCommandOptions{ + ExpectedError: "either --name or --id is required", + }) +} + +func TestDeleteNexusEndpoint_BothNameAndId(t *testing.T) { + temporalcloudcli.TestCommand(t, &temporalcloudcli.CloudNexusEndpointDeleteCommand{ + Name: "my-endpoint", + Id: "ep-123", + }, temporalcloudcli.TestCommandOptions{ + ExpectedError: "--name and --id are mutually exclusive", + }) +} + // --- UpdateNexusEndpoint --- // TestUpdateNexusEndpoint uses table-driven tests for the update nexus endpoint command. @@ -493,6 +531,35 @@ func TestUpdateNexusEndpoint(t *testing.T) { promptOptions: temporalcloudcli.TestPromptOptions{ExpectPrompApply: true, PromptResult: true}, asyncPollerOptions: temporalcloudcli.TestAsyncPollerOptions{AsyncOperationID: "op-upd"}, }, + { + name: "TargetNamespaceById", + setupCmd: func(cmd *temporalcloudcli.CloudNexusEndpointUpdateCommand) { + cmd.Name = "" + cmd.Id = "ep-123" + cmd.Command.Flags().StringVar(&cmd.TargetNamespace, "target-namespace", "", "") + require.NoError(t, cmd.Command.Flags().Set("target-namespace", "new-ns")) + }, + cloudClientExpectations: func(c *cloudmock.MockCloudServiceClient) { + c.EXPECT(). + GetNexusEndpoint(mock.Anything, &cloudservice.GetNexusEndpointRequest{ + EndpointId: "ep-123", + }, mock.Anything). + Return(&cloudservice.GetNexusEndpointResponse{ + Endpoint: existingEndpoint, + }, nil) + c.EXPECT(). + UpdateNexusEndpoint(mock.Anything, mock.MatchedBy(func(req *cloudservice.UpdateNexusEndpointRequest) bool { + return req.EndpointId == "ep-123" && + req.Spec.TargetSpec.GetWorkerTargetSpec().NamespaceId == "new-ns" && + req.ResourceVersion == "v1" + }), mock.Anything). + Return(&cloudservice.UpdateNexusEndpointResponse{ + AsyncOperation: &operation.AsyncOperation{Id: "op-upd"}, + }, nil) + }, + promptOptions: temporalcloudcli.TestPromptOptions{ExpectPrompApply: true, PromptResult: true}, + asyncPollerOptions: temporalcloudcli.TestAsyncPollerOptions{AsyncOperationID: "op-upd"}, + }, { name: "TargetTaskQueue", setupCmd: func(cmd *temporalcloudcli.CloudNexusEndpointUpdateCommand) { @@ -674,6 +741,21 @@ func TestUpdateNexusEndpoint(t *testing.T) { } } +func TestUpdateNexusEndpoint_NeitherNameNorId(t *testing.T) { + temporalcloudcli.TestCommand(t, &temporalcloudcli.CloudNexusEndpointUpdateCommand{}, temporalcloudcli.TestCommandOptions{ + ExpectedError: "either --name or --id is required", + }) +} + +func TestUpdateNexusEndpoint_BothNameAndId(t *testing.T) { + temporalcloudcli.TestCommand(t, &temporalcloudcli.CloudNexusEndpointUpdateCommand{ + Name: "my-endpoint", + Id: "ep-123", + }, temporalcloudcli.TestCommandOptions{ + ExpectedError: "--name and --id are mutually exclusive", + }) +} + // --- Allowed Namespace Commands --- // newTestEndpointWithPolicies returns a fresh endpoint with existing allowed namespace policy specs. diff --git a/temporalcloudcli/commands.yml b/temporalcloudcli/commands.yml index 38d0072..a7c2197 100644 --- a/temporalcloudcli/commands.yml +++ b/temporalcloudcli/commands.yml @@ -1630,9 +1630,10 @@ commands: Mutually exclusive with --description. - name: cloud nexus endpoint delete - summary: Delete a Nexus Endpoint + summary: Delete a Nexus Endpoint by name or ID description: | Delete a Nexus Endpoint on the Cloud Account. + Specify either `--name` or `--id` (exactly one is required). has-init: false option-sets: - client @@ -1641,15 +1642,19 @@ commands: options: - name: name type: string - required: true description: | The name of the Nexus Endpoint to delete. + - name: id + type: string + description: | + The ID of the Nexus Endpoint to delete. - name: cloud nexus endpoint update - summary: Update an existing Nexus Endpoint + summary: Update an existing Nexus Endpoint by name or ID description: | Update an existing Nexus Endpoint on the Cloud Account. An endpoint name is used in workflow code to invoke Nexus operations. + Specify either `--name` or `--id` to identify the endpoint (exactly one is required). The endpoint is patched leaving any existing fields for which flags are not provided as they were. @@ -1667,9 +1672,12 @@ commands: options: - name: name type: string - required: true description: | The name of the Nexus Endpoint to update. + - name: id + type: string + description: | + The ID of the Nexus Endpoint to update. - name: target-namespace type: string description: |