6262 cds hooks returns 400 when extension passed in the cdsservicerequestjson (#6274)

* fail test

* potential fix

* spotless

* alternative fix

* spotless

* remove the new method and replace usage for the old one

* spotless

* fix issue with context failing deserialization

* add validation for context, hook and hook instance

* spotless

* add message codes

* spotless

* changelog

* cleanup

* spotless

* bump version to 7.5.1-SNAPSHOT
This commit is contained in:
Aditya Dave 2024-09-18 10:01:17 -04:00 committed by GitHub
parent 02d38bce14
commit 1fad912193
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
88 changed files with 305 additions and 214 deletions

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-bom</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<packaging>pom</packaging>
<name>HAPI FHIR BOM</name>
@ -12,7 +12,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-cli</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -0,0 +1,4 @@
---
type: fix
issue: 6262
title: "Previously, when a `extension` was passed in as a part of the CDS hooks request, it would result in a `400 service not found`. This behaviour has now been fixed."

View File

@ -11,7 +11,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -3,7 +3,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -3,7 +3,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -7,7 +7,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -45,7 +45,7 @@ public interface ICdsServiceRegistry {
* @param theCdsServiceRequestJson the service request
* @return the service response
*/
CdsServiceResponseJson callService(String theServiceId, CdsServiceRequestJson theCdsServiceRequestJson);
CdsServiceResponseJson callService(String theServiceId, Object theCdsServiceRequestJson);
/**
* This is the REST method available at https://example.com/cds-services/{theServiceId}/feedback

View File

@ -20,9 +20,11 @@
package ca.uhn.hapi.fhir.cdshooks.api.json;
import ca.uhn.fhir.model.api.IModelJson;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
/**
* Users can define CDS Hooks extensions by extending this class.
* Implementors can extend this class for defining their custom extensions.
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class CdsHooksExtension implements IModelJson {}

View File

@ -21,6 +21,7 @@ package ca.uhn.hapi.fhir.cdshooks.api.json;
import ca.uhn.fhir.model.api.IModelJson;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import org.hl7.fhir.instance.model.api.IBaseResource;
import java.util.Collections;
@ -29,6 +30,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
@JsonIgnoreProperties(ignoreUnknown = true)
public class CdsServiceRequestContextJson extends BaseCdsServiceJson implements IModelJson {
@JsonAnyGetter

View File

@ -31,6 +31,7 @@ import ca.uhn.hapi.fhir.cdshooks.api.ICdsConfigService;
import ca.uhn.hapi.fhir.cdshooks.api.ICdsHooksDaoAuthorizationSvc;
import ca.uhn.hapi.fhir.cdshooks.api.ICdsServiceRegistry;
import ca.uhn.hapi.fhir.cdshooks.module.CdsHooksObjectMapperFactory;
import ca.uhn.hapi.fhir.cdshooks.serializer.CdsServiceRequestJsonDeserializer;
import ca.uhn.hapi.fhir.cdshooks.svc.CdsConfigServiceImpl;
import ca.uhn.hapi.fhir.cdshooks.svc.CdsHooksContextBooter;
import ca.uhn.hapi.fhir.cdshooks.svc.CdsServiceRegistryImpl;
@ -100,13 +101,15 @@ public class CdsHooksConfig {
ICdsCrServiceFactory theCdsCrServiceFactory,
ICrDiscoveryServiceFactory theCrDiscoveryServiceFactory,
FhirContext theFhirContext) {
final CdsServiceRequestJsonDeserializer cdsServiceRequestJsonDeserializer =
new CdsServiceRequestJsonDeserializer(theFhirContext, theObjectMapper);
return new CdsServiceRegistryImpl(
theCdsHooksContextBooter,
theCdsPrefetchSvc,
theObjectMapper,
theCdsCrServiceFactory,
theCrDiscoveryServiceFactory,
theFhirContext);
cdsServiceRequestJsonDeserializer);
}
@Bean

View File

@ -21,7 +21,6 @@ package ca.uhn.hapi.fhir.cdshooks.controller;
import ca.uhn.hapi.fhir.cdshooks.api.ICdsServiceRegistry;
import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceFeedbackJson;
import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceRequestJson;
import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseJson;
import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServicesJson;
import org.springframework.http.HttpStatus;
@ -73,7 +72,7 @@ public class CdsHooksController {
method = {RequestMethod.POST},
consumes = {MediaType.APPLICATION_JSON_VALUE})
public ResponseEntity<CdsServiceResponseJson> cdsServiceRequest(
@PathVariable("cds_hook") String theCdsHook, @RequestBody CdsServiceRequestJson theCdsServiceRequestJson) {
@PathVariable("cds_hook") String theCdsHook, @RequestBody Object theCdsServiceRequestJson) {
CdsServiceResponseJson response = myCdsServiceRegistry.callService(theCdsHook, theCdsServiceRequestJson);
return ResponseEntity.status(200)
.contentType(MediaType.APPLICATION_JSON)

View File

@ -20,84 +20,60 @@
package ca.uhn.hapi.fhir.cdshooks.serializer;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.serializer.FhirResourceDeserializer;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.hapi.fhir.cdshooks.api.json.CdsHooksExtension;
import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceJson;
import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceRequestContextJson;
import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceRequestJson;
import ca.uhn.hapi.fhir.cdshooks.svc.CdsServiceRegistryImpl;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import jakarta.annotation.Nonnull;
import org.hl7.fhir.instance.model.api.IBaseResource;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
public class CdsServiceRequestJsonDeserializer extends StdDeserializer<CdsServiceRequestJson> {
private final CdsServiceRegistryImpl myCdsServiceRegistry;
public class CdsServiceRequestJsonDeserializer {
private final ObjectMapper myObjectMapper;
private final FhirContext myFhirContext;
private final IParser myParser;
public CdsServiceRequestJsonDeserializer(CdsServiceRegistryImpl theCdsServiceRegistry, FhirContext theFhirContext) {
super(CdsServiceRequestJson.class);
myCdsServiceRegistry = theCdsServiceRegistry;
public CdsServiceRequestJsonDeserializer(
@Nonnull FhirContext theFhirContext, @Nonnull ObjectMapper theObjectMapper) {
myFhirContext = theFhirContext;
myParser = myFhirContext.newJsonParser().setPrettyPrint(true);
// We create a new ObjectMapper instead of using the one from the ApplicationContext to avoid an infinite loop
// during deserialization.
myObjectMapper = new ObjectMapper();
configureObjectMapper(myObjectMapper);
myObjectMapper = theObjectMapper;
}
@Override
public CdsServiceRequestJson deserialize(JsonParser theJsonParser, DeserializationContext theDeserializationContext)
throws IOException {
final JsonNode cdsServiceRequestJsonNode = theJsonParser.getCodec().readTree(theJsonParser);
final JsonNode hookNode = cdsServiceRequestJsonNode.get("hook");
final JsonNode extensionNode = cdsServiceRequestJsonNode.get("extension");
final JsonNode requestContext = cdsServiceRequestJsonNode.get("context");
final CdsServiceRequestJson cdsServiceRequestJson =
myObjectMapper.treeToValue(cdsServiceRequestJsonNode, CdsServiceRequestJson.class);
if (extensionNode != null) {
CdsHooksExtension myRequestExtension = deserializeExtension(hookNode.textValue(), extensionNode.toString());
cdsServiceRequestJson.setExtension(myRequestExtension);
public CdsServiceRequestJson deserialize(
@Nonnull CdsServiceJson theCdsServiceJson, @Nonnull Object theCdsServiceRequestJson) {
final JsonNode cdsServiceRequestJsonNode =
myObjectMapper.convertValue(theCdsServiceRequestJson, JsonNode.class);
final JsonNode contextNode = cdsServiceRequestJsonNode.get("context");
validateHookInstance(cdsServiceRequestJsonNode.get("hookInstance"));
validateHook(cdsServiceRequestJsonNode.get("hook"));
validateContext(contextNode);
try {
final JsonNode extensionNode = cdsServiceRequestJsonNode.get("extension");
final CdsServiceRequestJson cdsServiceRequestJson =
myObjectMapper.convertValue(cdsServiceRequestJsonNode, CdsServiceRequestJson.class);
LinkedHashMap<String, Object> map = myObjectMapper.readValue(contextNode.toString(), LinkedHashMap.class);
cdsServiceRequestJson.setContext(deserializeContext(map));
if (extensionNode != null) {
CdsHooksExtension myRequestExtension =
deserializeExtension(theCdsServiceJson, extensionNode.toString());
cdsServiceRequestJson.setExtension(myRequestExtension);
}
return cdsServiceRequestJson;
} catch (JsonProcessingException | IllegalArgumentException theEx) {
throw new InvalidRequestException(Msg.code(2551) + "Invalid CdsServiceRequest received. " + theEx);
}
if (requestContext != null) {
LinkedHashMap<String, Object> map =
myObjectMapper.readValue(requestContext.toString(), LinkedHashMap.class);
cdsServiceRequestJson.setContext(deserializeRequestContext(map));
}
return cdsServiceRequestJson;
}
void configureObjectMapper(ObjectMapper theObjectMapper) {
SimpleModule module = new SimpleModule();
module.addDeserializer(IBaseResource.class, new FhirResourceDeserializer(myFhirContext));
theObjectMapper.registerModule(module);
// set this as we will need to ignore properties which are not defined by specific implementation.
theObjectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}
CdsHooksExtension deserializeExtension(String theServiceId, String theExtension) throws JsonProcessingException {
final CdsServiceJson cdsServicesJson = myCdsServiceRegistry.getCdsServiceJson(theServiceId);
Class<? extends CdsHooksExtension> extensionClass = cdsServicesJson.getExtensionClass();
if (extensionClass == null) {
return null;
}
return myObjectMapper.readValue(theExtension, extensionClass);
}
CdsServiceRequestContextJson deserializeRequestContext(LinkedHashMap<String, Object> theMap)
CdsServiceRequestContextJson deserializeContext(LinkedHashMap<String, Object> theMap)
throws JsonProcessingException {
final CdsServiceRequestContextJson cdsServiceRequestContextJson = new CdsServiceRequestContextJson();
for (Map.Entry<String, Object> entry : theMap.entrySet()) {
@ -114,4 +90,31 @@ public class CdsServiceRequestJsonDeserializer extends StdDeserializer<CdsServic
}
return cdsServiceRequestContextJson;
}
private CdsHooksExtension deserializeExtension(
@Nonnull CdsServiceJson theCdsServiceJson, @Nonnull String theExtension) throws JsonProcessingException {
Class<? extends CdsHooksExtension> extensionClass = theCdsServiceJson.getExtensionClass();
if (extensionClass == null) {
return null;
}
return myObjectMapper.readValue(theExtension, extensionClass);
}
private void validateHook(JsonNode hookIdNode) {
if (hookIdNode == null) {
throw new InvalidRequestException(Msg.code(2549) + "hook cannot be null for a CdsServiceRequest.");
}
}
private void validateHookInstance(JsonNode hookInstanceNode) {
if (hookInstanceNode == null) {
throw new InvalidRequestException(Msg.code(2548) + "hookInstance cannot be null for a CdsServiceRequest.");
}
}
private void validateContext(JsonNode requestContextNode) {
if (requestContextNode == null) {
throw new InvalidRequestException(Msg.code(2550) + "context cannot be null for a CdsServiceRequest.");
}
}
}

View File

@ -20,7 +20,6 @@
package ca.uhn.hapi.fhir.cdshooks.svc;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.hapi.fhir.cdshooks.api.ICdsMethod;
@ -38,7 +37,6 @@ import ca.uhn.hapi.fhir.cdshooks.svc.cr.discovery.ICrDiscoveryServiceFactory;
import ca.uhn.hapi.fhir.cdshooks.svc.prefetch.CdsPrefetchSvc;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.google.common.annotations.VisibleForTesting;
import jakarta.annotation.Nonnull;
import jakarta.annotation.PostConstruct;
@ -50,7 +48,7 @@ import java.util.function.Function;
public class CdsServiceRegistryImpl implements ICdsServiceRegistry {
private static final Logger ourLog = LoggerFactory.getLogger(CdsServiceRegistryImpl.class);
private final CdsServiceRequestJsonDeserializer myCdsServiceRequestJsonDeserializer;
private CdsServiceCache myServiceCache;
private final CdsHooksContextBooter myCdsHooksContextBooter;
@ -65,19 +63,13 @@ public class CdsServiceRegistryImpl implements ICdsServiceRegistry {
ObjectMapper theObjectMapper,
ICdsCrServiceFactory theCdsCrServiceFactory,
ICrDiscoveryServiceFactory theCrDiscoveryServiceFactory,
FhirContext theFhirContext) {
CdsServiceRequestJsonDeserializer theCdsServiceRequestJsonDeserializer) {
myCdsHooksContextBooter = theCdsHooksContextBooter;
myCdsPrefetchSvc = theCdsPrefetchSvc;
myObjectMapper = theObjectMapper;
// registering this deserializer here instead of
// CdsHooksObjectMapperFactory to avoid circular
// dependency
SimpleModule module = new SimpleModule();
module.addDeserializer(
CdsServiceRequestJson.class, new CdsServiceRequestJsonDeserializer(this, theFhirContext));
myObjectMapper.registerModule(module);
myCdsCrServiceFactory = theCdsCrServiceFactory;
myCrDiscoveryServiceFactory = theCrDiscoveryServiceFactory;
myCdsServiceRequestJsonDeserializer = theCdsServiceRequestJsonDeserializer;
}
@PostConstruct
@ -91,10 +83,13 @@ public class CdsServiceRegistryImpl implements ICdsServiceRegistry {
}
@Override
public CdsServiceResponseJson callService(String theServiceId, CdsServiceRequestJson theCdsServiceRequestJson) {
public CdsServiceResponseJson callService(String theServiceId, Object theCdsServiceRequestJson) {
final CdsServiceJson cdsServiceJson = getCdsServiceJson(theServiceId);
final CdsServiceRequestJson deserializedRequest =
myCdsServiceRequestJsonDeserializer.deserialize(cdsServiceJson, theCdsServiceRequestJson);
ICdsServiceMethod serviceMethod = (ICdsServiceMethod) getCdsServiceMethodOrThrowException(theServiceId);
myCdsPrefetchSvc.augmentRequest(theCdsServiceRequestJson, serviceMethod);
Object response = serviceMethod.invoke(myObjectMapper, theCdsServiceRequestJson, theServiceId);
myCdsPrefetchSvc.augmentRequest(deserializedRequest, serviceMethod);
Object response = serviceMethod.invoke(myObjectMapper, deserializedRequest, theServiceId);
return encodeServiceResponse(theServiceId, response);
}

View File

@ -5,6 +5,7 @@ import ca.uhn.hapi.fhir.cdshooks.api.ICdsServiceRegistry;
import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceFeebackOutcomeEnum;
import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceFeedbackJson;
import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceJson;
import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceRequestContextJson;
import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceRequestJson;
import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseCardJson;
import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseJson;
@ -118,6 +119,7 @@ public class CdsHooksControllerTest {
request.setHookInstance(TEST_HOOK_INSTANCE);
request.setHook(HelloWorldService.TEST_HOOK);
request.setFhirServer(TEST_FHIR_SERVER);
request.setContext( withCdsServiceRequestContext());
String requestBody = myObjectMapper.writeValueAsString(request);
@ -142,8 +144,9 @@ public class CdsHooksControllerTest {
CdsServiceRequestJson request = new CdsServiceRequestJson();
request.setExtension(requestExtension);
request.setFhirServer(TEST_FHIR_SERVER);
request.setHook(HelloWorldService.TEST_HOOK_UNIVERSE_ID);
request.setHook(HelloWorldService.TEST_HOOK);
request.setContext(withCdsServiceRequestContext());
request.setHookInstance(UUID.randomUUID().toString());
String requestBody = myObjectMapper.writeValueAsString(request);
@ -163,6 +166,7 @@ public class CdsHooksControllerTest {
request.setHookInstance(TEST_HOOK_INSTANCE);
request.setHook(HelloWorldService.TEST_HOOK);
request.setFhirServer(TEST_FHIR_SERVER);
request.setContext(withCdsServiceRequestContext());
String requestBody = myObjectMapper.writeValueAsString(request);
@ -268,4 +272,11 @@ public class CdsHooksControllerTest {
return JsonUtil.serialize(input, true);
}
@Nonnull
private static CdsServiceRequestContextJson withCdsServiceRequestContext() {
CdsServiceRequestContextJson cdsServiceRequestContextJson = new CdsServiceRequestContextJson();
cdsServiceRequestContextJson.put("patientId", "Patient/123");
return cdsServiceRequestContextJson;
}
}

View File

@ -1,106 +1,152 @@
package ca.uhn.hapi.fhir.cdshooks.serializer;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.hapi.fhir.cdshooks.api.json.CdsHooksExtension;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceJson;
import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceRequestContextJson;
import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceRequestJson;
import ca.uhn.hapi.fhir.cdshooks.custom.extensions.model.ExampleExtension;
import ca.uhn.hapi.fhir.cdshooks.svc.CdsServiceRegistryImpl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.annotation.Nonnull;
import org.hl7.fhir.r4.model.Patient;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.LinkedHashMap;
import java.util.UUID;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.doReturn;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
@ExtendWith(MockitoExtension.class)
class CdsServiceRequestJsonDeserializerTest {
@Mock
private CdsServiceRegistryImpl myCdsServiceRegistry;
private static final String SERVICE_ID = "service-id";
private static final String EXAMPLE_PROPERTY_VALUE = "example-value";
private static final String EXAMPLE_PROPERTY_KEY = "example-property";
private static final String HOOK_ID = "hook-id";
private final FhirContext myFhirContext = FhirContext.forR4();
private final ObjectMapper myObjectMapper = new ObjectMapper();
private CdsServiceRequestJsonDeserializer myFixture;
@BeforeEach()
void setup() {
myFixture = new CdsServiceRequestJsonDeserializer(myCdsServiceRegistry, myFhirContext);
myFixture = new CdsServiceRequestJsonDeserializer(myFhirContext, myObjectMapper);
}
@Test
void configureObjectMapper() {
void deserialize_shouldDeserialize_whenValidCdsServiceRequestWithExtensionReceived() {
// setup
ObjectMapper input = new ObjectMapper();
final CdsServiceJson cdsServiceJson = withCdsServiceJsonIncludingExtensionClass();
final LinkedHashMap<String, Object> extension = withExtension();
final LinkedHashMap<String, Object> request = withRequest(extension);
request.put("context", withContext());
// execute
myFixture.configureObjectMapper(input);
final CdsServiceRequestJson actual = myFixture.deserialize(cdsServiceJson, request);
// validate
assertThat(input.getRegisteredModuleIds()).hasSize(1);
assertThat(actual.getExtension()).isInstanceOf(ExampleExtension.class);
final ExampleExtension actualExtension = (ExampleExtension) actual.getExtension();
assertThat(actualExtension.getExampleProperty()).isEqualTo(EXAMPLE_PROPERTY_VALUE);
}
@Test
void deserializeExtensionWhenClassFoundShouldDeserializeExtension() throws JsonProcessingException {
void deserialize_shouldIgnoreExtraFieldsInsideExtension_whenExtensionContainsMoreFieldsThanDefinedInClass() {
// setup
final CdsServiceJson cdsServiceJson = withCdsServiceJsonIncludingExtensionClass();
final LinkedHashMap<String, Object> extension = withExtension();
extension.put("example-extra-property", "example-extra-value");
final LinkedHashMap<String, Object> request = withRequest(extension);
request.put("context", withContext());
// execute
final CdsServiceRequestJson actual = myFixture.deserialize(cdsServiceJson, request);
// validate
assertThat(actual.getExtension()).isInstanceOf(ExampleExtension.class);
final ExampleExtension actualExtension = (ExampleExtension) actual.getExtension();
assertThat(actualExtension.getExampleProperty()).isEqualTo(EXAMPLE_PROPERTY_VALUE);
assertThat(actual.getContext().get("encounterId")).isEqualTo("Encounter/123");
}
@Nonnull
private static LinkedHashMap<String, Object> withContext() {
final LinkedHashMap<String, Object> context = new LinkedHashMap<>();
context.put("encounterId", "Encounter/123");
return context;
}
@Test
void deserialize_shouldThrow_whenCdsServiceRequestIncludesInvalidProperty() {
// setup
final CdsServiceJson cdsServiceJson = withCdsServiceJsonIncludingExtensionClass();
final LinkedHashMap<String, Object> extension = withExtension();
final LinkedHashMap<String, Object> request = withRequest(extension);
request.put("invalid-key", "some-value");
request.put("context", withContext());
// execute & validate
assertThatThrownBy(
() -> myFixture.deserialize(cdsServiceJson, request))
.isInstanceOf(InvalidRequestException.class)
.hasMessageContaining("HAPI-2551:")
.hasMessageContaining("Invalid CdsServiceRequest received.");
}
@Test
void deserialize_shouldReturnNullExtension_whenNotClassFound() {
// setup
final String serviceId = "service-id";
final String extension = """
{
"example-property": "example-value"
}
""";
final CdsServiceJson cdsServiceJson = new CdsServiceJson();
cdsServiceJson.setId(serviceId);
cdsServiceJson.setExtensionClass(ExampleExtension.class);
doReturn(cdsServiceJson).when(myCdsServiceRegistry).getCdsServiceJson(serviceId);
cdsServiceJson.setId(SERVICE_ID);
final LinkedHashMap<String, Object> extension = withExtension();
extension.put("example-extra-property", "example-extra-value");
final LinkedHashMap<String, Object> request = withRequest(extension);
request.put("context", withContext());
// execute
final ExampleExtension actual = (ExampleExtension) myFixture.deserializeExtension(serviceId, extension);
final CdsServiceRequestJson actual = myFixture.deserialize(cdsServiceJson, request);
// validate
assertThat(actual.getExampleProperty()).isEqualTo("example-value");
assertThat(actual.getExtension()).isNull();
}
@Test
void deserializeExtensionWhenClassFoundButExtensionHasExtraPropertiesShouldIgnoreExtraProperties() throws JsonProcessingException {
void deserialize_shouldThrow_whenHookNotFoundInRequest() {
// setup
final String serviceId = "service-id";
final String extension = """
{
"example-property": "example-value",
"example-extra-property": "example-extra-value"
}
""";
final CdsServiceJson cdsServiceJson = new CdsServiceJson();
cdsServiceJson.setId(serviceId);
cdsServiceJson.setExtensionClass(ExampleExtension.class);
doReturn(cdsServiceJson).when(myCdsServiceRegistry).getCdsServiceJson(serviceId);
// execute
final ExampleExtension actual = (ExampleExtension) myFixture.deserializeExtension(serviceId, extension);
// validate
assertThat(actual.getExampleProperty()).isEqualTo("example-value");
final CdsServiceJson cdsServiceJson = withCdsServiceJsonIncludingExtensionClass();
final LinkedHashMap<String, Object> request = new LinkedHashMap<>();
request.put("context", withContext());
request.put("hookInstance", UUID.randomUUID().toString());
// execute and validate
assertThatThrownBy(() -> myFixture.deserialize(cdsServiceJson, request))
.isInstanceOf(InvalidRequestException.class)
.hasMessageContaining("HAPI-2549:")
.hasMessageContaining("hook cannot be null for a CdsServiceRequest.");
}
@Test
void deserializeExtensionWhenNotClassFoundShouldReturnNull() throws JsonProcessingException {
void deserialize_shouldThrow_whenContextNotFoundInRequest() {
// setup
final String serviceId = "service-id";
final String extension = """
{
"example-property": "example-value"
}
""";
final CdsServiceJson cdsServiceJson = new CdsServiceJson();
cdsServiceJson.setId(serviceId);
doReturn(cdsServiceJson).when(myCdsServiceRegistry).getCdsServiceJson(serviceId);
// execute
final CdsHooksExtension actual = myFixture.deserializeExtension(serviceId, extension);
// validate
assertThat(actual).isNull();
final CdsServiceJson cdsServiceJson = withCdsServiceJsonIncludingExtensionClass();
final LinkedHashMap<String, Object> request = new LinkedHashMap<>();
request.put("hook", HOOK_ID);
request.put("hookInstance", UUID.randomUUID().toString());
// execute and validate
assertThatThrownBy(() -> myFixture.deserialize(cdsServiceJson, request))
.isInstanceOf(InvalidRequestException.class)
.hasMessageContaining("HAPI-2550:")
.hasMessageContaining("context cannot be null for a CdsServiceRequest.");
}
@Test
void deserializeRequestContextShouldDeserializeValidContext() throws JsonProcessingException {
void deserialize_shouldThrow_whenHookInstanceNotFoundInRequest() {
// setup
final CdsServiceJson cdsServiceJson = withCdsServiceJsonIncludingExtensionClass();
final LinkedHashMap<String, Object> request = new LinkedHashMap<>();
request.put("context", withContext());
request.put("hook", HOOK_ID);
// execute and validate
assertThatThrownBy(() -> myFixture.deserialize(cdsServiceJson, request))
.isInstanceOf(InvalidRequestException.class)
.hasMessageContaining("HAPI-2548:")
.hasMessageContaining("hookInstance cannot be null for a CdsServiceRequest.");
}
@Test
void deserializeRequestContext_shouldDeserialize_whenContextIsValid() throws JsonProcessingException {
// setup
final String encounterId = "123";
final Patient patientContext = new Patient();
@ -109,9 +155,34 @@ class CdsServiceRequestJsonDeserializerTest {
input.put("encounterId", encounterId);
input.put("patient", patientContext);
// execute
final CdsServiceRequestContextJson actual = myFixture.deserializeRequestContext(input);
final CdsServiceRequestContextJson actual = myFixture.deserializeContext(input);
// validate
assertThat(actual.get("encounterId")).isEqualTo(encounterId);
assertThat(actual.get("patient")).usingRecursiveComparison().isEqualTo(patientContext);
}
@Nonnull
private static LinkedHashMap<String, Object> withExtension() {
final LinkedHashMap<String, Object> extension = new LinkedHashMap<>();
extension.put(EXAMPLE_PROPERTY_KEY, EXAMPLE_PROPERTY_VALUE);
return extension;
}
@Nonnull
private static CdsServiceJson withCdsServiceJsonIncludingExtensionClass() {
final CdsServiceJson cdsServiceJson = new CdsServiceJson();
cdsServiceJson.setId(SERVICE_ID);
cdsServiceJson.setExtensionClass(ExampleExtension.class);
cdsServiceJson.setHook(HOOK_ID);
return cdsServiceJson;
}
@Nonnull
private static LinkedHashMap<String, Object> withRequest(@Nonnull LinkedHashMap<String, Object> theExtension) {
final LinkedHashMap<String, Object> request = new LinkedHashMap<>();
request.put("extension", theExtension);
request.put("hookInstance", UUID.randomUUID().toString());
request.put("hook", HOOK_ID);
return request;
}
}

View File

@ -1,10 +1,10 @@
package ca.uhn.hapi.fhir.cdshooks.svc;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceFeedbackJson;
import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceJson;
import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseJson;
import ca.uhn.hapi.fhir.cdshooks.serializer.CdsServiceRequestJsonDeserializer;
import ca.uhn.hapi.fhir.cdshooks.svc.cr.ICdsCrServiceFactory;
import ca.uhn.hapi.fhir.cdshooks.svc.cr.discovery.ICrDiscoveryServiceFactory;
import ca.uhn.hapi.fhir.cdshooks.svc.prefetch.CdsPrefetchSvc;
@ -33,13 +33,14 @@ class CdsServiceRegistryImplTest {
private ICrDiscoveryServiceFactory myCrDiscoveryServiceFactory;
@Mock
private CdsServiceCache myCdsServiceCache;
@Mock
private CdsServiceRequestJsonDeserializer myCdsServiceRequestJsonDeserializer;
private final ObjectMapper myObjectMapper = new ObjectMapper();
private final FhirContext myFhirContext = FhirContext.forR4();
private CdsServiceRegistryImpl myFixture;
@BeforeEach()
void setup() {
myFixture = new CdsServiceRegistryImpl(myCdsHooksContextBooter, myCdsPrefetchSvc, myObjectMapper, myCdsCrServiceFactory, myCrDiscoveryServiceFactory, myFhirContext);
myFixture = new CdsServiceRegistryImpl(myCdsHooksContextBooter, myCdsPrefetchSvc, myObjectMapper, myCdsCrServiceFactory, myCrDiscoveryServiceFactory, myCdsServiceRequestJsonDeserializer);
}
@Test

View File

@ -7,7 +7,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -7,7 +7,7 @@
<parent>
<artifactId>hapi-fhir-serviceloaders</artifactId>
<groupId>ca.uhn.hapi.fhir</groupId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -7,7 +7,7 @@
<parent>
<artifactId>hapi-fhir-serviceloaders</artifactId>
<groupId>ca.uhn.hapi.fhir</groupId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
@ -21,7 +21,7 @@
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-caching-api</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
</dependency>
<dependency>

View File

@ -7,7 +7,7 @@
<parent>
<artifactId>hapi-fhir-serviceloaders</artifactId>
<groupId>ca.uhn.hapi.fhir</groupId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -7,7 +7,7 @@
<parent>
<artifactId>hapi-fhir</artifactId>
<groupId>ca.uhn.hapi.fhir</groupId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>hapi-deployable-pom</artifactId>
<groupId>ca.uhn.hapi.fhir</groupId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
</parent>
<artifactId>hapi-fhir-spring-boot-sample-client-apache</artifactId>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-spring-boot</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -7,7 +7,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -7,7 +7,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -7,7 +7,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -8,7 +8,7 @@
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<packaging>pom</packaging>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<name>HAPI-FHIR</name>
<description>An open-source implementation of the FHIR specification in Java.</description>

View File

@ -7,7 +7,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>7.5.0-SNAPSHOT</version>
<version>7.5.1-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>