Updated utils
This commit is contained in:
parent
5747d5d841
commit
142fd4c108
|
@ -27,6 +27,7 @@ import org.hl7.fhir.instance.model.api.IBaseDatatype;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseExtension;
|
import org.hl7.fhir.instance.model.api.IBaseExtension;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseHasExtensions;
|
import org.hl7.fhir.instance.model.api.IBaseHasExtensions;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
@ -54,6 +55,34 @@ public class ExtensionUtil {
|
||||||
return extension;
|
return extension;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an new empty extension.
|
||||||
|
*
|
||||||
|
* @param theBase Base resource to add the extension to
|
||||||
|
* @return Returns a new extension
|
||||||
|
* @throws IllegalArgumentException IllegalArgumentException is thrown in case resource doesn't support extensions
|
||||||
|
*/
|
||||||
|
public static IBaseExtension<?, ?> addExtension(IBase theBase) {
|
||||||
|
return addExtension(theBase, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an extension with the specified URL
|
||||||
|
*
|
||||||
|
* @param theBase Base resource to add the extension to
|
||||||
|
* @param theUrl URL for the extension
|
||||||
|
* @return Returns a new extension with the specified URL.
|
||||||
|
* @throws IllegalArgumentException IllegalArgumentException is thrown in case resource doesn't support extensions
|
||||||
|
*/
|
||||||
|
public static IBaseExtension<?, ?> addExtension(IBase theBase, String theUrl) {
|
||||||
|
IBaseHasExtensions baseHasExtensions = validateExtensionSupport(theBase);
|
||||||
|
IBaseExtension extension = baseHasExtensions.addExtension();
|
||||||
|
if (theUrl != null) {
|
||||||
|
extension.setUrl(theUrl);
|
||||||
|
}
|
||||||
|
return extension;
|
||||||
|
}
|
||||||
|
|
||||||
private static IBaseHasExtensions validateExtensionSupport(IBase theBase) {
|
private static IBaseHasExtensions validateExtensionSupport(IBase theBase) {
|
||||||
if (!(theBase instanceof IBaseHasExtensions)) {
|
if (!(theBase instanceof IBaseHasExtensions)) {
|
||||||
throw new IllegalArgumentException(String.format("Expected instance that supports extensions, but got %s", theBase));
|
throw new IllegalArgumentException(String.format("Expected instance that supports extensions, but got %s", theBase));
|
||||||
|
@ -105,10 +134,15 @@ public class ExtensionUtil {
|
||||||
* @return Returns the first available extension with the specified URL, or null if such extension doesn't exist
|
* @return Returns the first available extension with the specified URL, or null if such extension doesn't exist
|
||||||
*/
|
*/
|
||||||
public static IBaseExtension<?, ?> getExtension(IBase theBase, String theExtensionUrl) {
|
public static IBaseExtension<?, ?> getExtension(IBase theBase, String theExtensionUrl) {
|
||||||
return validateExtensionSupport(theBase)
|
Predicate<IBaseExtension> filter;
|
||||||
.getExtension()
|
if (theExtensionUrl == null) {
|
||||||
|
filter = (e -> true);
|
||||||
|
} else {
|
||||||
|
filter = (e -> theExtensionUrl.equals(e.getUrl()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return getExtensions(theBase, filter)
|
||||||
.stream()
|
.stream()
|
||||||
.filter(e -> theExtensionUrl.equals(e.getUrl()))
|
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
}
|
}
|
||||||
|
@ -129,10 +163,21 @@ public class ExtensionUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets all extensions with the specified URL
|
* Removes all extensions.
|
||||||
*
|
*
|
||||||
* @param theBase The resource to get the extension for
|
* @param theBase The resource to clear the extension for
|
||||||
* @return Returns all extension with the specified URL, or an empty list if such extensions do not exist
|
* @return Returns all extension that were removed
|
||||||
|
*/
|
||||||
|
public static List<IBaseExtension<?, ?>> clearExtensions(IBase theBase) {
|
||||||
|
return clearExtensions(theBase, (e -> true));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all extensions that match the specified predicate
|
||||||
|
*
|
||||||
|
* @param theBase The base object to clear the extension for
|
||||||
|
* @param theFilter Defines which extensions should be cleared
|
||||||
|
* @return Returns all extension that were removed
|
||||||
*/
|
*/
|
||||||
public static List<IBaseExtension<?, ?>> clearExtensions(IBase theBase, Predicate<? super IBaseExtension> theFilter) {
|
public static List<IBaseExtension<?, ?>> clearExtensions(IBase theBase, Predicate<? super IBaseExtension> theFilter) {
|
||||||
List<IBaseExtension<?, ?>> retVal = getExtensions(theBase, theFilter);
|
List<IBaseExtension<?, ?>> retVal = getExtensions(theBase, theFilter);
|
||||||
|
|
|
@ -334,11 +334,11 @@ public final class TerserUtil {
|
||||||
setFieldByFhirPath(theFhirContext.newTerser(), theFhirPath, theResource, theValue);
|
setFieldByFhirPath(theFhirContext.newTerser(), theFhirPath, theResource, theValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<IBase> getFieldByFhirPath(FhirContext theFhirContext, String theFhirPath, IBaseResource theResource) {
|
public static List<IBase> getFieldByFhirPath(FhirContext theFhirContext, String theFhirPath, IBase theResource) {
|
||||||
return theFhirContext.newTerser().getValues(theResource, theFhirPath, false, false);
|
return theFhirContext.newTerser().getValues(theResource, theFhirPath, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IBase getFirstFieldByFhirPath(FhirContext theFhirContext, String theFhirPath, IBaseResource theResource) {
|
public static IBase getFirstFieldByFhirPath(FhirContext theFhirContext, String theFhirPath, IBase theResource) {
|
||||||
List<IBase> values = getFieldByFhirPath(theFhirContext, theFhirPath, theResource);
|
List<IBase> values = getFieldByFhirPath(theFhirContext, theFhirPath, theResource);
|
||||||
if (values == null || values.isEmpty()) {
|
if (values == null || values.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -108,15 +108,35 @@ public class TerserUtilHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets values of the specified field.
|
* Gets values for the specified child field.
|
||||||
*
|
*
|
||||||
* @param theField The field to get values from
|
* @param theField The field to get values from
|
||||||
* @return Returns a collection of values containing values or null if the specified field doesn't exist
|
* @return Returns a list of retrieved values or null if the specified field doesn't exist
|
||||||
*/
|
*/
|
||||||
public List<IBase> getFieldValues(String theField) {
|
public List<IBase> getFieldValues(String theField) {
|
||||||
return TerserUtil.getValues(myContext, myResource, theField);
|
return TerserUtil.getValues(myContext, myResource, theField);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets values for the specified field values by FHIRPath.
|
||||||
|
*
|
||||||
|
* @param theFhirPath The FHIR path expression to get the values from
|
||||||
|
* @return Returns a collection of values or null if the specified field doesn't exist
|
||||||
|
*/
|
||||||
|
public List<IBase> getFieldValuesByFhirPath(String theFhirPath) {
|
||||||
|
return TerserUtil.getFieldByFhirPath(myContext, theFhirPath, myResource);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets first available value for the specified field values by FHIRPath.
|
||||||
|
*
|
||||||
|
* @param theFhirPath The FHIR path expression to get the values from
|
||||||
|
* @return Returns the value or null if the specified field doesn't exist or is empty
|
||||||
|
*/
|
||||||
|
public IBase getFieldValueByFhirPath(String theFhirPath) {
|
||||||
|
return TerserUtil.getFirstFieldByFhirPath(myContext, theFhirPath, myResource);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets first available values of the specified field.
|
* Gets first available values of the specified field.
|
||||||
*
|
*
|
||||||
|
|
|
@ -1,148 +0,0 @@
|
||||||
package ca.uhn.fhir.jpa.interceptor;
|
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
|
||||||
import ca.uhn.fhir.interceptor.api.Hook;
|
|
||||||
import ca.uhn.fhir.interceptor.api.Interceptor;
|
|
||||||
import ca.uhn.fhir.interceptor.api.Pointcut;
|
|
||||||
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
|
||||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
|
||||||
import ca.uhn.fhir.model.primitive.CodeDt;
|
|
||||||
import ca.uhn.fhir.model.primitive.StringDt;
|
|
||||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
|
||||||
import ca.uhn.fhir.util.ExtensionUtil;
|
|
||||||
import ca.uhn.fhir.util.TerserUtil;
|
|
||||||
import ca.uhn.fhir.util.TerserUtilHelper;
|
|
||||||
import org.hl7.fhir.instance.model.api.IBase;
|
|
||||||
import org.hl7.fhir.instance.model.api.IBaseExtension;
|
|
||||||
import org.hl7.fhir.instance.model.api.IBaseReference;
|
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
|
||||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Interceptor
|
|
||||||
public class ResourceFlatteningInterceptor {
|
|
||||||
|
|
||||||
private static final Logger ourLog = LoggerFactory.getLogger(ResourceFlatteningInterceptor.class);
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private FhirContext myFhirContext;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private DaoRegistry myDaoRegistry;
|
|
||||||
|
|
||||||
public ResourceFlatteningInterceptor(FhirContext theFhirContext, DaoRegistry theDaoRegistry) {
|
|
||||||
myFhirContext = theFhirContext;
|
|
||||||
myDaoRegistry = theDaoRegistry;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Hook(Pointcut.STORAGE_PRECOMMIT_RESOURCE_UPDATED)
|
|
||||||
public void resourceUpdatedPreCommit(RequestDetails theRequest, IBaseResource theResource) {
|
|
||||||
ourLog.debug("Validating address on for create {}, {}", theResource, theRequest);
|
|
||||||
handleRequest(theRequest, theResource);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Hook(Pointcut.STORAGE_PRECOMMIT_RESOURCE_CREATED)
|
|
||||||
public void resourceCreatedPreCommit(RequestDetails theRequest, IBaseResource theResource) {
|
|
||||||
ourLog.debug("Validating address on for create {}, {}", theResource, theRequest);
|
|
||||||
handleRequest(theRequest, theResource);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleRequest(RequestDetails theRequest, IBaseResource theResource) {
|
|
||||||
switch (myFhirContext.getResourceType(theResource)) {
|
|
||||||
case "Location":
|
|
||||||
flattenLocation(theResource);
|
|
||||||
return;
|
|
||||||
case "Account":
|
|
||||||
flattenAccount(theResource);
|
|
||||||
return;
|
|
||||||
case "Role":
|
|
||||||
flattenRole(theResource);
|
|
||||||
return;
|
|
||||||
default:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void flattenLocation(IBaseResource theResource) {
|
|
||||||
TerserUtilHelper helper = TerserUtilHelper.newHelper(myFhirContext, theResource);
|
|
||||||
IBase managingOrganizationsRef = helper.getFieldValue("managingOrganization");
|
|
||||||
if (managingOrganizationsRef == null) {
|
|
||||||
ourLog.info("No organization is associated with {}", theResource);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
IBaseResource referencedOrg = ((IBaseReference) managingOrganizationsRef).getResource();
|
|
||||||
if (referencedOrg == null) {
|
|
||||||
ourLog.warn("Missing value for reference {}", referencedOrg);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
TerserUtil.getValues(myFhirContext, referencedOrg, "address").clear();
|
|
||||||
|
|
||||||
IBase newAddress = helper.getFieldValue("address");
|
|
||||||
TerserUtil.setFieldByFhirPath(myFhirContext, "address", referencedOrg, newAddress);
|
|
||||||
|
|
||||||
IFhirResourceDao dao = getDao(referencedOrg);
|
|
||||||
dao.update(referencedOrg);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void flattenAccount(IBaseResource theAccount) {
|
|
||||||
TerserUtilHelper account = TerserUtilHelper.newHelper(myFhirContext, theAccount);
|
|
||||||
List<IBase> owner = account.getFieldValues("owner");
|
|
||||||
if (owner.isEmpty()) {
|
|
||||||
ourLog.info("No organization is associated with {}", theAccount);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (IBase o : owner) {
|
|
||||||
IBaseResource org = ((IBaseReference) o).getResource();
|
|
||||||
if (org == null) {
|
|
||||||
ourLog.warn("Missing value for reference {}", o);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
IBaseExtension ext = ExtensionUtil.getOrCreateExtension(org, "http://hapifhir.org/Flattener/Account");
|
|
||||||
IPrimitiveType status = (IPrimitiveType) TerserUtil.getFirstFieldByFhirPath(myFhirContext, "status", theAccount);
|
|
||||||
ExtensionUtil.setExtension(myFhirContext, ext, status.getValueAsString());
|
|
||||||
|
|
||||||
IFhirResourceDao dao = getDao(org);
|
|
||||||
dao.update(org);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// PractitionerRole.specialty becomes Practitioner.Extension.valueCode
|
|
||||||
private void flattenRole(IBaseResource theRole) {
|
|
||||||
TerserUtilHelper role = TerserUtilHelper.newHelper(myFhirContext, theRole);
|
|
||||||
|
|
||||||
List<IBase> specialties = role.getFieldValues("specialty");
|
|
||||||
if (specialties.isEmpty()) {
|
|
||||||
ourLog.info("No specialties are associated with {}", theRole);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (IBase o : specialties) {
|
|
||||||
IBaseResource practitioner = (IBaseResource) TerserUtil.getValue(myFhirContext, theRole, "practitioner");
|
|
||||||
if (practitioner == null) {
|
|
||||||
ourLog.warn("Practitioner is not set on {}", theRole);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
String roleUrl = "http://hapifhir.org/Flattener/Role/" + theRole.toString();
|
|
||||||
|
|
||||||
IBaseExtension ext = ExtensionUtil.getOrCreateExtension(practitioner, roleUrl);
|
|
||||||
ext.setValue(new CodeDt(String.valueOf(role.getFieldValue("status"))));
|
|
||||||
|
|
||||||
IFhirResourceDao dao = getDao(practitioner);
|
|
||||||
dao.update(practitioner);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private IFhirResourceDao getDao(IBaseResource theReferencedOrg) {
|
|
||||||
String resourceType = myFhirContext.getResourceType(theReferencedOrg);
|
|
||||||
return myDaoRegistry.getResourceDao(resourceType);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,124 +0,0 @@
|
||||||
package ca.uhn.fhir.jpa.interceptor;
|
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
|
||||||
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
|
||||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
|
||||||
import ca.uhn.fhir.jpa.provider.r4.BaseResourceProviderR4Test;
|
|
||||||
import ca.uhn.fhir.util.TerserUtilHelper;
|
|
||||||
import org.checkerframework.checker.units.qual.A;
|
|
||||||
import org.hl7.fhir.instance.model.api.IBase;
|
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
|
||||||
import org.hl7.fhir.r4.model.Account;
|
|
||||||
import org.hl7.fhir.r4.model.Address;
|
|
||||||
import org.hl7.fhir.r4.model.CodeableConcept;
|
|
||||||
import org.hl7.fhir.r4.model.Location;
|
|
||||||
import org.hl7.fhir.r4.model.Organization;
|
|
||||||
import org.hl7.fhir.r4.model.PractitionerRole;
|
|
||||||
import org.hl7.fhir.r4.model.Reference;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
|
||||||
|
|
||||||
class ResourceFlatteningInterceptorTest extends BaseResourceProviderR4Test {
|
|
||||||
|
|
||||||
private ResourceFlatteningInterceptor myInterceptor;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private DaoRegistry myDaoRegistry;
|
|
||||||
@Autowired
|
|
||||||
private FhirContext myFhirContext;
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
public void init() {
|
|
||||||
myInterceptor = new ResourceFlatteningInterceptor(myFhirContext, myDaoRegistry);
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T extends IBaseResource> IFhirResourceDao<T> getDao(IBaseResource theResource) {
|
|
||||||
String type = myFhirContext.getResourceType(theResource);
|
|
||||||
return myDaoRegistry.getResourceDao(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testFlatteningOfLocation() {
|
|
||||||
// Location.address becomes Organization.address
|
|
||||||
Organization newOrganization = newPersistentOrganization();
|
|
||||||
|
|
||||||
Location location = new Location();
|
|
||||||
Address newAddress = new Address();
|
|
||||||
newAddress.addLine("NEW LINE 1").setCity("NEW CITY");
|
|
||||||
location.setAddress(newAddress);
|
|
||||||
location.setManagingOrganization(new Reference(newOrganization));
|
|
||||||
|
|
||||||
myInterceptor.resourceCreatedPreCommit(null, location);
|
|
||||||
|
|
||||||
Organization savedOrganization = (Organization) getDao(newOrganization)
|
|
||||||
.read(newOrganization.getIdElement());
|
|
||||||
assertEquals(1, savedOrganization.getAddress().size(), "Expect old addresses cleared up");
|
|
||||||
|
|
||||||
Address savedAddress = savedOrganization.getAddressFirstRep();
|
|
||||||
assertTrue(newAddress.equalsDeep(savedAddress), "Expect organization address replaced");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
private Organization newPersistentOrganization() {
|
|
||||||
Organization newOrganization = newOrganization();
|
|
||||||
return (Organization) getDao(newOrganization).create(newOrganization).getResource();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
private Organization newOrganization() {
|
|
||||||
Organization retVal = new Organization();
|
|
||||||
retVal.addAddress()
|
|
||||||
.addLine("Address 1 Line 1")
|
|
||||||
.setCity("Address 1 City");
|
|
||||||
retVal.addAddress()
|
|
||||||
.addLine("Address 2 Line 1")
|
|
||||||
.setCity("Address 2 City");
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testFlatteningOfLocationWithEmptyManagingOrg() {
|
|
||||||
try {
|
|
||||||
myInterceptor.resourceCreatedPreCommit(null, new Location());
|
|
||||||
} catch (Exception e) {
|
|
||||||
fail("Expected no errors");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testFlatteningOfAccount() {
|
|
||||||
// Account.status become Organization.Extension.valueCode
|
|
||||||
Organization organization = newPersistentOrganization();
|
|
||||||
assertTrue(organization.getExtension().isEmpty());
|
|
||||||
|
|
||||||
Account account = new Account();
|
|
||||||
account.setName("Test Account");
|
|
||||||
account.setStatus(Account.AccountStatus.ACTIVE);
|
|
||||||
account.setDescription("Test Active Account");
|
|
||||||
account.setOwner(new Reference(organization));
|
|
||||||
|
|
||||||
myInterceptor.resourceCreatedPreCommit(null, account);
|
|
||||||
|
|
||||||
Organization savedOrganization = (Organization) getDao(organization).read(organization.getIdElement());
|
|
||||||
assertFalse(organization.getExtension().isEmpty());
|
|
||||||
|
|
||||||
assertEquals(Account.AccountStatus.ACTIVE.toCode(), organization.getExtension().get(0).getValue().primitiveValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testFlatteningOfAnEmptyAccout() {
|
|
||||||
myInterceptor.resourceCreatedPreCommit(null, new Account());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testFlatteningOfPractitionerRole() {
|
|
||||||
// PractitionerRole.specialty becomes Practitioner.Extension.valueCode
|
|
||||||
PractitionerRole role = new PractitionerRole();
|
|
||||||
CodeableConcept specialty = role.addSpecialty();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
Loading…
Reference in New Issue