From 29a36b2f584a002b7427af4803e27d6d5b6ecd46 Mon Sep 17 00:00:00 2001 From: GuyP <105154237+guyp-descope@users.noreply.github.com> Date: Mon, 18 Nov 2024 13:12:07 +0200 Subject: [PATCH] Adjust and add test user functionality (#473) * Adjust and add test user functionality * fix pr issues * update README file * fixed usage for test user search --- README.md | 9 ++++ descope/api/client.go | 12 +++++ descope/internal/mgmt/user.go | 32 +++++++++++- descope/internal/mgmt/user_test.go | 61 ++++++++++++++++++++++ descope/sdk/mgmt.go | 8 +++ descope/tests/mocks/mgmt/managementmock.go | 12 +++++ 6 files changed, 132 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d3c6a350..58f2aca7 100644 --- a/README.md +++ b/README.md @@ -1614,6 +1614,15 @@ user, err := descopeClient.Management.User().CreateTestUser(context.Background() {TenantID: "tenant-ID2"}, }) +// Search all test users, optionally according to tenant and/or role filter +// Results can be paginated using the limit and page parameters +usersResp, total, err := descopeClient.Management.User().SearchAllTestUsers(context.Background(), &descope.UserSearchOptions{TenantIDs: []string{"my-tenant-id"}}) +if err == nil { + for _, user := range usersResp { + // Do something + } +} + // Now test user got created, and this user will be available until you delete it, // you can use any management operation for test user CRUD. // You can also delete all test users. diff --git a/descope/api/client.go b/descope/api/client.go index 76eb56ae..d04876ea 100644 --- a/descope/api/client.go +++ b/descope/api/client.go @@ -101,6 +101,7 @@ var ( ssoApplicationLoad: "mgmt/sso/idp/app/load", ssoApplicationLoadAll: "mgmt/sso/idp/apps/load", userCreate: "mgmt/user/create", + testUserCreate: "mgmt/user/create/test", userCreateBatch: "mgmt/user/create/batch", userUpdate: "mgmt/user/update", userPatch: "mgmt/user/patch", @@ -109,6 +110,7 @@ var ( userImport: "mgmt/user/import", userLoad: "mgmt/user", userSearchAll: "mgmt/user/search", + testUserSearchAll: "mgmt/user/search/test", userUpdateStatus: "mgmt/user/update/status", userUpdateLoginID: "mgmt/user/update/loginid", userUpdateEmail: "mgmt/user/update/email", @@ -300,6 +302,7 @@ type mgmtEndpoints struct { ssoApplicationLoadAll string userCreate string + testUserCreate string userCreateBatch string userUpdate string userPatch string @@ -308,6 +311,7 @@ type mgmtEndpoints struct { userImport string userLoad string userSearchAll string + testUserSearchAll string userUpdateStatus string userUpdateLoginID string userUpdateEmail string @@ -709,6 +713,10 @@ func (e *endpoints) ManagementUserCreate() string { return path.Join(e.version, e.mgmt.userCreate) } +func (e *endpoints) ManagementTestUserCreate() string { + return path.Join(e.version, e.mgmt.testUserCreate) +} + func (e *endpoints) ManagementUserCreateBatch() string { return path.Join(e.version, e.mgmt.userCreateBatch) } @@ -741,6 +749,10 @@ func (e *endpoints) ManagementUserSearchAll() string { return path.Join(e.versionV2, e.mgmt.userSearchAll) } +func (e *endpoints) ManagementTestUserSearchAll() string { + return path.Join(e.versionV2, e.mgmt.testUserSearchAll) +} + func (e *endpoints) ManagementUserUpdateStatus() string { return path.Join(e.version, e.mgmt.userUpdateStatus) } diff --git a/descope/internal/mgmt/user.go b/descope/internal/mgmt/user.go index 5eb72801..5d4809ff 100644 --- a/descope/internal/mgmt/user.go +++ b/descope/internal/mgmt/user.go @@ -97,7 +97,14 @@ func (u *user) create(ctx context.Context, loginID, email, phone, displayName, g ssoAppIDs: ssoAppIDs, }) - res, err := u.client.DoPostRequest(ctx, api.Routes.ManagementUserCreate(), req, nil, u.conf.ManagementKey) + var res *api.HTTPResponse + var err error + if test { + res, err = u.client.DoPostRequest(ctx, api.Routes.ManagementTestUserCreate(), req, nil, u.conf.ManagementKey) + } else { + res, err = u.client.DoPostRequest(ctx, api.Routes.ManagementUserCreate(), req, nil, u.conf.ManagementKey) + } + if err != nil { return nil, err } @@ -234,6 +241,20 @@ func (u *user) load(ctx context.Context, loginID, userID string) (*descope.UserR } func (u *user) SearchAll(ctx context.Context, options *descope.UserSearchOptions) ([]*descope.UserResponse, int, error) { + return u.searchAll(ctx, options, false) +} + +func (u *user) SearchAllTestUsers(ctx context.Context, options *descope.UserSearchOptions) ([]*descope.UserResponse, int, error) { + // Init empty options if non given + if options == nil { + options = &descope.UserSearchOptions{} + } + options.WithTestUsers = true + options.TestUsersOnly = true + return u.searchAll(ctx, options, true) +} + +func (u *user) searchAll(ctx context.Context, options *descope.UserSearchOptions, useTestEndpoint bool) ([]*descope.UserResponse, int, error) { // Init empty options if non given if options == nil { options = &descope.UserSearchOptions{} @@ -250,7 +271,14 @@ func (u *user) SearchAll(ctx context.Context, options *descope.UserSearchOptions } req := makeSearchAllRequest(options) - res, err := u.client.DoPostRequest(ctx, api.Routes.ManagementUserSearchAll(), req, nil, u.conf.ManagementKey) + + var res *api.HTTPResponse + var err error + if useTestEndpoint { + res, err = u.client.DoPostRequest(ctx, api.Routes.ManagementTestUserSearchAll(), req, nil, u.conf.ManagementKey) + } else { + res, err = u.client.DoPostRequest(ctx, api.Routes.ManagementUserSearchAll(), req, nil, u.conf.ManagementKey) + } if err != nil { return nil, 0, err } diff --git a/descope/internal/mgmt/user_test.go b/descope/internal/mgmt/user_test.go index a3cf5ab9..e70cbb7e 100644 --- a/descope/internal/mgmt/user_test.go +++ b/descope/internal/mgmt/user_test.go @@ -22,6 +22,8 @@ func TestUserCreateSuccess(t *testing.T) { i := 0 m := newTestMgmt(nil, helpers.DoOkWithBody(func(r *http.Request) { require.Equal(t, r.Header.Get("Authorization"), "Bearer a:key") + require.NotNil(t, r.URL) + require.Equal(t, "/v1/mgmt/user/create", r.URL.Path) req := map[string]any{} require.NoError(t, helpers.ReadBody(r, &req)) require.Equal(t, "abc", req["loginId"]) @@ -245,6 +247,8 @@ func TestUserCreateTestUserSuccess(t *testing.T) { }} m := newTestMgmt(nil, helpers.DoOkWithBody(func(r *http.Request) { require.Equal(t, r.Header.Get("Authorization"), "Bearer a:key") + require.NotNil(t, r.URL) + require.Equal(t, "/v1/mgmt/user/create/test", r.URL.Path) req := map[string]any{} require.NoError(t, helpers.ReadBody(r, &req)) require.Equal(t, "abc", req["loginId"]) @@ -730,6 +734,8 @@ func TestSearchAllUsersSuccess(t *testing.T) { roleNames := []string{"role1"} m := newTestMgmt(nil, helpers.DoOkWithBody(func(r *http.Request) { require.Equal(t, r.Header.Get("Authorization"), "Bearer a:key") + require.NotNil(t, r.URL) + require.Equal(t, "/v2/mgmt/user/search", r.URL.Path) req := map[string]any{} require.NoError(t, helpers.ReadBody(r, &req)) require.EqualValues(t, tenantIDs[0], req["tenantIds"].([]any)[0]) @@ -764,6 +770,61 @@ func TestSearchAllUsersSuccess(t *testing.T) { require.Equal(t, 85, total) } +func TestSearchAllTestUsersSuccess(t *testing.T) { + response := map[string]any{ + "users": []map[string]any{{ + "email": "a@b.c", + "test": true, + }}, + "total": 85, + } + + m := newTestMgmt(nil, helpers.DoOkWithBody(func(r *http.Request) { + require.Equal(t, r.Header.Get("Authorization"), "Bearer a:key") + require.NotNil(t, r.URL) + require.Equal(t, "/v2/mgmt/user/search/test", r.URL.Path) + req := map[string]any{} + require.NoError(t, helpers.ReadBody(r, &req)) + require.EqualValues(t, true, req["testUsersOnly"].(bool)) + require.EqualValues(t, true, req["withTestUser"].(bool)) + require.EqualValues(t, []any{"a@b.com"}, req["emails"]) + }, response)) + res, _, err := m.User().SearchAllTestUsers(context.Background(), &descope.UserSearchOptions{ + Emails: []string{"a@b.com"}, + }) + require.NoError(t, err) + require.NotNil(t, res) + require.Len(t, res, 1) + require.Equal(t, "a@b.c", res[0].Email) + require.Equal(t, true, res[0].Test) +} + +func TestSearchAllTestUsersSuccessEmptyOptions(t *testing.T) { + response := map[string]any{ + "users": []map[string]any{{ + "email": "a@b.c", + "test": true, + }}, + "total": 85, + } + + m := newTestMgmt(nil, helpers.DoOkWithBody(func(r *http.Request) { + require.Equal(t, r.Header.Get("Authorization"), "Bearer a:key") + require.NotNil(t, r.URL) + require.Equal(t, "/v2/mgmt/user/search/test", r.URL.Path) + req := map[string]any{} + require.NoError(t, helpers.ReadBody(r, &req)) + require.EqualValues(t, true, req["testUsersOnly"].(bool)) + require.EqualValues(t, true, req["withTestUser"].(bool)) + }, response)) + res, _, err := m.User().SearchAllTestUsers(context.Background(), nil) + require.NoError(t, err) + require.NotNil(t, res) + require.Len(t, res, 1) + require.Equal(t, "a@b.c", res[0].Email) + require.Equal(t, true, res[0].Test) +} + func TestSearchAllUsersError(t *testing.T) { m := newTestMgmt(nil, helpers.DoBadRequest(nil)) res, total, err := m.User().SearchAll(context.Background(), nil) diff --git a/descope/sdk/mgmt.go b/descope/sdk/mgmt.go index fd2ca362..1b5aef58 100644 --- a/descope/sdk/mgmt.go +++ b/descope/sdk/mgmt.go @@ -241,6 +241,14 @@ type User interface { // Returns slice of users and total number of users for the query SearchAll(ctx context.Context, options *descope.UserSearchOptions) ([]*descope.UserResponse, int, error) + // Search all test users according to given filters + // + // The options optional parameter allows to fine-tune the search filters + // and results. Using nil will result in a filter-less query with a set amount of + // results. + // Returns slice of users and total number of users for the query + SearchAllTestUsers(ctx context.Context, options *descope.UserSearchOptions) ([]*descope.UserResponse, int, error) + // Activate an existing user. Activate(ctx context.Context, loginID string) (*descope.UserResponse, error) diff --git a/descope/tests/mocks/mgmt/managementmock.go b/descope/tests/mocks/mgmt/managementmock.go index d4495504..0874360f 100644 --- a/descope/tests/mocks/mgmt/managementmock.go +++ b/descope/tests/mocks/mgmt/managementmock.go @@ -283,6 +283,11 @@ type MockUser struct { SearchAllTotalResponse int SearchAllError error + SearchAllTestUsersAssert func(options *descope.UserSearchOptions) + SearchAllTestUsersResponse []*descope.UserResponse + SearchAllTestUsersTotalResponse int + SearchAllTestUsersError error + ActivateAssert func(loginID string) ActivateResponse *descope.UserResponse ActivateError error @@ -517,6 +522,13 @@ func (m *MockUser) SearchAll(_ context.Context, options *descope.UserSearchOptio return m.SearchAllResponse, m.SearchAllTotalResponse, m.SearchAllError } +func (m *MockUser) SearchAllTestUsers(_ context.Context, options *descope.UserSearchOptions) ([]*descope.UserResponse, int, error) { + if m.SearchAllTestUsersAssert != nil { + m.SearchAllTestUsersAssert(options) + } + return m.SearchAllTestUsersResponse, m.SearchAllTestUsersTotalResponse, m.SearchAllTestUsersError +} + func (m *MockUser) Activate(_ context.Context, loginID string) (*descope.UserResponse, error) { if m.ActivateAssert != nil { m.ActivateAssert(loginID)