From f060a107ad2185cbf63947280d6a730c10a8d788 Mon Sep 17 00:00:00 2001 From: Kiran Kumar Date: Mon, 26 Jan 2026 16:19:16 +0530 Subject: [PATCH 01/34] feat(organization): add Invitations method to OrganizationAPI - Implemented the Invitations method in the OrganizationAPI interface to list invitations for an organization. - Generated corresponding mock methods in organization_mock.go for testing. - This change enhances the API's functionality by allowing retrieval of organization invitations, aligning with the management API documentation. --- internal/auth0/mock/organization_mock.go | 20 ++++++++++++++++++++ internal/auth0/organization.go | 5 +++++ 2 files changed, 25 insertions(+) diff --git a/internal/auth0/mock/organization_mock.go b/internal/auth0/mock/organization_mock.go index 58b1df7a1..3cef6f024 100644 --- a/internal/auth0/mock/organization_mock.go +++ b/internal/auth0/mock/organization_mock.go @@ -171,6 +171,26 @@ func (mr *MockOrganizationAPIMockRecorder) DiscoveryDomains(ctx, id interface{}, return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DiscoveryDomains", reflect.TypeOf((*MockOrganizationAPI)(nil).DiscoveryDomains), varargs...) } +// Invitations mocks base method. +func (m *MockOrganizationAPI) Invitations(ctx context.Context, id string, opts ...management.RequestOption) (*management.OrganizationInvitationList, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, id} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Invitations", varargs...) + ret0, _ := ret[0].(*management.OrganizationInvitationList) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Invitations indicates an expected call of Invitations. +func (mr *MockOrganizationAPIMockRecorder) Invitations(ctx, id interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, id}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Invitations", reflect.TypeOf((*MockOrganizationAPI)(nil).Invitations), varargs...) +} + // List mocks base method. func (m *MockOrganizationAPI) List(ctx context.Context, opts ...management.RequestOption) (*management.OrganizationList, error) { m.ctrl.T.Helper() diff --git a/internal/auth0/organization.go b/internal/auth0/organization.go index 9027c5ad2..0b6fce40c 100644 --- a/internal/auth0/organization.go +++ b/internal/auth0/organization.go @@ -63,4 +63,9 @@ type OrganizationAPI interface { // UpdateDiscoveryDomain updates a specific discovery domain for an organization. UpdateDiscoveryDomain(ctx context.Context, id string, domainID string, d *management.OrganizationDiscoveryDomain, opts ...management.RequestOption) (err error) + + // Invitations lists invitations for an organization. + // + // See: https://auth0.com/docs/api/management/v2#!/Organizations/get_invitations + Invitations(ctx context.Context, id string, opts ...management.RequestOption) (o *management.OrganizationInvitationList, err error) } From b5d8a1752ae5b7f57d4e66cbd568b5de551dfd67 Mon Sep 17 00:00:00 2001 From: Kiran Kumar Date: Mon, 26 Jan 2026 16:43:09 +0530 Subject: [PATCH 02/34] feat(invitations): add commands to list organization invitations - Introduced `invitationsOrganizationCmd` to handle invitations management. - Added `listInvitationsOrganizationCmd` for listing organization invitations with options for pagination and output formats. - Implemented `getOrgInvitationsWithSpinner` and `getOrgInvitations` methods for fetching invitations from the API. - Created `invitationsView` struct for displaying invitation details in a structured format. --- internal/cli/organizations.go | 121 ++++++++++++++++++++++++++++++++ internal/display/invitations.go | 66 +++++++++++++++++ 2 files changed, 187 insertions(+) create mode 100644 internal/display/invitations.go diff --git a/internal/cli/organizations.go b/internal/cli/organizations.go index f669efb75..2ec923637 100644 --- a/internal/cli/organizations.go +++ b/internal/cli/organizations.go @@ -104,6 +104,7 @@ func organizationsCmd(cli *cli) *cobra.Command { cmd.AddCommand(openOrganizationCmd(cli)) cmd.AddCommand(membersOrganizationCmd(cli)) cmd.AddCommand(rolesOrganizationCmd(cli)) + cmd.AddCommand(invitationsOrganizationCmd(cli)) return cmd } @@ -919,3 +920,123 @@ func (cli *cli) getOrgRoleMembersWithSpinner( return roleMembers, err } + +func invitationsOrganizationCmd(cli *cli) *cobra.Command { + cmd := &cobra.Command{ + Use: "invitations", + Short: "Manage invitations of an organization", + Long: "Manage invitations of an organization.", + } + + cmd.SetUsageTemplate(resourceUsageTemplate()) + cmd.AddCommand(listInvitationsOrganizationCmd(cli)) + + return cmd +} + +func listInvitationsOrganizationCmd(cli *cli) *cobra.Command { + var inputs struct { + ID string + Number int + } + + cmd := &cobra.Command{ + Use: "list", + Aliases: []string{"ls"}, + Args: cobra.MaximumNArgs(1), + Short: "List invitations of an organization", + Long: "List the invitations of an organization.", + Example: ` auth0 orgs invitations list + auth0 orgs invitations ls + auth0 orgs invitations list --number 100 + auth0 orgs invitations ls -n 100 --json + auth0 orgs invitations ls -n 100 --json-compact + auth0 orgs invitations ls --csv`, + RunE: func(cmd *cobra.Command, args []string) error { + if inputs.Number < 1 || inputs.Number > 1000 { + return fmt.Errorf("number flag invalid, please pass a number between 1 and 1000") + } + + if len(args) == 0 { + if err := organizationID.Pick(cmd, &inputs.ID, cli.organizationPickerOptions); err != nil { + return err + } + } else { + inputs.ID = args[0] + } + + invitations, err := cli.getOrgInvitationsWithSpinner(cmd.Context(), inputs.ID, inputs.Number) + if err != nil { + return err + } + + sortInvitations(invitations) + + cli.renderer.InvitationsList(invitations) + + return nil + }, + } + + organizationNumber.Help = "Number of organization invitations to retrieve. Minimum 1, maximum 1000." + organizationNumber.RegisterInt(cmd, &inputs.Number, defaultPageSize) + + cmd.Flags().BoolVar(&cli.json, "json", false, "Output in json format.") + cmd.Flags().BoolVar(&cli.jsonCompact, "json-compact", false, "Output in compact json format.") + cmd.Flags().BoolVar(&cli.csv, "csv", false, "Output in csv format.") + cmd.MarkFlagsMutuallyExclusive("json", "json-compact", "csv") + cmd.SetUsageTemplate(resourceUsageTemplate()) + + return cmd +} + +func (cli *cli) getOrgInvitationsWithSpinner(context context.Context, orgID string, number int, +) ([]management.OrganizationInvitation, error) { + var invitations []management.OrganizationInvitation + + err := ansi.Waiting(func() (err error) { + invitations, err = cli.getOrgInvitations(context, orgID, number) + return err + }) + + return invitations, err +} + +func (cli *cli) getOrgInvitations( + context context.Context, + orgID string, + number int, +) ([]management.OrganizationInvitation, error) { + list, err := getWithPagination( + number, + func(opts ...management.RequestOption) (result []interface{}, hasNext bool, apiErr error) { + invitations, apiErr := cli.api.Organization.Invitations(context, url.PathEscape(orgID), opts...) + if apiErr != nil { + return nil, false, apiErr + } + var output []interface{} + for _, invitation := range invitations.OrganizationInvitations { + if invitation != nil { + output = append(output, *invitation) + } + } + return output, invitations.HasNext(), nil + }, + ) + if err != nil { + return nil, fmt.Errorf("failed to list invitations of organization with ID %q: %w", orgID, err) + } + + var typedList []management.OrganizationInvitation + for _, item := range list { + typedList = append(typedList, item.(management.OrganizationInvitation)) + } + + return typedList, nil +} + +func sortInvitations(invitations []management.OrganizationInvitation) { + sort.Slice(invitations, func(i, j int) bool { + return strings.ToLower(invitations[i].GetCreatedAt()) < strings.ToLower(invitations[j].GetCreatedAt()) + }) +} diff --git a/internal/display/invitations.go b/internal/display/invitations.go new file mode 100644 index 000000000..e3996da5b --- /dev/null +++ b/internal/display/invitations.go @@ -0,0 +1,66 @@ +package display + +import ( + "github.com/auth0/go-auth0/management" + + "github.com/auth0/auth0-cli/internal/ansi" +) + +type invitationsView struct { + ID string + ClientId string + ConnectionId string + InviterName string + InviteeEmail string + CreatedAt string + ExpiresAt string + raw interface{} + // TODO: Check if OrganizationId, InvitationURL, Role assignments are needed +} + +func (v *invitationsView) AsTableHeader() []string { + return []string{"ID", "Client ID", "Connection ID", "Inviter Name", "Invitee Email", "Created At", "Expires At"} +} + +func (v *invitationsView) AsTableRow() []string { + return []string{ansi.Faint(v.ID), v.ClientId, v.ConnectionId, v.InviterName, v.InviteeEmail, v.CreatedAt, v.ExpiresAt} +} + +func (v *invitationsView) KeyValues() [][]string { + return [][]string{} +} + +func (v *invitationsView) Object() interface{} { + return v.raw +} + +func (r *Renderer) InvitationsList(invitations []management.OrganizationInvitation) { + resource := "organization invitations" + + r.Heading(resource) + + if len(invitations) == 0 { + r.EmptyState(resource, "") + return + } + + var res []View + for _, m := range invitations { + res = append(res, makeInvitationsView(m)) + } + + r.Results(res) +} + +func makeInvitationsView(invitation management.OrganizationInvitation) *invitationsView { + return &invitationsView{ + ID: invitation.GetID(), + InviterName: invitation.GetInviter().GetName(), + InviteeEmail: invitation.GetInvitee().GetEmail(), + CreatedAt: invitation.GetCreatedAt(), + ExpiresAt: invitation.GetExpiresAt(), + ClientId: invitation.GetClientID(), + ConnectionId: invitation.GetConnectionID(), + raw: invitation, + } +} From e70e74dff8e40734f69e0f44667b02e748278ccf Mon Sep 17 00:00:00 2001 From: Kiran Kumar Date: Tue, 27 Jan 2026 10:59:55 +0530 Subject: [PATCH 03/34] feat(invitations): add documentation for managing organization invitations - Introduced a new section for managing invitations within organizations. - Added a new file `auth0_orgs_invitations.md` detailing the invitation management commands. - Created `auth0_orgs_invitations_list.md` to document the command for listing invitations. - Updated existing documentation files to include links to the new invitations section. This change enhances the usability of the Auth0 CLI by providing clear guidance on managing organization invitations. --- docs/auth0_orgs.md | 1 + docs/auth0_orgs_create.md | 1 + docs/auth0_orgs_delete.md | 1 + docs/auth0_orgs_invitations.md | 13 ++++++++ docs/auth0_orgs_invitations_list.md | 51 +++++++++++++++++++++++++++++ docs/auth0_orgs_list.md | 1 + docs/auth0_orgs_open.md | 1 + docs/auth0_orgs_show.md | 1 + docs/auth0_orgs_update.md | 1 + 9 files changed, 71 insertions(+) create mode 100644 docs/auth0_orgs_invitations.md create mode 100644 docs/auth0_orgs_invitations_list.md diff --git a/docs/auth0_orgs.md b/docs/auth0_orgs.md index 29655ddfe..91963324d 100644 --- a/docs/auth0_orgs.md +++ b/docs/auth0_orgs.md @@ -11,6 +11,7 @@ The Auth0 Organizations feature best supports business-to-business (B2B) impleme - [auth0 orgs create](auth0_orgs_create.md) - Create a new organization - [auth0 orgs delete](auth0_orgs_delete.md) - Delete an organization +- [auth0 orgs invitations](auth0_orgs_invitations.md) - Manage invitations of an organization - [auth0 orgs list](auth0_orgs_list.md) - List your organizations - [auth0 orgs members](auth0_orgs_members.md) - Manage members of an organization - [auth0 orgs open](auth0_orgs_open.md) - Open the settings page of an organization diff --git a/docs/auth0_orgs_create.md b/docs/auth0_orgs_create.md index 5aafec556..632f3027a 100644 --- a/docs/auth0_orgs_create.md +++ b/docs/auth0_orgs_create.md @@ -55,6 +55,7 @@ auth0 orgs create [flags] - [auth0 orgs create](auth0_orgs_create.md) - Create a new organization - [auth0 orgs delete](auth0_orgs_delete.md) - Delete an organization +- [auth0 orgs invitations](auth0_orgs_invitations.md) - Manage invitations of an organization - [auth0 orgs list](auth0_orgs_list.md) - List your organizations - [auth0 orgs members](auth0_orgs_members.md) - Manage members of an organization - [auth0 orgs open](auth0_orgs_open.md) - Open the settings page of an organization diff --git a/docs/auth0_orgs_delete.md b/docs/auth0_orgs_delete.md index 547fd1004..251f87257 100644 --- a/docs/auth0_orgs_delete.md +++ b/docs/auth0_orgs_delete.md @@ -49,6 +49,7 @@ auth0 orgs delete [flags] - [auth0 orgs create](auth0_orgs_create.md) - Create a new organization - [auth0 orgs delete](auth0_orgs_delete.md) - Delete an organization +- [auth0 orgs invitations](auth0_orgs_invitations.md) - Manage invitations of an organization - [auth0 orgs list](auth0_orgs_list.md) - List your organizations - [auth0 orgs members](auth0_orgs_members.md) - Manage members of an organization - [auth0 orgs open](auth0_orgs_open.md) - Open the settings page of an organization diff --git a/docs/auth0_orgs_invitations.md b/docs/auth0_orgs_invitations.md new file mode 100644 index 000000000..659766d43 --- /dev/null +++ b/docs/auth0_orgs_invitations.md @@ -0,0 +1,13 @@ +--- +layout: default +has_toc: false +has_children: true +--- +# auth0 orgs invitations + +Manage invitations of an organization. + +## Commands + +- [auth0 orgs invitations list](auth0_orgs_invitations_list.md) - List invitations of an organization + diff --git a/docs/auth0_orgs_invitations_list.md b/docs/auth0_orgs_invitations_list.md new file mode 100644 index 000000000..0e1cf27d5 --- /dev/null +++ b/docs/auth0_orgs_invitations_list.md @@ -0,0 +1,51 @@ +--- +layout: default +parent: auth0 orgs invitations +has_toc: false +--- +# auth0 orgs invitations list + +List the invitations of an organization. + +## Usage +``` +auth0 orgs invitations list [flags] +``` + +## Examples + +``` + auth0 orgs invitations list + auth0 orgs invitations ls + auth0 orgs invitations list --number 100 + auth0 orgs invitations ls -n 100 --json + auth0 orgs invitations ls -n 100 --json-compact + auth0 orgs invitations ls --csv +``` + + +## Flags + +``` + --csv Output in csv format. + --json Output in json format. + --json-compact Output in compact json format. + -n, --number int Number of organization invitations to retrieve. Minimum 1, maximum 1000. (default 100) +``` + + +## Inherited Flags + +``` + --debug Enable debug mode. + --no-color Disable colors. + --no-input Disable interactivity. + --tenant string Specific tenant to use. +``` + + +## Related Commands + +- [auth0 orgs invitations list](auth0_orgs_invitations_list.md) - List invitations of an organization + + diff --git a/docs/auth0_orgs_list.md b/docs/auth0_orgs_list.md index d256fb772..8225fce23 100644 --- a/docs/auth0_orgs_list.md +++ b/docs/auth0_orgs_list.md @@ -48,6 +48,7 @@ auth0 orgs list [flags] - [auth0 orgs create](auth0_orgs_create.md) - Create a new organization - [auth0 orgs delete](auth0_orgs_delete.md) - Delete an organization +- [auth0 orgs invitations](auth0_orgs_invitations.md) - Manage invitations of an organization - [auth0 orgs list](auth0_orgs_list.md) - List your organizations - [auth0 orgs members](auth0_orgs_members.md) - Manage members of an organization - [auth0 orgs open](auth0_orgs_open.md) - Open the settings page of an organization diff --git a/docs/auth0_orgs_open.md b/docs/auth0_orgs_open.md index 2e2c64511..96acd2676 100644 --- a/docs/auth0_orgs_open.md +++ b/docs/auth0_orgs_open.md @@ -36,6 +36,7 @@ auth0 orgs open [flags] - [auth0 orgs create](auth0_orgs_create.md) - Create a new organization - [auth0 orgs delete](auth0_orgs_delete.md) - Delete an organization +- [auth0 orgs invitations](auth0_orgs_invitations.md) - Manage invitations of an organization - [auth0 orgs list](auth0_orgs_list.md) - List your organizations - [auth0 orgs members](auth0_orgs_members.md) - Manage members of an organization - [auth0 orgs open](auth0_orgs_open.md) - Open the settings page of an organization diff --git a/docs/auth0_orgs_show.md b/docs/auth0_orgs_show.md index ee0be9ad1..28b52f677 100644 --- a/docs/auth0_orgs_show.md +++ b/docs/auth0_orgs_show.md @@ -44,6 +44,7 @@ auth0 orgs show [flags] - [auth0 orgs create](auth0_orgs_create.md) - Create a new organization - [auth0 orgs delete](auth0_orgs_delete.md) - Delete an organization +- [auth0 orgs invitations](auth0_orgs_invitations.md) - Manage invitations of an organization - [auth0 orgs list](auth0_orgs_list.md) - List your organizations - [auth0 orgs members](auth0_orgs_members.md) - Manage members of an organization - [auth0 orgs open](auth0_orgs_open.md) - Open the settings page of an organization diff --git a/docs/auth0_orgs_update.md b/docs/auth0_orgs_update.md index 242ca64b2..da7b8b099 100644 --- a/docs/auth0_orgs_update.md +++ b/docs/auth0_orgs_update.md @@ -53,6 +53,7 @@ auth0 orgs update [flags] - [auth0 orgs create](auth0_orgs_create.md) - Create a new organization - [auth0 orgs delete](auth0_orgs_delete.md) - Delete an organization +- [auth0 orgs invitations](auth0_orgs_invitations.md) - Manage invitations of an organization - [auth0 orgs list](auth0_orgs_list.md) - List your organizations - [auth0 orgs members](auth0_orgs_members.md) - Manage members of an organization - [auth0 orgs open](auth0_orgs_open.md) - Open the settings page of an organization From 0f9a89b24518353d6fc62aa8e9fcb0848f45630a Mon Sep 17 00:00:00 2001 From: Kiran Kumar Date: Tue, 3 Feb 2026 19:12:54 +0530 Subject: [PATCH 04/34] feat(organization): add CreateInvitation method to OrganizationAPI - Implemented CreateInvitation method in OrganizationAPI interface to facilitate creating invitations for organizations. - Added corresponding mock methods in organization_mock.go for testing purposes. - This change enhances the API's functionality, allowing users to manage organization invitations more effectively. --- internal/auth0/mock/organization_mock.go | 19 +++++++++++++++++++ internal/auth0/organization.go | 5 +++++ 2 files changed, 24 insertions(+) diff --git a/internal/auth0/mock/organization_mock.go b/internal/auth0/mock/organization_mock.go index 3cef6f024..3362e439b 100644 --- a/internal/auth0/mock/organization_mock.go +++ b/internal/auth0/mock/organization_mock.go @@ -93,6 +93,25 @@ func (mr *MockOrganizationAPIMockRecorder) CreateDiscoveryDomain(ctx, id, d inte return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateDiscoveryDomain", reflect.TypeOf((*MockOrganizationAPI)(nil).CreateDiscoveryDomain), varargs...) } +// CreateInvitation mocks base method. +func (m *MockOrganizationAPI) CreateInvitation(ctx context.Context, id string, i *management.OrganizationInvitation, opts ...management.RequestOption) error { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, id, i} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "CreateInvitation", varargs...) + ret0, _ := ret[0].(error) + return ret0 +} + +// CreateInvitation indicates an expected call of CreateInvitation. +func (mr *MockOrganizationAPIMockRecorder) CreateInvitation(ctx, id, i interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, id, i}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateInvitation", reflect.TypeOf((*MockOrganizationAPI)(nil).CreateInvitation), varargs...) +} + // Delete mocks base method. func (m *MockOrganizationAPI) Delete(ctx context.Context, id string, opts ...management.RequestOption) error { m.ctrl.T.Helper() diff --git a/internal/auth0/organization.go b/internal/auth0/organization.go index 0b6fce40c..e482b9343 100644 --- a/internal/auth0/organization.go +++ b/internal/auth0/organization.go @@ -68,4 +68,9 @@ type OrganizationAPI interface { // // See: https://auth0.com/docs/api/management/v2#!/Organizations/get_invitations Invitations(ctx context.Context, id string, opts ...management.RequestOption) (o *management.OrganizationInvitationList, err error) + + // CreateInvitation creates invitations to an organization. + // + // See: https://auth0.com/docs/api/management/v2/#!/Organizations/post_invitations + CreateInvitation(ctx context.Context, id string, i *management.OrganizationInvitation, opts ...management.RequestOption) (err error) } From 70d9d4c2e6e863a90663573b51104f1a42e5278a Mon Sep 17 00:00:00 2001 From: Kiran Kumar Date: Tue, 3 Feb 2026 19:27:29 +0530 Subject: [PATCH 05/34] feat(invitations): implement create invitation command and update docs - Added `create` command to manage organization invitations, allowing users to create new invitations. - Introduced flags for inviter name, invitee email, client ID, connection ID, TTL seconds, and metadata. - Updated `auth0_orgs_invitations.md` to include the new command. - Created detailed documentation in `auth0_orgs_invitations_create.md` for usage and examples. - Enhanced `auth0_orgs_invitations_list.md` to reference the new create command. - Updated `invitations.go` to reflect changes in field names for consistency. --- docs/auth0_orgs_invitations.md | 1 + docs/auth0_orgs_invitations_create.md | 60 +++++++++ docs/auth0_orgs_invitations_list.md | 1 + internal/cli/organizations.go | 170 +++++++++++++++++++++++++- internal/display/invitations.go | 19 +-- 5 files changed, 243 insertions(+), 8 deletions(-) create mode 100644 docs/auth0_orgs_invitations_create.md diff --git a/docs/auth0_orgs_invitations.md b/docs/auth0_orgs_invitations.md index 659766d43..11b2e949a 100644 --- a/docs/auth0_orgs_invitations.md +++ b/docs/auth0_orgs_invitations.md @@ -9,5 +9,6 @@ Manage invitations of an organization. ## Commands +- [auth0 orgs invitations create](auth0_orgs_invitations_create.md) - Create a new invitation to an organization - [auth0 orgs invitations list](auth0_orgs_invitations_list.md) - List invitations of an organization diff --git a/docs/auth0_orgs_invitations_create.md b/docs/auth0_orgs_invitations_create.md new file mode 100644 index 000000000..3f427645b --- /dev/null +++ b/docs/auth0_orgs_invitations_create.md @@ -0,0 +1,60 @@ +--- +layout: default +parent: auth0 orgs invitations +has_toc: false +--- +# auth0 orgs invitations create + +Create a new invitation to an organization. + +## Usage +``` +auth0 orgs invitations create [flags] +``` + +## Examples + +``` + auth0 orgs invitations create + auth0 orgs invitations create + auth0 orgs invitations create --inviter-name "Inviter Name" --invitee-email "invitee@example.com" + auth0 orgs invitations create --invitee-email "invitee@example.com" --client-id "client_id" + auth0 orgs invitations create -n "Inviter Name" -e "invitee@example.com" -c "client_id" -connection-id "connection_id" -t 86400 + auth0 orgs invitations create --json --inviter-name "Inviter Name" +``` + + +## Flags + +``` + -a, --app-metadata stringToString Application metadata for the invited user in key=value format. (default []) + -c, --client-id string Auth0 client ID. Used to resolve the application's login initiation endpoint. + --connection-id string The id of the connection to force invitee to authenticate with. + --csv Output in csv format. + -e, --invitee-email string Email address of the person being invited. + -n, --inviter-name string Name of the person sending the invitation. + --json Output in json format. + --json-compact Output in compact json format. + -r, --roles strings Roles IDs to associate with the user. + -s, --send-email Whether to send the invitation email to the invitee. (default true) + -t, --ttl-sec int Number of seconds for which the invitation is valid before expiration. + -u, --user-metadata stringToString User metadata for the invited user in key=value format. (default []) +``` + + +## Inherited Flags + +``` + --debug Enable debug mode. + --no-color Disable colors. + --no-input Disable interactivity. + --tenant string Specific tenant to use. +``` + + +## Related Commands + +- [auth0 orgs invitations create](auth0_orgs_invitations_create.md) - Create a new invitation to an organization +- [auth0 orgs invitations list](auth0_orgs_invitations_list.md) - List invitations of an organization + + diff --git a/docs/auth0_orgs_invitations_list.md b/docs/auth0_orgs_invitations_list.md index 0e1cf27d5..47eb9fb90 100644 --- a/docs/auth0_orgs_invitations_list.md +++ b/docs/auth0_orgs_invitations_list.md @@ -46,6 +46,7 @@ auth0 orgs invitations list [flags] ## Related Commands +- [auth0 orgs invitations create](auth0_orgs_invitations_create.md) - Create a new invitation to an organization - [auth0 orgs invitations list](auth0_orgs_invitations_list.md) - List invitations of an organization diff --git a/internal/cli/organizations.go b/internal/cli/organizations.go index 2ec923637..17519313c 100644 --- a/internal/cli/organizations.go +++ b/internal/cli/organizations.go @@ -22,7 +22,7 @@ const ( var ( organizationID = Argument{ - Name: "ID", + Name: "Org ID", Help: "ID of the organization.", } @@ -84,6 +84,71 @@ var ( LongForm: "number", ShortForm: "n", } + + inviterName = Flag{ + Name: "Inviter Name", + LongForm: "inviter-name", + ShortForm: "n", + Help: "Name of the person sending the invitation.", + IsRequired: true, + } + + inviteeEmail = Flag{ + Name: "Invitee Email", + LongForm: "invitee-email", + ShortForm: "e", + Help: "Email address of the person being invited.", + IsRequired: true, + } + + clientID = Flag{ + Name: "Client ID", + LongForm: "client-id", + ShortForm: "c", + Help: "Auth0 client ID. Used to resolve the application's login initiation endpoint.", + IsRequired: true, + } + + connectionID = Flag{ + Name: "Connection ID", + LongForm: "connection-id", + Help: "The id of the connection to force invitee to authenticate with.", + } + + ttlSeconds = Flag{ + Name: "TTL Seconds", + LongForm: "ttl-sec", + ShortForm: "t", + Help: "Number of seconds for which the invitation is valid before expiration.", + } + + sendInvitationEmail = Flag{ + Name: "Send Invitation Email", + LongForm: "send-email", + ShortForm: "s", + Help: "Whether to send the invitation email to the invitee.", + } + + organizationRoles = Flag{ + Name: "Roles", + LongForm: "roles", + ShortForm: "r", + Help: "Roles IDs to associate with the user.", + } + + applicationMetadata = Flag{ + Name: "App Metadata", + LongForm: "app-metadata", + ShortForm: "a", + Help: "Application metadata for the invited user in key=value format.", + } + + userMetadata = Flag{ + Name: "User Metadata", + LongForm: "user-metadata", + ShortForm: "u", + Help: "User metadata for the invited user in key=value format.", + } ) func organizationsCmd(cli *cli) *cobra.Command { @@ -930,6 +995,7 @@ func invitationsOrganizationCmd(cli *cli) *cobra.Command { cmd.SetUsageTemplate(resourceUsageTemplate()) cmd.AddCommand(listInvitationsOrganizationCmd(cli)) + cmd.AddCommand(createInvitationOrganizationCmd(cli)) return cmd } @@ -1040,3 +1106,105 @@ func sortInvitations(invitations []management.OrganizationInvitation) { return strings.ToLower(invitations[i].GetCreatedAt()) < strings.ToLower(invitations[j].GetCreatedAt()) }) } + +func createInvitationOrganizationCmd(cli *cli) *cobra.Command { + var inputs struct { + OrgID string + InviterName string + InviteeEmail string + ClientID string + ConnectionID string + TTLSeconds int + SendInvitationEmail bool + Roles []string + AppMetadata map[string]string + UserMetadata map[string]string + } + + cmd := &cobra.Command{ + Use: "create", + Args: cobra.MaximumNArgs(1), + Short: "Create a new invitation to an organization", + Long: "Create a new invitation to an organization.", + Example: ` auth0 orgs invitations create + auth0 orgs invitations create + auth0 orgs invitations create --inviter-name "Inviter Name" --invitee-email "invitee@example.com" + auth0 orgs invitations create --invitee-email "invitee@example.com" --client-id "client_id" + auth0 orgs invitations create -n "Inviter Name" -e "invitee@example.com" -c "client_id" -connection-id "connection_id" -t 86400 + auth0 orgs invitations create --json --inviter-name "Inviter Name"`, + RunE: func(cmd *cobra.Command, args []string) error { + if len(args) == 0 { + if err := organizationID.Pick(cmd, &inputs.OrgID, cli.organizationPickerOptions); err != nil { + return err + } + } else { + inputs.OrgID = args[0] + } + if err := clientID.Pick(cmd, &inputs.ClientID, cli.appPickerOptions()); err != nil { + return err + } + if err := inviterName.Ask(cmd, &inputs.InviterName, nil); err != nil { + return err + } + if err := inviteeEmail.Ask(cmd, &inputs.InviteeEmail, nil); err != nil { + return err + } + + invitation := &management.OrganizationInvitation{ + Inviter: &management.OrganizationInvitationInviter{Name: &inputs.InviterName}, + Invitee: &management.OrganizationInvitationInvitee{Email: &inputs.InviteeEmail}, + ClientID: &inputs.ClientID, + TTLSec: &inputs.TTLSeconds, + SendInvitationEmail: &inputs.SendInvitationEmail, + } + if inputs.ConnectionID != "" { + invitation.ConnectionID = &inputs.ConnectionID + } + if len(inputs.AppMetadata) > 0 { + appMetadata := make(map[string]interface{}, len(inputs.AppMetadata)) + for k, v := range inputs.AppMetadata { + appMetadata[k] = v + } + invitation.AppMetadata = appMetadata + } + if len(inputs.UserMetadata) > 0 { + userMetadata := make(map[string]interface{}, len(inputs.UserMetadata)) + for k, v := range inputs.UserMetadata { + userMetadata[k] = v + } + invitation.UserMetadata = userMetadata + } + if len(inputs.Roles) > 0 { + invitation.Roles = inputs.Roles + } + + if err := ansi.Waiting(func() error { + return cli.api.Organization.CreateInvitation(cmd.Context(), inputs.OrgID, invitation) + }); err != nil { + return fmt.Errorf("failed to create invitation for organization with ID %q: %w", inputs.OrgID, err) + } + + cli.renderer.InvitationsCreate(*invitation) + return nil + }, + } + + inviterName.RegisterString(cmd, &inputs.InviterName, "") + inviteeEmail.RegisterString(cmd, &inputs.InviteeEmail, "") + clientID.RegisterString(cmd, &inputs.ClientID, "") + connectionID.RegisterString(cmd, &inputs.ConnectionID, "") + ttlSeconds.RegisterInt(cmd, &inputs.TTLSeconds, 0) + sendInvitationEmail.RegisterBool(cmd, &inputs.SendInvitationEmail, true) + organizationRoles.RegisterStringSlice(cmd, &inputs.Roles, nil) + applicationMetadata.RegisterStringMap(cmd, &inputs.AppMetadata, nil) + userMetadata.RegisterStringMap(cmd, &inputs.UserMetadata, nil) + + cmd.Flags().BoolVar(&cli.json, "json", false, "Output in json format.") + cmd.Flags().BoolVar(&cli.jsonCompact, "json-compact", false, "Output in compact json format.") + cmd.Flags().BoolVar(&cli.csv, "csv", false, "Output in csv format.") + cmd.MarkFlagsMutuallyExclusive("json", "json-compact", "csv") + // TODO shouldn't set csv? + cmd.SetUsageTemplate(resourceUsageTemplate()) + + return cmd +} diff --git a/internal/display/invitations.go b/internal/display/invitations.go index e3996da5b..dbbbb57a1 100644 --- a/internal/display/invitations.go +++ b/internal/display/invitations.go @@ -8,14 +8,14 @@ import ( type invitationsView struct { ID string - ClientId string - ConnectionId string + ClientID string + ConnectionID string InviterName string InviteeEmail string CreatedAt string ExpiresAt string raw interface{} - // TODO: Check if OrganizationId, InvitationURL, Role assignments are needed + // TODO: Check if OrganizationId, InvitationURL, Role assignments etc are needed. } func (v *invitationsView) AsTableHeader() []string { @@ -23,7 +23,7 @@ func (v *invitationsView) AsTableHeader() []string { } func (v *invitationsView) AsTableRow() []string { - return []string{ansi.Faint(v.ID), v.ClientId, v.ConnectionId, v.InviterName, v.InviteeEmail, v.CreatedAt, v.ExpiresAt} + return []string{ansi.Faint(v.ID), v.ClientID, v.ConnectionID, v.InviterName, v.InviteeEmail, v.CreatedAt, v.ExpiresAt} } func (v *invitationsView) KeyValues() [][]string { @@ -40,7 +40,7 @@ func (r *Renderer) InvitationsList(invitations []management.OrganizationInvitati r.Heading(resource) if len(invitations) == 0 { - r.EmptyState(resource, "") + r.EmptyState(resource, "Use 'auth0 orgs invitations create' to add one") return } @@ -52,6 +52,11 @@ func (r *Renderer) InvitationsList(invitations []management.OrganizationInvitati r.Results(res) } +func (r *Renderer) InvitationsCreate(invitation management.OrganizationInvitation) { + r.Heading("organization invitation created") + r.Result(makeInvitationsView(invitation)) +} + func makeInvitationsView(invitation management.OrganizationInvitation) *invitationsView { return &invitationsView{ ID: invitation.GetID(), @@ -59,8 +64,8 @@ func makeInvitationsView(invitation management.OrganizationInvitation) *invitati InviteeEmail: invitation.GetInvitee().GetEmail(), CreatedAt: invitation.GetCreatedAt(), ExpiresAt: invitation.GetExpiresAt(), - ClientId: invitation.GetClientID(), - ConnectionId: invitation.GetConnectionID(), + ClientID: invitation.GetClientID(), + ConnectionID: invitation.GetConnectionID(), raw: invitation, } } From d862ff47927c40d97d995dad86c956d9f3fab971 Mon Sep 17 00:00:00 2001 From: Kiran Kumar Date: Thu, 5 Feb 2026 18:18:06 +0530 Subject: [PATCH 06/34] feat(invitations): add delete invitation command and related functionality - Implemented the DeleteInvitation method in the OrganizationAPI interface to allow deletion of invitations. - Added DeleteInvitation mock methods in organization_mock.go for testing purposes. - Created a new delete command in organizations.go to handle invitation deletions, including interactive and non-interactive modes. - Introduced invitationPickerOptions method to facilitate selection of invitations for deletion. - Added unit tests for invitationPickerOptions to ensure correct behavior under various scenarios. --- docs/auth0_orgs_invitations.md | 1 + docs/auth0_orgs_invitations_create.md | 12 +-- docs/auth0_orgs_invitations_delete.md | 53 +++++++++++ docs/auth0_orgs_invitations_list.md | 1 + internal/auth0/mock/organization_mock.go | 19 ++++ internal/auth0/organization.go | 5 ++ internal/cli/organizations.go | 108 ++++++++++++++++++++--- internal/cli/organizations_test.go | 86 ++++++++++++++++++ 8 files changed, 267 insertions(+), 18 deletions(-) create mode 100644 docs/auth0_orgs_invitations_delete.md diff --git a/docs/auth0_orgs_invitations.md b/docs/auth0_orgs_invitations.md index 11b2e949a..05648da0c 100644 --- a/docs/auth0_orgs_invitations.md +++ b/docs/auth0_orgs_invitations.md @@ -10,5 +10,6 @@ Manage invitations of an organization. ## Commands - [auth0 orgs invitations create](auth0_orgs_invitations_create.md) - Create a new invitation to an organization +- [auth0 orgs invitations delete](auth0_orgs_invitations_delete.md) - Delete invitation(s) from an organization - [auth0 orgs invitations list](auth0_orgs_invitations_list.md) - List invitations of an organization diff --git a/docs/auth0_orgs_invitations_create.md b/docs/auth0_orgs_invitations_create.md index 3f427645b..53280263d 100644 --- a/docs/auth0_orgs_invitations_create.md +++ b/docs/auth0_orgs_invitations_create.md @@ -16,11 +16,11 @@ auth0 orgs invitations create [flags] ``` auth0 orgs invitations create - auth0 orgs invitations create - auth0 orgs invitations create --inviter-name "Inviter Name" --invitee-email "invitee@example.com" - auth0 orgs invitations create --invitee-email "invitee@example.com" --client-id "client_id" - auth0 orgs invitations create -n "Inviter Name" -e "invitee@example.com" -c "client_id" -connection-id "connection_id" -t 86400 - auth0 orgs invitations create --json --inviter-name "Inviter Name" + auth0 orgs invitations create + auth0 orgs invitations create --inviter-name "Inviter Name" --invitee-email "invitee@example.com" + auth0 orgs invitations create --invitee-email "invitee@example.com" --client-id "client_id" + auth0 orgs invitations create -n "Inviter Name" -e "invitee@example.com" -c "client_id" -connection-id "connection_id" -t 86400 + auth0 orgs invitations create --json --inviter-name "Inviter Name" ``` @@ -30,7 +30,6 @@ auth0 orgs invitations create [flags] -a, --app-metadata stringToString Application metadata for the invited user in key=value format. (default []) -c, --client-id string Auth0 client ID. Used to resolve the application's login initiation endpoint. --connection-id string The id of the connection to force invitee to authenticate with. - --csv Output in csv format. -e, --invitee-email string Email address of the person being invited. -n, --inviter-name string Name of the person sending the invitation. --json Output in json format. @@ -55,6 +54,7 @@ auth0 orgs invitations create [flags] ## Related Commands - [auth0 orgs invitations create](auth0_orgs_invitations_create.md) - Create a new invitation to an organization +- [auth0 orgs invitations delete](auth0_orgs_invitations_delete.md) - Delete invitation(s) from an organization - [auth0 orgs invitations list](auth0_orgs_invitations_list.md) - List invitations of an organization diff --git a/docs/auth0_orgs_invitations_delete.md b/docs/auth0_orgs_invitations_delete.md new file mode 100644 index 000000000..cb3205304 --- /dev/null +++ b/docs/auth0_orgs_invitations_delete.md @@ -0,0 +1,53 @@ +--- +layout: default +parent: auth0 orgs invitations +has_toc: false +--- +# auth0 orgs invitations delete + +Delete invitation(s) from an organization. + +To delete interactively, use `auth0 orgs invitations delete` with no arguments. + +To delete non-interactively, supply the organization id, invitation id(s) and the `--force` flag to skip confirmation. + +## Usage +``` +auth0 orgs invitations delete [flags] +``` + +## Examples + +``` + auth0 orgs invitations delete + auth0 orgs invitations rm + auth0 orgs invitations delete + auth0 orgs invitations delete --force + auth0 orgs invitations delete +``` + + +## Flags + +``` + --force Skip confirmation. +``` + + +## Inherited Flags + +``` + --debug Enable debug mode. + --no-color Disable colors. + --no-input Disable interactivity. + --tenant string Specific tenant to use. +``` + + +## Related Commands + +- [auth0 orgs invitations create](auth0_orgs_invitations_create.md) - Create a new invitation to an organization +- [auth0 orgs invitations delete](auth0_orgs_invitations_delete.md) - Delete invitation(s) from an organization +- [auth0 orgs invitations list](auth0_orgs_invitations_list.md) - List invitations of an organization + + diff --git a/docs/auth0_orgs_invitations_list.md b/docs/auth0_orgs_invitations_list.md index 47eb9fb90..049e61d88 100644 --- a/docs/auth0_orgs_invitations_list.md +++ b/docs/auth0_orgs_invitations_list.md @@ -47,6 +47,7 @@ auth0 orgs invitations list [flags] ## Related Commands - [auth0 orgs invitations create](auth0_orgs_invitations_create.md) - Create a new invitation to an organization +- [auth0 orgs invitations delete](auth0_orgs_invitations_delete.md) - Delete invitation(s) from an organization - [auth0 orgs invitations list](auth0_orgs_invitations_list.md) - List invitations of an organization diff --git a/internal/auth0/mock/organization_mock.go b/internal/auth0/mock/organization_mock.go index 3362e439b..751bd50ed 100644 --- a/internal/auth0/mock/organization_mock.go +++ b/internal/auth0/mock/organization_mock.go @@ -150,6 +150,25 @@ func (mr *MockOrganizationAPIMockRecorder) DeleteDiscoveryDomain(ctx, id, domain return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteDiscoveryDomain", reflect.TypeOf((*MockOrganizationAPI)(nil).DeleteDiscoveryDomain), varargs...) } +// DeleteInvitation mocks base method. +func (m *MockOrganizationAPI) DeleteInvitation(ctx context.Context, id, invitationID string, opts ...management.RequestOption) error { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, id, invitationID} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "DeleteInvitation", varargs...) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteInvitation indicates an expected call of DeleteInvitation. +func (mr *MockOrganizationAPIMockRecorder) DeleteInvitation(ctx, id, invitationID interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, id, invitationID}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteInvitation", reflect.TypeOf((*MockOrganizationAPI)(nil).DeleteInvitation), varargs...) +} + // DiscoveryDomain mocks base method. func (m *MockOrganizationAPI) DiscoveryDomain(ctx context.Context, id, domainID string, opts ...management.RequestOption) (*management.OrganizationDiscoveryDomain, error) { m.ctrl.T.Helper() diff --git a/internal/auth0/organization.go b/internal/auth0/organization.go index e482b9343..a17a69c2f 100644 --- a/internal/auth0/organization.go +++ b/internal/auth0/organization.go @@ -73,4 +73,9 @@ type OrganizationAPI interface { // // See: https://auth0.com/docs/api/management/v2/#!/Organizations/post_invitations CreateInvitation(ctx context.Context, id string, i *management.OrganizationInvitation, opts ...management.RequestOption) (err error) + + // DeleteInvitation deletes an invitation to an organization. + // + // See: https://auth0.com/docs/api/management/v2/#!/Organizations/delete_invitations_by_invitation_id + DeleteInvitation(ctx context.Context, id string, invitationID string, opts ...management.RequestOption) (err error) } diff --git a/internal/cli/organizations.go b/internal/cli/organizations.go index 17519313c..e654fd77c 100644 --- a/internal/cli/organizations.go +++ b/internal/cli/organizations.go @@ -26,6 +26,11 @@ var ( Help: "ID of the organization.", } + invitationID = Argument{ + Name: "Invitation ID", + Help: "ID of the invitation.", + } + organizationName = Flag{ Name: "Name", LongForm: "name", @@ -811,6 +816,26 @@ func (cli *cli) organizationPickerOptions(ctx context.Context) (pickerOptions, e return opts, nil } +func (cli *cli) invitationPickerOptions(ctx context.Context, orgID string) (pickerOptions, error) { + invitations, err := cli.getOrgInvitations(ctx, orgID, 1000) + if err != nil { + return nil, err + } + + var opts pickerOptions + for _, inv := range invitations { + value := inv.GetID() + label := fmt.Sprintf("%s %s", inv.Invitee.GetEmail(), ansi.Faint("("+value+")")) + opts = append(opts, pickerOption{value: value, label: label}) + } + + if len(opts) == 0 { + return nil, errors.New("there are currently no invitations to choose from") + } + + return opts, nil +} + func formatOrganizationDetailsPath(id string) string { if len(id) == 0 { return "" @@ -996,6 +1021,7 @@ func invitationsOrganizationCmd(cli *cli) *cobra.Command { cmd.SetUsageTemplate(resourceUsageTemplate()) cmd.AddCommand(listInvitationsOrganizationCmd(cli)) cmd.AddCommand(createInvitationOrganizationCmd(cli)) + cmd.AddCommand(deleteInvitationOrganizationCmd(cli)) return cmd } @@ -1037,9 +1063,7 @@ func listInvitationsOrganizationCmd(cli *cli) *cobra.Command { } sortInvitations(invitations) - cli.renderer.InvitationsList(invitations) - return nil }, } @@ -1051,7 +1075,6 @@ func listInvitationsOrganizationCmd(cli *cli) *cobra.Command { cmd.Flags().BoolVar(&cli.jsonCompact, "json-compact", false, "Output in compact json format.") cmd.Flags().BoolVar(&cli.csv, "csv", false, "Output in csv format.") cmd.MarkFlagsMutuallyExclusive("json", "json-compact", "csv") - cmd.SetUsageTemplate(resourceUsageTemplate()) return cmd } @@ -1127,11 +1150,11 @@ func createInvitationOrganizationCmd(cli *cli) *cobra.Command { Short: "Create a new invitation to an organization", Long: "Create a new invitation to an organization.", Example: ` auth0 orgs invitations create - auth0 orgs invitations create - auth0 orgs invitations create --inviter-name "Inviter Name" --invitee-email "invitee@example.com" - auth0 orgs invitations create --invitee-email "invitee@example.com" --client-id "client_id" - auth0 orgs invitations create -n "Inviter Name" -e "invitee@example.com" -c "client_id" -connection-id "connection_id" -t 86400 - auth0 orgs invitations create --json --inviter-name "Inviter Name"`, + auth0 orgs invitations create + auth0 orgs invitations create --inviter-name "Inviter Name" --invitee-email "invitee@example.com" + auth0 orgs invitations create --invitee-email "invitee@example.com" --client-id "client_id" + auth0 orgs invitations create -n "Inviter Name" -e "invitee@example.com" -c "client_id" -connection-id "connection_id" -t 86400 + auth0 orgs invitations create --json --inviter-name "Inviter Name"`, RunE: func(cmd *cobra.Command, args []string) error { if len(args) == 0 { if err := organizationID.Pick(cmd, &inputs.OrgID, cli.organizationPickerOptions); err != nil { @@ -1201,10 +1224,71 @@ func createInvitationOrganizationCmd(cli *cli) *cobra.Command { cmd.Flags().BoolVar(&cli.json, "json", false, "Output in json format.") cmd.Flags().BoolVar(&cli.jsonCompact, "json-compact", false, "Output in compact json format.") - cmd.Flags().BoolVar(&cli.csv, "csv", false, "Output in csv format.") - cmd.MarkFlagsMutuallyExclusive("json", "json-compact", "csv") - // TODO shouldn't set csv? - cmd.SetUsageTemplate(resourceUsageTemplate()) + + return cmd +} + +func deleteInvitationOrganizationCmd(cli *cli) *cobra.Command { + var inputs struct { + OrgID string + } + + cmd := &cobra.Command{ + Use: "delete", + Aliases: []string{"rm"}, + Short: "Delete invitation(s) from an organization", + Long: "Delete invitation(s) from an organization.\n\n" + + "To delete interactively, use `auth0 orgs invitations delete` with no arguments.\n\n" + + "To delete non-interactively, supply the organization id, invitation id(s) and " + + "the `--force` flag to skip confirmation.", + Example: ` auth0 orgs invitations delete + auth0 orgs invitations rm + auth0 orgs invitations delete + auth0 orgs invitations delete --force + auth0 orgs invitations delete `, + RunE: func(cmd *cobra.Command, args []string) error { + if len(args) == 0 { + if err := organizationID.Pick(cmd, &inputs.OrgID, cli.organizationPickerOptions); err != nil { + return err + } + } else { + inputs.OrgID = args[0] + args = args[1:] + } + + invitationIDs := make([]string, len(args)) + if len(args) == 0 { + if err := invitationID.PickMany( + cmd, + &invitationIDs, + func(ctx context.Context) (pickerOptions, error) { + return cli.invitationPickerOptions(ctx, inputs.OrgID) + }, + ); err != nil { + return err + } + } else { + invitationIDs = append(invitationIDs, args...) + } + + if !cli.force && canPrompt(cmd) { + if confirmed := prompt.Confirm("Are you sure you want to proceed?"); !confirmed { + return nil + } + } + + return ansi.ProgressBar("Deleting invitation(s)", invitationIDs, func(_ int, invitationID string) error { + if invitationID != "" { + if err := cli.api.Organization.DeleteInvitation(cmd.Context(), inputs.OrgID, invitationID); err != nil { + return fmt.Errorf("failed to delete invitation with ID %q from organization %q: %w", invitationID, inputs.OrgID, err) + } + } + return nil + }) + }, + } + + cmd.Flags().BoolVar(&cli.force, "force", false, "Skip confirmation.") return cmd } diff --git a/internal/cli/organizations_test.go b/internal/cli/organizations_test.go index d27a65c1d..780c5eee6 100644 --- a/internal/cli/organizations_test.go +++ b/internal/cli/organizations_test.go @@ -92,3 +92,89 @@ func TestOrganizationsPickerOptions(t *testing.T) { }) } } + +func TestInvitationsPickerOptions(t *testing.T) { + tests := []struct { + name string + invitations []*management.OrganizationInvitation + apiError error + assertOutput func(t testing.TB, options pickerOptions) + assertError func(t testing.TB, err error) + }{ + { + name: "happy path", + invitations: []*management.OrganizationInvitation{ + { + ID: auth0.String("inv-id-1"), + Invitee: &management.OrganizationInvitationInvitee{ + Email: auth0.String("user1@example.com"), + }, + }, + { + ID: auth0.String("inv-id-2"), + Invitee: &management.OrganizationInvitationInvitee{ + Email: auth0.String("user2@example.com"), + }, + }, + }, + assertOutput: func(t testing.TB, options pickerOptions) { + assert.Len(t, options, 2) + assert.Equal(t, "user1@example.com (inv-id-1)", options[0].label) + assert.Equal(t, "inv-id-1", options[0].value) + assert.Equal(t, "user2@example.com (inv-id-2)", options[1].label) + assert.Equal(t, "inv-id-2", options[1].value) + }, + assertError: func(t testing.TB, err error) { + t.Fail() + }, + }, + { + name: "no invitations", + invitations: []*management.OrganizationInvitation{}, + assertOutput: func(t testing.TB, options pickerOptions) { + t.Fail() + }, + assertError: func(t testing.TB, err error) { + assert.Error(t, err) + assert.ErrorContains(t, err, "there are currently no invitations to choose from") + }, + }, + { + name: "API error", + apiError: errors.New("api error"), + assertOutput: func(t testing.TB, options pickerOptions) { + t.Fail() + }, + assertError: func(t testing.TB, err error) { + assert.Error(t, err) + assert.ErrorContains(t, err, "api error") + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + organizationAPI := mock.NewMockOrganizationAPI(ctrl) + organizationAPI.EXPECT(). + Invitations(gomock.Any(), gomock.Any(), gomock.Any()). + Return(&management.OrganizationInvitationList{ + OrganizationInvitations: test.invitations, + }, test.apiError) + + cli := &cli{ + api: &auth0.API{Organization: organizationAPI}, + } + + options, err := cli.invitationPickerOptions(context.Background(), "test-org-id") + + if err != nil { + test.assertError(t, err) + } else { + test.assertOutput(t, options) + } + }) + } +} From 04ea06e909448254d4b9cc43c86c22f332a3e0a8 Mon Sep 17 00:00:00 2001 From: Kiran Kumar Date: Thu, 5 Feb 2026 18:32:01 +0530 Subject: [PATCH 07/34] fix(arguments): ensure if user can be prompted before fetching picker options - Fixed PickMany to validate if the command can prompt for required arguments. - This prevents making redundant GET api calls to list picker options. - Removed the unused selectArgument function to streamline the code. --- internal/cli/arguments.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/internal/cli/arguments.go b/internal/cli/arguments.go index 5e1ea559d..4c311b0a1 100644 --- a/internal/cli/arguments.go +++ b/internal/cli/arguments.go @@ -37,6 +37,10 @@ func (a *Argument) Ask(cmd *cobra.Command, value interface{}) error { type pickerOptionsFunc func(ctx context.Context) (pickerOptions, error) func (a *Argument) Pick(cmd *cobra.Command, result *string, fn pickerOptionsFunc) error { + if !canPrompt(cmd) { + return fmt.Errorf("Missing a required argument: %s", a.GetName()) + } + var opts pickerOptions err := ansi.Waiting(func() error { var err error @@ -50,7 +54,7 @@ func (a *Argument) Pick(cmd *cobra.Command, result *string, fn pickerOptionsFunc defaultLabel := opts.defaultLabel() var val string - if err := selectArgument(cmd, a, &val, opts.labels(), &defaultLabel); err != nil { + if err := _select(a, &val, opts.labels(), &defaultLabel, false); err != nil { return err } @@ -59,6 +63,10 @@ func (a *Argument) Pick(cmd *cobra.Command, result *string, fn pickerOptionsFunc } func (a *Argument) PickMany(cmd *cobra.Command, result *[]string, fn pickerOptionsFunc) error { + if !canPrompt(cmd) { + return fmt.Errorf("Missing a required argument: %s", a.GetName()) + } + var opts pickerOptions err := ansi.Waiting(func() error { var err error @@ -79,14 +87,6 @@ func (a *Argument) PickMany(cmd *cobra.Command, result *[]string, fn pickerOptio return nil } -func selectArgument(cmd *cobra.Command, a *Argument, value interface{}, options []string, defaultValue *string) error { - if canPrompt(cmd) { - return _select(a, value, options, defaultValue, false) - } - - return fmt.Errorf("Missing a required argument: %s", a.GetName()) -} - func askArgument(cmd *cobra.Command, i commandInput, value interface{}) error { if canPrompt(cmd) { return ask(i, value, nil, false) From f247d1a174604aad8aa2eb28b4d70b4ad7d1827c Mon Sep 17 00:00:00 2001 From: Kiran Kumar Date: Thu, 5 Feb 2026 19:27:02 +0530 Subject: [PATCH 08/34] feat(invitations): add show invitation command and related functionality - Implemented the `show` command for displaying organization invitations. - Added `Invitation` method to the `OrganizationAPI` interface in `organization.go`. - Created mock methods in `organization_mock.go` for testing the new functionality. - Updated `invitations.go` to include a new renderer method for showing invitations. --- docs/auth0_orgs_invitations.md | 1 + docs/auth0_orgs_invitations_create.md | 1 + docs/auth0_orgs_invitations_delete.md | 1 + docs/auth0_orgs_invitations_list.md | 1 + docs/auth0_orgs_invitations_show.md | 50 ++++++++++++++++++++++ internal/auth0/mock/organization_mock.go | 20 +++++++++ internal/auth0/organization.go | 5 +++ internal/cli/organizations.go | 54 ++++++++++++++++++++++++ internal/display/invitations.go | 17 +++++++- 9 files changed, 148 insertions(+), 2 deletions(-) create mode 100644 docs/auth0_orgs_invitations_show.md diff --git a/docs/auth0_orgs_invitations.md b/docs/auth0_orgs_invitations.md index 05648da0c..5b192c45b 100644 --- a/docs/auth0_orgs_invitations.md +++ b/docs/auth0_orgs_invitations.md @@ -12,4 +12,5 @@ Manage invitations of an organization. - [auth0 orgs invitations create](auth0_orgs_invitations_create.md) - Create a new invitation to an organization - [auth0 orgs invitations delete](auth0_orgs_invitations_delete.md) - Delete invitation(s) from an organization - [auth0 orgs invitations list](auth0_orgs_invitations_list.md) - List invitations of an organization +- [auth0 orgs invitations show](auth0_orgs_invitations_show.md) - Show an organization invitation diff --git a/docs/auth0_orgs_invitations_create.md b/docs/auth0_orgs_invitations_create.md index 53280263d..d86f0f07f 100644 --- a/docs/auth0_orgs_invitations_create.md +++ b/docs/auth0_orgs_invitations_create.md @@ -56,5 +56,6 @@ auth0 orgs invitations create [flags] - [auth0 orgs invitations create](auth0_orgs_invitations_create.md) - Create a new invitation to an organization - [auth0 orgs invitations delete](auth0_orgs_invitations_delete.md) - Delete invitation(s) from an organization - [auth0 orgs invitations list](auth0_orgs_invitations_list.md) - List invitations of an organization +- [auth0 orgs invitations show](auth0_orgs_invitations_show.md) - Show an organization invitation diff --git a/docs/auth0_orgs_invitations_delete.md b/docs/auth0_orgs_invitations_delete.md index cb3205304..e9e3e94cf 100644 --- a/docs/auth0_orgs_invitations_delete.md +++ b/docs/auth0_orgs_invitations_delete.md @@ -49,5 +49,6 @@ auth0 orgs invitations delete [flags] - [auth0 orgs invitations create](auth0_orgs_invitations_create.md) - Create a new invitation to an organization - [auth0 orgs invitations delete](auth0_orgs_invitations_delete.md) - Delete invitation(s) from an organization - [auth0 orgs invitations list](auth0_orgs_invitations_list.md) - List invitations of an organization +- [auth0 orgs invitations show](auth0_orgs_invitations_show.md) - Show an organization invitation diff --git a/docs/auth0_orgs_invitations_list.md b/docs/auth0_orgs_invitations_list.md index 049e61d88..139c75383 100644 --- a/docs/auth0_orgs_invitations_list.md +++ b/docs/auth0_orgs_invitations_list.md @@ -49,5 +49,6 @@ auth0 orgs invitations list [flags] - [auth0 orgs invitations create](auth0_orgs_invitations_create.md) - Create a new invitation to an organization - [auth0 orgs invitations delete](auth0_orgs_invitations_delete.md) - Delete invitation(s) from an organization - [auth0 orgs invitations list](auth0_orgs_invitations_list.md) - List invitations of an organization +- [auth0 orgs invitations show](auth0_orgs_invitations_show.md) - Show an organization invitation diff --git a/docs/auth0_orgs_invitations_show.md b/docs/auth0_orgs_invitations_show.md new file mode 100644 index 000000000..99e9c86f0 --- /dev/null +++ b/docs/auth0_orgs_invitations_show.md @@ -0,0 +1,50 @@ +--- +layout: default +parent: auth0 orgs invitations +has_toc: false +--- +# auth0 orgs invitations show + +Display information about an organization invitation. + +## Usage +``` +auth0 orgs invitations show [flags] +``` + +## Examples + +``` + auth0 orgs invitations show + auth0 orgs invitations show + auth0 orgs invitations show + auth0 orgs invitations show --json +``` + + +## Flags + +``` + --json Output in json format. + --json-compact Output in compact json format. +``` + + +## Inherited Flags + +``` + --debug Enable debug mode. + --no-color Disable colors. + --no-input Disable interactivity. + --tenant string Specific tenant to use. +``` + + +## Related Commands + +- [auth0 orgs invitations create](auth0_orgs_invitations_create.md) - Create a new invitation to an organization +- [auth0 orgs invitations delete](auth0_orgs_invitations_delete.md) - Delete invitation(s) from an organization +- [auth0 orgs invitations list](auth0_orgs_invitations_list.md) - List invitations of an organization +- [auth0 orgs invitations show](auth0_orgs_invitations_show.md) - Show an organization invitation + + diff --git a/internal/auth0/mock/organization_mock.go b/internal/auth0/mock/organization_mock.go index 751bd50ed..a1b088717 100644 --- a/internal/auth0/mock/organization_mock.go +++ b/internal/auth0/mock/organization_mock.go @@ -209,6 +209,26 @@ func (mr *MockOrganizationAPIMockRecorder) DiscoveryDomains(ctx, id interface{}, return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DiscoveryDomains", reflect.TypeOf((*MockOrganizationAPI)(nil).DiscoveryDomains), varargs...) } +// Invitation mocks base method. +func (m *MockOrganizationAPI) Invitation(ctx context.Context, id, invitationID string, opts ...management.RequestOption) (*management.OrganizationInvitation, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, id, invitationID} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Invitation", varargs...) + ret0, _ := ret[0].(*management.OrganizationInvitation) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Invitation indicates an expected call of Invitation. +func (mr *MockOrganizationAPIMockRecorder) Invitation(ctx, id, invitationID interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, id, invitationID}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Invitation", reflect.TypeOf((*MockOrganizationAPI)(nil).Invitation), varargs...) +} + // Invitations mocks base method. func (m *MockOrganizationAPI) Invitations(ctx context.Context, id string, opts ...management.RequestOption) (*management.OrganizationInvitationList, error) { m.ctrl.T.Helper() diff --git a/internal/auth0/organization.go b/internal/auth0/organization.go index a17a69c2f..521092592 100644 --- a/internal/auth0/organization.go +++ b/internal/auth0/organization.go @@ -69,6 +69,11 @@ type OrganizationAPI interface { // See: https://auth0.com/docs/api/management/v2#!/Organizations/get_invitations Invitations(ctx context.Context, id string, opts ...management.RequestOption) (o *management.OrganizationInvitationList, err error) + // Invitation retrieves an invitation to an organization. + // + // See: https://auth0.com/docs/api/management/v2/#!/Organizations/get_invitations_by_invitation_id + Invitation(ctx context.Context, id string, invitationID string, opts ...management.RequestOption) (i *management.OrganizationInvitation, err error) + // CreateInvitation creates invitations to an organization. // // See: https://auth0.com/docs/api/management/v2/#!/Organizations/post_invitations diff --git a/internal/cli/organizations.go b/internal/cli/organizations.go index e654fd77c..10f27f42f 100644 --- a/internal/cli/organizations.go +++ b/internal/cli/organizations.go @@ -1020,12 +1020,66 @@ func invitationsOrganizationCmd(cli *cli) *cobra.Command { cmd.SetUsageTemplate(resourceUsageTemplate()) cmd.AddCommand(listInvitationsOrganizationCmd(cli)) + cmd.AddCommand(showInvitationOrganizationCmd(cli)) cmd.AddCommand(createInvitationOrganizationCmd(cli)) cmd.AddCommand(deleteInvitationOrganizationCmd(cli)) return cmd } +func showInvitationOrganizationCmd(cli *cli) *cobra.Command { + var inputs struct { + OrgID string + InvitationID string + } + + cmd := &cobra.Command{ + Use: "show", + Args: cobra.MaximumNArgs(2), + Short: "Show an organization invitation", + Long: "Display information about an organization invitation.", + Example: ` auth0 orgs invitations show + auth0 orgs invitations show + auth0 orgs invitations show + auth0 orgs invitations show --json`, + RunE: func(cmd *cobra.Command, args []string) error { + if len(args) == 0 { + if err := organizationID.Pick(cmd, &inputs.OrgID, cli.organizationPickerOptions); err != nil { + return err + } + } else { + inputs.OrgID = args[0] + } + + if len(args) <= 1 { + if err := invitationID.Pick(cmd, &inputs.InvitationID, func(ctx context.Context) (pickerOptions, error) { + return cli.invitationPickerOptions(ctx, inputs.OrgID) + }); err != nil { + return err + } + } else { + inputs.InvitationID = args[1] + } + + var invitation *management.OrganizationInvitation + if err := ansi.Waiting(func() (err error) { + invitation, err = cli.api.Organization.Invitation(cmd.Context(), inputs.OrgID, inputs.InvitationID) + return err + }); err != nil { + return fmt.Errorf("failed to read organization invitation %q: %w", inputs.InvitationID, err) + } + + cli.renderer.InvitationsShow(*invitation) + return nil + }, + } + + cmd.Flags().BoolVar(&cli.json, "json", false, "Output in json format.") + cmd.Flags().BoolVar(&cli.jsonCompact, "json-compact", false, "Output in compact json format.") + + return cmd +} + func listInvitationsOrganizationCmd(cli *cli) *cobra.Command { var inputs struct { ID string diff --git a/internal/display/invitations.go b/internal/display/invitations.go index dbbbb57a1..f0ffebda4 100644 --- a/internal/display/invitations.go +++ b/internal/display/invitations.go @@ -15,7 +15,7 @@ type invitationsView struct { CreatedAt string ExpiresAt string raw interface{} - // TODO: Check if OrganizationId, InvitationURL, Role assignments etc are needed. + // TODO: Check if OrganizationId, InvitationURL, Role assignments etc are needed to be displayed in table view. } func (v *invitationsView) AsTableHeader() []string { @@ -27,7 +27,15 @@ func (v *invitationsView) AsTableRow() []string { } func (v *invitationsView) KeyValues() [][]string { - return [][]string{} + return [][]string{ + {"ID", ansi.Faint(v.ID)}, + {"CLIENT ID", v.ClientID}, + {"CONNECTION ID", v.ConnectionID}, + {"INVITER NAME", v.InviterName}, + {"INVITEE EMAIL", v.InviteeEmail}, + {"CREATED AT", v.CreatedAt}, + {"EXPIRES AT", v.ExpiresAt}, + } } func (v *invitationsView) Object() interface{} { @@ -57,6 +65,11 @@ func (r *Renderer) InvitationsCreate(invitation management.OrganizationInvitatio r.Result(makeInvitationsView(invitation)) } +func (r *Renderer) InvitationsShow(invitation management.OrganizationInvitation) { + r.Heading("organization invitation") + r.Result(makeInvitationsView(invitation)) +} + func makeInvitationsView(invitation management.OrganizationInvitation) *invitationsView { return &invitationsView{ ID: invitation.GetID(), From fc538b26d90b8be1a7549c117b383f2178cb6384 Mon Sep 17 00:00:00 2001 From: Kiran Kumar Date: Thu, 5 Feb 2026 19:34:55 +0530 Subject: [PATCH 09/34] feat(invitations): add aliase invs for invitations commands - Introduced aliases for the invitations commands to enhance usability: - Updated examples in the command documentation to reflect the new aliases. - This change aims to streamline command usage and improve user experience. --- docs/auth0_orgs_invitations_create.md | 12 +++---- docs/auth0_orgs_invitations_delete.md | 12 +++---- docs/auth0_orgs_invitations_list.md | 12 +++---- docs/auth0_orgs_invitations_show.md | 8 ++--- internal/cli/organizations.go | 51 ++++++++++++++------------- 5 files changed, 48 insertions(+), 47 deletions(-) diff --git a/docs/auth0_orgs_invitations_create.md b/docs/auth0_orgs_invitations_create.md index d86f0f07f..ed6d11f9e 100644 --- a/docs/auth0_orgs_invitations_create.md +++ b/docs/auth0_orgs_invitations_create.md @@ -15,12 +15,12 @@ auth0 orgs invitations create [flags] ## Examples ``` - auth0 orgs invitations create - auth0 orgs invitations create - auth0 orgs invitations create --inviter-name "Inviter Name" --invitee-email "invitee@example.com" - auth0 orgs invitations create --invitee-email "invitee@example.com" --client-id "client_id" - auth0 orgs invitations create -n "Inviter Name" -e "invitee@example.com" -c "client_id" -connection-id "connection_id" -t 86400 - auth0 orgs invitations create --json --inviter-name "Inviter Name" + auth0 orgs invs create + auth0 orgs invs create + auth0 orgs invs create --inviter-name "Inviter Name" --invitee-email "invitee@example.com" + auth0 orgs invs create --invitee-email "invitee@example.com" --client-id "client_id" + auth0 orgs invs create -n "Inviter Name" -e "invitee@example.com" -c "client_id" -connection-id "connection_id" -t 86400 + auth0 orgs invs create --json --inviter-name "Inviter Name" ``` diff --git a/docs/auth0_orgs_invitations_delete.md b/docs/auth0_orgs_invitations_delete.md index e9e3e94cf..535fc9f06 100644 --- a/docs/auth0_orgs_invitations_delete.md +++ b/docs/auth0_orgs_invitations_delete.md @@ -7,7 +7,7 @@ has_toc: false Delete invitation(s) from an organization. -To delete interactively, use `auth0 orgs invitations delete` with no arguments. +To delete interactively, use `auth0 orgs invs delete` with no arguments. To delete non-interactively, supply the organization id, invitation id(s) and the `--force` flag to skip confirmation. @@ -19,11 +19,11 @@ auth0 orgs invitations delete [flags] ## Examples ``` - auth0 orgs invitations delete - auth0 orgs invitations rm - auth0 orgs invitations delete - auth0 orgs invitations delete --force - auth0 orgs invitations delete + auth0 orgs invs delete + auth0 orgs invs rm + auth0 orgs invs delete + auth0 orgs invs delete --force + auth0 orgs invs delete ``` diff --git a/docs/auth0_orgs_invitations_list.md b/docs/auth0_orgs_invitations_list.md index 139c75383..5686cb714 100644 --- a/docs/auth0_orgs_invitations_list.md +++ b/docs/auth0_orgs_invitations_list.md @@ -15,12 +15,12 @@ auth0 orgs invitations list [flags] ## Examples ``` - auth0 orgs invitations list - auth0 orgs invitations ls - auth0 orgs invitations list --number 100 - auth0 orgs invitations ls -n 100 --json - auth0 orgs invitations ls -n 100 --json-compact - auth0 orgs invitations ls --csv + auth0 orgs invs list + auth0 orgs invs ls + auth0 orgs invs list --number 100 + auth0 orgs invs ls -n 100 --json + auth0 orgs invs ls -n 100 --json-compact + auth0 orgs invs ls --csv ``` diff --git a/docs/auth0_orgs_invitations_show.md b/docs/auth0_orgs_invitations_show.md index 99e9c86f0..597d2fd6e 100644 --- a/docs/auth0_orgs_invitations_show.md +++ b/docs/auth0_orgs_invitations_show.md @@ -15,10 +15,10 @@ auth0 orgs invitations show [flags] ## Examples ``` - auth0 orgs invitations show - auth0 orgs invitations show - auth0 orgs invitations show - auth0 orgs invitations show --json + auth0 orgs invs show + auth0 orgs invs show + auth0 orgs invs show + auth0 orgs invs show --json ``` diff --git a/internal/cli/organizations.go b/internal/cli/organizations.go index 10f27f42f..7c47a66f3 100644 --- a/internal/cli/organizations.go +++ b/internal/cli/organizations.go @@ -1013,9 +1013,10 @@ func (cli *cli) getOrgRoleMembersWithSpinner( func invitationsOrganizationCmd(cli *cli) *cobra.Command { cmd := &cobra.Command{ - Use: "invitations", - Short: "Manage invitations of an organization", - Long: "Manage invitations of an organization.", + Use: "invitations", + Aliases: []string{"invs"}, + Short: "Manage invitations of an organization", + Long: "Manage invitations of an organization.", } cmd.SetUsageTemplate(resourceUsageTemplate()) @@ -1038,10 +1039,10 @@ func showInvitationOrganizationCmd(cli *cli) *cobra.Command { Args: cobra.MaximumNArgs(2), Short: "Show an organization invitation", Long: "Display information about an organization invitation.", - Example: ` auth0 orgs invitations show - auth0 orgs invitations show - auth0 orgs invitations show - auth0 orgs invitations show --json`, + Example: ` auth0 orgs invs show + auth0 orgs invs show + auth0 orgs invs show + auth0 orgs invs show --json`, RunE: func(cmd *cobra.Command, args []string) error { if len(args) == 0 { if err := organizationID.Pick(cmd, &inputs.OrgID, cli.organizationPickerOptions); err != nil { @@ -1092,12 +1093,12 @@ func listInvitationsOrganizationCmd(cli *cli) *cobra.Command { Args: cobra.MaximumNArgs(1), Short: "List invitations of an organization", Long: "List the invitations of an organization.", - Example: ` auth0 orgs invitations list - auth0 orgs invitations ls - auth0 orgs invitations list --number 100 - auth0 orgs invitations ls -n 100 --json - auth0 orgs invitations ls -n 100 --json-compact - auth0 orgs invitations ls --csv`, + Example: ` auth0 orgs invs list + auth0 orgs invs ls + auth0 orgs invs list --number 100 + auth0 orgs invs ls -n 100 --json + auth0 orgs invs ls -n 100 --json-compact + auth0 orgs invs ls --csv`, RunE: func(cmd *cobra.Command, args []string) error { if inputs.Number < 1 || inputs.Number > 1000 { return fmt.Errorf("number flag invalid, please pass a number between 1 and 1000") @@ -1203,12 +1204,12 @@ func createInvitationOrganizationCmd(cli *cli) *cobra.Command { Args: cobra.MaximumNArgs(1), Short: "Create a new invitation to an organization", Long: "Create a new invitation to an organization.", - Example: ` auth0 orgs invitations create - auth0 orgs invitations create - auth0 orgs invitations create --inviter-name "Inviter Name" --invitee-email "invitee@example.com" - auth0 orgs invitations create --invitee-email "invitee@example.com" --client-id "client_id" - auth0 orgs invitations create -n "Inviter Name" -e "invitee@example.com" -c "client_id" -connection-id "connection_id" -t 86400 - auth0 orgs invitations create --json --inviter-name "Inviter Name"`, + Example: ` auth0 orgs invs create + auth0 orgs invs create + auth0 orgs invs create --inviter-name "Inviter Name" --invitee-email "invitee@example.com" + auth0 orgs invs create --invitee-email "invitee@example.com" --client-id "client_id" + auth0 orgs invs create -n "Inviter Name" -e "invitee@example.com" -c "client_id" -connection-id "connection_id" -t 86400 + auth0 orgs invs create --json --inviter-name "Inviter Name"`, RunE: func(cmd *cobra.Command, args []string) error { if len(args) == 0 { if err := organizationID.Pick(cmd, &inputs.OrgID, cli.organizationPickerOptions); err != nil { @@ -1292,14 +1293,14 @@ func deleteInvitationOrganizationCmd(cli *cli) *cobra.Command { Aliases: []string{"rm"}, Short: "Delete invitation(s) from an organization", Long: "Delete invitation(s) from an organization.\n\n" + - "To delete interactively, use `auth0 orgs invitations delete` with no arguments.\n\n" + + "To delete interactively, use `auth0 orgs invs delete` with no arguments.\n\n" + "To delete non-interactively, supply the organization id, invitation id(s) and " + "the `--force` flag to skip confirmation.", - Example: ` auth0 orgs invitations delete - auth0 orgs invitations rm - auth0 orgs invitations delete - auth0 orgs invitations delete --force - auth0 orgs invitations delete `, + Example: ` auth0 orgs invs delete + auth0 orgs invs rm + auth0 orgs invs delete + auth0 orgs invs delete --force + auth0 orgs invs delete `, RunE: func(cmd *cobra.Command, args []string) error { if len(args) == 0 { if err := organizationID.Pick(cmd, &inputs.OrgID, cli.organizationPickerOptions); err != nil { From d3856fc74371e0b5fd953c93956a72cf2248fc50 Mon Sep 17 00:00:00 2001 From: Kiran Kumar Date: Fri, 6 Feb 2026 15:40:40 +0530 Subject: [PATCH 10/34] feat(invitations): add integration tests for organization invitations - Implemented new test cases for listing, creating, showing, and deleting organization invitations. - Added tests to verify behavior with valid and invalid inputs, including edge cases for missing arguments and invalid IDs. - Created a new script `get-org-invitation-id.sh` to facilitate the retrieval of invitation IDs for testing. - Updated existing `get-app-id.sh` script to ensure compatibility with the organization-invitation functionality. --- internal/display/invitations.go | 2 +- .../integration/organizations-test-cases.yaml | 142 ++++++++++++++++++ test/integration/scripts/get-app-id.sh | 7 +- .../scripts/get-org-invitation-id.sh | 20 +++ 4 files changed, 169 insertions(+), 2 deletions(-) create mode 100755 test/integration/scripts/get-org-invitation-id.sh diff --git a/internal/display/invitations.go b/internal/display/invitations.go index f0ffebda4..6b58b3507 100644 --- a/internal/display/invitations.go +++ b/internal/display/invitations.go @@ -48,7 +48,7 @@ func (r *Renderer) InvitationsList(invitations []management.OrganizationInvitati r.Heading(resource) if len(invitations) == 0 { - r.EmptyState(resource, "Use 'auth0 orgs invitations create' to add one") + r.EmptyState(resource, "Use 'auth0 orgs invs create' to add one") return } diff --git a/test/integration/organizations-test-cases.yaml b/test/integration/organizations-test-cases.yaml index 113b2c60e..72c119344 100644 --- a/test/integration/organizations-test-cases.yaml +++ b/test/integration/organizations-test-cases.yaml @@ -176,3 +176,145 @@ tests: stderr: contains: - Number flag invalid, please pass a number between 1 and 1000 + + 023 - list organization invitations with no data: + command: auth0 orgs invs list $(./test/integration/scripts/get-org-id.sh) + exit-code: 0 + stderr: + contains: + - Use 'auth0 orgs invs create' to add one + + 024 - list organization invitations with no data (json): + command: auth0 orgs invs list $(./test/integration/scripts/get-org-id.sh) --json + exit-code: 0 + stdout: + exactly: "[]" + + 025 - list organization invitations with invalid number arg: + command: auth0 orgs invs list $(./test/integration/scripts/get-org-id.sh) --number 1001 + exit-code: 1 + stderr: + contains: + - Number flag invalid, please pass a number between 1 and 1000 + + 026 - list organization invitations without org id and no-input flag: + command: auth0 orgs invitations list --no-input + exit-code: 1 + stderr: + contains: + - "Missing a required argument: Org ID" + + 027 - create organization invitation and check json output: + command: auth0 orgs invs create $(./test/integration/scripts/get-org-id.sh) --inviter-name "Integration Tester" --invitee-email "test@test.com" --client-id $(./test/integration/scripts/get-app-id.sh) --json --no-input + exit-code: 0 + stdout: + json: + inviter.name: "Integration Tester" + invitee.email: "test@test.com" + + 028 - create organization invitation and check table output: + command: auth0 orgs invs create $(./test/integration/scripts/get-org-id.sh) --inviter-name "Integration Tester" --invitee-email "test@test.com" --client-id $(./test/integration/scripts/get-app-id.sh) --ttl-sec 86400 --no-input + exit-code: 0 + stdout: + contains: + - ID + - INVITER NAME + - Integration Tester + - INVITEE EMAIL + - test@test.com + + 029 - list organization invitations with data: + command: auth0 orgs invs list $(./test/integration/scripts/get-org-id.sh) + exit-code: 0 + stdout: + contains: + - ID + - INVITER NAME + - INVITEE EMAIL + + 030 - list organization invitations with data (json): + command: auth0 orgs invs ls $(./test/integration/scripts/get-org-id.sh) --json + exit-code: 0 + stdout: + contains: + - '"inviter":' + - '"invitee":' + - '"email":' + + 031 - list organization invitations with csv output: + command: auth0 orgs invs ls $(./test/integration/scripts/get-org-id.sh) --csv + exit-code: 0 + stdout: + contains: + - ID,Client ID,Connection ID,Inviter Name,Invitee Email,Created At,Expires At + + 032 - show organization invitation and check json output: + command: auth0 orgs invs show $(./test/integration/scripts/get-org-id.sh) $(./test/integration/scripts/get-org-invitation-id.sh) --json + exit-code: 0 + stdout: + contains: + json: + inviter.name: "Integration Tester" + invitee.email: "test@test.com" + config: + retries: 5 + + 033 - show organization invitation and check table output: + command: auth0 orgs invs show $(./test/integration/scripts/get-org-id.sh) $(./test/integration/scripts/get-org-invitation-id.sh) + exit-code: 0 + stdout: + contains: + - ID + - INVITER NAME + - Integration Tester + - INVITEE EMAIL + - test@test.com + config: + retries: 3 + + 034 - show organization invitation with invalid invitation ID: + command: auth0 orgs invs show $(./test/integration/scripts/get-org-id.sh) "uinv_invalid123" + exit-code: 1 + stderr: + contains: + - Failed to read organization invitation + + 035 - show organization invitation with invalid organization ID: + command: auth0 orgs invs show "org_invalid123" "uinv_something" + exit-code: 1 + stderr: + contains: + - Failed to read organization invitation + + 036 - show organization invitation without org id and no-input flag: + command: auth0 orgs invitations show --no-input + exit-code: 1 + stderr: + contains: + - "Missing a required argument: Org ID" + + 037 - show organization invitation without invitation id and no-input flag: + command: auth0 orgs invitations show $(./test/integration/scripts/get-org-id.sh) --no-input + exit-code: 1 + stderr: + contains: + - "Missing a required argument: Invitation ID" + + 038 - delete organization invitation with confirmation: + command: auth0 orgs invs delete $(./test/integration/scripts/get-org-id.sh) $(./test/integration/scripts/get-org-invitation-id.sh) --force + exit-code: 0 + + 039 - delete organization invitation with invalid invitation ID: + command: auth0 orgs invs delete $(./test/integration/scripts/get-org-id.sh) "uinv_invalid123" --force --no-input + exit-code: 1 + stderr: + contains: + - Failed to delete invitation + + 040 - verify invitations list is empty after deletion: + command: auth0 orgs invs list $(./test/integration/scripts/get-org-id.sh) --json + exit-code: 0 + stdout: + exactly: "[]" + config: + retries: 5 diff --git a/test/integration/scripts/get-app-id.sh b/test/integration/scripts/get-app-id.sh index 425f25d0a..e9f15487b 100755 --- a/test/integration/scripts/get-app-id.sh +++ b/test/integration/scripts/get-app-id.sh @@ -8,6 +8,11 @@ fi app=$( auth0 apps create -n integration-test-app-newapp -t native --description NewApp --json --no-input ) +client_id=$( echo "$app" | jq -r '.["client_id"]' ) + +# Enable organization support to allow the app to be used for org invitations +auth0 api patch "clients/${client_id}" --data '{"organization_usage":"allow","initiate_login_uri":"https://example.com/login"}' > /dev/null + mkdir -p ./test/integration/identifiers -echo "$app" | jq -r '.["client_id"]' > $FILE +echo "$client_id" > $FILE cat $FILE diff --git a/test/integration/scripts/get-org-invitation-id.sh b/test/integration/scripts/get-org-invitation-id.sh new file mode 100755 index 000000000..3653470bb --- /dev/null +++ b/test/integration/scripts/get-org-invitation-id.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +FILE=./test/integration/identifiers/org-invitation-id +if [ -f "$FILE" ]; then + cat $FILE + exit 0 +fi + +org_id=$(./test/integration/scripts/get-org-id.sh) +app_id=$(./test/integration/scripts/get-app-id.sh) + +invitation=$( auth0 orgs invitations create "$org_id" \ + --inviter-name "Integration Tester" \ + --invitee-email "test@test.com" \ + --client-id "$app_id" \ + --json --no-input ) + +mkdir -p ./test/integration/identifiers +echo "$invitation" | jq -r '.["id"]' > $FILE +cat $FILE From 4812075944da715ee51729ce6dac40833e2b45d4 Mon Sep 17 00:00:00 2001 From: Kiran Kumar Date: Fri, 6 Feb 2026 15:41:12 +0530 Subject: [PATCH 11/34] feat(invitations): add required scopes for organization invitations - Added new required scopes for managing organization invitations: - `read:organization_invitations` - `create:organization_invitations` - `delete:organization_invitations` This change is necessary to ensure that the application has the appropriate permissions to handle organization invitations effectively. --- internal/auth/auth.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/auth/auth.go b/internal/auth/auth.go index 7d039b0f2..3398f1999 100644 --- a/internal/auth/auth.go +++ b/internal/auth/auth.go @@ -149,6 +149,7 @@ var RequiredScopes = []string{ "read:event_streams", "create:event_streams", "update:event_streams", "delete:event_streams", "read:network_acls", "create:network_acls", "update:network_acls", "delete:network_acls", "read:token_exchange_profiles", "create:token_exchange_profiles", "update:token_exchange_profiles", "delete:token_exchange_profiles", + "read:organization_invitations", "create:organization_invitations", "delete:organization_invitations", } // GetDeviceCode kicks-off the device authentication flow by requesting From 1489749156932c06fbc9f56c4dce486b3b343dd7 Mon Sep 17 00:00:00 2001 From: Kiran Kumar Date: Fri, 6 Feb 2026 15:52:44 +0530 Subject: [PATCH 12/34] feat(tests): Use generic extra scopes for testing instead of organization_invitations - Replaced the previous organization invitation scopes with generic extra scopes for better test coverage. --- internal/config/tenant_test.go | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/internal/config/tenant_test.go b/internal/config/tenant_test.go index 1531c8fe7..906855969 100644 --- a/internal/config/tenant_test.go +++ b/internal/config/tenant_test.go @@ -21,21 +21,17 @@ func TestTenant_GetExtraRequestedScopes(t *testing.T) { }) t.Run("tenant has extra requested scopes", func(t *testing.T) { - tenant := &Tenant{ - Scopes: []string{ - "create:organization_invitations", - "read:organization_invitations", - "delete:organization_invitations", - }, + extraScopes := []string{ + "create:extra_scope1", + "read:extra_scope2", + "delete:extra_scope3", } - expected := []string{ - "create:organization_invitations", - "read:organization_invitations", - "delete:organization_invitations", + tenant := &Tenant{ + Scopes: extraScopes, } - assert.ElementsMatch(t, expected, tenant.GetExtraRequestedScopes()) + assert.ElementsMatch(t, extraScopes, tenant.GetExtraRequestedScopes()) }) } From 1e11881edde18a01ee4515595b28c3e16f19a812 Mon Sep 17 00:00:00 2001 From: Kiran Kumar Date: Fri, 6 Feb 2026 18:14:07 +0530 Subject: [PATCH 13/34] chore(invitations): increase retry count for organization invitation tests --- test/integration/organizations-test-cases.yaml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/test/integration/organizations-test-cases.yaml b/test/integration/organizations-test-cases.yaml index 72c119344..abd22bff7 100644 --- a/test/integration/organizations-test-cases.yaml +++ b/test/integration/organizations-test-cases.yaml @@ -211,6 +211,8 @@ tests: json: inviter.name: "Integration Tester" invitee.email: "test@test.com" + config: + retries: 5 028 - create organization invitation and check table output: command: auth0 orgs invs create $(./test/integration/scripts/get-org-id.sh) --inviter-name "Integration Tester" --invitee-email "test@test.com" --client-id $(./test/integration/scripts/get-app-id.sh) --ttl-sec 86400 --no-input @@ -222,6 +224,8 @@ tests: - Integration Tester - INVITEE EMAIL - test@test.com + config: + retries: 5 029 - list organization invitations with data: command: auth0 orgs invs list $(./test/integration/scripts/get-org-id.sh) @@ -231,6 +235,8 @@ tests: - ID - INVITER NAME - INVITEE EMAIL + config: + retries: 5 030 - list organization invitations with data (json): command: auth0 orgs invs ls $(./test/integration/scripts/get-org-id.sh) --json @@ -240,6 +246,8 @@ tests: - '"inviter":' - '"invitee":' - '"email":' + config: + retries: 5 031 - list organization invitations with csv output: command: auth0 orgs invs ls $(./test/integration/scripts/get-org-id.sh) --csv @@ -247,6 +255,8 @@ tests: stdout: contains: - ID,Client ID,Connection ID,Inviter Name,Invitee Email,Created At,Expires At + config: + retries: 5 032 - show organization invitation and check json output: command: auth0 orgs invs show $(./test/integration/scripts/get-org-id.sh) $(./test/integration/scripts/get-org-invitation-id.sh) --json @@ -270,7 +280,7 @@ tests: - INVITEE EMAIL - test@test.com config: - retries: 3 + retries: 5 034 - show organization invitation with invalid invitation ID: command: auth0 orgs invs show $(./test/integration/scripts/get-org-id.sh) "uinv_invalid123" @@ -303,6 +313,8 @@ tests: 038 - delete organization invitation with confirmation: command: auth0 orgs invs delete $(./test/integration/scripts/get-org-id.sh) $(./test/integration/scripts/get-org-invitation-id.sh) --force exit-code: 0 + config: + retries: 5 039 - delete organization invitation with invalid invitation ID: command: auth0 orgs invs delete $(./test/integration/scripts/get-org-id.sh) "uinv_invalid123" --force --no-input From 615ff9c0da6adacdcb026dd1600c2c0693bf381f Mon Sep 17 00:00:00 2001 From: Kiran Kumar Date: Fri, 6 Feb 2026 19:44:43 +0530 Subject: [PATCH 14/34] feat(tests): add cleanup script for app identifier after deletion - Introduced a new script `delete-app-id.sh` to remove the app identifier file created during integration tests. - This script ensures that the identifier file is deleted only if the app is successfully removed, preventing stale data in subsequent test runs. - Updated the test case for app deletion to call this cleanup script after the app is deleted. --- test/integration/apps-test-cases.yaml | 2 +- test/integration/organizations-test-cases.yaml | 14 -------------- test/integration/scripts/delete-app-id.sh | 15 +++++++++++++++ 3 files changed, 16 insertions(+), 15 deletions(-) create mode 100755 test/integration/scripts/delete-app-id.sh diff --git a/test/integration/apps-test-cases.yaml b/test/integration/apps-test-cases.yaml index 50b6ad874..be6c84ec3 100644 --- a/test/integration/apps-test-cases.yaml +++ b/test/integration/apps-test-cases.yaml @@ -311,7 +311,7 @@ tests: - DEVICE BINDING asn 041 - given a test app, it successfully deletes the app: - command: auth0 apps delete $(./test/integration/scripts/get-app-id.sh) --force + command: auth0 apps delete $(./test/integration/scripts/get-app-id.sh) --force && ./test/integration/scripts/delete-app-id.sh exit-code: 0 042 - it successfully creates a resource server app with resource-server-identifier: diff --git a/test/integration/organizations-test-cases.yaml b/test/integration/organizations-test-cases.yaml index abd22bff7..37112831b 100644 --- a/test/integration/organizations-test-cases.yaml +++ b/test/integration/organizations-test-cases.yaml @@ -211,8 +211,6 @@ tests: json: inviter.name: "Integration Tester" invitee.email: "test@test.com" - config: - retries: 5 028 - create organization invitation and check table output: command: auth0 orgs invs create $(./test/integration/scripts/get-org-id.sh) --inviter-name "Integration Tester" --invitee-email "test@test.com" --client-id $(./test/integration/scripts/get-app-id.sh) --ttl-sec 86400 --no-input @@ -224,8 +222,6 @@ tests: - Integration Tester - INVITEE EMAIL - test@test.com - config: - retries: 5 029 - list organization invitations with data: command: auth0 orgs invs list $(./test/integration/scripts/get-org-id.sh) @@ -235,8 +231,6 @@ tests: - ID - INVITER NAME - INVITEE EMAIL - config: - retries: 5 030 - list organization invitations with data (json): command: auth0 orgs invs ls $(./test/integration/scripts/get-org-id.sh) --json @@ -246,8 +240,6 @@ tests: - '"inviter":' - '"invitee":' - '"email":' - config: - retries: 5 031 - list organization invitations with csv output: command: auth0 orgs invs ls $(./test/integration/scripts/get-org-id.sh) --csv @@ -255,8 +247,6 @@ tests: stdout: contains: - ID,Client ID,Connection ID,Inviter Name,Invitee Email,Created At,Expires At - config: - retries: 5 032 - show organization invitation and check json output: command: auth0 orgs invs show $(./test/integration/scripts/get-org-id.sh) $(./test/integration/scripts/get-org-invitation-id.sh) --json @@ -266,8 +256,6 @@ tests: json: inviter.name: "Integration Tester" invitee.email: "test@test.com" - config: - retries: 5 033 - show organization invitation and check table output: command: auth0 orgs invs show $(./test/integration/scripts/get-org-id.sh) $(./test/integration/scripts/get-org-invitation-id.sh) @@ -279,8 +267,6 @@ tests: - Integration Tester - INVITEE EMAIL - test@test.com - config: - retries: 5 034 - show organization invitation with invalid invitation ID: command: auth0 orgs invs show $(./test/integration/scripts/get-org-id.sh) "uinv_invalid123" diff --git a/test/integration/scripts/delete-app-id.sh b/test/integration/scripts/delete-app-id.sh new file mode 100755 index 000000000..38520f478 --- /dev/null +++ b/test/integration/scripts/delete-app-id.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +// This script is used to delete the app identifier file created during integration tests. It checks if the file exists and removes it if it does. +// This script should be called only if the app is successfully deleted in the test cases, to ensure that the identifier file is cleaned up for subsequent test runs. + +FILE=./test/integration/identifiers/app-id + +# Remove the app identifier file only if it exists +if [ -f "$FILE" ]; then + rm "$FILE" + exit $? +fi + +# File doesn't exist, nothing to do +exit 0 From 662a8d893ed0856ee55fe9afc1b9a2c7627d4c33 Mon Sep 17 00:00:00 2001 From: Kiran Kumar Date: Fri, 6 Feb 2026 20:05:23 +0530 Subject: [PATCH 15/34] chore(invitations): remove TODO comment regarding additional fields in invitationsView --- internal/display/invitations.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/display/invitations.go b/internal/display/invitations.go index 6b58b3507..72b9d534a 100644 --- a/internal/display/invitations.go +++ b/internal/display/invitations.go @@ -15,7 +15,6 @@ type invitationsView struct { CreatedAt string ExpiresAt string raw interface{} - // TODO: Check if OrganizationId, InvitationURL, Role assignments etc are needed to be displayed in table view. } func (v *invitationsView) AsTableHeader() []string { From 43865dd4686ac32d85514487a72ea077aeb25fda Mon Sep 17 00:00:00 2001 From: Kiran Kumar Date: Mon, 9 Feb 2026 18:26:28 +0530 Subject: [PATCH 16/34] chore(invitations): remove the short form for the `--client-id` flag in the command - Updated example commands in the documentation to reflect the removal of the short form. - Ensured consistency in the command usage by using the long form for `--client-id`. --- docs/auth0_orgs_invitations_create.md | 4 ++-- internal/cli/organizations.go | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/auth0_orgs_invitations_create.md b/docs/auth0_orgs_invitations_create.md index ed6d11f9e..d3c66c9f4 100644 --- a/docs/auth0_orgs_invitations_create.md +++ b/docs/auth0_orgs_invitations_create.md @@ -19,7 +19,7 @@ auth0 orgs invitations create [flags] auth0 orgs invs create auth0 orgs invs create --inviter-name "Inviter Name" --invitee-email "invitee@example.com" auth0 orgs invs create --invitee-email "invitee@example.com" --client-id "client_id" - auth0 orgs invs create -n "Inviter Name" -e "invitee@example.com" -c "client_id" -connection-id "connection_id" -t 86400 + auth0 orgs invs create -n "Inviter Name" -e "invitee@example.com" --client-id "client_id" -connection-id "connection_id" -t 86400 auth0 orgs invs create --json --inviter-name "Inviter Name" ``` @@ -28,7 +28,7 @@ auth0 orgs invitations create [flags] ``` -a, --app-metadata stringToString Application metadata for the invited user in key=value format. (default []) - -c, --client-id string Auth0 client ID. Used to resolve the application's login initiation endpoint. + --client-id string Auth0 client ID. Used to resolve the application's login initiation endpoint. --connection-id string The id of the connection to force invitee to authenticate with. -e, --invitee-email string Email address of the person being invited. -n, --inviter-name string Name of the person sending the invitation. diff --git a/internal/cli/organizations.go b/internal/cli/organizations.go index 7c47a66f3..94d19c886 100644 --- a/internal/cli/organizations.go +++ b/internal/cli/organizations.go @@ -109,7 +109,6 @@ var ( clientID = Flag{ Name: "Client ID", LongForm: "client-id", - ShortForm: "c", Help: "Auth0 client ID. Used to resolve the application's login initiation endpoint.", IsRequired: true, } @@ -1208,7 +1207,7 @@ func createInvitationOrganizationCmd(cli *cli) *cobra.Command { auth0 orgs invs create auth0 orgs invs create --inviter-name "Inviter Name" --invitee-email "invitee@example.com" auth0 orgs invs create --invitee-email "invitee@example.com" --client-id "client_id" - auth0 orgs invs create -n "Inviter Name" -e "invitee@example.com" -c "client_id" -connection-id "connection_id" -t 86400 + auth0 orgs invs create -n "Inviter Name" -e "invitee@example.com" --client-id "client_id" -connection-id "connection_id" -t 86400 auth0 orgs invs create --json --inviter-name "Inviter Name"`, RunE: func(cmd *cobra.Command, args []string) error { if len(args) == 0 { From 11646ac321c36b9fd06423b352704b5e32471265 Mon Sep 17 00:00:00 2001 From: Kiran Kumar Date: Mon, 9 Feb 2026 18:27:02 +0530 Subject: [PATCH 17/34] fix(invitations): update invitation retrieval method in organizationPickerOptions - Changed the method of fetching organization invitations from a local function to a direct API call. - Updated the variable names for clarity, changing `invitations` to `orgInvitations` and `value` to `id`. --- internal/cli/organizations.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/internal/cli/organizations.go b/internal/cli/organizations.go index 94d19c886..807b53dff 100644 --- a/internal/cli/organizations.go +++ b/internal/cli/organizations.go @@ -816,16 +816,16 @@ func (cli *cli) organizationPickerOptions(ctx context.Context) (pickerOptions, e } func (cli *cli) invitationPickerOptions(ctx context.Context, orgID string) (pickerOptions, error) { - invitations, err := cli.getOrgInvitations(ctx, orgID, 1000) + orgInvitations, err := cli.api.Organization.Invitations(ctx, url.PathEscape(orgID)) if err != nil { return nil, err } var opts pickerOptions - for _, inv := range invitations { - value := inv.GetID() - label := fmt.Sprintf("%s %s", inv.Invitee.GetEmail(), ansi.Faint("("+value+")")) - opts = append(opts, pickerOption{value: value, label: label}) + for _, inv := range orgInvitations.OrganizationInvitations { + id := inv.GetID() + label := fmt.Sprintf("%s %s", inv.Invitee.GetEmail(), ansi.Faint("("+id+")")) + opts = append(opts, pickerOption{value: id, label: label}) } if len(opts) == 0 { From 2d9319bc9a1f55821ce798bbab4fbc3278e58857 Mon Sep 17 00:00:00 2001 From: Kiran Kumar Date: Mon, 9 Feb 2026 18:41:24 +0530 Subject: [PATCH 18/34] fix(invitations): update metadata handling in invitation creation - Changed AppMetadata and UserMetadata from map[string]string to string to accept JSON formatted input. - Updated help text for flags to clarify expected data format. - Adjusted JSON unmarshalling logic in createInvitationOrganizationCmd to handle new string format. - Ensured compatibility with existing functionality while improving data handling for user and app metadata. --- docs/auth0_orgs_invitations_create.md | 22 ++++++++-------- internal/cli/organizations.go | 37 ++++++++++++++------------- 2 files changed, 30 insertions(+), 29 deletions(-) diff --git a/docs/auth0_orgs_invitations_create.md b/docs/auth0_orgs_invitations_create.md index d3c66c9f4..25f959a2c 100644 --- a/docs/auth0_orgs_invitations_create.md +++ b/docs/auth0_orgs_invitations_create.md @@ -27,17 +27,17 @@ auth0 orgs invitations create [flags] ## Flags ``` - -a, --app-metadata stringToString Application metadata for the invited user in key=value format. (default []) - --client-id string Auth0 client ID. Used to resolve the application's login initiation endpoint. - --connection-id string The id of the connection to force invitee to authenticate with. - -e, --invitee-email string Email address of the person being invited. - -n, --inviter-name string Name of the person sending the invitation. - --json Output in json format. - --json-compact Output in compact json format. - -r, --roles strings Roles IDs to associate with the user. - -s, --send-email Whether to send the invitation email to the invitee. (default true) - -t, --ttl-sec int Number of seconds for which the invitation is valid before expiration. - -u, --user-metadata stringToString User metadata for the invited user in key=value format. (default []) + -a, --app-metadata string Data related to the user that does affect the application's core functionality, formatted as JSON + --client-id string Auth0 client ID. Used to resolve the application's login initiation endpoint. + --connection-id string The id of the connection to force invitee to authenticate with. + -e, --invitee-email string Email address of the person being invited. + -n, --inviter-name string Name of the person sending the invitation. + --json Output in json format. + --json-compact Output in compact json format. + -r, --roles strings Roles IDs to associate with the user. + -s, --send-email Whether to send the invitation email to the invitee. (default true) + -t, --ttl-sec int Number of seconds for which the invitation is valid before expiration. + -u, --user-metadata string Data related to the user that does not affect the application's core functionality, formatted as JSON ``` diff --git a/internal/cli/organizations.go b/internal/cli/organizations.go index 807b53dff..0c84c827b 100644 --- a/internal/cli/organizations.go +++ b/internal/cli/organizations.go @@ -2,6 +2,7 @@ package cli import ( "context" + "encoding/json" "errors" "fmt" "net/url" @@ -133,7 +134,7 @@ var ( Help: "Whether to send the invitation email to the invitee.", } - organizationRoles = Flag{ + roles = Flag{ Name: "Roles", LongForm: "roles", ShortForm: "r", @@ -144,14 +145,14 @@ var ( Name: "App Metadata", LongForm: "app-metadata", ShortForm: "a", - Help: "Application metadata for the invited user in key=value format.", + Help: "Data related to the user that does affect the application's core functionality, formatted as JSON", } userMetadata = Flag{ Name: "User Metadata", LongForm: "user-metadata", ShortForm: "u", - Help: "User metadata for the invited user in key=value format.", + Help: "Data related to the user that does not affect the application's core functionality, formatted as JSON", } ) @@ -1194,8 +1195,8 @@ func createInvitationOrganizationCmd(cli *cli) *cobra.Command { TTLSeconds int SendInvitationEmail bool Roles []string - AppMetadata map[string]string - UserMetadata map[string]string + AppMetadata string + UserMetadata string } cmd := &cobra.Command{ @@ -1237,19 +1238,19 @@ func createInvitationOrganizationCmd(cli *cli) *cobra.Command { if inputs.ConnectionID != "" { invitation.ConnectionID = &inputs.ConnectionID } - if len(inputs.AppMetadata) > 0 { - appMetadata := make(map[string]interface{}, len(inputs.AppMetadata)) - for k, v := range inputs.AppMetadata { - appMetadata[k] = v + if inputs.AppMetadata != "" { + var metadata map[string]interface{} + if err := json.Unmarshal([]byte(inputs.AppMetadata), &metadata); err != nil { + return fmt.Errorf("invalid JSON for app metadata: %w", err) } - invitation.AppMetadata = appMetadata + invitation.AppMetadata = metadata } - if len(inputs.UserMetadata) > 0 { - userMetadata := make(map[string]interface{}, len(inputs.UserMetadata)) - for k, v := range inputs.UserMetadata { - userMetadata[k] = v + if inputs.UserMetadata != "" { + var metadata map[string]interface{} + if err := json.Unmarshal([]byte(inputs.UserMetadata), &metadata); err != nil { + return fmt.Errorf("invalid JSON for user metadata: %w", err) } - invitation.UserMetadata = userMetadata + invitation.UserMetadata = metadata } if len(inputs.Roles) > 0 { invitation.Roles = inputs.Roles @@ -1272,9 +1273,9 @@ func createInvitationOrganizationCmd(cli *cli) *cobra.Command { connectionID.RegisterString(cmd, &inputs.ConnectionID, "") ttlSeconds.RegisterInt(cmd, &inputs.TTLSeconds, 0) sendInvitationEmail.RegisterBool(cmd, &inputs.SendInvitationEmail, true) - organizationRoles.RegisterStringSlice(cmd, &inputs.Roles, nil) - applicationMetadata.RegisterStringMap(cmd, &inputs.AppMetadata, nil) - userMetadata.RegisterStringMap(cmd, &inputs.UserMetadata, nil) + roles.RegisterStringSlice(cmd, &inputs.Roles, nil) + applicationMetadata.RegisterString(cmd, &inputs.AppMetadata, "") + userMetadata.RegisterString(cmd, &inputs.UserMetadata, "") cmd.Flags().BoolVar(&cli.json, "json", false, "Output in json format.") cmd.Flags().BoolVar(&cli.jsonCompact, "json-compact", false, "Output in compact json format.") From 563fcadabc39853c09d1861831e0ea6cc3d815c8 Mon Sep 17 00:00:00 2001 From: Kiran Kumar Date: Mon, 9 Feb 2026 19:03:01 +0530 Subject: [PATCH 19/34] fix(invitations): enhance documentation for organization invitations - Updated the long descriptions for commands related to organization invitations to provide clearer guidance on usage. - Added interactive and non-interactive usage instructions for the following commands: - `auth0 orgs invs create` - `auth0 orgs invs list` - `auth0 orgs invs show` - Improved user understanding of how to manage invitations effectively. --- docs/auth0_orgs_invitations.md | 2 +- docs/auth0_orgs_invitations_create.md | 6 +++++- docs/auth0_orgs_invitations_list.md | 4 ++++ docs/auth0_orgs_invitations_show.md | 4 ++++ internal/cli/organizations.go | 16 ++++++++++++---- 5 files changed, 26 insertions(+), 6 deletions(-) diff --git a/docs/auth0_orgs_invitations.md b/docs/auth0_orgs_invitations.md index 5b192c45b..bf00acfba 100644 --- a/docs/auth0_orgs_invitations.md +++ b/docs/auth0_orgs_invitations.md @@ -5,7 +5,7 @@ has_children: true --- # auth0 orgs invitations -Manage invitations of an organization. +Manage invitations of an organization. Invitations allow you to add users to an organization by sending them an email with a link to join. To learn more, read [Invite Members to Organizations](https://auth0.com/docs/manage-users/organizations/configure-organizations/invite-members). ## Commands diff --git a/docs/auth0_orgs_invitations_create.md b/docs/auth0_orgs_invitations_create.md index 25f959a2c..662721d68 100644 --- a/docs/auth0_orgs_invitations_create.md +++ b/docs/auth0_orgs_invitations_create.md @@ -5,7 +5,11 @@ has_toc: false --- # auth0 orgs invitations create -Create a new invitation to an organization. +Create a new invitation to an organization with required and optional parameters. + +To create interactively, use `auth0 orgs invs create` with no arguments and answer the prompts. + +To create non-interactively, supply the organization id through the arguments and the other parameters through flags. ## Usage ``` diff --git a/docs/auth0_orgs_invitations_list.md b/docs/auth0_orgs_invitations_list.md index 5686cb714..898663ed0 100644 --- a/docs/auth0_orgs_invitations_list.md +++ b/docs/auth0_orgs_invitations_list.md @@ -7,6 +7,10 @@ has_toc: false List the invitations of an organization. +To list interactively, use `auth0 orgs invs list` with no arguments. + +To list non-interactively, supply the organization id through the arguments. + ## Usage ``` auth0 orgs invitations list [flags] diff --git a/docs/auth0_orgs_invitations_show.md b/docs/auth0_orgs_invitations_show.md index 597d2fd6e..f87da61ec 100644 --- a/docs/auth0_orgs_invitations_show.md +++ b/docs/auth0_orgs_invitations_show.md @@ -7,6 +7,10 @@ has_toc: false Display information about an organization invitation. +To show interactively, use `auth0 orgs invs show` with no arguments. + +To show non-interactively, supply the organization id and invitation id through the arguments. + ## Usage ``` auth0 orgs invitations show [flags] diff --git a/internal/cli/organizations.go b/internal/cli/organizations.go index 0c84c827b..0f0f252ba 100644 --- a/internal/cli/organizations.go +++ b/internal/cli/organizations.go @@ -1016,7 +1016,9 @@ func invitationsOrganizationCmd(cli *cli) *cobra.Command { Use: "invitations", Aliases: []string{"invs"}, Short: "Manage invitations of an organization", - Long: "Manage invitations of an organization.", + Long: "Manage invitations of an organization. " + + "Invitations allow you to add users to an organization by sending them an email with a link to join. " + + "To learn more, read [Invite Members to Organizations](https://auth0.com/docs/manage-users/organizations/configure-organizations/invite-members).", } cmd.SetUsageTemplate(resourceUsageTemplate()) @@ -1038,7 +1040,9 @@ func showInvitationOrganizationCmd(cli *cli) *cobra.Command { Use: "show", Args: cobra.MaximumNArgs(2), Short: "Show an organization invitation", - Long: "Display information about an organization invitation.", + Long: "Display information about an organization invitation.\n\n" + + "To show interactively, use `auth0 orgs invs show` with no arguments.\n\n" + + "To show non-interactively, supply the organization id and invitation id through the arguments.", Example: ` auth0 orgs invs show auth0 orgs invs show auth0 orgs invs show @@ -1092,7 +1096,9 @@ func listInvitationsOrganizationCmd(cli *cli) *cobra.Command { Aliases: []string{"ls"}, Args: cobra.MaximumNArgs(1), Short: "List invitations of an organization", - Long: "List the invitations of an organization.", + Long: "List the invitations of an organization.\n\n" + + "To list interactively, use `auth0 orgs invs list` with no arguments.\n\n" + + "To list non-interactively, supply the organization id through the arguments.", Example: ` auth0 orgs invs list auth0 orgs invs ls auth0 orgs invs list --number 100 @@ -1203,7 +1209,9 @@ func createInvitationOrganizationCmd(cli *cli) *cobra.Command { Use: "create", Args: cobra.MaximumNArgs(1), Short: "Create a new invitation to an organization", - Long: "Create a new invitation to an organization.", + Long: "Create a new invitation to an organization with required and optional parameters.\n\n" + + "To create interactively, use `auth0 orgs invs create` with no arguments and answer the prompts.\n\n" + + "To create non-interactively, supply the organization id through the arguments and the other parameters through flags.", Example: ` auth0 orgs invs create auth0 orgs invs create auth0 orgs invs create --inviter-name "Inviter Name" --invitee-email "invitee@example.com" From fa021cf896f07aa4f4d975796e2afbdf5812dc23 Mon Sep 17 00:00:00 2001 From: Kiran Kumar Date: Tue, 10 Feb 2026 10:02:20 +0530 Subject: [PATCH 20/34] chore(invitations): improve organization invitation commands - Changed the variable name from `ID` to `OrgID` for clarity in `listInvitationsOrganizationCmd`. - Removed the `getOrgInvitationsWithSpinner` function as it was redundant. - Added sorting flag to invitation list API req. --- internal/cli/organizations.go | 35 +++++++++-------------------------- 1 file changed, 9 insertions(+), 26 deletions(-) diff --git a/internal/cli/organizations.go b/internal/cli/organizations.go index 0f0f252ba..30e57f9b7 100644 --- a/internal/cli/organizations.go +++ b/internal/cli/organizations.go @@ -1040,7 +1040,7 @@ func showInvitationOrganizationCmd(cli *cli) *cobra.Command { Use: "show", Args: cobra.MaximumNArgs(2), Short: "Show an organization invitation", - Long: "Display information about an organization invitation.\n\n" + + Long: "Display information about an organization invitation.\n\n" + "To show interactively, use `auth0 orgs invs show` with no arguments.\n\n" + "To show non-interactively, supply the organization id and invitation id through the arguments.", Example: ` auth0 orgs invs show @@ -1087,7 +1087,7 @@ func showInvitationOrganizationCmd(cli *cli) *cobra.Command { func listInvitationsOrganizationCmd(cli *cli) *cobra.Command { var inputs struct { - ID string + OrgID string Number int } @@ -1096,7 +1096,7 @@ func listInvitationsOrganizationCmd(cli *cli) *cobra.Command { Aliases: []string{"ls"}, Args: cobra.MaximumNArgs(1), Short: "List invitations of an organization", - Long: "List the invitations of an organization.\n\n" + + Long: "List the invitations of an organization.\n\n" + "To list interactively, use `auth0 orgs invs list` with no arguments.\n\n" + "To list non-interactively, supply the organization id through the arguments.", Example: ` auth0 orgs invs list @@ -1111,19 +1111,18 @@ func listInvitationsOrganizationCmd(cli *cli) *cobra.Command { } if len(args) == 0 { - if err := organizationID.Pick(cmd, &inputs.ID, cli.organizationPickerOptions); err != nil { + if err := organizationID.Pick(cmd, &inputs.OrgID, cli.organizationPickerOptions); err != nil { return err } } else { - inputs.ID = args[0] + inputs.OrgID = args[0] } - invitations, err := cli.getOrgInvitationsWithSpinner(cmd.Context(), inputs.ID, inputs.Number) + invitations, err := cli.getOrgInvitations(cmd.Context(), inputs.OrgID, inputs.Number) if err != nil { return err } - sortInvitations(invitations) cli.renderer.InvitationsList(invitations) return nil }, @@ -1140,18 +1139,6 @@ func listInvitationsOrganizationCmd(cli *cli) *cobra.Command { return cmd } -func (cli *cli) getOrgInvitationsWithSpinner(context context.Context, orgID string, number int, -) ([]management.OrganizationInvitation, error) { - var invitations []management.OrganizationInvitation - - err := ansi.Waiting(func() (err error) { - invitations, err = cli.getOrgInvitations(context, orgID, number) - return err - }) - - return invitations, err -} - func (cli *cli) getOrgInvitations( context context.Context, orgID string, @@ -1160,6 +1147,8 @@ func (cli *cli) getOrgInvitations( list, err := getWithPagination( number, func(opts ...management.RequestOption) (result []interface{}, hasNext bool, apiErr error) { + // Add sort option to existing opts. + opts = append(opts, management.Sort("created_at:-1")) invitations, apiErr := cli.api.Organization.Invitations(context, url.PathEscape(orgID), opts...) if apiErr != nil { return nil, false, apiErr @@ -1185,12 +1174,6 @@ func (cli *cli) getOrgInvitations( return typedList, nil } -func sortInvitations(invitations []management.OrganizationInvitation) { - sort.Slice(invitations, func(i, j int) bool { - return strings.ToLower(invitations[i].GetCreatedAt()) < strings.ToLower(invitations[j].GetCreatedAt()) - }) -} - func createInvitationOrganizationCmd(cli *cli) *cobra.Command { var inputs struct { OrgID string @@ -1209,7 +1192,7 @@ func createInvitationOrganizationCmd(cli *cli) *cobra.Command { Use: "create", Args: cobra.MaximumNArgs(1), Short: "Create a new invitation to an organization", - Long: "Create a new invitation to an organization with required and optional parameters.\n\n" + + Long: "Create a new invitation to an organization with required and optional parameters.\n\n" + "To create interactively, use `auth0 orgs invs create` with no arguments and answer the prompts.\n\n" + "To create non-interactively, supply the organization id through the arguments and the other parameters through flags.", Example: ` auth0 orgs invs create From f1774f87a4f1669710a9a45cb99331f460c58fcc Mon Sep 17 00:00:00 2001 From: Kiran Kumar Date: Tue, 10 Feb 2026 12:22:32 +0530 Subject: [PATCH 21/34] fix(invitations): update invitation list has next logic - Enhanced the logic in `getOrgInvitations` to determine if there are more invitations based on whether the current page is empty, using a new helper function `isEmptyInvitationList`. --- docs/auth0_orgs_invitations_list.md | 4 ++-- internal/cli/organizations.go | 11 ++++++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/docs/auth0_orgs_invitations_list.md b/docs/auth0_orgs_invitations_list.md index 898663ed0..1752b9ed3 100644 --- a/docs/auth0_orgs_invitations_list.md +++ b/docs/auth0_orgs_invitations_list.md @@ -22,8 +22,8 @@ auth0 orgs invitations list [flags] auth0 orgs invs list auth0 orgs invs ls auth0 orgs invs list --number 100 - auth0 orgs invs ls -n 100 --json - auth0 orgs invs ls -n 100 --json-compact + auth0 orgs invs ls -n 50 --json + auth0 orgs invs ls -n 500 --json-compact auth0 orgs invs ls --csv ``` diff --git a/internal/cli/organizations.go b/internal/cli/organizations.go index 30e57f9b7..dd1f0b11a 100644 --- a/internal/cli/organizations.go +++ b/internal/cli/organizations.go @@ -1102,8 +1102,8 @@ func listInvitationsOrganizationCmd(cli *cli) *cobra.Command { Example: ` auth0 orgs invs list auth0 orgs invs ls auth0 orgs invs list --number 100 - auth0 orgs invs ls -n 100 --json - auth0 orgs invs ls -n 100 --json-compact + auth0 orgs invs ls -n 50 --json + auth0 orgs invs ls -n 500 --json-compact auth0 orgs invs ls --csv`, RunE: func(cmd *cobra.Command, args []string) error { if inputs.Number < 1 || inputs.Number > 1000 { @@ -1159,7 +1159,8 @@ func (cli *cli) getOrgInvitations( output = append(output, *invitation) } } - return output, invitations.HasNext(), nil + // Invitations does not return total count yet, so we determine there is a next page if current page is not empty. + return output, !isEmptyInvitationList(invitations), nil }, ) if err != nil { @@ -1174,6 +1175,10 @@ func (cli *cli) getOrgInvitations( return typedList, nil } +func isEmptyInvitationList(invs *management.OrganizationInvitationList) bool { + return invs == nil || len(invs.OrganizationInvitations) == 0 +} + func createInvitationOrganizationCmd(cli *cli) *cobra.Command { var inputs struct { OrgID string From 5f740371046e4f7f730a80f18905dfe462c62662 Mon Sep 17 00:00:00 2001 From: Kiran Kumar Date: Tue, 10 Feb 2026 15:07:17 +0530 Subject: [PATCH 22/34] feat(invitations): enhance organization invitation commands to read org and inv id as flags - Added new flags for Organization ID and Invitation ID to improve command usability. - Updated command arguments to use flags instead of positional arguments for better clarity. - Revised command descriptions and examples to reflect the new flag usage. - Ensured that the commands now support interactive and non-interactive modes consistently. --- docs/auth0_orgs_invitations.md | 2 +- docs/auth0_orgs_invitations_create.md | 19 ++--- docs/auth0_orgs_invitations_list.md | 23 +++--- docs/auth0_orgs_invitations_show.md | 17 +++-- internal/cli/organizations.go | 106 +++++++++++++------------- 5 files changed, 88 insertions(+), 79 deletions(-) diff --git a/docs/auth0_orgs_invitations.md b/docs/auth0_orgs_invitations.md index bf00acfba..50af74325 100644 --- a/docs/auth0_orgs_invitations.md +++ b/docs/auth0_orgs_invitations.md @@ -5,7 +5,7 @@ has_children: true --- # auth0 orgs invitations -Manage invitations of an organization. Invitations allow you to add users to an organization by sending them an email with a link to join. To learn more, read [Invite Members to Organizations](https://auth0.com/docs/manage-users/organizations/configure-organizations/invite-members). +Manage invitations of an organization. Invitations enable adding users to an organization by sending an email containing the join link. To learn more, read [Invite Members to Organizations](https://auth0.com/docs/manage-users/organizations/configure-organizations/invite-members). ## Commands diff --git a/docs/auth0_orgs_invitations_create.md b/docs/auth0_orgs_invitations_create.md index 662721d68..0d72151d7 100644 --- a/docs/auth0_orgs_invitations_create.md +++ b/docs/auth0_orgs_invitations_create.md @@ -7,9 +7,9 @@ has_toc: false Create a new invitation to an organization with required and optional parameters. -To create interactively, use `auth0 orgs invs create` with no arguments and answer the prompts. +To create interactively, use `auth0 orgs invs create` with no flags and answer the prompts. -To create non-interactively, supply the organization id through the arguments and the other parameters through flags. +To create non-interactively, supply the organization id and the other parameters through flags. ## Usage ``` @@ -20,26 +20,27 @@ auth0 orgs invitations create [flags] ``` auth0 orgs invs create - auth0 orgs invs create - auth0 orgs invs create --inviter-name "Inviter Name" --invitee-email "invitee@example.com" - auth0 orgs invs create --invitee-email "invitee@example.com" --client-id "client_id" - auth0 orgs invs create -n "Inviter Name" -e "invitee@example.com" --client-id "client_id" -connection-id "connection_id" -t 86400 - auth0 orgs invs create --json --inviter-name "Inviter Name" + auth0 orgs invs create --org-id + auth0 orgs invs create --org-id --inviter-name "Inviter Name" --invitee-email "invitee@example.com" + auth0 orgs invs create --org-id --invitee-email "invitee@example.com" --client-id "client_id" + auth0 orgs invs create --org-id -n "Inviter Name" -e "invitee@example.com" --client-id "client_id" --connection-id "connection_id" -t 86400 + auth0 orgs invs create --org-id --json --inviter-name "Inviter Name" ``` ## Flags ``` - -a, --app-metadata string Data related to the user that does affect the application's core functionality, formatted as JSON + -a, --app-metadata string Data related to the user that affects the application's core functionality, formatted as JSON --client-id string Auth0 client ID. Used to resolve the application's login initiation endpoint. --connection-id string The id of the connection to force invitee to authenticate with. -e, --invitee-email string Email address of the person being invited. -n, --inviter-name string Name of the person sending the invitation. --json Output in json format. --json-compact Output in compact json format. + --org-id string ID of the organization. -r, --roles strings Roles IDs to associate with the user. - -s, --send-email Whether to send the invitation email to the invitee. (default true) + -s, --send-email Whether to send the invitation email to the invitee. -t, --ttl-sec int Number of seconds for which the invitation is valid before expiration. -u, --user-metadata string Data related to the user that does not affect the application's core functionality, formatted as JSON ``` diff --git a/docs/auth0_orgs_invitations_list.md b/docs/auth0_orgs_invitations_list.md index 1752b9ed3..a293cb426 100644 --- a/docs/auth0_orgs_invitations_list.md +++ b/docs/auth0_orgs_invitations_list.md @@ -7,9 +7,9 @@ has_toc: false List the invitations of an organization. -To list interactively, use `auth0 orgs invs list` with no arguments. +To list interactively, use `auth0 orgs invs list` with no flags. -To list non-interactively, supply the organization id through the arguments. +To list non-interactively, supply the organization id through the flags. ## Usage ``` @@ -20,21 +20,22 @@ auth0 orgs invitations list [flags] ``` auth0 orgs invs list - auth0 orgs invs ls - auth0 orgs invs list --number 100 - auth0 orgs invs ls -n 50 --json - auth0 orgs invs ls -n 500 --json-compact - auth0 orgs invs ls --csv + auth0 orgs invs ls --org-id + auth0 orgs invs list --org-id --number 100 + auth0 orgs invs ls --org-id -n 50 --json + auth0 orgs invs ls --org-id -n 500 --json-compact + auth0 orgs invs ls --org-id --csv ``` ## Flags ``` - --csv Output in csv format. - --json Output in json format. - --json-compact Output in compact json format. - -n, --number int Number of organization invitations to retrieve. Minimum 1, maximum 1000. (default 100) + --csv Output in csv format. + --json Output in json format. + --json-compact Output in compact json format. + -n, --number int Number of organization invitations to retrieve. Minimum 1, maximum 1000. (default 100) + --org-id string ID of the organization. ``` diff --git a/docs/auth0_orgs_invitations_show.md b/docs/auth0_orgs_invitations_show.md index f87da61ec..1eefc72b3 100644 --- a/docs/auth0_orgs_invitations_show.md +++ b/docs/auth0_orgs_invitations_show.md @@ -7,9 +7,9 @@ has_toc: false Display information about an organization invitation. -To show interactively, use `auth0 orgs invs show` with no arguments. +To show interactively, use `auth0 orgs invs show` with no flags. -To show non-interactively, supply the organization id and invitation id through the arguments. +To show non-interactively, supply the organization id and invitation id through the flags. ## Usage ``` @@ -20,17 +20,20 @@ auth0 orgs invitations show [flags] ``` auth0 orgs invs show - auth0 orgs invs show - auth0 orgs invs show - auth0 orgs invs show --json + auth0 orgs invs show --org-id + auth0 orgs invs show --org-id --invitation-id + auth0 orgs invs show --org-id --invitation-id --json + auth0 orgs invs show --org-id --i --json-compact ``` ## Flags ``` - --json Output in json format. - --json-compact Output in compact json format. + -i, --invitation-id string ID of the invitation. + --json Output in json format. + --json-compact Output in compact json format. + --org-id string ID of the organization. ``` diff --git a/internal/cli/organizations.go b/internal/cli/organizations.go index dd1f0b11a..cbae15f80 100644 --- a/internal/cli/organizations.go +++ b/internal/cli/organizations.go @@ -32,6 +32,21 @@ var ( Help: "ID of the invitation.", } + organizationIDFlag = Flag{ + Name: "Organization ID", + LongForm: "org-id", + Help: "ID of the organization.", + IsRequired: true, + } + + invitationIDFlag = Flag{ + Name: "Invitation ID", + LongForm: "invitation-id", + ShortForm: "i", + Help: "ID of the invitation.", + IsRequired: true, + } + organizationName = Flag{ Name: "Name", LongForm: "name", @@ -145,7 +160,7 @@ var ( Name: "App Metadata", LongForm: "app-metadata", ShortForm: "a", - Help: "Data related to the user that does affect the application's core functionality, formatted as JSON", + Help: "Data related to the user that affects the application's core functionality, formatted as JSON", } userMetadata = Flag{ @@ -1017,7 +1032,7 @@ func invitationsOrganizationCmd(cli *cli) *cobra.Command { Aliases: []string{"invs"}, Short: "Manage invitations of an organization", Long: "Manage invitations of an organization. " + - "Invitations allow you to add users to an organization by sending them an email with a link to join. " + + "Invitations enable adding users to an organization by sending an email containing the join link. " + "To learn more, read [Invite Members to Organizations](https://auth0.com/docs/manage-users/organizations/configure-organizations/invite-members).", } @@ -1038,32 +1053,25 @@ func showInvitationOrganizationCmd(cli *cli) *cobra.Command { cmd := &cobra.Command{ Use: "show", - Args: cobra.MaximumNArgs(2), + Args: cobra.NoArgs, Short: "Show an organization invitation", Long: "Display information about an organization invitation.\n\n" + - "To show interactively, use `auth0 orgs invs show` with no arguments.\n\n" + - "To show non-interactively, supply the organization id and invitation id through the arguments.", + "To show interactively, use `auth0 orgs invs show` with no flags.\n\n" + + "To show non-interactively, supply the organization id and invitation id through the flags.", Example: ` auth0 orgs invs show - auth0 orgs invs show - auth0 orgs invs show - auth0 orgs invs show --json`, + auth0 orgs invs show --org-id + auth0 orgs invs show --org-id --invitation-id + auth0 orgs invs show --org-id --invitation-id --json + auth0 orgs invs show --org-id --i --json-compact`, RunE: func(cmd *cobra.Command, args []string) error { - if len(args) == 0 { - if err := organizationID.Pick(cmd, &inputs.OrgID, cli.organizationPickerOptions); err != nil { - return err - } - } else { - inputs.OrgID = args[0] + if err := organizationIDFlag.Pick(cmd, &inputs.OrgID, cli.organizationPickerOptions); err != nil { + return err } - if len(args) <= 1 { - if err := invitationID.Pick(cmd, &inputs.InvitationID, func(ctx context.Context) (pickerOptions, error) { - return cli.invitationPickerOptions(ctx, inputs.OrgID) - }); err != nil { - return err - } - } else { - inputs.InvitationID = args[1] + if err := invitationIDFlag.Pick(cmd, &inputs.InvitationID, func(ctx context.Context) (pickerOptions, error) { + return cli.invitationPickerOptions(ctx, inputs.OrgID) + }); err != nil { + return err } var invitation *management.OrganizationInvitation @@ -1079,6 +1087,8 @@ func showInvitationOrganizationCmd(cli *cli) *cobra.Command { }, } + organizationIDFlag.RegisterString(cmd, &inputs.OrgID, "") + invitationIDFlag.RegisterString(cmd, &inputs.InvitationID, "") cmd.Flags().BoolVar(&cli.json, "json", false, "Output in json format.") cmd.Flags().BoolVar(&cli.jsonCompact, "json-compact", false, "Output in compact json format.") @@ -1094,28 +1104,24 @@ func listInvitationsOrganizationCmd(cli *cli) *cobra.Command { cmd := &cobra.Command{ Use: "list", Aliases: []string{"ls"}, - Args: cobra.MaximumNArgs(1), + Args: cobra.NoArgs, Short: "List invitations of an organization", Long: "List the invitations of an organization.\n\n" + - "To list interactively, use `auth0 orgs invs list` with no arguments.\n\n" + - "To list non-interactively, supply the organization id through the arguments.", + "To list interactively, use `auth0 orgs invs list` with no flags.\n\n" + + "To list non-interactively, supply the organization id through the flags.", Example: ` auth0 orgs invs list - auth0 orgs invs ls - auth0 orgs invs list --number 100 - auth0 orgs invs ls -n 50 --json - auth0 orgs invs ls -n 500 --json-compact - auth0 orgs invs ls --csv`, + auth0 orgs invs ls --org-id + auth0 orgs invs list --org-id --number 100 + auth0 orgs invs ls --org-id -n 50 --json + auth0 orgs invs ls --org-id -n 500 --json-compact + auth0 orgs invs ls --org-id --csv`, RunE: func(cmd *cobra.Command, args []string) error { if inputs.Number < 1 || inputs.Number > 1000 { return fmt.Errorf("number flag invalid, please pass a number between 1 and 1000") } - if len(args) == 0 { - if err := organizationID.Pick(cmd, &inputs.OrgID, cli.organizationPickerOptions); err != nil { - return err - } - } else { - inputs.OrgID = args[0] + if err := organizationIDFlag.Pick(cmd, &inputs.OrgID, cli.organizationPickerOptions); err != nil { + return err } invitations, err := cli.getOrgInvitations(cmd.Context(), inputs.OrgID, inputs.Number) @@ -1128,6 +1134,7 @@ func listInvitationsOrganizationCmd(cli *cli) *cobra.Command { }, } + organizationIDFlag.RegisterString(cmd, &inputs.OrgID, "") organizationNumber.Help = "Number of organization invitations to retrieve. Minimum 1, maximum 1000." organizationNumber.RegisterInt(cmd, &inputs.Number, defaultPageSize) @@ -1195,24 +1202,20 @@ func createInvitationOrganizationCmd(cli *cli) *cobra.Command { cmd := &cobra.Command{ Use: "create", - Args: cobra.MaximumNArgs(1), + Args: cobra.NoArgs, Short: "Create a new invitation to an organization", Long: "Create a new invitation to an organization with required and optional parameters.\n\n" + - "To create interactively, use `auth0 orgs invs create` with no arguments and answer the prompts.\n\n" + - "To create non-interactively, supply the organization id through the arguments and the other parameters through flags.", + "To create interactively, use `auth0 orgs invs create` with no flags and answer the prompts.\n\n" + + "To create non-interactively, supply the organization id and the other parameters through flags.", Example: ` auth0 orgs invs create - auth0 orgs invs create - auth0 orgs invs create --inviter-name "Inviter Name" --invitee-email "invitee@example.com" - auth0 orgs invs create --invitee-email "invitee@example.com" --client-id "client_id" - auth0 orgs invs create -n "Inviter Name" -e "invitee@example.com" --client-id "client_id" -connection-id "connection_id" -t 86400 - auth0 orgs invs create --json --inviter-name "Inviter Name"`, + auth0 orgs invs create --org-id + auth0 orgs invs create --org-id --inviter-name "Inviter Name" --invitee-email "invitee@example.com" + auth0 orgs invs create --org-id --invitee-email "invitee@example.com" --client-id "client_id" + auth0 orgs invs create --org-id -n "Inviter Name" -e "invitee@example.com" --client-id "client_id" --connection-id "connection_id" -t 86400 + auth0 orgs invs create --org-id --json --inviter-name "Inviter Name"`, RunE: func(cmd *cobra.Command, args []string) error { - if len(args) == 0 { - if err := organizationID.Pick(cmd, &inputs.OrgID, cli.organizationPickerOptions); err != nil { - return err - } - } else { - inputs.OrgID = args[0] + if err := organizationIDFlag.Pick(cmd, &inputs.OrgID, cli.organizationPickerOptions); err != nil { + return err } if err := clientID.Pick(cmd, &inputs.ClientID, cli.appPickerOptions()); err != nil { return err @@ -1263,12 +1266,13 @@ func createInvitationOrganizationCmd(cli *cli) *cobra.Command { }, } + organizationIDFlag.RegisterString(cmd, &inputs.OrgID, "") inviterName.RegisterString(cmd, &inputs.InviterName, "") inviteeEmail.RegisterString(cmd, &inputs.InviteeEmail, "") clientID.RegisterString(cmd, &inputs.ClientID, "") connectionID.RegisterString(cmd, &inputs.ConnectionID, "") ttlSeconds.RegisterInt(cmd, &inputs.TTLSeconds, 0) - sendInvitationEmail.RegisterBool(cmd, &inputs.SendInvitationEmail, true) + sendInvitationEmail.RegisterBool(cmd, &inputs.SendInvitationEmail, false) roles.RegisterStringSlice(cmd, &inputs.Roles, nil) applicationMetadata.RegisterString(cmd, &inputs.AppMetadata, "") userMetadata.RegisterString(cmd, &inputs.UserMetadata, "") From e3bb5114dc286e40404e0bda447e7d98d3920b12 Mon Sep 17 00:00:00 2001 From: Kiran Kumar Date: Tue, 10 Feb 2026 15:29:07 +0530 Subject: [PATCH 23/34] chore(invitations): add an example to disable email sending for invitations --- docs/auth0_orgs_invitations_create.md | 2 +- internal/cli/organizations.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/auth0_orgs_invitations_create.md b/docs/auth0_orgs_invitations_create.md index 0d72151d7..1e611863b 100644 --- a/docs/auth0_orgs_invitations_create.md +++ b/docs/auth0_orgs_invitations_create.md @@ -24,7 +24,7 @@ auth0 orgs invitations create [flags] auth0 orgs invs create --org-id --inviter-name "Inviter Name" --invitee-email "invitee@example.com" auth0 orgs invs create --org-id --invitee-email "invitee@example.com" --client-id "client_id" auth0 orgs invs create --org-id -n "Inviter Name" -e "invitee@example.com" --client-id "client_id" --connection-id "connection_id" -t 86400 - auth0 orgs invs create --org-id --json --inviter-name "Inviter Name" + auth0 orgs invs create --org-id --json --send-email=false --inviter-name "Inviter Name" ``` diff --git a/internal/cli/organizations.go b/internal/cli/organizations.go index cbae15f80..3f345b6c0 100644 --- a/internal/cli/organizations.go +++ b/internal/cli/organizations.go @@ -1212,7 +1212,7 @@ func createInvitationOrganizationCmd(cli *cli) *cobra.Command { auth0 orgs invs create --org-id --inviter-name "Inviter Name" --invitee-email "invitee@example.com" auth0 orgs invs create --org-id --invitee-email "invitee@example.com" --client-id "client_id" auth0 orgs invs create --org-id -n "Inviter Name" -e "invitee@example.com" --client-id "client_id" --connection-id "connection_id" -t 86400 - auth0 orgs invs create --org-id --json --inviter-name "Inviter Name"`, + auth0 orgs invs create --org-id --json --send-email=false --inviter-name "Inviter Name"`, RunE: func(cmd *cobra.Command, args []string) error { if err := organizationIDFlag.Pick(cmd, &inputs.OrgID, cli.organizationPickerOptions); err != nil { return err From b3a4c218e651be480d38ecb69e4b33f4e1abed4b Mon Sep 17 00:00:00 2001 From: Kiran Kumar Date: Tue, 10 Feb 2026 16:08:51 +0530 Subject: [PATCH 24/34] fix(invitations): remove CreatedAt field from invitations view - Removed the CreatedAt field from the invitationsView struct and its related methods. - Updated AsTableHeader, AsTableRow, and KeyValues methods to reflect this change. - This simplifies the invitation display by focusing on relevant fields, improving clarity for users. --- internal/display/invitations.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/internal/display/invitations.go b/internal/display/invitations.go index 72b9d534a..7558cc23d 100644 --- a/internal/display/invitations.go +++ b/internal/display/invitations.go @@ -12,17 +12,16 @@ type invitationsView struct { ConnectionID string InviterName string InviteeEmail string - CreatedAt string ExpiresAt string raw interface{} } func (v *invitationsView) AsTableHeader() []string { - return []string{"ID", "Client ID", "Connection ID", "Inviter Name", "Invitee Email", "Created At", "Expires At"} + return []string{"ID", "Client ID", "Connection ID", "Inviter Name", "Invitee Email", "Expires At"} } func (v *invitationsView) AsTableRow() []string { - return []string{ansi.Faint(v.ID), v.ClientID, v.ConnectionID, v.InviterName, v.InviteeEmail, v.CreatedAt, v.ExpiresAt} + return []string{ansi.Faint(v.ID), v.ClientID, v.ConnectionID, v.InviterName, v.InviteeEmail, v.ExpiresAt} } func (v *invitationsView) KeyValues() [][]string { @@ -32,7 +31,6 @@ func (v *invitationsView) KeyValues() [][]string { {"CONNECTION ID", v.ConnectionID}, {"INVITER NAME", v.InviterName}, {"INVITEE EMAIL", v.InviteeEmail}, - {"CREATED AT", v.CreatedAt}, {"EXPIRES AT", v.ExpiresAt}, } } @@ -74,7 +72,6 @@ func makeInvitationsView(invitation management.OrganizationInvitation) *invitati ID: invitation.GetID(), InviterName: invitation.GetInviter().GetName(), InviteeEmail: invitation.GetInvitee().GetEmail(), - CreatedAt: invitation.GetCreatedAt(), ExpiresAt: invitation.GetExpiresAt(), ClientID: invitation.GetClientID(), ConnectionID: invitation.GetConnectionID(), From 85d66befe932f4f5d045e5a2b9b48bc26718946c Mon Sep 17 00:00:00 2001 From: Kiran Kumar Date: Tue, 10 Feb 2026 16:23:49 +0530 Subject: [PATCH 25/34] feat(invitations): add unit tests for invitationsView methods - Implemented unit tests for the invitationsView struct to ensure proper functionality. - Added tests for AsTableHeader, AsTableRow, KeyValues, Object, and makeInvitationsView methods. - These tests validate the expected output for various invitation attributes, enhancing code reliability and maintainability. --- internal/display/invitations_test.go | 83 ++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 internal/display/invitations_test.go diff --git a/internal/display/invitations_test.go b/internal/display/invitations_test.go new file mode 100644 index 000000000..1ad7f1abd --- /dev/null +++ b/internal/display/invitations_test.go @@ -0,0 +1,83 @@ +package display + +import ( + "testing" + + "github.com/auth0/auth0-cli/internal/auth0" + "github.com/auth0/go-auth0/management" + "github.com/stretchr/testify/assert" +) + +func Test_invitationsView_AsTableHeader(t *testing.T) { + mockInvitationsView := invitationsView{} + + assert.Equal(t, []string{"ID", "Client ID", "Connection ID", "Inviter Name", "Invitee Email", "Expires At"}, mockInvitationsView.AsTableHeader()) +} + +func Test_invitationsView_AsTableRow(t *testing.T) { + mockInvitationsView := invitationsView{ + ID: "invitation-id", + ClientID: "client-id", + ConnectionID: "connection-id", + InviterName: "inviter-name", + InviteeEmail: "invitee-email", + ExpiresAt: "expires-at", + } + + assert.Equal(t, []string{"invitation-id", "client-id", "connection-id", "inviter-name", "invitee-email", "expires-at"}, mockInvitationsView.AsTableRow()) +} + +func Test_invitationsView_KeyValues(t *testing.T) { + mockInvitationsView := invitationsView{ + ID: "invitation-id", + ClientID: "client-id", + ConnectionID: "connection-id", + InviterName: "inviter-name", + InviteeEmail: "invitee-email", + ExpiresAt: "expires-at", + } + + expected := [][]string{ + {"ID", "invitation-id"}, + {"CLIENT ID", "client-id"}, + {"CONNECTION ID", "connection-id"}, + {"INVITER NAME", "inviter-name"}, + {"INVITEE EMAIL", "invitee-email"}, + {"EXPIRES AT", "expires-at"}, + } + + assert.Equal(t, expected, mockInvitationsView.KeyValues()) +} + +func Test_invitationsView_Object(t *testing.T) { + mockInvitationsView := invitationsView{ + raw: "raw data", + } + + assert.Equal(t, "raw data", mockInvitationsView.Object()) +} + +func Test_makeInvitationsView(t *testing.T) { + mockInvitation := management.OrganizationInvitation{ + ID: auth0.String("invitation-id"), + Inviter: &management.OrganizationInvitationInviter{ + Name: auth0.String("inviter-name"), + }, + Invitee: &management.OrganizationInvitationInvitee{ + Email: auth0.String("invitee-email"), + }, + ExpiresAt: auth0.String("expires-at"), + ClientID: auth0.String("client-id"), + ConnectionID: auth0.String("connection-id"), + } + + view := makeInvitationsView(mockInvitation) + + assert.Equal(t, "invitation-id", view.ID) + assert.Equal(t, "client-id", view.ClientID) + assert.Equal(t, "connection-id", view.ConnectionID) + assert.Equal(t, "inviter-name", view.InviterName) + assert.Equal(t, "invitee-email", view.InviteeEmail) + assert.Equal(t, "expires-at", view.ExpiresAt) + assert.Equal(t, mockInvitation, view.raw) +} From a7d1b7faefa63a3cf78e20885f1928abf4ed7f43 Mon Sep 17 00:00:00 2001 From: Kiran Kumar Date: Tue, 10 Feb 2026 16:35:39 +0530 Subject: [PATCH 26/34] chore(invitations): replace auth0.String with pointer function for invitation fields --- internal/display/invitations_test.go | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/internal/display/invitations_test.go b/internal/display/invitations_test.go index 1ad7f1abd..0ccb81489 100644 --- a/internal/display/invitations_test.go +++ b/internal/display/invitations_test.go @@ -3,7 +3,6 @@ package display import ( "testing" - "github.com/auth0/auth0-cli/internal/auth0" "github.com/auth0/go-auth0/management" "github.com/stretchr/testify/assert" ) @@ -59,16 +58,16 @@ func Test_invitationsView_Object(t *testing.T) { func Test_makeInvitationsView(t *testing.T) { mockInvitation := management.OrganizationInvitation{ - ID: auth0.String("invitation-id"), + ID: pointer("invitation-id"), Inviter: &management.OrganizationInvitationInviter{ - Name: auth0.String("inviter-name"), + Name: pointer("inviter-name"), }, Invitee: &management.OrganizationInvitationInvitee{ - Email: auth0.String("invitee-email"), + Email: pointer("invitee-email"), }, - ExpiresAt: auth0.String("expires-at"), - ClientID: auth0.String("client-id"), - ConnectionID: auth0.String("connection-id"), + ExpiresAt: pointer("expires-at"), + ClientID: pointer("client-id"), + ConnectionID: pointer("connection-id"), } view := makeInvitationsView(mockInvitation) @@ -81,3 +80,7 @@ func Test_makeInvitationsView(t *testing.T) { assert.Equal(t, "expires-at", view.ExpiresAt) assert.Equal(t, mockInvitation, view.raw) } + +func pointer(s string) *string { + return &s +} From 8a8088256b4394450852ddf70843b9fd62f5c55b Mon Sep 17 00:00:00 2001 From: Kiran Kumar Date: Tue, 10 Feb 2026 16:39:57 +0530 Subject: [PATCH 27/34] feat(arguments, flags, input): introduced `PickMany` and `PickManyU` methods in the `Flag` struct to handle multiple flag selections. - Updated `askMultiSelect` to accept an `isUpdate` parameter for better control over input validation. - Modified `PickMany` in the `Argument` struct to support the new multi-select behavior. --- internal/cli/arguments.go | 2 +- internal/cli/flags.go | 32 ++++++++++++++++++++++++++++++++ internal/cli/input.go | 18 ++++++++++++++++-- 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/internal/cli/arguments.go b/internal/cli/arguments.go index 4c311b0a1..3898070a0 100644 --- a/internal/cli/arguments.go +++ b/internal/cli/arguments.go @@ -79,7 +79,7 @@ func (a *Argument) PickMany(cmd *cobra.Command, result *[]string, fn pickerOptio } var values []string - if err := askMultiSelect(a, &values, opts.labels()...); err != nil { + if err := askMultiSelect(a, &values, false, opts.labels()...); err != nil { return err } diff --git a/internal/cli/flags.go b/internal/cli/flags.go index 8627a5821..e6699641b 100644 --- a/internal/cli/flags.go +++ b/internal/cli/flags.go @@ -97,6 +97,14 @@ func (f *Flag) PickU(cmd *cobra.Command, result *string, fn pickerOptionsFunc) e return pickFlag(cmd, f, result, fn, true) } +func (f *Flag) PickMany(cmd *cobra.Command, result *[]string, fn pickerOptionsFunc) error { + return pickManyFlag(cmd, f, result, fn, false) +} + +func (f *Flag) PickManyU(cmd *cobra.Command, result *[]string, fn pickerOptionsFunc) error { + return pickManyFlag(cmd, f, result, fn, true) +} + func (f *Flag) OpenEditor(cmd *cobra.Command, value *string, defaultValue, filename string, infoFn func()) error { return openEditorFlag(cmd, f, value, defaultValue, filename, infoFn, nil, false) } @@ -240,6 +248,30 @@ func pickFlag(cmd *cobra.Command, f *Flag, result *string, fn pickerOptionsFunc, return nil } +func pickManyFlag(cmd *cobra.Command, f *Flag, result *[]string, fn pickerOptionsFunc, isUpdate bool) error { + if shouldAsk(cmd, f, isUpdate) { + var opts pickerOptions + err := ansi.Waiting(func() error { + var err error + opts, err = fn(cmd.Context()) + return err + }) + + if err != nil { + return err + } + + var values []string + if err := askMultiSelect(f, &values, isUpdate, opts.labels()...); err != nil { + return err + } + + *result = opts.getValues(values...) + } + + return nil +} + func askPasswordFlag(cmd *cobra.Command, f *Flag, value *string, isUpdate bool) error { if shouldAsk(cmd, f, isUpdate) { if err := askPassword(f, value, isUpdate); err != nil { diff --git a/internal/cli/input.go b/internal/cli/input.go index bd7811486..7626b0c4d 100644 --- a/internal/cli/input.go +++ b/internal/cli/input.go @@ -60,12 +60,26 @@ func askPassword(i commandInput, value interface{}, isUpdate bool) error { return nil } -func askMultiSelect(i commandInput, value interface{}, options ...string) error { +func askMultiSelect(i commandInput, value interface{}, isUpdate bool, options ...string) error { v := reflect.ValueOf(options) if v.Kind() != reflect.Slice || v.Len() <= 0 { return handleInputError(fmt.Errorf("there is not enough data to select from")) } - if err := prompt.AskMultiSelect(i.GetLabel(), value, options...); err != nil { + + isRequired := isInputRequired(i, isUpdate) + + input := &survey.Question{ + Prompt: &survey.MultiSelect{ + Message: i.GetLabel(), + Options: options, + }, + } + + if isRequired { + input.Validate = survey.Required + } + + if err := prompt.AskOne(input, value); err != nil { return handleInputError(err) } return nil From 7b95a0b800102cbdda15cb13a3caf84f7e53e599 Mon Sep 17 00:00:00 2001 From: Kiran Kumar Date: Tue, 10 Feb 2026 16:42:12 +0530 Subject: [PATCH 28/34] refactor(invitations): update invs delete to accept org and inv ids as flag. - Enhanced the delete command to accept multiple invitation IDs in flag for deletion. - Improved command examples for clarity on usage. --- docs/auth0_orgs_invitations_create.md | 2 +- docs/auth0_orgs_invitations_delete.md | 12 ++-- internal/cli/organizations.go | 83 +++++++++++---------------- 3 files changed, 42 insertions(+), 55 deletions(-) diff --git a/docs/auth0_orgs_invitations_create.md b/docs/auth0_orgs_invitations_create.md index 1e611863b..b957ed27d 100644 --- a/docs/auth0_orgs_invitations_create.md +++ b/docs/auth0_orgs_invitations_create.md @@ -40,7 +40,7 @@ auth0 orgs invitations create [flags] --json-compact Output in compact json format. --org-id string ID of the organization. -r, --roles strings Roles IDs to associate with the user. - -s, --send-email Whether to send the invitation email to the invitee. + -s, --send-email Whether to send the invitation email to the invitee. (default true) -t, --ttl-sec int Number of seconds for which the invitation is valid before expiration. -u, --user-metadata string Data related to the user that does not affect the application's core functionality, formatted as JSON ``` diff --git a/docs/auth0_orgs_invitations_delete.md b/docs/auth0_orgs_invitations_delete.md index 535fc9f06..e81451492 100644 --- a/docs/auth0_orgs_invitations_delete.md +++ b/docs/auth0_orgs_invitations_delete.md @@ -7,7 +7,7 @@ has_toc: false Delete invitation(s) from an organization. -To delete interactively, use `auth0 orgs invs delete` with no arguments. +To delete interactively, use `auth0 orgs invs delete` with no flags. To delete non-interactively, supply the organization id, invitation id(s) and the `--force` flag to skip confirmation. @@ -21,16 +21,18 @@ auth0 orgs invitations delete [flags] ``` auth0 orgs invs delete auth0 orgs invs rm - auth0 orgs invs delete - auth0 orgs invs delete --force - auth0 orgs invs delete + auth0 orgs invs delete --org-id --invitation-id + auth0 orgs invs delete --org-id --invitation-id ,, + auth0 orgs invs delete --org-id --invitation-id --force ``` ## Flags ``` - --force Skip confirmation. + --force Skip confirmation. + -i, --invitation-id strings ID of the invitation. + --org-id string ID of the organization. ``` diff --git a/internal/cli/organizations.go b/internal/cli/organizations.go index 3f345b6c0..191d8864d 100644 --- a/internal/cli/organizations.go +++ b/internal/cli/organizations.go @@ -27,26 +27,6 @@ var ( Help: "ID of the organization.", } - invitationID = Argument{ - Name: "Invitation ID", - Help: "ID of the invitation.", - } - - organizationIDFlag = Flag{ - Name: "Organization ID", - LongForm: "org-id", - Help: "ID of the organization.", - IsRequired: true, - } - - invitationIDFlag = Flag{ - Name: "Invitation ID", - LongForm: "invitation-id", - ShortForm: "i", - Help: "ID of the invitation.", - IsRequired: true, - } - organizationName = Flag{ Name: "Name", LongForm: "name", @@ -106,6 +86,21 @@ var ( ShortForm: "n", } + organizationIDFlag = Flag{ + Name: "Organization ID", + LongForm: "org-id", + Help: "ID of the organization.", + IsRequired: true, + } + + invitationID = Flag{ + Name: "Invitation ID", + LongForm: "invitation-id", + ShortForm: "i", + Help: "ID of the invitation.", + IsRequired: true, + } + inviterName = Flag{ Name: "Inviter Name", LongForm: "inviter-name", @@ -1068,7 +1063,7 @@ func showInvitationOrganizationCmd(cli *cli) *cobra.Command { return err } - if err := invitationIDFlag.Pick(cmd, &inputs.InvitationID, func(ctx context.Context) (pickerOptions, error) { + if err := invitationID.Pick(cmd, &inputs.InvitationID, func(ctx context.Context) (pickerOptions, error) { return cli.invitationPickerOptions(ctx, inputs.OrgID) }); err != nil { return err @@ -1088,7 +1083,7 @@ func showInvitationOrganizationCmd(cli *cli) *cobra.Command { } organizationIDFlag.RegisterString(cmd, &inputs.OrgID, "") - invitationIDFlag.RegisterString(cmd, &inputs.InvitationID, "") + invitationID.RegisterString(cmd, &inputs.InvitationID, "") cmd.Flags().BoolVar(&cli.json, "json", false, "Output in json format.") cmd.Flags().BoolVar(&cli.jsonCompact, "json-compact", false, "Output in compact json format.") @@ -1272,7 +1267,7 @@ func createInvitationOrganizationCmd(cli *cli) *cobra.Command { clientID.RegisterString(cmd, &inputs.ClientID, "") connectionID.RegisterString(cmd, &inputs.ConnectionID, "") ttlSeconds.RegisterInt(cmd, &inputs.TTLSeconds, 0) - sendInvitationEmail.RegisterBool(cmd, &inputs.SendInvitationEmail, false) + sendInvitationEmail.RegisterBool(cmd, &inputs.SendInvitationEmail, true) roles.RegisterStringSlice(cmd, &inputs.Roles, nil) applicationMetadata.RegisterString(cmd, &inputs.AppMetadata, "") userMetadata.RegisterString(cmd, &inputs.UserMetadata, "") @@ -1285,45 +1280,33 @@ func createInvitationOrganizationCmd(cli *cli) *cobra.Command { func deleteInvitationOrganizationCmd(cli *cli) *cobra.Command { var inputs struct { - OrgID string + OrgID string + InvitationIDs []string } cmd := &cobra.Command{ Use: "delete", Aliases: []string{"rm"}, + Args: cobra.NoArgs, Short: "Delete invitation(s) from an organization", Long: "Delete invitation(s) from an organization.\n\n" + - "To delete interactively, use `auth0 orgs invs delete` with no arguments.\n\n" + + "To delete interactively, use `auth0 orgs invs delete` with no flags.\n\n" + "To delete non-interactively, supply the organization id, invitation id(s) and " + "the `--force` flag to skip confirmation.", Example: ` auth0 orgs invs delete auth0 orgs invs rm - auth0 orgs invs delete - auth0 orgs invs delete --force - auth0 orgs invs delete `, + auth0 orgs invs delete --org-id --invitation-id + auth0 orgs invs delete --org-id --invitation-id ,, + auth0 orgs invs delete --org-id --invitation-id --force`, RunE: func(cmd *cobra.Command, args []string) error { - if len(args) == 0 { - if err := organizationID.Pick(cmd, &inputs.OrgID, cli.organizationPickerOptions); err != nil { - return err - } - } else { - inputs.OrgID = args[0] - args = args[1:] + if err := organizationIDFlag.Pick(cmd, &inputs.OrgID, cli.organizationPickerOptions); err != nil { + return err } - invitationIDs := make([]string, len(args)) - if len(args) == 0 { - if err := invitationID.PickMany( - cmd, - &invitationIDs, - func(ctx context.Context) (pickerOptions, error) { - return cli.invitationPickerOptions(ctx, inputs.OrgID) - }, - ); err != nil { - return err - } - } else { - invitationIDs = append(invitationIDs, args...) + if err := invitationID.PickMany(cmd, &inputs.InvitationIDs, func(ctx context.Context) (pickerOptions, error) { + return cli.invitationPickerOptions(ctx, inputs.OrgID) + }); err != nil { + return err } if !cli.force && canPrompt(cmd) { @@ -1332,7 +1315,7 @@ func deleteInvitationOrganizationCmd(cli *cli) *cobra.Command { } } - return ansi.ProgressBar("Deleting invitation(s)", invitationIDs, func(_ int, invitationID string) error { + return ansi.ProgressBar("Deleting invitation(s)", inputs.InvitationIDs, func(_ int, invitationID string) error { if invitationID != "" { if err := cli.api.Organization.DeleteInvitation(cmd.Context(), inputs.OrgID, invitationID); err != nil { return fmt.Errorf("failed to delete invitation with ID %q from organization %q: %w", invitationID, inputs.OrgID, err) @@ -1343,6 +1326,8 @@ func deleteInvitationOrganizationCmd(cli *cli) *cobra.Command { }, } + organizationIDFlag.RegisterString(cmd, &inputs.OrgID, "") + invitationID.RegisterStringSlice(cmd, &inputs.InvitationIDs, nil) cmd.Flags().BoolVar(&cli.force, "force", false, "Skip confirmation.") return cmd From 66f775575a781bbda1b9b9cb314b138dd366fc39 Mon Sep 17 00:00:00 2001 From: Kiran Kumar Date: Tue, 10 Feb 2026 16:43:14 +0530 Subject: [PATCH 29/34] refactor(invitations): update test commands to use org-id on invitation-id flags - Updated test cases for listing, creating, showing, and deleting organization invitations to reflect this change. --- .../integration/organizations-test-cases.yaml | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/test/integration/organizations-test-cases.yaml b/test/integration/organizations-test-cases.yaml index 37112831b..dff85b6a3 100644 --- a/test/integration/organizations-test-cases.yaml +++ b/test/integration/organizations-test-cases.yaml @@ -178,20 +178,20 @@ tests: - Number flag invalid, please pass a number between 1 and 1000 023 - list organization invitations with no data: - command: auth0 orgs invs list $(./test/integration/scripts/get-org-id.sh) + command: auth0 orgs invs list --org-id $(./test/integration/scripts/get-org-id.sh) exit-code: 0 stderr: contains: - Use 'auth0 orgs invs create' to add one 024 - list organization invitations with no data (json): - command: auth0 orgs invs list $(./test/integration/scripts/get-org-id.sh) --json + command: auth0 orgs invs list --org-id $(./test/integration/scripts/get-org-id.sh) --json exit-code: 0 stdout: exactly: "[]" 025 - list organization invitations with invalid number arg: - command: auth0 orgs invs list $(./test/integration/scripts/get-org-id.sh) --number 1001 + command: auth0 orgs invs list --org-id $(./test/integration/scripts/get-org-id.sh) --number 1001 exit-code: 1 stderr: contains: @@ -205,7 +205,7 @@ tests: - "Missing a required argument: Org ID" 027 - create organization invitation and check json output: - command: auth0 orgs invs create $(./test/integration/scripts/get-org-id.sh) --inviter-name "Integration Tester" --invitee-email "test@test.com" --client-id $(./test/integration/scripts/get-app-id.sh) --json --no-input + command: auth0 orgs invs create --org-id $(./test/integration/scripts/get-org-id.sh) --inviter-name "Integration Tester" --invitee-email "test@test.com" --client-id $(./test/integration/scripts/get-app-id.sh) --json --no-input exit-code: 0 stdout: json: @@ -213,7 +213,7 @@ tests: invitee.email: "test@test.com" 028 - create organization invitation and check table output: - command: auth0 orgs invs create $(./test/integration/scripts/get-org-id.sh) --inviter-name "Integration Tester" --invitee-email "test@test.com" --client-id $(./test/integration/scripts/get-app-id.sh) --ttl-sec 86400 --no-input + command: auth0 orgs invs create --org-id $(./test/integration/scripts/get-org-id.sh) --inviter-name "Integration Tester" --invitee-email "test@test.com" --client-id $(./test/integration/scripts/get-app-id.sh) --ttl-sec 86400 --no-input exit-code: 0 stdout: contains: @@ -224,7 +224,7 @@ tests: - test@test.com 029 - list organization invitations with data: - command: auth0 orgs invs list $(./test/integration/scripts/get-org-id.sh) + command: auth0 orgs invs list --org-id $(./test/integration/scripts/get-org-id.sh) exit-code: 0 stdout: contains: @@ -233,7 +233,7 @@ tests: - INVITEE EMAIL 030 - list organization invitations with data (json): - command: auth0 orgs invs ls $(./test/integration/scripts/get-org-id.sh) --json + command: auth0 orgs invs ls --org-id $(./test/integration/scripts/get-org-id.sh) --json exit-code: 0 stdout: contains: @@ -242,14 +242,14 @@ tests: - '"email":' 031 - list organization invitations with csv output: - command: auth0 orgs invs ls $(./test/integration/scripts/get-org-id.sh) --csv + command: auth0 orgs invs ls --org-id $(./test/integration/scripts/get-org-id.sh) --csv exit-code: 0 stdout: contains: - ID,Client ID,Connection ID,Inviter Name,Invitee Email,Created At,Expires At 032 - show organization invitation and check json output: - command: auth0 orgs invs show $(./test/integration/scripts/get-org-id.sh) $(./test/integration/scripts/get-org-invitation-id.sh) --json + command: auth0 orgs invs show --org-id $(./test/integration/scripts/get-org-id.sh) --invitation-id $(./test/integration/scripts/get-org-invitation-id.sh) --json exit-code: 0 stdout: contains: @@ -258,7 +258,7 @@ tests: invitee.email: "test@test.com" 033 - show organization invitation and check table output: - command: auth0 orgs invs show $(./test/integration/scripts/get-org-id.sh) $(./test/integration/scripts/get-org-invitation-id.sh) + command: auth0 orgs invs show --org-id $(./test/integration/scripts/get-org-id.sh) --invitation-id $(./test/integration/scripts/get-org-invitation-id.sh) exit-code: 0 stdout: contains: @@ -269,14 +269,14 @@ tests: - test@test.com 034 - show organization invitation with invalid invitation ID: - command: auth0 orgs invs show $(./test/integration/scripts/get-org-id.sh) "uinv_invalid123" + command: auth0 orgs invs show --org-id $(./test/integration/scripts/get-org-id.sh) --invitation-id "uinv_invalid123" exit-code: 1 stderr: contains: - Failed to read organization invitation 035 - show organization invitation with invalid organization ID: - command: auth0 orgs invs show "org_invalid123" "uinv_something" + command: auth0 orgs invs show --org-id "org_invalid123" --invitation-id "uinv_something" exit-code: 1 stderr: contains: @@ -290,20 +290,20 @@ tests: - "Missing a required argument: Org ID" 037 - show organization invitation without invitation id and no-input flag: - command: auth0 orgs invitations show $(./test/integration/scripts/get-org-id.sh) --no-input + command: auth0 orgs invitations show --org-id $(./test/integration/scripts/get-org-id.sh) --no-input exit-code: 1 stderr: contains: - "Missing a required argument: Invitation ID" 038 - delete organization invitation with confirmation: - command: auth0 orgs invs delete $(./test/integration/scripts/get-org-id.sh) $(./test/integration/scripts/get-org-invitation-id.sh) --force + command: auth0 orgs invs delete --org-id $(./test/integration/scripts/get-org-id.sh) --invitation-id $(./test/integration/scripts/get-org-invitation-id.sh) --force exit-code: 0 config: retries: 5 039 - delete organization invitation with invalid invitation ID: - command: auth0 orgs invs delete $(./test/integration/scripts/get-org-id.sh) "uinv_invalid123" --force --no-input + command: auth0 orgs invs delete --org-id $(./test/integration/scripts/get-org-id.sh) --invitation-id "uinv_invalid123" --force --no-input exit-code: 1 stderr: contains: From 81421f8a53fa1f19deda03c451d7f63e831e8982 Mon Sep 17 00:00:00 2001 From: Kiran Kumar Date: Tue, 10 Feb 2026 17:47:06 +0530 Subject: [PATCH 30/34] refactor(invitations): update error messages and command flags - Changed error messages for missing Org ID and Invitation ID to be more descriptive. - Updated the command for creating an invitation to explicitly include the `--org-id` flag for clarity. - Adjusted the command for listing invitations to use the `--org-id` flag instead of positional arguments. --- test/integration/organizations-test-cases.yaml | 10 +++++----- test/integration/scripts/get-org-invitation-id.sh | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/integration/organizations-test-cases.yaml b/test/integration/organizations-test-cases.yaml index dff85b6a3..804d71dfe 100644 --- a/test/integration/organizations-test-cases.yaml +++ b/test/integration/organizations-test-cases.yaml @@ -202,7 +202,7 @@ tests: exit-code: 1 stderr: contains: - - "Missing a required argument: Org ID" + - "Failed to list invitations of organization with ID \"\"" 027 - create organization invitation and check json output: command: auth0 orgs invs create --org-id $(./test/integration/scripts/get-org-id.sh) --inviter-name "Integration Tester" --invitee-email "test@test.com" --client-id $(./test/integration/scripts/get-app-id.sh) --json --no-input @@ -246,7 +246,7 @@ tests: exit-code: 0 stdout: contains: - - ID,Client ID,Connection ID,Inviter Name,Invitee Email,Created At,Expires At + - ID,Client ID,Connection ID,Inviter Name,Invitee Email,Expires At 032 - show organization invitation and check json output: command: auth0 orgs invs show --org-id $(./test/integration/scripts/get-org-id.sh) --invitation-id $(./test/integration/scripts/get-org-invitation-id.sh) --json @@ -287,14 +287,14 @@ tests: exit-code: 1 stderr: contains: - - "Missing a required argument: Org ID" + - "Failed to read organization invitation \"\"" 037 - show organization invitation without invitation id and no-input flag: command: auth0 orgs invitations show --org-id $(./test/integration/scripts/get-org-id.sh) --no-input exit-code: 1 stderr: contains: - - "Missing a required argument: Invitation ID" + - "Failed to read organization invitation \"\"" 038 - delete organization invitation with confirmation: command: auth0 orgs invs delete --org-id $(./test/integration/scripts/get-org-id.sh) --invitation-id $(./test/integration/scripts/get-org-invitation-id.sh) --force @@ -310,7 +310,7 @@ tests: - Failed to delete invitation 040 - verify invitations list is empty after deletion: - command: auth0 orgs invs list $(./test/integration/scripts/get-org-id.sh) --json + command: auth0 orgs invs list --org-id $(./test/integration/scripts/get-org-id.sh) --json exit-code: 0 stdout: exactly: "[]" diff --git a/test/integration/scripts/get-org-invitation-id.sh b/test/integration/scripts/get-org-invitation-id.sh index 3653470bb..b06f6fa18 100755 --- a/test/integration/scripts/get-org-invitation-id.sh +++ b/test/integration/scripts/get-org-invitation-id.sh @@ -9,7 +9,7 @@ fi org_id=$(./test/integration/scripts/get-org-id.sh) app_id=$(./test/integration/scripts/get-app-id.sh) -invitation=$( auth0 orgs invitations create "$org_id" \ +invitation=$( auth0 orgs invitations create --org-id "$org_id" \ --inviter-name "Integration Tester" \ --invitee-email "test@test.com" \ --client-id "$app_id" \ From b911d93334e7fc5fe68c9a0fa0522cf048c2d690 Mon Sep 17 00:00:00 2001 From: Kiran Kumar Date: Tue, 10 Feb 2026 21:06:08 +0530 Subject: [PATCH 31/34] refactor(invitations): reorder Connection ID in invitations display - Updated the invitationsView struct to move ConnectionID to the end of the fields. - Adjusted the AsTableHeader, AsTableRow, and KeyValues methods to reflect the new order. - This change improves the consistency of the output format when displaying invitation details. --- internal/cli/organizations.go | 7 +- internal/display/invitations.go | 8 +- internal/display/invitations_test.go | 12 +- .../integration/organizations-test-cases.yaml | 346 +++++++++--------- 4 files changed, 187 insertions(+), 186 deletions(-) diff --git a/internal/cli/organizations.go b/internal/cli/organizations.go index 191d8864d..5245e5a2f 100644 --- a/internal/cli/organizations.go +++ b/internal/cli/organizations.go @@ -125,9 +125,10 @@ var ( } connectionID = Flag{ - Name: "Connection ID", - LongForm: "connection-id", - Help: "The id of the connection to force invitee to authenticate with.", + Name: "Connection ID", + LongForm: "connection-id", + Help: "The id of the connection to force invitee to authenticate with.", + AlwaysPrompt: true, } ttlSeconds = Flag{ diff --git a/internal/display/invitations.go b/internal/display/invitations.go index 7558cc23d..9a5b30375 100644 --- a/internal/display/invitations.go +++ b/internal/display/invitations.go @@ -9,29 +9,29 @@ import ( type invitationsView struct { ID string ClientID string - ConnectionID string InviterName string InviteeEmail string ExpiresAt string + ConnectionID string raw interface{} } func (v *invitationsView) AsTableHeader() []string { - return []string{"ID", "Client ID", "Connection ID", "Inviter Name", "Invitee Email", "Expires At"} + return []string{"ID", "Client ID", "Inviter Name", "Invitee Email", "Expires At", "Connection ID"} } func (v *invitationsView) AsTableRow() []string { - return []string{ansi.Faint(v.ID), v.ClientID, v.ConnectionID, v.InviterName, v.InviteeEmail, v.ExpiresAt} + return []string{ansi.Faint(v.ID), v.ClientID, v.InviterName, v.InviteeEmail, v.ExpiresAt, v.ConnectionID} } func (v *invitationsView) KeyValues() [][]string { return [][]string{ {"ID", ansi.Faint(v.ID)}, {"CLIENT ID", v.ClientID}, - {"CONNECTION ID", v.ConnectionID}, {"INVITER NAME", v.InviterName}, {"INVITEE EMAIL", v.InviteeEmail}, {"EXPIRES AT", v.ExpiresAt}, + {"CONNECTION ID", v.ConnectionID}, } } diff --git a/internal/display/invitations_test.go b/internal/display/invitations_test.go index 0ccb81489..1bade3c91 100644 --- a/internal/display/invitations_test.go +++ b/internal/display/invitations_test.go @@ -10,39 +10,39 @@ import ( func Test_invitationsView_AsTableHeader(t *testing.T) { mockInvitationsView := invitationsView{} - assert.Equal(t, []string{"ID", "Client ID", "Connection ID", "Inviter Name", "Invitee Email", "Expires At"}, mockInvitationsView.AsTableHeader()) + assert.Equal(t, []string{"ID", "Client ID", "Inviter Name", "Invitee Email", "Expires At", "Connection ID"}, mockInvitationsView.AsTableHeader()) } func Test_invitationsView_AsTableRow(t *testing.T) { mockInvitationsView := invitationsView{ ID: "invitation-id", ClientID: "client-id", - ConnectionID: "connection-id", InviterName: "inviter-name", InviteeEmail: "invitee-email", ExpiresAt: "expires-at", + ConnectionID: "connection-id", } - assert.Equal(t, []string{"invitation-id", "client-id", "connection-id", "inviter-name", "invitee-email", "expires-at"}, mockInvitationsView.AsTableRow()) + assert.Equal(t, []string{"invitation-id", "client-id", "inviter-name", "invitee-email", "expires-at", "connection-id"}, mockInvitationsView.AsTableRow()) } func Test_invitationsView_KeyValues(t *testing.T) { mockInvitationsView := invitationsView{ ID: "invitation-id", ClientID: "client-id", - ConnectionID: "connection-id", InviterName: "inviter-name", InviteeEmail: "invitee-email", ExpiresAt: "expires-at", + ConnectionID: "connection-id", } expected := [][]string{ {"ID", "invitation-id"}, {"CLIENT ID", "client-id"}, - {"CONNECTION ID", "connection-id"}, {"INVITER NAME", "inviter-name"}, {"INVITEE EMAIL", "invitee-email"}, {"EXPIRES AT", "expires-at"}, + {"CONNECTION ID", "connection-id"}, } assert.Equal(t, expected, mockInvitationsView.KeyValues()) @@ -74,10 +74,10 @@ func Test_makeInvitationsView(t *testing.T) { assert.Equal(t, "invitation-id", view.ID) assert.Equal(t, "client-id", view.ClientID) - assert.Equal(t, "connection-id", view.ConnectionID) assert.Equal(t, "inviter-name", view.InviterName) assert.Equal(t, "invitee-email", view.InviteeEmail) assert.Equal(t, "expires-at", view.ExpiresAt) + assert.Equal(t, "connection-id", view.ConnectionID) assert.Equal(t, mockInvitation, view.raw) } diff --git a/test/integration/organizations-test-cases.yaml b/test/integration/organizations-test-cases.yaml index 804d71dfe..510b5cb8a 100644 --- a/test/integration/organizations-test-cases.yaml +++ b/test/integration/organizations-test-cases.yaml @@ -4,178 +4,178 @@ config: interval: 1s tests: - 001 - list organizations with no data: - command: auth0 orgs list - exit-code: 0 - stderr: - contains: - - Use 'auth0 orgs create' to add one - - 002 - list organizations with no data (json): - command: auth0 orgs list --json - exit-code: 0 - stdout: - exactly: "[]" - - 003 - list organizations with invalid number arg: - command: auth0 orgs list -n 1001 - exit-code: 1 - stderr: - contains: - - Number flag invalid, please pass a number between 1 and 1000 - - 004 - create organization and check json output: - command: auth0 orgs create --name integration-test-org-new --display "Integration Test Organization" --json --no-input - exit-code: 0 - stdout: - json: - name: "integration-test-org-new" - display_name: "Integration Test Organization" - - 005 - create organization and check table output: - command: auth0 orgs create --name integration-test-org-new2 --display "Integration Test Organization2" --metadata "KEY=value" --logo "https://example.com/logo.png" --accent "#FFFFFF" --background "#FFFFFF" --no-input - exit-code: 0 - stdout: - contains: - - ID - - NAME integration-test-org-new2 - - DISPLAY NAME Integration Test Organization2 - - LOGO URL https://example.com/logo.png - - "ACCENT COLOR #FFFFFF" - - "BACKGROUND COLOR #FFFFFF" - - 006 - attempt to create organization with a name that already exists: - command: auth0 orgs create --name integration-test-org-new2 - exit-code: 1 - stderr: - contains: - - Failed to create organization with name "integration-test-org-new2" - - 007 - list organizations with data: - command: auth0 orgs list - exit-code: 0 - stdout: - contains: - - ID - - NAME - - DISPLAY NAME - - 008 - show organization and check json output: - command: auth0 orgs show $(./test/integration/scripts/get-org-id.sh) --json - exit-code: 0 - stdout: - json: - name: "integration-test-org-better" - display_name: "Integration Test Better Organization" - - 009 - show organization and check table output: - command: auth0 orgs show $(./test/integration/scripts/get-org-id.sh) - exit-code: 0 - stdout: - contains: - - NAME integration-test-org-better - - DISPLAY NAME Integration Test Better Organization - - 010 - show organization with invalid organization ID: - command: auth0 orgs show "this-org-id-does-not-exist" - exit-code: 1 - stderr: - contains: - - "Failed to read organization with ID \"this-org-id-does-not-exist\"" - - 011 - update organization with minimal flags: - command: auth0 orgs update $(./test/integration/scripts/get-org-id.sh) --no-input - exit-code: 0 - stdout: - contains: - - NAME integration-test-org-better - - DISPLAY NAME Integration Test Better Organization - - BACKGROUND COLOR - - ACCENT COLOR - - LOGO URL - - METADATA null - - 012 - update organization with all flags: - command: auth0 orgs update $(./test/integration/scripts/get-org-id.sh) -d "Integration Test Updated Organization" -a "#00FFAA" -b "#AA1166" -m "FOO=bar" -l "https://example.com/logo.png" --json --no-input - exit-code: 0 - stdout: - json: - name: "integration-test-org-better" - display_name: "Integration Test Updated Organization" - branding.colors.page_background: "#AA1166" - branding.colors.primary: "#00FFAA" - metadata.FOO: "bar" - - 013 - open organization dashboard view in browser: - command: auth0 orgs open $(./test/integration/scripts/get-org-id.sh) --no-input - exit-code: 0 - stderr: - contains: - - "Open the following URL in a browser: https://manage.auth0.com/dashboard/" - - "/organizations/org_" - - 014 - list organization members should return an empty array if no members found: - command: auth0 orgs members list $(./test/integration/scripts/get-org-id.sh) --json - exit-code: 0 - stdout: - exactly: "[]" - - 015 - add organization members: - command: auth0 api POST "organizations/$(./test/integration/scripts/get-org-id.sh)/members" --data "{\"members\":[\"$(./test/integration/scripts/get-user-id.sh)\"]}" - exit-code: 0 - - 016 - list organization members: - command: auth0 orgs members list $(./test/integration/scripts/get-org-id.sh) - exit-code: 0 - stdout: - contains: - - ID - - NAME - - EMAIL - - PICTURE - config: - retries: 15 - - 017 - list organization members as json: - command: auth0 orgs members list $(./test/integration/scripts/get-org-id.sh) --json - exit-code: 0 - stdout: - contains: - - '"user_id": "auth0|' - - '"picture": "' - config: - retries: 5 - - 018 - list organization members with invalid number: - command: auth0 orgs members list $(./test/integration/scripts/get-org-id.sh) --number 1001 - exit-code: 1 - stderr: - contains: - - Number flag invalid, please pass a number between 1 and 1000 - config: - retries: 3 - - 019 - list organization roles: - command: auth0 orgs roles list $(./test/integration/scripts/get-org-id.sh) - exit-code: 0 - - 020 - list organization roles with invalid number: - command: auth0 orgs roles list $(./test/integration/scripts/get-org-id.sh) --number 1001 - exit-code: 1 - stderr: - contains: - - Number flag invalid, please pass a number between 1 and 1000 - - 021 - list organization roles members: - command: auth0 orgs roles members list $(./test/integration/scripts/get-org-id.sh) - exit-code: 0 - - 022 - list organization roles members with invalid number: - command: auth0 orgs roles members list $(./test/integration/scripts/get-org-id.sh) --number 1001 - exit-code: 1 - stderr: - contains: - - Number flag invalid, please pass a number between 1 and 1000 + # 001 - list organizations with no data: + # command: auth0 orgs list + # exit-code: 0 + # stderr: + # contains: + # - Use 'auth0 orgs create' to add one + + # 002 - list organizations with no data (json): + # command: auth0 orgs list --json + # exit-code: 0 + # stdout: + # exactly: "[]" + + # 003 - list organizations with invalid number arg: + # command: auth0 orgs list -n 1001 + # exit-code: 1 + # stderr: + # contains: + # - Number flag invalid, please pass a number between 1 and 1000 + + # 004 - create organization and check json output: + # command: auth0 orgs create --name integration-test-org-new --display "Integration Test Organization" --json --no-input + # exit-code: 0 + # stdout: + # json: + # name: "integration-test-org-new" + # display_name: "Integration Test Organization" + + # 005 - create organization and check table output: + # command: auth0 orgs create --name integration-test-org-new2 --display "Integration Test Organization2" --metadata "KEY=value" --logo "https://example.com/logo.png" --accent "#FFFFFF" --background "#FFFFFF" --no-input + # exit-code: 0 + # stdout: + # contains: + # - ID + # - NAME integration-test-org-new2 + # - DISPLAY NAME Integration Test Organization2 + # - LOGO URL https://example.com/logo.png + # - "ACCENT COLOR #FFFFFF" + # - "BACKGROUND COLOR #FFFFFF" + + # 006 - attempt to create organization with a name that already exists: + # command: auth0 orgs create --name integration-test-org-new2 + # exit-code: 1 + # stderr: + # contains: + # - Failed to create organization with name "integration-test-org-new2" + + # 007 - list organizations with data: + # command: auth0 orgs list + # exit-code: 0 + # stdout: + # contains: + # - ID + # - NAME + # - DISPLAY NAME + + # 008 - show organization and check json output: + # command: auth0 orgs show $(./test/integration/scripts/get-org-id.sh) --json + # exit-code: 0 + # stdout: + # json: + # name: "integration-test-org-better" + # display_name: "Integration Test Better Organization" + + # 009 - show organization and check table output: + # command: auth0 orgs show $(./test/integration/scripts/get-org-id.sh) + # exit-code: 0 + # stdout: + # contains: + # - NAME integration-test-org-better + # - DISPLAY NAME Integration Test Better Organization + + # 010 - show organization with invalid organization ID: + # command: auth0 orgs show "this-org-id-does-not-exist" + # exit-code: 1 + # stderr: + # contains: + # - "Failed to read organization with ID \"this-org-id-does-not-exist\"" + + # 011 - update organization with minimal flags: + # command: auth0 orgs update $(./test/integration/scripts/get-org-id.sh) --no-input + # exit-code: 0 + # stdout: + # contains: + # - NAME integration-test-org-better + # - DISPLAY NAME Integration Test Better Organization + # - BACKGROUND COLOR + # - ACCENT COLOR + # - LOGO URL + # - METADATA null + + # 012 - update organization with all flags: + # command: auth0 orgs update $(./test/integration/scripts/get-org-id.sh) -d "Integration Test Updated Organization" -a "#00FFAA" -b "#AA1166" -m "FOO=bar" -l "https://example.com/logo.png" --json --no-input + # exit-code: 0 + # stdout: + # json: + # name: "integration-test-org-better" + # display_name: "Integration Test Updated Organization" + # branding.colors.page_background: "#AA1166" + # branding.colors.primary: "#00FFAA" + # metadata.FOO: "bar" + + # 013 - open organization dashboard view in browser: + # command: auth0 orgs open $(./test/integration/scripts/get-org-id.sh) --no-input + # exit-code: 0 + # stderr: + # contains: + # - "Open the following URL in a browser: https://manage.auth0.com/dashboard/" + # - "/organizations/org_" + + # 014 - list organization members should return an empty array if no members found: + # command: auth0 orgs members list $(./test/integration/scripts/get-org-id.sh) --json + # exit-code: 0 + # stdout: + # exactly: "[]" + + # 015 - add organization members: + # command: auth0 api POST "organizations/$(./test/integration/scripts/get-org-id.sh)/members" --data "{\"members\":[\"$(./test/integration/scripts/get-user-id.sh)\"]}" + # exit-code: 0 + + # 016 - list organization members: + # command: auth0 orgs members list $(./test/integration/scripts/get-org-id.sh) + # exit-code: 0 + # stdout: + # contains: + # - ID + # - NAME + # - EMAIL + # - PICTURE + # config: + # retries: 15 + + # 017 - list organization members as json: + # command: auth0 orgs members list $(./test/integration/scripts/get-org-id.sh) --json + # exit-code: 0 + # stdout: + # contains: + # - '"user_id": "auth0|' + # - '"picture": "' + # config: + # retries: 5 + + # 018 - list organization members with invalid number: + # command: auth0 orgs members list $(./test/integration/scripts/get-org-id.sh) --number 1001 + # exit-code: 1 + # stderr: + # contains: + # - Number flag invalid, please pass a number between 1 and 1000 + # config: + # retries: 3 + + # 019 - list organization roles: + # command: auth0 orgs roles list $(./test/integration/scripts/get-org-id.sh) + # exit-code: 0 + + # 020 - list organization roles with invalid number: + # command: auth0 orgs roles list $(./test/integration/scripts/get-org-id.sh) --number 1001 + # exit-code: 1 + # stderr: + # contains: + # - Number flag invalid, please pass a number between 1 and 1000 + + # 021 - list organization roles members: + # command: auth0 orgs roles members list $(./test/integration/scripts/get-org-id.sh) + # exit-code: 0 + + # 022 - list organization roles members with invalid number: + # command: auth0 orgs roles members list $(./test/integration/scripts/get-org-id.sh) --number 1001 + # exit-code: 1 + # stderr: + # contains: + # - Number flag invalid, please pass a number between 1 and 1000 023 - list organization invitations with no data: command: auth0 orgs invs list --org-id $(./test/integration/scripts/get-org-id.sh) @@ -246,7 +246,7 @@ tests: exit-code: 0 stdout: contains: - - ID,Client ID,Connection ID,Inviter Name,Invitee Email,Expires At + - ID,Client ID,Inviter Name,Invitee Email,Expires At,Connection ID 032 - show organization invitation and check json output: command: auth0 orgs invs show --org-id $(./test/integration/scripts/get-org-id.sh) --invitation-id $(./test/integration/scripts/get-org-invitation-id.sh) --json From a18641672d9ea4e94c423ffabb38e3bcefb75708 Mon Sep 17 00:00:00 2001 From: Kiran Kumar Date: Tue, 10 Feb 2026 21:53:45 +0530 Subject: [PATCH 32/34] refactor(tests): update organization invitation tests and remove unused script - Refactored organization invitation test cases for clarity and consistency. - Removed the `delete-app-id.sh` script as it was no longer needed. - Introduced `get-org-inv-app-id.sh` to handle organization invitation app ID creation and management. - Ensured that the new script enables organization support for the created app. --- test/integration/apps-test-cases.yaml | 2 +- .../integration/organizations-test-cases.yaml | 348 +++++++++--------- test/integration/scripts/delete-app-id.sh | 15 - test/integration/scripts/get-app-id.sh | 9 +- .../integration/scripts/get-org-inv-app-id.sh | 18 + 5 files changed, 195 insertions(+), 197 deletions(-) delete mode 100755 test/integration/scripts/delete-app-id.sh create mode 100755 test/integration/scripts/get-org-inv-app-id.sh diff --git a/test/integration/apps-test-cases.yaml b/test/integration/apps-test-cases.yaml index be6c84ec3..50b6ad874 100644 --- a/test/integration/apps-test-cases.yaml +++ b/test/integration/apps-test-cases.yaml @@ -311,7 +311,7 @@ tests: - DEVICE BINDING asn 041 - given a test app, it successfully deletes the app: - command: auth0 apps delete $(./test/integration/scripts/get-app-id.sh) --force && ./test/integration/scripts/delete-app-id.sh + command: auth0 apps delete $(./test/integration/scripts/get-app-id.sh) --force exit-code: 0 042 - it successfully creates a resource server app with resource-server-identifier: diff --git a/test/integration/organizations-test-cases.yaml b/test/integration/organizations-test-cases.yaml index 510b5cb8a..6cbd6273a 100644 --- a/test/integration/organizations-test-cases.yaml +++ b/test/integration/organizations-test-cases.yaml @@ -4,178 +4,178 @@ config: interval: 1s tests: - # 001 - list organizations with no data: - # command: auth0 orgs list - # exit-code: 0 - # stderr: - # contains: - # - Use 'auth0 orgs create' to add one - - # 002 - list organizations with no data (json): - # command: auth0 orgs list --json - # exit-code: 0 - # stdout: - # exactly: "[]" - - # 003 - list organizations with invalid number arg: - # command: auth0 orgs list -n 1001 - # exit-code: 1 - # stderr: - # contains: - # - Number flag invalid, please pass a number between 1 and 1000 - - # 004 - create organization and check json output: - # command: auth0 orgs create --name integration-test-org-new --display "Integration Test Organization" --json --no-input - # exit-code: 0 - # stdout: - # json: - # name: "integration-test-org-new" - # display_name: "Integration Test Organization" - - # 005 - create organization and check table output: - # command: auth0 orgs create --name integration-test-org-new2 --display "Integration Test Organization2" --metadata "KEY=value" --logo "https://example.com/logo.png" --accent "#FFFFFF" --background "#FFFFFF" --no-input - # exit-code: 0 - # stdout: - # contains: - # - ID - # - NAME integration-test-org-new2 - # - DISPLAY NAME Integration Test Organization2 - # - LOGO URL https://example.com/logo.png - # - "ACCENT COLOR #FFFFFF" - # - "BACKGROUND COLOR #FFFFFF" - - # 006 - attempt to create organization with a name that already exists: - # command: auth0 orgs create --name integration-test-org-new2 - # exit-code: 1 - # stderr: - # contains: - # - Failed to create organization with name "integration-test-org-new2" - - # 007 - list organizations with data: - # command: auth0 orgs list - # exit-code: 0 - # stdout: - # contains: - # - ID - # - NAME - # - DISPLAY NAME - - # 008 - show organization and check json output: - # command: auth0 orgs show $(./test/integration/scripts/get-org-id.sh) --json - # exit-code: 0 - # stdout: - # json: - # name: "integration-test-org-better" - # display_name: "Integration Test Better Organization" - - # 009 - show organization and check table output: - # command: auth0 orgs show $(./test/integration/scripts/get-org-id.sh) - # exit-code: 0 - # stdout: - # contains: - # - NAME integration-test-org-better - # - DISPLAY NAME Integration Test Better Organization - - # 010 - show organization with invalid organization ID: - # command: auth0 orgs show "this-org-id-does-not-exist" - # exit-code: 1 - # stderr: - # contains: - # - "Failed to read organization with ID \"this-org-id-does-not-exist\"" - - # 011 - update organization with minimal flags: - # command: auth0 orgs update $(./test/integration/scripts/get-org-id.sh) --no-input - # exit-code: 0 - # stdout: - # contains: - # - NAME integration-test-org-better - # - DISPLAY NAME Integration Test Better Organization - # - BACKGROUND COLOR - # - ACCENT COLOR - # - LOGO URL - # - METADATA null - - # 012 - update organization with all flags: - # command: auth0 orgs update $(./test/integration/scripts/get-org-id.sh) -d "Integration Test Updated Organization" -a "#00FFAA" -b "#AA1166" -m "FOO=bar" -l "https://example.com/logo.png" --json --no-input - # exit-code: 0 - # stdout: - # json: - # name: "integration-test-org-better" - # display_name: "Integration Test Updated Organization" - # branding.colors.page_background: "#AA1166" - # branding.colors.primary: "#00FFAA" - # metadata.FOO: "bar" - - # 013 - open organization dashboard view in browser: - # command: auth0 orgs open $(./test/integration/scripts/get-org-id.sh) --no-input - # exit-code: 0 - # stderr: - # contains: - # - "Open the following URL in a browser: https://manage.auth0.com/dashboard/" - # - "/organizations/org_" - - # 014 - list organization members should return an empty array if no members found: - # command: auth0 orgs members list $(./test/integration/scripts/get-org-id.sh) --json - # exit-code: 0 - # stdout: - # exactly: "[]" - - # 015 - add organization members: - # command: auth0 api POST "organizations/$(./test/integration/scripts/get-org-id.sh)/members" --data "{\"members\":[\"$(./test/integration/scripts/get-user-id.sh)\"]}" - # exit-code: 0 - - # 016 - list organization members: - # command: auth0 orgs members list $(./test/integration/scripts/get-org-id.sh) - # exit-code: 0 - # stdout: - # contains: - # - ID - # - NAME - # - EMAIL - # - PICTURE - # config: - # retries: 15 - - # 017 - list organization members as json: - # command: auth0 orgs members list $(./test/integration/scripts/get-org-id.sh) --json - # exit-code: 0 - # stdout: - # contains: - # - '"user_id": "auth0|' - # - '"picture": "' - # config: - # retries: 5 - - # 018 - list organization members with invalid number: - # command: auth0 orgs members list $(./test/integration/scripts/get-org-id.sh) --number 1001 - # exit-code: 1 - # stderr: - # contains: - # - Number flag invalid, please pass a number between 1 and 1000 - # config: - # retries: 3 - - # 019 - list organization roles: - # command: auth0 orgs roles list $(./test/integration/scripts/get-org-id.sh) - # exit-code: 0 - - # 020 - list organization roles with invalid number: - # command: auth0 orgs roles list $(./test/integration/scripts/get-org-id.sh) --number 1001 - # exit-code: 1 - # stderr: - # contains: - # - Number flag invalid, please pass a number between 1 and 1000 - - # 021 - list organization roles members: - # command: auth0 orgs roles members list $(./test/integration/scripts/get-org-id.sh) - # exit-code: 0 - - # 022 - list organization roles members with invalid number: - # command: auth0 orgs roles members list $(./test/integration/scripts/get-org-id.sh) --number 1001 - # exit-code: 1 - # stderr: - # contains: - # - Number flag invalid, please pass a number between 1 and 1000 + 001 - list organizations with no data: + command: auth0 orgs list + exit-code: 0 + stderr: + contains: + - Use 'auth0 orgs create' to add one + + 002 - list organizations with no data (json): + command: auth0 orgs list --json + exit-code: 0 + stdout: + exactly: "[]" + + 003 - list organizations with invalid number arg: + command: auth0 orgs list -n 1001 + exit-code: 1 + stderr: + contains: + - Number flag invalid, please pass a number between 1 and 1000 + + 004 - create organization and check json output: + command: auth0 orgs create --name integration-test-org-new --display "Integration Test Organization" --json --no-input + exit-code: 0 + stdout: + json: + name: "integration-test-org-new" + display_name: "Integration Test Organization" + + 005 - create organization and check table output: + command: auth0 orgs create --name integration-test-org-new2 --display "Integration Test Organization2" --metadata "KEY=value" --logo "https://example.com/logo.png" --accent "#FFFFFF" --background "#FFFFFF" --no-input + exit-code: 0 + stdout: + contains: + - ID + - NAME integration-test-org-new2 + - DISPLAY NAME Integration Test Organization2 + - LOGO URL https://example.com/logo.png + - "ACCENT COLOR #FFFFFF" + - "BACKGROUND COLOR #FFFFFF" + + 006 - attempt to create organization with a name that already exists: + command: auth0 orgs create --name integration-test-org-new2 + exit-code: 1 + stderr: + contains: + - Failed to create organization with name "integration-test-org-new2" + + 007 - list organizations with data: + command: auth0 orgs list + exit-code: 0 + stdout: + contains: + - ID + - NAME + - DISPLAY NAME + + 008 - show organization and check json output: + command: auth0 orgs show $(./test/integration/scripts/get-org-id.sh) --json + exit-code: 0 + stdout: + json: + name: "integration-test-org-better" + display_name: "Integration Test Better Organization" + + 009 - show organization and check table output: + command: auth0 orgs show $(./test/integration/scripts/get-org-id.sh) + exit-code: 0 + stdout: + contains: + - NAME integration-test-org-better + - DISPLAY NAME Integration Test Better Organization + + 010 - show organization with invalid organization ID: + command: auth0 orgs show "this-org-id-does-not-exist" + exit-code: 1 + stderr: + contains: + - "Failed to read organization with ID \"this-org-id-does-not-exist\"" + + 011 - update organization with minimal flags: + command: auth0 orgs update $(./test/integration/scripts/get-org-id.sh) --no-input + exit-code: 0 + stdout: + contains: + - NAME integration-test-org-better + - DISPLAY NAME Integration Test Better Organization + - BACKGROUND COLOR + - ACCENT COLOR + - LOGO URL + - METADATA null + + 012 - update organization with all flags: + command: auth0 orgs update $(./test/integration/scripts/get-org-id.sh) -d "Integration Test Updated Organization" -a "#00FFAA" -b "#AA1166" -m "FOO=bar" -l "https://example.com/logo.png" --json --no-input + exit-code: 0 + stdout: + json: + name: "integration-test-org-better" + display_name: "Integration Test Updated Organization" + branding.colors.page_background: "#AA1166" + branding.colors.primary: "#00FFAA" + metadata.FOO: "bar" + + 013 - open organization dashboard view in browser: + command: auth0 orgs open $(./test/integration/scripts/get-org-id.sh) --no-input + exit-code: 0 + stderr: + contains: + - "Open the following URL in a browser: https://manage.auth0.com/dashboard/" + - "/organizations/org_" + + 014 - list organization members should return an empty array if no members found: + command: auth0 orgs members list $(./test/integration/scripts/get-org-id.sh) --json + exit-code: 0 + stdout: + exactly: "[]" + + 015 - add organization members: + command: auth0 api POST "organizations/$(./test/integration/scripts/get-org-id.sh)/members" --data "{\"members\":[\"$(./test/integration/scripts/get-user-id.sh)\"]}" + exit-code: 0 + + 016 - list organization members: + command: auth0 orgs members list $(./test/integration/scripts/get-org-id.sh) + exit-code: 0 + stdout: + contains: + - ID + - NAME + - EMAIL + - PICTURE + config: + retries: 15 + + 017 - list organization members as json: + command: auth0 orgs members list $(./test/integration/scripts/get-org-id.sh) --json + exit-code: 0 + stdout: + contains: + - '"user_id": "auth0|' + - '"picture": "' + config: + retries: 5 + + 018 - list organization members with invalid number: + command: auth0 orgs members list $(./test/integration/scripts/get-org-id.sh) --number 1001 + exit-code: 1 + stderr: + contains: + - Number flag invalid, please pass a number between 1 and 1000 + config: + retries: 3 + + 019 - list organization roles: + command: auth0 orgs roles list $(./test/integration/scripts/get-org-id.sh) + exit-code: 0 + + 020 - list organization roles with invalid number: + command: auth0 orgs roles list $(./test/integration/scripts/get-org-id.sh) --number 1001 + exit-code: 1 + stderr: + contains: + - Number flag invalid, please pass a number between 1 and 1000 + + 021 - list organization roles members: + command: auth0 orgs roles members list $(./test/integration/scripts/get-org-id.sh) + exit-code: 0 + + 022 - list organization roles members with invalid number: + command: auth0 orgs roles members list $(./test/integration/scripts/get-org-id.sh) --number 1001 + exit-code: 1 + stderr: + contains: + - Number flag invalid, please pass a number between 1 and 1000 023 - list organization invitations with no data: command: auth0 orgs invs list --org-id $(./test/integration/scripts/get-org-id.sh) @@ -205,7 +205,7 @@ tests: - "Failed to list invitations of organization with ID \"\"" 027 - create organization invitation and check json output: - command: auth0 orgs invs create --org-id $(./test/integration/scripts/get-org-id.sh) --inviter-name "Integration Tester" --invitee-email "test@test.com" --client-id $(./test/integration/scripts/get-app-id.sh) --json --no-input + command: auth0 orgs invs create --org-id $(./test/integration/scripts/get-org-id.sh) --inviter-name "Integration Tester" --invitee-email "test@test.com" --client-id $(./test/integration/scripts/get-org-inv-app-id.sh) --json --no-input exit-code: 0 stdout: json: @@ -213,7 +213,7 @@ tests: invitee.email: "test@test.com" 028 - create organization invitation and check table output: - command: auth0 orgs invs create --org-id $(./test/integration/scripts/get-org-id.sh) --inviter-name "Integration Tester" --invitee-email "test@test.com" --client-id $(./test/integration/scripts/get-app-id.sh) --ttl-sec 86400 --no-input + command: auth0 orgs invs create --org-id $(./test/integration/scripts/get-org-id.sh) --inviter-name "Integration Tester" --invitee-email "test@test.com" --client-id $(./test/integration/scripts/get-org-inv-app-id.sh) --ttl-sec 86400 --no-input exit-code: 0 stdout: contains: diff --git a/test/integration/scripts/delete-app-id.sh b/test/integration/scripts/delete-app-id.sh deleted file mode 100755 index 38520f478..000000000 --- a/test/integration/scripts/delete-app-id.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash - -// This script is used to delete the app identifier file created during integration tests. It checks if the file exists and removes it if it does. -// This script should be called only if the app is successfully deleted in the test cases, to ensure that the identifier file is cleaned up for subsequent test runs. - -FILE=./test/integration/identifiers/app-id - -# Remove the app identifier file only if it exists -if [ -f "$FILE" ]; then - rm "$FILE" - exit $? -fi - -# File doesn't exist, nothing to do -exit 0 diff --git a/test/integration/scripts/get-app-id.sh b/test/integration/scripts/get-app-id.sh index e9f15487b..93a5e5d45 100755 --- a/test/integration/scripts/get-app-id.sh +++ b/test/integration/scripts/get-app-id.sh @@ -8,11 +8,6 @@ fi app=$( auth0 apps create -n integration-test-app-newapp -t native --description NewApp --json --no-input ) -client_id=$( echo "$app" | jq -r '.["client_id"]' ) - -# Enable organization support to allow the app to be used for org invitations -auth0 api patch "clients/${client_id}" --data '{"organization_usage":"allow","initiate_login_uri":"https://example.com/login"}' > /dev/null - mkdir -p ./test/integration/identifiers -echo "$client_id" > $FILE -cat $FILE +echo "$app" | jq -r '.["client_id"]' > $FILE +cat $FILE \ No newline at end of file diff --git a/test/integration/scripts/get-org-inv-app-id.sh b/test/integration/scripts/get-org-inv-app-id.sh new file mode 100755 index 000000000..df589e6cd --- /dev/null +++ b/test/integration/scripts/get-org-inv-app-id.sh @@ -0,0 +1,18 @@ +#! /bin/bash + +FILE=./test/integration/identifiers/org-inv-app-id +if [ -f "$FILE" ]; then + cat $FILE + exit 0 +fi + +app=$( auth0 apps create -n integration-test-org-inv-app -t native --description NewApp --json --no-input ) + +client_id=$( echo "$app" | jq -r '.["client_id"]' ) + +# Enable organization support to allow the app to be used for org invitations +auth0 api patch "clients/${client_id}" --data '{"organization_usage":"allow","initiate_login_uri":"https://example.com/login"}' > /dev/null + +mkdir -p ./test/integration/identifiers +echo "$client_id" > $FILE +cat $FILE From 70affeb05decd939312bd4c6431ee16ac86ada7d Mon Sep 17 00:00:00 2001 From: Kiran Kumar Date: Tue, 10 Feb 2026 22:10:34 +0530 Subject: [PATCH 33/34] refactor(flags, input): simplify flag multi-selection methods - Removed the isUpdate parameter from PickMany and pickManyFlag methods to streamline the flag selection process. - Updated askMultiSelect to directly use prompt.AskMultiSelect --- internal/cli/arguments.go | 2 +- internal/cli/flags.go | 12 ++++-------- internal/cli/input.go | 18 ++---------------- 3 files changed, 7 insertions(+), 25 deletions(-) diff --git a/internal/cli/arguments.go b/internal/cli/arguments.go index 3898070a0..4c311b0a1 100644 --- a/internal/cli/arguments.go +++ b/internal/cli/arguments.go @@ -79,7 +79,7 @@ func (a *Argument) PickMany(cmd *cobra.Command, result *[]string, fn pickerOptio } var values []string - if err := askMultiSelect(a, &values, false, opts.labels()...); err != nil { + if err := askMultiSelect(a, &values, opts.labels()...); err != nil { return err } diff --git a/internal/cli/flags.go b/internal/cli/flags.go index e6699641b..c3eab463b 100644 --- a/internal/cli/flags.go +++ b/internal/cli/flags.go @@ -98,11 +98,7 @@ func (f *Flag) PickU(cmd *cobra.Command, result *string, fn pickerOptionsFunc) e } func (f *Flag) PickMany(cmd *cobra.Command, result *[]string, fn pickerOptionsFunc) error { - return pickManyFlag(cmd, f, result, fn, false) -} - -func (f *Flag) PickManyU(cmd *cobra.Command, result *[]string, fn pickerOptionsFunc) error { - return pickManyFlag(cmd, f, result, fn, true) + return pickManyFlag(cmd, f, result, fn) } func (f *Flag) OpenEditor(cmd *cobra.Command, value *string, defaultValue, filename string, infoFn func()) error { @@ -248,8 +244,8 @@ func pickFlag(cmd *cobra.Command, f *Flag, result *string, fn pickerOptionsFunc, return nil } -func pickManyFlag(cmd *cobra.Command, f *Flag, result *[]string, fn pickerOptionsFunc, isUpdate bool) error { - if shouldAsk(cmd, f, isUpdate) { +func pickManyFlag(cmd *cobra.Command, f *Flag, result *[]string, fn pickerOptionsFunc) error { + if shouldAsk(cmd, f, false) { var opts pickerOptions err := ansi.Waiting(func() error { var err error @@ -262,7 +258,7 @@ func pickManyFlag(cmd *cobra.Command, f *Flag, result *[]string, fn pickerOption } var values []string - if err := askMultiSelect(f, &values, isUpdate, opts.labels()...); err != nil { + if err := askMultiSelect(f, &values, opts.labels()...); err != nil { return err } diff --git a/internal/cli/input.go b/internal/cli/input.go index 7626b0c4d..bd7811486 100644 --- a/internal/cli/input.go +++ b/internal/cli/input.go @@ -60,26 +60,12 @@ func askPassword(i commandInput, value interface{}, isUpdate bool) error { return nil } -func askMultiSelect(i commandInput, value interface{}, isUpdate bool, options ...string) error { +func askMultiSelect(i commandInput, value interface{}, options ...string) error { v := reflect.ValueOf(options) if v.Kind() != reflect.Slice || v.Len() <= 0 { return handleInputError(fmt.Errorf("there is not enough data to select from")) } - - isRequired := isInputRequired(i, isUpdate) - - input := &survey.Question{ - Prompt: &survey.MultiSelect{ - Message: i.GetLabel(), - Options: options, - }, - } - - if isRequired { - input.Validate = survey.Required - } - - if err := prompt.AskOne(input, value); err != nil { + if err := prompt.AskMultiSelect(i.GetLabel(), value, options...); err != nil { return handleInputError(err) } return nil From 4664cc56eb58d14663706e832341532415ae7f79 Mon Sep 17 00:00:00 2001 From: Kiran Kumar Date: Tue, 10 Feb 2026 22:17:02 +0530 Subject: [PATCH 34/34] refactor(scripts): update app_id retrieval in get-org-invitation-id.sh - Changed the app_id retrieval in get-org-invitation-id.sh to use get-org-inv-app-id.sh instead of get-app-id.sh. - This change ensures that the correct application ID is used for organization invitations, improving the accuracy of the integration tests. --- test/integration/scripts/get-app-id.sh | 2 +- test/integration/scripts/get-org-invitation-id.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/integration/scripts/get-app-id.sh b/test/integration/scripts/get-app-id.sh index 93a5e5d45..425f25d0a 100755 --- a/test/integration/scripts/get-app-id.sh +++ b/test/integration/scripts/get-app-id.sh @@ -10,4 +10,4 @@ app=$( auth0 apps create -n integration-test-app-newapp -t native --description mkdir -p ./test/integration/identifiers echo "$app" | jq -r '.["client_id"]' > $FILE -cat $FILE \ No newline at end of file +cat $FILE diff --git a/test/integration/scripts/get-org-invitation-id.sh b/test/integration/scripts/get-org-invitation-id.sh index b06f6fa18..e2631c342 100755 --- a/test/integration/scripts/get-org-invitation-id.sh +++ b/test/integration/scripts/get-org-invitation-id.sh @@ -7,7 +7,7 @@ if [ -f "$FILE" ]; then fi org_id=$(./test/integration/scripts/get-org-id.sh) -app_id=$(./test/integration/scripts/get-app-id.sh) +app_id=$(./test/integration/scripts/get-org-inv-app-id.sh) invitation=$( auth0 orgs invitations create --org-id "$org_id" \ --inviter-name "Integration Tester" \