Fix #212 - Dont accept invalid IDs but do accept IDs starting with a

number
This commit is contained in:
James Agnew 2015-08-28 15:36:56 -04:00
parent 4032a2674d
commit bd13b53099
14 changed files with 818 additions and 627 deletions

View File

@ -199,6 +199,34 @@ public class IdDt extends UriDt implements IPrimitiveDatatype<String>, IIdType {
return getIdPartAsBigDecimal(); return getIdPartAsBigDecimal();
} }
private String determineLocalPrefix(String theValue) {
if (theValue == null || theValue.isEmpty()) {
return null;
}
if (theValue.startsWith("#")) {
return "#";
}
int lastPrefix = -1;
for (int i = 0; i < theValue.length(); i++) {
char nextChar = theValue.charAt(i);
if (nextChar == ':') {
lastPrefix = i;
} else if (!Character.isLetter(nextChar) || !Character.isLowerCase(nextChar)) {
break;
}
}
if (lastPrefix != -1) {
String candidate = theValue.substring(0, lastPrefix + 1);
if (candidate.startsWith("cid:") || candidate.startsWith("urn:")) {
return candidate;
} else {
return null;
}
} else {
return null;
}
}
@Override @Override
public boolean equals(Object theArg0) { public boolean equals(Object theArg0) {
if (!(theArg0 instanceof IdDt)) { if (!(theArg0 instanceof IdDt)) {
@ -383,6 +411,34 @@ public class IdDt extends UriDt implements IPrimitiveDatatype<String>, IIdType {
return isBlank(getValue()); return isBlank(getValue());
} }
@Override
public boolean isIdPartValid() {
String id = getIdPart();
if (StringUtils.isBlank(id)) {
return false;
}
if (id.length() > 64) {
return false;
}
for (int i = 0; i < id.length(); i++) {
char nextChar = id.charAt(i);
if (nextChar >= 'a' && nextChar <= 'z') {
continue;
}
if (nextChar >= 'A' && nextChar <= 'Z') {
continue;
}
if (nextChar >= '0' && nextChar <= '9') {
continue;
}
if (nextChar == '-' || nextChar == '.') {
continue;
}
return false;
}
return true;
}
/** /**
* Returns <code>true</code> if the unqualified ID is a valid {@link Long} value (in other words, it consists only of digits) * Returns <code>true</code> if the unqualified ID is a valid {@link Long} value (in other words, it consists only of digits)
*/ */
@ -407,7 +463,7 @@ public class IdDt extends UriDt implements IPrimitiveDatatype<String>, IIdType {
public boolean isLocal() { public boolean isLocal() {
return "#".equals(myBaseUrl); return "#".equals(myBaseUrl);
} }
/** /**
* Copies the value from the given IdDt to <code>this</code> IdDt. It is generally not neccesary to use this method but it is provided for consistency with the rest of the API. * Copies the value from the given IdDt to <code>this</code> IdDt. It is generally not neccesary to use this method but it is provided for consistency with the rest of the API.
*/ */
@ -416,34 +472,6 @@ public class IdDt extends UriDt implements IPrimitiveDatatype<String>, IIdType {
setValue(theId.getValue()); setValue(theId.getValue());
} }
private String determineLocalPrefix(String theValue) {
if (theValue == null || theValue.isEmpty()) {
return null;
}
if (theValue.startsWith("#")) {
return "#";
}
int lastPrefix = -1;
for (int i = 0; i < theValue.length(); i++) {
char nextChar = theValue.charAt(i);
if (nextChar == ':') {
lastPrefix = i;
} else if (!Character.isLetter(nextChar) || !Character.isLowerCase(nextChar)) {
break;
}
}
if (lastPrefix != -1) {
String candidate = theValue.substring(0, lastPrefix + 1);
if (candidate.startsWith("cid:") || candidate.startsWith("urn:")) {
return candidate;
} else {
return null;
}
} else {
return null;
}
}
/** /**
* Set the value * Set the value
* *
@ -602,6 +630,14 @@ public class IdDt extends UriDt implements IPrimitiveDatatype<String>, IIdType {
return new IdDt(value + '/' + Constants.PARAM_HISTORY + '/' + theVersion); return new IdDt(value + '/' + Constants.PARAM_HISTORY + '/' + theVersion);
} }
/**
* Construct a new ID with with form "urn:uuid:[UUID]" where [UUID] is a new, randomly
* created UUID generated by {@link UUID#randomUUID()}
*/
public static IdDt newRandomUuid() {
return new IdDt("urn:uuid:" + UUID.randomUUID().toString());
}
/** /**
* Retrieves the ID from the given resource instance * Retrieves the ID from the given resource instance
*/ */
@ -634,12 +670,4 @@ public class IdDt extends UriDt implements IPrimitiveDatatype<String>, IIdType {
return theIdPart.toString(); return theIdPart.toString();
} }
/**
* Construct a new ID with with form "urn:uuid:[UUID]" where [UUID] is a new, randomly
* created UUID generated by {@link UUID#randomUUID()}
*/
public static IdDt newRandomUuid() {
return new IdDt("urn:uuid:" + UUID.randomUUID().toString());
}
} }

View File

