From ed18ea83def89626ba42cbe3f38d96e50aabd2f6 Mon Sep 17 00:00:00 2001 From: Bartosz Gardziejewski Date: Tue, 26 Jan 2021 07:43:22 +0100 Subject: Add message from VES to PNF simulator response when performing one time event request Signed-off-by: Bartosz Gardziejewski Change-Id: I0a12263daafacc52643838ff80482e4c7168b7ea Issue-ID: INT-1804 --- pnfsimulator/README.md | 7 +- .../integration/VesSimulatorController.java | 28 +++++-- .../integration/OptionalTemplatesTest.java | 65 ++++++++++++-- .../pnfsimulator/rest/SimulatorController.java | 63 ++++++++------ .../pnfsimulator/simulator/SimulatorService.java | 5 +- .../client/HttpApacheResponseAdapterFactory.java | 45 ++++++++++ .../simulator/client/HttpClientAdapter.java | 3 +- .../simulator/client/HttpClientAdapterImpl.java | 11 ++- .../simulator/client/HttpResponseAdapter.java | 41 +++++++++ .../pnfsimulator/rest/SimulatorControllerTest.java | 3 + .../simulator/SimulatorServiceTest.java | 31 ++++--- .../HttpApacheResponseAdapterFactoryTest.java | 98 ++++++++++++++++++++++ .../client/HttpClientAdapterImplTest.java | 36 ++++++-- .../simulator/client/HttpTestUtils.java | 55 ++++++++++++ 14 files changed, 427 insertions(+), 64 deletions(-) create mode 100644 pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/client/HttpApacheResponseAdapterFactory.java create mode 100644 pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/client/HttpResponseAdapter.java create mode 100644 pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/client/HttpApacheResponseAdapterFactoryTest.java create mode 100644 pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/client/HttpTestUtils.java diff --git a/pnfsimulator/README.md b/pnfsimulator/README.md index 9f7e996..52bf8bd 100644 --- a/pnfsimulator/README.md +++ b/pnfsimulator/README.md @@ -56,7 +56,12 @@ Sample Request: Enables direct, immediate event sending without need to have template deployed on backend. Keywords are supported,thus once passed, will also be substituted with proper strings. Passed event body must be valid and complete event according to VES Collector interface. -To trigger sending use following endpoint *http://:5000/simulator/event*. +To trigger sending use following endpoint *http://:5000/simulator/event*. +After sending event, response message from VES will be passed as response message from Simulator. +Thanks to that when sending one-time event user will receive information about request. +This is helpful when authentication fail or VES response with "Forbidden" when using http instead of https. +In a situation when given URL address is not pointing to VES, Simulator response with status ```421``` +and information about communication problem. Supported method: *POST* Headers: diff --git a/pnfsimulator/integration/src/main/java/org/onap/pnfsimulator/integration/VesSimulatorController.java b/pnfsimulator/integration/src/main/java/org/onap/pnfsimulator/integration/VesSimulatorController.java index 70e0c60..304df60 100644 --- a/pnfsimulator/integration/src/main/java/org/onap/pnfsimulator/integration/VesSimulatorController.java +++ b/pnfsimulator/integration/src/main/java/org/onap/pnfsimulator/integration/VesSimulatorController.java @@ -22,18 +22,27 @@ package org.onap.pnfsimulator.integration; import com.google.gson.Gson; import com.google.gson.JsonObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; + @RequestMapping("ves-simulator") @RestController public class VesSimulatorController { + private static final Logger LOGGER = LoggerFactory.getLogger(VesSimulatorController.class); private final VesSimulatorService vesSimulatorService; private final Gson gson; + private final ResponseEntity response = ResponseEntity + .status(HttpStatus.ACCEPTED) + .body("Accepted"); @Autowired public VesSimulatorController(VesSimulatorService vesSimulatorService, Gson gson) { @@ -42,18 +51,21 @@ public class VesSimulatorController { } @PostMapping("eventListener/v5") - String sendEventToDmaapV5(@RequestBody String body) { - System.out.println("Received event" + body); - JsonObject jsonObject = gson.fromJson(body, JsonObject.class); + public ResponseEntity sendEventToDmaapV5(@RequestBody String body) { + JsonObject jsonObject = getJsonObjectFromBody(body); vesSimulatorService.sendEventToDmaapV5(jsonObject); - return "MessageAccepted"; + return response; } @PostMapping("eventListener/v7") - String sendEventToDmaapV7(@RequestBody String body) { - System.out.println("Received event" + body); - JsonObject jsonObject = gson.fromJson(body, JsonObject.class); + public ResponseEntity sendEventToDmaapV7(@RequestBody String body) { + JsonObject jsonObject = getJsonObjectFromBody(body); vesSimulatorService.sendEventToDmaapV7(jsonObject); - return "MessageAccepted"; + return response; + } + + private JsonObject getJsonObjectFromBody(@RequestBody String body) { + LOGGER.info(String.format("Received event: %s", body)); + return gson.fromJson(body, JsonObject.class); } } diff --git a/pnfsimulator/integration/src/test/java/org/onap/pnfsimulator/integration/OptionalTemplatesTest.java b/pnfsimulator/integration/src/test/java/org/onap/pnfsimulator/integration/OptionalTemplatesTest.java index a5ffe4d..8c8767d 100644 --- a/pnfsimulator/integration/src/test/java/org/onap/pnfsimulator/integration/OptionalTemplatesTest.java +++ b/pnfsimulator/integration/src/test/java/org/onap/pnfsimulator/integration/OptionalTemplatesTest.java @@ -23,6 +23,7 @@ package org.onap.pnfsimulator.integration; import static io.restassured.RestAssured.given; import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.stringContainsInOrder; import com.google.gson.JsonObject; import com.mongodb.MongoClient; @@ -39,6 +40,8 @@ import java.net.NetworkInterface; import java.net.SocketException; import java.net.UnknownHostException; import java.util.Collections; +import java.util.List; + import org.assertj.core.api.Assertions; import org.bson.Document; import org.junit.After; @@ -50,6 +53,7 @@ import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.http.HttpStatus; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @@ -109,8 +113,8 @@ public class OptionalTemplatesTest { .when() .post(SINGLE_EVENT_URL) .then() - .statusCode(202) - .body("message", equalTo("One-time direct event sent successfully")); + .statusCode(HttpStatus.ACCEPTED.value()) + .body("message", equalTo("Accepted")); //then long afterExecution = Instant.now().getEpochSecond(); @@ -124,7 +128,7 @@ public class OptionalTemplatesTest { .get("sourceName").getAsString()).isEqualTo("Single_sourceName"); assertThat(value .getAsJsonObject(COMMON_EVENT_HEADER) - .get("eventId1").getAsString().length()).isEqualTo(20); + .get("eventId1").getAsString()).hasSize(20); assertThat(value .getAsJsonObject(COMMON_EVENT_HEADER) .get("eventId2").getAsString()).isEqualTo("10"); @@ -160,8 +164,8 @@ public class OptionalTemplatesTest { .when() .post(SINGLE_EVENT_URL) .then() - .statusCode(202) - .body("message", equalTo("One-time direct event sent successfully")); + .statusCode(HttpStatus.ACCEPTED.value()) + .body("message", equalTo("Accepted")); //then Mockito.verify(vesSimulatorService, @@ -173,6 +177,57 @@ public class OptionalTemplatesTest { .isEqualTo("{\"commonEventHeader\":{\"sourceName\":\"HistoricalEvent\",\"version\":3}}"); } + @Test + public void whenTriggeredSimulatorWithWrongVesIpInformationShouldBeReturned() { + //given + String body = "{\n" + + "\"vesServerUrl\": \"https://" + currentVesSimulatorIp + ":8080/ves-simulator/eventListener/v5\",\n" + + "\"event\": { \n" + + "\"commonEventHeader\": {\n" + + "\"sourceName\": \"HistoricalEvent\",\n" + + "\"version\": 3" + + "}\n" + + "}\n" + + "}"; + + //when + given() + .contentType("application/json") + .body(body) + .when() + .post(SINGLE_EVENT_URL) + .then() + .statusCode(421) + .body("message", + equalTo( + "Fail to connect with ves: Connect to "+currentVesSimulatorIp+":8080 " + + "[/"+currentVesSimulatorIp+"] " + + "failed: Connection refused (Connection refused)")); + } + + @Test + public void whenTriggeredSimulatorWithWrongEventShouldReturnedError() { + //given + String body = "{\n" + + "\"vesServerUrl\": \"https://" + currentVesSimulatorIp + ":9443/ves-simulator/eventListener/v5\",\n" + + "\"event\": { \n" + + "this is not JSON {}" + + "}\n" + + "}"; + + //when + given() + .contentType("application/json") + .body(body) + .when() + .post(SINGLE_EVENT_URL) + .then() + .statusCode(HttpStatus.BAD_REQUEST.value()) + .body("message", + stringContainsInOrder(List.of("JSON parse error:","Unexpected character ('t' (code 116)):")) + ); + } + private Document findSourceNameInMongoDB() throws UnknownHostException { MongoCredential credential = MongoCredential .createCredential(PNF_SIMULATOR_DB_USER, PNF_SIMULATOR_DB, PNF_SIMULATOR_DB_PSWD.toCharArray()); diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/rest/SimulatorController.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/rest/SimulatorController.java index 023b163..f2c70dd 100644 --- a/pnfsimulator/src/main/java/org/onap/pnfsimulator/rest/SimulatorController.java +++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/rest/SimulatorController.java @@ -30,6 +30,7 @@ import org.onap.pnfsimulator.rest.model.SimulatorRequest; import org.onap.pnfsimulator.rest.util.DateUtil; import org.onap.pnfsimulator.rest.util.ResponseBuilder; import org.onap.pnfsimulator.simulator.SimulatorService; +import org.onap.pnfsimulator.simulator.client.HttpResponseAdapter; import org.onap.pnfsimulator.simulatorconfig.SimulatorConfig; import org.quartz.SchedulerException; import org.slf4j.Logger; @@ -68,7 +69,6 @@ import static org.onap.pnfsimulator.logging.MdcVariables.X_INVOCATION_ID; import static org.onap.pnfsimulator.logging.MdcVariables.X_ONAP_REQUEST_ID; import static org.onap.pnfsimulator.rest.util.ResponseBuilder.MESSAGE; import static org.onap.pnfsimulator.rest.util.ResponseBuilder.TIMESTAMP; -import static org.springframework.http.HttpStatus.ACCEPTED; import static org.springframework.http.HttpStatus.BAD_REQUEST; import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR; import static org.springframework.http.HttpStatus.NOT_FOUND; @@ -99,7 +99,7 @@ public class SimulatorController { */ @PostMapping("test") @Deprecated - public ResponseEntity> test(@Valid @RequestBody SimulatorRequest simulatorRequest) { + public ResponseEntity> test(@Valid @RequestBody SimulatorRequest simulatorRequest) { MDC.put("test", "test"); String simulatorRequestString = simulatorRequest.toString(); LOGGER.info(ENTRY, simulatorRequestString); @@ -107,8 +107,8 @@ public class SimulatorController { } @PostMapping(value = "start") - public ResponseEntity> start(@RequestHeader HttpHeaders headers, - @Valid @RequestBody SimulatorRequest triggerEventRequest) { + public ResponseEntity> start(@RequestHeader HttpHeaders headers, + @Valid @RequestBody SimulatorRequest triggerEventRequest) { logContextHeaders(headers, "/simulator/start"); LOGGER.info(ENTRY, "Simulator started"); @@ -120,23 +120,23 @@ public class SimulatorController { LOGGER.warn("Cannot trigger event, invalid json format: {}", e.getMessage()); LOGGER.debug("Received json has invalid format", e); return buildResponse(BAD_REQUEST, ImmutableMap.of(MESSAGE, String - .format(INCORRECT_TEMPLATE_MESSAGE, triggerEventRequest.getTemplateName(), - e.getMessage()))); + .format(INCORRECT_TEMPLATE_MESSAGE, triggerEventRequest.getTemplateName(), + e.getMessage()))); } catch (GeneralSecurityException e) { MDC.put(RESPONSE_CODE, INTERNAL_SERVER_ERROR.toString()); LOGGER.error("Client certificate validation failed: {}", e.getMessage()); return buildResponse(INTERNAL_SERVER_ERROR, - ImmutableMap.of(MESSAGE, "Invalid or misconfigured client certificate")); + ImmutableMap.of(MESSAGE, "Invalid or misconfigured client certificate")); } catch (IOException e) { MDC.put(RESPONSE_CODE, BAD_REQUEST.toString()); LOGGER.warn("Json validation failed: {}", e.getMessage()); return buildResponse(BAD_REQUEST, - ImmutableMap.of(MESSAGE, String.format(NOT_EXISTING_TEMPLATE, triggerEventRequest.getTemplateName()))); + ImmutableMap.of(MESSAGE, String.format(NOT_EXISTING_TEMPLATE, triggerEventRequest.getTemplateName()))); } catch (Exception e) { MDC.put(RESPONSE_CODE, INTERNAL_SERVER_ERROR.toString()); LOGGER.error("Cannot trigger event - unexpected exception", e); return buildResponse(INTERNAL_SERVER_ERROR, - ImmutableMap.of(MESSAGE, "Unexpected exception: " + e.getMessage())); + ImmutableMap.of(MESSAGE, "Unexpected exception: " + e.getMessage())); } finally { MDC.clear(); } @@ -147,30 +147,30 @@ public class SimulatorController { */ @GetMapping("all-events") @Deprecated - public ResponseEntity> allEvents() { + public ResponseEntity> allEvents() { List eventDataList = eventDataService.getAllEvents(); StringBuilder sb = new StringBuilder(); eventDataList.forEach(e -> sb.append(e).append(System.lineSeparator())); return ResponseBuilder - .status(OK).put(MESSAGE, sb.toString()) - .build(); + .status(OK).put(MESSAGE, sb.toString()) + .build(); } @GetMapping("config") - public ResponseEntity> getConfig() { + public ResponseEntity> getConfig() { SimulatorConfig configToGet = simulatorService.getConfiguration(); return buildResponse(OK, ImmutableMap.of("simulatorConfig", configToGet)); } @PutMapping("config") - public ResponseEntity> updateConfig(@Valid @RequestBody SimulatorConfig newConfig) { + public ResponseEntity> updateConfig(@Valid @RequestBody SimulatorConfig newConfig) { SimulatorConfig updatedConfig = simulatorService.updateConfiguration(newConfig); return buildResponse(OK, ImmutableMap.of("simulatorConfig", updatedConfig)); } @PostMapping("cancel/{jobName}") - public ResponseEntity> cancelEvent(@PathVariable String jobName) throws SchedulerException { + public ResponseEntity> cancelEvent(@PathVariable String jobName) throws SchedulerException { String jobNameNoBreakingCharacters = replaceBreakingCharacters(jobName); LOGGER.info(ENTRY, "Cancel called on {}.", jobNameNoBreakingCharacters); boolean isCancelled = simulatorService.cancelEvent(jobName); @@ -178,41 +178,52 @@ public class SimulatorController { } @PostMapping("cancel") - public ResponseEntity> cancelAllEvent() throws SchedulerException { + public ResponseEntity> cancelAllEvent() throws SchedulerException { LOGGER.info(ENTRY, "Cancel called on all jobs"); boolean isCancelled = simulatorService.cancelAllEvents(); return createCancelEventResponse(isCancelled); } @PostMapping("event") - public ResponseEntity> sendEventDirectly(@RequestHeader HttpHeaders headers, @Valid @RequestBody FullEvent event) - throws IOException, GeneralSecurityException { + public ResponseEntity> sendEventDirectly(@RequestHeader HttpHeaders headers, @Valid @RequestBody FullEvent event) + throws IOException, GeneralSecurityException { logContextHeaders(headers, "/simulator/event"); LOGGER.info(ENTRY, "Trying to send one-time event directly to VES Collector"); - simulatorService.triggerOneTimeEvent(event); - return buildResponse(ACCEPTED, ImmutableMap.of(MESSAGE, "One-time direct event sent successfully")); + HttpResponseAdapter response = simulatorService.triggerOneTimeEvent(event); + return buildResponse(response); } private String replaceBreakingCharacters(String jobName) { return jobName.replaceAll(BREAKING_CHARACTER_REGEX, "_"); } - private ResponseEntity> processRequest(SimulatorRequest triggerEventRequest) - throws IOException, SchedulerException, GeneralSecurityException { + private ResponseEntity> processRequest(SimulatorRequest triggerEventRequest) + throws IOException, SchedulerException, GeneralSecurityException { String jobName = simulatorService.triggerEvent(triggerEventRequest); MDC.put(RESPONSE_CODE, OK.toString()); return buildResponse(OK, ImmutableMap.of(MESSAGE, "Request started", "jobName", jobName)); } - private ResponseEntity> buildResponse(HttpStatus endStatus, Map parameters) { + private ResponseEntity> buildResponse(HttpStatus endStatus, Map parameters) { ResponseBuilder builder = ResponseBuilder - .status(endStatus) - .put(TIMESTAMP, DateUtil.getTimestamp(responseDateFormat)); + .status(endStatus) + .put(TIMESTAMP, DateUtil.getTimestamp(responseDateFormat)); parameters.forEach(builder::put); return builder.build(); } + private ResponseEntity> buildResponse(HttpResponseAdapter response) { + HttpStatus status = HttpStatus.valueOf(response.getCode()); + Map parameters; + if (response.getMessage().isEmpty()) { + parameters = Map.of(MESSAGE, "One-time direct event sent successfully"); + } else { + parameters = Map.of(MESSAGE, response.getMessage()); + } + return buildResponse(status, parameters); + } + private void logContextHeaders(HttpHeaders headers, String serviceName) { MDC.put(REQUEST_ID, headers.getFirst(X_ONAP_REQUEST_ID)); MDC.put(INVOCATION_ID, headers.getFirst(X_INVOCATION_ID)); @@ -220,7 +231,7 @@ public class SimulatorController { MDC.put(SERVICE_NAME, serviceName); } - private ResponseEntity> createCancelEventResponse(boolean isCancelled) { + private ResponseEntity> createCancelEventResponse(boolean isCancelled) { if (isCancelled) { return buildResponse(OK, ImmutableMap.of(MESSAGE, "Event(s) was cancelled")); } else { diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/SimulatorService.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/SimulatorService.java index e5576b8..218dbc8 100644 --- a/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/SimulatorService.java +++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/SimulatorService.java @@ -29,6 +29,7 @@ import org.onap.pnfsimulator.rest.model.SimulatorParams; import org.onap.pnfsimulator.rest.model.SimulatorRequest; import org.onap.pnfsimulator.simulator.client.HttpClientAdapter; import org.onap.pnfsimulator.simulator.client.HttpClientAdapterImpl; +import org.onap.pnfsimulator.simulator.client.HttpResponseAdapter; import org.onap.pnfsimulator.simulator.client.utils.ssl.SslAuthenticationHelper; import org.onap.pnfsimulator.simulator.scheduler.EventScheduler; import org.onap.pnfsimulator.simulatorconfig.SimulatorConfig; @@ -93,14 +94,14 @@ public class SimulatorService { patchedJsonWithVariablesSubstituted); } - public void triggerOneTimeEvent(FullEvent event) throws IOException, GeneralSecurityException { + public HttpResponseAdapter triggerOneTimeEvent(FullEvent event) throws IOException, GeneralSecurityException { KeywordsHandler keywordsHandler = new KeywordsHandler(new KeywordsExtractor(), id -> 1); JsonObject withKeywordsSubstituted = keywordsHandler.substituteKeywords(event.getEvent(), "").getAsJsonObject(); HttpClientAdapter client = createHttpClientAdapter(event.getVesServerUrl()); eventDataService.persistEventData(EMPTY_JSON_OBJECT, withKeywordsSubstituted, event.getEvent(), EMPTY_JSON_OBJECT); - client.send(withKeywordsSubstituted.toString()); + return client.send(withKeywordsSubstituted.toString()); } public SimulatorConfig getConfiguration() { diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/client/HttpApacheResponseAdapterFactory.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/client/HttpApacheResponseAdapterFactory.java new file mode 100644 index 0000000..36ba922 --- /dev/null +++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/client/HttpApacheResponseAdapterFactory.java @@ -0,0 +1,45 @@ +/* + * ============LICENSE_START======================================================= + * PNF-REGISTRATION-HANDLER + * ================================================================================ + * Copyright (C) 2021 Nokia. 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.pnfsimulator.simulator.client; + +import org.apache.http.HttpResponse; +import org.apache.http.util.EntityUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; + +public class HttpApacheResponseAdapterFactory { + + private static final Logger LOGGER = LoggerFactory.getLogger(HttpApacheResponseAdapterFactory.class); + + public HttpResponseAdapter create(HttpResponse response) { + String message; + try { + message = EntityUtils.toString(response.getEntity()); + } catch (IllegalArgumentException | IOException e) { + LOGGER.warn("Response from VES was empty"); + message = ""; + } + return new HttpResponseAdapter(response.getStatusLine().getStatusCode(), message); + } + +} diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/client/HttpClientAdapter.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/client/HttpClientAdapter.java index e7d113d..8cb6aa2 100644 --- a/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/client/HttpClientAdapter.java +++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/client/HttpClientAdapter.java @@ -22,5 +22,6 @@ package org.onap.pnfsimulator.simulator.client; public interface HttpClientAdapter { - void send(String content); + HttpResponseAdapter send(String content); + } diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/client/HttpClientAdapterImpl.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/client/HttpClientAdapterImpl.java index 5d2a024..4f249b9 100644 --- a/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/client/HttpClientAdapterImpl.java +++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/client/HttpClientAdapterImpl.java @@ -48,11 +48,12 @@ public class HttpClientAdapterImpl implements HttpClientAdapter { private static final String CONTENT_TYPE = "Content-Type"; private static final String APPLICATION_JSON = "application/json"; private static final Marker INVOKE = MarkerFactory.getMarker("INVOKE"); + private static final HttpApacheResponseAdapterFactory responseFactory = new HttpApacheResponseAdapterFactory(); private final HttpClient client; private final String targetUrl; public HttpClientAdapterImpl(String targetUrl, SslAuthenticationHelper sslAuthenticationHelper) - throws IOException, GeneralSecurityException { + throws IOException, GeneralSecurityException { this.client = HttpClientFactoryFacade.create(targetUrl, sslAuthenticationHelper); this.targetUrl = targetUrl; } @@ -63,14 +64,18 @@ public class HttpClientAdapterImpl implements HttpClientAdapter { } @Override - public void send(String content) { + public HttpResponseAdapter send(String content) { + HttpResponseAdapter vesResponse; try { HttpResponse response = sendAndRetrieve(content); - EntityUtils.consumeQuietly(response.getEntity()); //response has to be fully consumed otherwise apache won't release connection LOGGER.info(INVOKE, "Message sent, ves response code: {}", response.getStatusLine()); + vesResponse = responseFactory.create(response); + EntityUtils.consumeQuietly(response.getEntity()); //response has to be fully consumed otherwise apache won't release connection } catch (IOException e) { LOGGER.warn("Error sending message to ves: {}", e.getMessage(), e.getCause()); + vesResponse = new HttpResponseAdapter(421, String.format("Fail to connect with ves: %s", e.getMessage())); } + return vesResponse; } private HttpResponse sendAndRetrieve(String content) throws IOException { diff --git a/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/client/HttpResponseAdapter.java b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/client/HttpResponseAdapter.java new file mode 100644 index 0000000..e78b8a3 --- /dev/null +++ b/pnfsimulator/src/main/java/org/onap/pnfsimulator/simulator/client/HttpResponseAdapter.java @@ -0,0 +1,41 @@ +/* + * ============LICENSE_START======================================================= + * PNF-REGISTRATION-HANDLER + * ================================================================================ + * Copyright (C) 2021 Nokia. 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.pnfsimulator.simulator.client; + +public class HttpResponseAdapter { + + private final int code; + private final String message; + + public HttpResponseAdapter(int code, String message) { + this.code = code; + this.message = message; + } + + public int getCode() { + return code; + } + + public String getMessage() { + return message; + } + +} diff --git a/pnfsimulator/src/test/java/org/onap/pnfsimulator/rest/SimulatorControllerTest.java b/pnfsimulator/src/test/java/org/onap/pnfsimulator/rest/SimulatorControllerTest.java index 4fa5021..8dba750 100644 --- a/pnfsimulator/src/test/java/org/onap/pnfsimulator/rest/SimulatorControllerTest.java +++ b/pnfsimulator/src/test/java/org/onap/pnfsimulator/rest/SimulatorControllerTest.java @@ -36,6 +36,7 @@ import org.onap.pnfsimulator.rest.model.FullEvent; import org.onap.pnfsimulator.rest.model.SimulatorParams; import org.onap.pnfsimulator.rest.model.SimulatorRequest; import org.onap.pnfsimulator.simulator.SimulatorService; +import org.onap.pnfsimulator.simulator.client.HttpResponseAdapter; import org.onap.pnfsimulator.simulatorconfig.SimulatorConfig; import org.quartz.SchedulerException; import org.springframework.http.MediaType; @@ -81,6 +82,7 @@ class SimulatorControllerTest { private static final String SAMPLE_ID = "sampleId"; private static final Gson GSON_OBJ = new Gson(); private static final String JOB_NAME = "testJobName"; + private static final HttpResponseAdapter TEST_HTTP_ACCEPTED_RESPONSE = new HttpResponseAdapter(202,""); private static String simulatorRequestBody; private MockMvc mockMvc; @InjectMocks @@ -103,6 +105,7 @@ class SimulatorControllerTest { void setup() throws IOException, SchedulerException, GeneralSecurityException { MockitoAnnotations.initMocks(this); when(simulatorService.triggerEvent(any())).thenReturn("jobName"); + when(simulatorService.triggerOneTimeEvent(any())).thenReturn(TEST_HTTP_ACCEPTED_RESPONSE); mockMvc = MockMvcBuilders .standaloneSetup(controller) .build(); diff --git a/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/SimulatorServiceTest.java b/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/SimulatorServiceTest.java index 45e1ed4..d5426ec 100644 --- a/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/SimulatorServiceTest.java +++ b/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/SimulatorServiceTest.java @@ -24,6 +24,7 @@ import com.google.gson.Gson; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonSyntaxException; +import org.apache.http.HttpStatus; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; @@ -33,6 +34,8 @@ import org.onap.pnfsimulator.rest.model.FullEvent; import org.onap.pnfsimulator.rest.model.SimulatorParams; import org.onap.pnfsimulator.rest.model.SimulatorRequest; import org.onap.pnfsimulator.simulator.client.HttpClientAdapter; +import org.onap.pnfsimulator.simulator.client.HttpResponseAdapter; +import org.onap.pnfsimulator.simulator.client.HttpTestUtils; import org.onap.pnfsimulator.simulator.client.utils.ssl.SslAuthenticationHelper; import org.onap.pnfsimulator.simulator.scheduler.EventScheduler; import org.onap.pnfsimulator.simulatorconfig.SimulatorConfig; @@ -46,12 +49,9 @@ import java.security.GeneralSecurityException; import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; @@ -67,7 +67,7 @@ class SimulatorServiceTest { private static final JsonObject VALID_PATCH = GSON.fromJson("{\"event\": {\n" + " \"commonEventHeader\": {\n" + " \"sourceName\": \"SomeCustomSource\"}}}\n", JsonObject.class); - private static JsonObject VALID_FULL_EVENT = GSON.fromJson("{\"event\": {\n" + private static final JsonObject VALID_FULL_EVENT = GSON.fromJson("{\"event\": {\n" + " \"commonEventHeader\": {\n" + " \"domain\": \"notification\",\n" + " \"eventName\": \"vFirewallBroadcastPackets\"\n" @@ -77,7 +77,7 @@ class SimulatorServiceTest { + " \"name\": \"A20161221.1031-1041.bin.gz\",\n" + " \"hashMap\": {\n" + " \"fileformatType\": \"org.3GPP.32.435#measCollec\"}}]}}}", JsonObject.class); - private static JsonObject FULL_EVENT_WITH_KEYWORDS = GSON.fromJson("{\"event\":{ \n" + private static final JsonObject FULL_EVENT_WITH_KEYWORDS = GSON.fromJson("{\"event\":{ \n" + " \"commonEventHeader\":{ \n" + " \"domain\":\"notification\",\n" + " \"eventName\":\"#RandomString(20)\",\n" @@ -99,14 +99,15 @@ class SimulatorServiceTest { private final ArgumentCaptor eventIdCaptor = ArgumentCaptor.forClass(String.class); private final ArgumentCaptor vesUrlCaptor = ArgumentCaptor.forClass(String.class); private final ArgumentCaptor eventContentCaptor = ArgumentCaptor.forClass(String.class); + private final SslAuthenticationHelper sslAuthenticationHelper = new SslAuthenticationHelper(); + private final TemplatePatcher templatePatcher = new TemplatePatcher(); + private final TemplateReader templateReader = new FilesystemTemplateReader( + "src/test/resources/org/onap/pnfsimulator/simulator/", GSON); + private SimulatorService simulatorService; private EventDataService eventDataService; private EventScheduler eventScheduler; private SimulatorConfigService simulatorConfigService; - private SslAuthenticationHelper sslAuthenticationHelper = new SslAuthenticationHelper() ; - private static TemplatePatcher templatePatcher = new TemplatePatcher(); - private static TemplateReader templateReader = new FilesystemTemplateReader( - "src/test/resources/org/onap/pnfsimulator/simulator/", GSON); @BeforeEach void setUp() throws MalformedURLException { @@ -197,8 +198,7 @@ class SimulatorServiceTest { new SslAuthenticationHelper())); HttpClientAdapter adapterMock = mock(HttpClientAdapter.class); - doNothing().when(adapterMock).send(eventContentCaptor.capture()); - doReturn(adapterMock).when(spiedTestedService).createHttpClientAdapter(any(String.class)); + prepareMocksWithAcceptedResponse(spiedTestedService, adapterMock); FullEvent event = new FullEvent(VES_URL, VALID_FULL_EVENT); spiedTestedService.triggerOneTimeEvent(event); @@ -218,8 +218,7 @@ class SimulatorServiceTest { ); HttpClientAdapter adapterMock = mock(HttpClientAdapter.class); - doNothing().when(adapterMock).send(eventContentCaptor.capture()); - doReturn(adapterMock).when(spiedTestedService).createHttpClientAdapter(any(String.class)); + prepareMocksWithAcceptedResponse(spiedTestedService, adapterMock); FullEvent event = new FullEvent(VES_URL, FULL_EVENT_WITH_KEYWORDS); spiedTestedService.triggerOneTimeEvent(event); @@ -262,6 +261,12 @@ class SimulatorServiceTest { assertTrue(simulatorService.cancelEvent(jobName)); } + private void prepareMocksWithAcceptedResponse(SimulatorService spiedTestedService, HttpClientAdapter adapterMock) throws IOException, GeneralSecurityException { + HttpResponseAdapter response = new HttpResponseAdapter(HttpStatus.SC_ACCEPTED, HttpTestUtils.HTTP_MESSAGE_ACCEPTER); + doReturn(response).when(adapterMock).send(eventContentCaptor.capture()); + doReturn(adapterMock).when(spiedTestedService).createHttpClientAdapter(any(String.class)); + } + private void assertEventHasExpectedStructure(String expectedVesUrl, String templateName, String sourceNameString) throws SchedulerException, IOException, GeneralSecurityException { verify(eventScheduler, times(1)).scheduleEvent(vesUrlCaptor.capture(), intervalCaptor.capture(), repeatCountCaptor.capture(), templateNameCaptor.capture(), eventIdCaptor.capture(), bodyCaptor.capture()); diff --git a/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/client/HttpApacheResponseAdapterFactoryTest.java b/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/client/HttpApacheResponseAdapterFactoryTest.java new file mode 100644 index 0000000..2f8c6b3 --- /dev/null +++ b/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/client/HttpApacheResponseAdapterFactoryTest.java @@ -0,0 +1,98 @@ +/* + * ============LICENSE_START======================================================= + * PNF-REGISTRATION-HANDLER + * ================================================================================ + * Copyright (C) 2021 Nokia. 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.pnfsimulator.simulator.client; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.BeforeEach; + +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.onap.pnfsimulator.simulator.client.HttpTestUtils.createMockedHttpEntity; +import static org.onap.pnfsimulator.simulator.client.HttpTestUtils.createStatusLine; + +class HttpApacheResponseAdapterFactoryTest { + + private HttpResponse httpResponse; + + @BeforeEach + void setup() { + httpResponse = mock(HttpResponse.class); + } + + @Test + void shouldCreateCorrectHttpResponseAdapterFromApacheHttpAcceptedResponse() throws IOException { + // given + final int responseCode = HttpStatus.SC_ACCEPTED; + final String responseBody = HttpTestUtils.HTTP_MESSAGE_ACCEPTER; + prepareHttpResponseMock(responseCode, createMockedHttpEntity(responseBody)); + + // when + HttpResponseAdapter httpResponseAdapter = new HttpApacheResponseAdapterFactory().create(httpResponse); + + // then + assertHttpResponseIsCorrect(responseCode, responseBody, httpResponseAdapter); + } + + + @Test + void shouldCreateCorrectHttpResponseAdapterFromApacheHttpForbiddenResponse() throws IOException { + // given + final int responseCode = HttpStatus.SC_FORBIDDEN; + final String responseBody = HttpTestUtils.HTTP_MESSAGE_FORBIDDEN; + prepareHttpResponseMock(responseCode, createMockedHttpEntity(responseBody)); + + // when + HttpResponseAdapter httpResponseAdapter = new HttpApacheResponseAdapterFactory().create(httpResponse); + + // then + assertHttpResponseIsCorrect(responseCode, responseBody, httpResponseAdapter); + } + + @Test + void shouldCreateCorrectHttpResponseAdapterFromApacheHttpResponseWithEmptyEntity() { + // given + final int responseCode = HttpStatus.SC_INTERNAL_SERVER_ERROR; + prepareHttpResponseMock(responseCode, null); + + // when + HttpResponseAdapter httpResponseAdapter = new HttpApacheResponseAdapterFactory().create(httpResponse); + + + assertHttpResponseIsCorrect(responseCode, "", httpResponseAdapter); + } + + private void prepareHttpResponseMock(int responseCode, HttpEntity httpEntity) { + doReturn(createStatusLine(responseCode)).when(httpResponse).getStatusLine(); + doReturn(httpEntity).when(httpResponse).getEntity(); + } + + private void assertHttpResponseIsCorrect(int responseCode, String responseBody, HttpResponseAdapter httpResponseAdapter) { + assertEquals(responseCode, httpResponseAdapter.getCode()); + assertEquals(responseBody, httpResponseAdapter.getMessage()); + } + +} diff --git a/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/client/HttpClientAdapterImplTest.java b/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/client/HttpClientAdapterImplTest.java index 9eaab5c..cfb03d0 100644 --- a/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/client/HttpClientAdapterImplTest.java +++ b/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/client/HttpClientAdapterImplTest.java @@ -21,6 +21,7 @@ package org.onap.pnfsimulator.simulator.client; import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; import org.apache.http.client.HttpClient; import org.apache.http.conn.socket.PlainConnectionSocketFactory; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; @@ -33,11 +34,15 @@ import java.net.MalformedURLException; import java.security.GeneralSecurityException; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import static org.onap.pnfsimulator.simulator.client.HttpTestUtils.createMockedHttpEntity; +import static org.onap.pnfsimulator.simulator.client.HttpTestUtils.createStatusLine; class HttpClientAdapterImplTest { @@ -55,12 +60,17 @@ class HttpClientAdapterImplTest { @Test void sendShouldSuccessfullySendRequestGivenValidUrl() throws IOException { - assertAdapterSentRequest("http://valid-url:8080"); + assertAdapterSentRequest("http://valid-url:8080", HttpStatus.SC_FORBIDDEN, HttpTestUtils.HTTP_MESSAGE_FORBIDDEN); } @Test void sendShouldSuccessfullySendRequestGivenValidUrlUsingHttps() throws IOException { - assertAdapterSentRequest("https://valid-url:8443"); + assertAdapterSentRequest("https://valid-url:8443", HttpStatus.SC_ACCEPTED, HttpTestUtils.HTTP_MESSAGE_ACCEPTER); + } + + @Test + void sendShouldFailToSendRequestGivenInvalidUrlUsingAdnShouldInformUser() throws IOException { + assertAdapterInformsUserWhenServiceIsUnavailable("https://invalid-url:8080"); } @Test @@ -88,13 +98,29 @@ class HttpClientAdapterImplTest { } } - private void assertAdapterSentRequest(String targetUrl) throws IOException { + private void assertAdapterSentRequest(String targetUrl, int responseCode, String responseMessage) throws IOException { HttpClientAdapter adapter = new HttpClientAdapterImpl(httpClient, targetUrl); doReturn(httpResponse).when(httpClient).execute(any()); + doReturn(createStatusLine(responseCode)).when(httpResponse).getStatusLine(); + doReturn(createMockedHttpEntity(responseMessage)).when(httpResponse).getEntity(); + + HttpResponseAdapter response = adapter.send("test-msg"); + + verify(httpClient).execute(any()); + assertEquals(responseCode, response.getCode()); + assertEquals(responseMessage, response.getMessage()); + } - adapter.send("test-msg"); + private void assertAdapterInformsUserWhenServiceIsUnavailable(String targetUrl) throws IOException { + HttpClientAdapter adapter = new HttpClientAdapterImpl(httpClient, targetUrl); + String exceptionMessage = "test message"; + doThrow(new IOException(exceptionMessage)).when(httpClient).execute(any()); + + HttpResponseAdapter response = adapter.send("test-msg"); verify(httpClient).execute(any()); - verify(httpResponse).getStatusLine(); + assertEquals(421, response.getCode()); + assertEquals(String.format("Fail to connect with ves: %s", exceptionMessage), response.getMessage()); } + } diff --git a/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/client/HttpTestUtils.java b/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/client/HttpTestUtils.java new file mode 100644 index 0000000..02ff531 --- /dev/null +++ b/pnfsimulator/src/test/java/org/onap/pnfsimulator/simulator/client/HttpTestUtils.java @@ -0,0 +1,55 @@ +/* + * ============LICENSE_START======================================================= + * PNF-REGISTRATION-HANDLER + * ================================================================================ + * Copyright (C) 2021 Nokia. 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.pnfsimulator.simulator.client; + +import org.apache.http.HttpEntity; +import org.apache.http.ProtocolVersion; +import org.apache.http.message.BasicStatusLine; + +import java.io.ByteArrayInputStream; +import java.io.IOException; + +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; + +public class HttpTestUtils { + + private HttpTestUtils() { + } + + public static final String HTTP_MESSAGE_ACCEPTER = "Accepted"; + public static final String HTTP_MESSAGE_FORBIDDEN = "Forbidden"; + + static HttpEntity createMockedHttpEntity(String responseBody) throws IOException { + HttpEntity httpEntity = mock(HttpEntity.class); + doReturn(new ByteArrayInputStream(responseBody.getBytes())).when(httpEntity).getContent(); + return httpEntity; + } + + static BasicStatusLine createStatusLine(int responseCode) { + return new BasicStatusLine( + new ProtocolVersion("1.0.0", 1, 0), + responseCode, + "" + ); + } + +} -- cgit 1.2.3-korg