From b68e2610b8e4e3b7f0474531175dda24e0cded3d Mon Sep 17 00:00:00 2001 From: Amelia Dong Date: Fri, 29 May 2026 14:04:33 -0700 Subject: [PATCH 1/2] Add CLI support for SCIM token management --- .cli-generation-checksum | 2 +- cmd/lint/main.go | 2 + go.mod | 2 +- go.sum | 4 +- internal/command.go | 2 +- internal/organization/command.go | 13 ++- internal/organization/command_scim_token.go | 90 +++++++++++++++++++ .../organization/command_scim_token_create.go | 50 +++++++++++ .../organization/command_scim_token_delete.go | 46 ++++++++++ .../organization/command_scim_token_list.go | 51 +++++++++++ pkg/ccloudv2/org.go | 47 ++++++++++ .../org/scim_token/create_scim_token.json | 16 ++++ .../scim_token/read_created_scim_token.json | 16 ++++ .../create-expire-duration-mins.golden | 7 ++ .../output/org/scim-token/create.golden | 7 ++ .../org/scim-token/delete-invalid.golden | 1 + .../org/scim-token/delete-multiple.golden | 1 + .../org/scim-token/delete-no-force.golden | 1 + .../output/org/scim-token/delete.golden | 1 + .../output/org/scim-token/list-json.golden | 9 ++ .../output/org/scim-token/list-yaml.golden | 5 ++ .../output/org/scim-token/list.golden | 3 + test/fixtures/output/organization/help.golden | 1 + .../scim-token/create-help.golden | 14 +++ .../scim-token/delete-help.golden | 13 +++ .../organization/scim-token/help.golden | 19 ++++ .../organization/scim-token/list-help.golden | 13 +++ test/live/scim_token_live_test.go | 46 ++++++++++ test/scim_token_test.go | 53 +++++++++++ test/test-server/ccloudv2_router.go | 2 + test/test-server/scim_token_handler.go | 66 ++++++++++++++ 31 files changed, 594 insertions(+), 9 deletions(-) create mode 100644 internal/organization/command_scim_token.go create mode 100644 internal/organization/command_scim_token_create.go create mode 100644 internal/organization/command_scim_token_delete.go create mode 100644 internal/organization/command_scim_token_list.go create mode 100644 test/fixtures/input/org/scim_token/create_scim_token.json create mode 100644 test/fixtures/input/org/scim_token/read_created_scim_token.json create mode 100644 test/fixtures/output/org/scim-token/create-expire-duration-mins.golden create mode 100644 test/fixtures/output/org/scim-token/create.golden create mode 100644 test/fixtures/output/org/scim-token/delete-invalid.golden create mode 100644 test/fixtures/output/org/scim-token/delete-multiple.golden create mode 100644 test/fixtures/output/org/scim-token/delete-no-force.golden create mode 100644 test/fixtures/output/org/scim-token/delete.golden create mode 100644 test/fixtures/output/org/scim-token/list-json.golden create mode 100644 test/fixtures/output/org/scim-token/list-yaml.golden create mode 100644 test/fixtures/output/org/scim-token/list.golden create mode 100644 test/fixtures/output/organization/scim-token/create-help.golden create mode 100644 test/fixtures/output/organization/scim-token/delete-help.golden create mode 100644 test/fixtures/output/organization/scim-token/help.golden create mode 100644 test/fixtures/output/organization/scim-token/list-help.golden create mode 100644 test/live/scim_token_live_test.go create mode 100644 test/scim_token_test.go create mode 100644 test/test-server/scim_token_handler.go diff --git a/.cli-generation-checksum b/.cli-generation-checksum index 57f994b270..aa296f747b 100644 --- a/.cli-generation-checksum +++ b/.cli-generation-checksum @@ -1 +1 @@ -4b4b331c9a9a8e2a28db070b9537b1b55c480d15a4a64543b296ed127f147123 +682da461ca244227d8b1287879dee7544336b4f4c28a02bb3a250b4ee65abf5d diff --git a/cmd/lint/main.go b/cmd/lint/main.go index 80cf81531a..1e03e20945 100644 --- a/cmd/lint/main.go +++ b/cmd/lint/main.go @@ -189,6 +189,7 @@ var properNouns = []string{ "ksqlDB Server", "ksqlDB", "Node.js", + "Organization", "Prometheus", "Python", "Real Time Context Engine", @@ -337,6 +338,7 @@ var vocabWords = []string{ "savepoints", "scala", "schemas", + "scim", "server", "signup", "siv", diff --git a/go.mod b/go.mod index e6e95ff4d9..8bc0fc67c0 100644 --- a/go.mod +++ b/go.mod @@ -45,7 +45,7 @@ require ( github.com/confluentinc/ccloud-sdk-go-v2/networking-gateway v0.7.0 github.com/confluentinc/ccloud-sdk-go-v2/networking-ip v0.2.0 github.com/confluentinc/ccloud-sdk-go-v2/networking-privatelink v0.3.0 - github.com/confluentinc/ccloud-sdk-go-v2/org v0.9.0 + github.com/confluentinc/ccloud-sdk-go-v2/org v0.13.0 github.com/confluentinc/ccloud-sdk-go-v2/provider-integration v0.2.0 github.com/confluentinc/ccloud-sdk-go-v2/rtce v0.1.0 github.com/confluentinc/ccloud-sdk-go-v2/service-quota v0.2.0 diff --git a/go.sum b/go.sum index 2754e64a8e..329147950a 100644 --- a/go.sum +++ b/go.sum @@ -250,8 +250,8 @@ github.com/confluentinc/ccloud-sdk-go-v2/networking-ip v0.2.0 h1:ZHNF2DeqVlNPuKG github.com/confluentinc/ccloud-sdk-go-v2/networking-ip v0.2.0/go.mod h1:KTShFBZA7WG8LcxlWjJpoZFdWkJ+uOw3dDuwAHs5eKU= github.com/confluentinc/ccloud-sdk-go-v2/networking-privatelink v0.3.0 h1:mC0E1nKUt57AxMM4Lpdfd+KA/YZwJVwro9ER+dCUFi8= github.com/confluentinc/ccloud-sdk-go-v2/networking-privatelink v0.3.0/go.mod h1:GIHF2cYOUKx+6ycYokr4i8E4cuNBC22xqvO/IhqZ31U= -github.com/confluentinc/ccloud-sdk-go-v2/org v0.9.0 h1:FtaqHX0kBTK7fCQK+9SJcOso+XpWCWzY2roT3gBQGfw= -github.com/confluentinc/ccloud-sdk-go-v2/org v0.9.0/go.mod h1:X0uaTYPp+mr19W1R/Z1LuB1ePZJZrH7kxnQckDx6zoc= +github.com/confluentinc/ccloud-sdk-go-v2/org v0.13.0 h1:/H/6SmonnvOzhjLA/6xSTSvq6N/uMuoPc66VEp/qxV8= +github.com/confluentinc/ccloud-sdk-go-v2/org v0.13.0/go.mod h1:BNnhFTvGhJSbyaklv0YkoTDWUdiWppLyu2hzfdxOWYM= github.com/confluentinc/ccloud-sdk-go-v2/provider-integration v0.2.0 h1:UN2a+aqYhk95ro+wVLkeB/8W7n+UV2KsE3jNFbbDCSw= github.com/confluentinc/ccloud-sdk-go-v2/provider-integration v0.2.0/go.mod h1:TzompS9F0G6awN5xMC+nguNG8ULElN5UqX2XOBOIPuM= github.com/confluentinc/ccloud-sdk-go-v2/rtce v0.1.0 h1:OBa2vm09bOG1oojOP1vNj8V7+M2AfUkYP1sRQ+xlRm4= diff --git a/internal/command.go b/internal/command.go index d9b7f05690..c5fcad48d5 100644 --- a/internal/command.go +++ b/internal/command.go @@ -126,7 +126,7 @@ func NewConfluentCommand(cfg *config.Config) *cobra.Command { cmd.AddCommand(login.New(cfg, prerunner, ccloudClientFactory, mdsClientManager, loginCredentialsManager, loginOrganizationManager, authTokenHandler)) cmd.AddCommand(logout.New(cfg, prerunner, authTokenHandler)) cmd.AddCommand(network.New(cfg, prerunner)) - cmd.AddCommand(organization.New(prerunner)) + cmd.AddCommand(organization.New(cfg, prerunner)) cmd.AddCommand(plugin.New(cfg, prerunner)) cmd.AddCommand(prompt.New(cfg)) cmd.AddCommand(providerintegration.New(prerunner)) diff --git a/internal/organization/command.go b/internal/organization/command.go index 557a924eb7..8f71e48932 100644 --- a/internal/organization/command.go +++ b/internal/organization/command.go @@ -4,13 +4,14 @@ import ( "github.com/spf13/cobra" pcmd "github.com/confluentinc/cli/v4/pkg/cmd" + "github.com/confluentinc/cli/v4/pkg/config" ) type command struct { *pcmd.AuthenticatedCLICommand } -func New(prerunner pcmd.PreRunner) *cobra.Command { +func New(cfg *config.Config, prerunner pcmd.PreRunner) *cobra.Command { cmd := &cobra.Command{ Use: "organization", Aliases: []string{"org"}, @@ -20,9 +21,13 @@ func New(prerunner pcmd.PreRunner) *cobra.Command { c := &command{pcmd.NewAuthenticatedCLICommand(cmd, prerunner)} - cmd.AddCommand(c.newDescribeCommand()) - cmd.AddCommand(c.newListCommand()) - cmd.AddCommand(c.newUpdateCommand()) + cmd.AddCommand( + c.newDescribeCommand(), + c.newListCommand(), + c.newUpdateCommand(), + newScimTokenCommand(cfg, prerunner), + // cli-tfgen:cli-subcommands + ) return cmd } diff --git a/internal/organization/command_scim_token.go b/internal/organization/command_scim_token.go new file mode 100644 index 0000000000..1a51612c03 --- /dev/null +++ b/internal/organization/command_scim_token.go @@ -0,0 +1,90 @@ +// Code generated by cli-terraform-generator; DO NOT EDIT. + +package organization + +import ( + "time" + + "github.com/spf13/cobra" + + orgv2 "github.com/confluentinc/ccloud-sdk-go-v2/org/v2" + + pcmd "github.com/confluentinc/cli/v4/pkg/cmd" + "github.com/confluentinc/cli/v4/pkg/config" + "github.com/confluentinc/cli/v4/pkg/output" +) + +type scimTokenCommand struct { + *pcmd.AuthenticatedCLICommand +} + +type scimTokenOut struct { + ID string `human:"ID" serialized:"id"` + ConnectionName string `human:"Connection Name" serialized:"connection_name"` + Token string `human:"Token" serialized:"token"` + CreatedAt string `human:"Created At" serialized:"created_at"` + ExpiresAt string `human:"Expires At" serialized:"expires_at"` +} + +func newScimTokenCommand(cfg *config.Config, prerunner pcmd.PreRunner) *cobra.Command { //nolint:unparam + cmd := &cobra.Command{ + Use: "scim-token", + Aliases: []string{"st"}, + Short: "Manage organization scim tokens.", + Annotations: map[string]string{pcmd.RunRequirement: pcmd.RequireNonAPIKeyCloudLogin}, + } + + c := &scimTokenCommand{ + AuthenticatedCLICommand: pcmd.NewAuthenticatedCLICommand(cmd, prerunner), + } + + cmd.AddCommand( + c.newCreateCommand(), + c.newDeleteCommand(), + c.newListCommand(), + ) + + return cmd +} + +func printScimToken(cmd *cobra.Command, scimToken orgv2.OrgV2ScimToken) error { + table := output.NewTable(cmd) + out := &scimTokenOut{ + ID: scimToken.GetId(), + ConnectionName: scimToken.GetConnectionName(), + Token: scimToken.GetToken(), + CreatedAt: scimToken.GetCreatedAt().Format(time.RFC3339), + ExpiresAt: scimToken.GetExpiresAt().Format(time.RFC3339), + } + table.Add(out) + return table.Print() +} + +func (c *scimTokenCommand) validArgs(cmd *cobra.Command, args []string) []string { + if len(args) > 0 { + return nil + } + + return c.validArgsMultiple(cmd, args) +} + +func (c *scimTokenCommand) validArgsMultiple(cmd *cobra.Command, args []string) []string { + if err := c.PersistentPreRunE(cmd, args); err != nil { + return nil + } + + return c.autocompleteScimTokens() +} + +func (c *scimTokenCommand) autocompleteScimTokens() []string { + scimTokens, err := c.V2Client.ListOrgScimTokens() + if err != nil { + return nil + } + + suggestions := make([]string, len(scimTokens)) + for i, scimToken := range scimTokens { + suggestions[i] = scimToken.GetId() + } + return suggestions +} diff --git a/internal/organization/command_scim_token_create.go b/internal/organization/command_scim_token_create.go new file mode 100644 index 0000000000..a908b929ad --- /dev/null +++ b/internal/organization/command_scim_token_create.go @@ -0,0 +1,50 @@ +// Code generated by cli-terraform-generator; DO NOT EDIT. + +package organization + +import ( + "github.com/spf13/cobra" + + orgv2 "github.com/confluentinc/ccloud-sdk-go-v2/org/v2" + + pcmd "github.com/confluentinc/cli/v4/pkg/cmd" + "github.com/confluentinc/cli/v4/pkg/errors" +) + +func (c *scimTokenCommand) newCreateCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "create", + Short: "Create an organization scim token.", + Args: cobra.NoArgs, + RunE: c.create, + } + + // Required flags + + // Optional flags + cmd.Flags().Int32("expire-duration-mins", 0, "The duration in minutes after which the token expires. Defaults to 6 months (259200 minutes) if not specified. Minimum: 1 month (43200 minutes). Maximum: 2 years (1051200 minutes).") + + pcmd.AddContextFlag(cmd, c.CLICommand) + pcmd.AddOutputFlag(cmd) + + return cmd +} + +func (c *scimTokenCommand) create(cmd *cobra.Command, args []string) error { + createReq := orgv2.InlineObject{} + + expireDurationMins, err := cmd.Flags().GetInt32("expire-duration-mins") + if err != nil { + return err + } + if expireDurationMins != 0 { + createReq.ExpireDurationMins = orgv2.PtrInt32(expireDurationMins) + } + + scimToken, httpResp, err := c.V2Client.CreateOrgScimToken(createReq) + if err != nil { + return errors.CatchCCloudV2Error(err, httpResp) + } + + return printScimToken(cmd, scimToken) +} diff --git a/internal/organization/command_scim_token_delete.go b/internal/organization/command_scim_token_delete.go new file mode 100644 index 0000000000..5cdbf6af1d --- /dev/null +++ b/internal/organization/command_scim_token_delete.go @@ -0,0 +1,46 @@ +// Code generated by cli-terraform-generator; DO NOT EDIT. + +package organization + +import ( + "github.com/spf13/cobra" + + pcmd "github.com/confluentinc/cli/v4/pkg/cmd" + "github.com/confluentinc/cli/v4/pkg/deletion" +) + +func (c *scimTokenCommand) newDeleteCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "delete [id-2] ... [id-n]", + Short: "Delete one or more organization scim tokens.", + Args: cobra.MinimumNArgs(1), + ValidArgsFunction: pcmd.NewValidArgsFunction(c.validArgsMultiple), + RunE: c.delete, + } + + // Required flags + + // Optional flags + + pcmd.AddContextFlag(cmd, c.CLICommand) + pcmd.AddForceFlag(cmd) + + return cmd +} + +func (c *scimTokenCommand) delete(cmd *cobra.Command, args []string) error { + existenceFunc := func(primaryId string) bool { + return true + } + + if err := deletion.ValidateAndConfirm(cmd, args, existenceFunc, "organization scim token"); err != nil { + return err + } + + deleteFunc := func(primaryId string) error { + return c.V2Client.DeleteOrgScimToken(primaryId) + } + + _, err := deletion.Delete(cmd, args, deleteFunc, "organization scim token") + return err +} diff --git a/internal/organization/command_scim_token_list.go b/internal/organization/command_scim_token_list.go new file mode 100644 index 0000000000..8d816f9573 --- /dev/null +++ b/internal/organization/command_scim_token_list.go @@ -0,0 +1,51 @@ +// Code generated by cli-terraform-generator; DO NOT EDIT. + +package organization + +import ( + "time" + + "github.com/spf13/cobra" + + pcmd "github.com/confluentinc/cli/v4/pkg/cmd" + "github.com/confluentinc/cli/v4/pkg/output" +) + +func (c *scimTokenCommand) newListCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "list", + Short: "List organization scim tokens.", + Args: cobra.NoArgs, + RunE: c.list, + } + + // Required flags + + // Optional flags + + pcmd.AddContextFlag(cmd, c.CLICommand) + pcmd.AddOutputFlag(cmd) + + return cmd +} + +func (c *scimTokenCommand) list(cmd *cobra.Command, _ []string) error { + + scimTokens, err := c.V2Client.ListOrgScimTokens() + if err != nil { + return err + } + + list := output.NewList(cmd) + for _, scimToken := range scimTokens { + out := &scimTokenOut{ + ID: scimToken.GetId(), + ConnectionName: scimToken.GetConnectionName(), + Token: scimToken.GetToken(), + CreatedAt: scimToken.GetCreatedAt().Format(time.RFC3339), + ExpiresAt: scimToken.GetExpiresAt().Format(time.RFC3339), + } + list.Add(out) + } + return list.Print() +} diff --git a/pkg/ccloudv2/org.go b/pkg/ccloudv2/org.go index 835c9d0568..acd95e3682 100644 --- a/pkg/ccloudv2/org.go +++ b/pkg/ccloudv2/org.go @@ -106,3 +106,50 @@ func (c *Client) executeListOrganizations(pageToken string) (orgv2.OrgV2Organiza } return req.Execute() } + +// ===== org scim tokens API calls ===== + +func (c *Client) CreateOrgScimToken(req orgv2.InlineObject) (orgv2.OrgV2ScimToken, *http.Response, error) { + createReq := c.OrgClient.ScimTokensOrgV2Api. + CreateOrgV2ScimToken(c.orgApiContext()). + InlineObject(req) + return createReq.Execute() +} + +func (c *Client) DeleteOrgScimToken(id string) error { + deleteReq := c.OrgClient.ScimTokensOrgV2Api. + DeleteOrgV2ScimToken(c.orgApiContext(), id) + httpResp, err := deleteReq.Execute() + return errors.CatchCCloudV2Error(err, httpResp) +} + +func (c *Client) ListOrgScimTokens() ([]orgv2.OrgV2ScimToken, error) { + var list []orgv2.OrgV2ScimToken + + done := false + pageToken := "" + for !done { + page, httpResp, err := c.executeListScimTokens(pageToken) + if err != nil { + return nil, errors.CatchCCloudV2Error(err, httpResp) + } + list = append(list, page.GetData()...) + + pageToken, done, err = extractNextPageToken(page.GetMetadata().Next) + if err != nil { + return nil, err + } + } + + return list, nil +} + +func (c *Client) executeListScimTokens(pageToken string) (orgv2.OrgV2ScimTokenList, *http.Response, error) { + req := c.OrgClient.ScimTokensOrgV2Api. + ListOrgV2ScimTokens(c.orgApiContext()). + PageSize(ccloudV2ListPageSize) + if pageToken != "" { + req = req.PageToken(pageToken) + } + return req.Execute() +} diff --git a/test/fixtures/input/org/scim_token/create_scim_token.json b/test/fixtures/input/org/scim_token/create_scim_token.json new file mode 100644 index 0000000000..09ac05bfb8 --- /dev/null +++ b/test/fixtures/input/org/scim_token/create_scim_token.json @@ -0,0 +1,16 @@ +{ + "api_version": "org/v2", + "connection_name": "test-connection", + "created_at": "2026-04-16T10:00:00Z", + "expires_at": "2026-10-16T10:00:00Z", + "id": "dlz-f3a90de", + "kind": "ScimToken", + "metadata": { + "created_at": "2006-01-02T15:04:05-07:00", + "deleted_at": "2006-01-02T15:04:05-07:00", + "resource_name": "crn://confluent.cloud/organization=9bb441c4-edef-46ac-8a41-c49e44a3fd9a/scim-token=st-12345", + "self": "https://api.confluent.cloud/org/v2/scim-tokens/st-12345", + "updated_at": "2006-01-02T15:04:05-07:00" + }, + "token": "cflt-scim_eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." +} diff --git a/test/fixtures/input/org/scim_token/read_created_scim_token.json b/test/fixtures/input/org/scim_token/read_created_scim_token.json new file mode 100644 index 0000000000..09ac05bfb8 --- /dev/null +++ b/test/fixtures/input/org/scim_token/read_created_scim_token.json @@ -0,0 +1,16 @@ +{ + "api_version": "org/v2", + "connection_name": "test-connection", + "created_at": "2026-04-16T10:00:00Z", + "expires_at": "2026-10-16T10:00:00Z", + "id": "dlz-f3a90de", + "kind": "ScimToken", + "metadata": { + "created_at": "2006-01-02T15:04:05-07:00", + "deleted_at": "2006-01-02T15:04:05-07:00", + "resource_name": "crn://confluent.cloud/organization=9bb441c4-edef-46ac-8a41-c49e44a3fd9a/scim-token=st-12345", + "self": "https://api.confluent.cloud/org/v2/scim-tokens/st-12345", + "updated_at": "2006-01-02T15:04:05-07:00" + }, + "token": "cflt-scim_eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." +} diff --git a/test/fixtures/output/org/scim-token/create-expire-duration-mins.golden b/test/fixtures/output/org/scim-token/create-expire-duration-mins.golden new file mode 100644 index 0000000000..f48e0d17ed --- /dev/null +++ b/test/fixtures/output/org/scim-token/create-expire-duration-mins.golden @@ -0,0 +1,7 @@ ++-----------------+---------------------------------------------------+ +| ID | dlz-f3a90de | +| Connection Name | test-connection | +| Token | cflt-scim_eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... | +| Created At | 2026-04-16T10:00:00Z | +| Expires At | 2026-10-16T10:00:00Z | ++-----------------+---------------------------------------------------+ diff --git a/test/fixtures/output/org/scim-token/create.golden b/test/fixtures/output/org/scim-token/create.golden new file mode 100644 index 0000000000..f48e0d17ed --- /dev/null +++ b/test/fixtures/output/org/scim-token/create.golden @@ -0,0 +1,7 @@ ++-----------------+---------------------------------------------------+ +| ID | dlz-f3a90de | +| Connection Name | test-connection | +| Token | cflt-scim_eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... | +| Created At | 2026-04-16T10:00:00Z | +| Expires At | 2026-10-16T10:00:00Z | ++-----------------+---------------------------------------------------+ diff --git a/test/fixtures/output/org/scim-token/delete-invalid.golden b/test/fixtures/output/org/scim-token/delete-invalid.golden new file mode 100644 index 0000000000..e741b01213 --- /dev/null +++ b/test/fixtures/output/org/scim-token/delete-invalid.golden @@ -0,0 +1 @@ +Are you sure you want to delete organization scim token "invalid"? (y/n): Error: failed to read input diff --git a/test/fixtures/output/org/scim-token/delete-multiple.golden b/test/fixtures/output/org/scim-token/delete-multiple.golden new file mode 100644 index 0000000000..e99d68a202 --- /dev/null +++ b/test/fixtures/output/org/scim-token/delete-multiple.golden @@ -0,0 +1 @@ +Are you sure you want to delete organization scim tokens "id-1" and "id-2"? (y/n): Deleted organization scim tokens "id-1" and "id-2". diff --git a/test/fixtures/output/org/scim-token/delete-no-force.golden b/test/fixtures/output/org/scim-token/delete-no-force.golden new file mode 100644 index 0000000000..9a602e1830 --- /dev/null +++ b/test/fixtures/output/org/scim-token/delete-no-force.golden @@ -0,0 +1 @@ +Are you sure you want to delete organization scim token "id-1"? (y/n): Deleted organization scim token "id-1". diff --git a/test/fixtures/output/org/scim-token/delete.golden b/test/fixtures/output/org/scim-token/delete.golden new file mode 100644 index 0000000000..db2b3f9e32 --- /dev/null +++ b/test/fixtures/output/org/scim-token/delete.golden @@ -0,0 +1 @@ +Deleted organization scim token "id-1". diff --git a/test/fixtures/output/org/scim-token/list-json.golden b/test/fixtures/output/org/scim-token/list-json.golden new file mode 100644 index 0000000000..6b4896d4c5 --- /dev/null +++ b/test/fixtures/output/org/scim-token/list-json.golden @@ -0,0 +1,9 @@ +[ + { + "id": "dlz-f3a90de", + "connection_name": "test-connection", + "token": "cflt-scim_eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", + "created_at": "2026-04-16T10:00:00Z", + "expires_at": "2026-10-16T10:00:00Z" + } +] diff --git a/test/fixtures/output/org/scim-token/list-yaml.golden b/test/fixtures/output/org/scim-token/list-yaml.golden new file mode 100644 index 0000000000..72143f984c --- /dev/null +++ b/test/fixtures/output/org/scim-token/list-yaml.golden @@ -0,0 +1,5 @@ +- id: dlz-f3a90de + connection_name: test-connection + token: cflt-scim_eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... + created_at: "2026-04-16T10:00:00Z" + expires_at: "2026-10-16T10:00:00Z" diff --git a/test/fixtures/output/org/scim-token/list.golden b/test/fixtures/output/org/scim-token/list.golden new file mode 100644 index 0000000000..bd9237b4b6 --- /dev/null +++ b/test/fixtures/output/org/scim-token/list.golden @@ -0,0 +1,3 @@ + ID | Connection Name | Token | Created At | Expires At +--------------+-----------------+---------------------------------------------------+----------------------+----------------------- + dlz-f3a90de | test-connection | cflt-scim_eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... | 2026-04-16T10:00:00Z | 2026-10-16T10:00:00Z diff --git a/test/fixtures/output/organization/help.golden b/test/fixtures/output/organization/help.golden index f11f9c852d..ea1a510417 100644 --- a/test/fixtures/output/organization/help.golden +++ b/test/fixtures/output/organization/help.golden @@ -9,6 +9,7 @@ Aliases: Available Commands: describe Describe the current Confluent Cloud organization. list List Confluent Cloud organizations. + scim-token Manage organization scim tokens. update Update the current Confluent Cloud organization. Global Flags: diff --git a/test/fixtures/output/organization/scim-token/create-help.golden b/test/fixtures/output/organization/scim-token/create-help.golden new file mode 100644 index 0000000000..4c2142dcc3 --- /dev/null +++ b/test/fixtures/output/organization/scim-token/create-help.golden @@ -0,0 +1,14 @@ +Create an organization scim token. + +Usage: + confluent organization scim-token create [flags] + +Flags: + --expire-duration-mins int32 The duration in minutes after which the token expires. Defaults to 6 months (259200 minutes) if not specified. Minimum: 1 month (43200 minutes). Maximum: 2 years (1051200 minutes). + --context string CLI context name. + -o, --output string Specify the output format as "human", "json", or "yaml". (default "human") + +Global Flags: + -h, --help Show help for this command. + --unsafe-trace Equivalent to -vvvv, but also log HTTP requests and responses which might contain plaintext secrets. + -v, --verbose count Increase verbosity (-v for warn, -vv for info, -vvv for debug, -vvvv for trace). diff --git a/test/fixtures/output/organization/scim-token/delete-help.golden b/test/fixtures/output/organization/scim-token/delete-help.golden new file mode 100644 index 0000000000..4f4a4c239e --- /dev/null +++ b/test/fixtures/output/organization/scim-token/delete-help.golden @@ -0,0 +1,13 @@ +Delete one or more organization scim tokens. + +Usage: + confluent organization scim-token delete [id-2] ... [id-n] [flags] + +Flags: + --context string CLI context name. + --force Skip the deletion confirmation prompt. + +Global Flags: + -h, --help Show help for this command. + --unsafe-trace Equivalent to -vvvv, but also log HTTP requests and responses which might contain plaintext secrets. + -v, --verbose count Increase verbosity (-v for warn, -vv for info, -vvv for debug, -vvvv for trace). diff --git a/test/fixtures/output/organization/scim-token/help.golden b/test/fixtures/output/organization/scim-token/help.golden new file mode 100644 index 0000000000..3d26142db5 --- /dev/null +++ b/test/fixtures/output/organization/scim-token/help.golden @@ -0,0 +1,19 @@ +Manage organization scim tokens. + +Usage: + confluent organization scim-token [command] + +Aliases: + scim-token, st + +Available Commands: + create Create an organization scim token. + delete Delete one or more organization scim tokens. + list List organization scim tokens. + +Global Flags: + -h, --help Show help for this command. + --unsafe-trace Equivalent to -vvvv, but also log HTTP requests and responses which might contain plaintext secrets. + -v, --verbose count Increase verbosity (-v for warn, -vv for info, -vvv for debug, -vvvv for trace). + +Use "confluent organization scim-token [command] --help" for more information about a command. diff --git a/test/fixtures/output/organization/scim-token/list-help.golden b/test/fixtures/output/organization/scim-token/list-help.golden new file mode 100644 index 0000000000..c41ad0d90b --- /dev/null +++ b/test/fixtures/output/organization/scim-token/list-help.golden @@ -0,0 +1,13 @@ +List organization scim tokens. + +Usage: + confluent organization scim-token list [flags] + +Flags: + --context string CLI context name. + -o, --output string Specify the output format as "human", "json", or "yaml". (default "human") + +Global Flags: + -h, --help Show help for this command. + --unsafe-trace Equivalent to -vvvv, but also log HTTP requests and responses which might contain plaintext secrets. + -v, --verbose count Increase verbosity (-v for warn, -vv for info, -vvv for debug, -vvvv for trace). diff --git a/test/live/scim_token_live_test.go b/test/live/scim_token_live_test.go new file mode 100644 index 0000000000..50d2066ca1 --- /dev/null +++ b/test/live/scim_token_live_test.go @@ -0,0 +1,46 @@ +//go:build live_test && (all || core) + +// Code generated by cli-terraform-generator; DO NOT EDIT. + +package live + +import ( + "testing" +) + +func (s *CLILiveTestSuite) TestScimTokenCRUDLive() { + t := s.T() + t.Parallel() + state := s.setupTestContext(t) + + // Cleanup (LIFO — execution is reverse-registration order) + s.registerCleanup(t, "organization scim-token delete {{.scim_token_id}} --force", state) + + steps := []CLILiveTest{ + { + Name: "Create organization scim token", + Args: "organization scim-token create -o json", + ExitCode: 0, + JSONFieldsExist: []string{"id"}, + CaptureID: "scim_token_id", + }, + { + Name: "List organization scim tokens", + Args: "organization scim-token list", + UseStateVars: true, + ExitCode: 0, + }, + { + Name: "Delete organization scim token", + Args: "organization scim-token delete {{.scim_token_id}} --force", + UseStateVars: true, + ExitCode: 0, + }, + } + + for _, step := range steps { + t.Run(step.Name, func(t *testing.T) { + s.runLiveCommand(t, step, state) + }) + } +} diff --git a/test/scim_token_test.go b/test/scim_token_test.go new file mode 100644 index 0000000000..49a0562634 --- /dev/null +++ b/test/scim_token_test.go @@ -0,0 +1,53 @@ +// Code generated by cli-terraform-generator; DO NOT EDIT. + +package test + +func (s *CLITestSuite) TestOrgScimTokenCreate() { + tests := []CLITest{ + {args: "org scim-token create", fixture: "org/scim-token/create.golden"}, + {args: "org scim-token create --expire-duration-mins 259200", fixture: "org/scim-token/create-expire-duration-mins.golden"}, + } + + for _, test := range tests { + test.login = "cloud" + s.runIntegrationTest(test) + } +} + +func (s *CLITestSuite) TestOrgScimTokenDelete() { + tests := []CLITest{ + {args: "org scim-token delete id-1 --force", fixture: "org/scim-token/delete.golden"}, + {args: "org scim-token delete id-1", input: "y\n", fixture: "org/scim-token/delete-no-force.golden"}, + {args: "org scim-token delete id-1 id-2", input: "y\n", fixture: "org/scim-token/delete-multiple.golden"}, + {args: "org scim-token delete invalid", fixture: "org/scim-token/delete-invalid.golden", exitCode: 1}, + } + + for _, test := range tests { + test.login = "cloud" + s.runIntegrationTest(test) + } +} + +func (s *CLITestSuite) TestOrgScimTokenList() { + tests := []CLITest{ + {args: "org scim-token list", fixture: "org/scim-token/list.golden"}, + {args: "org scim-token list -o json", fixture: "org/scim-token/list-json.golden"}, + {args: "org scim-token list -o yaml", fixture: "org/scim-token/list-yaml.golden"}, + } + + for _, test := range tests { + test.login = "cloud" + s.runIntegrationTest(test) + } +} + +func (s *CLITestSuite) TestOrgScimToken_Autocomplete() { + tests := []CLITest{ + {args: "__complete org scim-token delete \"\"", fixture: "org/scim-token/delete-autocomplete.golden"}, + } + + for _, test := range tests { + test.login = "cloud" + s.runIntegrationTest(test) + } +} diff --git a/test/test-server/ccloudv2_router.go b/test/test-server/ccloudv2_router.go index a5aa9ed980..d12ef209ea 100644 --- a/test/test-server/ccloudv2_router.go +++ b/test/test-server/ccloudv2_router.go @@ -116,6 +116,8 @@ var ccloudV2Routes = []route{ {"/org/v2/environments/{id}", handleOrgEnvironment}, {"/org/v2/organizations", handleOrgOrganizations}, {"/org/v2/organizations/{id}", handleOrgOrganization}, + {"/org/v2/scim-tokens", handleOrgV2ScimTokens}, + {"/org/v2/scim-tokens/{id}", handleOrgV2ScimTokensId}, {"/pim/v1/integrations", handleProviderIntegrations}, {"/pim/v1/integrations/{id}", handleProviderIntegration}, {"/pim/v2/integrations", handleProviderIntegrationsV2}, diff --git a/test/test-server/scim_token_handler.go b/test/test-server/scim_token_handler.go new file mode 100644 index 0000000000..8d6b76bb03 --- /dev/null +++ b/test/test-server/scim_token_handler.go @@ -0,0 +1,66 @@ +// Code generated by cli-terraform-generator; DO NOT EDIT. + +package testserver + +import ( + "encoding/json" + "net/http" + "os" + "path/filepath" + "testing" + + "github.com/gorilla/mux" + "github.com/stretchr/testify/require" + + orgv2 "github.com/confluentinc/ccloud-sdk-go-v2/org/v2" +) + +// Handler for "/org/v2/scim-tokens" +func handleOrgV2ScimTokens(t *testing.T) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + switch r.Method { + case http.MethodGet: + scimToken := readOrgV2ScimTokenFile(t, "read_created_scim_token.json") + + scimTokenList := &orgv2.OrgV2ScimTokenList{ + Data: []orgv2.OrgV2ScimToken{scimToken}, + } + + err := json.NewEncoder(w).Encode(scimTokenList) + require.NoError(t, err) + case http.MethodPost: + scimToken := readOrgV2ScimTokenFile(t, "create_scim_token.json") + + err := json.NewEncoder(w).Encode(scimToken) + require.NoError(t, err) + } + } +} + +// Handler for "/org/v2/scim-tokens/{id}" +func handleOrgV2ScimTokensId(t *testing.T) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + id := mux.Vars(r)["id"] + switch r.Method { + case http.MethodDelete: + switch id { + case "invalid": + w.WriteHeader(http.StatusNotFound) + default: + w.WriteHeader(http.StatusNoContent) + } + } + } +} + +func readOrgV2ScimTokenFile(t *testing.T, filename string) orgv2.OrgV2ScimToken { + jsonPath := filepath.Join("test", "fixtures", "input", "org", "scim_token", filename) + jsonData, err := os.ReadFile(jsonPath) + require.NoError(t, err) + + scimToken := orgv2.OrgV2ScimToken{} + err = json.Unmarshal(jsonData, &scimToken) + require.NoError(t, err) + + return scimToken +} From 1b0de88cc7f77e62ef2da7e7691fa2908a91254b Mon Sep 17 00:00:00 2001 From: Amelia Dong Date: Fri, 29 May 2026 16:03:30 -0700 Subject: [PATCH 2/2] Fix lint-cli: capitalize Organization in descriptions, add mins vocab word Capitalize "organization" to "Organization" in the SCIM token command descriptions and the login long description, and add "mins" to the vocabWords list so --expire-duration-mins passes the word check. Co-Authored-By: Claude Opus 4.8 (1M context) --- cmd/lint/main.go | 1 + internal/login/command.go | 2 +- internal/organization/command_scim_token.go | 2 +- internal/organization/command_scim_token_create.go | 2 +- internal/organization/command_scim_token_delete.go | 2 +- internal/organization/command_scim_token_list.go | 2 +- test/fixtures/output/login/help-onprem.golden | 2 +- test/fixtures/output/login/help.golden | 2 +- test/fixtures/output/organization/help.golden | 2 +- .../output/organization/scim-token/create-help.golden | 2 +- .../output/organization/scim-token/delete-help.golden | 2 +- test/fixtures/output/organization/scim-token/help.golden | 8 ++++---- .../output/organization/scim-token/list-help.golden | 2 +- 13 files changed, 16 insertions(+), 15 deletions(-) diff --git a/cmd/lint/main.go b/cmd/lint/main.go index 1e03e20945..ad342b6088 100644 --- a/cmd/lint/main.go +++ b/cmd/lint/main.go @@ -305,6 +305,7 @@ var vocabWords = []string{ "mcp_server", "md", "mds", + "mins", "mongodb", "name1", "name2", diff --git a/internal/login/command.go b/internal/login/command.go index d71c27b258..997526999c 100644 --- a/internal/login/command.go +++ b/internal/login/command.go @@ -39,7 +39,7 @@ func New(cfg *config.Config, prerunner pcmd.PreRunner, ccloudClientFactory pauth cmd := &cobra.Command{ Use: "login", Short: "Log in to Confluent Cloud or Confluent Platform.", - Long: fmt.Sprintf("Confluent Cloud:\n\nLog in to Confluent Cloud using your email and password, or using single sign-on (SSO) credentials.\n\nEmail and password login can be accomplished non-interactively using the `%s` and `%s` environment variables.\n\nEmail and password can also be stored locally for non-interactive re-authentication with the `--save` flag.\n\nSSO login can be accomplished headlessly using the `--no-browser` flag, but non-interactive login is not natively supported. Authentication tokens last 8 hours and are automatically refreshed with CLI client usage. If the client is not used for more than 8 hours, you have to log in again.\n\nLog in to a specific Confluent Cloud organization using the `--organization` flag, or by setting the environment variable `%s`.\n\n", pauth.ConfluentCloudEmail, pauth.ConfluentCloudPassword, pauth.ConfluentCloudOrganizationId) + + Long: fmt.Sprintf("Confluent Cloud:\n\nLog in to Confluent Cloud using your email and password, or using single sign-on (SSO) credentials.\n\nEmail and password login can be accomplished non-interactively using the `%s` and `%s` environment variables.\n\nEmail and password can also be stored locally for non-interactive re-authentication with the `--save` flag.\n\nSSO login can be accomplished headlessly using the `--no-browser` flag, but non-interactive login is not natively supported. Authentication tokens last 8 hours and are automatically refreshed with CLI client usage. If the client is not used for more than 8 hours, you have to log in again.\n\nLog in to a specific Confluent Cloud Organization using the `--organization` flag, or by setting the environment variable `%s`.\n\n", pauth.ConfluentCloudEmail, pauth.ConfluentCloudPassword, pauth.ConfluentCloudOrganizationId) + fmt.Sprintf("Confluent Platform:\n\nLog in to Confluent Platform with your username and password, the `--url` flag to identify the location of your Metadata Service (MDS), and the `--certificate-authority-path` flag to identify your self-signed certificate chain.\n\nLogin can be accomplished non-interactively using the `%s`, `%s`, `%s`, and `%s` environment variables.\n\nIn a non-interactive login, `%s` replaces the `--url` flag, and `%s` replaces the `--certificate-authority-path` flag.\n\nEven with the environment variables set, you can force an interactive login using the `--prompt` flag.", pauth.ConfluentPlatformUsername, pauth.ConfluentPlatformPassword, pauth.ConfluentPlatformMDSURL, pauth.ConfluentPlatformCertificateAuthorityPath, pauth.ConfluentPlatformMDSURL, pauth.ConfluentPlatformCertificateAuthorityPath), Args: cobra.NoArgs, Example: examples.BuildExampleString( diff --git a/internal/organization/command_scim_token.go b/internal/organization/command_scim_token.go index 1a51612c03..396af28f38 100644 --- a/internal/organization/command_scim_token.go +++ b/internal/organization/command_scim_token.go @@ -30,7 +30,7 @@ func newScimTokenCommand(cfg *config.Config, prerunner pcmd.PreRunner) *cobra.Co cmd := &cobra.Command{ Use: "scim-token", Aliases: []string{"st"}, - Short: "Manage organization scim tokens.", + Short: "Manage Organization scim tokens.", Annotations: map[string]string{pcmd.RunRequirement: pcmd.RequireNonAPIKeyCloudLogin}, } diff --git a/internal/organization/command_scim_token_create.go b/internal/organization/command_scim_token_create.go index a908b929ad..8f501a575a 100644 --- a/internal/organization/command_scim_token_create.go +++ b/internal/organization/command_scim_token_create.go @@ -14,7 +14,7 @@ import ( func (c *scimTokenCommand) newCreateCommand() *cobra.Command { cmd := &cobra.Command{ Use: "create", - Short: "Create an organization scim token.", + Short: "Create an Organization scim token.", Args: cobra.NoArgs, RunE: c.create, } diff --git a/internal/organization/command_scim_token_delete.go b/internal/organization/command_scim_token_delete.go index 5cdbf6af1d..4a0440f9fe 100644 --- a/internal/organization/command_scim_token_delete.go +++ b/internal/organization/command_scim_token_delete.go @@ -12,7 +12,7 @@ import ( func (c *scimTokenCommand) newDeleteCommand() *cobra.Command { cmd := &cobra.Command{ Use: "delete [id-2] ... [id-n]", - Short: "Delete one or more organization scim tokens.", + Short: "Delete one or more Organization scim tokens.", Args: cobra.MinimumNArgs(1), ValidArgsFunction: pcmd.NewValidArgsFunction(c.validArgsMultiple), RunE: c.delete, diff --git a/internal/organization/command_scim_token_list.go b/internal/organization/command_scim_token_list.go index 8d816f9573..f9dbc8ce35 100644 --- a/internal/organization/command_scim_token_list.go +++ b/internal/organization/command_scim_token_list.go @@ -14,7 +14,7 @@ import ( func (c *scimTokenCommand) newListCommand() *cobra.Command { cmd := &cobra.Command{ Use: "list", - Short: "List organization scim tokens.", + Short: "List Organization scim tokens.", Args: cobra.NoArgs, RunE: c.list, } diff --git a/test/fixtures/output/login/help-onprem.golden b/test/fixtures/output/login/help-onprem.golden index 4d0615a0f9..f228e542a6 100644 --- a/test/fixtures/output/login/help-onprem.golden +++ b/test/fixtures/output/login/help-onprem.golden @@ -8,7 +8,7 @@ Email and password can also be stored locally for non-interactive re-authenticat SSO login can be accomplished headlessly using the `--no-browser` flag, but non-interactive login is not natively supported. Authentication tokens last 8 hours and are automatically refreshed with CLI client usage. If the client is not used for more than 8 hours, you have to log in again. -Log in to a specific Confluent Cloud organization using the `--organization` flag, or by setting the environment variable `CONFLUENT_CLOUD_ORGANIZATION_ID`. +Log in to a specific Confluent Cloud Organization using the `--organization` flag, or by setting the environment variable `CONFLUENT_CLOUD_ORGANIZATION_ID`. Confluent Platform: diff --git a/test/fixtures/output/login/help.golden b/test/fixtures/output/login/help.golden index 4d0615a0f9..f228e542a6 100644 --- a/test/fixtures/output/login/help.golden +++ b/test/fixtures/output/login/help.golden @@ -8,7 +8,7 @@ Email and password can also be stored locally for non-interactive re-authenticat SSO login can be accomplished headlessly using the `--no-browser` flag, but non-interactive login is not natively supported. Authentication tokens last 8 hours and are automatically refreshed with CLI client usage. If the client is not used for more than 8 hours, you have to log in again. -Log in to a specific Confluent Cloud organization using the `--organization` flag, or by setting the environment variable `CONFLUENT_CLOUD_ORGANIZATION_ID`. +Log in to a specific Confluent Cloud Organization using the `--organization` flag, or by setting the environment variable `CONFLUENT_CLOUD_ORGANIZATION_ID`. Confluent Platform: diff --git a/test/fixtures/output/organization/help.golden b/test/fixtures/output/organization/help.golden index ea1a510417..4f87bcf66b 100644 --- a/test/fixtures/output/organization/help.golden +++ b/test/fixtures/output/organization/help.golden @@ -9,7 +9,7 @@ Aliases: Available Commands: describe Describe the current Confluent Cloud organization. list List Confluent Cloud organizations. - scim-token Manage organization scim tokens. + scim-token Manage Organization scim tokens. update Update the current Confluent Cloud organization. Global Flags: diff --git a/test/fixtures/output/organization/scim-token/create-help.golden b/test/fixtures/output/organization/scim-token/create-help.golden index 4c2142dcc3..cbf56a2ce5 100644 --- a/test/fixtures/output/organization/scim-token/create-help.golden +++ b/test/fixtures/output/organization/scim-token/create-help.golden @@ -1,4 +1,4 @@ -Create an organization scim token. +Create an Organization scim token. Usage: confluent organization scim-token create [flags] diff --git a/test/fixtures/output/organization/scim-token/delete-help.golden b/test/fixtures/output/organization/scim-token/delete-help.golden index 4f4a4c239e..248cfacc6d 100644 --- a/test/fixtures/output/organization/scim-token/delete-help.golden +++ b/test/fixtures/output/organization/scim-token/delete-help.golden @@ -1,4 +1,4 @@ -Delete one or more organization scim tokens. +Delete one or more Organization scim tokens. Usage: confluent organization scim-token delete [id-2] ... [id-n] [flags] diff --git a/test/fixtures/output/organization/scim-token/help.golden b/test/fixtures/output/organization/scim-token/help.golden index 3d26142db5..7e5bd0c3f8 100644 --- a/test/fixtures/output/organization/scim-token/help.golden +++ b/test/fixtures/output/organization/scim-token/help.golden @@ -1,4 +1,4 @@ -Manage organization scim tokens. +Manage Organization scim tokens. Usage: confluent organization scim-token [command] @@ -7,9 +7,9 @@ Aliases: scim-token, st Available Commands: - create Create an organization scim token. - delete Delete one or more organization scim tokens. - list List organization scim tokens. + create Create an Organization scim token. + delete Delete one or more Organization scim tokens. + list List Organization scim tokens. Global Flags: -h, --help Show help for this command. diff --git a/test/fixtures/output/organization/scim-token/list-help.golden b/test/fixtures/output/organization/scim-token/list-help.golden index c41ad0d90b..d613f19899 100644 --- a/test/fixtures/output/organization/scim-token/list-help.golden +++ b/test/fixtures/output/organization/scim-token/list-help.golden @@ -1,4 +1,4 @@ -List organization scim tokens. +List Organization scim tokens. Usage: confluent organization scim-token list [flags]