2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2020-2021 Nokia. All rights reserved.
6 * Copyright (C) 2023 AT&T Intellectual Property. All rights reserved.
7 * ================================================================================
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 * ============LICENSE_END=========================================================
22 package org.onap.dcae.restapi;
24 import com.fasterxml.jackson.databind.ObjectMapper;
25 import com.google.common.reflect.TypeToken;
26 import com.google.gson.Gson;
27 import com.networknt.schema.JsonSchema;
28 import io.vavr.collection.HashMap;
29 import org.jetbrains.annotations.NotNull;
30 import org.junit.jupiter.api.BeforeEach;
31 import org.junit.jupiter.api.Test;
32 import org.junit.jupiter.api.extension.ExtendWith;
33 import org.junit.jupiter.params.ParameterizedTest;
34 import org.junit.jupiter.params.provider.Arguments;
35 import org.junit.jupiter.params.provider.MethodSource;
36 import org.mockito.ArgumentCaptor;
37 import org.mockito.Mock;
38 import org.mockito.junit.jupiter.MockitoExtension;
39 import org.onap.dcae.ApplicationSettings;
40 import org.onap.dcae.JSonSchemasSupplier;
41 import org.onap.dcae.common.EventSender;
42 import org.onap.dcae.common.EventTransformation;
43 import org.onap.dcae.common.HeaderUtils;
44 import org.onap.dcae.common.JsonDataLoader;
45 import org.onap.dcae.common.model.InternalException;
46 import org.onap.dcae.common.model.PayloadToLargeException;
47 import org.onap.dcae.common.publishing.DMaaPEventPublisher;
48 import org.onap.dcae.common.validator.StndDefinedDataValidator;
49 import org.slf4j.Logger;
50 import org.springframework.http.HttpStatus;
51 import org.springframework.http.ResponseEntity;
52 import org.springframework.mock.web.MockHttpServletRequest;
53 import org.springframework.web.context.request.RequestContextHolder;
54 import org.springframework.web.context.request.ServletRequestAttributes;
56 import java.io.FileReader;
57 import java.io.IOException;
58 import java.lang.reflect.Type;
60 import java.net.URISyntaxException;
61 import java.util.List;
63 import java.util.stream.Stream;
65 import static org.assertj.core.api.Assertions.assertThat;
66 import static org.junit.jupiter.params.provider.Arguments.arguments;
67 import static org.mockito.ArgumentMatchers.any;
68 import static org.mockito.ArgumentMatchers.anyString;
69 import static org.mockito.ArgumentMatchers.eq;
70 import static org.mockito.Mockito.never;
71 import static org.mockito.Mockito.times;
72 import static org.mockito.Mockito.verify;
73 import static org.mockito.Mockito.when;
75 import java.nio.file.Paths;
77 @ExtendWith(MockitoExtension.class)
78 public class VesRestControllerTest {
80 private static final String EVENT_TRANSFORM_FILE_PATH = "/eventTransform.json";
81 private static final String ACCEPTED = "Successfully send event";
82 private static final String VERSION_V7 = "v7";
83 static final String VES_FAULT_TOPIC = "ves-fault";
84 static final String VES_3_GPP_FAULT_SUPERVISION_TOPIC = "ves-3gpp-fault-supervision";
86 private VesRestController vesRestController;
89 private ApplicationSettings applicationSettings;
92 private Logger logger;
95 private Logger errorLogger;
98 private HeaderUtils headerUtils;
101 private DMaaPEventPublisher eventPublisher;
104 private StndDefinedDataValidator stndDefinedDataValidator;
108 final HashMap<String, String> streamIds = HashMap.of(
109 "fault", VES_FAULT_TOPIC,
110 "3GPP-FaultSupervision", VES_3_GPP_FAULT_SUPERVISION_TOPIC
112 this.vesRestController = new VesRestController(applicationSettings, logger,
113 errorLogger, new EventSender(eventPublisher, streamIds), headerUtils, stndDefinedDataValidator);
117 void shouldReportThatApiVersionIsNotSupported() {
119 when(applicationSettings.isVersionSupported("v20")).thenReturn(false);
120 MockHttpServletRequest request = givenMockHttpServletRequest();
123 final ResponseEntity<String> event = vesRestController.event("", "v20", request);
126 assertThat(event.getStatusCodeValue()).isEqualTo(HttpStatus.BAD_REQUEST.value());
127 assertThat(event.getBody()).isEqualTo("API version v20 is not supported");
128 verifyThatEventWasNotSend();
132 void shouldTransformEventAccordingToEventTransformFile() throws IOException, URISyntaxException{
134 configureEventTransformations();
135 configureHeadersForEventListener();
137 MockHttpServletRequest request = givenMockHttpServletRequest();
138 String validEvent = JsonDataLoader.loadContent("/ves7_valid_30_1_1_event.json");
139 when(eventPublisher.sendEvent(any(), any())).thenReturn((HttpStatus.OK));
142 final ResponseEntity<String> response = vesRestController.event(validEvent, VERSION_V7, request);
145 assertThat(response.getStatusCodeValue()).isEqualTo(HttpStatus.OK.value());
146 assertThat(response.getBody()).isEqualTo(ACCEPTED);
147 verifyThatTransformedEventWasSend(eventPublisher, validEvent);
152 void shouldSendBatchEvent() throws IOException, URISyntaxException {
154 configureEventTransformations();
155 configureHeadersForEventListener();
157 MockHttpServletRequest request = givenMockHttpServletRequest();
159 String validEvent = JsonDataLoader.loadContent("/ves7_batch_valid.json");
160 when(eventPublisher.sendEvent(any(), any())).thenReturn(HttpStatus.OK);
162 final ResponseEntity<String> response = vesRestController.events(validEvent, VERSION_V7, request);
165 assertThat(response.getStatusCodeValue()).isEqualTo(HttpStatus.OK.value());
166 assertThat(response.getBody()).isEqualTo(ACCEPTED);
167 verify(eventPublisher, times(1)).sendEvent(any(),any());
171 void shouldSendStndDomainEventIntoDomainStream() throws IOException, URISyntaxException{
173 configureEventTransformations();
174 configureHeadersForEventListener();
176 MockHttpServletRequest request = givenMockHttpServletRequest();
177 configureSchemasSupplierForStndDefineEvent();
179 String validEvent = JsonDataLoader.loadContent("/ves_stdnDefined_valid.json");
180 when(eventPublisher.sendEvent(any(), any())).thenReturn(HttpStatus.OK);
183 final ResponseEntity<String> response = vesRestController.event(validEvent, VERSION_V7, request);
186 assertThat(response.getStatusCodeValue()).isEqualTo(HttpStatus.OK.value());
187 assertThat(response.getBody()).isEqualTo(ACCEPTED);
188 verify(eventPublisher).sendEvent(any(),eq(VES_3_GPP_FAULT_SUPERVISION_TOPIC));
193 void shouldReportThatStndDomainEventHasntGotNamespaceParameter() throws IOException, URISyntaxException {
195 configureEventTransformations();
196 configureHeadersForEventListener();
198 MockHttpServletRequest request = givenMockHttpServletRequest();
199 configureSchemasSupplierForStndDefineEvent();
201 String validEvent = JsonDataLoader.loadContent("/ves_stdnDefined_missing_namespace_invalid.json");
204 final ResponseEntity<String> response = vesRestController.event(validEvent, VERSION_V7, request);
207 assertThat(response.getStatusCodeValue()).isEqualTo(HttpStatus.BAD_REQUEST.value());
211 "Mandatory input %1 %2 is missing from request",
212 List.of("attribute", "event.commonEventHeader.stndDefinedNamespace")
214 verifyThatEventWasNotSend();
218 void shouldReportThatStndDomainEventNamespaceParameterIsEmpty() throws IOException, URISyntaxException {
220 configureEventTransformations();
221 configureHeadersForEventListener();
223 MockHttpServletRequest request = givenMockHttpServletRequest();
224 configureSchemasSupplierForStndDefineEvent();
226 String validEvent = JsonDataLoader.loadContent("/ves_stdnDefined_empty_namespace_invalid.json");
229 final ResponseEntity<String> response = vesRestController.event(validEvent, VERSION_V7, request);
232 assertThat(response.getStatusCodeValue()).isEqualTo(HttpStatus.BAD_REQUEST.value());
236 "Mandatory input %1 %2 is empty in request",
237 List.of("attribute", "event.commonEventHeader.stndDefinedNamespace")
239 verifyThatEventWasNotSend();
243 void shouldNotSendStndDomainEventWhenTopicCannotBeFoundInConfiguration() throws IOException, URISyntaxException {
245 configureEventTransformations();
246 configureHeadersForEventListener();
248 MockHttpServletRequest request = givenMockHttpServletRequest();
249 String validEvent = JsonDataLoader.loadContent("/ves_stdnDefined_valid_unknown_topic.json");
252 final ResponseEntity<String> response = vesRestController.event(validEvent, VERSION_V7, request);
255 assertThat(response.getStatusCodeValue()).isEqualTo(HttpStatus.BAD_REQUEST.value());
256 verifyThatEventWasNotSend();
260 void shouldExecuteStndDefinedValidationWhenFlagIsOnTrue() throws IOException, URISyntaxException{
262 configureEventTransformations();
263 configureHeadersForEventListener();
265 MockHttpServletRequest request = givenMockHttpServletRequest();
266 String validEvent = JsonDataLoader.loadContent("/ves7_batch_with_stndDefined_valid.json");
267 when(applicationSettings.getExternalSchemaValidationCheckflag()).thenReturn(true);
268 when(eventPublisher.sendEvent(any(), any())).thenReturn(HttpStatus.OK);
270 final ResponseEntity<String> response = vesRestController.events(validEvent, VERSION_V7, request);
273 assertThat(response.getStatusCodeValue()).isEqualTo(HttpStatus.OK.value());
274 assertThat(response.getBody()).isEqualTo(ACCEPTED);
275 verify(stndDefinedDataValidator, times(2)).validate(any());
279 void shouldNotExecuteStndDefinedValidationWhenFlagIsOnFalse() throws IOException, URISyntaxException {
281 configureEventTransformations();
282 configureHeadersForEventListener();
284 MockHttpServletRequest request = givenMockHttpServletRequest();
285 String validEvent = JsonDataLoader.loadContent("/ves7_batch_with_stndDefined_valid.json");
286 when(applicationSettings.getExternalSchemaValidationCheckflag()).thenReturn(false);
287 when(eventPublisher.sendEvent(any(), any())).thenReturn(HttpStatus.OK);
290 final ResponseEntity<String> response = vesRestController.events(validEvent, VERSION_V7, request);
293 assertThat(response.getStatusCodeValue()).isEqualTo(HttpStatus.OK.value());
294 assertThat(response.getBody()).isEqualTo(ACCEPTED);
295 verify(stndDefinedDataValidator, times(0)).validate(any());
299 void shouldReturn413WhenPayloadIsTooLarge() throws IOException, URISyntaxException {
301 configureEventTransformations();
302 configureHeadersForEventListener();
304 MockHttpServletRequest request = givenMockHttpServletRequest();
305 when(eventPublisher.sendEvent(any(), any())).thenThrow(new PayloadToLargeException());
306 String validEvent = JsonDataLoader.loadContent("/ves7_valid_30_1_1_event.json");
309 final ResponseEntity<String> response = vesRestController.event(validEvent, VERSION_V7, request);
312 assertThat(response.getStatusCodeValue()).isEqualTo(HttpStatus.PAYLOAD_TOO_LARGE.value());
316 "The following service error occurred: %1. Error code is %2",
317 List.of("Request Entity Too Large","413")
322 @MethodSource("errorsCodeAndResponseBody")
323 void shouldMapErrorTo503AndReturnOriginalBody(ApiException apiException,String bodyVariable,String bodyVariable2) throws IOException, URISyntaxException {
325 configureEventTransformations();
326 configureHeadersForEventListener();
328 MockHttpServletRequest request = givenMockHttpServletRequest();
329 when(eventPublisher.sendEvent(any(), any())).thenThrow(new InternalException(apiException));
330 String validEvent = JsonDataLoader.loadContent("/ves7_valid_30_1_1_event.json");
333 final ResponseEntity<String> response = vesRestController.event(validEvent, VERSION_V7, request);
336 assertThat(response.getStatusCodeValue()).isEqualTo(HttpStatus.SERVICE_UNAVAILABLE.value());
340 "The following service error occurred: %1. Error code is %2",
341 List.of(bodyVariable,bodyVariable2)
345 private static Stream<Arguments> errorsCodeAndResponseBody() {
347 arguments(ApiException.NOT_FOUND, "Not Found","404"),
348 arguments(ApiException.REQUEST_TIMEOUT, "Request Timeout","408"),
349 arguments(ApiException.TOO_MANY_REQUESTS, "Too Many Requests","429"),
350 arguments(ApiException.INTERNAL_SERVER_ERROR, "Internal Server Error","500"),
351 arguments(ApiException.BAD_GATEWAY, "Bad Gateway","502"),
352 arguments(ApiException.SERVICE_UNAVAILABLE, "Service Unavailable","503"),
353 arguments(ApiException.GATEWAY_TIMEOUT, "Gateway Timeout","504")
357 private void verifyThatEventWasNotSend() {
358 verify(eventPublisher, never()).sendEvent(any(), any());
361 private void configureSchemasSupplierForStndDefineEvent() {
362 String collectorSchemaFile = "{\"v7\":\"./etc/CommonEventFormat_30.2_ONAP.json\"}";
363 final io.vavr.collection.Map<String, JsonSchema> loadedJsonSchemas = new JSonSchemasSupplier().loadJsonSchemas(collectorSchemaFile);
365 when(applicationSettings.eventSchemaValidationEnabled()).thenReturn(true);
366 when(applicationSettings.jsonSchema(eq(VERSION_V7))).thenReturn(loadedJsonSchemas.get(VERSION_V7).get());
369 private void verifyErrorResponse(ResponseEntity<String> response, String messageId, String messageText, List<String> variables) throws com.fasterxml.jackson.core.JsonProcessingException {
370 final Map<String, ?> errorDetails = fetchErrorDetails(response);
371 assertThat((Map<String, String>)errorDetails).containsEntry("messageId", messageId);
372 assertThat((Map<String, String>)errorDetails).containsEntry("text", messageText);
373 assertThat((Map<String, List<String>>)errorDetails).containsEntry("variables", variables);
376 private Map<String, ?> fetchErrorDetails(ResponseEntity<String> response) throws com.fasterxml.jackson.core.JsonProcessingException {
377 final String body = response.getBody();
378 ObjectMapper mapper = new ObjectMapper();
379 Map<String, Map<String, Map<String,String>>> map = mapper.readValue(body, Map.class);
380 return map.get("requestError").get("ServiceException");
383 private void configureEventTransformations() throws IOException, URISyntaxException {
384 final List<EventTransformation> eventTransformations = loadEventTransformations();
385 when(applicationSettings.isVersionSupported(VERSION_V7)).thenReturn(true);
386 when(applicationSettings.eventTransformingEnabled()).thenReturn(true);
387 when(applicationSettings.getEventTransformations()).thenReturn((eventTransformations));
390 private void configureHeadersForEventListener() {
391 when(headerUtils.getRestApiIdentify(anyString())).thenReturn("eventListener");
392 when(applicationSettings.getApiVersionDescriptionFilepath()).thenReturn("etc/api_version_description.json");
395 private void verifyThatTransformedEventWasSend(DMaaPEventPublisher eventPublisher, String eventBeforeTransformation) {
396 // event before transformation
397 assertThat(eventBeforeTransformation).contains("\"version\": \"4.0.1\"");
398 assertThat(eventBeforeTransformation).contains("\"faultFieldsVersion\": \"4.0\"");
400 ArgumentCaptor<List> argument = ArgumentCaptor.forClass(List.class);
401 ArgumentCaptor<String> domain = ArgumentCaptor.forClass(String.class);
402 verify(eventPublisher).sendEvent(argument.capture(), domain.capture());
404 final String transformedEvent = argument.getValue().toString();
405 final String eventSentAtTopic = domain.getValue();
407 // event after transformation
408 assertThat(transformedEvent).contains("\"priority\":\"High\",\"version\":3,");
409 assertThat(transformedEvent).contains(",\"faultFieldsVersion\":3,\"specificProblem");
410 assertThat(eventSentAtTopic).isEqualTo(VES_FAULT_TOPIC);
414 private MockHttpServletRequest givenMockHttpServletRequest() {
415 MockHttpServletRequest request = new MockHttpServletRequest();
416 request.setContentType("application/json");
418 RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request));
422 private List<EventTransformation> loadEventTransformations() throws IOException, URISyntaxException {
423 Type EVENT_TRANSFORM_LIST_TYPE = new TypeToken<List<EventTransformation>>() {
426 URI resource = this.getClass().getResource(EVENT_TRANSFORM_FILE_PATH).toURI();
427 try (FileReader fr = new FileReader(resource.getPath())) {
428 return new Gson().fromJson(fr, EVENT_TRANSFORM_LIST_TYPE);