Skip to content

Commit

Permalink
[PM-12777] Fixed Issue #4034, API endpoint now handles optional param…
Browse files Browse the repository at this point in the history
…eters (#4812)

* resolves issue #4043 default values for itemsPerPage and startIndex

* UsersController#Get now uses a queryParamModel
Co-authored-by: Ahmad Mustafa Jebran <[email protected]>
Co-authored-by: Luris Solis <[email protected]>

* Test now passes, default 50 is represented

---------

Co-authored-by: Jared McCannon <[email protected]>
  • Loading branch information
BensonB12 and jrmccannon authored Oct 17, 2024
1 parent 7a509d2 commit da04218
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 16 deletions.
10 changes: 4 additions & 6 deletions bitwarden_license/src/Scim/Controllers/v2/UsersController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,17 +57,15 @@ public async Task<IActionResult> Get(Guid organizationId, Guid id)
[HttpGet("")]
public async Task<IActionResult> Get(
Guid organizationId,
[FromQuery] string filter,
[FromQuery] int? count,
[FromQuery] int? startIndex)
[FromQuery] GetUsersQueryParamModel model)
{
var usersListQueryResult = await _getUsersListQuery.GetUsersListAsync(organizationId, filter, count, startIndex);
var usersListQueryResult = await _getUsersListQuery.GetUsersListAsync(organizationId, model);
var scimListResponseModel = new ScimListResponseModel<ScimUserResponseModel>
{
Resources = usersListQueryResult.userList.Select(u => new ScimUserResponseModel(u)).ToList(),
ItemsPerPage = count.GetValueOrDefault(usersListQueryResult.userList.Count()),
ItemsPerPage = model.Count,
TotalResults = usersListQueryResult.totalResults,
StartIndex = startIndex.GetValueOrDefault(1),
StartIndex = model.StartIndex,
};
return Ok(scimListResponseModel);
}
Expand Down
12 changes: 12 additions & 0 deletions bitwarden_license/src/Scim/Models/GetUserQueryParamModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.ComponentModel.DataAnnotations;

public class GetUsersQueryParamModel
{
public string Filter { get; init; } = string.Empty;

[Range(1, int.MaxValue)]
public int Count { get; init; } = 50;

[Range(1, int.MaxValue)]
public int StartIndex { get; init; } = 1;
}
13 changes: 9 additions & 4 deletions bitwarden_license/src/Scim/Users/GetUsersListQuery.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,16 @@ public GetUsersListQuery(IOrganizationUserRepository organizationUserRepository)
_organizationUserRepository = organizationUserRepository;
}

public async Task<(IEnumerable<OrganizationUserUserDetails> userList, int totalResults)> GetUsersListAsync(Guid organizationId, string filter, int? count, int? startIndex)
public async Task<(IEnumerable<OrganizationUserUserDetails> userList, int totalResults)> GetUsersListAsync(Guid organizationId, GetUsersQueryParamModel userQueryParams)
{
string emailFilter = null;
string usernameFilter = null;
string externalIdFilter = null;

int count = userQueryParams.Count;
int startIndex = userQueryParams.StartIndex;
string filter = userQueryParams.Filter;

if (!string.IsNullOrWhiteSpace(filter))
{
var filterLower = filter.ToLowerInvariant();
Expand Down Expand Up @@ -56,11 +61,11 @@ public GetUsersListQuery(IOrganizationUserRepository organizationUserRepository)
}
totalResults = userList.Count;
}
else if (string.IsNullOrWhiteSpace(filter) && startIndex.HasValue && count.HasValue)
else if (string.IsNullOrWhiteSpace(filter))
{
userList = orgUsers.OrderBy(ou => ou.Email)
.Skip(startIndex.Value - 1)
.Take(count.Value)
.Skip(startIndex - 1)
.Take(count)
.ToList();
totalResults = orgUsers.Count;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ namespace Bit.Scim.Users.Interfaces;

public interface IGetUsersListQuery
{
Task<(IEnumerable<OrganizationUserUserDetails> userList, int totalResults)> GetUsersListAsync(Guid organizationId, string filter, int? count, int? startIndex);
Task<(IEnumerable<OrganizationUserUserDetails> userList, int totalResults)> GetUsersListAsync(Guid organizationId, GetUsersQueryParamModel userQueryParams);
}
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,46 @@ public async Task GetList_EmptyResult_Success()
AssertHelper.AssertPropertyEqual(expectedResponse, responseModel);
}

