From e8f660aea1f03c94444f4495603b9ba1c87bc437 Mon Sep 17 00:00:00 2001 From: Serhii Vydiuk Date: Mon, 23 Dec 2024 23:43:49 +0200 Subject: [PATCH] First attemt to send really collected data to the server --- .../CustomUserDataCollectorConfig.java | 1 - .../pom.xml | 2 +- .../DataCollectionAutoConfiguration.java | 26 ++++++++++++++---- .../datacollection/DataCollectionFilter.java | 12 ++++----- .../ServletRequestDataCollector.java | 27 ++++++++++++++----- .../userinfo/ServletUserDataCollector.java | 10 +++---- .../userinfo/ServletUserDataExtractor.java | 2 +- .../DataCollectionFilterTest.java | 6 ++--- .../ServletUserDataCollectorTest.java | 6 ++--- .../dataextraction/payload/ApiKeyMasker.java | 26 ++++++++++++++++++ .../readme/datatransfer/HttpDataSender.java | 4 +-- .../datatransfer/HttpDataSenderTest.java | 16 +---------- 12 files changed, 88 insertions(+), 50 deletions(-) create mode 100644 packages/java/readme-metrics/src/main/java/com/readme/dataextraction/payload/ApiKeyMasker.java diff --git a/packages/java/examples/OwlTestApp/src/main/java/com/readme/example/CustomUserDataCollectorConfig.java b/packages/java/examples/OwlTestApp/src/main/java/com/readme/example/CustomUserDataCollectorConfig.java index aee161660..f971870b9 100644 --- a/packages/java/examples/OwlTestApp/src/main/java/com/readme/example/CustomUserDataCollectorConfig.java +++ b/packages/java/examples/OwlTestApp/src/main/java/com/readme/example/CustomUserDataCollectorConfig.java @@ -1,6 +1,5 @@ package com.readme.example; -import com.readme.dataextraction.UserDataCollector; import org.springframework.context.annotation.Configuration; /** diff --git a/packages/java/readme-metrics-spring-boot-starter/pom.xml b/packages/java/readme-metrics-spring-boot-starter/pom.xml index 377871aff..ba743b364 100644 --- a/packages/java/readme-metrics-spring-boot-starter/pom.xml +++ b/packages/java/readme-metrics-spring-boot-starter/pom.xml @@ -32,7 +32,7 @@ com.readme - readme-metrics + metrics-core ${readme-metrics.version} diff --git a/packages/java/readme-metrics-spring-boot-starter/src/main/java/com/readme/starter/config/DataCollectionAutoConfiguration.java b/packages/java/readme-metrics-spring-boot-starter/src/main/java/com/readme/starter/config/DataCollectionAutoConfiguration.java index 9c26311a3..f09f97ab6 100644 --- a/packages/java/readme-metrics-spring-boot-starter/src/main/java/com/readme/starter/config/DataCollectionAutoConfiguration.java +++ b/packages/java/readme-metrics-spring-boot-starter/src/main/java/com/readme/starter/config/DataCollectionAutoConfiguration.java @@ -1,15 +1,18 @@ package com.readme.starter.config; -import com.readme.dataextraction.RequestDataCollector; -import com.readme.dataextraction.UserDataCollector; -import com.readme.dataextraction.UserDataExtractor; +import com.readme.config.CoreConfig; + +import com.readme.dataextraction.payload.RequestDataCollector; +import com.readme.dataextraction.user.UserDataCollector; +import com.readme.dataextraction.user.UserDataExtractor; +import com.readme.datatransfer.DataSender; +import com.readme.datatransfer.HttpDataSender; import com.readme.starter.datacollection.DataCollectionFilter; import com.readme.starter.datacollection.ServletDataPayloadAdapter; import com.readme.starter.datacollection.userinfo.ServletUserDataCollector; import lombok.AllArgsConstructor; -import lombok.extern.log4j.Log4j; import lombok.extern.slf4j.Slf4j; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import okhttp3.OkHttpClient; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; @@ -34,6 +37,8 @@ @Slf4j public class DataCollectionAutoConfiguration { + private ReadmeConfigurationProperties readmeProperties; + @Bean public FilterRegistrationBean metricsFilter( RequestDataCollector requestDataCollector, @@ -52,4 +57,15 @@ public UserDataCollector userDataCollector(UserDataPr log.info("readme-metrics: Creating of default user data collector"); return new ServletUserDataCollector(userDataProperties, extractionService); } + + @Bean + public DataSender dataSender() { + String readmeApiKey = readmeProperties.getReadmeApiKey(); + CoreConfig coreConfig = CoreConfig.builder() + .readmeAPIKey(readmeApiKey) + .build(); + OkHttpClient okHttpClient = new OkHttpClient(); + + return new HttpDataSender(okHttpClient, coreConfig); + } } diff --git a/packages/java/readme-metrics-spring-boot-starter/src/main/java/com/readme/starter/datacollection/DataCollectionFilter.java b/packages/java/readme-metrics-spring-boot-starter/src/main/java/com/readme/starter/datacollection/DataCollectionFilter.java index 85a8f5afd..0adfcb69e 100644 --- a/packages/java/readme-metrics-spring-boot-starter/src/main/java/com/readme/starter/datacollection/DataCollectionFilter.java +++ b/packages/java/readme-metrics-spring-boot-starter/src/main/java/com/readme/starter/datacollection/DataCollectionFilter.java @@ -1,20 +1,18 @@ package com.readme.starter.datacollection; -import com.readme.dataextraction.RequestDataCollector; -import com.readme.dataextraction.UserDataCollector; -import com.readme.domain.UserData; + +import com.readme.dataextraction.payload.RequestDataCollector; +import com.readme.dataextraction.user.UserData; +import com.readme.dataextraction.user.UserDataCollector; import jakarta.servlet.*; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.util.StreamUtils; import org.springframework.web.util.ContentCachingRequestWrapper; import org.springframework.web.util.ContentCachingResponseWrapper; - import java.io.IOException; -import static org.springframework.http.HttpMethod.GET; import static org.springframework.http.HttpMethod.OPTIONS; @@ -43,7 +41,7 @@ public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain } else { //TODO: Handle case if SDK user configured getting request user data from body, but GET req doesn't have it //TODO: Validate user data. Collect request data only if user data is valid ? - + //TODO: Does it make sense to collect everything except body before chain execution?.... chain.doFilter(request, response); ServletDataPayloadAdapter payload = new ServletDataPayloadAdapter(request, response); diff --git a/packages/java/readme-metrics-spring-boot-starter/src/main/java/com/readme/starter/datacollection/ServletRequestDataCollector.java b/packages/java/readme-metrics-spring-boot-starter/src/main/java/com/readme/starter/datacollection/ServletRequestDataCollector.java index 07f9def30..6a351f797 100644 --- a/packages/java/readme-metrics-spring-boot-starter/src/main/java/com/readme/starter/datacollection/ServletRequestDataCollector.java +++ b/packages/java/readme-metrics-spring-boot-starter/src/main/java/com/readme/starter/datacollection/ServletRequestDataCollector.java @@ -1,24 +1,39 @@ package com.readme.starter.datacollection; -import com.readme.dataextraction.RequestDataCollector; -import com.readme.domain.UserData; -import com.readme.starter.config.ReadmeConfigurationProperties; + +import com.readme.dataextraction.payload.ApiKeyMasker; +import com.readme.dataextraction.payload.PayloadData; +import com.readme.dataextraction.payload.RequestDataCollector; +import com.readme.dataextraction.user.UserData; +import com.readme.datatransfer.DataSender; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; +import java.util.UUID; + +import static com.readme.dataextraction.payload.ApiKeyMasker.*; + @Slf4j @AllArgsConstructor @Component public class ServletRequestDataCollector implements RequestDataCollector { - private ReadmeConfigurationProperties readmeProperties; + private DataSender dataSender; @Override public void collect(ServletDataPayloadAdapter dataPayload, UserData userData) { - String readmeAPIKey = readmeProperties.getReadmeApiKey(); + String maskedApiKey = mask(userData.getApiKey()); + PayloadData payloadData = PayloadData.builder() + .apiKey(maskedApiKey) + .email(userData.getEmail()) + .label(userData.getLabel()) + .logId(UUID.randomUUID()) + .requestBody(dataPayload.getRequestBody()) + .build(); - log.info(">>>>>>>> Sending data to the server with key {}", readmeAPIKey); + dataSender.send(payloadData); + log.info(">>>>>>>> Sending data to the server..."); log.info(">>>>>>>> and user data: {}", userData); } } diff --git a/packages/java/readme-metrics-spring-boot-starter/src/main/java/com/readme/starter/datacollection/userinfo/ServletUserDataCollector.java b/packages/java/readme-metrics-spring-boot-starter/src/main/java/com/readme/starter/datacollection/userinfo/ServletUserDataCollector.java index 3f1ef68f5..64bde0c7d 100644 --- a/packages/java/readme-metrics-spring-boot-starter/src/main/java/com/readme/starter/datacollection/userinfo/ServletUserDataCollector.java +++ b/packages/java/readme-metrics-spring-boot-starter/src/main/java/com/readme/starter/datacollection/userinfo/ServletUserDataCollector.java @@ -1,16 +1,14 @@ package com.readme.starter.datacollection.userinfo; import com.readme.config.FieldMapping; -import com.readme.dataextraction.UserDataCollector; -import com.readme.dataextraction.UserDataExtractor; -import com.readme.dataextraction.UserDataSource; -import com.readme.domain.UserData; +import com.readme.dataextraction.user.UserData; +import com.readme.dataextraction.user.UserDataCollector; +import com.readme.dataextraction.user.UserDataExtractor; +import com.readme.dataextraction.user.UserDataSource; import com.readme.starter.config.UserDataProperties; import com.readme.starter.datacollection.ServletDataPayloadAdapter; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.stereotype.Component; /** * Responsible for selecting the appropriate {@link UserDataExtractor} diff --git a/packages/java/readme-metrics-spring-boot-starter/src/main/java/com/readme/starter/datacollection/userinfo/ServletUserDataExtractor.java b/packages/java/readme-metrics-spring-boot-starter/src/main/java/com/readme/starter/datacollection/userinfo/ServletUserDataExtractor.java index cffa75e1c..82824c2c6 100644 --- a/packages/java/readme-metrics-spring-boot-starter/src/main/java/com/readme/starter/datacollection/userinfo/ServletUserDataExtractor.java +++ b/packages/java/readme-metrics-spring-boot-starter/src/main/java/com/readme/starter/datacollection/userinfo/ServletUserDataExtractor.java @@ -3,7 +3,7 @@ import com.auth0.jwt.interfaces.DecodedJWT; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import com.readme.dataextraction.UserDataExtractor; +import com.readme.dataextraction.user.UserDataExtractor; import com.readme.starter.datacollection.ServletDataPayloadAdapter; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; diff --git a/packages/java/readme-metrics-spring-boot-starter/src/test/java/com/readme/starter/datacollection/DataCollectionFilterTest.java b/packages/java/readme-metrics-spring-boot-starter/src/test/java/com/readme/starter/datacollection/DataCollectionFilterTest.java index 5a2bc8cee..2855533f5 100644 --- a/packages/java/readme-metrics-spring-boot-starter/src/test/java/com/readme/starter/datacollection/DataCollectionFilterTest.java +++ b/packages/java/readme-metrics-spring-boot-starter/src/test/java/com/readme/starter/datacollection/DataCollectionFilterTest.java @@ -1,8 +1,8 @@ package com.readme.starter.datacollection; -import com.readme.dataextraction.RequestDataCollector; -import com.readme.dataextraction.UserDataCollector; -import com.readme.domain.UserData; +import com.readme.dataextraction.payload.RequestDataCollector; +import com.readme.dataextraction.user.UserData; +import com.readme.dataextraction.user.UserDataCollector; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; diff --git a/packages/java/readme-metrics-spring-boot-starter/src/test/java/com/readme/starter/datacollection/userinfo/ServletUserDataCollectorTest.java b/packages/java/readme-metrics-spring-boot-starter/src/test/java/com/readme/starter/datacollection/userinfo/ServletUserDataCollectorTest.java index 0aa639b1b..0813e95a3 100644 --- a/packages/java/readme-metrics-spring-boot-starter/src/test/java/com/readme/starter/datacollection/userinfo/ServletUserDataCollectorTest.java +++ b/packages/java/readme-metrics-spring-boot-starter/src/test/java/com/readme/starter/datacollection/userinfo/ServletUserDataCollectorTest.java @@ -1,9 +1,9 @@ package com.readme.starter.datacollection.userinfo; import com.readme.config.FieldMapping; -import com.readme.dataextraction.UserDataExtractor; -import com.readme.dataextraction.UserDataSource; -import com.readme.domain.UserData; +import com.readme.dataextraction.user.UserData; +import com.readme.dataextraction.user.UserDataExtractor; +import com.readme.dataextraction.user.UserDataSource; import com.readme.starter.config.UserDataProperties; import com.readme.starter.datacollection.ServletDataPayloadAdapter; import org.junit.jupiter.api.BeforeEach; diff --git a/packages/java/readme-metrics/src/main/java/com/readme/dataextraction/payload/ApiKeyMasker.java b/packages/java/readme-metrics/src/main/java/com/readme/dataextraction/payload/ApiKeyMasker.java new file mode 100644 index 000000000..da444ae22 --- /dev/null +++ b/packages/java/readme-metrics/src/main/java/com/readme/dataextraction/payload/ApiKeyMasker.java @@ -0,0 +1,26 @@ +package com.readme.dataextraction.payload; + +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Base64; + +public class ApiKeyMasker { + + public static String mask(String apiKey) { + try { + String base64Hash = Base64.getEncoder() + .encodeToString(MessageDigest + .getInstance("SHA-512") + .digest(apiKey.getBytes(StandardCharsets.UTF_8))); + + String last4Digits = apiKey.substring(apiKey.length() - 4); + return "sha512-" + base64Hash + "?" + last4Digits; + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException("SHA-512 algorithm not available", e); + } catch (StringIndexOutOfBoundsException e) { + throw new IllegalArgumentException("API key must be at least 4 characters long", e); + } + } + +} \ No newline at end of file diff --git a/packages/java/readme-metrics/src/main/java/com/readme/datatransfer/HttpDataSender.java b/packages/java/readme-metrics/src/main/java/com/readme/datatransfer/HttpDataSender.java index c34a5f5d4..9afbf5045 100644 --- a/packages/java/readme-metrics/src/main/java/com/readme/datatransfer/HttpDataSender.java +++ b/packages/java/readme-metrics/src/main/java/com/readme/datatransfer/HttpDataSender.java @@ -31,7 +31,7 @@ public HttpDataSender(OkHttpClient client, CoreConfig coreConfig) { @Override public int send(PayloadData payloadData) { - if (payloadData != null && payloadData.getBody() != null && !payloadData.getBody().isEmpty()) { + if (payloadData != null) { String encodedReadmeApiKey = getEncodedReadmeApiKey(); Request request = createRequest(payloadData, encodedReadmeApiKey); @@ -45,7 +45,7 @@ public int send(PayloadData payloadData) { } private static Request createRequest(PayloadData payloadData, String encodedReadmeApiKey) { - RequestBody body = RequestBody.create(payloadData.getBody(), MediaType.get(APPLICATION_JSON_TYPE)); + RequestBody body = RequestBody.create(payloadData.getRequestBody(), MediaType.get(APPLICATION_JSON_TYPE)); return new Request.Builder() .url(README_METRICS_URL) .header("Accept", APPLICATION_JSON_TYPE) diff --git a/packages/java/readme-metrics/src/test/java/com/readme/datatransfer/HttpDataSenderTest.java b/packages/java/readme-metrics/src/test/java/com/readme/datatransfer/HttpDataSenderTest.java index 2e40d87eb..ae43a9814 100644 --- a/packages/java/readme-metrics/src/test/java/com/readme/datatransfer/HttpDataSenderTest.java +++ b/packages/java/readme-metrics/src/test/java/com/readme/datatransfer/HttpDataSenderTest.java @@ -29,20 +29,6 @@ public void testSendOnSuccess() throws IOException { assertEquals(200, httpDataSender.send(payloadData)); } - @Test - public void testSendOnBodyDoesntExist() throws IOException { - OkHttpClient client = mock(OkHttpClient.class); - Call call = mock(Call.class); - Response response = mockResponse(); - PayloadData payloadData = PayloadData.builder().build(); - - when(client.newCall(any(Request.class))).thenReturn(call); - when(call.execute()).thenReturn(response); - HttpDataSender httpDataSender = new HttpDataSender(client, mockCoreConfig()); - - assertThrows(EmptyRequestBodyException.class, () -> httpDataSender.send(payloadData)); - } - @NotNull private static Response mockResponse() { return new Response.Builder() @@ -58,7 +44,7 @@ private static Response mockResponse() { private static PayloadData mockRequestMetadata() { return PayloadData.builder() - .body("body") + .requestBody("body") .build(); }