mirror of
https://github.com/bitwarden/server
synced 2025-12-06 00:03:34 +00:00
[PM-28871] Default startIndex and count values on SCIM groups list API (#6648)
* default startindex and count values on SCIM groups list api * convert params to a model, like users * review feedback * fix file name to be plural * added integration test
This commit is contained in:
@@ -61,17 +61,15 @@ public class GroupsController : Controller
|
|||||||
[HttpGet("")]
|
[HttpGet("")]
|
||||||
public async Task<IActionResult> Get(
|
public async Task<IActionResult> Get(
|
||||||
Guid organizationId,
|
Guid organizationId,
|
||||||
[FromQuery] string filter,
|
[FromQuery] GetGroupsQueryParamModel model)
|
||||||
[FromQuery] int? count,
|
|
||||||
[FromQuery] int? startIndex)
|
|
||||||
{
|
{
|
||||||
var groupsListQueryResult = await _getGroupsListQuery.GetGroupsListAsync(organizationId, filter, count, startIndex);
|
var groupsListQueryResult = await _getGroupsListQuery.GetGroupsListAsync(organizationId, model);
|
||||||
var scimListResponseModel = new ScimListResponseModel<ScimGroupResponseModel>
|
var scimListResponseModel = new ScimListResponseModel<ScimGroupResponseModel>
|
||||||
{
|
{
|
||||||
Resources = groupsListQueryResult.groupList.Select(g => new ScimGroupResponseModel(g)).ToList(),
|
Resources = groupsListQueryResult.groupList.Select(g => new ScimGroupResponseModel(g)).ToList(),
|
||||||
ItemsPerPage = count.GetValueOrDefault(groupsListQueryResult.groupList.Count()),
|
ItemsPerPage = model.Count,
|
||||||
TotalResults = groupsListQueryResult.totalResults,
|
TotalResults = groupsListQueryResult.totalResults,
|
||||||
StartIndex = startIndex.GetValueOrDefault(1),
|
StartIndex = model.StartIndex,
|
||||||
};
|
};
|
||||||
return Ok(scimListResponseModel);
|
return Ok(scimListResponseModel);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
using Bit.Core.AdminConsole.Entities;
|
using Bit.Core.AdminConsole.Entities;
|
||||||
using Bit.Core.AdminConsole.Repositories;
|
using Bit.Core.AdminConsole.Repositories;
|
||||||
using Bit.Scim.Groups.Interfaces;
|
using Bit.Scim.Groups.Interfaces;
|
||||||
|
using Bit.Scim.Models;
|
||||||
|
|
||||||
namespace Bit.Scim.Groups;
|
namespace Bit.Scim.Groups;
|
||||||
|
|
||||||
@@ -16,10 +17,16 @@ public class GetGroupsListQuery : IGetGroupsListQuery
|
|||||||
_groupRepository = groupRepository;
|
_groupRepository = groupRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<(IEnumerable<Group> groupList, int totalResults)> GetGroupsListAsync(Guid organizationId, string filter, int? count, int? startIndex)
|
public async Task<(IEnumerable<Group> groupList, int totalResults)> GetGroupsListAsync(
|
||||||
|
Guid organizationId, GetGroupsQueryParamModel groupQueryParams)
|
||||||
{
|
{
|
||||||
string nameFilter = null;
|
string nameFilter = null;
|
||||||
string externalIdFilter = null;
|
string externalIdFilter = null;
|
||||||
|
|
||||||
|
int count = groupQueryParams.Count;
|
||||||
|
int startIndex = groupQueryParams.StartIndex;
|
||||||
|
string filter = groupQueryParams.Filter;
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(filter))
|
if (!string.IsNullOrWhiteSpace(filter))
|
||||||
{
|
{
|
||||||
if (filter.StartsWith("displayName eq "))
|
if (filter.StartsWith("displayName eq "))
|
||||||
@@ -53,11 +60,11 @@ public class GetGroupsListQuery : IGetGroupsListQuery
|
|||||||
}
|
}
|
||||||
totalResults = groupList.Count;
|
totalResults = groupList.Count;
|
||||||
}
|
}
|
||||||
else if (string.IsNullOrWhiteSpace(filter) && startIndex.HasValue && count.HasValue)
|
else if (string.IsNullOrWhiteSpace(filter))
|
||||||
{
|
{
|
||||||
groupList = groups.OrderBy(g => g.Name)
|
groupList = groups.OrderBy(g => g.Name)
|
||||||
.Skip(startIndex.Value - 1)
|
.Skip(startIndex - 1)
|
||||||
.Take(count.Value)
|
.Take(count)
|
||||||
.ToList();
|
.ToList();
|
||||||
totalResults = groups.Count;
|
totalResults = groups.Count;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
using Bit.Core.AdminConsole.Entities;
|
using Bit.Core.AdminConsole.Entities;
|
||||||
|
using Bit.Scim.Models;
|
||||||
|
|
||||||
namespace Bit.Scim.Groups.Interfaces;
|
namespace Bit.Scim.Groups.Interfaces;
|
||||||
|
|
||||||
public interface IGetGroupsListQuery
|
public interface IGetGroupsListQuery
|
||||||
{
|
{
|
||||||
Task<(IEnumerable<Group> groupList, int totalResults)> GetGroupsListAsync(Guid organizationId, string filter, int? count, int? startIndex);
|
Task<(IEnumerable<Group> groupList, int totalResults)> GetGroupsListAsync(Guid organizationId, GetGroupsQueryParamModel model);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace Bit.Scim.Models;
|
||||||
|
|
||||||
|
public class GetGroupsQueryParamModel
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace Bit.Scim.Models;
|
||||||
|
|
||||||
public class GetUsersQueryParamModel
|
public class GetUsersQueryParamModel
|
||||||
{
|
{
|
||||||
public string Filter { get; init; } = string.Empty;
|
public string Filter { get; init; } = string.Empty;
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
|
using Bit.Scim.Models;
|
||||||
using Bit.Scim.Users.Interfaces;
|
using Bit.Scim.Users.Interfaces;
|
||||||
|
|
||||||
namespace Bit.Scim.Users;
|
namespace Bit.Scim.Users;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
||||||
|
using Bit.Scim.Models;
|
||||||
|
|
||||||
namespace Bit.Scim.Users.Interfaces;
|
namespace Bit.Scim.Users.Interfaces;
|
||||||
|
|
||||||
|
|||||||
@@ -200,6 +200,38 @@ public class GroupsControllerTests : IClassFixture<ScimApplicationFactory>, IAsy
|
|||||||
AssertHelper.AssertPropertyEqual(expectedResponse, responseModel);
|
AssertHelper.AssertPropertyEqual(expectedResponse, responseModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GetList_SearchDisplayNameWithoutOptionalParameters_Success()
|
||||||
|
{
|
||||||
|
string filter = "displayName eq Test Group 2";
|
||||||
|
int? itemsPerPage = null;
|
||||||
|
int? startIndex = null;
|
||||||
|
var expectedResponse = new ScimListResponseModel<ScimGroupResponseModel>
|
||||||
|
{
|
||||||
|
ItemsPerPage = 50, //default value
|
||||||
|
TotalResults = 1,
|
||||||
|
StartIndex = 1, //default value
|
||||||
|
Resources = new List<ScimGroupResponseModel>
|
||||||
|
{
|
||||||
|
new ScimGroupResponseModel
|
||||||
|
{
|
||||||
|
Id = ScimApplicationFactory.TestGroupId2,
|
||||||
|
DisplayName = "Test Group 2",
|
||||||
|
ExternalId = "B",
|
||||||
|
Schemas = new List<string> { ScimConstants.Scim2SchemaGroup }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Schemas = new List<string> { ScimConstants.Scim2SchemaListResponse }
|
||||||
|
};
|
||||||
|
|
||||||
|
var context = await _factory.GroupsGetListAsync(ScimApplicationFactory.TestOrganizationId1, filter, itemsPerPage, startIndex);
|
||||||
|
|
||||||
|
Assert.Equal(StatusCodes.Status200OK, context.Response.StatusCode);
|
||||||
|
|
||||||
|
var responseModel = JsonSerializer.Deserialize<ScimListResponseModel<ScimGroupResponseModel>>(context.Response.Body, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
|
||||||
|
AssertHelper.AssertPropertyEqual(expectedResponse, responseModel);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task Post_Success()
|
public async Task Post_Success()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using Bit.Core.AdminConsole.Entities;
|
using Bit.Core.AdminConsole.Entities;
|
||||||
using Bit.Core.AdminConsole.Repositories;
|
using Bit.Core.AdminConsole.Repositories;
|
||||||
using Bit.Scim.Groups;
|
using Bit.Scim.Groups;
|
||||||
|
using Bit.Scim.Models;
|
||||||
using Bit.Test.Common.AutoFixture;
|
using Bit.Test.Common.AutoFixture;
|
||||||
using Bit.Test.Common.AutoFixture.Attributes;
|
using Bit.Test.Common.AutoFixture.Attributes;
|
||||||
using Bit.Test.Common.Helpers;
|
using Bit.Test.Common.Helpers;
|
||||||
@@ -24,7 +25,7 @@ public class GetGroupsListCommandTests
|
|||||||
.GetManyByOrganizationIdAsync(organizationId)
|
.GetManyByOrganizationIdAsync(organizationId)
|
||||||
.Returns(groups);
|
.Returns(groups);
|
||||||
|
|
||||||
var result = await sutProvider.Sut.GetGroupsListAsync(organizationId, null, count, startIndex);
|
var result = await sutProvider.Sut.GetGroupsListAsync(organizationId, new GetGroupsQueryParamModel { Count = count, StartIndex = startIndex });
|
||||||
|
|
||||||
AssertHelper.AssertPropertyEqual(groups.Skip(startIndex - 1).Take(count).ToList(), result.groupList);
|
AssertHelper.AssertPropertyEqual(groups.Skip(startIndex - 1).Take(count).ToList(), result.groupList);
|
||||||
AssertHelper.AssertPropertyEqual(groups.Count, result.totalResults);
|
AssertHelper.AssertPropertyEqual(groups.Count, result.totalResults);
|
||||||
@@ -47,7 +48,7 @@ public class GetGroupsListCommandTests
|
|||||||
.GetManyByOrganizationIdAsync(organizationId)
|
.GetManyByOrganizationIdAsync(organizationId)
|
||||||
.Returns(groups);
|
.Returns(groups);
|
||||||
|
|
||||||
var result = await sutProvider.Sut.GetGroupsListAsync(organizationId, filter, null, null);
|
var result = await sutProvider.Sut.GetGroupsListAsync(organizationId, new GetGroupsQueryParamModel { Filter = filter });
|
||||||
|
|
||||||
AssertHelper.AssertPropertyEqual(expectedGroupList, result.groupList);
|
AssertHelper.AssertPropertyEqual(expectedGroupList, result.groupList);
|
||||||
AssertHelper.AssertPropertyEqual(expectedTotalResults, result.totalResults);
|
AssertHelper.AssertPropertyEqual(expectedTotalResults, result.totalResults);
|
||||||
@@ -67,7 +68,7 @@ public class GetGroupsListCommandTests
|
|||||||
.GetManyByOrganizationIdAsync(organizationId)
|
.GetManyByOrganizationIdAsync(organizationId)
|
||||||
.Returns(groups);
|
.Returns(groups);
|
||||||
|
|
||||||
var result = await sutProvider.Sut.GetGroupsListAsync(organizationId, filter, null, null);
|
var result = await sutProvider.Sut.GetGroupsListAsync(organizationId, new GetGroupsQueryParamModel { Filter = filter });
|
||||||
|
|
||||||
AssertHelper.AssertPropertyEqual(expectedGroupList, result.groupList);
|
AssertHelper.AssertPropertyEqual(expectedGroupList, result.groupList);
|
||||||
AssertHelper.AssertPropertyEqual(expectedTotalResults, result.totalResults);
|
AssertHelper.AssertPropertyEqual(expectedTotalResults, result.totalResults);
|
||||||
@@ -90,7 +91,7 @@ public class GetGroupsListCommandTests
|
|||||||
.GetManyByOrganizationIdAsync(organizationId)
|
.GetManyByOrganizationIdAsync(organizationId)
|
||||||
.Returns(groups);
|
.Returns(groups);
|
||||||
|
|
||||||
var result = await sutProvider.Sut.GetGroupsListAsync(organizationId, filter, null, null);
|
var result = await sutProvider.Sut.GetGroupsListAsync(organizationId, new GetGroupsQueryParamModel { Filter = filter });
|
||||||
|
|
||||||
AssertHelper.AssertPropertyEqual(expectedGroupList, result.groupList);
|
AssertHelper.AssertPropertyEqual(expectedGroupList, result.groupList);
|
||||||
AssertHelper.AssertPropertyEqual(expectedTotalResults, result.totalResults);
|
AssertHelper.AssertPropertyEqual(expectedTotalResults, result.totalResults);
|
||||||
@@ -112,7 +113,7 @@ public class GetGroupsListCommandTests
|
|||||||
.GetManyByOrganizationIdAsync(organizationId)
|
.GetManyByOrganizationIdAsync(organizationId)
|
||||||
.Returns(groups);
|
.Returns(groups);
|
||||||
|
|
||||||
var result = await sutProvider.Sut.GetGroupsListAsync(organizationId, filter, null, null);
|
var result = await sutProvider.Sut.GetGroupsListAsync(organizationId, new GetGroupsQueryParamModel { Filter = filter });
|
||||||
|
|
||||||
AssertHelper.AssertPropertyEqual(expectedGroupList, result.groupList);
|
AssertHelper.AssertPropertyEqual(expectedGroupList, result.groupList);
|
||||||
AssertHelper.AssertPropertyEqual(expectedTotalResults, result.totalResults);
|
AssertHelper.AssertPropertyEqual(expectedTotalResults, result.totalResults);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
|
using Bit.Scim.Models;
|
||||||
using Bit.Scim.Users;
|
using Bit.Scim.Users;
|
||||||
using Bit.Test.Common.AutoFixture;
|
using Bit.Test.Common.AutoFixture;
|
||||||
using Bit.Test.Common.AutoFixture.Attributes;
|
using Bit.Test.Common.AutoFixture.Attributes;
|
||||||
|
|||||||
Reference in New Issue
Block a user