Skip to content
Merged
6 changes: 4 additions & 2 deletions nhs-england-developer-information.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ We provide a mock MHS adaptor for local development and testing.

### How to transform arbitrary json ASR payload files

This is an interoperability testing tool to transform arbitrary/ad-hoc json ASR payloads and access the outputs.
This is an interoperability testing tool to transform arbitrary/ad-hoc json ASR payloads, access the outputs and validate the produced XML against the relevant schema.

1. Navigate to the input folder and place all Json files to convert here.
`integration-adaptor-gp2gp/transformJsonToXml/input/`
Expand All @@ -203,9 +203,11 @@ This is an interoperability testing tool to transform arbitrary/ad-hoc json ASR
./TransformJsonToXml.sh
```

3. The Converted .Xml files will be located in the output folder.
3. The converted XML files will be located in the output folder.
`integration-adaptor-gp2gp/transformJsonToXml/output/`

4. Any schema validation errors will be located with the extension `.validation-errors.log` and will be located in the same output folder as the converted XML files.

## Troubleshooting

### "Invalid source release 17" error
Expand Down
1 change: 1 addition & 0 deletions service/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ sonar {
property("sonar.host.url", "https://sonarcloud.io")
property("sonar.projectKey", "NHSDigital_integration-adaptor-gp2gp")
property("sonar.organization", "nhsdigital")
property("sonar.exclusions", "**/transformjsontoxmltool/*")
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@
matchIfMissing = false)
@Component
public class TransformJsonToXml implements CommandLineRunner {

private static final String JSON_FILE_INPUT_PATH =
Paths.get("src/").toFile().getAbsoluteFile().getAbsolutePath() + "/../../transformJsonToXml/input/";
private static final String XML_OUTPUT_PATH =
Expand All @@ -57,6 +56,7 @@
private final MessageContext messageContext;
private final OutputMessageWrapperMapper outputMessageWrapperMapper;
private final EhrExtractMapper ehrExtractMapper;
private final XmlSchemaValidator xmlSchemaValidator;

public static void main(String[] args) {
SpringApplication.run(TransformJsonToXml.class, args).close();
Expand All @@ -68,6 +68,7 @@
getFiles().forEach(file -> {
String xmlResult = mapJsonToXml(file.getJsonFileInput());
writeToFile(xmlResult, file.getJsonFileName());
xmlSchemaValidator.validateOutputToXmlSchema(file.getJsonFileName(), xmlResult);

Check warning on line 71 in service/src/main/java/uk/nhs/adaptors/gp2gp/transformjsontoxmltool/TransformJsonToXml.java

View workflow job for this annotation

GitHub Actions / pitest

2 different changes can be made to a lambda on line 71 without causing a test to fail

swapped parameters 1 and 2 in call to validateOutputToXmlSchema in 1st lambda in run (no tests cover this line ParamSwapMutator) removed call to uk/nhs/adaptors/gp2gp/transformjsontoxmltool/XmlSchemaValidator::validateOutputToXmlSchema in 1st lambda in run (no tests cover this line VoidMethodCallMutator)
});
} catch (NHSNumberNotFound | UnreadableJsonFileException | NoJsonFileFound | Hl7TranslatedResponseError e) {
LOGGER.error("error: " + e.getMessage());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package uk.nhs.adaptors.gp2gp.transformjsontoxmltool;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.springframework.stereotype.Component;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import uk.nhs.adaptors.gp2gp.common.configuration.RedactionsContext;

import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.StringReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Paths;

@Component
@Slf4j
@RequiredArgsConstructor()
public class XmlSchemaValidator {
private final RedactionsContext redactionsContext;

private static final String SCHEMA_PATH = "../service/src/test/resources/mim/Schemas/";
private static final String RCMR_IN030000UK06_SCHEMA_PATH = SCHEMA_PATH + RedactionsContext.NON_REDACTION_INTERACTION_ID + ".xsd";
private static final String RCMR_IN030000UK07_SCHEMA_PATH = SCHEMA_PATH + RedactionsContext.REDACTION_INTERACTION_ID + ".xsd";
private static final String OUTPUT_PATH =
Paths.get("src/").toFile().getAbsoluteFile().getAbsolutePath() + "/../../transformJsonToXml/output/";

private static final String VALIDATING_AGAINST_SCHEMA_TEMPLATE = "Validating {} against {} schema";
private static final String COULD_NOT_LOAD_SCHEMA_FILE_TEMPLATE = "Could not load schema file for {} context.";
private static final String FAILED_TO_VALIDATE_SCHEMA_TEMPLATE = "Failed to validate {} against {} schema";
private static final String COULD_NOT_READ_FROM_STREAM_SOURCE_TEMPLATE = "Could not read from stream source for produced XML for {}";
private static final String SUCCESSFULLY_VALIDATED_SCHEMA_TEMPLATE = "Successfully validated {} against {} schema";
private static final String VALIDATION_ERRORS_WRITTEN_TEMPLATE = "Validation errors written to {}";
private static final String COULD_NOT_WRITE_VALIDATION_ERRORS_TEMPLATE = "Could not write validation errors to {}";

public void validateOutputToXmlSchema(String inputJsonFilename, String xmlResult) {
LOGGER.info(VALIDATING_AGAINST_SCHEMA_TEMPLATE, inputJsonFilename, RedactionsContext.REDACTION_INTERACTION_ID);

var xsdErrorHandler = new XsdErrorHandler();
Validator xmlValidator;

try {
xmlValidator = getXmlValidator(xsdErrorHandler);
} catch (SAXException e) {
LOGGER.error(COULD_NOT_LOAD_SCHEMA_FILE_TEMPLATE, RedactionsContext.REDACTION_INTERACTION_ID);
return;
}

try {
var xmlResultSource = new StreamSource(new StringReader(xmlResult));
xmlValidator.validate(xmlResultSource);

Check warning on line 57 in service/src/main/java/uk/nhs/adaptors/gp2gp/transformjsontoxmltool/XmlSchemaValidator.java

View workflow job for this annotation

GitHub Actions / pitest

A change can be made to line 57 without causing a test to fail

removed call to javax/xml/validation/Validator::validate (no tests cover this line VoidMethodCallMutator)
} catch (SAXParseException parseException) {
LOGGER.warn(FAILED_TO_VALIDATE_SCHEMA_TEMPLATE, inputJsonFilename, RedactionsContext.REDACTION_INTERACTION_ID);
writeValidationExceptionsToFile(xsdErrorHandler, inputJsonFilename);

Check warning on line 60 in service/src/main/java/uk/nhs/adaptors/gp2gp/transformjsontoxmltool/XmlSchemaValidator.java

View workflow job for this annotation

GitHub Actions / pitest

A change can be made to line 60 without causing a test to fail

removed call to writeValidationExceptionsToFile (no tests cover this line VoidMethodCallMutator)
} catch (IOException e) {
LOGGER.error(COULD_NOT_READ_FROM_STREAM_SOURCE_TEMPLATE, inputJsonFilename);
return;
} catch (Exception e) {
throw new RuntimeException(e);
}

if (!xsdErrorHandler.isValid()) {

Check warning on line 68 in service/src/main/java/uk/nhs/adaptors/gp2gp/transformjsontoxmltool/XmlSchemaValidator.java

View workflow job for this annotation

GitHub Actions / pitest

2 different changes can be made to line 68 without causing a test to fail

removed conditional - replaced equality check with false (no tests cover this line RemoveConditionalMutator_EQUAL_ELSE) removed conditional - replaced equality check with true (no tests cover this line RemoveConditionalMutator_EQUAL_IF)
LOGGER.warn(FAILED_TO_VALIDATE_SCHEMA_TEMPLATE, inputJsonFilename, RedactionsContext.REDACTION_INTERACTION_ID);
writeValidationExceptionsToFile(xsdErrorHandler, inputJsonFilename);

Check warning on line 70 in service/src/main/java/uk/nhs/adaptors/gp2gp/transformjsontoxmltool/XmlSchemaValidator.java

View workflow job for this annotation

GitHub Actions / pitest

A change can be made to line 70 without causing a test to fail

removed call to writeValidationExceptionsToFile (no tests cover this line VoidMethodCallMutator)
return;
}

LOGGER.info(SUCCESSFULLY_VALIDATED_SCHEMA_TEMPLATE, inputJsonFilename, RedactionsContext.REDACTION_INTERACTION_ID);
}

private void writeValidationExceptionsToFile(XsdErrorHandler xsdErrorHandler, String fileName) {
String outputFileName = FilenameUtils.removeExtension(fileName) + ".validation-errors.log";
try (BufferedWriter writer = new BufferedWriter(new FileWriter(OUTPUT_PATH + outputFileName, StandardCharsets.UTF_8))) {
for (SAXParseException e : xsdErrorHandler.getExceptions()) {
var message = String.format("[%d:%d] %s", e.getLineNumber(), e.getColumnNumber(), e.getMessage());

Check warning on line 81 in service/src/main/java/uk/nhs/adaptors/gp2gp/transformjsontoxmltool/XmlSchemaValidator.java

View workflow job for this annotation

GitHub Actions / pitest

A change can be made to line 81 without causing a test to fail

removed last varargs argument in call to format (no tests cover this line VarargsMutator)
writer.write(message);

Check warning on line 82 in service/src/main/java/uk/nhs/adaptors/gp2gp/transformjsontoxmltool/XmlSchemaValidator.java

View workflow job for this annotation

GitHub Actions / pitest

A change can be made to line 82 without causing a test to fail

removed call to java/io/BufferedWriter::write (no tests cover this line VoidMethodCallMutator)
writer.newLine();

Check warning on line 83 in service/src/main/java/uk/nhs/adaptors/gp2gp/transformjsontoxmltool/XmlSchemaValidator.java

View workflow job for this annotation

GitHub Actions / pitest

A change can be made to line 83 without causing a test to fail

removed call to java/io/BufferedWriter::newLine (no tests cover this line VoidMethodCallMutator)
}
LOGGER.info(VALIDATION_ERRORS_WRITTEN_TEMPLATE, outputFileName);
} catch (IOException e) {
LOGGER.error(COULD_NOT_WRITE_VALIDATION_ERRORS_TEMPLATE, outputFileName, e);
}
}

private Validator getXmlValidator(XsdErrorHandler xsdErrorHandler) throws SAXException {
var schemaPath = RedactionsContext.REDACTION_INTERACTION_ID.equals(redactionsContext.ehrExtractInteractionId())

Check warning on line 92 in service/src/main/java/uk/nhs/adaptors/gp2gp/transformjsontoxmltool/XmlSchemaValidator.java

View workflow job for this annotation

GitHub Actions / pitest

2 different changes can be made to line 92 without causing a test to fail

removed conditional - replaced equality check with false (no tests cover this line RemoveConditionalMutator_EQUAL_ELSE) removed conditional - replaced equality check with true (no tests cover this line RemoveConditionalMutator_EQUAL_IF)
? RCMR_IN030000UK07_SCHEMA_PATH
: RCMR_IN030000UK06_SCHEMA_PATH;

var schemaFactory = SchemaFactory.newDefaultInstance();
var schemaFileStream = new StreamSource(new File(schemaPath));
var schema = schemaFactory.newSchema(schemaFileStream);
var validator = schema.newValidator();

validator.setErrorHandler(xsdErrorHandler);

Check warning on line 101 in service/src/main/java/uk/nhs/adaptors/gp2gp/transformjsontoxmltool/XmlSchemaValidator.java

View workflow job for this annotation

GitHub Actions / pitest

A change can be made to line 101 without causing a test to fail

removed call to javax/xml/validation/Validator::setErrorHandler (no tests cover this line VoidMethodCallMutator)

return validator;

Check warning on line 103 in service/src/main/java/uk/nhs/adaptors/gp2gp/transformjsontoxmltool/XmlSchemaValidator.java

View workflow job for this annotation

GitHub Actions / pitest

A change can be made to line 103 without causing a test to fail

replaced return value with null for getXmlValidator (no tests cover this line NullReturnValsMutator)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package uk.nhs.adaptors.gp2gp.transformjsontoxmltool;

import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXParseException;

import java.util.ArrayList;
import java.util.List;

@Slf4j
@Getter
public class XsdErrorHandler implements ErrorHandler {

private final List<SAXParseException> exceptions = new ArrayList<>();

public boolean isValid() {
return exceptions.isEmpty();

Check warning on line 18 in service/src/main/java/uk/nhs/adaptors/gp2gp/transformjsontoxmltool/XsdErrorHandler.java

View workflow job for this annotation

GitHub Actions / pitest

2 different changes can be made to line 18 without causing a test to fail

replaced boolean return with false for isValid (no tests cover this line BooleanFalseReturnValsMutator) replaced boolean return with true for isValid (no tests cover this line BooleanTrueReturnValsMutator)
}

@Override
public void warning(SAXParseException exception) {
exceptions.add(exception);
}

@Override
public void error(SAXParseException exception) {
exceptions.add(exception);
}

@Override
public void fatalError(SAXParseException exception) throws SAXParseException {
exceptions.add(exception);
throw exception;
}
}