Skip to content

Commit 07d8ab4

Browse files
author
Raja Maragani
authored
Added lenientValidation support to REMReM generate (#128)
* Added lenientValidation support to REMReM generate Co-authored-by: raja-maragani
1 parent f02c1da commit 07d8ab4

File tree

6 files changed

+291
-73
lines changed

6 files changed

+291
-73
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
## 2.1.0
2+
- Updated REMReM Generate to support lenient validation for Eiffel Semantics protocol.
3+
14
## 2.0.13
25
- Removed the validation of REMReM should not accept both a CAUSE and a CONTEXT link types in the same event.
36

pom.xml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<version>2.0.4</version>
88
</parent>
99
<artifactId>eiffel-remrem-semantics</artifactId>
10-
<version>2.0.13</version>
10+
<version>2.1.0</version>
1111
<packaging>jar</packaging>
1212
<properties>
1313
<eclipse.jgit.version>5.0.1.201806211838-r</eclipse.jgit.version>
@@ -23,7 +23,7 @@
2323
<dependency>
2424
<groupId>com.github.eiffel-community</groupId>
2525
<artifactId>eiffel-remrem-protocol-interface</artifactId>
26-
<version>2.0.4</version>
26+
<version>2.1.0</version>
2727
<scope>compile</scope>
2828
</dependency>
2929
<dependency>
@@ -67,6 +67,11 @@
6767
<version>${eclipse.jgit.version}</version>
6868
<scope>compile</scope>
6969
</dependency>
70+
<dependency>
71+
<groupId>com.jayway.jsonpath</groupId>
72+
<artifactId>json-path</artifactId>
73+
<version>2.4.0</version>
74+
</dependency>
7075
<dependency>
7176
<groupId>org.mockito</groupId>
7277
<artifactId>mockito-core</artifactId>

src/main/java/com/ericsson/eiffel/remrem/semantics/SemanticsService.java

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -185,12 +185,16 @@ public static Map<EiffelEventType, Class<? extends Event>> eventType() {
185185
eventTypes.put(ALERT_CEASED, EiffelAlertCeasedEvent.class);
186186
eventTypes.put(ALERT_RAISED, EiffelAlertRaisedEvent.class);
187187
eventTypes.put(ISSUE_DEFINED, EiffelIssueDefinedEvent.class);
188-
189188
return eventTypes;
190189
}
191190

192191
@Override
193192
public String generateMsg(String msgType, JsonObject bodyJson) {
193+
return generateMsg(msgType, bodyJson, false);
194+
}
195+
196+
@Override
197+
public String generateMsg(String msgType, JsonObject bodyJson, Boolean lenientValidation) {
194198
try {
195199
if (purlSerializerFlag) {
196200
return createErrorResponse("Serializer info of eiffel-remrem-semantics is missing",
@@ -202,7 +206,6 @@ public String generateMsg(String msgType, JsonObject bodyJson) {
202206
return createErrorResponse(msgType, supportedEventTypes);
203207
}
204208
Class<? extends Event> eventType = eventTypes.get(eiffelType);
205-
206209
JsonObject msgNodes = null;
207210
JsonObject eventNodes = null;
208211
if (bodyJson.get(MSG_PARAMS) != null && bodyJson.get(EVENT_PARAMS) != null
@@ -230,12 +233,10 @@ public String generateMsg(String msgType, JsonObject bodyJson) {
230233
return createErrorResponse(eiffelType.getEventName(),
231234
"Mismatch of eventype in request query parameter with property 'type' in the input json message");
232235
}
233-
234236
Event event = eventCreation(eventType, msgNodes, eventNodes);
235-
236237
String result = gson.toJson(event);
237-
outputValidate(eiffelType, result);
238-
return result;
238+
JsonObject outputValidate = outputValidate(eiffelType, result, lenientValidation);
239+
return gson.toJson(outputValidate);
239240

240241
} catch (EiffelValidationException e) {
241242
log.error("Could not validate message. Reason:" + e.getMessage() + "\nCause: " + e.getCause().toString());
@@ -275,12 +276,32 @@ private String createErrorResponse(final String message, final ArrayList<String>
275276
return errorResponse.toString();
276277
}
277278

278-
private void outputValidate(EiffelEventType eiffelType, String jsonStringInput) throws EiffelValidationException {
279+
private JsonObject outputValidate(EiffelEventType eiffelType, String jsonStringInput, Boolean lenientValidation) throws EiffelValidationException {
279280
EiffelValidator validator = EiffelOutputValidatorFactory.getEiffelValidator(eiffelType);
280281
JsonObject jsonObject = new JsonParser().parse(jsonStringInput).getAsJsonObject();
281-
validator.validate(jsonObject);
282+
JsonObject validate = validator.validate(jsonObject, lenientValidation);
282283
// custom validations on an event which is not covered in schema.
283-
validator.customValidation(jsonObject);
284+
validator.customValidation(validate);
285+
return validate;
286+
}
287+
288+
@Override
289+
public ValidationResult validateMsg(String msgType, JsonObject jsonvalidateMessage) {
290+
return validateMsg(msgType, jsonvalidateMessage, false);
291+
}
292+
293+
@Override
294+
public ValidationResult validateMsg(String msgType, JsonObject jsonvalidateMessage, Boolean lenientValidation) {
295+
ValidationResult validationResult = null;
296+
EiffelEventType eiffelType = EiffelEventType.fromString(msgType);
297+
String result = gson.toJson(jsonvalidateMessage);
298+
try {
299+
outputValidate(eiffelType, result, lenientValidation);
300+
validationResult = new ValidationResult(true, "");
301+
} catch (EiffelValidationException e) {
302+
validationResult = new ValidationResult(false, e.getLocalizedMessage());
303+
}
304+
return validationResult;
284305
}
285306

286307
@Override
@@ -381,20 +402,6 @@ public String getServiceName() {
381402
return EIFFELSEMANTICS;
382403
}
383404

384-
@Override
385-
public ValidationResult validateMsg(String msgType, JsonObject jsonvalidateMessage) {
386-
ValidationResult validationResult = null;
387-
EiffelEventType eiffelType = EiffelEventType.fromString(msgType);
388-
String result = gson.toJson(jsonvalidateMessage);
389-
try {
390-
outputValidate(eiffelType, result);
391-
validationResult = new ValidationResult(true, "");
392-
} catch (EiffelValidationException e) {
393-
validationResult = new ValidationResult(false, e.getLocalizedMessage());
394-
}
395-
return validationResult;
396-
}
397-
398405
/**
399406
* Returns the domain Id from json formatted eiffel message.
400407
*

src/main/java/com/ericsson/eiffel/remrem/semantics/validator/EiffelValidator.java

Lines changed: 135 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@
2020
import java.util.List;
2121
import java.util.Map;
2222
import java.util.Set;
23+
import java.util.regex.Matcher;
24+
import java.util.regex.Pattern;
2325

26+
import javax.xml.bind.ValidationException;
2427
import org.apache.commons.lang3.StringUtils;
2528
import org.slf4j.Logger;
2629
import org.slf4j.LoggerFactory;
@@ -33,16 +36,29 @@
3336
import com.github.fge.jsonschema.core.report.ProcessingReport;
3437
import com.github.fge.jsonschema.main.JsonSchema;
3538
import com.github.fge.jsonschema.main.JsonSchemaFactory;
36-
import com.google.gson.Gson;
37-
import com.google.gson.GsonBuilder;
3839
import com.google.gson.JsonArray;
3940
import com.google.gson.JsonElement;
4041
import com.google.gson.JsonObject;
4142
import com.google.gson.JsonParser;
43+
import com.jayway.jsonpath.DocumentContext;
44+
import com.jayway.jsonpath.JsonPath;
4245

4346
public class EiffelValidator {
44-
public static final Logger log = LoggerFactory.getLogger(EiffelValidator.class);
4547

48+
private static final String REGEX_PATH = "\\/\\d*";
49+
private static final String CUSTOM_DATA = "customData";
50+
private static final String SLASH = "/";
51+
private static final String DOT = ".";
52+
private static final String REMREM_GENERATE_FAILURES = "remremGenerateFailures";
53+
private static final String PATH = "path";
54+
private static final String MESSAGE = "message";
55+
private static final String TYPE = "type";
56+
private static final String REQUIRED = "required";
57+
private static final String POINTER = "pointer";
58+
private static final String INSTANCE = "instance";
59+
private static final String KEYWORD = "keyword";
60+
public static final Logger log = LoggerFactory.getLogger(EiffelValidator.class);
61+
private static final String DATA = "data";
4662
private JsonSchema validationSchema;
4763
private String schemaResourceName;
4864
private String eventType;
@@ -73,41 +89,139 @@ public EiffelValidator(String schemaResourceName, String eventType, List<String>
7389
}
7490
}
7591

76-
public void validate(JsonObject jsonObjectInput) throws EiffelValidationException {
92+
public JsonObject validate(JsonObject jsonObjectInput) throws EiffelValidationException {
93+
return validate(jsonObjectInput, false);
94+
}
95+
96+
public JsonObject validate(JsonObject jsonObjectInput, Boolean lenientValidation) throws EiffelValidationException {
97+
JsonArray remremGenerateFailures = new JsonArray();
98+
log.debug("VALIDATING Schema used: {}", schemaResourceName);
7799
try {
78-
ProcessingReport report = validationSchema.validate(JsonLoader.fromString(jsonObjectInput.toString()));
79-
if (!report.isSuccess()) {
80-
log.warn(jsonObjectInput.toString());
81-
throw new EiffelValidationException(getErrorsList(report));
100+
final ProcessingReport report = validationSchema.validate(JsonLoader.fromString(jsonObjectInput.toString()),
101+
true);
102+
if (!report.isSuccess() && lenientValidation) {
103+
log.info("Trying to revalidate the Eiffel message with only mandatory Eiffel message fields.");
104+
String revalidatedJson = removeErrorProperties(report, jsonObjectInput, remremGenerateFailures);
105+
ProcessingReport report2 = validationSchema.validate(JsonLoader.fromString(revalidatedJson), true);
106+
handleErrorReport(jsonObjectInput, report2);
107+
log.debug("VALIDATED. Schema used: {}", schemaResourceName);
108+
return addRemremGenerateFailuresToCustomData(new JsonParser().parse(revalidatedJson).getAsJsonObject(), remremGenerateFailures);
109+
} else {
110+
handleErrorReport(jsonObjectInput, report);
82111
}
83112
log.debug("VALIDATED. Schema used: {}", schemaResourceName);
113+
return jsonObjectInput;
84114
} catch (Exception e) {
85-
String message = "Cannot validate given JSON string";
115+
final String message = "Cannot validate given JSON string";
86116
log.debug(message, e);
87117
throw new EiffelValidationException(message, e);
88118
}
89119
}
90120

121+
private void handleErrorReport(JsonObject jsonObjectInput, ProcessingReport report) throws EiffelValidationException {
122+
if (!report.isSuccess()) {
123+
throw new EiffelValidationException(getErrorsList(report));
124+
}
125+
}
126+
127+
/**
128+
* removeErrorProperties for removing the validation failures from Eiffel event and list the validation failures
129+
* @param report
130+
* @param jsonObjectInput
131+
* @param remremGenerateFailures
132+
* @return
133+
* @throws ValidationException
134+
*/
135+
private String removeErrorProperties(ProcessingReport report, JsonObject jsonObjectInput, JsonArray remremGenerateFailures) throws EiffelValidationException {
136+
JsonParser parser = new JsonParser();
137+
DocumentContext doc = JsonPath.parse(jsonObjectInput.toString());
138+
for (ProcessingMessage processingMessage : report) {
139+
if (LogLevel.ERROR.equals(processingMessage.getLogLevel())) {
140+
JsonElement element = parser.parse(processingMessage.asJson().toString());
141+
if (element.getAsJsonObject().get(KEYWORD).getAsString().equals(REQUIRED) || element.getAsJsonObject().get(KEYWORD).getAsString().equals(TYPE)) {
142+
throw new EiffelValidationException(getErrorsList(report));
143+
}
144+
String errorPath = element.getAsJsonObject().get(INSTANCE).getAsJsonObject().get(POINTER)
145+
.getAsString();
146+
JsonObject addValidationFailurs = addValidationFailures(element, processingMessage.getMessage());
147+
remremGenerateFailures.add(addValidationFailurs);
148+
doc.delete(getPath(errorPath));
149+
}
150+
}
151+
return doc.jsonString();
152+
}
153+
154+
/**
155+
* getPath for create the DocumentContext path from validation failure path
156+
* example
157+
* 1) meta/tags/0 --> $.meta.tags[0]
158+
* 2) links/0/target --> $.links[0]
159+
* 3) /data/outcome/conclusion --> $.data.outcome.conclusion
160+
* @param errorPath
161+
* @return path
162+
*/
163+
private String getPath(String errorPath) {
164+
final Pattern pattern = Pattern.compile(REGEX_PATH, Pattern.MULTILINE);
165+
final Matcher matcher = pattern.matcher(errorPath);
166+
while (matcher.find()) {
167+
if(matcher.group(0).length()>1) {
168+
errorPath = errorPath.substring(0, errorPath.indexOf(matcher.group(0))+matcher.group(0).length());
169+
errorPath = errorPath.replaceAll(matcher.group(0), "["+matcher.group(0).substring(1)+"]");
170+
}
171+
}
172+
return "$" + errorPath.replace(SLASH, DOT);
173+
}
174+
175+
private JsonObject addValidationFailures(JsonElement element, String message) {
176+
log.debug("Adding the error field information to the array");
177+
String type = element.getAsJsonObject().get(KEYWORD).getAsString();
178+
String path = element.getAsJsonObject().get(INSTANCE).getAsJsonObject().get(POINTER).getAsString();
179+
JsonObject object = new JsonObject();
180+
object.addProperty(TYPE, type);
181+
object.addProperty(MESSAGE, message);
182+
object.addProperty(PATH, path);
183+
return object;
184+
}
185+
186+
private JsonObject addRemremGenerateFailuresToCustomData(JsonObject inputJson, JsonArray remremGenerateFailures) {
187+
if (remremGenerateFailures.size() > 0) {
188+
JsonArray customData = getCustomData(inputJson);
189+
JsonObject object = new JsonObject();
190+
object.addProperty("key", REMREM_GENERATE_FAILURES);
191+
object.add("value", remremGenerateFailures);
192+
customData.add(object);
193+
}
194+
return inputJson;
195+
}
196+
197+
/**
198+
* Gets the customData array from an Eiffel message
199+
* @param Eiffel message
200+
* @return customData array
201+
*/
202+
public JsonArray getCustomData(JsonObject json) {
203+
if (json.isJsonObject() && json.getAsJsonObject().has(DATA)
204+
&& json.getAsJsonObject().getAsJsonObject(DATA).has(CUSTOM_DATA)) {
205+
return json.getAsJsonObject().getAsJsonObject(DATA).get(CUSTOM_DATA).getAsJsonArray();
206+
}
207+
return null;
208+
}
209+
91210
/**
92211
*
93212
* @param report json validation report
94213
* @return error message
95214
*/
96-
private String getErrorsList(ProcessingReport report) {
97-
Gson gson = new GsonBuilder().setPrettyPrinting().create();
98-
JsonParser parser = new JsonParser();
99-
String message = "";
100-
for (ProcessingMessage processingMessage : report) {
101-
if(LogLevel.ERROR.equals(processingMessage.getLogLevel())) {
102-
JsonElement element=parser.parse(processingMessage.asJson().toString());
103-
element.getAsJsonObject().remove("schema");
104-
element.getAsJsonObject().remove("level");
105-
message = gson.toJson(element);
106-
log.debug(message);
215+
private String getErrorsList(final ProcessingReport report) {
216+
List<String> messages = new ArrayList<String>();
217+
for (final ProcessingMessage processingMessage : report) {
218+
if (LogLevel.ERROR.equals(processingMessage.getLogLevel())) {
219+
messages.add(processingMessage.getMessage().toString());
107220
}
108221
}
109-
return message;
222+
return messages.toString();
110223
}
224+
111225
/**
112226
* This method is used to validate links in an event
113227
* @param JsonArray of links in an event

0 commit comments

Comments
 (0)