Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
## 2.4.3
- Optional validation of PURLs added.

## 2.4.2
- Typo fix.

Expand Down
11 changes: 8 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
<parent>
<groupId>com.github.eiffel-community</groupId>
<artifactId>eiffel-remrem-parent</artifactId>
<version>2.0.14</version>
<version>2.0.18</version>
</parent>
<artifactId>eiffel-remrem-semantics</artifactId>
<version>2.4.2</version>
<version>2.4.3</version>
<packaging>jar</packaging>
<properties>
<eclipse.jgit.version>5.0.1.201806211838-r</eclipse.jgit.version>
Expand All @@ -24,7 +24,7 @@
<dependency>
<groupId>com.github.eiffel-community</groupId>
<artifactId>eiffel-remrem-protocol-interface</artifactId>
<version>2.2.0</version>
<version>2.2.1</version>
<scope>compile</scope>
</dependency>
<dependency>
Expand All @@ -48,6 +48,11 @@
<artifactId>semver4j</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>com.github.package-url</groupId>
<artifactId>packageurl-java</artifactId>
<version>1.5.0</version>
</dependency>
<dependency>
<groupId>org.glassfish.hk2.external</groupId>
<artifactId>javax.inject</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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";
Expand All @@ -143,6 +144,11 @@ public class SemanticsService implements MsgService {
private static Gson gson = new Gson();
private static Map<EiffelEventType, Class<? extends Event>> eventTypes = SemanticsService.eventType();

/**
* Name of validation property applicable for generateMsg(String, JsonObject, Map<String, Object>).
*/
public static final String VALIDATE_PURL_FOR_ART_C = "validatePurlForArtC";

public SemanticsService() {
for (final EiffelEventType msg : EiffelEventType.values()) {
supportedEventTypes.add(msg.getEventName());
Expand Down Expand Up @@ -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:
* <ul>
* <li>lenientValidation: boolean,</li>
* <li>validatePurlForArtC: boolean.</li>
* </ul>
* @return the generated and validate Eiffel messages as json String
*/
@Override
public String generateMsg(String msgType, JsonObject bodyJson, HashMap<String, Object> validationProperties) {
try {
if (purlSerializerFlag) {
return createErrorResponse("Serializer info of eiffel-remrem-semantics is missing",
Expand All @@ -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,
Expand All @@ -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) {
Expand Down Expand Up @@ -284,13 +306,19 @@ private String createErrorResponse(final String message, final ArrayList<String>
return errorResponse.toString();
}

private JsonObject outputValidate(EiffelEventType eiffelType, String jsonStringInput, Boolean lenientValidation) throws EiffelValidationException {
private JsonObject outputValidate(EiffelEventType eiffelType, String jsonStringInput, HashMap<String, Object> 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<String, Object> createPropertiesWithLenientValidation(boolean lenientValidation) {
HashMap<String, Object> validationProperties = new HashMap<>();
validationProperties.put(LENIENT_VALIDATION, lenientValidation);
return validationProperties;
}

@Override
Expand All @@ -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<String, Object> 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());
Expand Down Expand Up @@ -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) {
Expand Down
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this class required , we can directly fetch the details from the hashmap ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The last method of the class ensures that property is a Boolean. If fetching from a HashMap directly, the code would have to follow the fetch making it harder to read. That's why a new class was introduced.

Original file line number Diff line number Diff line change
@@ -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> T getProperty(HashMap<String, Object> validationProperties, String property) {
return getProperty(validationProperties, property, null);
}

public static <T> T getProperty(HashMap<String, Object> validationProperties, String property, T defValue) {
Object object = validationProperties.get(property);
if (object == null)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

code can be optimised as follows:
object instanceof Boolean ? (Boolean) object : defValue;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but then I lose the error message. And it's important here as it would be difficult to investigate why default value was returned instead of value from HashMap. I tried to find how to check it dynamically (something linek 'object instance of T'), but generics are compile-time-only feature...

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;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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*";
Expand Down Expand Up @@ -94,11 +100,19 @@ public JsonObject validate(JsonObject jsonObjectInput) throws EiffelValidationEx
}

public JsonObject validate(JsonObject jsonObjectInput, Boolean lenientValidation) throws EiffelValidationException {
HashMap<String, Object> validationProperties = new HashMap<>();
validationProperties.put(LENIENT_VALIDATION, lenientValidation);
return validate(jsonObjectInput, validationProperties);
}

public JsonObject validate(JsonObject jsonObjectInput, HashMap<String, Object> 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);
Expand Down Expand Up @@ -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));
}
}
Expand Down Expand Up @@ -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<String, Object> validationProperties) throws EiffelValidationException {
validateLinks(jsonObjectInput);
validateArtC(jsonObjectInput, validationProperties);
}

public void validateLinks(JsonObject jsonObjectInput) throws EiffelValidationException {
// Links validation
Map<String, Integer> linksCountMapForEvent = new HashMap<String, Integer>();
try {
Expand Down Expand Up @@ -271,4 +295,47 @@ public void customValidation(JsonObject jsonObjectInput) throws EiffelValidation
throw new EiffelValidationException(message, e);
}
}

public void validateArtC(JsonObject jsonObjectInput, HashMap<String, Object> 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);
}
}
}
Loading