diff --git a/src/Keycloak.AuthServices.Sdk/Admin/ApiUrls.cs b/src/Keycloak.AuthServices.Sdk/Admin/ApiUrls.cs index 9bbf1d5c..8d379d09 100644 --- a/src/Keycloak.AuthServices.Sdk/Admin/ApiUrls.cs +++ b/src/Keycloak.AuthServices.Sdk/Admin/ApiUrls.cs @@ -19,6 +19,8 @@ internal static class ApiUrls internal const string GetUser = $"{GetRealm}/users/{{id}}"; + internal const string GetUserCount = $"{GetRealm}/users/count"; + internal const string CreateUser = $"{GetRealm}/users"; internal const string UpdateUser = $"{GetRealm}/users/{{id}}"; diff --git a/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakUserClient.cs b/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakUserClient.cs index 3193842e..6f8dbfc6 100644 --- a/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakUserClient.cs +++ b/src/Keycloak.AuthServices.Sdk/Admin/IKeycloakUserClient.cs @@ -40,6 +40,41 @@ async Task> GetUsersAsync( ?? Enumerable.Empty(); } + /// + /// Get the integer amount of users on the realm that match the provided . + /// + /// Note that the response is not JSON, but simply the integer value as a string. + /// + /// Realm name (not ID). + /// Optional query parameters. + /// + /// An integer amount of users + Task GetUserCountWithResponseAsync( + string realm, + GetUserCountRequestParameters? parameters = default, + CancellationToken cancellationToken = default + ); + + /// + /// Get the integer amount of users on the realm that match the provided . + /// + /// Realm name (not ID). + /// Optional query parameters. + /// + /// An integer amount of users + async Task GetUserCountAsync( + string realm, + GetUserCountRequestParameters? parameters = default, + CancellationToken cancellationToken = default + ) + { + var response = await this.GetUserCountWithResponseAsync(realm, parameters, cancellationToken); + var stringContent = await response.Content.ReadAsStringAsync(cancellationToken); +#pragma warning disable CA1305 // Specify IFormatProvider + return Convert.ToInt32(stringContent); +#pragma warning restore CA1305 // Specify IFormatProvider + } + /// /// Get representation of a user. /// diff --git a/src/Keycloak.AuthServices.Sdk/Admin/KeycloakClient.cs b/src/Keycloak.AuthServices.Sdk/Admin/KeycloakClient.cs index 245682c8..a60662f8 100644 --- a/src/Keycloak.AuthServices.Sdk/Admin/KeycloakClient.cs +++ b/src/Keycloak.AuthServices.Sdk/Admin/KeycloakClient.cs @@ -11,7 +11,7 @@ /// /// Represents a client for interacting with the Keycloak Admin API. /// -public partial class KeycloakClient : IKeycloakClient +public class KeycloakClient : IKeycloakClient { private readonly HttpClient httpClient; @@ -94,7 +94,42 @@ public async Task GetUsersWithResponseAsync( var responseMessage = await this.httpClient.GetAsync(path + query, cancellationToken); - return responseMessage!; + return responseMessage; + } + + /// + public async Task GetUserCountWithResponseAsync( + string realm, + GetUserCountRequestParameters? parameters = default, + CancellationToken cancellationToken = default + ) + { + var path = ApiUrls.GetUserCount.WithRealm(realm); + + var query = string.Empty; + + if (parameters is not null) + { + var queryParameters = new List>() + { + new("email", parameters.Email), + new("emailVerified", parameters.EmailVerified?.ToString()), + new("enabled", parameters.Enabled?.ToString()), + new("firstName", parameters.FirstName), + new("lastName", parameters.LastName), + new("q", parameters.Query), + new("search", parameters.Search), + new("username", parameters.Username) + }; + + query = new QueryBuilder(queryParameters.Where(q => q.Value is not null)!) + .ToQueryString() + .ToString(); + } + + var responseMessage = await this.httpClient.GetAsync(path + query, cancellationToken); + + return responseMessage; } /// diff --git a/src/Keycloak.AuthServices.Sdk/Admin/Requests/Users/GetUserCountRequestParameters.cs b/src/Keycloak.AuthServices.Sdk/Admin/Requests/Users/GetUserCountRequestParameters.cs new file mode 100644 index 00000000..6a9ebea5 --- /dev/null +++ b/src/Keycloak.AuthServices.Sdk/Admin/Requests/Users/GetUserCountRequestParameters.cs @@ -0,0 +1,50 @@ +namespace Keycloak.AuthServices.Sdk.Admin.Requests.Users; + +/// +/// Optional request parameters for the endpoint. +/// If these are not specified, the total amount of users in the provided realm will be returned, otherwise, the amount +/// of users in the realm that match the criteria defined in these parameters will be returned. +/// +public class GetUserCountRequestParameters +{ + /// + /// An optional filter for a user's email. + /// + public string? Email { get; init; } + + /// + /// Whether a user's email has to be verified to be included. + /// + public bool? EmailVerified { get; init; } + + /// + /// Whether a user has to be enabled to be included. + /// + public bool? Enabled { get; init; } + + /// + /// An optional filter for a user's first name. + /// + public string? FirstName { get; init; } + + /// + /// An optional filter for a user's last name. + /// + public string? LastName { get; init; } + + /// + /// An optional query to search for custom attributes, in the format "key1:value2 key2:value2". + /// + public string? Query { get; init; } + + /// + /// An optional search string for all the fields of a user. + /// Default search behavior is prefix-based (e.g., foo or foo*). Use foo for infix search and "foo" for exact search. + /// + public string? Search { get; init; } + + /// + /// An optional filter for a user's username. + /// + public string? Username { get; init; } +}