@ -42,20 +42,22 @@ public class TokenParam extends BaseParam implements IQueryParameterType {
} }
/** /**
* Constructor which copies the {@link InternalCodingDt#getSystemElement() system} and {@link InternalCodingDt#getCodeElement() code} from a {@link InternalCodingDt} instance and adds it as a parameter * Constructor which copies the {@link InternalCodingDt#getSystemElement() system} and {@link InternalCodingDt#getCodeElement() code} from a {@link InternalCodingDt} instance and adds it as a
* parameter
* *
* @param theCodingDt * @param theCodingDt
* The coding * The coding
*/ */
public TokenParam(BaseCodingDt theCodingDt) { public TokenParam(BaseCodingDt theCodingDt) {
this(toSystemValue(theCodingDt.getSystemElement()), theCodingDt.getCodeElement().getValue()); this(toSystemValue(theCodingDt.getSystemElement()), theCodingDt.getCodeElement().getValue());
} }
/** /**
* Constructor which copies the {@link BaseIdentifierDt#getSystemElement() system} and {@link BaseIdentifierDt#getValueElement() value} from a {@link BaseIdentifierDt} instance and adds it as a parameter * Constructor which copies the {@link BaseIdentifierDt#getSystemElement() system} and {@link BaseIdentifierDt#getValueElement() value} from a {@link BaseIdentifierDt} instance and adds it as a
* parameter
* *
* @param theIdentifierDt * @param theIdentifierDt
* The identifier * The identifier
*/ */
public TokenParam(BaseIdentifierDt theIdentifierDt) { public TokenParam(BaseIdentifierDt theIdentifierDt) {
this(toSystemValue(theIdentifierDt.getSystemElement()), theIdentifierDt.getValueElement().getValue()); this(toSystemValue(theIdentifierDt.getSystemElement()), theIdentifierDt.getValueElement().getValue());
@ -102,12 +104,18 @@ public class TokenParam extends BaseParam implements IQueryParameterType {
*/ */
@Override @Override
void doSetValueAsQueryToken(String theQualifier, String theParameter) { void doSetValueAsQueryToken(String theQualifier, String theParameter) {
int barIndex = ParameterUtil.nonEscapedIndexOf(theParameter, '|'); setText(Constants.PARAMQUALIFIER_TOKEN_TEXT.equals(theQualifier));
if (barIndex != -1) { setSystem(null);
setSystem(theParameter.substring(0, barIndex)); if (theParameter == null) {
setValue(ParameterUtil.unescape(theParameter.substring(barIndex + 1))); setValue(null);
} else { } else {
setValue(ParameterUtil.unescape(theParameter)); int barIndex = ParameterUtil.nonEscapedIndexOf(theParameter, '|');
if (barIndex != -1) {
setSystem(theParameter.substring(0, barIndex));
setValue(ParameterUtil.unescape(theParameter.substring(barIndex + 1)));
} else {
setValue(ParameterUtil.unescape(theParameter));
}
} }
} }

View File

@ -92,6 +92,19 @@ public interface IIdType {
*/ */
boolean isAbsolute(); boolean isAbsolute();
/**
* Returns <code>true</code> if the {@link #getIdPart() ID part of this object} is valid according to the FHIR rules for valid IDs.
* <p>
* The FHIR specification states:
* <code>Any combination of upper or lower case ASCII letters ('A'..'Z', and 'a'..'z', numerals ('0'..'9'), '-' and '.', with a length limit of 64 characters. (This might be an integer, an un-prefixed OID, UUID or any other identifier pattern that meets these constraints.) regex: [A-Za-z0-9\-\.]{1,64}</code>
* </p>
*/
boolean isIdPartValid();
/**
* Returns <code>true</code> if the {@link #getIdPart() ID part of this object} contains
* only numbers
*/
boolean isIdPartValidLong(); boolean isIdPartValidLong();
Long getIdPartAsLong(); Long getIdPartAsLong();

View File

@ -52,7 +52,8 @@ ca.uhn.fhir.jpa.dao.BaseHapiFhirSystemDao.transactionMissingUrl=Unable to perfor
ca.uhn.fhir.jpa.dao.BaseHapiFhirSystemDao.transactionInvalidUrl=Unable to perform {0}, URL provided is invalid: {1} ca.uhn.fhir.jpa.dao.BaseHapiFhirSystemDao.transactionInvalidUrl=Unable to perform {0}, URL provided is invalid: {1}
ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.duplicateCreateForcedId=Can not create entity with ID[{0}], a resource with this ID already exists ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.duplicateCreateForcedId=Can not create entity with ID[{0}], a resource with this ID already exists
ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.failedToCreateWithClientAssignedNumericId=Can not create resource with ID[{0}], no resource with this ID exists and clients may only assign IDs which begin with a non-numeric character on this server ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.failedToCreateWithInvalidId=Can not process entity with ID[{0}], this is not a valid FHIR ID
ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.failedToCreateWithClientAssignedNumericId=Can not create resource with ID[{0}], no resource with this ID exists and clients may only assign IDs which contain at least one non-numeric character
ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.failedToCreateWithClientAssignedId=Can not create resource with ID[{0}], ID must not be supplied on a create (POST) operation ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.failedToCreateWithClientAssignedId=Can not create resource with ID[{0}], ID must not be supplied on a create (POST) operation
ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.invalidParameterChain=Invalid parameter chain: {0} ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.invalidParameterChain=Invalid parameter chain: {0}
ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.multipleParamsWithSameNameOneIsMissingTrue=This server does not know how to handle multiple "{0}" parameters where one has a value of :missing=true ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.multipleParamsWithSameNameOneIsMissingTrue=This server does not know how to handle multiple "{0}" parameters where one has a value of :missing=true

View File

@ -18,7 +18,6 @@
<groupId>ca.uhn.hapi.example</groupId> <groupId>ca.uhn.hapi.example</groupId>
<artifactId>hapi-fhir-examples-uploader</artifactId> <artifactId>hapi-fhir-examples-uploader</artifactId>
<version>1.1-SNAPSHOT</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>HAPI FHIR - Examples Uploader</name> <name>HAPI FHIR - Examples Uploader</name>
@ -36,17 +35,19 @@
<artifactId>hapi-fhir-structures-dstu2</artifactId> <artifactId>hapi-fhir-structures-dstu2</artifactId>
<version>1.1-SNAPSHOT</version> <version>1.1-SNAPSHOT</version>
</dependency> </dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-hl7org-dstu2</artifactId>
<version>1.1-SNAPSHOT</version>
</dependency>
<dependency> <dependency>
<groupId>ch.qos.logback</groupId> <groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId> <artifactId>logback-classic</artifactId>
<version>1.1.2</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>javax.servlet</groupId> <groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId> <artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency> </dependency>

View File

@ -2,7 +2,6 @@ package ca.uhn.fhir.exampleuploader;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@ -10,7 +9,6 @@ import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream; import java.util.zip.ZipInputStream;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.CloseableHttpClient;
@ -113,10 +111,10 @@ public class Uploader {
for (Entry next : bundle.getEntry()) { for (Entry next : bundle.getEntry()) {
List<ResourceReferenceInfo> refs = ctx.newTerser().getAllResourceReferences(next.getResource()); List<ResourceReferenceInfo> refs = ctx.newTerser().getAllResourceReferences(next.getResource());
for (ResourceReferenceInfo nextRef : refs) { for (ResourceReferenceInfo nextRef : refs) {
// if (nextRef.getResourceReference().getReferenceElement().isAbsolute()) { // if (nextRef.getResourceReference().getReferenceElement().isAbsolute()) {
// ourLog.info("Discarding absolute reference: {}", nextRef.getResourceReference().getReferenceElement().getValue()); // ourLog.info("Discarding absolute reference: {}", nextRef.getResourceReference().getReferenceElement().getValue());
// nextRef.getResourceReference().getReferenceElement().setValue(null); // nextRef.getResourceReference().getReferenceElement().setValue(null);
// } // }
nextRef.getResourceReference().getReferenceElement().setValue(nextRef.getResourceReference().getReferenceElement().toUnqualifiedVersionless().getValue()); nextRef.getResourceReference().getReferenceElement().setValue(nextRef.getResourceReference().getReferenceElement().toUnqualifiedVersionless().getValue());
String value = nextRef.getResourceReference().getReferenceElement().toUnqualifiedVersionless().getValue(); String value = nextRef.getResourceReference().getReferenceElement().toUnqualifiedVersionless().getValue();
if (!ids.contains(value) && !nextRef.getResourceReference().getReferenceElement().isLocal()) { if (!ids.contains(value) && !nextRef.getResourceReference().getReferenceElement().isLocal()) {
@ -127,13 +125,13 @@ public class Uploader {
} }
} }
} }
// for (Entry next : bundle.getEntry()) { // for (Entry next : bundle.getEntry()) {
// if (next.getResource().getId().hasIdPart() && Character.isLetter(next.getResource().getId().getIdPart().charAt(0))) { // if (next.getResource().getId().hasIdPart() && Character.isLetter(next.getResource().getId().getIdPart().charAt(0))) {
// next.getTransaction().setUrl(next.getResource().getResourceName() + '/' + next.getResource().getId().getIdPart()); // next.getTransaction().setUrl(next.getResource().getResourceName() + '/' + next.getResource().getId().getIdPart());
// next.getTransaction().setMethod(HTTPVerbEnum.PUT); // next.getTransaction().setMethod(HTTPVerbEnum.PUT);
// } // }
// } // }
ourLog.info("{} good references", goodRefs); ourLog.info("{} good references", goodRefs);

View File

@ -1696,13 +1696,17 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
} }
/** /**
* May be implemented by subclasses to validate resources prior to storage * May be overridden by subclasses to validate resources prior to storage
* *
* @param theResource * @param theResource
* The resource that is about to be stored * The resource that is about to be stored
*/ */
protected void preProcessResourceForStorage(T theResource) { protected void preProcessResourceForStorage(T theResource) {
// nothing by default if (theResource.getId().hasIdPart()) {
if (!theResource.getId().isIdPartValid()) {
throw new InvalidRequestException(getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "failedToCreateWithInvalidId", theResource.getId().getIdPart()));
}
}
} }
@Override @Override
@ -2296,7 +2300,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
try { try {
entity = readEntityLatestVersion(resourceId); entity = readEntityLatestVersion(resourceId);
} catch (ResourceNotFoundException e) { } catch (ResourceNotFoundException e) {
if (Character.isDigit(theResource.getId().getIdPart().charAt(0))) { if (resourceId.isIdPartValidLong()) {
throw new InvalidRequestException(getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "failedToCreateWithClientAssignedNumericId", theResource.getId().getIdPart())); throw new InvalidRequestException(getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "failedToCreateWithClientAssignedNumericId", theResource.getId().getIdPart()));
} }
return doCreate(theResource, null, thePerformIndexing); return doCreate(theResource, null, thePerformIndexing);

View File

@ -32,6 +32,7 @@ import javax.measure.quantity.Quantity;
import javax.measure.unit.NonSI; import javax.measure.unit.NonSI;
import javax.measure.unit.Unit; import javax.measure.unit.Unit;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.ConfigurationException;
@ -80,8 +81,7 @@ public class SearchParamExtractorDstu2 extends BaseSearchParamExtractor implemen
/* /*
* (non-Javadoc) * (non-Javadoc)
* *
* @see ca.uhn.fhir.jpa.dao.ISearchParamExtractor#extractSearchParamDates(ca.uhn.fhir.jpa.entity.ResourceTable, * @see ca.uhn.fhir.jpa.dao.ISearchParamExtractor#extractSearchParamDates(ca.uhn.fhir.jpa.entity.ResourceTable, ca.uhn.fhir.model.api.IResource)
* ca.uhn.fhir.model.api.IResource)
*/ */
@Override @Override
public List<ResourceIndexedSearchParamDate> extractSearchParamDates(ResourceTable theEntity, IResource theResource) { public List<ResourceIndexedSearchParamDate> extractSearchParamDates(ResourceTable theEntity, IResource theResource) {
@ -143,8 +143,7 @@ public class SearchParamExtractorDstu2 extends BaseSearchParamExtractor implemen
/* /*
* (non-Javadoc) * (non-Javadoc)
* *
* @see ca.uhn.fhir.jpa.dao.ISearchParamExtractor#extractSearchParamNumber(ca.uhn.fhir.jpa.entity.ResourceTable, * @see ca.uhn.fhir.jpa.dao.ISearchParamExtractor#extractSearchParamNumber(ca.uhn.fhir.jpa.entity.ResourceTable, ca.uhn.fhir.model.api.IResource)
* ca.uhn.fhir.model.api.IResource)
*/ */
@Override @Override
public ArrayList<ResourceIndexedSearchParamNumber> extractSearchParamNumber(ResourceTable theEntity, IResource theResource) { public ArrayList<ResourceIndexedSearchParamNumber> extractSearchParamNumber(ResourceTable theEntity, IResource theResource) {
@ -191,18 +190,12 @@ public class SearchParamExtractorDstu2 extends BaseSearchParamExtractor implemen
nextValue = newValue; nextValue = newValue;
/* /*
* @SuppressWarnings("unchecked") PhysicsUnit<? extends * @SuppressWarnings("unchecked") PhysicsUnit<? extends org.unitsofmeasurement.quantity.Quantity<?>> unit = (PhysicsUnit<? extends org.unitsofmeasurement.quantity.Quantity<?>>)
* org.unitsofmeasurement.quantity.Quantity<?>> unit = (PhysicsUnit<? extends * UCUMFormat.getCaseInsensitiveInstance().parse(nextValue.getCode().getValue(), null); if (unit.isCompatible(UCUM.DAY)) {
* org.unitsofmeasurement.quantity.Quantity<?>>)
* UCUMFormat.getCaseInsensitiveInstance().parse(nextValue.getCode().getValue(), null); if
* (unit.isCompatible(UCUM.DAY)) {
* *
* @SuppressWarnings("unchecked") PhysicsUnit<org.unitsofmeasurement.quantity.Time> timeUnit * @SuppressWarnings("unchecked") PhysicsUnit<org.unitsofmeasurement.quantity.Time> timeUnit = (PhysicsUnit<Time>) unit; UnitConverter conv = timeUnit.getConverterTo(UCUM.DAY);
* = (PhysicsUnit<Time>) unit; UnitConverter conv = timeUnit.getConverterTo(UCUM.DAY); * double dayValue = conv.convert(nextValue.getValue().getValue().doubleValue()); DurationDt newValue = new DurationDt(); newValue.setSystem(UCUM_NS);
* double dayValue = conv.convert(nextValue.getValue().getValue().doubleValue()); DurationDt * newValue.setCode(UCUM.DAY.getSymbol()); newValue.setValue(dayValue); nextValue=newValue; }
* newValue = new DurationDt(); newValue.setSystem(UCUM_NS);
* newValue.setCode(UCUM.DAY.getSymbol()); newValue.setValue(dayValue); nextValue=newValue;
* }
*/ */
} }
} }
@ -246,8 +239,7 @@ public class SearchParamExtractorDstu2 extends BaseSearchParamExtractor implemen
/* /*
* (non-Javadoc) * (non-Javadoc)
* *
* @see ca.uhn.fhir.jpa.dao.ISearchParamExtractor#extractSearchParamQuantity(ca.uhn.fhir.jpa.entity.ResourceTable, * @see ca.uhn.fhir.jpa.dao.ISearchParamExtractor#extractSearchParamQuantity(ca.uhn.fhir.jpa.entity.ResourceTable, ca.uhn.fhir.model.api.IResource)
* ca.uhn.fhir.model.api.IResource)
*/ */
@Override @Override
public List<ResourceIndexedSearchParamQuantity> extractSearchParamQuantity(ResourceTable theEntity, IResource theResource) { public List<ResourceIndexedSearchParamQuantity> extractSearchParamQuantity(ResourceTable theEntity, IResource theResource) {
@ -281,7 +273,8 @@ public class SearchParamExtractorDstu2 extends BaseSearchParamExtractor implemen
continue; continue;
} }
ResourceIndexedSearchParamQuantity nextEntity = new ResourceIndexedSearchParamQuantity(resourceName, nextValue.getValueElement().getValue(), nextValue.getSystemElement().getValueAsString(), nextValue.getCode()); ResourceIndexedSearchParamQuantity nextEntity = new ResourceIndexedSearchParamQuantity(resourceName, nextValue.getValueElement().getValue(),
nextValue.getSystemElement().getValueAsString(), nextValue.getCode());
nextEntity.setResource(theEntity); nextEntity.setResource(theEntity);
retVal.add(nextEntity); retVal.add(nextEntity);
} else { } else {
@ -302,8 +295,7 @@ public class SearchParamExtractorDstu2 extends BaseSearchParamExtractor implemen
/* /*
* (non-Javadoc) * (non-Javadoc)
* *
* @see ca.uhn.fhir.jpa.dao.ISearchParamExtractor#extractSearchParamStrings(ca.uhn.fhir.jpa.entity.ResourceTable, * @see ca.uhn.fhir.jpa.dao.ISearchParamExtractor#extractSearchParamStrings(ca.uhn.fhir.jpa.entity.ResourceTable, ca.uhn.fhir.model.api.IResource)
* ca.uhn.fhir.model.api.IResource)
*/ */
@Override @Override
public List<ResourceIndexedSearchParamString> extractSearchParamStrings(ResourceTable theEntity, IResource theResource) { public List<ResourceIndexedSearchParamString> extractSearchParamStrings(ResourceTable theEntity, IResource theResource) {
@ -317,11 +309,11 @@ public class SearchParamExtractorDstu2 extends BaseSearchParamExtractor implemen
String nextPath = nextSpDef.getPath(); String nextPath = nextSpDef.getPath();
String resourceName = nextSpDef.getName(); String resourceName = nextSpDef.getName();
if (isBlank(nextPath)) { if (isBlank(nextPath)) {
// TODO: implement phonetic, and any others that have no path // TODO: implement phonetic, and any others that have no path
if ("Questionnaire".equals(def.getName()) && nextSpDef.getName().equals("title")) { if ("Questionnaire".equals(def.getName()) && nextSpDef.getName().equals("title")) {
Questionnaire q = (Questionnaire) theResource; Questionnaire q = (Questionnaire) theResource;
String title = q.getGroup().getTitle(); String title = q.getGroup().getTitle();
@ -399,8 +391,7 @@ public class SearchParamExtractorDstu2 extends BaseSearchParamExtractor implemen
/* /*
* (non-Javadoc) * (non-Javadoc)
* *
* @see ca.uhn.fhir.jpa.dao.ISearchParamExtractor#extractSearchParamTokens(ca.uhn.fhir.jpa.entity.ResourceTable, * @see ca.uhn.fhir.jpa.dao.ISearchParamExtractor#extractSearchParamTokens(ca.uhn.fhir.jpa.entity.ResourceTable, ca.uhn.fhir.model.api.IResource)
* ca.uhn.fhir.model.api.IResource)
*/ */
@Override @Override
public List<BaseResourceIndexedSearchParam> extractSearchParamTokens(ResourceTable theEntity, IResource theResource) { public List<BaseResourceIndexedSearchParam> extractSearchParamTokens(ResourceTable theEntity, IResource theResource) {
@ -411,7 +402,7 @@ public class SearchParamExtractorDstu2 extends BaseSearchParamExtractor implemen
ValueSet vs = (ValueSet) theResource; ValueSet vs = (ValueSet) theResource;
useSystem = vs.getCodeSystem().getSystem(); useSystem = vs.getCodeSystem().getSystem();
} }
RuntimeResourceDefinition def = getContext().getResourceDefinition(theResource); RuntimeResourceDefinition def = getContext().getResourceDefinition(theResource);
for (RuntimeSearchParam nextSpDef : def.getSearchParams()) { for (RuntimeSearchParam nextSpDef : def.getSearchParams()) {
if (nextSpDef.getParamType() != RestSearchParameterTypeEnum.TOKEN) { if (nextSpDef.getParamType() != RestSearchParameterTypeEnum.TOKEN) {
@ -442,32 +433,41 @@ public class SearchParamExtractorDstu2 extends BaseSearchParamExtractor implemen
} }
for (Object nextObject : extractValues(nextPath, theResource)) { for (Object nextObject : extractValues(nextPath, theResource)) {
// Patient:language // Patient:language
if (nextObject instanceof Patient.Communication) { if (nextObject instanceof Patient.Communication) {
Communication nextValue = (Patient.Communication) nextObject; Communication nextValue = (Patient.Communication) nextObject;
nextObject= nextValue.getLanguage(); nextObject = nextValue.getLanguage();
} }
if (nextObject instanceof IdentifierDt) { if (nextObject instanceof IdentifierDt) {
IdentifierDt nextValue = (IdentifierDt) nextObject; IdentifierDt nextValue = (IdentifierDt) nextObject;
if (nextValue.isEmpty()) { if (nextValue.isEmpty()) {
continue; continue;
} }
systems.add(nextValue.getSystemElement().getValueAsString()); String system = StringUtils.defaultIfBlank(nextValue.getSystemElement().getValueAsString(), null);
codes.add(nextValue.getValueElement().getValue()); String value = nextValue.getValueElement().getValue();
if (isNotBlank(value)) {
systems.add(system);
codes.add(value);
}
if (isNotBlank(nextValue.getType().getText())) {
addStringParam(theEntity, retVal, nextSpDef, nextValue.getType().getText());
}
} else if (nextObject instanceof ContactPointDt) { } else if (nextObject instanceof ContactPointDt) {
ContactPointDt nextValue = (ContactPointDt) nextObject; ContactPointDt nextValue = (ContactPointDt) nextObject;
if (nextValue.isEmpty()) { if (nextValue.isEmpty()) {
continue;
}
if (isNotBlank(needContactPointSystem)) {
if (!needContactPointSystem.equals(nextValue.getSystemElement().getValueAsString())) {
continue; continue;
} }
if (isNotBlank(needContactPointSystem)) { }
if (!needContactPointSystem.equals(nextValue.getSystemElement().getValueAsString())) { systems.add(nextValue.getSystemElement().getValueAsString());
continue; codes.add(nextValue.getValueElement().getValue());
}
}
systems.add(nextValue.getSystemElement().getValueAsString());
codes.add(nextValue.getValueElement().getValue());
} else if (nextObject instanceof IPrimitiveDatatype<?>) { } else if (nextObject instanceof IPrimitiveDatatype<?>) {
IPrimitiveDatatype<?> nextValue = (IPrimitiveDatatype<?>) nextObject; IPrimitiveDatatype<?> nextValue = (IPrimitiveDatatype<?>) nextObject;
if (nextValue.isEmpty()) { if (nextValue.isEmpty()) {
@ -481,38 +481,19 @@ public class SearchParamExtractorDstu2 extends BaseSearchParamExtractor implemen
codes.add(nextValue.getValueAsString()); codes.add(nextValue.getValueAsString());
} else if (nextObject instanceof CodingDt) { } else if (nextObject instanceof CodingDt) {
CodingDt nextValue = (CodingDt) nextObject; CodingDt nextValue = (CodingDt) nextObject;
if (nextValue.isEmpty()) { extractTokensFromCoding(systems, codes, theEntity, retVal, nextSpDef, nextValue);
continue;
}
String nextSystem = nextValue.getSystemElement().getValueAsString();
String nextCode = nextValue.getCodeElement().getValue();
if (isNotBlank(nextSystem) || isNotBlank(nextCode)) {
systems.add(nextSystem);
codes.add(nextCode);
}
if (!nextValue.getDisplayElement().isEmpty()) {
systems.add(null);
codes.add(nextValue.getDisplayElement().getValue());
}
} else if (nextObject instanceof CodeableConceptDt) { } else if (nextObject instanceof CodeableConceptDt) {
CodeableConceptDt nextCC = (CodeableConceptDt) nextObject; CodeableConceptDt nextCC = (CodeableConceptDt) nextObject;
if (!nextCC.getTextElement().isEmpty()) { if (!nextCC.getTextElement().isEmpty()) {
String value = nextCC.getTextElement().getValue(); addStringParam(theEntity, retVal, nextSpDef, nextCC.getTextElement().getValue());
if (value.length() > ResourceIndexedSearchParamString.MAX_LENGTH) {
value = value.substring(0, ResourceIndexedSearchParamString.MAX_LENGTH);
}
ResourceIndexedSearchParamString nextEntity = new ResourceIndexedSearchParamString(nextSpDef.getName(), BaseHapiFhirDao.normalizeString(value), value);
nextEntity.setResource(theEntity);
retVal.add(nextEntity);
} }
extractTokensFromCodeableConcept(systems, codes, nextCC); extractTokensFromCodeableConcept(systems, codes, nextCC, theEntity, retVal, nextSpDef);
} else if (nextObject instanceof RestSecurity) { } else if (nextObject instanceof RestSecurity) {
// Conformance.security search param points to something kind of useless right now - This should probably be fixed. // Conformance.security search param points to something kind of useless right now - This should probably be fixed.
RestSecurity sec = (RestSecurity)nextObject; RestSecurity sec = (RestSecurity) nextObject;
for (BoundCodeableConceptDt<RestfulSecurityServiceEnum> nextCC : sec.getService()) { for (BoundCodeableConceptDt<RestfulSecurityServiceEnum> nextCC : sec.getService()) {
extractTokensFromCodeableConcept(systems, codes, nextCC); extractTokensFromCodeableConcept(systems, codes, nextCC, theEntity, retVal, nextSpDef);
} }
} else { } else {
if (!multiType) { if (!multiType) {
@ -560,22 +541,35 @@ public class SearchParamExtractorDstu2 extends BaseSearchParamExtractor implemen
return retVal; return retVal;
} }
private void extractTokensFromCodeableConcept(List<String> systems, List<String> codes, CodeableConceptDt nextCC) { private void addStringParam(ResourceTable theEntity, ArrayList<BaseResourceIndexedSearchParam> retVal, RuntimeSearchParam nextSpDef, String value) {
for (CodingDt nextCoding : nextCC.getCoding()) { if (value.length() > ResourceIndexedSearchParamString.MAX_LENGTH) {
if (nextCoding.isEmpty()) { value = value.substring(0, ResourceIndexedSearchParamString.MAX_LENGTH);
continue; }
} ResourceIndexedSearchParamString nextEntity = new ResourceIndexedSearchParamString(nextSpDef.getName(), BaseHapiFhirDao.normalizeString(value), value);
nextEntity.setResource(theEntity);
retVal.add(nextEntity);
}
private void extractTokensFromCodeableConcept(List<String> theSystems, List<String> theCodes, CodeableConceptDt theCodeableConcept, ResourceTable theEntity,
ArrayList<BaseResourceIndexedSearchParam> theListToPopulate, RuntimeSearchParam theParameterDef) {
for (CodingDt nextCoding : theCodeableConcept.getCoding()) {
extractTokensFromCoding(theSystems, theCodes, theEntity, theListToPopulate, theParameterDef, nextCoding);
}
}
private void extractTokensFromCoding(List<String> theSystems, List<String> theCodes, ResourceTable theEntity, ArrayList<BaseResourceIndexedSearchParam> theListToPopulate,
RuntimeSearchParam theParameterDef, CodingDt nextCoding) {
if (nextCoding != null && !nextCoding.isEmpty()) {
String nextSystem = nextCoding.getSystemElement().getValueAsString(); String nextSystem = nextCoding.getSystemElement().getValueAsString();
String nextCode = nextCoding.getCodeElement().getValue(); String nextCode = nextCoding.getCodeElement().getValue();
if (isNotBlank(nextSystem) || isNotBlank(nextCode)) { if (isNotBlank(nextSystem) || isNotBlank(nextCode)) {
systems.add(nextSystem); theSystems.add(nextSystem);
codes.add(nextCode); theCodes.add(nextCode);
} }
if (!nextCoding.getDisplayElement().isEmpty()) { if (!nextCoding.getDisplayElement().isEmpty()) {
systems.add(null); addStringParam(theEntity, theListToPopulate, theParameterDef, nextCoding.getDisplayElement().getValue());
codes.add(nextCoding.getDisplayElement().getValue());
} }
} }

View File

@ -72,7 +72,7 @@ public class FhirResourceDaoDstu1Test extends BaseJpaTest {
ourPatientDao.create(p); ourPatientDao.create(p);
fail(); fail();
} catch (InvalidRequestException e) { } catch (InvalidRequestException e) {
assertThat(e.getMessage(), containsString("clients may only assign IDs which begin with a non-numeric")); assertThat(e.getMessage(), containsString("clients may only assign IDs which contain at least one non-numeric"));
} }
} }
@ -117,7 +117,7 @@ public class FhirResourceDaoDstu1Test extends BaseJpaTest {
ourPatientDao.update(p1); ourPatientDao.update(p1);
fail(); fail();
} catch (InvalidRequestException e) { } catch (InvalidRequestException e) {
assertThat(e.getMessage(), containsString("clients may only assign IDs which begin with a non-numeric")); assertThat(e.getMessage(), containsString("clients may only assign IDs which contain at least one non-numeric"));
} }
} }

View File

@ -103,17 +103,22 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
private static IFhirResourceDao<DiagnosticReport> ourDiagnosticReportDao; private static IFhirResourceDao<DiagnosticReport> ourDiagnosticReportDao;
private static IFhirResourceDao<Encounter> ourEncounterDao; private static IFhirResourceDao<Encounter> ourEncounterDao;
private static FhirContext ourFhirCtx; private static FhirContext ourFhirCtx;
private static IServerInterceptor ourInterceptor;
private static IFhirResourceDao<Location> ourLocationDao; private static IFhirResourceDao<Location> ourLocationDao;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoDstu2Test.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoDstu2Test.class);
private static IFhirResourceDao<Observation> ourObservationDao; private static IFhirResourceDao<Observation> ourObservationDao;
private static IFhirResourceDao<Organization> ourOrganizationDao; private static IFhirResourceDao<Organization> ourOrganizationDao;
private static IFhirResourceDao<Patient> ourPatientDao; private static IFhirResourceDao<Patient> ourPatientDao;
private static IFhirResourceDao<Practitioner> ourPractitionerDao;
private static IFhirResourceDao<Questionnaire> ourQuestionnaireDao;
@SuppressWarnings("unused") @SuppressWarnings("unused")
private static IFhirResourceDao<QuestionnaireResponse> ourQuestionnaireResponseDao; private static IFhirResourceDao<QuestionnaireResponse> ourQuestionnaireResponseDao;
private static IFhirResourceDao<Questionnaire> ourQuestionnaireDao;
private static IFhirSystemDao<Bundle> ourSystemDao; private static IFhirSystemDao<Bundle> ourSystemDao;
private static IFhirResourceDao<Practitioner> ourPractitionerDao;
private static IServerInterceptor ourInterceptor; @Before
public void before() {
reset(ourInterceptor);
}
private List<String> extractNames(IBundleProvider theSearch) { private List<String> extractNames(IBundleProvider theSearch) {
ArrayList<String> retVal = new ArrayList<String>(); ArrayList<String> retVal = new ArrayList<String>();
@ -124,62 +129,6 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
return retVal; return retVal;
} }
@Test
public void testCreateOperationOutcome() {
/*
* If any of this ever fails, it means that one of the OperationOutcome issue severity codes has changed code
* value across versions. We store the string as a constant, so something will need to be fixed.
*/
assertEquals(org.hl7.fhir.instance.model.OperationOutcome.IssueSeverity.ERROR.toCode(), BaseHapiFhirResourceDao.OO_SEVERITY_ERROR);
assertEquals(ca.uhn.fhir.model.dstu.valueset.IssueSeverityEnum.ERROR.getCode(), BaseHapiFhirResourceDao.OO_SEVERITY_ERROR);
assertEquals(ca.uhn.fhir.model.dstu2.valueset.IssueSeverityEnum.ERROR.getCode(), BaseHapiFhirResourceDao.OO_SEVERITY_ERROR);
assertEquals(org.hl7.fhir.instance.model.OperationOutcome.IssueSeverity.INFORMATION.toCode(), BaseHapiFhirResourceDao.OO_SEVERITY_INFO);
assertEquals(ca.uhn.fhir.model.dstu.valueset.IssueSeverityEnum.INFORMATION.getCode(), BaseHapiFhirResourceDao.OO_SEVERITY_INFO);
assertEquals(ca.uhn.fhir.model.dstu2.valueset.IssueSeverityEnum.INFORMATION.getCode(), BaseHapiFhirResourceDao.OO_SEVERITY_INFO);
assertEquals(org.hl7.fhir.instance.model.OperationOutcome.IssueSeverity.WARNING.toCode(), BaseHapiFhirResourceDao.OO_SEVERITY_WARN);
assertEquals(ca.uhn.fhir.model.dstu.valueset.IssueSeverityEnum.WARNING.getCode(), BaseHapiFhirResourceDao.OO_SEVERITY_WARN);
assertEquals(ca.uhn.fhir.model.dstu2.valueset.IssueSeverityEnum.WARNING.getCode(), BaseHapiFhirResourceDao.OO_SEVERITY_WARN);
}
@Test
public void testRead() {
Observation o1 = new Observation();
o1.getCode().addCoding().setSystem("foo").setCode("testRead");
IIdType id1 = ourObservationDao.create(o1).getId();
/*
* READ
*/
reset(ourInterceptor);
Observation obs = ourObservationDao.read(id1.toUnqualifiedVersionless());
assertEquals(o1.getCode().getCoding().get(0).getCode(), obs.getCode().getCoding().get(0).getCode());
// Verify interceptor
ArgumentCaptor<ActionRequestDetails> detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
verify(ourInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.READ), detailsCapt.capture());
ActionRequestDetails details = detailsCapt.getValue();
assertEquals(id1.toUnqualifiedVersionless().getValue(), details.getId().toUnqualifiedVersionless().getValue());
assertEquals("Observation", details.getResourceType());
/*
* VREAD
*/
assertTrue(id1.hasVersionIdPart()); // just to make sure..
reset(ourInterceptor);
obs = ourObservationDao.read(id1);
assertEquals(o1.getCode().getCoding().get(0).getCode(), obs.getCode().getCoding().get(0).getCode());
// Verify interceptor
detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
verify(ourInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.VREAD), detailsCapt.capture());
details = detailsCapt.getValue();
assertEquals(id1.toUnqualified().getValue(), details.getId().toUnqualified().getValue());
assertEquals("Observation", details.getResourceType());
}
@Test @Test
public void testChoiceParamConcept() { public void testChoiceParamConcept() {
Observation o1 = new Observation(); Observation o1 = new Observation();
@ -193,7 +142,8 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
assertEquals(id1, found.getResources(0, 1).get(0).getIdElement()); assertEquals(id1, found.getResources(0, 1).get(0).getIdElement());
} }
} }
@Test @Test
public void testChoiceParamDate() { public void testChoiceParamDate() {
Observation o2 = new Observation(); Observation o2 = new Observation();
@ -269,17 +219,20 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
} }
@Test @Test
public void testCreateNumericIdFails() { public void testCreateOperationOutcome() {
Patient p = new Patient(); /*
p.addIdentifier().setSystem("urn:system").setValue("testCreateNumericIdFails"); * If any of this ever fails, it means that one of the OperationOutcome issue severity codes has changed code
p.addName().addFamily("Hello"); * value across versions. We store the string as a constant, so something will need to be fixed.
p.setId("Patient/123"); */
try { assertEquals(org.hl7.fhir.instance.model.OperationOutcome.IssueSeverity.ERROR.toCode(), BaseHapiFhirResourceDao.OO_SEVERITY_ERROR);
ourPatientDao.create(p); assertEquals(ca.uhn.fhir.model.dstu.valueset.IssueSeverityEnum.ERROR.getCode(), BaseHapiFhirResourceDao.OO_SEVERITY_ERROR);
fail(); assertEquals(ca.uhn.fhir.model.dstu2.valueset.IssueSeverityEnum.ERROR.getCode(), BaseHapiFhirResourceDao.OO_SEVERITY_ERROR);
} catch (InvalidRequestException e) { assertEquals(org.hl7.fhir.instance.model.OperationOutcome.IssueSeverity.INFORMATION.toCode(), BaseHapiFhirResourceDao.OO_SEVERITY_INFO);
assertThat(e.getMessage(), containsString("Can not create resource with ID[123], ID must not be supplied")); assertEquals(ca.uhn.fhir.model.dstu.valueset.IssueSeverityEnum.INFORMATION.getCode(), BaseHapiFhirResourceDao.OO_SEVERITY_INFO);
} assertEquals(ca.uhn.fhir.model.dstu2.valueset.IssueSeverityEnum.INFORMATION.getCode(), BaseHapiFhirResourceDao.OO_SEVERITY_INFO);
assertEquals(org.hl7.fhir.instance.model.OperationOutcome.IssueSeverity.WARNING.toCode(), BaseHapiFhirResourceDao.OO_SEVERITY_WARN);
assertEquals(ca.uhn.fhir.model.dstu.valueset.IssueSeverityEnum.WARNING.getCode(), BaseHapiFhirResourceDao.OO_SEVERITY_WARN);
assertEquals(ca.uhn.fhir.model.dstu2.valueset.IssueSeverityEnum.WARNING.getCode(), BaseHapiFhirResourceDao.OO_SEVERITY_WARN);
} }
@Test @Test
@ -298,20 +251,6 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
assertEquals("my message", oo.getIssue().get(0).getDiagnostics()); assertEquals("my message", oo.getIssue().get(0).getDiagnostics());
} }
@Test
public void testCreateTextIdFails() {
Patient p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue("testCreateTextIdFails");
p.addName().addFamily("Hello");
p.setId("Patient/ABC");
try {
ourPatientDao.create(p);
fail();
} catch (InvalidRequestException e) {
assertThat(e.getMessage(), containsString("Can not create resource with ID[ABC], ID must not be supplied"));
}
}
@Test @Test
public void testCreateSummaryFails() { public void testCreateSummaryFails() {
@ -331,6 +270,34 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
} }
} }
@Test
public void testCreateTextIdFails() {
Patient p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue("testCreateTextIdFails");
p.addName().addFamily("Hello");
p.setId("Patient/ABC");
try {
ourPatientDao.create(p);
fail();
} catch (InvalidRequestException e) {
assertThat(e.getMessage(), containsString("Can not create resource with ID[ABC], ID must not be supplied"));
}
}
@Test
public void testCreateWithIdFails() {
Patient p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue("testCreateNumericIdFails");
p.addName().addFamily("Hello");
p.setId("Patient/abc");
try {
ourPatientDao.create(p);
fail();
} catch (InvalidRequestException e) {
assertThat(e.getMessage(), containsString("Can not create resource with ID[abc], ID must not be supplied"));
}
}
@Test @Test
public void testCreateWithIfNoneExistBasic() { public void testCreateWithIfNoneExistBasic() {
String methodName = "testCreateWithIfNoneExistBasic"; String methodName = "testCreateWithIfNoneExistBasic";
@ -383,6 +350,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
} }
@Test @Test
public void testCreateWithInvalidReferenceFailsGracefully() { public void testCreateWithInvalidReferenceFailsGracefully() {
Patient patient = new Patient(); Patient patient = new Patient();
@ -1028,6 +996,44 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
} }
@Test
public void testRead() {
Observation o1 = new Observation();
o1.getCode().addCoding().setSystem("foo").setCode("testRead");
IIdType id1 = ourObservationDao.create(o1).getId();
/*
* READ
*/
reset(ourInterceptor);
Observation obs = ourObservationDao.read(id1.toUnqualifiedVersionless());
assertEquals(o1.getCode().getCoding().get(0).getCode(), obs.getCode().getCoding().get(0).getCode());
// Verify interceptor
ArgumentCaptor<ActionRequestDetails> detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
verify(ourInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.READ), detailsCapt.capture());
ActionRequestDetails details = detailsCapt.getValue();
assertEquals(id1.toUnqualifiedVersionless().getValue(), details.getId().toUnqualifiedVersionless().getValue());
assertEquals("Observation", details.getResourceType());
/*
* VREAD
*/
assertTrue(id1.hasVersionIdPart()); // just to make sure..
reset(ourInterceptor);
obs = ourObservationDao.read(id1);
assertEquals(o1.getCode().getCoding().get(0).getCode(), obs.getCode().getCoding().get(0).getCode());
// Verify interceptor
detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
verify(ourInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.VREAD), detailsCapt.capture());
details = detailsCapt.getValue();
assertEquals(id1.toUnqualified().getValue(), details.getId().toUnqualified().getValue());
assertEquals("Observation", details.getResourceType());
}
@Test @Test
public void testReadForcedIdVersionHistory() throws InterruptedException { public void testReadForcedIdVersionHistory() throws InterruptedException {
Patient p1 = new Patient(); Patient p1 = new Patient();
@ -1544,55 +1550,6 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
} }
} }
@Test
public void testSearchPractitionerPhoneAndEmailParam() {
String methodName = "testSearchPractitionerPhoneAndEmailParam";
IIdType id1;
{
Practitioner patient = new Practitioner();
patient.getName().addFamily(methodName);
patient.addTelecom().setSystem(ContactPointSystemEnum.PHONE).setValue("123");
id1 = ourPractitionerDao.create(patient).getId().toUnqualifiedVersionless();
}
IIdType id2;
{
Practitioner patient = new Practitioner();
patient.getName().addFamily(methodName);
patient.addTelecom().setSystem(ContactPointSystemEnum.EMAIL).setValue("abc");
id2 = ourPractitionerDao.create(patient).getId().toUnqualifiedVersionless();
}
Map<String, IQueryParameterType> params;
List<IIdType> patients;
params = new HashMap<String, IQueryParameterType>();
params.put(Practitioner.SP_FAMILY, new StringDt(methodName));
patients = toUnqualifiedVersionlessIds(ourPractitionerDao.search(params));
assertEquals(2, patients.size());
assertThat(patients, containsInAnyOrder(id1, id2));
params = new HashMap<String, IQueryParameterType>();
params.put(Practitioner.SP_FAMILY, new StringParam(methodName));
params.put(Practitioner.SP_EMAIL, new TokenParam(null, "abc"));
patients = toUnqualifiedVersionlessIds(ourPractitionerDao.search(params));
assertEquals(1, patients.size());
assertThat(patients, containsInAnyOrder(id2));
params = new HashMap<String, IQueryParameterType>();
params.put(Practitioner.SP_FAMILY, new StringParam(methodName));
params.put(Practitioner.SP_EMAIL, new TokenParam(null, "123"));
patients = toUnqualifiedVersionlessIds(ourPractitionerDao.search(params));
assertEquals(0, patients.size());
params = new HashMap<String, IQueryParameterType>();
params.put(Practitioner.SP_FAMILY, new StringParam(methodName));
params.put(Practitioner.SP_PHONE, new TokenParam(null, "123"));
patients = toUnqualifiedVersionlessIds(ourPractitionerDao.search(params));
assertEquals(1, patients.size());
assertThat(patients, containsInAnyOrder(id1));
}
@Test @Test
public void testSearchNameParam() { public void testSearchNameParam() {
IIdType id1; IIdType id1;
@ -1669,6 +1626,55 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
} }
} }
@Test
public void testSearchPractitionerPhoneAndEmailParam() {
String methodName = "testSearchPractitionerPhoneAndEmailParam";
IIdType id1;
{
Practitioner patient = new Practitioner();
patient.getName().addFamily(methodName);
patient.addTelecom().setSystem(ContactPointSystemEnum.PHONE).setValue("123");
id1 = ourPractitionerDao.create(patient).getId().toUnqualifiedVersionless();
}
IIdType id2;
{
Practitioner patient = new Practitioner();
patient.getName().addFamily(methodName);
patient.addTelecom().setSystem(ContactPointSystemEnum.EMAIL).setValue("abc");
id2 = ourPractitionerDao.create(patient).getId().toUnqualifiedVersionless();
}
Map<String, IQueryParameterType> params;
List<IIdType> patients;
params = new HashMap<String, IQueryParameterType>();
params.put(Practitioner.SP_FAMILY, new StringDt(methodName));
patients = toUnqualifiedVersionlessIds(ourPractitionerDao.search(params));
assertEquals(2, patients.size());
assertThat(patients, containsInAnyOrder(id1, id2));
params = new HashMap<String, IQueryParameterType>();
params.put(Practitioner.SP_FAMILY, new StringParam(methodName));
params.put(Practitioner.SP_EMAIL, new TokenParam(null, "abc"));
patients = toUnqualifiedVersionlessIds(ourPractitionerDao.search(params));
assertEquals(1, patients.size());
assertThat(patients, containsInAnyOrder(id2));
params = new HashMap<String, IQueryParameterType>();
params.put(Practitioner.SP_FAMILY, new StringParam(methodName));
params.put(Practitioner.SP_EMAIL, new TokenParam(null, "123"));
patients = toUnqualifiedVersionlessIds(ourPractitionerDao.search(params));
assertEquals(0, patients.size());
params = new HashMap<String, IQueryParameterType>();
params.put(Practitioner.SP_FAMILY, new StringParam(methodName));
params.put(Practitioner.SP_PHONE, new TokenParam(null, "123"));
patients = toUnqualifiedVersionlessIds(ourPractitionerDao.search(params));
assertEquals(1, patients.size());
assertThat(patients, containsInAnyOrder(id1));
}
@Test @Test
public void testSearchResourceLinkWithChain() { public void testSearchResourceLinkWithChain() {
Patient patient = new Patient(); Patient patient = new Patient();
@ -1977,10 +1983,23 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
assertEquals(0, ourPatientDao.search(map).size()); assertEquals(0, ourPatientDao.search(map).size());
} }
{ {
// Complete match
SearchParameterMap map = new SearchParameterMap(); SearchParameterMap map = new SearchParameterMap();
map.add(Patient.SP_LANGUAGE, new TokenParam(null, "testSearchTokenParamComText", true)); map.add(Patient.SP_LANGUAGE, new TokenParam(null, "testSearchTokenParamComText", true));
assertEquals(1, ourPatientDao.search(map).size()); assertEquals(1, ourPatientDao.search(map).size());
} }
{
// Left match
SearchParameterMap map = new SearchParameterMap();
map.add(Patient.SP_LANGUAGE, new TokenParam(null, "testSearchTokenParamcomtex", true));
assertEquals(1, ourPatientDao.search(map).size());
}
{
// Right match
SearchParameterMap map = new SearchParameterMap();
map.add(Patient.SP_LANGUAGE, new TokenParam(null, "testSearchTokenParamComTex", true));
assertEquals(1, ourPatientDao.search(map).size());
}
{ {
SearchParameterMap map = new SearchParameterMap(); SearchParameterMap map = new SearchParameterMap();
TokenOrListParam listParam = new TokenOrListParam(); TokenOrListParam listParam = new TokenOrListParam();
@ -2036,117 +2055,6 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
} }
@Test
public void testSearchWithTagParameter() {
String methodName = "testSearchWithTagParameter";
IIdType tag1id;
{
Organization org = new Organization();
org.getNameElement().setValue("FOO");
TagList tagList = new TagList();
tagList.addTag("urn:taglist", methodName + "1a");
tagList.addTag("urn:taglist", methodName + "1b");
ResourceMetadataKeyEnum.TAG_LIST.put(org, tagList);
tag1id = ourOrganizationDao.create(org).getId().toUnqualifiedVersionless();
}
IIdType tag2id;
{
Organization org = new Organization();
org.getNameElement().setValue("FOO");
TagList tagList = new TagList();
tagList.addTag("urn:taglist", methodName + "2a");
tagList.addTag("urn:taglist", methodName + "2b");
ResourceMetadataKeyEnum.TAG_LIST.put(org, tagList);
tag2id = ourOrganizationDao.create(org).getId().toUnqualifiedVersionless();
}
{
// One tag
SearchParameterMap params = new SearchParameterMap();
params.add("_tag", new TokenParam("urn:taglist", methodName + "1a"));
List<IIdType> patients = toUnqualifiedVersionlessIds(ourOrganizationDao.search(params));
assertThat(patients, containsInAnyOrder(tag1id));
}
{
// Code only
SearchParameterMap params = new SearchParameterMap();
params.add("_tag", new TokenParam(null, methodName + "1a"));
List<IIdType> patients = toUnqualifiedVersionlessIds(ourOrganizationDao.search(params));
assertThat(patients, containsInAnyOrder(tag1id));
}
{
// Or tags
SearchParameterMap params = new SearchParameterMap();
TokenOrListParam orListParam = new TokenOrListParam();
orListParam.add(new TokenParam("urn:taglist", methodName + "1a"));
orListParam.add(new TokenParam("urn:taglist", methodName + "2a"));
params.add("_tag", orListParam);
List<IIdType> patients = toUnqualifiedVersionlessIds(ourOrganizationDao.search(params));
assertThat(patients, containsInAnyOrder(tag1id, tag2id));
}
// TODO: get multiple/AND working
{
// And tags
SearchParameterMap params = new SearchParameterMap();
TokenAndListParam andListParam = new TokenAndListParam();
andListParam.addValue(new TokenOrListParam("urn:taglist", methodName + "1a"));
andListParam.addValue(new TokenOrListParam("urn:taglist", methodName + "2a"));
params.add("_tag", andListParam);
List<IIdType> patients = toUnqualifiedVersionlessIds(ourOrganizationDao.search(params));
assertEquals(0, patients.size());
}
{
// And tags
SearchParameterMap params = new SearchParameterMap();
TokenAndListParam andListParam = new TokenAndListParam();
andListParam.addValue(new TokenOrListParam("urn:taglist", methodName + "1a"));
andListParam.addValue(new TokenOrListParam("urn:taglist", methodName + "1b"));
params.add("_tag", andListParam);
List<IIdType> patients = toUnqualifiedVersionlessIds(ourOrganizationDao.search(params));
assertThat(patients, containsInAnyOrder(tag1id));
}
}
@Test
public void testSearchWithSecurityAndProfileParams() {
String methodName = "testSearchWithSecurityAndProfileParams";
IIdType tag1id;
{
Organization org = new Organization();
org.getNameElement().setValue("FOO");
List<BaseCodingDt> security = new ArrayList<BaseCodingDt>();
security.add(new CodingDt("urn:taglist", methodName + "1a"));
ResourceMetadataKeyEnum.SECURITY_LABELS.put(org, security);
tag1id = ourOrganizationDao.create(org).getId().toUnqualifiedVersionless();
}
IIdType tag2id;
{
Organization org = new Organization();
org.getNameElement().setValue("FOO");
List<IdDt> security = new ArrayList<IdDt>();
security.add(new IdDt("http://" + methodName));
ResourceMetadataKeyEnum.PROFILES.put(org, security);
tag2id = ourOrganizationDao.create(org).getId().toUnqualifiedVersionless();
}
{
SearchParameterMap params = new SearchParameterMap();
params.add("_security", new TokenParam("urn:taglist", methodName + "1a"));
List<IIdType> patients = toUnqualifiedVersionlessIds(ourOrganizationDao.search(params));
assertThat(patients, containsInAnyOrder(tag1id));
}
{
SearchParameterMap params = new SearchParameterMap();
params.add("_profile", new UriParam("http://" + methodName));
List<IIdType> patients = toUnqualifiedVersionlessIds(ourOrganizationDao.search(params));
assertThat(patients, containsInAnyOrder(tag2id));
}
}
@Test @Test
public void testSearchWithIncludes() { public void testSearchWithIncludes() {
IIdType parentOrgId; IIdType parentOrgId;
@ -2236,10 +2144,10 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
public void testSearchWithIncludesThatHaveTextId() { public void testSearchWithIncludesThatHaveTextId() {
{ {
Organization org = new Organization(); Organization org = new Organization();
org.setId("testSearchWithIncludesThatHaveTextId_id1"); org.setId("testSearchWithIncludesThatHaveTextIdid1");
org.getNameElement().setValue("testSearchWithIncludesThatHaveTextId_O1"); org.getNameElement().setValue("testSearchWithIncludesThatHaveTextId_O1");
IIdType orgId = ourOrganizationDao.update(org).getId(); IIdType orgId = ourOrganizationDao.update(org).getId();
assertThat(orgId.getValue(), endsWith("Organization/testSearchWithIncludesThatHaveTextId_id1/_history/1")); assertThat(orgId.getValue(), endsWith("Organization/testSearchWithIncludesThatHaveTextIdid1/_history/1"));
Patient patient = new Patient(); Patient patient = new Patient();
patient.addIdentifier().setSystem("urn:system").setValue("001"); patient.addIdentifier().setSystem("urn:system").setValue("001");
@ -2309,6 +2217,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
} }
} }
@Test @Test
public void testSearchWithMissingQuantity() { public void testSearchWithMissingQuantity() {
IIdType notMissing; IIdType notMissing;
@ -2449,6 +2358,116 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
} }
@Test
public void testSearchWithSecurityAndProfileParams() {
String methodName = "testSearchWithSecurityAndProfileParams";
IIdType tag1id;
{
Organization org = new Organization();
org.getNameElement().setValue("FOO");
List<BaseCodingDt> security = new ArrayList<BaseCodingDt>();
security.add(new CodingDt("urn:taglist", methodName + "1a"));
ResourceMetadataKeyEnum.SECURITY_LABELS.put(org, security);
tag1id = ourOrganizationDao.create(org).getId().toUnqualifiedVersionless();
}
IIdType tag2id;
{
Organization org = new Organization();
org.getNameElement().setValue("FOO");
List<IdDt> security = new ArrayList<IdDt>();
security.add(new IdDt("http://" + methodName));
ResourceMetadataKeyEnum.PROFILES.put(org, security);
tag2id = ourOrganizationDao.create(org).getId().toUnqualifiedVersionless();
}
{
SearchParameterMap params = new SearchParameterMap();
params.add("_security", new TokenParam("urn:taglist", methodName + "1a"));
List<IIdType> patients = toUnqualifiedVersionlessIds(ourOrganizationDao.search(params));
assertThat(patients, containsInAnyOrder(tag1id));
}
{
SearchParameterMap params = new SearchParameterMap();
params.add("_profile", new UriParam("http://" + methodName));
List<IIdType> patients = toUnqualifiedVersionlessIds(ourOrganizationDao.search(params));
assertThat(patients, containsInAnyOrder(tag2id));
}
}
@Test
public void testSearchWithTagParameter() {
String methodName = "testSearchWithTagParameter";
IIdType tag1id;
{
Organization org = new Organization();
org.getNameElement().setValue("FOO");
TagList tagList = new TagList();
tagList.addTag("urn:taglist", methodName + "1a");
tagList.addTag("urn:taglist", methodName + "1b");
ResourceMetadataKeyEnum.TAG_LIST.put(org, tagList);
tag1id = ourOrganizationDao.create(org).getId().toUnqualifiedVersionless();
}
IIdType tag2id;
{
Organization org = new Organization();
org.getNameElement().setValue("FOO");
TagList tagList = new TagList();
tagList.addTag("urn:taglist", methodName + "2a");
tagList.addTag("urn:taglist", methodName + "2b");
ResourceMetadataKeyEnum.TAG_LIST.put(org, tagList);
tag2id = ourOrganizationDao.create(org).getId().toUnqualifiedVersionless();
}
{
// One tag
SearchParameterMap params = new SearchParameterMap();
params.add("_tag", new TokenParam("urn:taglist", methodName + "1a"));
List<IIdType> patients = toUnqualifiedVersionlessIds(ourOrganizationDao.search(params));
assertThat(patients, containsInAnyOrder(tag1id));
}
{
// Code only
SearchParameterMap params = new SearchParameterMap();
params.add("_tag", new TokenParam(null, methodName + "1a"));
List<IIdType> patients = toUnqualifiedVersionlessIds(ourOrganizationDao.search(params));
assertThat(patients, containsInAnyOrder(tag1id));
}
{
// Or tags
SearchParameterMap params = new SearchParameterMap();
TokenOrListParam orListParam = new TokenOrListParam();
orListParam.add(new TokenParam("urn:taglist", methodName + "1a"));
orListParam.add(new TokenParam("urn:taglist", methodName + "2a"));
params.add("_tag", orListParam);
List<IIdType> patients = toUnqualifiedVersionlessIds(ourOrganizationDao.search(params));
assertThat(patients, containsInAnyOrder(tag1id, tag2id));
}
// TODO: get multiple/AND working
{
// And tags
SearchParameterMap params = new SearchParameterMap();
TokenAndListParam andListParam = new TokenAndListParam();
andListParam.addValue(new TokenOrListParam("urn:taglist", methodName + "1a"));
andListParam.addValue(new TokenOrListParam("urn:taglist", methodName + "2a"));
params.add("_tag", andListParam);
List<IIdType> patients = toUnqualifiedVersionlessIds(ourOrganizationDao.search(params));
assertEquals(0, patients.size());
}
{
// And tags
SearchParameterMap params = new SearchParameterMap();
TokenAndListParam andListParam = new TokenAndListParam();
andListParam.addValue(new TokenOrListParam("urn:taglist", methodName + "1a"));
andListParam.addValue(new TokenOrListParam("urn:taglist", methodName + "1b"));
params.add("_tag", andListParam);
List<IIdType> patients = toUnqualifiedVersionlessIds(ourOrganizationDao.search(params));
assertThat(patients, containsInAnyOrder(tag1id));
}
}
@Test @Test
public void testSearchWithToken() { public void testSearchWithToken() {
IIdType notMissing; IIdType notMissing;
@ -3066,18 +3085,13 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
} }
@Test @Test
public void testUpdateFailsForUnknownIdWithNumberThenText() { public void testUpdateDoesntFailForUnknownIdWithNumberThenText() {
String methodName = "testUpdateFailsForUnknownIdWithNumberThenText"; String methodName = "testUpdateFailsForUnknownIdWithNumberThenText";
Patient p = new Patient(); Patient p = new Patient();
p.setId("0" + methodName); p.setId("0" + methodName);
p.addName().addFamily(methodName); p.addName().addFamily(methodName);
try { ourPatientDao.update(p);
ourPatientDao.update(p);
fail();
} catch (InvalidRequestException e) {
assertThat(e.getMessage(), containsString("no resource with this ID exists and clients may only assign IDs which begin with a non-numeric character on this server"));
}
} }
@Test @Test
@ -3157,6 +3171,50 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
} }
} }
@Test
public void testUpdateWithNumericIdFails() {
Patient p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue("testCreateNumericIdFails");
p.addName().addFamily("Hello");
p.setId("Patient/123");
try {
ourPatientDao.update(p);
fail();
} catch (InvalidRequestException e) {
assertThat(e.getMessage(), containsString("clients may only assign IDs which contain at least one non-numeric"));
}
}
@Test
public void testUpdateWithInvalidIdFails() {
Patient p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue("testCreateNumericIdFails");
p.addName().addFamily("Hello");
p.setId("Patient/123:456");
try {
ourPatientDao.update(p);
fail();
} catch (InvalidRequestException e) {
assertEquals("Can not process entity with ID[123:456], this is not a valid FHIR ID", e.getMessage());
}
}
@Test
public void testUpdateWithNumericThenTextIdSucceeds() {
Patient p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue("testCreateNumericIdFails");
p.addName().addFamily("Hello");
p.setId("Patient/123abc");
IIdType id = ourPatientDao.update(p).getId();
assertEquals("123abc", id.getIdPart());
assertEquals("1", id.getVersionIdPart());
p = ourPatientDao.read(id.toUnqualifiedVersionless());
assertEquals("Patient/123abc", p.getId().toUnqualifiedVersionless().getValue());
assertEquals("Hello", p.getName().get(0).getFamily().get(0).getValue());
}
@SuppressWarnings({ "rawtypes" }) @SuppressWarnings({ "rawtypes" })
private List toList(IBundleProvider theSearch) { private List toList(IBundleProvider theSearch) {
return theSearch.getResources(0, theSearch.size()); return theSearch.getResources(0, theSearch.size());
@ -3197,11 +3255,6 @@ public class FhirResourceDaoDstu2Test extends BaseJpaTest {
daoConfig.setInterceptors(ourInterceptor); daoConfig.setInterceptors(ourInterceptor);
} }
@Before
public void before() {
reset(ourInterceptor);
}
private static void deleteEverything() { private static void deleteEverything() {
FhirSystemDaoDstu2Test.doDeleteEverything(ourSystemDao); FhirSystemDaoDstu2Test.doDeleteEverything(ourSystemDao);

View File

@ -33,6 +33,7 @@ import java.util.concurrent.TimeUnit;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpDelete; import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpGet;
@ -130,6 +131,13 @@ public class ResourceProviderDstu2Test extends BaseJpaTest {
// private static JpaConformanceProvider ourConfProvider; // private static JpaConformanceProvider ourConfProvider;
private void checkParamMissing(String paramName) throws IOException, ClientProtocolException {
HttpGet get = new HttpGet(ourServerBase + "/Observation?" + paramName + ":missing=false");
CloseableHttpResponse resp = ourHttpClient.execute(get);
IOUtils.closeQuietly(resp.getEntity().getContent());
assertEquals(200, resp.getStatusLine().getStatusCode());
}
private void delete(String theResourceType, String theParamName, String theParamValue) { private void delete(String theResourceType, String theParamName, String theParamValue) {
Bundle resources; Bundle resources;
do { do {
@ -145,148 +153,6 @@ public class ResourceProviderDstu2Test extends BaseJpaTest {
} while (resources.size() > 0); } while (resources.size() > 0);
} }
/**
* See #198
*/
@Test
public void testSortFromResourceProvider() {
Patient p;
String methodName = "testSortFromResourceProvider";
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
ourClient.create().resource(p).execute();
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addGiven("Daniel").addFamily("Adams");
ourClient.create().resource(p).execute();
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addGiven("Aaron").addFamily("Alexis");
ourClient.create().resource(p).execute();
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addGiven("Carol").addFamily("Allen");
ourClient.create().resource(p).execute();
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addGiven("Ruth").addFamily("Black");
ourClient.create().resource(p).execute();
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addGiven("Brian").addFamily("Brooks");
ourClient.create().resource(p).execute();
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addGiven("Susan").addFamily("Clark");
ourClient.create().resource(p).execute();
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addGiven("Amy").addFamily("Clark");
ourClient.create().resource(p).execute();
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addGiven("Anthony").addFamily("Coleman");
ourClient.create().resource(p).execute();
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addGiven("Steven").addFamily("Coleman");
ourClient.create().resource(p).execute();
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addGiven("Lisa").addFamily("Coleman");
ourClient.create().resource(p).execute();
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addGiven("Ruth").addFamily("Cook");
ourClient.create().resource(p).execute();
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addGiven("Betty").addFamily("Davis");
ourClient.create().resource(p).execute();
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addGiven("Joshua").addFamily("Diaz");
ourClient.create().resource(p).execute();
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addGiven("Brian").addFamily("Gracia");
ourClient.create().resource(p).execute();
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addGiven("Stephan").addFamily("Graham");
ourClient.create().resource(p).execute();
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addGiven("Sarah").addFamily("Graham");
ourClient.create().resource(p).execute();
//@formatter:off
Bundle resp = ourClient
.search()
.forResource(Patient.class)
.where(Patient.IDENTIFIER.exactly().systemAndCode("urn:system", methodName))
.sort().ascending(Patient.FAMILY)
.sort().ascending(Patient.GIVEN)
.limitTo(100)
.execute();
//@formatter:on
List<String> names = toNameList(resp);
ourLog.info(StringUtils.join(names, '\n'));
//@formatter:off
assertThat(names, contains( // this matches in order only
"Daniel Adams",
"Aaron Alexis",
"Carol Allen",
"Ruth Black",
"Brian Brooks",
"Amy Clark",
"Susan Clark",
"Anthony Coleman",
"Lisa Coleman",
"Steven Coleman",
"Ruth Cook",
"Betty Davis",
"Joshua Diaz",
"Brian Gracia",
"Sarah Graham",
"Stephan Graham"));
//@formatter:om
}
private List<String> toNameList(Bundle resp) {
List<String> names = new ArrayList<String>();
for (BundleEntry next : resp.getEntries()) {
Patient nextPt= (Patient) next.getResource();
String nextStr = nextPt.getNameFirstRep().getGivenAsSingleString()+ " " + nextPt.getNameFirstRep().getFamilyAsSingleString();
if (isNotBlank(nextStr)) {
names.add(nextStr);
}
}
return names;
}
private void deleteToken(String theResourceType, String theParamName, String theParamSystem, String theParamValue) { private void deleteToken(String theResourceType, String theParamName, String theParamSystem, String theParamValue) {
Bundle resources = ourClient.search().forResource(theResourceType).where(new TokenClientParam(theParamName).exactly().systemAndCode(theParamSystem, theParamValue)).execute(); Bundle resources = ourClient.search().forResource(theResourceType).where(new TokenClientParam(theParamName).exactly().systemAndCode(theParamSystem, theParamValue)).execute();
for (IResource next : resources.toListOfResources()) { for (IResource next : resources.toListOfResources()) {
@ -347,46 +213,34 @@ public class ResourceProviderDstu2Test extends BaseJpaTest {
} }
@Test @Test
public void testMetaOperations() throws Exception { public void testCreateQuestionnaireResponseWithValidation() throws IOException {
String methodName = "testMetaOperations"; ValueSet options = new ValueSet();
options.getCodeSystem().setSystem("urn:system").addConcept().setCode("code0");
IIdType optId = ourClient.create().resource(options).execute().getId();
Patient pt = new Patient(); Questionnaire q = new Questionnaire();
pt.addName().addFamily(methodName); q.getGroup().addQuestion().setLinkId("link0").setRequired(false).setType(AnswerFormatEnum.CHOICE).setOptions(new ResourceReferenceDt(optId));
IIdType id = ourClient.create().resource(pt).execute().getId().toUnqualifiedVersionless(); IIdType qId = ourClient.create().resource(q).execute().getId();
MetaDt meta = ourClient.meta().get(MetaDt.class).fromResource(id).execute(); QuestionnaireResponse qa;
assertEquals(0, meta.getTag().size());
MetaDt inMeta = new MetaDt(); // Good code
inMeta.addTag().setSystem("urn:system1").setCode("urn:code1");
meta = ourClient.meta().add().onResource(id).meta(inMeta).execute();
assertEquals(1, meta.getTag().size());
inMeta = new MetaDt(); qa = new QuestionnaireResponse();
inMeta.addTag().setSystem("urn:system1").setCode("urn:code1"); qa.getQuestionnaire().setReference(qId.toUnqualifiedVersionless().getValue());
meta = ourClient.meta().delete().onResource(id).meta(inMeta).execute(); qa.getGroup().addQuestion().setLinkId("link0").addAnswer().setValue(new CodingDt().setSystem("urn:system").setCode("code0"));
assertEquals(0, meta.getTag().size()); ourClient.create().resource(qa).execute();
} // Bad code
@Test qa = new QuestionnaireResponse();
public void testGetResourceCountsOperation() throws Exception { qa.getQuestionnaire().setReference(qId.toUnqualifiedVersionless().getValue());
String methodName = "testMetaOperations"; qa.getGroup().addQuestion().setLinkId("link0").addAnswer().setValue(new CodingDt().setSystem("urn:system").setCode("code1"));
Patient pt = new Patient();
pt.addName().addFamily(methodName);
ourClient.create().resource(pt).execute().getId().toUnqualifiedVersionless();
HttpGet get = new HttpGet(ourServerBase + "/$get-resource-counts");
CloseableHttpResponse response = ourHttpClient.execute(get);
try { try {
assertEquals(200, response.getStatusLine().getStatusCode()); ourClient.create().resource(qa).execute();
String output = IOUtils.toString(response.getEntity().getContent()); fail();
IOUtils.closeQuietly(response.getEntity().getContent()); } catch (UnprocessableEntityException e) {
ourLog.info(output); assertThat(e.getMessage(), containsString("Question with linkId[link0]"));
assertThat(output, containsString("<parameter><name value=\"Patient\"/><valueInteger value=\""));
} finally {
response.close();
} }
} }
@ -427,34 +281,22 @@ public class ResourceProviderDstu2Test extends BaseJpaTest {
} }
@Test @Test
public void testCreateQuestionnaireResponseWithValidation() throws IOException { public void testCreateResourceReturnsOperationOutcomeByDefault() throws IOException {
ValueSet options = new ValueSet(); String resource = "<Patient xmlns=\"http://hl7.org/fhir\"></Patient>";
options.getCodeSystem().setSystem("urn:system").addConcept().setCode("code0");
IIdType optId = ourClient.create().resource(options).execute().getId();
Questionnaire q = new Questionnaire();
q.getGroup().addQuestion().setLinkId("link0").setRequired(false).setType(AnswerFormatEnum.CHOICE).setOptions(new ResourceReferenceDt(optId));
IIdType qId = ourClient.create().resource(q).execute().getId();
QuestionnaireResponse qa; HttpPost post = new HttpPost(ourServerBase + "/Patient");
post.setEntity(new StringEntity(resource, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
// Good code CloseableHttpResponse response = ourHttpClient.execute(post);
qa = new QuestionnaireResponse();
qa.getQuestionnaire().setReference(qId.toUnqualifiedVersionless().getValue());
qa.getGroup().addQuestion().setLinkId("link0").addAnswer().setValue(new CodingDt().setSystem("urn:system").setCode("code0"));
ourClient.create().resource(qa).execute();
// Bad code
qa = new QuestionnaireResponse();
qa.getQuestionnaire().setReference(qId.toUnqualifiedVersionless().getValue());
qa.getGroup().addQuestion().setLinkId("link0").addAnswer().setValue(new CodingDt().setSystem("urn:system").setCode("code1"));
try { try {
ourClient.create().resource(qa).execute(); assertEquals(201, response.getStatusLine().getStatusCode());
fail(); String respString = IOUtils.toString(response.getEntity().getContent());
} catch (UnprocessableEntityException e) { ourLog.info(response.toString());
assertThat(e.getMessage(), containsString("Question with linkId[link0]")); ourLog.info(respString);
assertThat(respString, containsString("<OperationOutcome xmlns=\"http://hl7.org/fhir\">"));
} finally {
response.getEntity().getContent().close();
response.close();
} }
} }
@ -478,27 +320,6 @@ public class ResourceProviderDstu2Test extends BaseJpaTest {
} }
} }
@Test
public void testCreateResourceReturnsOperationOutcomeByDefault() throws IOException {
String resource = "<Patient xmlns=\"http://hl7.org/fhir\"></Patient>";
HttpPost post = new HttpPost(ourServerBase + "/Patient");
post.setEntity(new StringEntity(resource, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
CloseableHttpResponse response = ourHttpClient.execute(post);
try {
assertEquals(201, response.getStatusLine().getStatusCode());
String respString = IOUtils.toString(response.getEntity().getContent());
ourLog.info(response.toString());
ourLog.info(respString);
assertThat(respString, containsString("<OperationOutcome xmlns=\"http://hl7.org/fhir\">"));
} finally {
response.getEntity().getContent().close();
response.close();
}
}
@Test @Test
public void testDeepChaining() { public void testDeepChaining() {
delete("Location", Location.SP_NAME, "testDeepChainingL1"); delete("Location", Location.SP_NAME, "testDeepChainingL1");
@ -604,8 +425,7 @@ public class ResourceProviderDstu2Test extends BaseJpaTest {
} }
/* /*
* Try it with a raw socket call. The Apache client won't let us use the unescaped "|" in the URL but we want to * Try it with a raw socket call. The Apache client won't let us use the unescaped "|" in the URL but we want to make sure that works too..
* make sure that works too..
*/ */
Socket sock = new Socket(); Socket sock = new Socket();
sock.setSoTimeout(3000); sock.setSoTimeout(3000);
@ -845,6 +665,27 @@ public class ResourceProviderDstu2Test extends BaseJpaTest {
ourLog.info(ids.toString()); ourLog.info(ids.toString());
} }
@Test
public void testGetResourceCountsOperation() throws Exception {
String methodName = "testMetaOperations";
Patient pt = new Patient();
pt.addName().addFamily(methodName);
ourClient.create().resource(pt).execute().getId().toUnqualifiedVersionless();
HttpGet get = new HttpGet(ourServerBase + "/$get-resource-counts");
CloseableHttpResponse response = ourHttpClient.execute(get);
try {
assertEquals(200, response.getStatusLine().getStatusCode());
String output = IOUtils.toString(response.getEntity().getContent());
IOUtils.closeQuietly(response.getEntity().getContent());
ourLog.info(output);
assertThat(output, containsString("<parameter><name value=\"Patient\"/><valueInteger value=\""));
} finally {
response.close();
}
}
/** /**
* See issue #52 * See issue #52
*/ */
@ -863,6 +704,29 @@ public class ResourceProviderDstu2Test extends BaseJpaTest {
} }
@Test
public void testMetaOperations() throws Exception {
String methodName = "testMetaOperations";
Patient pt = new Patient();
pt.addName().addFamily(methodName);
IIdType id = ourClient.create().resource(pt).execute().getId().toUnqualifiedVersionless();
MetaDt meta = ourClient.meta().get(MetaDt.class).fromResource(id).execute();
assertEquals(0, meta.getTag().size());
MetaDt inMeta = new MetaDt();
inMeta.addTag().setSystem("urn:system1").setCode("urn:code1");
meta = ourClient.meta().add().onResource(id).meta(inMeta).execute();
assertEquals(1, meta.getTag().size());
inMeta = new MetaDt();
inMeta.addTag().setSystem("urn:system1").setCode("urn:code1");
meta = ourClient.meta().delete().onResource(id).meta(inMeta).execute();
assertEquals(0, meta.getTag().size());
}
/** /**
* Test for issue #60 * Test for issue #60
*/ */
@ -979,7 +843,8 @@ public class ResourceProviderDstu2Test extends BaseJpaTest {
p1.addIdentifier().setValue("testSearchByIdentifierWithoutSystem01"); p1.addIdentifier().setValue("testSearchByIdentifierWithoutSystem01");
IdDt p1Id = (IdDt) ourClient.create().resource(p1).execute().getId(); IdDt p1Id = (IdDt) ourClient.create().resource(p1).execute().getId();
Bundle actual = ourClient.search().forResource(Patient.class).where(Patient.IDENTIFIER.exactly().systemAndCode(null, "testSearchByIdentifierWithoutSystem01")).encodedJson().prettyPrint().execute(); Bundle actual = ourClient.search().forResource(Patient.class).where(Patient.IDENTIFIER.exactly().systemAndCode(null, "testSearchByIdentifierWithoutSystem01")).encodedJson().prettyPrint()
.execute();
assertEquals(1, actual.size()); assertEquals(1, actual.size());
assertEquals(p1Id.getIdPart(), actual.getEntries().get(0).getResource().getId().getIdPart()); assertEquals(p1Id.getIdPart(), actual.getEntries().get(0).getResource().getId().getIdPart());
@ -1172,6 +1037,33 @@ public class ResourceProviderDstu2Test extends BaseJpaTest {
assertEquals(BundleEntrySearchModeEnum.INCLUDE, found.getEntries().get(1).getResource().getResourceMetadata().get(ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE)); assertEquals(BundleEntrySearchModeEnum.INCLUDE, found.getEntries().get(1).getResource().getResourceMetadata().get(ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE));
} }
@Test
public void testSearchWithTextInexactMatch() throws Exception {
Observation obs = new Observation();
obs.getCode().setText("THIS_IS_THE_TEXT");
obs.getCode().addCoding().setSystem("SYSTEM").setCode("CODE").setDisplay("THIS_IS_THE_DISPLAY");
ourClient.create().resource(obs).execute();
testSearchReturnsResults("/Observation?code%3Atext=THIS_IS_THE_TEXT");
testSearchReturnsResults("/Observation?code%3Atext=THIS_IS_THE_");
testSearchReturnsResults("/Observation?code%3Atext=this_is_the_");
testSearchReturnsResults("/Observation?code%3Atext=THIS_IS_THE_DISPLAY");
testSearchReturnsResults("/Observation?code%3Atext=THIS_IS_THE_disp");
}
private void testSearchReturnsResults(String search) throws IOException, ClientProtocolException {
int matches;
HttpGet get = new HttpGet(ourServerBase + search);
CloseableHttpResponse response = ourHttpClient.execute(get);
String resp = IOUtils.toString(response.getEntity().getContent());
IOUtils.closeQuietly(response.getEntity().getContent());
ourLog.info(resp);
ca.uhn.fhir.model.dstu2.resource.Bundle bundle = ourCtx.newXmlParser().parseResource(ca.uhn.fhir.model.dstu2.resource.Bundle.class, resp);
matches = bundle.getTotal();
assertThat(matches, greaterThan(0));
}
@Test @Test
public void testSearchWithMissing() throws Exception { public void testSearchWithMissing() throws Exception {
ourLog.info("Starting testSearchWithMissing"); ourLog.info("Starting testSearchWithMissing");
@ -1242,6 +1134,144 @@ public class ResourceProviderDstu2Test extends BaseJpaTest {
assertThat("Wanted " + orgMissing + " but found: " + list, list, containsInRelativeOrder(orgMissing)); assertThat("Wanted " + orgMissing + " but found: " + list, list, containsInRelativeOrder(orgMissing));
} }
@Test
public void testSearchWithMissing2() throws Exception {
checkParamMissing(Observation.SP_CODE);
checkParamMissing(Observation.SP_CATEGORY);
checkParamMissing(Observation.SP_VALUE_STRING);
checkParamMissing(Observation.SP_ENCOUNTER);
checkParamMissing(Observation.SP_DATE);
}
/**
* See #198
*/
@Test
public void testSortFromResourceProvider() {
Patient p;
String methodName = "testSortFromResourceProvider";
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
ourClient.create().resource(p).execute();
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addGiven("Daniel").addFamily("Adams");
ourClient.create().resource(p).execute();
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addGiven("Aaron").addFamily("Alexis");
ourClient.create().resource(p).execute();
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addGiven("Carol").addFamily("Allen");
ourClient.create().resource(p).execute();
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addGiven("Ruth").addFamily("Black");
ourClient.create().resource(p).execute();
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addGiven("Brian").addFamily("Brooks");
ourClient.create().resource(p).execute();
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addGiven("Susan").addFamily("Clark");
ourClient.create().resource(p).execute();
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addGiven("Amy").addFamily("Clark");
ourClient.create().resource(p).execute();
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addGiven("Anthony").addFamily("Coleman");
ourClient.create().resource(p).execute();
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addGiven("Steven").addFamily("Coleman");
ourClient.create().resource(p).execute();
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addGiven("Lisa").addFamily("Coleman");
ourClient.create().resource(p).execute();
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addGiven("Ruth").addFamily("Cook");
ourClient.create().resource(p).execute();
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addGiven("Betty").addFamily("Davis");
ourClient.create().resource(p).execute();
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addGiven("Joshua").addFamily("Diaz");
ourClient.create().resource(p).execute();
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addGiven("Brian").addFamily("Gracia");
ourClient.create().resource(p).execute();
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addGiven("Stephan").addFamily("Graham");
ourClient.create().resource(p).execute();
p = new Patient();
p.addIdentifier().setSystem("urn:system").setValue(methodName);
p.addName().addGiven("Sarah").addFamily("Graham");
ourClient.create().resource(p).execute();
//@formatter:off
Bundle resp = ourClient
.search()
.forResource(Patient.class)
.where(Patient.IDENTIFIER.exactly().systemAndCode("urn:system", methodName))
.sort().ascending(Patient.FAMILY)
.sort().ascending(Patient.GIVEN)
.limitTo(100)
.execute();
//@formatter:on
List<String> names = toNameList(resp);
ourLog.info(StringUtils.join(names, '\n'));
//@formatter:off
assertThat(names, contains( // this matches in order only
"Daniel Adams",
"Aaron Alexis",
"Carol Allen",
"Ruth Black",
"Brian Brooks",
"Amy Clark",
"Susan Clark",
"Anthony Coleman",
"Lisa Coleman",
"Steven Coleman",
"Ruth Cook",
"Betty Davis",
"Joshua Diaz",
"Brian Gracia",
"Sarah Graham",
"Stephan Graham"));
//@formatter:om
}
/** /**
* Test for issue #60 * Test for issue #60
*/ */
@ -1578,6 +1608,18 @@ public class ResourceProviderDstu2Test extends BaseJpaTest {
return list; return list;
} }
private List<String> toNameList(Bundle resp) {
List<String> names = new ArrayList<String>();
for (BundleEntry next : resp.getEntries()) {
Patient nextPt = (Patient) next.getResource();
String nextStr = nextPt.getNameFirstRep().getGivenAsSingleString() + " " + nextPt.getNameFirstRep().getFamilyAsSingleString();
if (isNotBlank(nextStr)) {
names.add(nextStr);
}
}
return names;
}
@AfterClass @AfterClass
public static void afterClass() throws Exception { public static void afterClass() throws Exception {
ourServer.stop(); ourServer.stop();

View File

@ -4,6 +4,7 @@ import static org.junit.Assert.*;
import java.math.BigDecimal; import java.math.BigDecimal;
import org.apache.commons.lang3.StringUtils;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
@ -17,6 +18,19 @@ public class IdDtTest {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(IdDtTest.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(IdDtTest.class);
@Test
public void testDetectIsIdPartValid() {
assertTrue(new IdDt("0").isIdPartValid());
assertTrue(new IdDt("0a").isIdPartValid());
assertTrue(new IdDt("0abgZZ").isIdPartValid());
assertTrue(new IdDt("---").isIdPartValid());
assertTrue(new IdDt("1.2.3.4").isIdPartValid());
assertFalse(new IdDt(" 1").isIdPartValid());
assertFalse(new IdDt("1:1").isIdPartValid());
assertFalse(new IdDt(StringUtils.leftPad("", 65, '0')).isIdPartValid());
}
@Test @Test
public void testDetectLocal() { public void testDetectLocal() {
IdDt id; IdDt id;

View File

@ -654,7 +654,33 @@ public final class IdType extends UriType implements IPrimitiveType<String>, IId
} }
return theIdPart.toString(); return theIdPart.toString();
} }
@Override
public boolean isIdPartValid() {
String id = getIdPart();
if (StringUtils.isBlank(id)) {
return false;
}
if (id.length() > 64) {
return false;
}
for (int i = 0; i < id.length(); i++) {
char nextChar = id.charAt(i);
if (nextChar >= 'a' && nextChar <= 'z') {
continue;
}
if (nextChar >= 'A' && nextChar <= 'Z') {
continue;
}
if (nextChar >= '0' && nextChar <= '9') {
continue;
}
if (nextChar == '-' || nextChar == '.') {
continue;
}
return false;
}
return true;
}
/** /**
* Construct a new ID with with form "urn:uuid:[UUID]" where [UUID] is a new, randomly * Construct a new ID with with form "urn:uuid:[UUID]" where [UUID] is a new, randomly
* created UUID generated by {@link UUID#randomUUID()} * created UUID generated by {@link UUID#randomUUID()}

View File

@ -113,6 +113,15 @@
factory to be used (e.g. for custom authentication, etc.) Thanks factory to be used (e.g. for custom authentication, etc.) Thanks
to Chin Huang (@pukkaone) for the pull request! to Chin Huang (@pukkaone) for the pull request!
</action> </action>
<action type="fix" issue="212">
JPA server should reject IDs containing invalid characters (e.g. "abc:123")
but should allow client assigned IDs that contain text but do not start with
text. Thanks to Josh Mandel for reporting!
</action>
<action type="fix">
:text modifier on server and JPA server did not work correctly. Thanks to
Josh Mandel for reporting!
</action>
</release> </release>
<release version="1.1" date="2015-07-13"> <release version="1.1" date="2015-07-13">
<action type="add"> <action type="add">