1
0
mirror of https://github.com/keycloak/keycloak.git synced 2026-10-23 19:29:23 +03:00

fix: switching to a string for the fields (#50094)

* fix: switching to a string for the fields

closes: #48734

Signed-off-by: Steve Hawkins <shawkins@redhat.com>

* Apply suggestions from code review

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Steven Hawkins <shawkins@redhat.com>

* further suggestions and removing the request filter

Signed-off-by: Steve Hawkins <shawkins@redhat.com>

---------

Signed-off-by: Steve Hawkins <shawkins@redhat.com>
Signed-off-by: Steven Hawkins <shawkins@redhat.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Steven Hawkins
2026-06-18 11:42:26 -04:00
committed by GitHub
parent 173e146fdd
commit c7d38cf030
6 changed files with 60 additions and 10 deletions

View File

@@ -218,12 +218,12 @@ public class Keycloak implements AutoCloseable {
}
/**
* Create a secure proxy with endpoints targetting the server
* Create a secure proxy with endpoints targeting the server
*/
public <T> T proxy(Class<T> proxyClass) {
return CLIENT_PROVIDER.targetProxy(target, proxyClass);
}
/**
* Closes the underlying client. After calling this method, this <code>Keycloak</code> instance cannot be reused.
*/

View File

@@ -236,15 +236,16 @@
"tags" : [ "Clients (v2)" ],
"parameters" : [ {
"description" : "Set of fields to include in the response. Must be top-level fields. If omitted or empty, all fields will be populated.",
"name" : "fields",
"in" : "query",
"explode" : false,
"schema" : {
"type" : "array",
"uniqueItems" : true,
"items" : {
"type" : "string"
}
}
},
"name" : "fields",
"in" : "query"
}, {
"description" : "Filter expression using SCIM-like syntax, e.g. clientId eq \"my-app\" and enabled eq true",
"name" : "q",

View File

@@ -45,6 +45,11 @@
<artifactId>hibernate-validator</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>

View File

@@ -1,16 +1,23 @@
package org.keycloak.admin.api;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import jakarta.ws.rs.QueryParam;
import org.eclipse.microprofile.openapi.annotations.enums.Explode;
import org.eclipse.microprofile.openapi.annotations.enums.SchemaType;
import org.eclipse.microprofile.openapi.annotations.media.Schema;
import org.eclipse.microprofile.openapi.annotations.parameters.Parameter;
public class ListOptions {
@Parameter(description = "Set of fields to include in the response. Must be top-level fields. If omitted or empty, all fields will be populated.")
@Parameter(description = "Set of fields to include in the response. Must be top-level fields. If omitted or empty, all fields will be populated.",
explode = Explode.FALSE, schema = @Schema(type = SchemaType.ARRAY, uniqueItems = true, implementation = String.class))
@QueryParam("fields")
protected Set<String> fields;
protected String fields;
@Parameter(description = "Filter expression using SCIM-like syntax, e.g. clientId eq \"my-app\" and enabled eq true")
@QueryParam("q")
@@ -27,11 +34,17 @@ public class ListOptions {
}
public Set<String> getFields() {
return fields;
if (fields == null) {
return null;
}
if (fields.isEmpty()) {
return Set.of();
}
return new LinkedHashSet<>(List.of(fields.split(",")));
}
public void setFields(Set<String> fields) {
this.fields = fields;
this.fields = fields == null ? null : fields.stream().collect(Collectors.joining(","));
}
public String getQuery() {
@@ -41,5 +54,5 @@ public class ListOptions {
public void setQuery(String query) {
this.query = query;
}
}

View File

@@ -0,0 +1,26 @@
package org.keycloak.admin.api;
import java.util.Set;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class ListOptionsTest {
@Test
void testGetFieldsEmpty() {
ListOptions options = new ListOptions();
options.setFields(Set.of());
assertEquals("", options.fields);
assertEquals(Set.of(), options.getFields());
}
@Test
void testGetFields() {
ListOptions options = new ListOptions();
options.fields = "a,b";
assertEquals(Set.of("a", "b"), options.getFields());
}
}

View File

@@ -20,6 +20,7 @@ package org.keycloak.tests.admin.client.v2;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
@@ -365,6 +366,10 @@ public class ClientApiV2Test extends AbstractClientApiV2Test{
public void invalidFieldProjection() {
BadRequestException e = assertThrows(BadRequestException.class, () -> getClientsApi().getClients(new ListOptions().fields(Set.of("unknown!"))));
assertEquals("{\"error\":\"unknown! is an unknown field\"}", e.getResponse().readEntity(String.class));
// ensure that multiple fields are interpreted correctly
e = assertThrows(BadRequestException.class, () -> getClientsApi().getClients(new ListOptions().fields(new LinkedHashSet<>(List.of("clientId","unknown!")))));
assertEquals("{\"error\":\"unknown! is an unknown field\"}", e.getResponse().readEntity(String.class));
}
@Test