Properly support chains in JPA conditional URLs
This commit is contained in:
parent
5e2d94d745
commit
43aad1eb98
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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>
|
|
@ -169,8 +169,6 @@
|
|||
<configuration>
|
||||
<webApp>
|
||||
<contextPath>/hapi-fhir-jpaserver-example</contextPath>
|
||||
</webApp>
|
||||
<webAppConfig>
|
||||
<allowDuplicateFragmentNames>true</allowDuplicateFragmentNames>
|
||||
</webAppConfig>
|
||||
</configuration>
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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">
|
||||
|
|
Loading…
Reference in New Issue