Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import ca.bc.gov.educ.api.gradbusiness.util.EducGradBusinessApiConstants;
import ca.bc.gov.educ.api.gradbusiness.util.LogHelper;
import ca.bc.gov.educ.api.gradbusiness.util.ThreadLocalStateUtil;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.springframework.beans.factory.annotation.Autowired;
Expand All @@ -12,6 +13,7 @@
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.time.Instant;
import java.util.UUID;

@Component
@Slf4j
Expand All @@ -31,6 +33,7 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons
final long startTime = Instant.now().toEpochMilli();
request.setAttribute("startTime", startTime);
}
ThreadLocalStateUtil.setCorrelationID(UUID.randomUUID().toString());
return true;
}

Expand All @@ -45,10 +48,7 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons
@Override
public void afterCompletion(@NonNull final HttpServletRequest request, final HttpServletResponse response, @NonNull final Object handler, final Exception ex) {
LogHelper.logServerHttpReqResponseDetails(request, response, constants.isSplunkLogHelperEnabled());
val correlationID = request.getHeader(EducGradBusinessApiConstants.CORRELATION_ID);
if (correlationID != null) {
response.setHeader(EducGradBusinessApiConstants.CORRELATION_ID, request.getHeader(EducGradBusinessApiConstants.CORRELATION_ID));
}
ThreadLocalStateUtil.clear();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import ca.bc.gov.educ.api.gradbusiness.util.EducGradBusinessApiConstants;
import ca.bc.gov.educ.api.gradbusiness.util.LogHelper;
import ca.bc.gov.educ.api.gradbusiness.util.ThreadLocalStateUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
Expand All @@ -13,9 +14,13 @@
import org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizedClientManager;
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
import org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction;
import org.springframework.web.reactive.function.client.ClientRequest;
import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
import org.springframework.web.reactive.function.client.ExchangeStrategies;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

import java.util.UUID;

@Configuration
@Profile("!test")
Expand Down Expand Up @@ -43,6 +48,7 @@ public WebClient getGraduationClientWebClient(OAuth2AuthorizedClientManager auth
.maxInMemorySize(50 * 1024 * 1024))
.build())
.apply(filter.oauth2Configuration())
.filter(setRequestHeaders())
.filter(this.log())
.build();
}
Expand All @@ -59,6 +65,16 @@ public OAuth2AuthorizedClientManager authorizedClientManager(
return authorizedClientManager;
}

