From 12afea79026ec66cbe9046a06af7407da3f63231 Mon Sep 17 00:00:00 2001 From: Ittay Stern Date: Thu, 19 Sep 2019 18:26:45 +0300 Subject: Let request-id value derive from alternative request-headers Issue-ID: VID-253 Change-Id: Icae6caf5f720da0c7587f62380d1783669e69d9f Signed-off-by: Ittay Stern --- .../vid/controller/PromiseRequestIdFilterTest.java | 210 ------------- .../filter/PromiseRequestIdFilterTest.java | 334 +++++++++++++++++++++ 2 files changed, 334 insertions(+), 210 deletions(-) delete mode 100644 vid-app-common/src/test/java/org/onap/vid/controller/PromiseRequestIdFilterTest.java create mode 100644 vid-app-common/src/test/java/org/onap/vid/controller/filter/PromiseRequestIdFilterTest.java (limited to 'vid-app-common/src/test/java/org/onap/vid/controller') diff --git a/vid-app-common/src/test/java/org/onap/vid/controller/PromiseRequestIdFilterTest.java b/vid-app-common/src/test/java/org/onap/vid/controller/PromiseRequestIdFilterTest.java deleted file mode 100644 index 2304d46f3..000000000 --- a/vid-app-common/src/test/java/org/onap/vid/controller/PromiseRequestIdFilterTest.java +++ /dev/null @@ -1,210 +0,0 @@ -/*- - * ============LICENSE_START======================================================= - * VID - * ================================================================================ - * Copyright (C) 2017 - 2019 AT&T Intellectual Property. All rights reserved. - * ================================================================================ - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END========================================================= - */ - -package org.onap.vid.controller; - -import static org.apache.commons.lang3.StringUtils.equalsIgnoreCase; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.containsInAnyOrder; -import static org.hamcrest.Matchers.emptyOrNullString; -import static org.hamcrest.Matchers.equalToIgnoringCase; -import static org.hamcrest.Matchers.not; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.argThat; -import static org.onap.portalsdk.core.util.SystemProperties.ECOMP_REQUEST_ID; - -import com.google.common.collect.ImmutableMap; -import java.io.IOException; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Enumeration; -import java.util.Map; -import java.util.function.Function; -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import org.mockito.ArgumentCaptor; -import org.mockito.Mockito; -import org.mockito.stubbing.Answer; -import org.onap.portalsdk.core.web.support.UserUtils; -import org.onap.vid.controller.filter.PromiseRequestIdFilter; -import org.springframework.mock.web.MockHttpServletResponse; -import org.testng.annotations.Test; - -@Test -public class PromiseRequestIdFilterTest { - - private final String anotherHeader = "ANDREI_RUBLEV"; - private final String anotherValue = "foo value"; - private final String mixedCaseHeader = "x-ecomp-REQUESTID"; - - @Test - public void givenRequestIdHeader_headerValueNotChanged() throws IOException, ServletException { - - final String someTxId = "863850e2-8545-4efd-94b8-afba5f52b3d5"; - - final ImmutableMap incomingRequestHeaders = ImmutableMap.of( - anotherHeader, anotherValue, - ECOMP_REQUEST_ID, someTxId - ); - - buildRequestThenRunThroughFilterAndAssertResultRequestHeaders(incomingRequestHeaders, specificTxId(someTxId)); - } - - - @Test - public void givenRequestIdHeaderThatIsNotAUUID_headerValueChanged() throws IOException, ServletException { - - final String someTxId = "863850e28544efd94b8afba5f52b3d5"; - - final ImmutableMap incomingRequestHeaders = ImmutableMap.of( - anotherHeader, anotherValue, - ECOMP_REQUEST_ID, someTxId - ); - - buildRequestThenRunThroughFilterAndAssertResultRequestHeaders(incomingRequestHeaders, UserUtils::getRequestId); - } - - - @Test - public void givenMixedCaseRequestIdHeader_headerValueNotChanged() throws IOException, ServletException { - - final String someTxId = "729bbd8d-b0c2-4809-a794-dcccd9cda2c0"; - - final ImmutableMap incomingRequestHeaders = ImmutableMap.of( - mixedCaseHeader, someTxId, - anotherHeader, anotherValue - ); - - buildRequestThenRunThroughFilterAndAssertResultRequestHeaders(incomingRequestHeaders, specificTxId(someTxId)); - } - - @Test - public void givenNoRequestIdHeader_headerValueWasGenerated() throws IOException, ServletException { - - final ImmutableMap incomingRequestHeaders = ImmutableMap.of( - anotherHeader, anotherValue - ); - - buildRequestThenRunThroughFilterAndAssertResultRequestHeaders(incomingRequestHeaders, UserUtils::getRequestId); - } - - - private void buildRequestThenRunThroughFilterAndAssertResultRequestHeaders( - ImmutableMap originalRequestHeaders, - Function txIdExtractor - ) throws IOException, ServletException { - HttpServletRequest servletRequest = createMockedHttpServletRequest(originalRequestHeaders); - HttpServletResponse servletResponse = createMockedHttpServletResponse(); - - final FilterChain capturingFilterChain = Mockito.mock(FilterChain.class); - - ////////////////// - // - // doFilter() is the function under test - // - new PromiseRequestIdFilter().doFilter(servletRequest, servletResponse, capturingFilterChain); - // - ////////////////// - - final ServletRequest capturedServletRequest = extractCapturedServletRequest(capturingFilterChain); - final ServletResponse capturedServletResponse = extractCapturedServletResponse(capturingFilterChain); - final String expectedTxId = txIdExtractor.apply((HttpServletRequest) capturedServletRequest); - - assertRequestObjectHeaders(capturedServletRequest, expectedTxId); - assertResponseObjectHeaders(capturedServletResponse, expectedTxId); - } - - - private void assertRequestObjectHeaders(ServletRequest request, String expectedTxId) { - /* - Assert that: - - Two headers are in place - - Direct value extraction is as expected - - UserUtils.getRequestId() returns correct and valid value - */ - final HttpServletRequest httpServletRequest = (HttpServletRequest) request; - - assertThat(Collections.list(httpServletRequest.getHeaderNames()), - containsInAnyOrder(equalToIgnoringCase(ECOMP_REQUEST_ID), equalToIgnoringCase(anotherHeader))); - - assertThat(httpServletRequest.getHeader(anotherHeader), is(anotherValue)); - - assertThat(httpServletRequest.getHeader(ECOMP_REQUEST_ID), is(expectedTxId)); - assertThat(httpServletRequest.getHeader(mixedCaseHeader), is(expectedTxId)); - - assertThat(UserUtils.getRequestId(httpServletRequest), is(expectedTxId)); - assertThat(UserUtils.getRequestId(httpServletRequest), is(not(emptyOrNullString()))); - } - - private void assertResponseObjectHeaders(ServletResponse response, String txId) { - final String REQUEST_ID_HEADER_NAME_IN_RESPONSE = mixedCaseHeader + "-echo"; - final HttpServletResponse httpServletResponse = (HttpServletResponse) response; - - assertThat("header " + REQUEST_ID_HEADER_NAME_IN_RESPONSE.toLowerCase() + " in response must be provided", - httpServletResponse.getHeader(REQUEST_ID_HEADER_NAME_IN_RESPONSE), is(txId)); - } - - - - private HttpServletRequest createMockedHttpServletRequest(Map requestHeaders) { - HttpServletRequest servletRequest = Mockito.mock(HttpServletRequest.class); - requestHeaders.forEach((k, v) -> { - Mockito.when(servletRequest.getHeader(argThat(s -> equalsIgnoreCase(s, k)))).thenReturn(v); - Mockito.when(servletRequest.getHeaders(argThat(s -> equalsIgnoreCase(s, k)))).then(returnEnumerationAnswer(v)); - }); - Mockito.when(servletRequest.getHeaderNames()).then(returnEnumerationAnswer(requestHeaders.keySet())); - return servletRequest; - } - - private HttpServletResponse createMockedHttpServletResponse() { - return new MockHttpServletResponse(); - } - - private static Answer> returnEnumerationAnswer(String ... items) { - return returnEnumerationAnswer(Arrays.asList(items)); - } - - private static Answer> returnEnumerationAnswer(Collection items) { - return invocation -> Collections.enumeration(items); - } - - private Function specificTxId(String someTxId) { - return r -> someTxId; - } - - private ServletRequest extractCapturedServletRequest(FilterChain capturingFilterChain) throws IOException, ServletException { - ArgumentCaptor captor = ArgumentCaptor.forClass(ServletRequest.class); - Mockito.verify(capturingFilterChain).doFilter(captor.capture(), any()); - return captor.getValue(); - } - - private ServletResponse extractCapturedServletResponse(FilterChain capturingFilterChain) throws IOException, ServletException { - ArgumentCaptor captor = ArgumentCaptor.forClass(ServletResponse.class); - Mockito.verify(capturingFilterChain).doFilter(any(), captor.capture()); - return captor.getValue(); - } - -} diff --git a/vid-app-common/src/test/java/org/onap/vid/controller/filter/PromiseRequestIdFilterTest.java b/vid-app-common/src/test/java/org/onap/vid/controller/filter/PromiseRequestIdFilterTest.java new file mode 100644 index 000000000..a1d15f543 --- /dev/null +++ b/vid-app-common/src/test/java/org/onap/vid/controller/filter/PromiseRequestIdFilterTest.java @@ -0,0 +1,334 @@ +/*- + * ============LICENSE_START======================================================= + * VID + * ================================================================================ + * Copyright (C) 2017 - 2019 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.vid.controller.filter; + +import static org.apache.commons.lang3.StringUtils.equalsIgnoreCase; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.anyOf; +import static org.hamcrest.Matchers.emptyOrNullString; +import static org.hamcrest.Matchers.equalToIgnoringCase; +import static org.hamcrest.Matchers.hasItems; +import static org.hamcrest.Matchers.not; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.onap.portalsdk.core.util.SystemProperties.ECOMP_REQUEST_ID; + +import com.google.common.collect.ImmutableMap; +import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.Map; +import java.util.UUID; +import java.util.function.Function; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; +import org.mockito.stubbing.Answer; +import org.onap.portalsdk.core.web.support.UserUtils; +import org.onap.vid.logging.Headers; +import org.springframework.mock.web.MockHttpServletResponse; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +@Test +public class PromiseRequestIdFilterTest { + + private final String anotherHeader = "ANDREI_RUBLEV"; + private final String anotherValue = "foo value"; + private final String mixedCaseHeader = "x-ecomp-REQUESTID"; + + private static final String onapRequestIdHeader = "x-onap-requestid"; + private static final String transactionIdHeader = "x-transactionid"; + private static final String requestIdHeader = "x-requestid"; + + private final PromiseRequestIdFilter promiseRequestIdFilter = + new PromiseRequestIdFilter(new Headers()); + + @Test + public void givenRequestIdHeader_headerValueNotChanged() throws IOException, ServletException { + + final String someTxId = "863850e2-8545-4efd-94b8-afba5f52b3d5"; + + final ImmutableMap incomingRequestHeaders = ImmutableMap.of( + anotherHeader, anotherValue, + ECOMP_REQUEST_ID, someTxId + ); + + buildRequestThenRunThroughFilterAndAssertResultRequestHeaders(incomingRequestHeaders, specificTxId(someTxId)); + } + + + @Test + public void givenRequestIdHeaderThatIsNotAUUID_headerValueChanged() throws IOException, ServletException { + + final String someTxId = "863850e28544efd94b8afba5f52b3d5"; + + final ImmutableMap incomingRequestHeaders = ImmutableMap.of( + anotherHeader, anotherValue, + ECOMP_REQUEST_ID, someTxId + ); + + buildRequestThenRunThroughFilterAndAssertResultRequestHeaders(incomingRequestHeaders, UserUtils::getRequestId); + } + + + @Test + public void givenMixedCaseRequestIdHeader_headerValueNotChanged() throws IOException, ServletException { + + final String someTxId = "729bbd8d-b0c2-4809-a794-dcccd9cda2c0"; + + final ImmutableMap incomingRequestHeaders = ImmutableMap.of( + mixedCaseHeader, someTxId, + anotherHeader, anotherValue + ); + + buildRequestThenRunThroughFilterAndAssertResultRequestHeaders(incomingRequestHeaders, specificTxId(someTxId)); + } + + @Test + public void givenNoRequestIdHeader_headerValueWasGenerated() throws IOException, ServletException { + + final ImmutableMap incomingRequestHeaders = ImmutableMap.of( + anotherHeader, anotherValue + ); + + buildRequestThenRunThroughFilterAndAssertResultRequestHeaders(incomingRequestHeaders, UserUtils::getRequestId); + } + + @Test + public void givenTwoRequestIdHeader_onapHeaderValueIsUsed() throws IOException, ServletException { + + final String onapTxId = "863850e2-8545-4efd-94b8-AFBA5F52B3D5"; // note mixed case + final String ecompTxId = "6e8ff89e-88a4-4977-b63f-3142892b6e08"; + + final ImmutableMap incomingRequestHeaders = ImmutableMap.of( + anotherHeader, anotherValue, + ECOMP_REQUEST_ID, ecompTxId, + onapRequestIdHeader, onapTxId + ); + + buildRequestThenRunThroughFilterAndAssertResultRequestHeaders(incomingRequestHeaders, specificTxId(onapTxId)); + } + + @Test + public void givenTwoRequestIdHeaderAndHigherPriorityIsMalformed_headerValueIsGenerated() throws IOException, ServletException { + + final String malformedTxId = "6e8ff89e-88a4-4977-b63f-3142892b6e08-"; + final String anotherTxId = "863850e2-8545-4efd-94b8-afba5f52b3d5"; + + final ImmutableMap incomingRequestHeaders = ImmutableMap.of( + anotherHeader, anotherValue, + requestIdHeader, malformedTxId, // requestIdHeader as higher priority than transactionIdHeader + transactionIdHeader, anotherTxId + ); + + HttpServletRequest wrappedRequest = + buildRequestThenRunThroughFilterAndAssertResultRequestHeaders(incomingRequestHeaders, UserUtils::getRequestId); + + assertThat(UserUtils.getRequestId(wrappedRequest), + not(anyOf(equalTo(malformedTxId), equalTo(anotherTxId))) + ); + } + + + @Test + public void toUuidOrElse_givenValid_yieldSame() { + final String someTxId = "729bbd8d-b0c2-4809-a794-DCCCD9CDA2C0"; // note mixed case + UUID unexpected = UUID.randomUUID(); + assertThat(promiseRequestIdFilter.toUuidOrElse(someTxId, () -> unexpected), is(UUID.fromString(someTxId))); + } + + @Test + public void toUuidOrElse_givenNull_yieldSupplier() { + UUID expected = UUID.fromString("729bbd8d-b0c2-4809-a794-dcccd9cda2c0"); + assertThat(promiseRequestIdFilter.toUuidOrElse(null, () -> expected), is(expected)); + } + + @Test + public void toUuidOrElse_givenMalformed_yieldSupplier() { + UUID expected = UUID.fromString("729bbd8d-b0c2-4809-a794-dcccd9cda2c0"); + assertThat(promiseRequestIdFilter.toUuidOrElse("malformed uuid", () -> expected), is(expected)); + } + + @DataProvider + public static Object[][] severalRequestIdHeaders() { + String someTxId = "69fa2575-d7f2-482c-ad1b-53a63ca03617"; + String anotherTxId = "06de373b-7e19-4357-9bd1-ed95682ae3a4"; + + return new Object[][]{ + { + "header is selected when single", transactionIdHeader, + ImmutableMap.of( + transactionIdHeader, someTxId + ) + }, { + "header is selected when first", onapRequestIdHeader, + ImmutableMap.of( + onapRequestIdHeader, someTxId, + "noise-header", anotherTxId, + ECOMP_REQUEST_ID, anotherTxId + ) + }, { + "header is selected when last", onapRequestIdHeader, + ImmutableMap.of( + ECOMP_REQUEST_ID, anotherTxId, + "noise-header", anotherTxId, + onapRequestIdHeader, someTxId + ) + }, { + "header is selected when value is invalid uuid", onapRequestIdHeader, + ImmutableMap.of( + onapRequestIdHeader, "invalid-uuid" + ) + }, { + "header is selected when no ecomp-request-id", onapRequestIdHeader, + ImmutableMap.of( + requestIdHeader, anotherTxId, + onapRequestIdHeader, someTxId + ) + }, { + "ECOMP_REQUEST_ID is returned when no request-id header", ECOMP_REQUEST_ID, + ImmutableMap.of( + "tsamina-mina", anotherTxId, + "waka-waka", anotherTxId + ) + }, + }; + } + + @Test(dataProvider = "severalRequestIdHeaders") + public void highestPriorityHeader_givenSeveralRequestIdHeaders_correctHeaderIsUsed(String description, String expectedHeader, Map incomingRequestHeaders) { + PromiseRequestIdFilter testSubject = promiseRequestIdFilter; + + HttpServletRequest mockedHttpServletRequest = createMockedHttpServletRequest(incomingRequestHeaders); + + assertThat(description, + testSubject.highestPriorityHeader(mockedHttpServletRequest), equalToIgnoringCase(expectedHeader)); + } + + + private HttpServletRequest buildRequestThenRunThroughFilterAndAssertResultRequestHeaders( + ImmutableMap originalRequestHeaders, + Function txIdExtractor + ) throws IOException, ServletException { + HttpServletRequest servletRequest = createMockedHttpServletRequest(originalRequestHeaders); + HttpServletResponse servletResponse = createMockedHttpServletResponse(); + + final FilterChain capturingFilterChain = Mockito.mock(FilterChain.class); + + ////////////////// + // + // doFilter() is the function under test + // + promiseRequestIdFilter.doFilter(servletRequest, servletResponse, capturingFilterChain); + // + ////////////////// + + final ServletRequest capturedServletRequest = extractCapturedServletRequest(capturingFilterChain); + final ServletResponse capturedServletResponse = extractCapturedServletResponse(capturingFilterChain); + final String expectedTxId = txIdExtractor.apply((HttpServletRequest) capturedServletRequest); + + assertRequestObjectHeaders(capturedServletRequest, expectedTxId); + assertResponseObjectHeaders(capturedServletResponse, expectedTxId); + + return (HttpServletRequest) capturedServletRequest; + } + + + private void assertRequestObjectHeaders(ServletRequest request, String expectedTxId) { + /* + Assert that: + - Two headers are in place + - Direct value extraction is as expected + - UserUtils.getRequestId() returns correct and valid value + */ + final HttpServletRequest httpServletRequest = (HttpServletRequest) request; + + assertThat(Collections.list(httpServletRequest.getHeaderNames()), + hasItems(equalToIgnoringCase(ECOMP_REQUEST_ID), equalToIgnoringCase(anotherHeader))); + + assertThat(httpServletRequest.getHeader(anotherHeader), is(anotherValue)); + + assertThat(httpServletRequest.getHeader(ECOMP_REQUEST_ID), equalToIgnoringCase(expectedTxId)); + assertThat(httpServletRequest.getHeader(mixedCaseHeader), equalToIgnoringCase(expectedTxId)); + + assertThat(UserUtils.getRequestId(httpServletRequest), equalToIgnoringCase(expectedTxId)); + assertThat(UserUtils.getRequestId(httpServletRequest), is(not(emptyOrNullString()))); + } + + private void assertResponseObjectHeaders(ServletResponse response, String txId) { + final String REQUEST_ID_HEADER_NAME_IN_RESPONSE = mixedCaseHeader + "-echo"; + final HttpServletResponse httpServletResponse = (HttpServletResponse) response; + + assertThat("header " + REQUEST_ID_HEADER_NAME_IN_RESPONSE.toLowerCase() + " in response must be provided", + httpServletResponse.getHeader(REQUEST_ID_HEADER_NAME_IN_RESPONSE), equalToIgnoringCase(txId)); + } + + + + private HttpServletRequest createMockedHttpServletRequest(Map requestHeaders) { + HttpServletRequest servletRequest = Mockito.mock(HttpServletRequest.class); + requestHeaders.forEach((k, v) -> { + Mockito.when(servletRequest.getHeader(argThat(s -> equalsIgnoreCase(s, k)))).thenReturn(v); + Mockito.when(servletRequest.getHeaders(argThat(s -> equalsIgnoreCase(s, k)))).then(returnEnumerationAnswer(v)); + }); + Mockito.when(servletRequest.getHeaderNames()).then(returnEnumerationAnswer(requestHeaders.keySet())); + return servletRequest; + } + + private HttpServletResponse createMockedHttpServletResponse() { + return new MockHttpServletResponse(); + } + + private static Answer> returnEnumerationAnswer(String ... items) { + return returnEnumerationAnswer(Arrays.asList(items)); + } + + private static Answer> returnEnumerationAnswer(Collection items) { + return invocation -> Collections.enumeration(items); + } + + private Function specificTxId(String someTxId) { + return r -> someTxId; + } + + private ServletRequest extractCapturedServletRequest(FilterChain capturingFilterChain) throws IOException, ServletException { + ArgumentCaptor captor = ArgumentCaptor.forClass(ServletRequest.class); + Mockito.verify(capturingFilterChain).doFilter(captor.capture(), any()); + return captor.getValue(); + } + + private ServletResponse extractCapturedServletResponse(FilterChain capturingFilterChain) throws IOException, ServletException { + ArgumentCaptor captor = ArgumentCaptor.forClass(ServletResponse.class); + Mockito.verify(capturingFilterChain).doFilter(any(), captor.capture()); + return captor.getValue(); + } + +} -- cgit 1.2.3-korg