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 String handlerTag;
|
||||||
private final CleverMicroAmqpClient client;
|
private final CleverMicroAmqpClient client;
|
||||||
private final ObjectMapper objectMapper;
|
protected final ObjectMapper objectMapper;
|
||||||
|
|
||||||
private final String queueName;
|
private final String queueName;
|
||||||
|
|
||||||
|
|||||||
+23
-5
@@ -2,20 +2,38 @@ package com.cleverthis.authservice.controller.rabbitmq.model;
|
|||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
import lombok.Data;
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The model for replied response.
|
* The model for replied response.
|
||||||
*/
|
*/
|
||||||
@Data
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@ToString
|
||||||
@Builder
|
@Builder
|
||||||
public class EndpointErrorResponse {
|
public final class EndpointErrorResponse extends EndpointResponse {
|
||||||
@JsonProperty("type")
|
|
||||||
private final String type = "ERROR";
|
|
||||||
@JsonProperty("exception")
|
@JsonProperty("exception")
|
||||||
private final String exception;
|
private final String exception;
|
||||||
@JsonProperty("message")
|
@JsonProperty("message")
|
||||||
private final String message;
|
private final String message;
|
||||||
@JsonProperty("additional_info")
|
@JsonProperty("additional_info")
|
||||||
private final Object additionalInfo;
|
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 com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
import lombok.Data;
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The model for replied response.
|
* The model for replied response.
|
||||||
*/
|
*/
|
||||||
@Data
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@ToString
|
||||||
@Builder
|
@Builder
|
||||||
public class EndpointOkResponse {
|
public class EndpointOkResponse extends EndpointResponse {
|
||||||
@JsonProperty("type")
|
|
||||||
private final String type = "OK";
|
|
||||||
@JsonProperty("result")
|
@JsonProperty("result")
|
||||||
private final List<?> 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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+13
-2
@@ -6,9 +6,11 @@ import com.cleverthis.authservice.dto.keycloakadmin.KeycloakInvitationRequest;
|
|||||||
import com.cleverthis.authservice.exception.RegistrationException;
|
import com.cleverthis.authservice.exception.RegistrationException;
|
||||||
import com.cleverthis.authservice.exception.ServiceUnavailableException;
|
import com.cleverthis.authservice.exception.ServiceUnavailableException;
|
||||||
import com.cleverthis.authservice.exception.UserAlreadyExistsException;
|
import com.cleverthis.authservice.exception.UserAlreadyExistsException;
|
||||||
|
import jakarta.ws.rs.NotFoundException;
|
||||||
import jakarta.ws.rs.core.Response;
|
import jakarta.ws.rs.core.Response;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.keycloak.admin.client.Keycloak;
|
import org.keycloak.admin.client.Keycloak;
|
||||||
import org.keycloak.representations.idm.ClientRepresentation;
|
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) {
|
public Mono<UserRepresentation> getUserDetails(String userId) {
|
||||||
return Mono.just(this.keycloak.realm(this.realm)
|
try {
|
||||||
.users().get(userId).toRepresentation());
|
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
|
name: auth-service
|
||||||
cloud:
|
cloud:
|
||||||
consul:
|
consul:
|
||||||
host: ${CONSUL_HOST:localhost}
|
host: ${CONSUL_HOST:127.0.0.1}
|
||||||
port: ${CONSUL_PORT:8500}
|
port: ${CONSUL_PORT:8500}
|
||||||
config:
|
config:
|
||||||
import-check:
|
import-check:
|
||||||
@@ -15,8 +15,8 @@ spring:
|
|||||||
|
|
||||||
# Keycloak Configuration (adjust values as needed)
|
# Keycloak Configuration (adjust values as needed)
|
||||||
keycloak:
|
keycloak:
|
||||||
keycloak-host: ${KEYCLOAK_AUTH_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://localhost:9100} # 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
|
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-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!)
|
client-secret: ${KEYCLOAK_AUTH_SERVICE_SECRET:UJ1sMcWciIRREfjjAGoLHUDynLT4aYdD} # Client Secret from Keycloak (use secrets management!)
|
||||||
|
|||||||
Reference in New Issue
Block a user