Properly support chains in JPA conditional URLs

This commit is contained in:
James Agnew 2015-10-04 15:38:58 -04:00
parent 5e2d94d745
commit 43aad1eb98
18 changed files with 924 additions and 109 deletions

View File

@ -84,6 +84,7 @@ import ca.uhn.fhir.rest.param.ResourceParameter.Mode;
import ca.uhn.fhir.rest.param.StringAndListParam;
import ca.uhn.fhir.rest.param.TokenAndListParam;
import ca.uhn.fhir.rest.param.TransactionParameter;
import ca.uhn.fhir.rest.param.UriAndListParam;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.IDynamicSearchResourceProvider;
@ -630,6 +631,9 @@ public class MethodUtil {
case TOKEN:
binder = new QueryParameterAndBinder(TokenAndListParam.class, Collections.<Class<? extends IQueryParameterType>> emptyList());
break;
case URI:
binder = new QueryParameterAndBinder(UriAndListParam.class, Collections.<Class<? extends IQueryParameterType>> emptyList());
break;
}
return binder.parse(theUnqualifiedParamName, theParameters);

View File

@ -1,6 +1,7 @@
package ca.uhn.fhir.cli;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@ -28,7 +29,6 @@ import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu2.resource.Bundle;
import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry;
import ca.uhn.fhir.model.dstu2.resource.Bundle.EntryRequest;
import ca.uhn.fhir.model.dstu2.resource.DataElement;
import ca.uhn.fhir.model.dstu2.resource.SearchParameter;
import ca.uhn.fhir.model.dstu2.valueset.HTTPVerbEnum;
import ca.uhn.fhir.model.primitive.IdDt;
@ -61,9 +61,14 @@ public class ExampleDataUploader extends BaseCommand {
opt.setRequired(false);
options.addOption(opt);
opt = new Option("t", "target", true, "Base URL for the target server");
opt = new Option("t", "target", true, "Base URL for the target server (e.g. \"http://example.com/fhir\")");
opt.setRequired(true);
options.addOption(opt);
opt = new Option("l", "limit", true, "Sets a limit to the number of resources the uploader will try to upload");
opt.setRequired(false);
options.addOption(opt);
return options;
}
@ -76,6 +81,15 @@ public class ExampleDataUploader extends BaseCommand {
} else if (targetServer.startsWith("http") == false) {
throw new ParseException("Invalid target server specified, must begin with 'http'");
}
Integer limit = null;
String limitString = theCommandLine.getOptionValue('l');
if (isNotBlank(limitString)) {
try {
limit = Integer.parseInt(limitString);
} catch (NumberFormatException e) {
throw new ParseException("Invalid number for limit (-l) option, must be a number: " + limitString);
}
}
FhirContext ctx = FhirContext.forDstu2();
String specUrl = "http://hl7.org/fhir/" + specVersion + "/examples-json.zip";
@ -100,7 +114,13 @@ public class ExampleDataUploader extends BaseCommand {
Bundle bundle = new Bundle();
int count = 0;
while (true) {
count++;
if (limit != null && count > limit) {
break;
}
ZipEntry nextEntry = zis.getNextEntry();
if (nextEntry == null) {
break;

View File

@ -23,8 +23,6 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.text.Normalizer;
import java.util.ArrayList;
import java.util.Collection;
@ -93,12 +91,14 @@ import ca.uhn.fhir.jpa.entity.TagDefinition;
import ca.uhn.fhir.jpa.entity.TagTypeEnum;
import ca.uhn.fhir.jpa.util.StopWatch;
import ca.uhn.fhir.model.api.IQueryParameterAnd;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.Tag;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.base.composite.BaseCodingDt;
import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt;
import ca.uhn.fhir.model.dstu.resource.BaseResource;
import ca.uhn.fhir.model.dstu2.composite.MetaDt;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
@ -110,8 +110,13 @@ import ca.uhn.fhir.rest.method.MethodUtil;
import ca.uhn.fhir.rest.method.QualifiedParamList;
import ca.uhn.fhir.rest.method.RestSearchParameterTypeEnum;
import ca.uhn.fhir.rest.param.DateRangeParam;
import ca.uhn.fhir.rest.param.StringAndListParam;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.param.TokenAndListParam;
import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.param.UriAndListParam;
import ca.uhn.fhir.rest.param.UriParam;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.IBundleProvider;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
@ -120,9 +125,36 @@ import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
import ca.uhn.fhir.util.FhirTerser;
import net.sourceforge.cobertura.CoverageIgnore;
public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
/**
* These are parameters which are supported by {@link BaseHapiFhirResourceDao#searchForIds(Map)}
*/
protected static final Map<String, Class<? extends IQueryParameterType>> RESOURCE_META_PARAMS;
/**
* These are parameters which are supported by {@link BaseHapiFhirResourceDao#searchForIds(Map)}
*/
protected static final Map<String, Class<? extends IQueryParameterAnd<?>>> RESOURCE_META_AND_PARAMS;
static {
Map<String, Class<? extends IQueryParameterType>> resourceMetaParams = new HashMap<String, Class<? extends IQueryParameterType>>();
Map<String, Class<? extends IQueryParameterAnd<?>>> resourceMetaAndParams = new HashMap<String, Class<? extends IQueryParameterAnd<?>>>();
resourceMetaParams.put(BaseResource.SP_RES_ID, StringParam.class);
resourceMetaAndParams.put(BaseResource.SP_RES_ID, StringAndListParam.class);
resourceMetaParams.put(BaseResource.SP_RES_LANGUAGE, StringParam.class);
resourceMetaAndParams.put(BaseResource.SP_RES_LANGUAGE, StringAndListParam.class);
resourceMetaParams.put(Constants.PARAM_TAG, TokenParam.class);
resourceMetaAndParams.put(Constants.PARAM_TAG, TokenAndListParam.class);
resourceMetaParams.put(Constants.PARAM_PROFILE, UriParam.class);
resourceMetaAndParams.put(Constants.PARAM_PROFILE, UriAndListParam.class);
resourceMetaParams.put(Constants.PARAM_SECURITY, TokenParam.class);
resourceMetaAndParams.put(Constants.PARAM_SECURITY, TokenAndListParam.class);
RESOURCE_META_PARAMS = Collections.unmodifiableMap(resourceMetaParams);
RESOURCE_META_AND_PARAMS = Collections.unmodifiableMap(resourceMetaAndParams);
}
public static final long INDEX_STATUS_INDEXED = Long.valueOf(1L);
public static final long INDEX_STATUS_INDEXING_FAILED = Long.valueOf(2L);
public static final String NS_JPA_PROFILE = "https://github.com/jamesagnew/hapi-fhir/ns/jpa/profile";
@ -703,6 +735,30 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
return false;
}
@CoverageIgnore
protected static IQueryParameterAnd<?> newInstanceAnd(String chain) {
IQueryParameterAnd<?> type;
Class<? extends IQueryParameterAnd<?>> clazz = RESOURCE_META_AND_PARAMS.get(chain);
try {
type = clazz.newInstance();
} catch (Exception e) {
throw new InternalErrorException("Failure creating instance of " + clazz, e);
}
return type;
}
@CoverageIgnore
protected static IQueryParameterType newInstanceType(String chain) {
IQueryParameterType type;
Class<? extends IQueryParameterType> clazz = RESOURCE_META_PARAMS.get(chain);
try {
type = clazz.newInstance();
} catch (Exception e) {
throw new InternalErrorException("Failure creating instance of " + clazz, e);
}
return type;
}
protected <R extends IResource> Set<Long> processMatchUrl(String theMatchUrl, Class<R> theResourceType) {
RuntimeResourceDefinition resourceDef = getContext().getResourceDefinition(theResourceType);
@ -745,13 +801,13 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
String paramName = next.getName();
String qualifier = null;
for (int i = 0; i < paramMap.size(); i++) {
for (int i = 0; i < paramName.length(); i++) {
switch (paramName.charAt(i)) {
case '.':
case ':':
qualifier = paramName.substring(i);
paramName = paramName.substring(0, i);
i = Integer.MAX_VALUE;
i = Integer.MAX_VALUE - 1;
break;
}
}
@ -787,10 +843,16 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
continue;
}
if (nextParamName.startsWith("_")) {
continue;
if (RESOURCE_META_PARAMS.containsKey(nextParamName)) {
if (isNotBlank(paramList.get(0).getQualifier()) && paramList.get(0).getQualifier().startsWith(".")) {
throw new InvalidRequestException("Invalid parameter chain: " + nextParamName + paramList.get(0).getQualifier());
}
IQueryParameterAnd<?> type = newInstanceAnd(nextParamName);
type.setValuesAsQueryTokens((paramList));
paramMap.add(nextParamName, type);
} else if (nextParamName.startsWith("_")) {
// ignore these since they aren't search params (e.g. _sort)
} else {
RuntimeSearchParam paramDef = resourceDef.getSearchParam(nextParamName);
if (paramDef == null) {
throw new InvalidRequestException("Failed to parse match URL[" + theMatchUrl + "] - Resource type " + resourceDef.getName() + " does not have a parameter with name: " + nextParamName);
@ -799,6 +861,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
IQueryParameterAnd<?> param = MethodUtil.parseQueryParams(paramDef, nextParamName, paramList);
paramMap.add(nextParamName, param);
}
}
return paramMap;
}

View File

@ -656,8 +656,6 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
}
boolean foundChainMatch = false;
for (Class<? extends IBaseResource> nextType : resourceTypes) {
RuntimeResourceDefinition typeDef = getContext().getResourceDefinition(nextType);
String chain = ref.getChain();
String remainingChain = null;
@ -667,29 +665,48 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
chain = chain.substring(0, chainDotIndex);
}
RuntimeSearchParam param = typeDef.getSearchParam(chain);
for (Class<? extends IBaseResource> nextType : resourceTypes) {
RuntimeResourceDefinition typeDef = getContext().getResourceDefinition(nextType);
IFhirResourceDao<?> dao = getDao(nextType);
if (dao == null) {
ourLog.debug("Don't have a DAO for type {}", nextType.getSimpleName());
continue;
}
int qualifierIndex = chain.indexOf(':');
String qualifier = null;
if (qualifierIndex != -1) {
qualifier = chain.substring(qualifierIndex);
chain = chain.substring(0, qualifierIndex);
}
boolean isMeta = RESOURCE_META_PARAMS.containsKey(chain);
RuntimeSearchParam param = null;
if (!isMeta) {
param = typeDef.getSearchParam(chain);
if (param == null) {
ourLog.debug("Type {} doesn't have search param {}", nextType.getSimpleName(), param);
continue;
}
IFhirResourceDao<?> dao = getDao(nextType);
if (dao == null) {
ourLog.debug("Don't have a DAO for type {}", nextType.getSimpleName(), param);
continue;
}
IQueryParameterType chainValue;
if (remainingChain != null) {
if (param.getParamType() != RestSearchParameterTypeEnum.REFERENCE) {
if (param == null || param.getParamType() != RestSearchParameterTypeEnum.REFERENCE) {
ourLog.debug("Type {} parameter {} is not a reference, can not chain {}", new Object[] { nextType.getSimpleName(), chain, remainingChain });
continue;
}
chainValue = new ReferenceParam();
chainValue.setValueAsQueryToken(null, resourceId);
chainValue.setValueAsQueryToken(qualifier, resourceId);
((ReferenceParam) chainValue).setChain(remainingChain);
} else if (isMeta) {
IQueryParameterType type = newInstanceType(chain);
type.setValueAsQueryToken(qualifier, resourceId);
chainValue = type;
} else {
chainValue = toParameterType(param, resourceId);
chainValue = toParameterType(param, qualifier, resourceId);
}
foundChainMatch = true;
@ -729,6 +746,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
return new HashSet<Long>(q.getResultList());
}
private Set<Long> addPredicateString(String theParamName, Set<Long> thePids, List<? extends IQueryParameterType> theList) {
if (theList == null || theList.isEmpty()) {
return thePids;
@ -783,7 +801,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
} else if (Constants.PARAM_SECURITY.equals(theParamName)) {
tagType = TagTypeEnum.SECURITY_LABEL;
} else {
throw new IllegalArgumentException("Paramname: " + theParamName); // shouldn't happen
throw new IllegalArgumentException("Param name: " + theParamName); // shouldn't happen
}
for (List<? extends IQueryParameterType> nextAndParams : theList) {
@ -1358,7 +1376,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
}
ourLog.info("Processed delete on {} (matched {} resource(s)) in {}ms", new Object[] {theUrl, resource.size(), w.getMillisAndRestart()});
ourLog.info("Processed delete on {} (matched {} resource(s)) in {}ms", new Object[] { theUrl, resource.size(), w.getMillisAndRestart() });
return new DaoMethodOutcome();
}
@ -2114,16 +2132,16 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
List<Long> pidsSubList = pids.subList(theFromIndex, theToIndex);
// Load includes
if (theParams.getEverythingMode()==null) {
if (theParams.getEverythingMode() == null) {
pidsSubList = new ArrayList<Long>(pidsSubList);
revIncludedPids.addAll(loadReverseIncludes(pidsSubList, theParams.getIncludes(), false, null));
}
// Execute the query and make sure we return distinct results
List<IBaseResource> retVal = new ArrayList<IBaseResource>();
loadResourcesByPid(pidsSubList, retVal, revIncludedPids, false);
List<IBaseResource> resources = new ArrayList<IBaseResource>();
loadResourcesByPid(pidsSubList, resources, revIncludedPids, false);
return retVal;
return resources;
}
});
@ -2140,13 +2158,14 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
}
};
ourLog.info("Processed search for {} on {} in {}ms", new Object[] { myResourceName, theParams, w.getMillisAndRestart() });
ourLog.info(" {} on {} in {}ms", new Object[] { myResourceName, theParams, w.getMillisAndRestart() });
return retVal;
}
private List<Long> processSort(final SearchParameterMap theParams, Set<Long> loadPids) {
private List<Long> processSort(final SearchParameterMap theParams, Set<Long> theLoadPids) {
final List<Long> pids;
Set<Long> loadPids = theLoadPids;
if (theParams.getSort() != null && isNotBlank(theParams.getSort().getParamName())) {
List<Order> orders = new ArrayList<Order>();
List<Predicate> predicates = new ArrayList<Predicate>();
@ -2220,7 +2239,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
for (Entry<String, List<List<? extends IQueryParameterType>>> nextParamEntry : params.entrySet()) {
String nextParamName = nextParamEntry.getKey();
if (nextParamName.equals("_id")) {
if (nextParamName.equals(BaseResource.SP_RES_ID)) {
if (nextParamEntry.getValue().isEmpty()) {
continue;
@ -2262,7 +2281,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
}
}
} else if (nextParamName.equals("_language")) {
} else if (nextParamName.equals(BaseResource.SP_RES_LANGUAGE)) {
pids = addPredicateLanguage(pids, nextParamEntry.getValue());
@ -2413,10 +2432,10 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
return qp;
}
private IQueryParameterType toParameterType(RuntimeSearchParam theParam, String theValueAsQueryToken) {
private IQueryParameterType toParameterType(RuntimeSearchParam theParam, String theQualifier, String theValueAsQueryToken) {
IQueryParameterType qp = toParameterType(theParam);
qp.setValueAsQueryToken(null, theValueAsQueryToken);
qp.setValueAsQueryToken(theQualifier, theValueAsQueryToken); // aaaa
return qp;
}

View File

@ -37,6 +37,7 @@ import org.springframework.beans.factory.annotation.Qualifier;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt;
@ -55,16 +56,19 @@ import ca.uhn.fhir.util.FhirTerser;
import ca.uhn.fhir.validation.DefaultProfileValidationSupport;
import ca.uhn.fhir.validation.FhirInstanceValidator;
import ca.uhn.fhir.validation.FhirValidator;
import ca.uhn.fhir.validation.IValidationContext;
import ca.uhn.fhir.validation.IValidationSupport;
import ca.uhn.fhir.validation.IValidatorModule;
import ca.uhn.fhir.validation.ValidationResult;
import ca.uhn.fhir.validation.ValidationSupportChain;
import net.sourceforge.cobertura.CoverageIgnore;
public class FhirResourceDaoDstu2<T extends IResource> extends BaseHapiFhirResourceDao<T> {
/**
* TODO: set this to required after the next release
*/
@Autowired(required=false)
@Autowired(required = false)
@Qualifier("myJpaValidationSupportDstu2")
private IValidationSupport myJpaValidationSupport;
@ -122,6 +126,8 @@ public class FhirResourceDaoDstu2<T extends IResource> extends BaseHapiFhirResou
val.setValidationSupport(new ValidationSupportChain(new DefaultProfileValidationSupport(), myJpaValidationSupport));
validator.registerValidatorModule(val);
validator.registerValidatorModule(new IdChecker(theMode));
ValidationResult result;
if (isNotBlank(theRawResource)) {
result = validator.validateWithResult(theRawResource);
@ -139,4 +145,35 @@ public class FhirResourceDaoDstu2<T extends IResource> extends BaseHapiFhirResou
}
private class IdChecker implements IValidatorModule {
private ValidationModeEnum myMode;
public IdChecker(ValidationModeEnum theMode) {
myMode = theMode;
}
@Override
public void validateResource(IValidationContext<IBaseResource> theCtx) {
boolean hasId = theCtx.getResource().getIdElement().hasIdPart();
if (myMode == ValidationModeEnum.CREATE) {
if (hasId) {
throw new InvalidRequestException("Resource has an ID - ID must not be populated for a FHIR create");
}
} else if (myMode == ValidationModeEnum.UPDATE) {
if (hasId == false) {
throw new InvalidRequestException("Resource has no ID - ID must be populated for a FHIR update");
}
}
}
@CoverageIgnore
@Override
public void validateBundle(IValidationContext<Bundle> theContext) {
throw new UnsupportedOperationException();
}
}
}

View File

@ -166,6 +166,8 @@ public class FhirResourceDaoSubscriptionDstu2 extends FhirResourceDaoDstu2<Subsc
mySubscriptionFlaggedResourceDataDao.save(flags);
ourLog.debug("Updating most recent match for subcription {} to {}", subscription.getId().getIdPart(), new InstantDt(mostRecentMatch));
theSubscriptionTable.setMostRecentMatch(mostRecentMatch);
myEntityManager.merge(theSubscriptionTable);
}

View File

@ -366,4 +366,83 @@ public class FhirResourceDaoDstu2SubscriptionTest extends BaseJpaDstu2Test {
}
@Test
public void testSubscriptionResourcesAppear2() throws Exception {
myDaoConfig.setSubscriptionPollDelay(0);
String methodName = "testSubscriptionResourcesAppear2";
Patient p = new Patient();
p.addName().addFamily(methodName);
IIdType pId = myPatientDao.create(p).getId().toUnqualifiedVersionless();
Observation obs = new Observation();
obs.getSubject().setReference(pId);
obs.setStatus(ObservationStatusEnum.FINAL);
myObservationDao.create(obs).getId().toUnqualifiedVersionless();
Subscription subs;
/*
* Create 2 identical subscriptions
*/
subs = new Subscription();
subs.getChannel().setType(SubscriptionChannelTypeEnum.WEBSOCKET);
subs.setCriteria("Observation?subject=Patient/" + pId.getIdPart());
subs.setStatus(SubscriptionStatusEnum.ACTIVE);
Long subsId1 = mySubscriptionDao.getSubscriptionTablePidForSubscriptionResource(mySubscriptionDao.create(subs).getId());
assertNull(mySubscriptionTableDao.findOne(subsId1).getLastClientPoll());
Thread.sleep(100);
ourLog.info("Before: {}", System.currentTimeMillis());
obs = new Observation();
obs.getSubject().setReference(pId);
obs.setStatus(ObservationStatusEnum.FINAL);
IIdType afterId1 = myObservationDao.create(obs).getId().toUnqualifiedVersionless();
obs = new Observation();
obs.getSubject().setReference(pId);
obs.setStatus(ObservationStatusEnum.FINAL);
IIdType afterId2 = myObservationDao.create(obs).getId().toUnqualifiedVersionless();
Thread.sleep(100);
ourLog.info("After: {}", System.currentTimeMillis());
List<IBaseResource> results;
List<IIdType> resultIds;
mySubscriptionDao.pollForNewUndeliveredResources();
assertEquals(2, mySubscriptionFlaggedResourceDataDao.count());
Thread.sleep(100);
mySubscriptionDao.pollForNewUndeliveredResources();
assertEquals(2, mySubscriptionFlaggedResourceDataDao.count());
Thread.sleep(100);
mySubscriptionDao.pollForNewUndeliveredResources();
assertEquals(2, mySubscriptionFlaggedResourceDataDao.count());
Thread.sleep(100);
obs = new Observation();
obs.getSubject().setReference(pId);
obs.setStatus(ObservationStatusEnum.FINAL);
myObservationDao.create(obs).getId().toUnqualifiedVersionless();
mySubscriptionDao.pollForNewUndeliveredResources();
assertEquals(3, mySubscriptionFlaggedResourceDataDao.count());
Thread.sleep(100);
mySubscriptionDao.pollForNewUndeliveredResources();
assertEquals(3, mySubscriptionFlaggedResourceDataDao.count());
}
}

View File

@ -803,6 +803,205 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
assertEquals(id2, gotId);
}
@Test
public void testDeleteWithMatchUrlChainedString() {
String methodName = "testDeleteWithMatchUrlChainedString";
Organization org = new Organization();
org.setName(methodName);
IIdType orgId = myOrganizationDao.create(org).getId().toUnqualifiedVersionless();
Patient p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.getManagingOrganization().setReference(orgId);
IIdType id = myPatientDao.create(p).getId().toUnqualifiedVersionless();
ourLog.info("Created patient, got it: {}", id);
myPatientDao.deleteByUrl("Patient?organization.name=" + methodName);
assertGone(id);
}
@Test
public void testDeleteWithMatchUrlChainedTag() {
String methodName = "testDeleteWithMatchUrlChainedString";
TagList tl = new TagList();
tl.addTag("http://foo", "term");
Organization org = new Organization();
ResourceMetadataKeyEnum.TAG_LIST.put(org, tl);
org.setName(methodName);
IIdType orgId = myOrganizationDao.create(org).getId().toUnqualifiedVersionless();
Patient p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.getManagingOrganization().setReference(orgId);
IIdType id = myPatientDao.create(p).getId().toUnqualifiedVersionless();
ourLog.info("Created patient, got it: {}", id);
myPatientDao.deleteByUrl("Patient?organization._tag=http://foo|term");
assertGone(id);
myOrganizationDao.deleteByUrl("Organization?_tag=http://foo|term");
try {
myOrganizationDao.read(orgId);
fail();
} catch (ResourceGoneException e) {
// good
}
try {
myPatientDao.deleteByUrl("Patient?organization._tag.identifier=http://foo|term");
fail();
} catch (InvalidRequestException e) {
assertEquals("Invalid parameter chain: organization._tag.identifier", e.getMessage());
}
try {
myOrganizationDao.deleteByUrl("Organization?_tag.identifier=http://foo|term");
fail();
} catch (InvalidRequestException e) {
assertEquals("Invalid parameter chain: _tag.identifier", e.getMessage());
}
}
@Test
public void testDeleteWithMatchUrlQualifierMissing() {
String methodName = "testDeleteWithMatchUrlChainedProfile";
/*
* Org 2 has no name
*/
Organization org1 = new Organization();
org1.addIdentifier().setValue(methodName);
IIdType org1Id = myOrganizationDao.create(org1).getId().toUnqualifiedVersionless();
Patient p1 = new Patient();
p1.addIdentifier().setSystem("urn:system").setValue(methodName);
p1.getManagingOrganization().setReference(org1Id);
IIdType patId1 = myPatientDao.create(p1).getId().toUnqualifiedVersionless();
/*
* Org 2 has a name
*/
Organization org2 = new Organization();
org2.setName(methodName);
org2.addIdentifier().setValue(methodName);
IIdType org2Id = myOrganizationDao.create(org2).getId().toUnqualifiedVersionless();
Patient p2 = new Patient();
p2.addIdentifier().setSystem("urn:system").setValue(methodName);
p2.getManagingOrganization().setReference(org2Id);
IIdType patId2 = myPatientDao.create(p2).getId().toUnqualifiedVersionless();
ourLog.info("Pat ID 1 : {}", patId1);
ourLog.info("Org ID 1 : {}", org1Id);
ourLog.info("Pat ID 2 : {}", patId2);
ourLog.info("Org ID 2 : {}", org2Id);
myPatientDao.deleteByUrl("Patient?organization.name:missing=true");
assertGone(patId1);
assertNotGone(patId2);
assertNotGone(org1Id);
assertNotGone(org2Id);
myOrganizationDao.deleteByUrl("Organization?name:missing=true");
assertGone(patId1);
assertNotGone(patId2);
assertGone(org1Id);
assertNotGone(org2Id);
myPatientDao.deleteByUrl("Patient?organization.name:missing=false");
assertGone(patId1);
assertGone(patId2);
assertGone(org1Id);
assertNotGone(org2Id);
myOrganizationDao.deleteByUrl("Organization?name:missing=false");
assertGone(patId1);
assertGone(patId2);
assertGone(org1Id);
assertGone(org2Id);
}
/**
* This gets called from assertGone too! Careful about exceptions...
*/
private void assertNotGone(IIdType theId) {
if ("Patient".equals(theId.getResourceType())) {
myPatientDao.read(theId);
} else if ("Organization".equals(theId.getResourceType())){
myOrganizationDao.read(theId);
} else {
fail("No type");
}
}
private void assertGone(IIdType theId) {
try {
assertNotGone(theId);
fail();
} catch (ResourceGoneException e) {
// good
}
}
@Test
public void testDeleteWithMatchUrlChainedProfile() {
String methodName = "testDeleteWithMatchUrlChainedProfile";
List<IdDt> profileList = new ArrayList<IdDt>();
profileList.add(new IdDt("http://foo"));
Organization org = new Organization();
ResourceMetadataKeyEnum.PROFILES.put(org, profileList);
org.setName(methodName);
IIdType orgId = myOrganizationDao.create(org).getId().toUnqualifiedVersionless();
Patient p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.getManagingOrganization().setReference(orgId);
IIdType id = myPatientDao.create(p).getId().toUnqualifiedVersionless();
ourLog.info("Created patient, got it: {}", id);
myPatientDao.deleteByUrl("Patient?organization._profile=http://foo");
assertGone(id);
myOrganizationDao.deleteByUrl("Organization?_profile=http://foo");
try {
myOrganizationDao.read(orgId);
fail();
} catch (ResourceGoneException e) {
// good
}
try {
myPatientDao.deleteByUrl("Patient?organization._profile.identifier=http://foo");
fail();
} catch (InvalidRequestException e) {
assertEquals("Invalid parameter chain: organization._profile.identifier", e.getMessage());
}
try {
myOrganizationDao.deleteByUrl("Organization?_profile.identifier=http://foo");
fail();
} catch (InvalidRequestException e) {
assertEquals("Invalid parameter chain: _profile.identifier", e.getMessage());
}
}
@Test
public void testDeleteWithMatchUrl() {
String methodName = "testDeleteWithMatchUrl";
@ -840,8 +1039,6 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
}
@Test
public void testHistoryByForcedId() {
IIdType idv1;
@ -1619,6 +1816,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
}
}
@Test
public void testReadWithDeletedResource() {
String methodName = "testReadWithDeletedResource";
@ -1628,12 +1826,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
IIdType id = myPatientDao.create(patient).getId().toVersionless();
myPatientDao.delete(id);
try {
myPatientDao.read(id);
fail();
} catch (ResourceGoneException e) {
// good
}
assertGone(id);
patient.setId(id);
patient.addAddress().addLine("AAA");

View File

@ -24,6 +24,7 @@ import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.ValidationModeEnum;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
@ -127,6 +128,70 @@ public class FhirResourceDaoDstu2ValidateTest extends BaseJpaDstu2Test {
}
@Test
public void testValidateForCreate() {
String methodName = "testValidateForCreate";
Patient pat = new Patient();
pat.setId("Patient/123");
pat.addName().addFamily(methodName);
try {
myPatientDao.validate(pat, null, null, null, ValidationModeEnum.CREATE, null);
fail();
} catch (InvalidRequestException e) {
assertThat(e.getMessage(), containsString("ID must not be populated"));
}
pat.setId("");
myPatientDao.validate(pat, null, null, null, ValidationModeEnum.CREATE, null);
}
@Test
public void testValidateForUpdate() {
String methodName = "testValidateForUpdate";
Patient pat = new Patient();
pat.setId("Patient/123");
pat.addName().addFamily(methodName);
myPatientDao.validate(pat, null, null, null, ValidationModeEnum.UPDATE, null);
pat.setId("");
try {
myPatientDao.validate(pat, null, null, null, ValidationModeEnum.UPDATE, null);
fail();
} catch (InvalidRequestException e) {
assertThat(e.getMessage(), containsString("ID must be populated"));
}
}
@Test
public void testValidateForUpdateWithContained() {
String methodName = "testValidateForUpdate";
Organization org = new Organization();
org.setId("#123");
Patient pat = new Patient();
pat.setId("Patient/123");
pat.addName().addFamily(methodName);
myPatientDao.validate(pat, null, null, null, ValidationModeEnum.UPDATE, null);
pat.setId("");
try {
myPatientDao.validate(pat, null, null, null, ValidationModeEnum.UPDATE, null);
fail();
} catch (InvalidRequestException e) {
assertThat(e.getMessage(), containsString("ID must be populated"));
}
}
@Test
public void testValidateForDelete() {
String methodName = "testValidateForDelete";

View File

@ -60,9 +60,7 @@ import ca.uhn.fhir.model.dstu2.valueset.IssueSeverityEnum;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.model.primitive.UriDt;
import ca.uhn.fhir.rest.api.PreferReturnEnum;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.client.IGenericClient;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.IBundleProvider;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
@ -75,6 +73,13 @@ public class FhirSystemDaoDstu2Test extends BaseJpaDstu2Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirSystemDaoDstu2Test.class);
@Test
public void testTransactionFromBundle6() throws Exception {
InputStream bundleRes = SystemProviderDstu2Test.class.getResourceAsStream("/simone_bundle3.xml");
String bundle = IOUtils.toString(bundleRes);
mySystemDao.transaction(myFhirCtx.newXmlParser().parseResource(Bundle.class, bundle));
}
@Test
public void testRendexing() {
Patient p = new Patient();
@ -292,6 +297,23 @@ public class FhirSystemDaoDstu2Test extends BaseJpaDstu2Test {
}
@Test
public void testEverythingType() {
Bundle request = new Bundle();
request.setType(BundleTypeEnum.SEARCH_RESULTS);
Patient p = new Patient();
request.addEntry().setResource(p).getRequest().setMethod(HTTPVerbEnum.POST);
try {
mySystemDao.transaction(request);
fail();
} catch (InvalidRequestException e) {
assertEquals("Unable to process transaction where incoming Bundle.type = searchset", e.getMessage());
}
}
@Test
public void testTransactionBatchWithFailingRead() {
String methodName = "testTransactionBatchWithFailingRead";

View File

@ -0,0 +1,13 @@
package ca.uhn.fhir.jpa.provider;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu2.resource.Questionnaire;
public class QuestionnaireResourceProviderDstu2 extends JpaResourceProviderDstu1<Questionnaire> {
@Override
public Class<? extends IResource> getResourceType() {
return Questionnaire.class;
}
}

View File

@ -2006,14 +2006,15 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
String inputStr = myFhirCtx.newXmlParser().encodeResourceToString(input);
ourLog.info(inputStr);
HttpPost post = new HttpPost(ourServerBase + "/Patient/$validate");
HttpPost post = new HttpPost(ourServerBase + "/Patient/$validate?_pretty=true");
post.setEntity(new StringEntity(inputStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
CloseableHttpResponse response = ourHttpClient.execute(post);
try {
String resp = IOUtils.toString(response.getEntity().getContent());
ourLog.info(resp);
assertEquals(412, response.getStatusLine().getStatusCode());
assertEquals(200, response.getStatusLine().getStatusCode());
assertThat(resp, not(containsString("Resource has no id")));
} finally {
IOUtils.closeQuietly(response.getEntity().getContent());
response.close();

View File

@ -3,8 +3,14 @@ package ca.uhn.fhir.jpa.provider;
import static org.junit.Assert.*;
import java.io.InputStream;
import java.util.concurrent.TimeUnit;
import org.apache.commons.io.IOUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
@ -17,14 +23,14 @@ import org.springframework.context.support.ClassPathXmlApplicationContext;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.dao.BaseJpaTest;
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.rp.dstu.ObservationResourceProvider;
import ca.uhn.fhir.jpa.rp.dstu.OrganizationResourceProvider;
import ca.uhn.fhir.jpa.rp.dstu.PatientResourceProvider;
import ca.uhn.fhir.jpa.rp.dstu2.ObservationResourceProvider;
import ca.uhn.fhir.jpa.rp.dstu2.OrganizationResourceProvider;
import ca.uhn.fhir.jpa.rp.dstu2.PatientResourceProvider;
import ca.uhn.fhir.jpa.testutil.RandomServerPortProvider;
import ca.uhn.fhir.model.dstu.resource.Observation;
import ca.uhn.fhir.model.dstu.resource.Organization;
import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.model.dstu.resource.Questionnaire;
import ca.uhn.fhir.model.dstu2.resource.Observation;
import ca.uhn.fhir.model.dstu2.resource.Organization;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.model.dstu2.resource.Questionnaire;
import ca.uhn.fhir.model.dstu2.resource.Bundle;
import ca.uhn.fhir.model.dstu2.resource.OperationDefinition;
import ca.uhn.fhir.model.dstu2.resource.OperationOutcome;
@ -40,7 +46,19 @@ public class SystemProviderDstu2Test extends BaseJpaTest {
private static ClassPathXmlApplicationContext ourAppCtx;
private static FhirContext ourCtx;
private static IGenericClient ourClient;
private static String ourServerBase;
private static CloseableHttpClient ourHttpClient;
@Test
public void testEverythingType() throws Exception {
HttpGet get = new HttpGet(ourServerBase + "/Patient/$everything");
CloseableHttpResponse http = ourHttpClient.execute(get);
try {
assertEquals(200, http.getStatusLine().getStatusCode());
} finally {
http.close();
}
}
@Test
public void testTransactionFromBundle4() throws Exception {
@ -71,6 +89,20 @@ public class SystemProviderDstu2Test extends BaseJpaTest {
}
}
@Test
public void testTransactionFromBundle6() throws Exception {
InputStream bundleRes = SystemProviderDstu2Test.class.getResourceAsStream("/simone_bundle3.xml");
String bundle = IOUtils.toString(bundleRes);
ourClient.transaction().withBundle(bundle).prettyPrint().execute();
// try {
// fail();
// } catch (InvalidRequestException e) {
// OperationOutcome oo = (OperationOutcome) e.getOperationOutcome();
// assertEquals("Invalid placeholder ID found: uri:uuid:bb0cd4bc-1839-4606-8c46-ba3069e69b1d - Must be of the form 'urn:uuid:[uuid]' or 'urn:oid:[oid]'", oo.getIssue().get(0).getDiagnostics());
// assertEquals("processing", oo.getIssue().get(0).getCode());
// }
}
@Test
public void testTransactionFromBundle() throws Exception {
InputStream bundleRes = SystemProviderDstu2Test.class.getResourceAsStream("/transaction_link_patient_eve.xml");
@ -82,7 +114,7 @@ public class SystemProviderDstu2Test extends BaseJpaTest {
/**
* This is Gramahe's test transaction - it requires some set up in order to work
*/
// @Test
// @Test
public void testTransactionFromBundle3() throws Exception {
InputStream bundleRes = SystemProviderDstu2Test.class.getResourceAsStream("/grahame-transaction.xml");
@ -150,7 +182,7 @@ public class SystemProviderDstu2Test extends BaseJpaTest {
patientRp.setDao(patientDao);
IFhirResourceDao<Questionnaire> questionnaireDao = (IFhirResourceDao<Questionnaire>) ourAppCtx.getBean("myQuestionnaireDaoDstu2", IFhirResourceDao.class);
QuestionnaireResourceProvider questionnaireRp = new QuestionnaireResourceProvider();
QuestionnaireResourceProviderDstu2 questionnaireRp = new QuestionnaireResourceProviderDstu2();
questionnaireRp.setDao(questionnaireDao);
IFhirResourceDao<Observation> observationDao = (IFhirResourceDao<Observation>) ourAppCtx.getBean("myObservationDaoDstu2", IFhirResourceDao.class);
@ -173,7 +205,7 @@ public class SystemProviderDstu2Test extends BaseJpaTest {
ServletContextHandler proxyHandler = new ServletContextHandler();
proxyHandler.setContextPath("/");
String serverBase = "http://localhost:" + myPort + "/fhir/context";
ourServerBase = "http://localhost:" + myPort + "/fhir/context";
ServletHolder servletHolder = new ServletHolder();
servletHolder.setServlet(restServer);
@ -185,9 +217,13 @@ public class SystemProviderDstu2Test extends BaseJpaTest {
ourServer.setHandler(proxyHandler);
ourServer.start();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
HttpClientBuilder builder = HttpClientBuilder.create();
builder.setConnectionManager(connectionManager);
ourHttpClient = builder.build();
ourCtx.getRestfulClientFactory().setSocketTimeout(600 * 1000);
ourClient = ourCtx.newRestfulGenericClient(serverBase);
ourClient = ourCtx.newRestfulGenericClient(ourServerBase);
ourClient.setLogRequestAndResponse(true);
}

View File

@ -1,13 +1,20 @@
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>DEBUG</level>
</filter>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
</pattern>
</encoder>
</appender>
<logger name="org.springframework.web.socket.handler.ExceptionWebSocketHandlerDecorator" additivity="false" info="debug">
<logger name="org.springframework.web.socket.handler.ExceptionWebSocketHandlerDecorator" additivity="false" level="debug">
<appender-ref ref="STDOUT" />
</logger>
<logger name="ca.uhn.fhir.jpa.dao.FhirResourceDaoSubscriptionDstu2" additivity="false" level="debug">
<appender-ref ref="STDOUT" />
</logger>

View File

@ -0,0 +1,241 @@
<Bundle xmlns="http://hl7.org/fhir">
<id value="20151004163829"/>
<type value="transaction"/>
<entry>
<fullUrl value="urn:uuid:87c8e948-e0d2-4b92-74bd-a824a4769ab4"/>
<resource>
<Patient>
<identifier>
<use value="usual"/>
<type>
<coding>
<system value="http://hl7.org/fhir/identifier-type"/>
<code value="NH"/>
</coding>
</type>
<system value="urn:oid:2.16.840.1.113883.2.1.4.1"/>
<value value="123412312345"/>
<assigner>
<display value="NHS"/>
</assigner>
</identifier>
<identifier>
<use value="usual"/>
<type>
<coding>
<system value="http://hl7.org/fhir/identifier-type"/>
<code value="MR"/>
</coding>
</type>
<system value="http://www.ghh.org/identifiers"/>
<value value="456756756745"/>
<assigner>
<display value="TCPAS"/>
</assigner>
</identifier>
<name>
<use value="official"/>
<family value="Connectathon"/>
<given value="Ten"/>
</name>
<telecom>
<system value="phone"/>
<value value="277543"/>
<use value="home"/>
</telecom>
<gender value="female"/>
<birthDate value="2015-05-24"/>
<address>
<line value="22 Peachtree Road"/>
<city value="Whitstable"/>
<state value="Kent"/>
<country value="CR5 1EL"/>
</address>
<contact>
<relationship>
<coding>
<system value="http://hl7.org/fhir/patient-contact-relationship"/>
<code value="parent"/>
</coding>
</relationship>
<name>
<family value="Connectathon"/>
<given value="Nine"/>
</name>
</contact>
<contact>
<relationship>
<coding>
<system value="http://hl7.org/fhir/patient-contact-relationship"/>
</coding>
</relationship>
<name>
<family value="Connectathon"/>
<given value="Eight"/>
</name>
</contact>
</Patient>
</resource>
<request>
<method value="PUT"/>
<url value="Patient?identifier=http://www.ghh.org/identifiers|456756756745"/>
</request>
</entry>
<entry>
<fullUrl value="urn:uuid:b23ee048-63c1-4373-6194-54adb2f64821"/>
<resource>
<Encounter>
<identifier>
<use value="usual"/>
<type>
<coding>
<system value="http://hl7.org/fhir/identifier-type"/>
<code value="MR"/>
</coding>
</type>
<system value="http://general-hospital.co.uk/Identifiers"/>
<value value="09876876876"/>
<assigner>
<display value="GENHOS"/>
</assigner>
</identifier>
<status value="in-progress"/>
<class value="inpatient"/>
<patient>
<reference value="urn:uuid:87c8e948-e0d2-4b92-74bd-a824a4769ab4"/>
<display value="Connectathon, Ten(*24.05.2015)"/>
</patient>
<period>
<start value="2015-05-02T09:00:00+01:00"/>
</period>
</Encounter>
</resource>
<request>
<method value="PUT"/>
<url value="Encounter?identifier=http://general-hospital.co.uk/Identifiers|09876876876"/>
</request>
</entry>
<entry>
<request>
<method value="DELETE"/>
<url value="AllergyIntolerance?patient.identifier=http://general-hospital.co.uk/Identifiers|123412312345"/>
</request>
</entry>
<entry>
<resource>
<AllergyIntolerance>
<identifier>
<system value="http://health-comm.de/identifiers"/>
<value value="123412312345-DA-1605"/>
<assigner>
<display value="CLOVERLEAF"/>
</assigner>
</identifier>
<patient>
<reference value="urn:uuid:87c8e948-e0d2-4b92-74bd-a824a4769ab4"/>
<display value="Connectathon, Ten(*24.05.2015)"/>
</patient>
<substance>
<coding>
<code value="1605"/>
<display value="L"/>
</coding>
<text value="acetaminophen"/>
</substance>
<category value="medication"/>
<reaction>
<manifestation>
<text value="Muscle Pain"/>
</manifestation>
<severity value="moderate"/>
</reaction>
<reaction>
<manifestation>
<text value="hair loss"/>
</manifestation>
<severity value="moderate"/>
</reaction>
</AllergyIntolerance>
</resource>
<request>
<method value="PUT"/>
<url value="AllergyIntolerance?identifier=http://health-comm.de/identifiers|123412312345-DA-1605"/>
</request>
</entry>
<entry>
<resource>
<AllergyIntolerance>
<identifier>
<system value="http://health-comm.de/identifiers"/>
<value value="123412312345-DA-1558"/>
<assigner>
<display value="CLOVERLEAF"/>
</assigner>
</identifier>
<patient>
<reference value="urn:uuid:87c8e948-e0d2-4b92-74bd-a824a4769ab4"/>
<display value="Connectathon, Ten(*24.05.2015)"/>
</patient>
<substance>
<coding>
<code value="1558"/>
<display value="L"/>
</coding>
<text value="Oxycodone"/>
</substance>
<category value="medication"/>
<reaction>
<manifestation>
<text value="Muscle Pain"/>
</manifestation>
<severity value="moderate"/>
</reaction>
<reaction>
<manifestation>
<text value="hair loss"/>
</manifestation>
<severity value="moderate"/>
</reaction>
</AllergyIntolerance>
</resource>
<request>
<method value="PUT"/>
<url value="AllergyIntolerance?identifier=http://health-comm.de/identifiers|123412312345-DA-1558"/>
</request>
</entry>
<entry>
<resource>
<AllergyIntolerance>
<identifier>
<system value="http://health-comm.de/identifiers"/>
<value value="123412312345-MA-2221"/>
<assigner>
<display value="CLOVERLEAF"/>
</assigner>
</identifier>
<patient>
<reference value="urn:uuid:87c8e948-e0d2-4b92-74bd-a824a4769ab4"/>
<display value="Connectathon, Ten(*24.05.2015)"/>
</patient>
<substance>
<coding>
<code value="2221"/>
<display value="L"/>
</coding>
<text value="Peanuts"/>
</substance>
<category value="environment"/>
<reaction>
<manifestation>
<text value="Anaphylactic Shock "/>
</manifestation>
<severity value="severe"/>
</reaction>
</AllergyIntolerance>
</resource>
<request>
<method value="PUT"/>
<url value="AllergyIntolerance?identifier=http://health-comm.de/identifiers|123412312345-MA-2221"/>
</request>
</entry>
</Bundle>

View File

@ -169,8 +169,6 @@
<configuration>
<webApp>
<contextPath>/hapi-fhir-jpaserver-example</contextPath>
</webApp>
<webAppConfig>
<allowDuplicateFragmentNames>true</allowDuplicateFragmentNames>
</webAppConfig>
</configuration>

View File

@ -1026,6 +1026,14 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
return parts.length > 2 && parts[parts.length - 1].equals("resource") && ((parts.length > 2 && parts[parts.length - 3].equals("entry")) || parts[parts.length - 2].equals("entry"));
}
private boolean isParametersEntry(String path) {
String[] parts = path.split("\\/");
if (path.startsWith("/f:"))
return parts.length == 4 && parts[parts.length-3].equals("f:Parameters") && parts[parts.length-2].startsWith("f:parameter") && parts[parts.length-1].startsWith("f:resource");
else
return parts.length == 4 && parts[parts.length-3].equals("Parameters") && parts[parts.length-2].startsWith("parameter") && parts[parts.length-1].startsWith("resource");
}
private boolean isPrimitiveType(String type) {
return type.equalsIgnoreCase("boolean") || type.equalsIgnoreCase("integer") || type.equalsIgnoreCase("string") || type.equalsIgnoreCase("decimal") || type.equalsIgnoreCase("uri")
|| type.equalsIgnoreCase("base64Binary") || type.equalsIgnoreCase("instant") || type.equalsIgnoreCase("date") || type.equalsIgnoreCase("uuid") || type.equalsIgnoreCase("id")
@ -1790,7 +1798,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if (type.equals("Extension")) {
checkExtension(errors, ei.path, ei.element, ei.definition, profile, localStack);
} else if (type.equals("Resource")) {
validateContains(errors, ei.path, ei.definition, definition, ei.element, localStack, !isBundleEntry(ei.path)); // if
validateContains(errors, ei.path, ei.definition, definition, ei.element, localStack, !isBundleEntry(ei.path) && !isParametersEntry(ei.path)); // if
// (str.matches(".*([.,/])work\\1$"))
} else {
StructureDefinition p = getProfileForType(type);

View File

@ -139,6 +139,13 @@
of the narrative section caused an invalid
re-encoding when encoding to JSON.
</action>
<action type="fix">
Conditional deletes in JPA did not correctly
process if the condition had a chain or a
qualifier, e.g. "Patient?organization.name" or
"Patient.identifier:missing"
</action>
</release>
<release version="1.2" date="2015-09-18">
<action type="add">