Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add get group count and get group endpoints to Admin API SDK #121

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/Keycloak.AuthServices.Sdk/Admin/ApiUrls.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ internal static class ApiUrls

internal const string GetGroups = $"{GetRealm}/groups";

internal const string GetGroupCount = $"{GetRealm}/groups/count";

internal const string GetGroupMembers = $"{GetRealm}/groups/{{id}}/members";

internal const string CreateGroup = $"{GetRealm}/groups";

internal const string GetGroup = $"{GetRealm}/groups/{{id}}";
Expand Down
72 changes: 72 additions & 0 deletions src/Keycloak.AuthServices.Sdk/Admin/IKeycloakGroupClient.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
namespace Keycloak.AuthServices.Sdk.Admin;

using System.Text.Json;
using System.Text.Json.Nodes;
using Keycloak.AuthServices.Sdk.Admin.Models;
using Keycloak.AuthServices.Sdk.Admin.Requests.Groups;

Expand All @@ -8,6 +10,40 @@
/// </summary>
public interface IKeycloakGroupClient
{
/// <summary>
/// Gets the integer amount of groups in the realm matching the given <see cref="GetGroupCountRequestParameters"/>.
/// </summary>
/// <param name="realm">Realm name (not ID).</param>
/// <param name="parameters">Optional query parameters.</param>
/// <param name="cancellationToken"></param>
/// <returns>The amount of groups in the realm</returns>
Task<HttpResponseMessage> GetGroupCountWithResponseAsync(
string realm,
GetGroupCountRequestParameters? parameters = default,
CancellationToken cancellationToken = default
);

/// <summary>
/// Gets the integer amount of groups in the realm matching the given <see cref="GetGroupCountRequestParameters"/>.
/// </summary>
/// <param name="realm">Realm name (not ID).</param>
/// <param name="parameters">Optional query parameters.</param>
/// <param name="cancellationToken"></param>
/// <returns>The amount of groups in the realm</returns>
async Task<int> GetGroupCountAsync(
string realm,
GetGroupCountRequestParameters? parameters = default,
CancellationToken cancellationToken = default
)
{
var response = await this.GetGroupCountWithResponseAsync(realm, parameters, cancellationToken);

var json = await response.Content.ReadAsStringAsync(cancellationToken);
var jsonNode = JsonSerializer.Deserialize<JsonNode>(json);

return jsonNode!.AsObject()["count"]!.GetValue<int>();
}

/// <summary>
/// Get a collection of groups on the realm.
/// </summary>
Expand Down Expand Up @@ -72,6 +108,42 @@ async Task<GroupRepresentation> GetGroupAsync(
return await response.GetResponseAsync<GroupRepresentation>(cancellationToken) ?? new();
}

/// <summary>
/// Gets all members of a given group.
/// </summary>
/// <param name="realm">Realm name (not ID).</param>
/// <param name="groupId">Group ID.</param>
/// <param name="parameters">Optional query parameters.</param>
/// <param name="cancellationToken"></param>
/// <returns>The stream of users (<see cref="UserRepresentation"/>s)</returns>
Task<HttpResponseMessage> GetGroupMembersWithResponseAsync(
string realm,
string groupId,
GetGroupMembersRequestParameters? parameters = default,
CancellationToken cancellationToken = default
);

/// <summary>
/// Gets all members of a given group.
/// </summary>
/// <param name="realm">Realm name (not ID).</param>
/// <param name="groupId">Group ID.</param>
/// <param name="parameters">Optional query parameters.</param>
/// <param name="cancellationToken"></param>
/// <returns>The stream of users (<see cref="UserRepresentation"/>s)</returns>
async Task<IEnumerable<UserRepresentation>> GetGroupMembersAsync(
string realm,
string groupId,
GetGroupMembersRequestParameters? parameters = default,
CancellationToken cancellationToken = default
)
{
var response = await this.GetGroupMembersWithResponseAsync(realm, groupId, parameters, cancellationToken);

return await response.GetResponseAsync<IEnumerable<UserRepresentation>>(cancellationToken)
?? Enumerable.Empty<UserRepresentation>();
}

/// <summary>
/// Create or add a top level realm groupSet or create child.
/// </summary>
Expand Down
62 changes: 62 additions & 0 deletions src/Keycloak.AuthServices.Sdk/Admin/KeycloakClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,35 @@ public async Task<HttpResponseMessage> LeaveGroupWithResponseAsync(

#region GroupRegion

/// <inheritdoc/>
public async Task<HttpResponseMessage> GetGroupCountWithResponseAsync(
string realm,
GetGroupCountRequestParameters? parameters = default,
CancellationToken cancellationToken = default
)
{
var path = ApiUrls.GetGroupCount.WithRealm(realm);

var query = string.Empty;

if (parameters is not null)
{
var queryParameters = new List<KeyValuePair<string, string?>>()
{
new("search", parameters.Search),
new("top", parameters.Top?.ToString())
};

query = new QueryBuilder(queryParameters.Where(q => q.Value is not null)!)
.ToQueryString()
.ToString();
}

var responseMessage = await this.httpClient.GetAsync(path + query, cancellationToken);

return responseMessage;
}

///<inheritdoc/>
public async Task<HttpResponseMessage> GetGroupsWithResponseAsync(
string realm,
Expand Down Expand Up @@ -390,6 +419,39 @@ public async Task<HttpResponseMessage> GetGroupWithResponseAsync(
return responseMessage!;
}

/// <inheritdoc/>
public async Task<HttpResponseMessage> GetGroupMembersWithResponseAsync(
string realm,
string groupId,
GetGroupMembersRequestParameters? parameters = default,
CancellationToken cancellationToken = default
)
{
var path = ApiUrls.GetGroupMembers.WithRealm(realm).Replace("{id}", groupId);

var query = string.Empty;

if (parameters is not null)
{
var queryParameters = new List<KeyValuePair<string, string?>>
{
new("briefRepresentation", parameters.BriefRepresentation?.ToString()),
#pragma warning disable CA1305 // use IFormatProvider
new("first", parameters.First?.ToString()),
new("max", parameters.Max?.ToString())
#pragma warning restore CA1305 // use IFormatProvider
};

query = new QueryBuilder(queryParameters.Where(q => q.Value is not null)!)
.ToQueryString()
.ToString();
}

var responseMessage = await this.httpClient.GetAsync(path + query, cancellationToken);

return responseMessage;
}

///<inheritdoc/>
public async Task<HttpResponseMessage> CreateGroupWithResponseAsync(
string realm,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace Keycloak.AuthServices.Sdk.Admin.Requests.Groups;

/// <summary>
/// Optional request parameters for the <see cref="IKeycloakClient.GetGroupCountAsync"/> endpoint.

Check warning on line 4 in src/Keycloak.AuthServices.Sdk/Admin/Requests/Groups/GetGroupCountRequestParameters.cs

View workflow job for this annotation

GitHub Actions / Build-ubuntu-latest

XML comment has cref attribute 'GetGroupCountAsync' that could not be resolved

Check warning on line 4 in src/Keycloak.AuthServices.Sdk/Admin/Requests/Groups/GetGroupCountRequestParameters.cs

View workflow job for this annotation

GitHub Actions / Build-ubuntu-latest

XML comment has cref attribute 'GetGroupCountAsync' that could not be resolved

Check warning on line 4 in src/Keycloak.AuthServices.Sdk/Admin/Requests/Groups/GetGroupCountRequestParameters.cs

View workflow job for this annotation

GitHub Actions / Build-ubuntu-latest

XML comment has cref attribute 'GetGroupCountAsync' that could not be resolved

Check warning on line 4 in src/Keycloak.AuthServices.Sdk/Admin/Requests/Groups/GetGroupCountRequestParameters.cs

View workflow job for this annotation

GitHub Actions / Build-windows-latest

XML comment has cref attribute 'GetGroupCountAsync' that could not be resolved

Check warning on line 4 in src/Keycloak.AuthServices.Sdk/Admin/Requests/Groups/GetGroupCountRequestParameters.cs

View workflow job for this annotation

GitHub Actions / Build-windows-latest

XML comment has cref attribute 'GetGroupCountAsync' that could not be resolved

Check warning on line 4 in src/Keycloak.AuthServices.Sdk/Admin/Requests/Groups/GetGroupCountRequestParameters.cs

View workflow job for this annotation

GitHub Actions / Build-windows-latest

XML comment has cref attribute 'GetGroupCountAsync' that could not be resolved
/// </summary>
public class GetGroupCountRequestParameters
{
/// <summary>
/// A search query for the groups that should be counted
/// </summary>
public string? Search { get; init; }

/// <summary>
/// Whether the top groups should be returned
/// </summary>
public bool? Top { get; init; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
namespace Keycloak.AuthServices.Sdk.Admin.Requests.Groups;

/// <summary>
/// Optional request parameters for the <see cref="IKeycloakClient.GetGroupMembersAsync"/> endpoint.

Check warning on line 4 in src/Keycloak.AuthServices.Sdk/Admin/Requests/Groups/GetGroupMembersRequestParameters.cs

View workflow job for this annotation

GitHub Actions / Build-ubuntu-latest

XML comment has cref attribute 'GetGroupMembersAsync' that could not be resolved

Check warning on line 4 in src/Keycloak.AuthServices.Sdk/Admin/Requests/Groups/GetGroupMembersRequestParameters.cs

View workflow job for this annotation

GitHub Actions / Build-ubuntu-latest

XML comment has cref attribute 'GetGroupMembersAsync' that could not be resolved

Check warning on line 4 in src/Keycloak.AuthServices.Sdk/Admin/Requests/Groups/GetGroupMembersRequestParameters.cs

View workflow job for this annotation

GitHub Actions / Build-ubuntu-latest

XML comment has cref attribute 'GetGroupMembersAsync' that could not be resolved

Check warning on line 4 in src/Keycloak.AuthServices.Sdk/Admin/Requests/Groups/GetGroupMembersRequestParameters.cs

View workflow job for this annotation

GitHub Actions / Build-windows-latest

XML comment has cref attribute 'GetGroupMembersAsync' that could not be resolved

Check warning on line 4 in src/Keycloak.AuthServices.Sdk/Admin/Requests/Groups/GetGroupMembersRequestParameters.cs

View workflow job for this annotation

GitHub Actions / Build-windows-latest

XML comment has cref attribute 'GetGroupMembersAsync' that could not be resolved

Check warning on line 4 in src/Keycloak.AuthServices.Sdk/Admin/Requests/Groups/GetGroupMembersRequestParameters.cs

View workflow job for this annotation

GitHub Actions / Build-windows-latest

XML comment has cref attribute 'GetGroupMembersAsync' that could not be resolved
/// </summary>
public class GetGroupMembersRequestParameters
{
/// <summary>
/// Defines whether brief representations are returned. Default is false.
/// </summary>
public bool? BriefRepresentation { get; init; }

/// <summary>
/// The pagination offset. Default is 0.
/// </summary>
public int? First { get; init; }

/// <summary>
/// The maximum results size. Default is 100.
/// </summary>
public int? Max { get; init; }
}
23 changes: 23 additions & 0 deletions tests/Keycloak.AuthServices.Sdk.Tests/KeycloakGroupClientTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
namespace Keycloak.AuthServices.Sdk.Tests;

using Admin;
using RichardSzalay.MockHttp;

public class KeycloakGroupClientTests : IDisposable
{
private const string BaseAddress = "http://localhost:8080";
private const string MediaType = "application/json";

private readonly MockHttpMessageHandler handler = new();
private readonly IKeycloakGroupClient groupClient;

public KeycloakGroupClientTests()
{
var httpClient = this.handler.ToHttpClient();
httpClient.BaseAddress = new Uri(BaseAddress);

this.groupClient = new KeycloakClient(httpClient);
}

public void Dispose() => this.handler.Dispose();

Check warning on line 22 in tests/Keycloak.AuthServices.Sdk.Tests/KeycloakGroupClientTests.cs

View workflow job for this annotation

GitHub Actions / Build-ubuntu-latest

Change KeycloakGroupClientTests.Dispose() to call GC.SuppressFinalize(object). This will prevent derived types that introduce a finalizer from needing to re-implement 'IDisposable' to call it. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1816)

Check warning on line 22 in tests/Keycloak.AuthServices.Sdk.Tests/KeycloakGroupClientTests.cs

View workflow job for this annotation

GitHub Actions / Build-ubuntu-latest

Change KeycloakGroupClientTests.Dispose() to call GC.SuppressFinalize(object). This will prevent derived types that introduce a finalizer from needing to re-implement 'IDisposable' to call it. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1816)

Check warning on line 22 in tests/Keycloak.AuthServices.Sdk.Tests/KeycloakGroupClientTests.cs

View workflow job for this annotation

GitHub Actions / Build-ubuntu-latest

Change KeycloakGroupClientTests.Dispose() to call GC.SuppressFinalize(object). This will prevent derived types that introduce a finalizer from needing to re-implement 'IDisposable' to call it. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1816)

Check warning on line 22 in tests/Keycloak.AuthServices.Sdk.Tests/KeycloakGroupClientTests.cs

View workflow job for this annotation

GitHub Actions / Build-windows-latest

Change KeycloakGroupClientTests.Dispose() to call GC.SuppressFinalize(object). This will prevent derived types that introduce a finalizer from needing to re-implement 'IDisposable' to call it. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1816)

Check warning on line 22 in tests/Keycloak.AuthServices.Sdk.Tests/KeycloakGroupClientTests.cs

View workflow job for this annotation

GitHub Actions / Build-windows-latest

Change KeycloakGroupClientTests.Dispose() to call GC.SuppressFinalize(object). This will prevent derived types that introduce a finalizer from needing to re-implement 'IDisposable' to call it. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1816)

Check warning on line 22 in tests/Keycloak.AuthServices.Sdk.Tests/KeycloakGroupClientTests.cs

View workflow job for this annotation

GitHub Actions / Build-windows-latest

Change KeycloakGroupClientTests.Dispose() to call GC.SuppressFinalize(object). This will prevent derived types that introduce a finalizer from needing to re-implement 'IDisposable' to call it. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1816)
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
using Keycloak.AuthServices.Sdk.Utils;
using RichardSzalay.MockHttp;

public class KeycloakPolicyClientTests
public class KeycloakPolicyClientTests : IDisposable
{
private const string BaseAddress = "http://localhost:8080";
private const string CurrentRealm = "master";
Expand Down Expand Up @@ -238,4 +238,6 @@
""id"":""{id}"",
""name"":""{name}""
}}";

