feat: implemented RabbitMQ RPC endpoint for query user info.
ISSUES RELATED: clevermicro/user-management#34
This commit is contained in:
@@ -26,7 +26,7 @@ public abstract class AbstractEndpoint implements AutoCloseable {
|
||||
|
||||
private final String handlerTag;
|
||||
private final CleverMicroAmqpClient client;
|
||||
private final ObjectMapper objectMapper;
|
||||
protected final ObjectMapper objectMapper;
|
||||
|
||||
private final String queueName;
|
||||
|
||||
|
||||
+23
-5
@@ -2,20 +2,38 @@ package com.cleverthis.authservice.controller.rabbitmq.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
|
||||
/**
|
||||
* The model for replied response.
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString
|
||||
@Builder
|
||||
public class EndpointErrorResponse {
|
||||
@JsonProperty("type")
|
||||
private final String type = "ERROR";
|
||||
public final class EndpointErrorResponse extends EndpointResponse {
|
||||
@JsonProperty("exception")
|
||||
private final String exception;
|
||||
@JsonProperty("message")
|
||||
private final String message;
|
||||
@JsonProperty("additional_info")
|
||||
private final Object additionalInfo;
|
||||
|
||||
/**
|
||||
* Create an error response.
|
||||
*/
|
||||
public EndpointErrorResponse(
|
||||
final String exception,
|
||||
final String message,
|
||||
final Object additionalInfo
|
||||
) {
|
||||
super("ERROR");
|
||||
this.exception = exception;
|
||||
this.message = message;
|
||||
this.additionalInfo = additionalInfo;
|
||||
}
|
||||
}
|
||||
|
||||
+13
-5
@@ -3,17 +3,25 @@ package com.cleverthis.authservice.controller.rabbitmq.model;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import java.util.List;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
|
||||
/**
|
||||
* The model for replied response.
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString
|
||||
@Builder
|
||||
public class EndpointOkResponse {
|
||||
@JsonProperty("type")
|
||||
private final String type = "OK";
|
||||
public class EndpointOkResponse extends EndpointResponse {
|
||||
@JsonProperty("result")
|
||||
private final List<?> result;
|
||||
|
||||
public EndpointOkResponse(final List<?> result) {
|
||||
super("OK");
|
||||
this.result = result;
|
||||
}
|
||||
}
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package com.cleverthis.authservice.controller.rabbitmq.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* The model for replied response.
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public abstract class EndpointResponse {
|
||||
@JsonProperty("type")
|
||||
protected final String type;
|
||||
|
||||
protected EndpointResponse(final String type) {
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
package com.cleverthis.authservice.controller.rabbitmq.v1.user;
|
||||
|
||||
import com.cleverthis.authservice.controller.rabbitmq.AbstractEndpoint;
|
||||
import com.cleverthis.authservice.controller.rabbitmq.exception.EndpointBadRequestException;
|
||||
import com.cleverthis.authservice.controller.rabbitmq.exception.EndpointException;
|
||||
import com.cleverthis.authservice.controller.rabbitmq.model.EndpointRequest;
|
||||
import com.cleverthis.authservice.controller.rabbitmq.model.EndpointResponses;
|
||||
import com.cleverthis.authservice.service.impl.KeycloakAdminReactiveClient;
|
||||
import com.cleverthis.clevermicro.client.amqp.CleverMicroAmqpClient;
|
||||
import com.cleverthis.clevermicro.client.amqp.handler.HandlerContext;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import java.io.IOException;
|
||||
import java.util.NoSuchElementException;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Serve endpoints on `user-service-v1`.
|
||||
*/
|
||||
@Component
|
||||
public class UserEndpointV1 extends AbstractEndpoint {
|
||||
private static final String ROUTING_KEY = "user-service-v1";
|
||||
|
||||
private final KeycloakAdminReactiveClient keycloakAdminClient;
|
||||
|
||||
public UserEndpointV1(
|
||||
final CleverMicroAmqpClient client,
|
||||
final ObjectMapper objectMapper,
|
||||
final KeycloakAdminReactiveClient keycloakAdminClient
|
||||
) throws IOException {
|
||||
super(client, objectMapper, ROUTING_KEY);
|
||||
this.keycloakAdminClient = keycloakAdminClient;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleRequest(
|
||||
final HandlerContext ctx, final EndpointRequest request
|
||||
) throws EndpointException {
|
||||
//noinspection SwitchStatementWithTooFewBranches
|
||||
final Object result = switch (request.getMethod()) {
|
||||
case "queryUserById" -> queryUserById(request);
|
||||
default -> throw new EndpointBadRequestException("Method not found");
|
||||
};
|
||||
reply(ctx, EndpointResponses.ok(result));
|
||||
}
|
||||
|
||||
private UserRepresentation queryUserById(
|
||||
final EndpointRequest request
|
||||
) throws EndpointException {
|
||||
final var argId = request.getArgs().get("id");
|
||||
if (argId == null) {
|
||||
throw new EndpointBadRequestException("Missing parameter 'id'");
|
||||
}
|
||||
if (!argId.isTextual()) {
|
||||
throw new EndpointBadRequestException(
|
||||
"Invalid type for parameter 'id', required non-null string");
|
||||
}
|
||||
// this should be non-null
|
||||
final var id = argId.textValue();
|
||||
assert id != null;
|
||||
try {
|
||||
return this.keycloakAdminClient.getUserDetails(id).block();
|
||||
} catch (final NoSuchElementException ex) {
|
||||
throw new EndpointException(
|
||||
"cleverthis.clevermicro.auth.user_not_found",
|
||||
"User id " + id + "not found",
|
||||
ex
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -6,9 +6,11 @@ import com.cleverthis.authservice.dto.keycloakadmin.KeycloakInvitationRequest;
|
||||
import com.cleverthis.authservice.exception.RegistrationException;
|
||||
import com.cleverthis.authservice.exception.ServiceUnavailableException;
|
||||
import com.cleverthis.authservice.exception.UserAlreadyExistsException;
|
||||
import jakarta.ws.rs.NotFoundException;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.keycloak.admin.client.Keycloak;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
@@ -460,9 +462,18 @@ public class KeycloakAdminReactiveClient {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user details with id.
|
||||
*
|
||||
* @throws NoSuchElementException in mono if keycloak returns 404.
|
||||
*/
|
||||
public Mono<UserRepresentation> getUserDetails(String userId) {
|
||||
try {
|
||||
return Mono.just(this.keycloak.realm(this.realm)
|
||||
.users().get(userId).toRepresentation());
|
||||
} catch (final NotFoundException ex) {
|
||||
return Mono.error(new NoSuchElementException("User with id " + userId + " not exists"));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -7,7 +7,7 @@ spring:
|
||||
name: auth-service
|
||||
cloud:
|
||||
consul:
|
||||
host: ${CONSUL_HOST:localhost}
|
||||
host: ${CONSUL_HOST:127.0.0.1}
|
||||
port: ${CONSUL_PORT:8500}
|
||||
config:
|
||||
import-check:
|
||||
@@ -15,8 +15,8 @@ spring:
|
||||
|
||||
# Keycloak Configuration (adjust values as needed)
|
||||
keycloak:
|
||||
keycloak-host: ${KEYCLOAK_AUTH_SERVER_URL:http://localhost:9100} # Base URL of your Keycloak instance (NO trailing slash)
|
||||
keycloak-admin-host: ${KEYCLOAK_AUTH_ADMIN_SERVER_URL:http://localhost:9100} # Base URL of your Keycloak instance (NO trailing slash)
|
||||
keycloak-host: ${KEYCLOAK_AUTH_SERVER_URL:http://keycloak.dev.localhost} # Base URL of your Keycloak instance (NO trailing slash)
|
||||
keycloak-admin-host: ${KEYCLOAK_AUTH_ADMIN_SERVER_URL:http://keycloak.dev.localhost} # Base URL of your Keycloak instance (NO trailing slash)
|
||||
realm: ${KEYCLOAK_AUTH_REALM:clevermicro-dev} # The realm name you are using
|
||||
client-id: ${KEYCLOAK_AUTH_SERVICE_CLIENT:auth-service} # Client ID created in Keycloak for this service
|
||||
client-secret: ${KEYCLOAK_AUTH_SERVICE_SECRET:UJ1sMcWciIRREfjjAGoLHUDynLT4aYdD} # Client Secret from Keycloak (use secrets management!)
|
||||
|
||||
Reference in New Issue
Block a user