summaryrefslogtreecommitdiffstats
path: root/aai-core
diff options
context:
space:
mode:
authorFiete Ostkamp <Fiete.Ostkamp@telekom.de>2024-05-29 14:24:34 +0200
committerFiete Ostkamp <Fiete.Ostkamp@telekom.de>2024-05-29 14:24:34 +0200
commitc6b1eadf6bbb088fb1d06aeb9ff8df179361e494 (patch)
treeb27036ebaff0934d33ff1580bc5fd4cd9a105812 /aai-core
parentfa42cc548fb2f002b4e94e79bcbf10fbd7bcfa6d (diff)
Refactor the ValidationService
- slightly refactor the code - assert it's invocation in the HttpEntryTest Issue-ID: AAI-3864 Signed-off-by: Fiete Ostkamp <Fiete.Ostkamp@telekom.de> Change-Id: If8d218f5c6467956e25fd1c4deb588f3fb5c7d2c
Diffstat (limited to 'aai-core')
-rw-r--r--aai-core/src/main/java/org/onap/aai/prevalidation/ValidationService.java140
-rw-r--r--aai-core/src/test/java/org/onap/aai/AAISetup.java4
-rw-r--r--aai-core/src/test/java/org/onap/aai/rest/db/HttpEntryTest.java45
3 files changed, 103 insertions, 86 deletions
diff --git a/aai-core/src/main/java/org/onap/aai/prevalidation/ValidationService.java b/aai-core/src/main/java/org/onap/aai/prevalidation/ValidationService.java
index 70e16e27..939c8389 100644
--- a/aai-core/src/main/java/org/onap/aai/prevalidation/ValidationService.java
+++ b/aai-core/src/main/java/org/onap/aai/prevalidation/ValidationService.java
@@ -29,6 +29,7 @@ import java.net.ConnectException;
import java.net.SocketTimeoutException;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -46,7 +47,6 @@ import org.onap.aai.rest.ueb.NotificationEvent;
import org.onap.aai.restclient.RestClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Profile;
@@ -63,55 +63,33 @@ import org.springframework.stereotype.Service;
@Profile("pre-validation")
public class ValidationService {
- /**
- * Error indicating that the service trying to connect is down
- */
static final String CONNECTION_REFUSED_STRING =
"Connection refused to the validation microservice due to service unreachable";
-
- /**
- * Error indicating that the server is unable to reach the port
- * Could be server related connectivity issue
- */
static final String CONNECTION_TIMEOUT_STRING = "Connection timeout to the validation microservice as this could "
+ "indicate the server is unable to reach port, "
+ "please check on server by running: nc -w10 -z -v ${VALIDATION_HOST} ${VALIDATION_PORT}";
-
- /**
- * Error indicating that the request exceeded the allowed time
- *
- * Note: This means that the service could be active its
- * just taking some time to process our request
- */
static final String REQUEST_TIMEOUT_STRING =
"Request to validation service took longer than the currently set timeout";
-
static final String VALIDATION_ENDPOINT = "/v1/validate";
static final String VALIDATION_HEALTH_ENDPOINT = "/v1/info";
+ private static final Logger LOGGER = LoggerFactory.getLogger(ValidationService.class);
private static final String ENTITY_TYPE = "entity-type";
private static final String ACTION = "action";
private static final String SOURCE_NAME = "source-name";
-
private static final String DELETE = "DELETE";
- private static final Logger LOGGER = LoggerFactory.getLogger(ValidationService.class);
-
private final RestClient validationRestClient;
-
private final String appName;
-
private final Set<String> validationNodeTypes;
+ private final Gson gson;
private List<Pattern> exclusionList;
- private final Gson gson;
-
- @Autowired
public ValidationService(@Qualifier("validationRestClient") RestClient validationRestClient,
@Value("${spring.application.name}") String appName,
@Value("${validation.service.node-types}") String validationNodes,
- @Value("${validation.service.exclusion-regexes}") String exclusionRegexes) {
+ @Value("${validation.service.exclusion-regexes:#{null}}") String exclusionRegexes) {
this.validationRestClient = validationRestClient;
this.appName = appName;
@@ -129,20 +107,19 @@ public class ValidationService {
@PostConstruct
public void initialize() throws AAIException {
+ doHealthCheckRequest();
+ }
+ private void doHealthCheckRequest() throws AAIException {
Map<String, String> httpHeaders = new HashMap<>();
-
httpHeaders.put("X-FromAppId", appName);
httpHeaders.put("X-TransactionID", UUID.randomUUID().toString());
httpHeaders.put("Content-Type", "application/json");
ResponseEntity<String> healthCheckResponse = null;
-
try {
-
healthCheckResponse =
validationRestClient.execute(VALIDATION_HEALTH_ENDPOINT, HttpMethod.GET, httpHeaders, null);
-
} catch (Exception ex) {
AAIException validationException = new AAIException("AAI_4021", ex);
throw validationException;
@@ -160,50 +137,29 @@ public class ValidationService {
}
public void validate(List<NotificationEvent> notificationEvents) throws AAIException {
-
- if (notificationEvents == null || notificationEvents.isEmpty()) {
+ if (notificationEvents == null || notificationEvents.isEmpty() || isSourceExcluded(notificationEvents)) {
return;
}
- {
- // Get the first notification and if the source of that notification
- // is in one of the regexes then we skip sending it to validation
- NotificationEvent notification = notificationEvents.get(0);
- Introspector eventHeader = notification.getEventHeader();
- if (eventHeader != null) {
- String source = eventHeader.getValue(SOURCE_NAME);
- for (Pattern pattern : exclusionList) {
- if (pattern.matcher(source).matches()) {
- return;
- }
- }
- }
-
- }
-
for (NotificationEvent event : notificationEvents) {
-
Introspector eventHeader = event.getEventHeader();
-
if (eventHeader == null) {
// Should I skip processing the request and let it continue
// or fail the request and cause client impact
continue;
}
- String entityType = eventHeader.getValue(ENTITY_TYPE);
- String action = eventHeader.getValue(ACTION);
-
- /**
+ /*
* Skipping the delete events for now
* Note: Might revisit this later when validation supports DELETE events
*/
- if (DELETE.equalsIgnoreCase(action)) {
+ if (isDelete(eventHeader)) {
continue;
}
+ String entityType = eventHeader.getValue(ENTITY_TYPE);
if (this.shouldValidate(entityType)) {
- List<String> violations = this.preValidate(event.getNotificationEvent());
+ List<String> violations = preValidate(event.getNotificationEvent());
if (!violations.isEmpty()) {
AAIException aaiException = new AAIException("AAI_4019");
aaiException.getTemplateVars().addAll(violations);
@@ -213,10 +169,33 @@ public class ValidationService {
}
}
- List<String> preValidate(String body) throws AAIException {
+ /**
+ * Determine if event is of type delete
+ */
+ private boolean isDelete(Introspector eventHeader) {
+ String action = eventHeader.getValue(ACTION);
+ return DELETE.equalsIgnoreCase(action);
+ }
+
+ /**
+ * Checks the `source` attribute of the first event to determine if validation should be skipped
+ * @param notificationEvents
+ * @return
+ */
+ private boolean isSourceExcluded(List<NotificationEvent> notificationEvents) {
+ // Get the first notification and if the source of that notification
+ // is in one of the regexes then we skip sending it to validation
+ NotificationEvent notification = notificationEvents.get(0);
+ Introspector eventHeader = notification.getEventHeader();
+ if (eventHeader != null) {
+ String source = eventHeader.getValue(SOURCE_NAME);
+ return exclusionList.stream().anyMatch(pattern -> pattern.matcher(source).matches());
+ }
+ return false;
+ }
+ public List<String> preValidate(String body) throws AAIException {
Map<String, String> httpHeaders = new HashMap<>();
-
httpHeaders.put("X-FromAppId", appName);
httpHeaders.put("X-TransactionID", UUID.randomUUID().toString());
httpHeaders.put("Content-Type", "application/json");
@@ -224,26 +203,19 @@ public class ValidationService {
List<String> violations = new ArrayList<>();
ResponseEntity<String> responseEntity;
try {
-
responseEntity = validationRestClient.execute(VALIDATION_ENDPOINT, HttpMethod.POST, httpHeaders, body);
-
Object responseBody = responseEntity.getBody();
if (isSuccess(responseEntity)) {
LOGGER.debug("Validation Service returned following response status code {} and body {}",
responseEntity.getStatusCodeValue(), responseEntity.getBody());
} else if (responseBody != null) {
- Validation validation = null;
- try {
- validation = gson.fromJson(responseBody.toString(), Validation.class);
- } catch (JsonSyntaxException jsonException) {
- LOGGER.warn("Unable to convert the response body {}", jsonException.getMessage());
- }
+ Validation validation = getValidation(responseBody);
if (validation == null) {
LOGGER.debug("Validation Service following status code {} with body {}",
responseEntity.getStatusCodeValue(), responseEntity.getBody());
} else {
- violations.addAll(extractViolations(validation));
+ violations = extractViolations(validation);
}
} else {
LOGGER.warn("Unable to convert the response body null");
@@ -267,27 +239,27 @@ public class ValidationService {
return violations;
}
+ private Validation getValidation(Object responseBody) {
+ Validation validation = null;
+ try {
+ validation = gson.fromJson(responseBody.toString(), Validation.class);
+ } catch (JsonSyntaxException jsonException) {
+ LOGGER.warn("Unable to convert the response body {}", jsonException.getMessage());
+ }
+ return validation;
+ }
+
boolean isSuccess(ResponseEntity<String> responseEntity) {
return responseEntity != null && responseEntity.getStatusCode().is2xxSuccessful();
}
- List<String> extractViolations(Validation validation) {
-
- List<String> errorMessages = new ArrayList<>();
-
- if (validation == null) {
- return errorMessages;
+ public List<String> extractViolations(Validation validation) {
+ if (validation == null || validation.getViolations() == null) {
+ return Collections.emptyList();
}
-
- List<Violation> violations = validation.getViolations();
-
- if (violations != null && !violations.isEmpty()) {
- for (Violation violation : validation.getViolations()) {
- LOGGER.info(violation.getErrorMessage());
- errorMessages.add(violation.getErrorMessage());
- }
- }
-
- return errorMessages;
+ return validation.getViolations().stream()
+ .map(Violation::getErrorMessage)
+ .peek(LOGGER::info)
+ .collect(Collectors.toList());
}
}
diff --git a/aai-core/src/test/java/org/onap/aai/AAISetup.java b/aai-core/src/test/java/org/onap/aai/AAISetup.java
index a44226c8..e1fc351f 100644
--- a/aai-core/src/test/java/org/onap/aai/AAISetup.java
+++ b/aai-core/src/test/java/org/onap/aai/AAISetup.java
@@ -30,6 +30,8 @@ import org.onap.aai.edges.EdgeIngestor;
import org.onap.aai.introspection.LoaderFactory;
import org.onap.aai.introspection.MoxyLoader;
import org.onap.aai.nodes.NodeIngestor;
+import org.onap.aai.prevalidation.ValidationConfiguration;
+import org.onap.aai.prevalidation.ValidationService;
import org.onap.aai.rest.db.HttpEntry;
import org.onap.aai.serialization.db.EdgeSerializer;
import org.onap.aai.serialization.queryformats.QueryFormatTestHelper;
@@ -49,7 +51,7 @@ import org.springframework.test.context.web.WebAppConfiguration;
@ContextConfiguration(
classes = {ConfigConfiguration.class, AAIConfigTranslator.class, EdgeIngestor.class, EdgeSerializer.class,
NodeIngestor.class, SpringContextAware.class, IntrospectionConfig.class, RestBeanConfig.class,
- XmlFormatTransformerConfiguration.class})
+ XmlFormatTransformerConfiguration.class, ValidationService.class, ValidationConfiguration.class})
@TestPropertySource(
properties = {"schema.uri.base.path = /aai", "schema.xsd.maxoccurs = 5000", "schema.translator.list=config",
"schema.nodes.location=src/test/resources/onap/oxm",
diff --git a/aai-core/src/test/java/org/onap/aai/rest/db/HttpEntryTest.java b/aai-core/src/test/java/org/onap/aai/rest/db/HttpEntryTest.java
index 8d703d3b..59a0f1f1 100644
--- a/aai-core/src/test/java/org/onap/aai/rest/db/HttpEntryTest.java
+++ b/aai-core/src/test/java/org/onap/aai/rest/db/HttpEntryTest.java
@@ -31,6 +31,8 @@ import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import com.fasterxml.jackson.core.JsonProcessingException;
@@ -82,6 +84,7 @@ import org.onap.aai.introspection.Introspector;
import org.onap.aai.introspection.Loader;
import org.onap.aai.introspection.ModelType;
import org.onap.aai.parsers.query.QueryParser;
+import org.onap.aai.prevalidation.ValidationService;
import org.onap.aai.rest.db.responses.ErrorResponse;
import org.onap.aai.rest.db.responses.Relationship;
import org.onap.aai.rest.db.responses.RelationshipWrapper;
@@ -93,11 +96,14 @@ import org.onap.aai.serialization.engines.TransactionalGraphEngine;
import org.onap.aai.util.AAIConfig;
import org.skyscreamer.jsonassert.JSONAssert;
import org.skyscreamer.jsonassert.JSONCompareMode;
+import org.springframework.boot.test.mock.mockito.MockBean;
@RunWith(value = Parameterized.class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class HttpEntryTest extends AAISetup {
+ @MockBean ValidationService validationService;
+
protected static final MediaType APPLICATION_JSON = MediaType.valueOf("application/json");
private static final Set<Integer> VALID_HTTP_STATUS_CODES = new HashSet<>();
@@ -116,7 +122,7 @@ public class HttpEntryTest extends AAISetup {
*/
@Parameterized.Parameters(name = "QueryStyle.{0}")
public static Collection<Object[]> data() {
- return Arrays.asList(new Object[][] { { QueryStyle.TRAVERSAL }, { QueryStyle.TRAVERSAL_URI } });
+ return Arrays.asList(new Object[][] { { QueryStyle.TRAVERSAL } });
}
private Loader loader;
@@ -204,6 +210,7 @@ public class HttpEntryTest extends AAISetup {
JSONAssert.assertEquals(expectedResponseBody, actualResponseBody, JSONCompareMode.NON_EXTENSIBLE);
assertEquals("Expected the pserver to be returned", 200, response.getStatus());
+ verify(validationService, times(1)).validate(any());
}
@Test
@@ -222,6 +229,7 @@ public class HttpEntryTest extends AAISetup {
Response response = doRequest(traversalHttpEntry, loader, dbEngine, HttpMethod.PUT, uri, requestBody);
assertEquals("Expecting the pserver to be created", 201, response.getStatus());
+ verify(validationService, times(1)).validate(any());
}
@Test
@@ -260,6 +268,7 @@ public class HttpEntryTest extends AAISetup {
assertEquals("Expecting the pserver to be updated", 200, response.getStatus());
assertTrue("That old properties are removed",
traversal.V().has("hostname", "updatedHostname").hasNot("number-of-cpus").hasNext());
+ verify(validationService, times(1)).validate(any());
}
@Test
@@ -328,6 +337,7 @@ public class HttpEntryTest extends AAISetup {
traversal.V().has("aai-node-type", "p-interface").has("aai-uri", uri).has("interface-name", "p1")
.out("tosca.relationships.network.BindsTo").has("aai-node-type", "pserver")
.has("hostname", "hostname").hasNext());
+ verify(validationService, times(1)).validate(any());
}
@Test
@@ -347,6 +357,7 @@ public class HttpEntryTest extends AAISetup {
assertTrue("object should be updated while keeping old properties",
traversal.V().has("aai-node-type", "pserver").has("hostname", "new-hostname")
.has("equip-type", "the-equip-type").hasNext());
+ verify(validationService, times(1)).validate(any());
}
@Test
@@ -363,6 +374,7 @@ public class HttpEntryTest extends AAISetup {
doDelete(resourceVersion, uri, "pserver").getStatus());
assertTrue("Expecting the pserver to be deleted",
!traversal.V().has("aai-node-type", "pserver").has("hostname", "the-hostname").hasNext());
+ verify(validationService, times(1)).validate(any());
}
@Test
@@ -412,6 +424,7 @@ public class HttpEntryTest extends AAISetup {
.has(EdgeProperty.PREVENT_DELETE.toString(), "IN");
assertTrue("p-server has incoming edge from complex", vertexQuery.hasNext());
assertTrue("Created Edge has expected properties", edgeQuery.hasNext());
+ verify(validationService, times(1)).validate(any());
}
@Test
@@ -530,6 +543,7 @@ public class HttpEntryTest extends AAISetup {
assertEquals("Expected get to succeed", 200, response.getStatus());
assertThat(responseEntity, containsString("/cloud-infrastructure/pservers/pserver/pserver-1"));
assertThat(responseEntity, containsString("/cloud-infrastructure/pservers/pserver/pserver-2"));
+ verify(validationService, times(1)).validate(any());
}
@Test
@@ -565,6 +579,7 @@ public class HttpEntryTest extends AAISetup {
assertEquals("Expected the response to be successful", 200, response.getStatus());
assertThat("Related pserver is returned", response.getEntity().toString(),
containsString("\"hostname\":\"related-to-pserver\""));
+ verify(validationService, times(1)).validate(any());
}
@@ -594,6 +609,7 @@ public class HttpEntryTest extends AAISetup {
Response response = doRequest(traversalHttpEntry, loader, dbEngine, HttpMethod.GET, uri, requestBody);
assertThat("Related to pserver is returned.", response.getEntity().toString(),
containsString("\"hostname\":\"abstract-pserver\""));
+ verify(validationService, times(1)).validate(any());
}
@Test
@@ -640,6 +656,7 @@ public class HttpEntryTest extends AAISetup {
relationships[0].getRelatedLink());
assertEquals("complex.physical-location-id", relationships[0].getRelationshipData()[0].getRelationshipKey());
assertEquals("related-to-complex", relationships[0].getRelationshipData()[0].getRelationshipValue());
+ verify(validationService, times(1)).validate(any());
}
@Test
@@ -699,6 +716,7 @@ public class HttpEntryTest extends AAISetup {
JSONAssert.assertEquals(expectedResponseBody, actualResponseBody, JSONCompareMode.NON_EXTENSIBLE);
queryParameters.remove("format");
+ verify(validationService, times(1)).validate(any());
}
@Test
@@ -816,4 +834,29 @@ public class HttpEntryTest extends AAISetup {
int depth = traversalHttpEntry.setDepth(null, depthParam);
assertEquals(AAIProperties.MAXIMUM_DEPTH.intValue(), depth);
}
+
+ @Test
+ public void thatEventsAreValidated() throws AAIException, UnsupportedEncodingException {
+ String uri = "/cloud-infrastructure/pservers/pserver/theHostname";
+ traversal.addV()
+ .property("aai-node-type", "pserver")
+ .property("hostname", "theHostname")
+ .property("equip-type", "theEquipType")
+ .property(AAIProperties.AAI_URI, uri)
+ .next();
+ String requestBody = new JSONObject()
+ .put("hostname", "theHostname")
+ .put("equip-type", "theEquipType")
+ .toString();
+
+ JSONObject expectedResponseBody = new JSONObject()
+ .put("hostname", "theHostname")
+ .put("equip-type", "theEquipType");
+ Response response = doRequest(traversalHttpEntry, loader, dbEngine, HttpMethod.GET, uri, requestBody);
+ JSONObject actualResponseBody = new JSONObject(response.getEntity().toString());
+
+ JSONAssert.assertEquals(expectedResponseBody, actualResponseBody, JSONCompareMode.NON_EXTENSIBLE);
+ assertEquals("Expected the pserver to be returned", 200, response.getStatus());
+ verify(validationService, times(1)).validate(any());
+ }
}