diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8425cb0e..41ed536a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,6 @@
+## 2.4.3
+- Optional validation of PURLs added.
+
## 2.4.2
- Typo fix.
diff --git a/pom.xml b/pom.xml
index e31dda0b..f1cd2392 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,10 +4,10 @@
com.github.eiffel-community
eiffel-remrem-parent
- 2.0.14
+ 2.0.18
eiffel-remrem-semantics
- 2.4.2
+ 2.4.3
jar
5.0.1.201806211838-r
@@ -24,7 +24,7 @@
com.github.eiffel-community
eiffel-remrem-protocol-interface
- 2.2.0
+ 2.2.1
compile
@@ -48,6 +48,11 @@
semver4j
3.1.0
+
+ com.github.package-url
+ packageurl-java
+ 1.5.0
+
org.glassfish.hk2.external
javax.inject
diff --git a/src/main/java/com/ericsson/eiffel/remrem/semantics/SemanticsService.java b/src/main/java/com/ericsson/eiffel/remrem/semantics/SemanticsService.java
index 14948f05..0ff5bc1d 100644
--- a/src/main/java/com/ericsson/eiffel/remrem/semantics/SemanticsService.java
+++ b/src/main/java/com/ericsson/eiffel/remrem/semantics/SemanticsService.java
@@ -121,8 +121,8 @@ public class SemanticsService implements MsgService {
private static final String SUPPORTED_EVENT_TYPES = "SUPPORTED_EVENT_TYPES";
private static final String RESULT = "result";
private static final String UNKNOWN_EVENT_TYPE_REQUESTED = "Unknown event type requested";
- private static final String EVENT_PARAMS = "eventParams";
- private static final String MSG_PARAMS = "msgParams";
+ public static final String EVENT_PARAMS = "eventParams";
+ public static final String MSG_PARAMS = "msgParams";
private static final String MESSAGE = "message";
private static final String CAUSE = "cause";
private static final String EIFFELSEMANTICS = "eiffelsemantics";
@@ -131,6 +131,7 @@ public class SemanticsService implements MsgService {
private static final String TYPE = "type";
private static final String SOURCE = "source";
private static final String DOMAIN_ID = "domainId";
+ public static final String IDENTITY = "identity";
private static final String PROTOCOL = "eiffel";
private static final String DOT = ".";
private static final String SEMANTICS_EDITION_NAME = "semanticsEditionName";
@@ -143,6 +144,11 @@ public class SemanticsService implements MsgService {
private static Gson gson = new Gson();
private static Map> eventTypes = SemanticsService.eventType();
+ /**
+ * Name of validation property applicable for generateMsg(String, JsonObject, Map).
+ */
+ public static final String VALIDATE_PURL_FOR_ART_C = "validatePurlForArtC";
+
public SemanticsService() {
for (final EiffelEventType msg : EiffelEventType.values()) {
supportedEventTypes.add(msg.getEventName());
@@ -203,6 +209,24 @@ public String generateMsg(String msgType, JsonObject bodyJson) {
@Override
public String generateMsg(String msgType, JsonObject bodyJson, Boolean lenientValidation) {
+ return generateMsg(msgType, bodyJson, createPropertiesWithLenientValidation(lenientValidation));
+ }
+
+ /**
+ * Takes a partial message in JSON format, validates it and adds mandatory fields before outputting a complete,
+ * valid Eiffel message.
+ *
+ * @param msgType Event type name.
+ * @param bodyJson Event template.
+ * @param validationProperties At the moment the following properties are supported:
+ *
+ * - lenientValidation: boolean,
+ * - validatePurlForArtC: boolean.
+ *
+ * @return the generated and validate Eiffel messages as json String
+ */
+ @Override
+ public String generateMsg(String msgType, JsonObject bodyJson, HashMap validationProperties) {
try {
if (purlSerializerFlag) {
return createErrorResponse("Serializer info of eiffel-remrem-semantics is missing",
@@ -228,10 +252,8 @@ public String generateMsg(String msgType, JsonObject bodyJson, Boolean lenientVa
}
// Compare the input JSON EventType with query parameter(-t) and
- // also
- // check type exist or not,
- // if input JSON EventType is missing adding query parameter as
- // Type.
+ // also check if type exists.
+ // If input JSON EventType is missing adding query parameter as Type.
String inputEventType = getInputEventType(bodyJson);
if (inputEventType == null || inputEventType.isEmpty()) {
bodyJson.get(MSG_PARAMS).getAsJsonObject().get(META).getAsJsonObject().addProperty(TYPE,
@@ -243,7 +265,7 @@ public String generateMsg(String msgType, JsonObject bodyJson, Boolean lenientVa
}
Event event = eventCreation(eventType, msgNodes, eventNodes);
String result = gson.toJson(event);
- JsonObject outputValidate = outputValidate(eiffelType, result, lenientValidation);
+ JsonObject outputValidate = outputValidate(eiffelType, result, validationProperties);
return gson.toJson(outputValidate);
} catch (EiffelValidationException e) {
@@ -284,13 +306,19 @@ private String createErrorResponse(final String message, final ArrayList
return errorResponse.toString();
}
- private JsonObject outputValidate(EiffelEventType eiffelType, String jsonStringInput, Boolean lenientValidation) throws EiffelValidationException {
+ private JsonObject outputValidate(EiffelEventType eiffelType, String jsonStringInput, HashMap validationProperiets) throws EiffelValidationException {
EiffelValidator validator = EiffelOutputValidatorFactory.getEiffelValidator(eiffelType);
JsonObject jsonObject = new JsonParser().parse(jsonStringInput).getAsJsonObject();
- JsonObject validate = validator.validate(jsonObject, lenientValidation);
+ JsonObject validated = validator.validate(jsonObject, validationProperiets);
// custom validations on an event which is not covered in schema.
- validator.customValidation(validate);
- return validate;
+ validator.customValidation(validated, validationProperiets);
+ return validated;
+ }
+
+ private HashMap createPropertiesWithLenientValidation(boolean lenientValidation) {
+ HashMap validationProperties = new HashMap<>();
+ validationProperties.put(LENIENT_VALIDATION, lenientValidation);
+ return validationProperties;
}
@Override
@@ -299,12 +327,17 @@ public ValidationResult validateMsg(String msgType, JsonObject jsonvalidateMessa
}
@Override
- public ValidationResult validateMsg(String msgType, JsonObject jsonvalidateMessage, Boolean lenientValidation) {
+ public ValidationResult validateMsg(String msgType, JsonObject jsonValidationMessage, Boolean lenientValidation) {
+ return validateMsg(msgType, jsonValidationMessage, createPropertiesWithLenientValidation(lenientValidation));
+ }
+
+ @Override
+ public ValidationResult validateMsg(String msgType, JsonObject jsonvalidateMessage, HashMap validationProperties) {
ValidationResult validationResult = null;
EiffelEventType eiffelType = EiffelEventType.fromString(msgType);
String result = gson.toJson(jsonvalidateMessage);
try {
- outputValidate(eiffelType, result, lenientValidation);
+ outputValidate(eiffelType, result, validationProperties);
validationResult = new ValidationResult(true, "");
} catch (EiffelValidationException e) {
validationResult = new ValidationResult(false, e.getLocalizedMessage());
@@ -375,8 +408,7 @@ public JsonElement getEventTemplate(String eventType) {
* Returns Family Routing Key Word from the messaging library based on the
* eiffel message eventType.
*
- * @param JsonObject
- * eiffelMessage
+ * @param eiffelMessage the message
* @return family routing key word in String format.
*/
private String getFamily(JsonObject eiffelMessage) {
diff --git a/src/main/java/com/ericsson/eiffel/remrem/semantics/util/PropertiesUtil.java b/src/main/java/com/ericsson/eiffel/remrem/semantics/util/PropertiesUtil.java
new file mode 100644
index 00000000..1b3aed3d
--- /dev/null
+++ b/src/main/java/com/ericsson/eiffel/remrem/semantics/util/PropertiesUtil.java
@@ -0,0 +1,29 @@
+package com.ericsson.eiffel.remrem.semantics.util;
+
+import com.ericsson.eiffel.remrem.semantics.validator.EiffelValidator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.HashMap;
+
+public class PropertiesUtil {
+ public static final Logger log = LoggerFactory.getLogger(PropertiesUtil.class);
+
+ public static T getProperty(HashMap validationProperties, String property) {
+ return getProperty(validationProperties, property, null);
+ }
+
+ public static T getProperty(HashMap validationProperties, String property, T defValue) {
+ Object object = validationProperties.get(property);
+ if (object == null)
+ return defValue;
+ if (object instanceof Boolean) {
+ return (T)object;
+ }
+ else {
+ log.error("Value of property '{}' should be a boolean, but is '{}'; returning default value '{}'",
+ property, object.getClass().getName(), defValue);
+ return defValue;
+ }
+ }
+}
diff --git a/src/main/java/com/ericsson/eiffel/remrem/semantics/validator/EiffelValidator.java b/src/main/java/com/ericsson/eiffel/remrem/semantics/validator/EiffelValidator.java
index c977ffb0..a86b1019 100644
--- a/src/main/java/com/ericsson/eiffel/remrem/semantics/validator/EiffelValidator.java
+++ b/src/main/java/com/ericsson/eiffel/remrem/semantics/validator/EiffelValidator.java
@@ -24,6 +24,12 @@
import java.util.regex.Pattern;
import javax.xml.bind.ValidationException;
+
+import com.ericsson.eiffel.remrem.semantics.EiffelEventType;
+import com.ericsson.eiffel.remrem.semantics.util.PropertiesUtil;
+import com.github.packageurl.MalformedPackageURLException;
+import com.github.packageurl.PackageURL;
+import com.google.gson.*;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -36,13 +42,13 @@
import com.github.fge.jsonschema.core.report.ProcessingReport;
import com.github.fge.jsonschema.main.JsonSchema;
import com.github.fge.jsonschema.main.JsonSchemaFactory;
-import com.google.gson.JsonArray;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-import com.google.gson.JsonParser;
import com.jayway.jsonpath.DocumentContext;
import com.jayway.jsonpath.JsonPath;
+import static com.ericsson.eiffel.remrem.protocol.MsgService.LENIENT_VALIDATION;
+import static com.ericsson.eiffel.remrem.semantics.SemanticsService.*;
+import static com.ericsson.eiffel.remrem.semantics.schemas.EiffelConstants.META;
+
public class EiffelValidator {
private static final String REGEX_PATH = "\\/\\d*";
@@ -94,11 +100,19 @@ public JsonObject validate(JsonObject jsonObjectInput) throws EiffelValidationEx
}
public JsonObject validate(JsonObject jsonObjectInput, Boolean lenientValidation) throws EiffelValidationException {
+ HashMap validationProperties = new HashMap<>();
+ validationProperties.put(LENIENT_VALIDATION, lenientValidation);
+ return validate(jsonObjectInput, validationProperties);
+ }
+
+ public JsonObject validate(JsonObject jsonObjectInput, HashMap validationProperties) throws EiffelValidationException {
JsonArray remremGenerateFailures = new JsonArray();
log.debug("VALIDATING Schema used: {}", schemaResourceName);
try {
final ProcessingReport report = validationSchema.validate(JsonLoader.fromString(jsonObjectInput.toString()),
true);
+ boolean lenientValidation = PropertiesUtil.getProperty(validationProperties, LENIENT_VALIDATION, false);
+
if (!report.isSuccess() && lenientValidation) {
log.info("Trying to revalidate the Eiffel message with only mandatory Eiffel message fields.");
String revalidatedJson = removeErrorProperties(report, jsonObjectInput, remremGenerateFailures);
@@ -143,8 +157,8 @@ private String removeErrorProperties(ProcessingReport report, JsonObject jsonObj
}
String errorPath = element.getAsJsonObject().get(INSTANCE).getAsJsonObject().get(POINTER)
.getAsString();
- JsonObject addValidationFailurs = addValidationFailures(element, processingMessage.getMessage());
- remremGenerateFailures.add(addValidationFailurs);
+ JsonObject addValidationFailures = addValidationFailures(element, processingMessage.getMessage());
+ remremGenerateFailures.add(addValidationFailures);
doc.delete(getPath(errorPath));
}
}
@@ -228,6 +242,16 @@ private String getErrorsList(final ProcessingReport report) {
* @throws EiffelValidationException if any validation fails
*/
public void customValidation(JsonObject jsonObjectInput) throws EiffelValidationException {
+ customValidation(jsonObjectInput, new HashMap<>());
+
+ }
+
+ public void customValidation(JsonObject jsonObjectInput, HashMap validationProperties) throws EiffelValidationException {
+ validateLinks(jsonObjectInput);
+ validateArtC(jsonObjectInput, validationProperties);
+ }
+
+ public void validateLinks(JsonObject jsonObjectInput) throws EiffelValidationException {
// Links validation
Map linksCountMapForEvent = new HashMap();
try {
@@ -271,4 +295,47 @@ public void customValidation(JsonObject jsonObjectInput) throws EiffelValidation
throw new EiffelValidationException(message, e);
}
}
+
+ public void validateArtC(JsonObject jsonObjectInput, HashMap validationProperties) throws EiffelValidationException {
+ Object validate = PropertiesUtil.getProperty(validationProperties, VALIDATE_PURL_FOR_ART_C, true);
+ if (validate == null || validate instanceof Boolean && !((Boolean)validate))
+ // Validation property not set, no need to continue.
+ return;
+
+ JsonObject meta = jsonObjectInput.getAsJsonObject(META);
+ if (meta == null) {
+ return;
+ }
+
+ JsonPrimitive type = meta.getAsJsonPrimitive(TYPE);
+ if (type == null) {
+ return;
+ }
+
+ if (EiffelEventType.fromString(type.getAsString()) != EiffelEventType.ARTIFACT_CREATED) {
+ // Not EiffelArtifactCreatedEvent, nothing to do...
+ return;
+ }
+
+ // OK, this is EiffelArtifactCreatedEvent, lets start checks...
+ JsonObject data = jsonObjectInput.getAsJsonObject(DATA);
+ if (data == null) {
+ return;
+ }
+
+ JsonPrimitive identity = data.getAsJsonPrimitive(IDENTITY);
+ if (identity == null) {
+ return;
+ }
+
+ String purl = identity.getAsString();
+ try {
+ PackageURL packageUrl = new PackageURL(purl);
+ }
+ catch (MalformedPackageURLException e) {
+ String message = "Cannot validate PURL '" + purl + "'";
+ log.debug(message, e);
+ throw new EiffelValidationException(message, e);
+ }
+ }
}
\ No newline at end of file
diff --git a/src/test/java/com/ericsson/eiffel/remrem/semantics/ServiceTest.java b/src/test/java/com/ericsson/eiffel/remrem/semantics/ServiceTest.java
index 200bc32f..31134c27 100644
--- a/src/test/java/com/ericsson/eiffel/remrem/semantics/ServiceTest.java
+++ b/src/test/java/com/ericsson/eiffel/remrem/semantics/ServiceTest.java
@@ -31,10 +31,12 @@
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.HashMap;
import java.util.List;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
+import com.ericsson.eiffel.remrem.semantics.validator.EiffelValidator;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
@@ -88,13 +90,19 @@ public void setUp() throws Exception {
@Test
public void testGenerate() {
+ HashMap properties = new HashMap<>();
+ // TODO Enable PURL validation
+ properties.put(SemanticsService.VALIDATE_PURL_FOR_ART_C, false);
try {
File file = new File("src/test/resources/input");
if (file.exists()) {
for (File inputFile : file.listFiles()) {
+ if (inputFile.isDirectory())
+ continue;
+
JsonObject object = parser.parse(new FileReader(inputFile)).getAsJsonObject();
String msgType=object.get("msgParams").getAsJsonObject().get("meta").getAsJsonObject().get("type").getAsString();
- String msg = service.generateMsg(msgType, object);
+ String msg = service.generateMsg(msgType, object, properties);
Assert.assertTrue(msg.contains("data"));
Assert.assertTrue(msg.contains("meta"));
Assert.assertTrue(msg.contains("links"));
@@ -106,7 +114,39 @@ public void testGenerate() {
Assert.assertFalse(true);
}
}
-
+
+ @Test
+ public void testGeneratePurlValidationOnPass() {
+ String fileName = "src/test/resources/input/valid-purl/ArtifactCreated.json";
+ String msg = testGeneratePurlValidationOn(fileName);
+ Assert.assertTrue(msg.contains("data"));
+ Assert.assertTrue(msg.contains("meta"));
+ Assert.assertTrue(msg.contains("links"));
+ }
+
+ @Test
+ public void testGeneratePurlValidationOnFail() {
+ String fileName = "src/test/resources/input/ArtifactCreated.json";
+ String msg = testGeneratePurlValidationOn(fileName);
+ Assert.assertTrue(msg.contains("Cannot validate PURL"));
+ }
+
+ private String testGeneratePurlValidationOn(String fileName) {
+ HashMap properties = new HashMap<>();
+ properties.put(SemanticsService.VALIDATE_PURL_FOR_ART_C, true);
+ try {
+ File inputFile = new File(fileName);
+ JsonObject object = parser.parse(new FileReader(inputFile)).getAsJsonObject();
+ String msgType=object.get("msgParams").getAsJsonObject().get("meta").getAsJsonObject().get("type").getAsString();
+ return service.generateMsg(msgType, object, properties);
+ } catch (JsonIOException | JsonSyntaxException | FileNotFoundException e) {
+ System.out.println("Exception occurred while generating event");
+ e.printStackTrace();
+ Assert.assertFalse(true);
+ }
+
+ return null;
+ }
@Test
public void testUnknownMessage() throws UnsupportedEncodingException, JsonIOException, JsonSyntaxException, FileNotFoundException {
@@ -251,16 +291,22 @@ public void testValidateTemplates() {
@Test
public void testInvalid_lv_Message_Success() throws JsonIOException, JsonSyntaxException, FileNotFoundException, UnsupportedEncodingException {
- URL url = getClass().getClassLoader().getResource("invalid/lv_EiffelArtifactCreatedEventInvalid.json");
- String path = URLDecoder.decode(url.getPath(), StandardCharsets.UTF_8.name());
- File file = new File(path);
- JsonObject input = parser.parse(new FileReader(file)).getAsJsonObject();
- String msg = service.generateMsg("EiffelArtifactCreatedEvent", input, true);
- Assert.assertTrue(msg.contains("remremGenerateFailures"));
- Assert.assertTrue(msg.contains("data"));
- Assert.assertTrue(msg.contains("meta"));
- Assert.assertTrue(msg.contains("links"));
+ HashMap properties = new HashMap<>();
+ // TODO enable PURL validation
+ properties.put(SemanticsService.VALIDATE_PURL_FOR_ART_C, false);
+ properties.put(SemanticsService.LENIENT_VALIDATION, true);
+
+ URL url = getClass().getClassLoader().getResource("invalid/lv_EiffelArtifactCreatedEventInvalid.json");
+ String path = URLDecoder.decode(url.getPath(), StandardCharsets.UTF_8.name());
+ File file = new File(path);
+ JsonObject input = parser.parse(new FileReader(file)).getAsJsonObject();
+ String msg = service.generateMsg("EiffelArtifactCreatedEvent", input, properties);
+ Assert.assertTrue(msg.contains("remremGenerateFailures"));
+ Assert.assertTrue(msg.contains("data"));
+ Assert.assertTrue(msg.contains("meta"));
+ Assert.assertTrue(msg.contains("links"));
}
+
@Test
public void testInvalid_lv_Message_fail() throws JsonIOException, JsonSyntaxException, FileNotFoundException, UnsupportedEncodingException {
URL url = getClass().getClassLoader().getResource("invalid/lv_EiffelArtifactCreatedEventInvalid.json");
diff --git a/src/test/resources/input/valid-purl/ArtifactCreated.json b/src/test/resources/input/valid-purl/ArtifactCreated.json
new file mode 100644
index 00000000..d604ecc2
--- /dev/null
+++ b/src/test/resources/input/valid-purl/ArtifactCreated.json
@@ -0,0 +1,71 @@
+{
+ "msgParams": {
+ "meta": {
+ "type": "EiffelArtifactCreatedEvent",
+ "version": "3.3.0",
+ "schemaUri": "https://eiffel/schemas/EiffelArtifactCreatedEvent/3.3.0.json",
+ "tags": [
+ "tag1",
+ "tag2"
+ ],
+ "source": {
+ "domainId": "domainID",
+ "host": "host",
+ "name": "name",
+ "uri": "http:\/\/java.sun.com\/j2se\/1.3\/",
+ "serializer":"pkg:maven"
+ },
+ "security": {
+ "authorIdentity": "test",
+ "encryptedDigest": "sample"
+ }
+ }
+ },
+ "eventParams": {
+ "data": {
+ "gav": {
+ "groupId": "G",
+ "artifactId": "A",
+ "version": "V"
+ },
+ "fileInformation": [{
+ "name":"name"
+ }],
+ "buildCommand": "trigger",
+ "requiresImplementation": "NONE",
+ "name": "event",
+ "dependsOn": [
+ ],
+ "implement": [
+
+ ],
+ "identity":"pkg:abc/def",
+ "name":"name",
+ "customData": [{
+ "key": "firstLog",
+ "value": "http:\/\/myHost.com\/firstLog"
+ },
+ {
+ "key": "otherLog",
+ "value": "http:\/\/myHost.com\/firstLog33"
+ }
+ ]
+ },
+ "links": [{
+ "type": "CAUSE",
+ "target": "aaaaaaaa-bbbb-5ccc-8ddd-eeeeeeeeeee1"
+ },
+ {
+ "type": "PREVIOUS_VERSION",
+ "target": "aaaaaaaa-bbbb-5ccc-8ddd-eeeeeeeeeee2"
+ },
+ {
+ "type": "COMPOSITION",
+ "target": "aaaaaaaa-bbbb-5ccc-8ddd-eeeeeeeeeee1"
+ },
+ {
+ "type": "ENVIRONMENT",
+ "target": "aaaaaaaa-bbbb-5ccc-8ddd-eeeeeeeeeee3"
+ }]
+ }
+}
\ No newline at end of file