Delete operation for Client v2 (#44335)

Closes: #43291

Signed-off-by: Peter Zaoral <pzaoral@redhat.com>
This commit is contained in:
Peter Zaoral
2025-11-20 12:12:33 +01:00
committed by GitHub
parent dc62067cfe
commit 0e959ad89e
5 changed files with 47 additions and 8 deletions

View File

@@ -33,8 +33,6 @@ public interface ClientService extends Service {
Stream<ClientRepresentation> getClients(ClientsResource clientsResource, RealmModel realm, ClientProjectionOptions projectionOptions, ClientSearchOptions searchOptions, ClientSortAndSliceOptions sortAndSliceOptions);
ClientRepresentation deleteClient(RealmModel realm, String clientId);
Stream<ClientRepresentation> deleteClients(RealmModel realm, ClientSearchOptions searchOptions);
CreateOrUpdateResult createOrUpdate(ClientsResource clientsResource, ClientResource clientResource, RealmModel realm, ClientRepresentation client, boolean allowUpdate) throws ServiceException;

View File

@@ -77,12 +77,6 @@ public class DefaultClientService implements ClientService {
return new CreateOrUpdateResult(updated, created);
}
@Override
public ClientRepresentation deleteClient(RealmModel realm, String clientId) {
// TODO Auto-generated method stub
return null;
}
@Override
public Stream<ClientRepresentation> deleteClients(RealmModel realm, ClientSearchOptions searchOptions) {
// TODO Auto-generated method stub

View File

@@ -2,6 +2,7 @@ package org.keycloak.admin.api.client;
import jakarta.validation.Valid;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.PATCH;
import jakarta.ws.rs.PUT;
@@ -31,4 +32,8 @@ public interface ClientApi {
@Produces(MediaType.APPLICATION_JSON)
ClientRepresentation patchClient(JsonNode patch);
@DELETE
@Produces(MediaType.APPLICATION_JSON)
void deleteClient();
}

View File

@@ -102,6 +102,14 @@ public class DefaultClientApi implements ClientApi {
}
}
@Override
public void deleteClient() {
if (clientResource == null) {
throw new NotFoundException("Cannot find the specified client");
}
clientResource.deleteClient();
}
static void validateUnknownFields(ClientRepresentation rep, HttpResponse response) {
if (!rep.getAdditionalFields().isEmpty()) {
throw new WebApplicationException("Payload contains unknown fields: " + rep.getAdditionalFields().keySet(), Response.Status.BAD_REQUEST);

View File

@@ -37,6 +37,7 @@ import org.keycloak.testframework.server.KeycloakServerConfigBuilder;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.http.HttpMessage;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPatch;
import org.apache.http.client.methods.HttpPost;
@@ -161,6 +162,39 @@ public class ClientApiV2Test {
}
}
@Test
public void deleteClient() throws Exception {
HttpPut createRequest = new HttpPut(HOSTNAME_LOCAL_ADMIN + "/realms/master/clients/to-delete");
setAuthHeader(createRequest);
createRequest.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON);
ClientRepresentation rep = new ClientRepresentation();
rep.setClientId("to-delete");
rep.setEnabled(true);
createRequest.setEntity(new StringEntity(mapper.writeValueAsString(rep)));
try (var response = client.execute(createRequest)) {
assertEquals(200, response.getStatusLine().getStatusCode());
}
HttpGet getRequest = new HttpGet(HOSTNAME_LOCAL_ADMIN + "/realms/master/clients/to-delete");
setAuthHeader(getRequest);
try (var response = client.execute(getRequest)) {
assertEquals(200, response.getStatusLine().getStatusCode());
}
HttpDelete deleteRequest = new HttpDelete(HOSTNAME_LOCAL_ADMIN + "/realms/master/clients/to-delete");
setAuthHeader(deleteRequest);
try (var response = client.execute(deleteRequest)) {
assertEquals(204, response.getStatusLine().getStatusCode());
}
try (var response = client.execute(getRequest)) {
assertEquals(404, response.getStatusLine().getStatusCode());
}
}
@Test
public void clientRepresentationValidation() throws Exception {
HttpPost request = new HttpPost(HOSTNAME_LOCAL_ADMIN + "/realms/master/clients");