diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/MethodUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/MethodUtil.java index d8dc8c5a182..565e148aac2 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/MethodUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/MethodUtil.java @@ -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.> emptyList()); break; + case URI: + binder = new QueryParameterAndBinder(UriAndListParam.class, Collections.> emptyList()); + break; } return binder.parse(theUnqualifiedParamName, theParameters); diff --git a/hapi-fhir-cli/src/main/java/ca/uhn/fhir/cli/ExampleDataUploader.java b/hapi-fhir-cli/src/main/java/ca/uhn/fhir/cli/ExampleDataUploader.java index a5e1515f789..f2a230b6fa0 100644 --- a/hapi-fhir-cli/src/main/java/ca/uhn/fhir/cli/ExampleDataUploader.java +++ b/hapi-fhir-cli/src/main/java/ca/uhn/fhir/cli/ExampleDataUploader.java @@ -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; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java index 6b2878a8b98..90539b37746 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java @@ -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 implements IDao { + /** + * These are parameters which are supported by {@link BaseHapiFhirResourceDao#searchForIds(Map)} + */ + protected static final Map> RESOURCE_META_PARAMS; + /** + * These are parameters which are supported by {@link BaseHapiFhirResourceDao#searchForIds(Map)} + */ + protected static final Map>> RESOURCE_META_AND_PARAMS; + + static { + Map> resourceMetaParams = new HashMap>(); + Map>> resourceMetaAndParams = new HashMap>>(); + 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 implements IDao { return false; } + @CoverageIgnore + protected static IQueryParameterAnd newInstanceAnd(String chain) { + IQueryParameterAnd type; + Class> 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 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 Set processMatchUrl(String theMatchUrl, Class theResourceType) { RuntimeResourceDefinition resourceDef = getContext().getResourceDefinition(theResourceType); @@ -745,13 +801,13 @@ public abstract class BaseHapiFhirDao 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,17 +843,24 @@ public abstract class BaseHapiFhirDao 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); + } + + IQueryParameterAnd param = MethodUtil.parseQueryParams(paramDef, nextParamName, paramList); + paramMap.add(nextParamName, param); } - - 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); - } - - IQueryParameterAnd param = MethodUtil.parseQueryParams(paramDef, nextParamName, paramList); - paramMap.add(nextParamName, param); } return paramMap; } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java index 7b5f7002baf..254dd04a3b9 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java @@ -152,7 +152,7 @@ public abstract class BaseHapiFhirResourceDao extends BaseH @Autowired private DaoConfig myDaoConfig; - + private String myResourceName; private Class myResourceType; private String mySecondaryPrimaryKeyParamName; @@ -656,40 +656,57 @@ public abstract class BaseHapiFhirResourceDao extends BaseH } boolean foundChainMatch = false; + + String chain = ref.getChain(); + String remainingChain = null; + int chainDotIndex = chain.indexOf('.'); + if (chainDotIndex != -1) { + remainingChain = chain.substring(chainDotIndex + 1); + chain = chain.substring(0, chainDotIndex); + } + for (Class nextType : resourceTypes) { RuntimeResourceDefinition typeDef = getContext().getResourceDefinition(nextType); - - String chain = ref.getChain(); - String remainingChain = null; - int chainDotIndex = chain.indexOf('.'); - if (chainDotIndex != -1) { - remainingChain = chain.substring(chainDotIndex + 1); - chain = chain.substring(0, chainDotIndex); - } - - RuntimeSearchParam 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); + 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; + } + } 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 extends BaseH return new HashSet(q.getResultList()); } + private Set addPredicateString(String theParamName, Set thePids, List theList) { if (theList == null || theList.isEmpty()) { return thePids; @@ -783,7 +801,7 @@ public abstract class BaseHapiFhirResourceDao 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 nextAndParams : theList) { @@ -1343,23 +1361,23 @@ public abstract class BaseHapiFhirResourceDao extends BaseH for (Long pid : resource) { ResourceTable entity = myEntityManager.find(ResourceTable.class, pid); - + validateOkToDeleteOrThrowResourceVersionConflictException(entity); - + // Notify interceptors IdDt idToDelete = entity.getIdDt(); ActionRequestDetails requestDetails = new ActionRequestDetails(idToDelete, idToDelete.getResourceType()); notifyInterceptors(RestOperationTypeEnum.DELETE, requestDetails); - + // Perform delete Date updateTime = new Date(); updateEntity(null, entity, true, updateTime, updateTime); notifyWriteCompleted(); - + } - - 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(); } @@ -1718,9 +1736,9 @@ public abstract class BaseHapiFhirResourceDao extends BaseH theMatches.add(next); } } - + pidsToInclude.removeAll(nextRoundOmit); - + addedSomeThisRound = allAdded.addAll(pidsToInclude); nextRoundMatches = pidsToInclude; } while (includes.size() > 0 && nextRoundMatches.size() > 0 && addedSomeThisRound); @@ -2058,7 +2076,7 @@ public abstract class BaseHapiFhirResourceDao extends BaseH CriteriaQuery cq = builder.createQuery(Long.class); Root from = cq.from(ResourceTable.class); cq.select(from.get("myId").as(Long.class)); - + Predicate predicateIds = (from.get("myId").in(loadPids)); Predicate predicateLower = lu.getLowerBoundAsInstant() != null ? builder.greaterThanOrEqualTo(from. get("myUpdated"), lu.getLowerBoundAsInstant()) : null; Predicate predicateUpper = lu.getUpperBoundAsInstant() != null ? builder.lessThanOrEqualTo(from. get("myUpdated"), lu.getUpperBoundAsInstant()) : null; @@ -2114,16 +2132,16 @@ public abstract class BaseHapiFhirResourceDao extends BaseH List pidsSubList = pids.subList(theFromIndex, theToIndex); // Load includes - if (theParams.getEverythingMode()==null) { + if (theParams.getEverythingMode() == null) { pidsSubList = new ArrayList(pidsSubList); revIncludedPids.addAll(loadReverseIncludes(pidsSubList, theParams.getIncludes(), false, null)); } // Execute the query and make sure we return distinct results - List retVal = new ArrayList(); - loadResourcesByPid(pidsSubList, retVal, revIncludedPids, false); + List resources = new ArrayList(); + loadResourcesByPid(pidsSubList, resources, revIncludedPids, false); - return retVal; + return resources; } }); @@ -2140,13 +2158,14 @@ public abstract class BaseHapiFhirResourceDao 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 processSort(final SearchParameterMap theParams, Set loadPids) { + private List processSort(final SearchParameterMap theParams, Set theLoadPids) { final List pids; + Set loadPids = theLoadPids; if (theParams.getSort() != null && isNotBlank(theParams.getSort().getParamName())) { List orders = new ArrayList(); List predicates = new ArrayList(); @@ -2220,7 +2239,7 @@ public abstract class BaseHapiFhirResourceDao extends BaseH for (Entry>> 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 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 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; } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2.java index 6a94380b24b..8dbd991e792 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2.java @@ -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 extends BaseHapiFhirResourceDao { /** * TODO: set this to required after the next release */ - @Autowired(required=false) + @Autowired(required = false) @Qualifier("myJpaValidationSupportDstu2") private IValidationSupport myJpaValidationSupport; @@ -114,21 +118,23 @@ public class FhirResourceDaoDstu2 extends BaseHapiFhirResou } return new MethodOutcome(new IdDt(theId.getValue()), oo); } - + FhirValidator validator = getContext().newValidator(); FhirInstanceValidator val = new FhirInstanceValidator(); val.setBestPracticeWarningLevel(BestPracticeWarningLevel.Warning); val.setValidationSupport(new ValidationSupportChain(new DefaultProfileValidationSupport(), myJpaValidationSupport)); validator.registerValidatorModule(val); - + + validator.registerValidatorModule(new IdChecker(theMode)); + ValidationResult result; if (isNotBlank(theRawResource)) { result = validator.validateWithResult(theRawResource); } else { result = validator.validateWithResult(theResource); } - + if (result.isSuccessful()) { MethodOutcome retVal = new MethodOutcome(); retVal.setOperationOutcome((OperationOutcome) result.toOperationOutcome()); @@ -136,7 +142,38 @@ public class FhirResourceDaoDstu2 extends BaseHapiFhirResou } else { throw new PreconditionFailedException("Validation failed", result.toOperationOutcome()); } - + + } + + private class IdChecker implements IValidatorModule { + + private ValidationModeEnum myMode; + + public IdChecker(ValidationModeEnum theMode) { + myMode = theMode; + } + + @Override + public void validateResource(IValidationContext 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 theContext) { + throw new UnsupportedOperationException(); + } + } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoSubscriptionDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoSubscriptionDstu2.java index 873d6270991..ebed2fae6ca 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoSubscriptionDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoSubscriptionDstu2.java @@ -166,6 +166,8 @@ public class FhirResourceDaoSubscriptionDstu2 extends FhirResourceDaoDstu2 results; + List 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()); + } + + } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2Test.java index 508eac46eb2..fb4a9aa6db7 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2Test.java @@ -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 profileList = new ArrayList(); + 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; @@ -1583,20 +1780,20 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test { @Test public void testReadInvalidVersion() throws Exception { String methodName = "testReadInvalidVersion"; - + Patient pat = new Patient(); pat.addIdentifier().setSystem("urn:system").setValue(methodName); IIdType id = myPatientDao.create(pat).getId(); assertEquals(methodName, myPatientDao.read(id).getIdentifier().get(0).getValue()); - + try { myPatientDao.read(id.withVersion("0")); fail(); } catch (ResourceNotFoundException e) { assertEquals("Version \"0\" is not valid for resource Patient/" + id.getIdPart(), e.getMessage()); } - + try { myPatientDao.read(id.withVersion("2")); fail(); @@ -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"); @@ -1925,7 +2118,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test { Observation o = new Observation(); o.getCode().setText("testSortByComposite"); myObservationDao.create(o); - + SearchParameterMap pm = new SearchParameterMap(); pm.setSort(new SortSpec(Observation.SP_CODE_VALUE_CONCEPT)); try { @@ -2039,13 +2232,13 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test { @Test public void testSortNoMatches() { String methodName = "testSortNoMatches"; - + Patient p = new Patient(); p.addIdentifier().setSystem("urn:system").setValue(methodName); IIdType id1 = myPatientDao.create(p).getId().toUnqualifiedVersionless(); - + SearchParameterMap map; - + map = new SearchParameterMap(); map.add(BaseResource.SP_RES_ID, new StringParam(id1.getIdPart())); map.setLastUpdated(new DateRangeParam("2001", "2003")); @@ -2059,7 +2252,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test { assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(map)), empty()); } - + @Test public void testSortById() { String methodName = "testSortBTyId"; diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2ValidateTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2ValidateTest.java index abac25951e7..61f1511fc8c 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2ValidateTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2ValidateTest.java @@ -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"; diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2Test.java index 79cbf1ade7d..fd2f7cb0c7a 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2Test.java @@ -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"; diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/QuestionnaireResourceProviderDstu2.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/QuestionnaireResourceProviderDstu2.java new file mode 100644 index 00000000000..16670ce7575 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/QuestionnaireResourceProviderDstu2.java @@ -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 { + + @Override + public Class getResourceType() { + return Questionnaire.class; + } + +} diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2Test.java index b787eaf4a5f..51f26a43b79 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2Test.java @@ -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(); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/SystemProviderDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/SystemProviderDstu2Test.java index b29e7591b99..cb6064277f2 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/SystemProviderDstu2Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/SystemProviderDstu2Test.java @@ -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; @@ -33,15 +39,27 @@ import ca.uhn.fhir.rest.client.IGenericClient; import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; -public class SystemProviderDstu2Test extends BaseJpaTest { +public class SystemProviderDstu2Test extends BaseJpaTest { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SystemProviderDstu2Test.class); private static Server ourServer; 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 { InputStream bundleRes = SystemProviderDstu2Test.class.getResourceAsStream("/simone_bundle.xml"); @@ -70,7 +88,21 @@ public class SystemProviderDstu2Test extends BaseJpaTest { assertEquals("processing", oo.getIssue().get(0).getCode()); } } - + + @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"); @@ -78,11 +110,11 @@ public class SystemProviderDstu2Test extends BaseJpaTest { String response = ourClient.transaction().withBundle(bundle).prettyPrint().execute(); ourLog.info(response); } - + /** * 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"); @@ -96,7 +128,7 @@ public class SystemProviderDstu2Test extends BaseJpaTest { OperationDefinition op = ourClient.read(OperationDefinition.class, "get-resource-counts"); assertEquals("$get-resource-counts", op.getCode()); } - + @Test public void testTransactionFromBundle2() throws Exception { @@ -104,7 +136,7 @@ public class SystemProviderDstu2Test extends BaseJpaTest { String bundle = IOUtils.toString(bundleRes); String response = ourClient.transaction().withBundle(bundle).prettyPrint().execute(); ourLog.info(response); - + Bundle resp = ourCtx.newXmlParser().parseResource(Bundle.class, response); IdDt id1_1 = new IdDt(resp.getEntry().get(0).getResponse().getLocation()); assertEquals("Provenance", id1_1.getResourceType()); @@ -115,25 +147,25 @@ public class SystemProviderDstu2Test extends BaseJpaTest { /* * Same bundle! */ - + bundleRes = SystemProviderDstu2Test.class.getResourceAsStream("/transaction_link_patient_eve_temp.xml"); bundle = IOUtils.toString(bundleRes); response = ourClient.transaction().withBundle(bundle).prettyPrint().execute(); ourLog.info(response); - + resp = ourCtx.newXmlParser().parseResource(Bundle.class, response); IdDt id2_1 = new IdDt(resp.getEntry().get(0).getResponse().getLocation()); IdDt id2_2 = new IdDt(resp.getEntry().get(1).getResponse().getLocation()); IdDt id2_3 = new IdDt(resp.getEntry().get(2).getResponse().getLocation()); IdDt id2_4 = new IdDt(resp.getEntry().get(3).getResponse().getLocation()); - + assertNotEquals(id1_1.toVersionless(), id2_1.toVersionless()); assertEquals("Provenance", id2_1.getResourceType()); assertEquals(id1_2.toVersionless(), id2_2.toVersionless()); assertEquals(id1_3.toVersionless(), id2_3.toVersionless()); assertEquals(id1_4.toVersionless(), id2_4.toVersionless()); } - + @AfterClass public static void afterClass() throws Exception { ourServer.stop(); @@ -150,7 +182,7 @@ public class SystemProviderDstu2Test extends BaseJpaTest { patientRp.setDao(patientDao); IFhirResourceDao questionnaireDao = (IFhirResourceDao) ourAppCtx.getBean("myQuestionnaireDaoDstu2", IFhirResourceDao.class); - QuestionnaireResourceProvider questionnaireRp = new QuestionnaireResourceProvider(); + QuestionnaireResourceProviderDstu2 questionnaireRp = new QuestionnaireResourceProviderDstu2(); questionnaireRp.setDao(questionnaireDao); IFhirResourceDao observationDao = (IFhirResourceDao) 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); @@ -181,14 +213,18 @@ public class SystemProviderDstu2Test extends BaseJpaTest { ourCtx = FhirContext.forDstu2(); restServer.setFhirContext(ourCtx); - + 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); } - + } diff --git a/hapi-fhir-jpaserver-base/src/test/resources/logback-test.xml b/hapi-fhir-jpaserver-base/src/test/resources/logback-test.xml index de7ef2a9e83..6a499d71d13 100644 --- a/hapi-fhir-jpaserver-base/src/test/resources/logback-test.xml +++ b/hapi-fhir-jpaserver-base/src/test/resources/logback-test.xml @@ -1,13 +1,20 @@ + + DEBUG + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n - + + + + + diff --git a/hapi-fhir-jpaserver-base/src/test/resources/simone_bundle3.xml b/hapi-fhir-jpaserver-base/src/test/resources/simone_bundle3.xml new file mode 100644 index 00000000000..2d9149fe6cc --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/resources/simone_bundle3.xml @@ -0,0 +1,241 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
\ No newline at end of file diff --git a/hapi-fhir-jpaserver-example/pom.xml b/hapi-fhir-jpaserver-example/pom.xml index d5c5f51bd19..7464ad651c6 100644 --- a/hapi-fhir-jpaserver-example/pom.xml +++ b/hapi-fhir-jpaserver-example/pom.xml @@ -169,8 +169,6 @@ /hapi-fhir-jpaserver-example - - true diff --git a/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/validation/InstanceValidator.java b/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/validation/InstanceValidator.java index a18bad817f4..319edb2a11d 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/validation/InstanceValidator.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/instance/validation/InstanceValidator.java @@ -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); diff --git a/src/changes/changes.xml b/src/changes/changes.xml index cf22e63c884..a979cd06914 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -139,6 +139,13 @@ of the narrative section caused an invalid re-encoding when encoding to JSON. + + 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" + +