public void Dispose() => this.handler.Dispose();

Check warning on line 242 in tests/Keycloak.AuthServices.Sdk.Tests/KeycloakPolicyClientTests.cs

View workflow job for this annotation

GitHub Actions / Build-ubuntu-latest

Change KeycloakPolicyClientTests.Dispose() to call GC.SuppressFinalize(object). This will prevent derived types that introduce a finalizer from needing to re-implement 'IDisposable' to call it. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1816)

Check warning on line 242 in tests/Keycloak.AuthServices.Sdk.Tests/KeycloakPolicyClientTests.cs

View workflow job for this annotation

GitHub Actions / Build-ubuntu-latest

Change KeycloakPolicyClientTests.Dispose() to call GC.SuppressFinalize(object). This will prevent derived types that introduce a finalizer from needing to re-implement 'IDisposable' to call it. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1816)

Check warning on line 242 in tests/Keycloak.AuthServices.Sdk.Tests/KeycloakPolicyClientTests.cs

View workflow job for this annotation

GitHub Actions / Build-ubuntu-latest

Change KeycloakPolicyClientTests.Dispose() to call GC.SuppressFinalize(object). This will prevent derived types that introduce a finalizer from needing to re-implement 'IDisposable' to call it. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1816)

Check warning on line 242 in tests/Keycloak.AuthServices.Sdk.Tests/KeycloakPolicyClientTests.cs