[Fact]
public async Task GetList_SearchUserNameWithoutOptionalParameters_Success()
{
string filter = "userName eq [email protected]";
int? itemsPerPage = null;
int? startIndex = null;
var expectedResponse = new ScimListResponseModel<ScimUserResponseModel>
{
ItemsPerPage = 50, //default value
TotalResults = 1,
StartIndex = 1, //default value
Resources = new List<ScimUserResponseModel>
{
new ScimUserResponseModel
{
Id = ScimApplicationFactory.TestOrganizationUserId2,
DisplayName = "Test User 2",
ExternalId = "UB",
Active = true,
Emails = new List<BaseScimUserModel.EmailModel>
{
new BaseScimUserModel.EmailModel { Primary = true, Type = "work", Value = "[email protected]" }
},
Groups = new List<string>(),
Name = new BaseScimUserModel.NameModel("Test User 2"),
UserName = "[email protected]",
Schemas = new List<string> { ScimConstants.Scim2SchemaUser }
}
},
Schemas = new List<string> { ScimConstants.Scim2SchemaListResponse }
};

var context = await _factory.UsersGetListAsync(ScimApplicationFactory.TestOrganizationId1, filter, itemsPerPage, startIndex);

Assert.Equal(StatusCodes.Status200OK, context.Response.StatusCode);

var responseModel = JsonSerializer.Deserialize<ScimListResponseModel<ScimUserResponseModel>>(context.Response.Body, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
AssertHelper.AssertPropertyEqual(expectedResponse, responseModel);
}

[Fact]
public async Task Post_Success()
{
Expand Down
10 changes: 5 additions & 5 deletions bitwarden_license/test/Scim.Test/Users/GetUsersListQueryTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public async Task GetUsersList_Success(int count, int startIndex, SutProvider<Ge
.GetManyDetailsByOrganizationAsync(organizationId)
.Returns(organizationUserUserDetails);

var result = await sutProvider.Sut.GetUsersListAsync(organizationId, null, count, startIndex);
var result = await sutProvider.Sut.GetUsersListAsync(organizationId, new GetUsersQueryParamModel { Count = count, StartIndex = startIndex });

await sutProvider.GetDependency<IOrganizationUserRepository>().Received(1).GetManyDetailsByOrganizationAsync(organizationId);

Expand All @@ -49,7 +49,7 @@ public async Task GetUsersList_FilterUserName_Success(string email, SutProvider<
.GetManyDetailsByOrganizationAsync(organizationId)
.Returns(organizationUserUserDetails);

var result = await sutProvider.Sut.GetUsersListAsync(organizationId, filter, null, null);
var result = await sutProvider.Sut.GetUsersListAsync(organizationId, new GetUsersQueryParamModel { Filter = filter });

await sutProvider.GetDependency<IOrganizationUserRepository>().Received(1).GetManyDetailsByOrganizationAsync(organizationId);

Expand All @@ -71,7 +71,7 @@ public async Task GetUsersList_FilterUserName_Empty(string email, SutProvider<Ge
.GetManyDetailsByOrganizationAsync(organizationId)
.Returns(organizationUserUserDetails);

var result = await sutProvider.Sut.GetUsersListAsync(organizationId, filter, null, null);
var result = await sutProvider.Sut.GetUsersListAsync(organizationId, new GetUsersQueryParamModel { Filter = filter });

await sutProvider.GetDependency<IOrganizationUserRepository>().Received(1).GetManyDetailsByOrganizationAsync(organizationId);

Expand All @@ -96,7 +96,7 @@ public async Task GetUsersList_FilterExternalId_Success(SutProvider<GetUsersList
.GetManyDetailsByOrganizationAsync(organizationId)
.Returns(organizationUserUserDetails);

var result = await sutProvider.Sut.GetUsersListAsync(organizationId, filter, null, null);
var result = await sutProvider.Sut.GetUsersListAsync(organizationId, new GetUsersQueryParamModel { Filter = filter });

await sutProvider.GetDependency<IOrganizationUserRepository>().Received(1).GetManyDetailsByOrganizationAsync(organizationId);

Expand All @@ -120,7 +120,7 @@ public async Task GetUsersList_FilterExternalId_Empty(string externalId, SutProv
.GetManyDetailsByOrganizationAsync(organizationId)
.Returns(organizationUserUserDetails);

var result = await sutProvider.Sut.GetUsersListAsync(organizationId, filter, null, null);
var result = await sutProvider.Sut.GetUsersListAsync(organizationId, new GetUsersQueryParamModel { Filter = filter });

await sutProvider.GetDependency<IOrganizationUserRepository>().Received(1).GetManyDetailsByOrganizationAsync(organizationId);

Expand Down

0 comments on commit da04218

Please sign in to comment.