private ExchangeFilterFunction setRequestHeaders() {
return (clientRequest, next) -> {
ClientRequest modifiedRequest = ClientRequest.from(clientRequest)
.header(EducGradBusinessApiConstants.CORRELATION_ID, ThreadLocalStateUtil.getCorrelationID())
.header(EducGradBusinessApiConstants.USER_NAME, ThreadLocalStateUtil.getCurrentUser())
.header(EducGradBusinessApiConstants.REQUEST_SOURCE, EducGradBusinessApiConstants.API_NAME)
.build();
return next.exchange(modifiedRequest);
};
}
/**
* Old web client. You can use a @Qualifier('default') to summon it.
*/
Expand All @@ -69,6 +85,7 @@ public WebClient webClient() {
.defaultCodecs()
.maxInMemorySize(300 * 1024 * 1024)) // 300MB
.build())
.filter(setRequestHeaders())
.filter(this.log())
.build();
}
Expand All @@ -81,6 +98,7 @@ private ExchangeFilterFunction log() {
clientRequest.url().toString(),
clientResponse.statusCode().value(),
clientRequest.headers().get(EducGradBusinessApiConstants.CORRELATION_ID),
clientRequest.headers().get(EducGradBusinessApiConstants.REQUEST_SOURCE),
constants.isSplunkLogHelperEnabled())
));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ public <T> T get(String url, Class<T> clazz) {
obj = graduationServiceWebClient
.get()
.uri(url)
.headers(h -> h.set(EducGradBusinessApiConstants.CORRELATION_ID, ThreadLocalStateUtil.getCorrelationID()))
.retrieve()
// if 5xx errors, throw Service error
.onStatus(HttpStatusCode::is5xxServerError,
Expand Down Expand Up @@ -63,7 +62,6 @@ public <T> T post(String url, Object body, Class<T> clazz) {
try {
obj = graduationServiceWebClient.post()
.uri(url)
.headers(h -> h.set(EducGradBusinessApiConstants.CORRELATION_ID, ThreadLocalStateUtil.getCorrelationID()))
.body(BodyInserters.fromValue(body))
.retrieve()
.onStatus(HttpStatusCode::is5xxServerError,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ public class EducGradBusinessApiConstants {
public static final String GRAD_STUDENT_API = "GRAD-STUDENT-API";
public static final String STREAM_NAME="GRAD_STATUS_EVENTS";
public static final String CORRELATION_ID = "correlationID";
public static final String USER_NAME = "User-Name";
public static final String REQUEST_SOURCE = "Request-Source";
public static final String API_NAME = "EDUC-GRAD-BUSINESS-API";

//API end-point Mapping constants
public static final String API_ROOT_MAPPING = "";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ public static void logServerHttpReqResponseDetails(@NonNull final HttpServletReq
if (correlationID != null) {
httpMap.put("correlation_id", correlationID);
}
val requestSource = request.getHeader(EducGradBusinessApiConstants.REQUEST_SOURCE);
if (requestSource != null) {
httpMap.put("request_source", requestSource);
}
httpMap.put("server_http_request_url", String.valueOf(request.getRequestURL()));
httpMap.put("server_http_request_processing_time_ms", totalTime);
httpMap.put("server_http_request_payload", String.valueOf(request.getAttribute("payload")));
Expand All @@ -61,7 +65,7 @@ public static void logServerHttpReqResponseDetails(@NonNull final HttpServletReq
* @param responseCode
* @param correlationID
*/
public static void logClientHttpReqResponseDetails(@NonNull final HttpMethod method, final String url, final int responseCode, final List<String> correlationID, final boolean logging) {
public static void logClientHttpReqResponseDetails(@NonNull final HttpMethod method, final String url, final int responseCode, final List<String> correlationID, final List<String> requestSource, final boolean logging) {
if (!logging) return;
try {
final Map<String, Object> httpMap = new HashMap<>();
Expand All @@ -71,6 +75,9 @@ public static void logClientHttpReqResponseDetails(@NonNull final HttpMethod met
if (correlationID != null) {
httpMap.put("correlation_id", String.join(",", correlationID));
}
if (requestSource != null) {
httpMap.put("requestSource", String.join(",", requestSource));
}
MDC.putCloseable("httpEvent", mapper.writeValueAsString(httpMap));
log.info("");
MDC.clear();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,36 @@
package ca.bc.gov.educ.api.gradbusiness.util;

public class ThreadLocalStateUtil {
private static ThreadLocal<String> transaction = new ThreadLocal<>();
private static InheritableThreadLocal<String> transaction = new InheritableThreadLocal<>();
private static final InheritableThreadLocal<String> user = new InheritableThreadLocal<String>();
private static final InheritableThreadLocal<String> requestSource = new InheritableThreadLocal<String>();

public static void setCorrelationID(String correlationID){
transaction.set(correlationID);
}
public static String getCorrelationID() {
return transaction.get();
}

public static String getCurrentUser() {
return user.get();
}

public static void setCurrentUser(String username) {
user.set(username);
}

public static void setRequestSource(String reqSource){
requestSource.set(reqSource);
}
public static String getRequestSource() {
return requestSource.get();
}

public static void clear() {
transaction.remove();
user.remove();
requestSource.remove();
}

}
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package ca.bc.gov.educ.api.gradbusiness.service;

import ca.bc.gov.educ.api.gradbusiness.exception.ServiceException;
import ca.bc.gov.educ.api.gradbusiness.util.ThreadLocalStateUtil;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
Expand Down Expand Up @@ -58,10 +60,12 @@ public class RESTServicePOSTTest {

@Before
public void setUp(){
Mockito.reset(webClient, graduationClientWebClient, responseMock, requestHeadersMock, requestBodyMock, requestBodyUriMock);
ThreadLocalStateUtil.clear();
when(this.webClient.post()).thenReturn(this.requestBodyUriMock);
when(this.graduationClientWebClient.post()).thenReturn(this.requestBodyUriMock);
when(this.requestBodyUriMock.uri(any(String.class))).thenReturn(this.requestBodyUriMock);
when(this.requestBodyUriMock.headers(any(Consumer.class))).thenReturn(this.requestBodyMock);
when(this.requestBodyUriMock.uri(any(String.class))).thenReturn(this.requestBodyMock);
when(this.requestBodyMock.headers(any(Consumer.class))).thenReturn(this.requestBodyMock);
when(this.requestBodyMock.contentType(any())).thenReturn(this.requestBodyMock);
when(this.requestBodyMock.body(any(BodyInserter.class))).thenReturn(this.requestHeadersMock);
when(this.requestHeadersMock.retrieve()).thenReturn(this.responseMock);
Expand All @@ -70,6 +74,8 @@ public void setUp(){

@Test
public void testPostOverride_GivenProperData_Expect200Response(){
ThreadLocalStateUtil.setCorrelationID("test-correlation-id");
ThreadLocalStateUtil.setCurrentUser("test-user");
when(this.responseMock.onStatus(any(), any())).thenReturn(this.responseMock);
byte[] response = this.restService.post(TEST_URL, TEST_BODY, byte[].class);
Assert.assertArrayEquals(TEST_BYTES, response);
Expand Down
Loading