How we implemented the Gazelle EVS Client's new validation API in Matchbox
27.08.24 | Quentin Ligier
Matchbox is used within the Gazelle ecosystem for validating FHIR-based content and we have recently integrated it with the new validation API from EVS Client. In this post, we look at the structure, requirements and advantages of the new API, and the reasons for our integration, before detailing the steps we took to implement it.
But first, some background.
About Gazelle and the EVS Client
Gazelle is an ecosystem of integrated tools — which includes Matchbox — designed to support interoperability testing and conformance assessment for health information technology systems.
The Gazelle platform was developed by the team at Kereval, for IHE Catalyst and integrates various technical components. It is used, for example, by IHE Catalyst to run Connectathons, where vendors and developers can test their implementations against each other. Gazelle is also used by various national health agencies, like Switzerland’s eHealth Suisse, to support the interoperability testing of IHE specifications in a national context.
The EVS Client — short for External Validation Service — is a technical component of the Gazelle platform. It offers a unified graphic (GUI) and programmatic (API) interface both to users and to other components, and contains the configurations of all available validators, as well as storing the results of all validations performed.
About Matchbox
Matchbox is the open-source FHIR server developed by ahdis, and based on the HAPI FHIR JPA Server Starter. We designed it to be a versatile FHIR server solution that can handle various deployment scenarios and offer robust support for FHIR implementation guides and related operations.
Matchbox’s key features include:
Implementation Guide Support: Matchbox can pre-load FHIR implementation guides from package servers for conformance resources such as StructureMap, Questionnaire, CodeSystem, ValueSet, ConceptMap, NamingSystem, and StructureDefinition.
Validation: Matchbox provides validation support through the [server]/$validate endpoint, which allows users to check whether FHIR resources conform to loaded implementation guides.
FHIR Mapping Language: Matchbox includes endpoints for creating StructureMaps and supports the StructureMap/$transform operation.
Structured Data Capture (SDC): Matchbox offers SDC extraction support based on the FHIR Mapping language and Questionnaire/$extract.
Because it also offers validation of FHIR-based IHE profiles like MHD, PDQm or PCF, it was recognized as a valuable addition to the EVS Client’s list of supported validators.
The new Validation API
The team at Kereval is currently refactoring major parts of Gazelle, including the EVS Client, to improve its usability, efficiency and capabilities. That work includes the new validation API, which allows interfacing with other specialized validators in the EVS Client GUI.
When natively integrated, the new API offers a number of important benefits, for example:
Decreasing the configuration work in the EVS Client, as it is now able to automatically discover supported profiles.
Increasing the performance of the validation process, as the EVS Client will be able to send multiple items to validate in a single request. What’s more, the EVS Client implements the API directly. (Previously, integration needed a separate component to implement the Matchbox validation, which would have required the new component to be updated for each new version, increasing both complexity and maintenance.)
Enabling the EVS Client to display more information about the validation process, such as the loaded implementation guides or the parameters used for the validation in additional metadata. Although information is also returned in the FHIR validation response, it is displayed as a validation message.
Decoupling the API from the FHIR version. With the new API, all FHIR versions like R5 and multiple FHIR versions can be supported with the same Matchbox instance.
Investigating the API
Because of these clear benefits, we decided to implement it in Matchbox. But it required some work up front.
The first step was to fully investigate the API and understand its structure and requirements. Documentation is still limited, but the first version of the API has already been implemented in the EVS Client, and some of its components are available in open-source repositories, including:
Gazelle/Library/validation service api, a library that provides all required APIs to build and integrate a new validation service with Gazelle Test Bed.
Gazelle/Public/Validation/HTTP Validator, an HTTP-validation engine that implements the new API.
In the new API repository, the web service definition can quickly be found in the ValidationApiWS class. It provides useful information about the API design and its particular implementation. It has two endpoints — POST /validate
and GET /profiles
— as well as models for requests and responses, and some OpenAPI annotations. The web service is defined with Jakarta RESTful Web Services (JAX-RS), in the new jakarta
namespace.
Structure of ValidationApiWS.java:
package net.ihe.gazelle.validation.interlay.ws; import jakarta.ws.rs.*; import jakarta.ws.rs.core.Response; public interface ValidationApiWS { @POST @Path("/validate") @Produces({"application/json", "application/gzl.validation.report+json"}) // @Schema(implementation = ValidationReportDTO.class) Response createValidationRequest(ValidationRequest validationRequest); @GET @Path("/profiles") @Produces({"application/json"}) // @Schema(type = SchemaType.ARRAY, implementation = ValidationProfileDTO.class) Response getValidationProfiles(); }
Although this implementation seems to provide everything to start developing our interface, we preferred not to rely on these dependencies.
Matchbox is already dependent on HAPI FHIR and Spring Boot, which are responsible for managing the servlet container(s) and don’t implement the JAX-RS specification. Spring Web MVC (which is already included in Matchbox) also provides its own way to define RESTful services. We felt that adding another dependency of a web service framework would be redundant and could lead to conflicts.
Fortunately, the Spring Web MVC annotations are very similar to the JAX-RS annotations, and can easily be mapped to it.
Equivalent implementation with Spring Web MVC:
package ch.ahdis.matchbox.gazelle; import org.springframework.web.bind.annotation.*; @RestController public class GazelleValidationWs { @PostMapping(path = "/validate", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) public ValidationReport postValidate(@RequestBody final ValidationRequest validationRequest) { // Perform validation } @GetMapping(path = "/profiles", produces = MediaType.APPLICATION_JSON_VALUE) public List<ValidationProfile> getProfiles() { // List profiles } }
From Investigation to Implementation
Investigation was a relatively simple job. But we still needed to implement the models for requests and responses.
The existing validation service implements the Data Transfer Objects (DTOs) with really Java-esque Design Patterns. There are interfaces, factories, builders, validators, visitors, mappers between DTOs and Domain Objects, and so on.
We don’t really need all that complexity to simply serialize and deserialize requests and responses to and from their JSON encoding. There are 13 classes in total, quickly built with Lombok annotations (immediately followed by Delombok to allow changing the source code in some places) and three enums.
While copying the fields from the original classes to the new ones, I noticed that the original classes were not simple DTOs: they also contained some business logic. I copied some of these methods to the new classes, but still kept them as simple as possible.
And voilà! We were pretty much done with the implementation of the new API itself.
Now, we need to process requests for both routes and generate meaningful responses to integrate with the EVS Client successfully.
Listing Validation Profiles
The easiest endpoint to process is the GET /profiles
route.
It enables the EVS Client to know all validators supported by Matchbox and show them in the EVS Client GUI.
In FHIR, these validators are called profiles, and they are used to validate FHIR resources. Looking at the HTTP validator gave us a first indication of what we needed to return in this endpoint.
We listed all the profiles loaded in Matchbox and returned them as a JSON array. We returned both the FHIR profile identifier (used by Matchbox to locate the profile) and the FHIR StructureDefinition’s title as it is a more user-friendly way of selecting from a list of choices.
We added the profile’s version, as Implementation Guides may have multiple versions loaded in Matchbox at the same time, and we need to distinguish between them. We also added the Implementation Guide ID, to allow the EVS Client to filter or group profiles in the interface. Finally, we filtered extensions from the list of profiles, because they are not validated directly.
An example of a profile item:
{ "profileID": "http://fhir.ch/ig/ch-core/StructureDefinition/ch-core-practitioner-epr|4.0.1", "profileName": "CH Core Practitioner EPR (4.0.1)", "domain": "ch.fhir.ig.ch-core", "coveredItems": [] }
The list of validation profiles, as displayed in the EVS Client GUI:
Validating Items
The second endpoint, POST /validate
, is more complex and required more work.
We received a list of items to validate against a specific profile, and we needed to return a validation report for each of them.
We started by initializing a Matchbox engine for the given profile, and validated each item with it. This step is quite straightforward, as its implementation is no different from the FHIR validation API.
The second step was to convert the FHIR validation report to what is expected by the EVS Client. Both have a similar, but slightly different, concepts. For example, FHIR’s issue severity must be converted to the EVS Client’s priority, level and severity. We mapped the fields as well as we could, and added some further information to the report, such as any available slicing information.
Eventually, we added extra metadata to the report, so the user could see this information in the EVS Client GUI. Information like the loaded Implementation Guides and the parameters used for the validation can be helpful to assert the validation quality.
Example of a validation report:
{ "modelVersion": "0.1", "uuid": "15634009-cc9f-4670-975b-c9e6697178c6", "dateTime": "2024-07-22T15:13:23.287+00:00", "disclaimer": "Matchbox disclaims", "validationMethod": { "validationServiceName": "Matchbox", "validationServiceVersion": "3.8.9", "validationProfileID": "http://fhir.ch/ig/ch-core/StructureDefinition/ch-core-document|4.0.1", "validationProfileVersion": "4.0.1" }, "overallResult": "PASSED", "additionalMetadata": [ { "name": "validatorVersion", "value": "powered by matchbox 3.8.9, hapi-fhir 7.0.2 and org.hl7.fhir.core 6.3.11" }, { "name": "package", "value": "ch.fhir.ig.ch-core#4.0.1" }, { "name": "profile", "value": "http://fhir.ch/ig/ch-core/StructureDefinition/ch-core-document" }, { "name": "profileVersion", "value": "4.0.1" } // … ], "reports": [ { "name": "Validation of item #first", "subReportResult": "PASSED", "assertionReports": [ { "assertionID": "VALIDATION_VAL_PROFILE_UNKNOWN_NOT_POLICY", "assertionType": "InstanceValidator", "description": "Profile reference 'http://fhir.ch/ig/ch-elm/StructureDefinition/ch-elm-organization-orderer' has not been checked because it could not be found, and the validator is set to not fetch unknown profiles", "subjectLocation": "line 317, column 6, FHIRPath: Bundle.entry[10].resource/*Organization/1Org-KsAbc*/.meta.profile[0]", "severity": "WARNING", "priority": "RECOMMENDED", "result": "FAILED" }, { "assertionID": "Bundle_BUNDLE_Entry_NotFound", "assertionType": "InstanceValidator", "description": "Can't find 'Specimen/1Spec-Specimen' in the bundle (Bundle.entry[5].resource.specimen[0])", "subjectLocation": "line 1, column 2, FHIRPath: Bundle", "severity": "INFO", "priority": "MANDATORY", "result": "PASSED" } // … ], "subCounters": { "numberOfAssertions": 23, "numberOfFailedWithInfos": 0, "numberOfFailedWithWarnings": 15, "numberOfFailedWithErrors": 0, "numberOfUnexpectedErrors": 0 } } ], "validationItems": [ { "itemId": "first", "content": "ewogICJyZXNvdXJjZVR5cGUiIDogIkJ1bmRsZSIsCiAgImlkIiA6ICIxYkRvYy1OZWlzc2VyaWFHb25vcnJob2VhZSIsC …", "role": "request", "location": "localhost" } ], "counters": { "numberOfAssertions": 23, "numberOfFailedWithInfos": 0, "numberOfFailedWithWarnings": 15, "numberOfFailedWithErrors": 0, "numberOfUnexpectedErrors": 0 } }
The validation report of an FHIR resource, as displayed in the EVS Client GUI:
Testing the Gazelle EVS Client Integration
Once we have implemented the new API, we needed to test it in real conditions. After discussing with the Gazelle team and iterating some parts of our implementation, with the Gazelle team gave us access to the test instance of the latest EVS Client. We could now test our implementation and verify that it worked as expected.
That step was crucial. We discovered a few remaining issues in both Matchbox and the EVS Client, but most of the fixes were straightforward, and we could quickly deploy a new version of Matchbox. Only one issue remains unresolved, because it requires more refactoring in the EVS Client. Hopefully, it is not a major issue, and the next test session will deliver excellent results.
Most recently, we tested the integration intensively during the IHE Europe Connectathon in Trieste, in June 2024. We discovered no new issues and the validator worked well during the whole week. Although it was not the most frequently used validator, it was still a very valuable stress test. Besides, we were very happy to not have more issues to fix during the Connectathon, with so much else going on.
The new implementation can now be seen in package ch.ahdis.matchbox.gazelle.
Thanks to an excellent collaboration with the Gazelle team, we were able to discuss various aspects of the API, get support and feedback on our implementation, and report bugs encountered during the testing phase. This really helped ease the integration of the new API in Matchbox, and we are looking forward to further collaboration.