2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017 - 2019 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * ============LICENSE_END=========================================================
21 package org.onap.vid.controller.filter;
23 import static org.apache.commons.lang3.StringUtils.equalsIgnoreCase;
24 import static org.hamcrest.CoreMatchers.equalTo;
25 import static org.hamcrest.CoreMatchers.is;
26 import static org.hamcrest.MatcherAssert.assertThat;
27 import static org.hamcrest.Matchers.anyOf;
28 import static org.hamcrest.Matchers.emptyOrNullString;
29 import static org.hamcrest.Matchers.equalToIgnoringCase;
30 import static org.hamcrest.Matchers.hasItems;
31 import static org.hamcrest.Matchers.not;
32 import static org.mockito.ArgumentMatchers.any;
33 import static org.mockito.ArgumentMatchers.argThat;
34 import static org.onap.portalsdk.core.util.SystemProperties.ECOMP_REQUEST_ID;
36 import com.google.common.collect.ImmutableMap;
37 import java.io.IOException;
38 import java.util.Arrays;
39 import java.util.Collection;
40 import java.util.Collections;
41 import java.util.Enumeration;
43 import java.util.UUID;
44 import java.util.function.Function;
45 import javax.servlet.FilterChain;
46 import javax.servlet.ServletException;
47 import javax.servlet.ServletRequest;
48 import javax.servlet.ServletResponse;
49 import javax.servlet.http.HttpServletRequest;
50 import javax.servlet.http.HttpServletResponse;
51 import org.mockito.ArgumentCaptor;
52 import org.mockito.Mockito;
53 import org.mockito.stubbing.Answer;
54 import org.onap.logging.ref.slf4j.ONAPLogConstants.MDCs;
55 import org.onap.portalsdk.core.web.support.UserUtils;
56 import org.onap.vid.logging.RequestIdHeader;
58 import org.springframework.mock.web.MockHttpServletResponse;
59 import org.testng.annotations.AfterMethod;
60 import org.testng.annotations.DataProvider;
61 import org.testng.annotations.Test;
64 public class PromiseRequestIdFilterTest {
66 private final String anotherHeader = "ANDREI_RUBLEV";
67 private final String anotherValue = "foo value";
68 private final String mixedCaseHeader = "x-ecomp-REQUESTID";
70 private static final String onapRequestIdHeader = "x-onap-requestid";
71 private static final String transactionIdHeader = "x-transactionid";
72 private static final String requestIdHeader = "x-requestid";
74 private final PromiseRequestIdFilter promiseRequestIdFilter = new PromiseRequestIdFilter();
77 public void tearDown() {
78 MDC.remove(MDCs.REQUEST_ID);
82 public void givenMdcValueAndRequestIdHeader_headerValueNotChanged() throws IOException, ServletException {
84 final String someTxId = "863850e2-8545-4efd-94b8-afba5f52b3d5";
85 final String mdcTxId = "ed752ff1-3970-4f18-8219-2d821fa4eaea";
87 MDC.put(MDCs.REQUEST_ID, mdcTxId);
89 final ImmutableMap<String, String> incomingRequestHeaders = ImmutableMap.of(
90 anotherHeader, anotherValue,
91 ECOMP_REQUEST_ID, someTxId
94 buildRequestThenRunThroughFilterAndAssertResultRequestHeaders(incomingRequestHeaders, specificTxId(someTxId));
99 public void givenRequestIdHeaderThatIsNotAUUID_headerValueChanged() throws IOException, ServletException {
101 final String someTxId = "863850e28544efd94b8afba5f52b3d5";
103 final ImmutableMap<String, String> incomingRequestHeaders = ImmutableMap.of(
104 anotherHeader, anotherValue,
105 ECOMP_REQUEST_ID, someTxId
108 buildRequestThenRunThroughFilterAndAssertResultRequestHeaders(incomingRequestHeaders, UserUtils::getRequestId);
113 public void givenMixedCaseRequestIdHeader_headerValueNotChanged() throws IOException, ServletException {
115 final String someTxId = "729bbd8d-b0c2-4809-a794-dcccd9cda2c0";
117 final ImmutableMap<String, String> incomingRequestHeaders = ImmutableMap.of(
118 mixedCaseHeader, someTxId,
119 anotherHeader, anotherValue
122 buildRequestThenRunThroughFilterAndAssertResultRequestHeaders(incomingRequestHeaders, specificTxId(someTxId));
126 public void givenNoRequestIdHeader_headerValueWasGenerated() throws IOException, ServletException {
128 final ImmutableMap<String, String> incomingRequestHeaders = ImmutableMap.of(
129 anotherHeader, anotherValue
132 buildRequestThenRunThroughFilterAndAssertResultRequestHeaders(incomingRequestHeaders, UserUtils::getRequestId);
136 public void givenMdcValueAndNoRequestIdHeader_headerValueWasFromMDC() throws IOException, ServletException {
138 final String mdcTxId = "ed752ff1-3970-4f18-8219-2d821fa4eaea";
140 MDC.put(MDCs.REQUEST_ID, mdcTxId);
142 final ImmutableMap<String, String> incomingRequestHeaders = ImmutableMap.of(
143 anotherHeader, anotherValue
146 buildRequestThenRunThroughFilterAndAssertResultRequestHeaders(incomingRequestHeaders, specificTxId(mdcTxId));
150 public void givenTwoRequestIdHeader_onapHeaderValueIsUsed() throws IOException, ServletException {
152 final String onapTxId = "863850e2-8545-4efd-94b8-AFBA5F52B3D5"; // note mixed case
153 final String ecompTxId = "6e8ff89e-88a4-4977-b63f-3142892b6e08";
155 final ImmutableMap<String, String> incomingRequestHeaders = ImmutableMap.of(
156 anotherHeader, anotherValue,
157 ECOMP_REQUEST_ID, ecompTxId,
158 onapRequestIdHeader, onapTxId
161 buildRequestThenRunThroughFilterAndAssertResultRequestHeaders(incomingRequestHeaders, specificTxId(onapTxId));
165 public void givenTwoRequestIdHeaderAndHigherPriorityIsMalformed_headerValueIsGenerated() throws IOException, ServletException {
167 final String malformedTxId = "6e8ff89e-88a4-4977-b63f-3142892b6e08-";
168 final String anotherTxId = "863850e2-8545-4efd-94b8-afba5f52b3d5";
170 final ImmutableMap<String, String> incomingRequestHeaders = ImmutableMap.of(
171 anotherHeader, anotherValue,
172 requestIdHeader, malformedTxId, // requestIdHeader as higher priority than transactionIdHeader
173 transactionIdHeader, anotherTxId
176 HttpServletRequest wrappedRequest =
177 buildRequestThenRunThroughFilterAndAssertResultRequestHeaders(incomingRequestHeaders, UserUtils::getRequestId);
179 assertThat(UserUtils.getRequestId(wrappedRequest),
180 not(anyOf(equalTo(malformedTxId), equalTo(anotherTxId)))
186 public void toUuidOrElse_givenValid_yieldSame() {
187 final String someTxId = "729bbd8d-b0c2-4809-a794-DCCCD9CDA2C0"; // note mixed case
188 UUID unexpected = UUID.randomUUID();
189 assertThat(promiseRequestIdFilter.toUuidOrElse(someTxId, () -> unexpected), is(UUID.fromString(someTxId)));
193 public void toUuidOrElse_givenNull_yieldSupplier() {
194 UUID expected = UUID.fromString("729bbd8d-b0c2-4809-a794-dcccd9cda2c0");
195 assertThat(promiseRequestIdFilter.toUuidOrElse(null, () -> expected), is(expected));
199 public void toUuidOrElse_givenMalformed_yieldSupplier() {
200 UUID expected = UUID.fromString("729bbd8d-b0c2-4809-a794-dcccd9cda2c0");
201 assertThat(promiseRequestIdFilter.toUuidOrElse("malformed uuid", () -> expected), is(expected));
205 public static Object[][] severalRequestIdHeaders() {
206 String someTxId = "69fa2575-d7f2-482c-ad1b-53a63ca03617";
207 String anotherTxId = "06de373b-7e19-4357-9bd1-ed95682ae3a4";
209 return new Object[][]{
211 "header is selected when single", RequestIdHeader.TRANSACTION_ID,
213 transactionIdHeader, someTxId
216 "header is selected when first", RequestIdHeader.ONAP_ID,
218 onapRequestIdHeader, someTxId,
219 "noise-header", anotherTxId,
220 ECOMP_REQUEST_ID, anotherTxId
223 "header is selected when last", RequestIdHeader.ONAP_ID,
225 ECOMP_REQUEST_ID, anotherTxId,
226 "noise-header", anotherTxId,
227 onapRequestIdHeader, someTxId
230 "header is selected when value is invalid uuid", RequestIdHeader.ONAP_ID,
232 onapRequestIdHeader, "invalid-uuid"
235 "header is selected when no ecomp-request-id", RequestIdHeader.ONAP_ID,
237 requestIdHeader, anotherTxId,
238 onapRequestIdHeader, someTxId
241 "ECOMP_REQUEST_ID is returned when no request-id header", RequestIdHeader.ECOMP_ID,
243 "tsamina-mina", anotherTxId,
244 "waka-waka", anotherTxId
250 @Test(dataProvider = "severalRequestIdHeaders")
251 public void highestPriorityHeader_givenSeveralRequestIdHeaders_correctHeaderIsUsed(String description, RequestIdHeader expectedHeader, Map<String, String> incomingRequestHeaders) {
253 HttpServletRequest mockedHttpServletRequest = createMockedHttpServletRequest(incomingRequestHeaders);
255 assertThat(description,
256 promiseRequestIdFilter.highestPriorityHeader(mockedHttpServletRequest), is(expectedHeader));
260 private HttpServletRequest buildRequestThenRunThroughFilterAndAssertResultRequestHeaders(
261 ImmutableMap<String, String> originalRequestHeaders,
262 Function<HttpServletRequest, String> txIdExtractor
263 ) throws IOException, ServletException {
264 HttpServletRequest servletRequest = createMockedHttpServletRequest(originalRequestHeaders);
265 HttpServletResponse servletResponse = createMockedHttpServletResponse();
267 final FilterChain capturingFilterChain = Mockito.mock(FilterChain.class);
271 // doFilter() is the function under test
273 promiseRequestIdFilter.doFilter(servletRequest, servletResponse, capturingFilterChain);
277 final ServletRequest capturedServletRequest = extractCapturedServletRequest(capturingFilterChain);
278 final ServletResponse capturedServletResponse = extractCapturedServletResponse(capturingFilterChain);
279 final String expectedTxId = txIdExtractor.apply((HttpServletRequest) capturedServletRequest);
281 assertRequestObjectHeaders(capturedServletRequest, expectedTxId);
282 assertResponseObjectHeaders(capturedServletResponse, expectedTxId);
284 return (HttpServletRequest) capturedServletRequest;
288 private void assertRequestObjectHeaders(ServletRequest request, String expectedTxId) {
291 - Two headers are in place
292 - Direct value extraction is as expected
293 - UserUtils.getRequestId() returns correct and valid value
295 final HttpServletRequest httpServletRequest = (HttpServletRequest) request;
297 assertThat(Collections.list(httpServletRequest.getHeaderNames()),
298 hasItems(equalToIgnoringCase(ECOMP_REQUEST_ID), equalToIgnoringCase(anotherHeader)));
300 assertThat(httpServletRequest.getHeader(anotherHeader), is(anotherValue));
302 assertThat(httpServletRequest.getHeader(ECOMP_REQUEST_ID), equalToIgnoringCase(expectedTxId));
303 assertThat(httpServletRequest.getHeader(mixedCaseHeader), equalToIgnoringCase(expectedTxId));
305 assertThat(UserUtils.getRequestId(httpServletRequest), equalToIgnoringCase(expectedTxId));
306 assertThat(UserUtils.getRequestId(httpServletRequest), is(not(emptyOrNullString())));
309 private void assertResponseObjectHeaders(ServletResponse response, String txId) {
310 final String REQUEST_ID_HEADER_NAME_IN_RESPONSE = mixedCaseHeader + "-echo";
311 final HttpServletResponse httpServletResponse = (HttpServletResponse) response;
313 assertThat("header " + REQUEST_ID_HEADER_NAME_IN_RESPONSE.toLowerCase() + " in response must be provided",
314 httpServletResponse.getHeader(REQUEST_ID_HEADER_NAME_IN_RESPONSE), equalToIgnoringCase(txId));
319 private HttpServletRequest createMockedHttpServletRequest(Map<String, String> requestHeaders) {
320 HttpServletRequest servletRequest = Mockito.mock(HttpServletRequest.class);
321 requestHeaders.forEach((k, v) -> {
322 Mockito.when(servletRequest.getHeader(argThat(s -> equalsIgnoreCase(s, k)))).thenReturn(v);
323 Mockito.when(servletRequest.getHeaders(argThat(s -> equalsIgnoreCase(s, k)))).then(returnEnumerationAnswer(v));
325 Mockito.when(servletRequest.getHeaderNames()).then(returnEnumerationAnswer(requestHeaders.keySet()));
326 return servletRequest;
329 private HttpServletResponse createMockedHttpServletResponse() {
330 return new MockHttpServletResponse();
333 private static Answer<Enumeration<String>> returnEnumerationAnswer(String ... items) {
334 return returnEnumerationAnswer(Arrays.asList(items));
337 private static Answer<Enumeration<String>> returnEnumerationAnswer(Collection<String> items) {
338 return invocation -> Collections.enumeration(items);
341 private Function<HttpServletRequest, String> specificTxId(String someTxId) {
342 return r -> someTxId;
345 private ServletRequest extractCapturedServletRequest(FilterChain capturingFilterChain) throws IOException, ServletException {
346 ArgumentCaptor<ServletRequest> captor = ArgumentCaptor.forClass(ServletRequest.class);
347 Mockito.verify(capturingFilterChain).doFilter(captor.capture(), any());
348 return captor.getValue();
351 private ServletResponse extractCapturedServletResponse(FilterChain capturingFilterChain) throws IOException, ServletException {
352 ArgumentCaptor<ServletResponse> captor = ArgumentCaptor.forClass(ServletResponse.class);
353 Mockito.verify(capturingFilterChain).doFilter(any(), captor.capture());
354 return captor.getValue();