View workflow job for this annotation

GitHub Actions / Build-windows-latest

Change KeycloakPolicyClientTests.Dispose() to call GC.SuppressFinalize(object). This will prevent derived types that introduce a finalizer from needing to re-implement 'IDisposable' to call it. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1816)

Check warning on line 242 in tests/Keycloak.AuthServices.Sdk.Tests/KeycloakPolicyClientTests.cs

View workflow job for this annotation

GitHub Actions / Build-windows-latest

Change KeycloakPolicyClientTests.Dispose() to call GC.SuppressFinalize(object). This will prevent derived types that introduce a finalizer from needing to re-implement 'IDisposable' to call it. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1816)

Check warning on line 242 in tests/Keycloak.AuthServices.Sdk.Tests/KeycloakPolicyClientTests.cs

View workflow job for this annotation

GitHub Actions / Build-windows-latest

Change KeycloakPolicyClientTests.Dispose() to call GC.SuppressFinalize(object). This will prevent derived types that introduce a finalizer from needing to re-implement 'IDisposable' to call it. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1816)
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
using Keycloak.AuthServices.Sdk.Utils;
using RichardSzalay.MockHttp;

public class KeycloakProtectedResourceClientTests
public class KeycloakProtectedResourceClientTests : IDisposable
{
private const string BaseAddress = "http://localhost:8080";

Expand Down Expand Up @@ -309,4 +309,6 @@
Type = "http://www.example.com/rsrcs/socialstream/140-compatible",
Attributes = { { "my_custom_attribute_key", "my_custom_attribute_value" } }
};

