diff --git a/pom.xml b/pom.xml
index fc897e2..aa8a8f1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
com.uid2
uid2-e2e
- 4.1.0
+ 4.1.2-alpha-72-SNAPSHOT
21
diff --git a/src/test/java/app/component/Operator.java b/src/test/java/app/component/Operator.java
index 493a123..b6b59a3 100644
--- a/src/test/java/app/component/Operator.java
+++ b/src/test/java/app/component/Operator.java
@@ -11,6 +11,8 @@
import lombok.Getter;
import okhttp3.Request;
import okhttp3.RequestBody;
+import org.junit.platform.commons.logging.Logger;
+import org.junit.platform.commons.logging.LoggerFactory;
import javax.crypto.*;
import javax.crypto.spec.GCMParameterSpec;
@@ -68,6 +70,7 @@ private record V2Envelope(String envelope, byte[] nonce) {
// When running via IntelliJ, environment variables are defined in the uid2-dev-workspace repo under .idea/runConfigurations.
// Test data is defined in the uid2-admin repo.
+ private static final Logger LOGGER = LoggerFactory.getLogger(Operator.class);
private static final ObjectMapper OBJECT_MAPPER = Mapper.getInstance();
private static final SecureRandom SECURE_RANDOM = new SecureRandom();
private static final int TIMESTAMP_LENGTH = 8;
@@ -78,6 +81,27 @@ private record V2Envelope(String envelope, byte[] nonce) {
public static final String CLIENT_API_KEY = EnvUtil.getEnv(Const.Config.Operator.CLIENT_API_KEY);
public static final String CLIENT_API_SECRET = EnvUtil.getEnv(Const.Config.Operator.CLIENT_API_SECRET);
+
+ static {
+ // Log operator configuration at class initialization
+ String maskedKey = CLIENT_API_KEY != null && CLIENT_API_KEY.length() > 20
+ ? CLIENT_API_KEY.substring(0, 10) + "..." + CLIENT_API_KEY.substring(CLIENT_API_KEY.length() - 10)
+ : "[null or too short]";
+ String maskedSecret = CLIENT_API_SECRET != null && CLIENT_API_SECRET.length() > 20
+ ? CLIENT_API_SECRET.substring(0, 10) + "..." + CLIENT_API_SECRET.substring(CLIENT_API_SECRET.length() - 10)
+ : "[null or too short]";
+ LOGGER.info(() -> String.format(
+ "[OPERATOR CONFIG] Initialized with:%n" +
+ " CLIENT_API_KEY: %s (length: %d)%n" +
+ " CLIENT_API_SECRET: %s (length: %d)%n" +
+ " E2E_ENV: %s%n" +
+ " IDENTITY_SCOPE: %s",
+ maskedKey, CLIENT_API_KEY != null ? CLIENT_API_KEY.length() : 0,
+ maskedSecret, CLIENT_API_SECRET != null ? CLIENT_API_SECRET.length() : 0,
+ EnvUtil.getEnv(Const.Config.ENV, false),
+ EnvUtil.getEnv(Const.Config.IDENTITY_SCOPE, false)
+ ));
+ }
// Local only - Sharing
public static final String CLIENT_API_KEY_SHARING_RECIPIENT = EnvUtil.getEnv(Const.Config.Operator.CLIENT_API_KEY_SHARING_RECIPIENT, EnabledCondition.isLocal());
@@ -274,9 +298,55 @@ public IdentityMapResponse v2IdentityMap(IdentityMapInput input) {
// Need to use the manual mapping for error cases - SDK won't allow creating input with bad emails
public JsonNode v3IdentityMap(String payload) throws Exception {
+ String baseUrl = getBaseUrl();
+ String endpoint = baseUrl + "/v3/identity/map";
+
+ LOGGER.info(() -> String.format(
+ "[v3IdentityMap] Preparing request:%n" +
+ " Operator Name: %s%n" +
+ " Operator Type: %s%n" +
+ " Base URL: %s%n" +
+ " Full Endpoint: %s%n" +
+ " CLIENT_API_KEY: %s (length: %d)%n" +
+ " CLIENT_API_SECRET: %s (length: %d)%n" +
+ " Payload (raw): %s",
+ getName(), type, baseUrl, endpoint,
+ CLIENT_API_KEY != null ? CLIENT_API_KEY.substring(0, Math.min(20, CLIENT_API_KEY.length())) + "..." : "[null]",
+ CLIENT_API_KEY != null ? CLIENT_API_KEY.length() : 0,
+ CLIENT_API_SECRET != null ? CLIENT_API_SECRET.substring(0, Math.min(20, CLIENT_API_SECRET.length())) + "..." : "[null]",
+ CLIENT_API_SECRET != null ? CLIENT_API_SECRET.length() : 0,
+ payload != null && payload.length() > 200 ? payload.substring(0, 200) + "..." : payload
+ ));
+
V2Envelope envelope = v2CreateEnvelope(payload, CLIENT_API_SECRET);
- String encryptedResponse = HttpClient.post(getBaseUrl() + "/v3/identity/map", envelope.envelope(), CLIENT_API_KEY);
- return v2DecryptEncryptedResponse(encryptedResponse, envelope.nonce(), CLIENT_API_SECRET);
+
+ LOGGER.info(() -> String.format(
+ "[v3IdentityMap] Created envelope:%n" +
+ " Envelope length: %d%n" +
+ " Nonce length: %d",
+ envelope.envelope().length(),
+ envelope.nonce().length
+ ));
+
+ try {
+ String encryptedResponse = HttpClient.post(endpoint, envelope.envelope(), CLIENT_API_KEY);
+ LOGGER.info(() -> String.format(
+ "[v3IdentityMap] Request successful, response length: %d",
+ encryptedResponse != null ? encryptedResponse.length() : 0
+ ));
+ return v2DecryptEncryptedResponse(encryptedResponse, envelope.nonce(), CLIENT_API_SECRET);
+ } catch (Exception e) {
+ final String errorMsg = e.getMessage();
+ final String errorType = e.getClass().getName();
+ LOGGER.error(() -> String.format(
+ "[v3IdentityMap] Request failed:%n" +
+ " Endpoint: %s%n" +
+ " Error: %s%n" +
+ " Error Type: %s",
+ endpoint, errorMsg, errorType
+ ));
+ throw e;
+ }
}
public IdentityMapV3Response v3IdentityMap(IdentityMapV3Input input) {
diff --git a/src/test/java/common/HttpClient.java b/src/test/java/common/HttpClient.java
index 47ab050..94c29d3 100644
--- a/src/test/java/common/HttpClient.java
+++ b/src/test/java/common/HttpClient.java
@@ -5,11 +5,13 @@
import com.uid2.shared.util.Mapper;
import lombok.Getter;
import okhttp3.*;
+import org.junit.platform.commons.logging.Logger;
+import org.junit.platform.commons.logging.LoggerFactory;
import java.util.Map;
-import java.util.Objects;
public final class HttpClient {
+ private static final Logger LOGGER = LoggerFactory.getLogger(HttpClient.class);
private static final ObjectMapper OBJECT_MAPPER = Mapper.getInstance();
public static final OkHttpClient RAW_CLIENT = new OkHttpClient();
@@ -71,13 +73,95 @@ public static String post(String url, String body, String bearerToken) throws Ex
}
public static String execute(Request request, HttpMethod method) throws Exception {
+ final String url = request.url().toString();
+ final String requestBodyForLog = extractRequestBodyForLog(request);
+ final String authHeader = extractAuthHeader(request);
+ final Headers headers = request.headers();
+
+ LOGGER.info(() -> String.format(
+ "[HTTP REQUEST] %s %s%n" +
+ " Authorization: %s%n" +
+ " Request Body: %s%n" +
+ " Headers: %s",
+ method, url,
+ authHeader,
+ requestBodyForLog,
+ headers.toString()
+ ));
+
try (Response response = RAW_CLIENT.newCall(request).execute()) {
+ final ResponseBody body = response.body();
+ // Read the FULL response body - don't truncate the actual data!
+ final String fullResponseBody = (body != null) ? body.string() : "";
+ final int statusCode = response.code();
+ final String statusMessage = response.message();
+ final Headers responseHeaders = response.headers();
+
+ // Only truncate for logging display
+ final String responseBodyForLog = truncateForLog(fullResponseBody, 1000);
+
+ LOGGER.info(() -> String.format(
+ "[HTTP RESPONSE] %s %s%n" +
+ " Status: %d %s%n" +
+ " Response Headers: %s%n" +
+ " Response Body: %s",
+ method, url,
+ statusCode, statusMessage,
+ responseHeaders.toString(),
+ responseBodyForLog
+ ));
+
if (!response.isSuccessful()) {
- throw new HttpException(method, request.url().toString(), response.code(), response.message(), Objects.requireNonNull(response.body()).string());
+ LOGGER.error(() -> String.format(
+ "[HTTP ERROR] Request failed: %s %s - Status: %d %s - Response: %s",
+ method, url, statusCode, statusMessage, responseBodyForLog
+ ));
+ throw new HttpException(method, url, statusCode, statusMessage, fullResponseBody);
}
- return Objects.requireNonNull(response.body()).string();
+ // Return the FULL response body, not truncated
+ return fullResponseBody;
}
}
+
+ private static String truncateForLog(String text, int maxLength) {
+ if (text == null || text.isEmpty()) {
+ return "[empty]";
+ }
+ if (text.length() > maxLength) {
+ return text.substring(0, maxLength) + "... [truncated, total length=" + text.length() + "]";
+ }
+ return text;
+ }
+
+ private static String extractRequestBodyForLog(Request request) {
+ if (request.body() == null) {
+ return "[empty]";
+ }
+ try {
+ okio.Buffer buffer = new okio.Buffer();
+ request.body().writeTo(buffer);
+ String body = buffer.readUtf8();
+ return truncateForLog(body, 500);
+ } catch (Exception e) {
+ return "[unable to read request body: " + e.getMessage() + "]";
+ }
+ }
+
+ private static String extractAuthHeader(Request request) {
+ Headers headers = request.headers();
+ for (int i = 0; i < headers.size(); i++) {
+ if ("Authorization".equalsIgnoreCase(headers.name(i))) {
+ String authValue = headers.value(i);
+ // Mask the token for security, but show first/last few chars
+ if (authValue.length() > 20) {
+ return authValue.substring(0, 10) + "..." + authValue.substring(authValue.length() - 10);
+ } else {
+ return "[masked]";
+ }
+ }
+ }
+ return "[none]";
+ }
private static Request buildRequest(HttpMethod method, String url, String body, String bearerToken) {
return buildRequest(method, url, body, bearerToken, null);