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..ad342b6088 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", @@ -304,6 +305,7 @@ var vocabWords = []string{ "mcp_server", "md", "mds", + "mins", "mongodb", "name1", "name2", @@ -337,6 +339,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/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.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..396af28f38 --- /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..8f501a575a --- /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..4a0440f9fe --- /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..f9dbc8ce35 --- /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/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/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..4f87bcf66b 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..cbf56a2ce5 --- /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..248cfacc6d --- /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..7e5bd0c3f8 --- /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..d613f19899 --- /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 +}