public void Dispose() => this.handler.Dispose();

Check warning on line 313 in tests/Keycloak.AuthServices.Sdk.Tests/KeycloakProtectedResourceClientTests.cs

View workflow job for this annotation

GitHub Actions / Build-ubuntu-latest

Change KeycloakProtectedResourceClientTests.Dispose() to call GC.SuppressFinalize(object). This will prevent derived types that introduce a finalizer from needing to re-implement 'IDisposable' to call it. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1816)

Check warning on line 313 in tests/Keycloak.AuthServices.Sdk.Tests/KeycloakProtectedResourceClientTests.cs

View workflow job for this annotation

GitHub Actions / Build-windows-latest

Change KeycloakProtectedResourceClientTests.Dispose() to call GC.SuppressFinalize(object). This will prevent derived types that introduce a finalizer from needing to re-implement 'IDisposable' to call it. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1816)
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
using Keycloak.AuthServices.Sdk.Admin;
using RichardSzalay.MockHttp;

public class KeycloakRealmClientTests
public class KeycloakRealmClientTests : IDisposable
{
private const string BaseAddress = "http://localhost:8080";

Expand Down Expand Up @@ -50,4 +50,6 @@

this.handler.VerifyNoOutstandingExpectation();
}

public void Dispose() => this.handler.Dispose();

