diff --git a/app/app_client.go b/app/app_client.go index f7b1d98dc58..c676920b648 100644 --- a/app/app_client.go +++ b/app/app_client.go @@ -857,6 +857,17 @@ func (c *AppClient) OrganizationGetLogo(ctx context.Context, orgID string) (stri return resp.Url, nil } +// ListOAuthApps gets the client's list of OAuth applications. +func (c *AppClient) ListOAuthApps(ctx context.Context, orgID string) (*pb.ListOAuthAppsResponse, error) { + resp, err := c.client.ListOAuthApps(ctx, &pb.ListOAuthAppsRequest{ + OrgId: orgID, + }) + if err != nil { + return nil, err + } + return resp, nil +} + // CreateLocation creates a location with the given name under the given organization. func (c *AppClient) CreateLocation(ctx context.Context, orgID, name string, opts *CreateLocationOptions) (*Location, error) { var parentID *string diff --git a/app/app_client_test.go b/app/app_client_test.go index 250ebf3ffd0..b4a8cdede8f 100644 --- a/app/app_client_test.go +++ b/app/app_client_test.go @@ -740,6 +740,21 @@ func TestAppClient(t *testing.T) { test.That(t, resp, test.ShouldEqual, "https://logo.com") }) + t.Run("ListOAuthApps", func(t *testing.T) { + grpcClient.ListOAuthAppsFunc = func( + ctx context.Context, in *pb.ListOAuthAppsRequest, opts ...grpc.CallOption, + ) (*pb.ListOAuthAppsResponse, error) { + test.That(t, in.OrgId, test.ShouldEqual, organizationID) + return &pb.ListOAuthAppsResponse{ + ClientIds: []string{"clientId"}, + }, nil + } + + resp, err := client.ListOAuthApps(context.Background(), organizationID) + test.That(t, err, test.ShouldBeNil) + test.That(t, resp.ClientIds, test.ShouldResemble, []string{"clientId"}) + }) + t.Run("GetSupportEmail", func(t *testing.T) { grpcClient.OrganizationGetSupportEmailFunc = func( ctx context.Context, in *pb.OrganizationGetSupportEmailRequest, opts ...grpc.CallOption, diff --git a/cli/app.go b/cli/app.go index ba15a22ca00..dd66c653876 100644 --- a/cli/app.go +++ b/cli/app.go @@ -465,6 +465,11 @@ var app = &cli.App{ }, }, }, + { + Name: "list-oauth-apps", + Usage: "List OAuth applications for an organization", + Action: createCommandWithT[listOAuthAppsArgs](ListOAuthAppsAction), + }, { Name: "support-email", Usage: "manage the support email for an organization", diff --git a/cli/client.go b/cli/client.go index 6ffc908bddc..f0960a0bdc7 100644 --- a/cli/client.go +++ b/cli/client.go @@ -425,6 +425,39 @@ func (c *viamClient) organizationsLogoGetAction(cCtx *cli.Context, orgID string) return nil } +type listOAuthAppsArgs struct { + OrgID string +} + +func ListOAuthAppsAction(cCtx *cli.Context, args listOAuthAppsArgs) error { + c, err := newViamClient(cCtx) + if err != nil { + return err + } + + if err := c.ensureLoggedIn(); err != nil { + return err + } + + resp, err := c.client.ListOAuthApps(cCtx.Context, &apppb.ListOAuthAppsRequest{ + OrgId: args.OrgID, + }) + if err != nil { + return err + } + + if len(resp.ClientIds) == 0 { + printf(cCtx.App.Writer, "No OAuth apps found for organization %q\n", args.OrgID) + return nil + } + + printf(cCtx.App.Writer, "OAuth apps for organization %q:\n", args.OrgID) + for _, id := range resp.ClientIds { + printf(cCtx.App.Writer, " - %s\n", id) + } + return nil +} + // ListLocationsAction is the corresponding Action for 'locations list'. func ListLocationsAction(c *cli.Context, args emptyArgs) error { client, err := newViamClient(c) diff --git a/testutils/inject/app_service_client.go b/testutils/inject/app_service_client.go index 45f35604723..bc617a3047e 100644 --- a/testutils/inject/app_service_client.go +++ b/testutils/inject/app_service_client.go @@ -56,6 +56,8 @@ type AppServiceClient struct { opts ...grpc.CallOption) (*apppb.OrganizationSetLogoResponse, error) OrganizationGetLogoFunc func(ctx context.Context, in *apppb.OrganizationGetLogoRequest, opts ...grpc.CallOption) (*apppb.OrganizationGetLogoResponse, error) + ListOAuthAppsFunc func(ctx context.Context, in *apppb.ListOAuthAppsRequest, + opts ...grpc.CallOption) (*apppb.ListOAuthAppsResponse, error) CreateLocationFunc func(ctx context.Context, in *apppb.CreateLocationRequest, opts ...grpc.CallOption) (*apppb.CreateLocationResponse, error) GetLocationFunc func(ctx context.Context, in *apppb.GetLocationRequest, @@ -403,6 +405,16 @@ func (asc *AppServiceClient) OrganizationGetLogo( return asc.OrganizationGetLogoFunc(ctx, in, opts...) } +// ListOAuthApps calls the injected ListOAuthAppsFunc or the real version. +func (asc *AppServiceClient) ListOAuthApps( + ctx context.Context, in *apppb.ListOAuthAppsRequest, opts ...grpc.CallOption, +) (*apppb.ListOAuthAppsResponse, error) { + if asc.ListOAuthAppsFunc == nil { + return asc.AppServiceClient.ListOAuthApps(ctx, in, opts...) + } + return asc.ListOAuthAppsFunc(ctx, in, opts...) +} + // CreateLocation calls the injected CreateLocationFunc or the real version. func (asc *AppServiceClient) CreateLocation( ctx context.Context, in *apppb.CreateLocationRequest, opts ...grpc.CallOption,