Check warning on line 54 in tests/Keycloak.AuthServices.Sdk.Tests/KeycloakRealmClientTests.cs

View workflow job for this annotation

GitHub Actions / Build-ubuntu-latest

Change KeycloakRealmClientTests.Dispose() to call GC.SuppressFinalize(object). This will prevent derived types that introduce a finalizer from needing to re-implement 'IDisposable' to call it. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1816)

Check warning on line 54 in tests/Keycloak.AuthServices.Sdk.Tests/KeycloakRealmClientTests.cs

View workflow job for this annotation

GitHub Actions / Build-ubuntu-latest

Change KeycloakRealmClientTests.Dispose() to call GC.SuppressFinalize(object). This will prevent derived types that introduce a finalizer from needing to re-implement 'IDisposable' to call it. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1816)

Check warning on line 54 in tests/Keycloak.AuthServices.Sdk.Tests/KeycloakRealmClientTests.cs

View workflow job for this annotation

GitHub Actions / Build-ubuntu-latest

Change KeycloakRealmClientTests.Dispose() to call GC.SuppressFinalize(object). This will prevent derived types that introduce a finalizer from needing to re-implement 'IDisposable' to call it. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1816)

Check warning on line 54 in tests/Keycloak.AuthServices.Sdk.Tests/KeycloakRealmClientTests.cs

View workflow job for this annotation

GitHub Actions / Build-windows-latest

Change KeycloakRealmClientTests.Dispose() to call GC.SuppressFinalize(object). This will prevent derived types that introduce a finalizer from needing to re-implement 'IDisposable' to call it. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1816)

Check warning on line 54 in tests/Keycloak.AuthServices.Sdk.Tests/KeycloakRealmClientTests.cs

View workflow job for this annotation

GitHub Actions / Build-windows-latest

Change KeycloakRealmClientTests.Dispose() to call GC.SuppressFinalize(object). This will prevent derived types that introduce a finalizer from needing to re-implement 'IDisposable' to call it. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1816)

Check warning on line 54 in tests/Keycloak.AuthServices.Sdk.Tests/KeycloakRealmClientTests.cs

View workflow job for this annotation

GitHub Actions / Build-windows-latest

Change KeycloakRealmClientTests.Dispose() to call GC.SuppressFinalize(object). This will prevent derived types that introduce a finalizer from needing to re-implement 'IDisposable' to call it. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1816)
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
using Keycloak.AuthServices.Sdk.Utils;
using RichardSzalay.MockHttp;

public class KeycloakUserClientTests
public class KeycloakUserClientTests : IDisposable
{
private const string BaseAddress = "http://localhost:8080";
private const string MediaType = "application/json";
Expand Down Expand Up @@ -491,4 +491,6 @@
}
}
""".Replace("{userId}", userId.ToString());

public void Dispose() => this.handler.Dispose();

Check warning on line 495 in tests/Keycloak.AuthServices.Sdk.Tests/KeycloakUserClientTests.cs

View workflow job for this annotation

GitHub Actions / Build-ubuntu-latest

Change KeycloakUserClientTests.Dispose() to call GC.SuppressFinalize(object). This will prevent derived types that introduce a finalizer from needing to re-implement 'IDisposable' to call it. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1816)

Check warning on line 495 in tests/Keycloak.AuthServices.Sdk.Tests/KeycloakUserClientTests.cs

View workflow job for this annotation

GitHub Actions / Build-windows-latest

Change KeycloakUserClientTests.Dispose() to call GC.SuppressFinalize(object). This will prevent derived types that introduce a finalizer from needing to re-implement 'IDisposable' to call it. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1816)
}
Loading