Add error checking and better handling for match URLs in JPA server
This commit is contained in:
parent
541e6cdcb1
commit
119a4f36d9
|
@ -6,7 +6,7 @@
|
|||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
|
||||
<classpathentry kind="src" output="target/classes" path="src/main/resources">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
|
@ -17,7 +17,7 @@
|
|||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources">
|
||||
<classpathentry kind="src" output="target/test-classes" path="src/test/resources">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
|
|
|
@ -15,26 +15,6 @@
|
|||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.ui.externaltools.ExternalToolBuilder</name>
|
||||
<triggers>full,incremental,</triggers>
|
||||
<arguments>
|
||||
<dictionary>
|
||||
<key>LaunchConfigHandle</key>
|
||||
<value><project>/.externalToolBuilders/org.eclipse.wst.validation.validationbuilder.launch</value>
|
||||
</dictionary>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.ui.externaltools.ExternalToolBuilder</name>
|
||||
<triggers>full,incremental,</triggers>
|
||||
<arguments>
|
||||
<dictionary>
|
||||
<key>LaunchConfigHandle</key>
|
||||
<value><project>/.externalToolBuilders/org.eclipse.m2e.core.maven2Builder (1).launch</value>
|
||||
</dictionary>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jem.workbench.JavaEMFNature</nature>
|
||||
|
|
|
@ -7,15 +7,15 @@
|
|||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="src" output="target/test-classes" path="src/test/resources"/>
|
||||
<classpathentry kind="src" path="src/main/java">
|
||||
<classpathentry kind="src" output="target/classes" path="src/main/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="src" path="src/main/resources"/>
|
||||
<classpathentry kind="src" path="target/generated-sources/tinder"/>
|
||||
<classpathentry kind="src" path="target/generated-resources/tinder">
|
||||
<classpathentry kind="src" output="target/classes" path="src/main/resources"/>
|
||||
<classpathentry kind="src" output="target/classes" path="target/generated-sources/tinder"/>
|
||||
<classpathentry kind="src" output="target/classes" path="target/generated-resources/tinder">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
|
|
|
@ -16,26 +16,6 @@
|
|||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.ui.externaltools.ExternalToolBuilder</name>
|
||||
<triggers>full,incremental,</triggers>
|
||||
<arguments>
|
||||
<dictionary>
|
||||
<key>LaunchConfigHandle</key>
|
||||
<value><project>/.externalToolBuilders/org.eclipse.wst.validation.validationbuilder (1).launch</value>
|
||||
</dictionary>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.ui.externaltools.ExternalToolBuilder</name>
|
||||
<triggers>full,incremental,</triggers>
|
||||
<arguments>
|
||||
<dictionary>
|
||||
<key>LaunchConfigHandle</key>
|
||||
<value><project>/.externalToolBuilders/org.eclipse.m2e.core.maven2Builder (2).launch</value>
|
||||
</dictionary>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jem.workbench.JavaEMFNature</nature>
|
||||
|
|
|
@ -174,8 +174,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
|
||||
RuntimeResourceDefinition def = getContext().getResourceDefinition(theResource);
|
||||
for (RuntimeSearchParam nextSpDef : def.getSearchParams()) {
|
||||
|
||||
|
||||
|
||||
if (nextSpDef.getParamType() != RestSearchParameterTypeEnum.REFERENCE) {
|
||||
continue;
|
||||
}
|
||||
|
@ -193,7 +192,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
String[] nextPathsSplit = nextPathsUnsplit.split("\\|");
|
||||
for (String nextPath : nextPathsSplit) {
|
||||
nextPath = nextPath.trim();
|
||||
|
||||
|
||||
List<Class<? extends IBaseResource>> allowedTypesInField = null;
|
||||
for (Object nextObject : extractValues(nextPath, theResource)) {
|
||||
if (nextObject == null) {
|
||||
|
@ -219,7 +218,8 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
try {
|
||||
resourceDefinition = getContext().getResourceDefinition(typeString);
|
||||
} catch (DataFormatException e) {
|
||||
throw new InvalidRequestException("Invalid resource reference found at path[" + nextPathsUnsplit + "] - Resource type is unknown or not supported on this server - " + nextValue.getReference().getValue());
|
||||
throw new InvalidRequestException(
|
||||
"Invalid resource reference found at path[" + nextPathsUnsplit + "] - Resource type is unknown or not supported on this server - " + nextValue.getReference().getValue());
|
||||
}
|
||||
|
||||
Class<? extends IBaseResource> type = resourceDefinition.getImplementingClass();
|
||||
|
@ -253,15 +253,16 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
String resName = targetResourceDef.getName();
|
||||
throw new InvalidRequestException("Resource " + resName + "/" + id + " not found, specified in path: " + nextPathsUnsplit);
|
||||
}
|
||||
|
||||
|
||||
if (!typeString.equals(target.getResourceType())) {
|
||||
throw new UnprocessableEntityException("Resource contains reference to " + nextValue.getReference().getValue() + " but resource with ID " + nextValue.getReference().getIdPart() + " is actually of type " + target.getResourceType());
|
||||
throw new UnprocessableEntityException("Resource contains reference to " + nextValue.getReference().getValue() + " but resource with ID " + nextValue.getReference().getIdPart()
|
||||
+ " is actually of type " + target.getResourceType());
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Is the target type an allowable type of resource for the path where it is referenced?
|
||||
*/
|
||||
|
||||
|
||||
if (allowedTypesInField == null) {
|
||||
BaseRuntimeChildDefinition childDef = getContext().newTerser().getDefinition(theResource.getClass(), nextPath);
|
||||
if (childDef instanceof RuntimeChildResourceDefinition) {
|
||||
|
@ -272,19 +273,19 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
allowedTypesInField.add(IBaseResource.class);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
boolean acceptableLink = false;
|
||||
for(Class<? extends IBaseResource> next : allowedTypesInField) {
|
||||
for (Class<? extends IBaseResource> next : allowedTypesInField) {
|
||||
if (next.isAssignableFrom(targetResourceDef.getImplementingClass())) {
|
||||
acceptableLink = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!acceptableLink) {
|
||||
throw new UnprocessableEntityException("Invalid reference found at path '" + nextPath + "'. Resource type '" + targetResourceDef.getName() + "' is not valid for this path");
|
||||
}
|
||||
|
||||
|
||||
nextEntity = new ResourceLink(nextPath, theEntity, target);
|
||||
} else {
|
||||
if (!multiType) {
|
||||
|
@ -683,11 +684,9 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
}
|
||||
|
||||
/**
|
||||
* This method is called when an update to an existing resource detects that the resource supplied for update is
|
||||
* missing a tag/profile/security label that the currently persisted resource holds.
|
||||
* This method is called when an update to an existing resource detects that the resource supplied for update is missing a tag/profile/security label that the currently persisted resource holds.
|
||||
* <p>
|
||||
* The default implementation removes any profile declarations, but leaves tags and security labels in place.
|
||||
* Subclasses may choose to override and change this behaviour.
|
||||
* The default implementation removes any profile declarations, but leaves tags and security labels in place. Subclasses may choose to override and change this behaviour.
|
||||
* </p>
|
||||
*
|
||||
* @param theEntity
|
||||
|
@ -695,8 +694,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
* @param theTag
|
||||
* The tag
|
||||
* @return Retturns <code>true</code> if the tag should be removed
|
||||
* @see <a href="http://hl7.org/fhir/2015Sep/resource.html#1.11.3.7">Updates to Tags, Profiles, and Security
|
||||
* Labels</a> for a description of the logic that the default behaviour folows.
|
||||
* @see <a href="http://hl7.org/fhir/2015Sep/resource.html#1.11.3.7">Updates to Tags, Profiles, and Security Labels</a> for a description of the logic that the default behaviour folows.
|
||||
*/
|
||||
protected boolean shouldDroppedTagBeRemovedOnUpdate(ResourceTable theEntity, ResourceTag theTag) {
|
||||
if (theTag.getTag().getTagType() == TagTypeEnum.PROFILE) {
|
||||
|
@ -709,6 +707,10 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
RuntimeResourceDefinition resourceDef = getContext().getResourceDefinition(theResourceType);
|
||||
|
||||
SearchParameterMap paramMap = translateMatchUrl(theMatchUrl, resourceDef);
|
||||
|
||||
if (paramMap.isEmpty()) {
|
||||
throw new InvalidRequestException("Invalid match URL[" + theMatchUrl + "] - URL has no search parameters");
|
||||
}
|
||||
|
||||
IFhirResourceDao<R> dao = getDao(theResourceType);
|
||||
Set<Long> ids = dao.searchForIdsWithAndOr(paramMap);
|
||||
|
@ -719,23 +721,28 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
public static SearchParameterMap translateMatchUrl(String theMatchUrl, RuntimeResourceDefinition resourceDef) {
|
||||
SearchParameterMap paramMap = new SearchParameterMap();
|
||||
List<NameValuePair> parameters;
|
||||
try {
|
||||
String matchUrl = theMatchUrl;
|
||||
if (matchUrl.indexOf('?') == -1) {
|
||||
throw new InvalidRequestException("Failed to parse match URL[" + theMatchUrl + "] - Error was: URL does not contain any parameters ('?' not detected)");
|
||||
}
|
||||
matchUrl = matchUrl.replace("|", "%7C");
|
||||
matchUrl = matchUrl.replace("=>=", "=%3E%3D");
|
||||
matchUrl = matchUrl.replace("=<=", "=%3C%3D");
|
||||
matchUrl = matchUrl.replace("=>", "=%3E");
|
||||
matchUrl = matchUrl.replace("=<", "=%3C");
|
||||
parameters = URLEncodedUtils.parse(new URI(matchUrl), "UTF-8");
|
||||
} catch (URISyntaxException e) {
|
||||
throw new InvalidRequestException("Failed to parse match URL[" + theMatchUrl + "] - Error was: " + e.toString());
|
||||
String matchUrl = theMatchUrl;
|
||||
int questionMarkIndex = matchUrl.indexOf('?');
|
||||
if (questionMarkIndex != -1) {
|
||||
matchUrl = matchUrl.substring(questionMarkIndex + 1);
|
||||
}
|
||||
matchUrl = matchUrl.replace("|", "%7C");
|
||||
matchUrl = matchUrl.replace("=>=", "=%3E%3D");
|
||||
matchUrl = matchUrl.replace("=<=", "=%3C%3D");
|
||||
matchUrl = matchUrl.replace("=>", "=%3E");
|
||||
matchUrl = matchUrl.replace("=<", "=%3C");
|
||||
if (matchUrl.contains(" ")) {
|
||||
throw new InvalidRequestException("Failed to parse match URL[" + theMatchUrl + "] - URL is invalid (must not contain spaces)");
|
||||
}
|
||||
|
||||
parameters = URLEncodedUtils.parse((matchUrl), Constants.CHARSET_UTF8, '&');
|
||||
|
||||
ArrayListMultimap<String, QualifiedParamList> nameToParamLists = ArrayListMultimap.create();
|
||||
for (NameValuePair next : parameters) {
|
||||
if (isBlank(next.getValue())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String paramName = next.getName();
|
||||
String qualifier = null;
|
||||
for (int i = 0; i < paramMap.size(); i++) {
|
||||
|
@ -779,11 +786,11 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (nextParamName.startsWith("_")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
|
@ -1030,8 +1037,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
}
|
||||
} else if (theForHistoryOperation) {
|
||||
/*
|
||||
* If the create and update times match, this was when the resource was created
|
||||
* so we should mark it as a POST. Otherwise, it's a PUT.
|
||||
* If the create and update times match, this was when the resource was created so we should mark it as a POST. Otherwise, it's a PUT.
|
||||
*/
|
||||
Date published = theEntity.getPublished().getValue();
|
||||
Date updated = theEntity.getUpdated().getValue();
|
||||
|
@ -1041,7 +1047,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.put(res, BundleEntryTransactionMethodEnum.PUT);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
res.setId(theEntity.getIdDt());
|
||||
|
||||
ResourceMetadataKeyEnum.VERSION.put(res, Long.toString(theEntity.getVersion()));
|
||||
|
@ -1125,8 +1131,9 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected ResourceTable updateEntity(final IResource theResource, ResourceTable theEntity, boolean theUpdateHistory, Date theDeletedTimestampOrNull, boolean thePerformIndexing, boolean theUpdateVersion, Date theUpdateTime) {
|
||||
|
||||
protected ResourceTable updateEntity(final IResource theResource, ResourceTable theEntity, boolean theUpdateHistory, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
|
||||
boolean theUpdateVersion, Date theUpdateTime) {
|
||||
|
||||
/*
|
||||
* This should be the very first thing..
|
||||
*/
|
||||
|
@ -1134,14 +1141,15 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
validateResourceForStorage((T) theResource, theEntity);
|
||||
String resourceType = myContext.getResourceDefinition(theResource).getName();
|
||||
if (isNotBlank(theEntity.getResourceType()) && !theEntity.getResourceType().equals(resourceType)) {
|
||||
throw new UnprocessableEntityException("Existing resource ID[" + theEntity.getIdDt().toUnqualifiedVersionless() + "] is of type[" + theEntity.getResourceType() + "] - Cannot update with [" + resourceType + "]");
|
||||
throw new UnprocessableEntityException(
|
||||
"Existing resource ID[" + theEntity.getIdDt().toUnqualifiedVersionless() + "] is of type[" + theEntity.getResourceType() + "] - Cannot update with [" + resourceType + "]");
|
||||
}
|
||||
}
|
||||
|
||||
if (theEntity.getPublished() == null) {
|
||||
theEntity.setPublished(theUpdateTime);
|
||||
}
|
||||
|
||||
|
||||
if (theUpdateHistory) {
|
||||
final ResourceHistoryTable historyEntry = theEntity.toHistory();
|
||||
myEntityManager.persist(historyEntry);
|
||||
|
@ -1238,7 +1246,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
theEntity.setResourceLinks(links);
|
||||
theEntity.setHasLinks(links.isEmpty() == false);
|
||||
theEntity.setIndexStatus(INDEX_STATUS_INDEXED);
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
populateResourceIntoEntity(theResource, theEntity);
|
||||
|
@ -1261,7 +1269,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
|
||||
} else {
|
||||
theEntity = myEntityManager.merge(theEntity);
|
||||
|
||||
|
||||
postUpdate(theEntity, (T) theResource);
|
||||
}
|
||||
|
||||
|
@ -1354,37 +1362,37 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
}
|
||||
|
||||
/**
|
||||
* Subclasses may override to provide behaviour. Called when a resource has been inserved into the database for the
|
||||
* first time.
|
||||
* Subclasses may override to provide behaviour. Called when a resource has been inserved into the database for the first time.
|
||||
*
|
||||
* @param theEntity
|
||||
* The resource
|
||||
* @param theResource The resource being persisted
|
||||
* @param theResource
|
||||
* The resource being persisted
|
||||
*/
|
||||
protected void postUpdate(ResourceTable theEntity, T theResource) {
|
||||
// nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses may override to provide behaviour. Called when a resource has been inserved into the database for the
|
||||
* first time.
|
||||
* Subclasses may override to provide behaviour. Called when a resource has been inserved into the database for the first time.
|
||||
*
|
||||
* @param theEntity
|
||||
* The resource
|
||||
* @param theResource The resource being persisted
|
||||
* @param theResource
|
||||
* The resource being persisted
|
||||
*/
|
||||
protected void postPersist(ResourceTable theEntity, T theResource) {
|
||||
// nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is invoked immediately before storing a new resource, or an update to an existing resource to allow
|
||||
* the DAO to ensure that it is valid for persistence. By default, checks for the "subsetted" tag and rejects
|
||||
* resources which have it. Subclasses should call the superclass implementation to preserve this check.
|
||||
* This method is invoked immediately before storing a new resource, or an update to an existing resource to allow the DAO to ensure that it is valid for persistence. By default, checks for the
|
||||
* "subsetted" tag and rejects resources which have it. Subclasses should call the superclass implementation to preserve this check.
|
||||
*
|
||||
* @param theResource
|
||||
* The resource that is about to be persisted
|
||||
* @param theEntityToSave TODO
|
||||
* @param theEntityToSave
|
||||
* TODO
|
||||
*/
|
||||
protected void validateResourceForStorage(T theResource, ResourceTable theEntityToSave) {
|
||||
IResource res = (IResource) theResource;
|
||||
|
|
|
@ -56,6 +56,7 @@ import ca.uhn.fhir.model.dstu.valueset.QuantityCompararatorEnum;
|
|||
import ca.uhn.fhir.model.dstu2.resource.Subscription;
|
||||
import ca.uhn.fhir.model.dstu2.valueset.SubscriptionStatusEnum;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
import ca.uhn.fhir.rest.api.SortOrderEnum;
|
||||
import ca.uhn.fhir.rest.api.SortSpec;
|
||||
|
@ -129,7 +130,8 @@ public class FhirResourceDaoSubscriptionDstu2 extends FhirResourceDaoDstu2<Subsc
|
|||
ourLog.trace("Skipping search for subscription");
|
||||
return;
|
||||
}
|
||||
ourLog.info("Subscription search from {} to {}", start, end);
|
||||
|
||||
ourLog.debug("Subscription {} search from {} to {}", new Object[] { subscription.getId().getIdPart(), new InstantDt(new Date(start)), new InstantDt(new Date(end)) });
|
||||
|
||||
DateRangeParam range = new DateRangeParam();
|
||||
range.setLowerBound(new DateParam(QuantityCompararatorEnum.GREATERTHAN, start));
|
||||
|
@ -176,7 +178,8 @@ public class FhirResourceDaoSubscriptionDstu2 extends FhirResourceDaoDstu2<Subsc
|
|||
}
|
||||
|
||||
@Override
|
||||
protected ResourceTable updateEntity(IResource theResource, ResourceTable theEntity, boolean theUpdateHistory, Date theDeletedTimestampOrNull, boolean thePerformIndexing, boolean theUpdateVersion, Date theUpdateTime) {
|
||||
protected ResourceTable updateEntity(IResource theResource, ResourceTable theEntity, boolean theUpdateHistory, Date theDeletedTimestampOrNull, boolean thePerformIndexing, boolean theUpdateVersion,
|
||||
Date theUpdateTime) {
|
||||
ResourceTable retVal = super.updateEntity(theResource, theEntity, theUpdateHistory, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime);
|
||||
|
||||
Subscription resource = (Subscription) theResource;
|
||||
|
@ -278,16 +281,17 @@ public class FhirResourceDaoSubscriptionDstu2 extends FhirResourceDaoDstu2<Subsc
|
|||
@Override
|
||||
public void purgeInactiveSubscriptions() {
|
||||
Long purgeInactiveAfterMillis = getConfig().getSubscriptionPurgeInactiveAfterMillis();
|
||||
if (getConfig().isSubscriptionEnabled()==false || purgeInactiveAfterMillis == null) {
|
||||
if (getConfig().isSubscriptionEnabled() == false || purgeInactiveAfterMillis == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Date cutoff = new Date(System.currentTimeMillis() - purgeInactiveAfterMillis);
|
||||
Collection<SubscriptionTable> toPurge = mySubscriptionTableDao.findInactiveBeforeCutoff(cutoff);
|
||||
for (SubscriptionTable subscriptionTable : toPurge) {
|
||||
|
||||
final IdDt subscriptionId = subscriptionTable.getSubscriptionResource().getIdDt();
|
||||
ourLog.info("Deleting inactive subscription {} - Created {}, last client poll {}", new Object[] { subscriptionId.toUnqualified(), subscriptionTable.getCreated(), subscriptionTable.getLastClientPoll() });
|
||||
ourLog.info("Deleting inactive subscription {} - Created {}, last client poll {}",
|
||||
new Object[] { subscriptionId.toUnqualified(), subscriptionTable.getCreated(), subscriptionTable.getLastClientPoll() });
|
||||
TransactionTemplate txTemplate = new TransactionTemplate(myTxManager);
|
||||
txTemplate.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRES_NEW);
|
||||
txTemplate.execute(new TransactionCallback<Void>() {
|
||||
|
|
|
@ -1,19 +1,32 @@
|
|||
package ca.uhn.fhir.jpa.dao;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.junit.AfterClass;
|
||||
|
||||
import ca.uhn.fhir.jpa.provider.SystemProviderDstu2Test;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Bundle;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry;
|
||||
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||
|
||||
public class BaseJpaTest {
|
||||
|
||||
public static String loadClasspath(String resource) throws IOException {
|
||||
InputStream bundleRes = SystemProviderDstu2Test.class.getResourceAsStream(resource);
|
||||
if (bundleRes == null) {
|
||||
throw new NullPointerException("Can not load " + resource);
|
||||
}
|
||||
String bundleStr = IOUtils.toString(bundleRes);
|
||||
return bundleStr;
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterClassShutdownDerby() throws SQLException {
|
||||
// try {
|
||||
|
|
|
@ -18,7 +18,6 @@ import java.util.Date;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.junit.AfterClass;
|
||||
|
@ -28,7 +27,6 @@ import org.springframework.context.support.ClassPathXmlApplicationContext;
|
|||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.jpa.entity.TagTypeEnum;
|
||||
import ca.uhn.fhir.jpa.provider.SystemProviderDstu2Test;
|
||||
import ca.uhn.fhir.model.api.Bundle;
|
||||
import ca.uhn.fhir.model.api.BundleEntry;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
|
@ -346,8 +344,8 @@ public class FhirSystemDaoDstu1Test extends BaseJpaTest {
|
|||
|
||||
@Test
|
||||
public void testTransactionWithCidIds2() throws Exception {
|
||||
InputStream bundleRes = SystemProviderDstu2Test.class.getResourceAsStream("/dstu1_bundle.xml");
|
||||
String bundleStr = IOUtils.toString(bundleRes);
|
||||
String resource = "/dstu1_bundle.xml";
|
||||
String bundleStr = loadClasspath(resource);
|
||||
Bundle bundle = ourFhirContext.newXmlParser().parseBundle(bundleStr);
|
||||
|
||||
List<IResource> res = new ArrayList<IResource>();
|
||||
|
@ -363,6 +361,7 @@ public class FhirSystemDaoDstu1Test extends BaseJpaTest {
|
|||
assertThat(encodeResourceToString, not(containsString("smsp")));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This is the correct way to do this, not {@link #testTransactionWithCidIds()}
|
||||
*/
|
||||
|
|
|
@ -44,6 +44,7 @@ import ca.uhn.fhir.model.dstu2.composite.MetaDt;
|
|||
import ca.uhn.fhir.model.dstu2.composite.ResourceReferenceDt;
|
||||
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.Bundle.EntryResponse;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Observation;
|
||||
import ca.uhn.fhir.model.dstu2.resource.OperationOutcome;
|
||||
|
@ -354,6 +355,42 @@ public class FhirSystemDaoDstu2Test extends BaseJpaDstu2Test {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransactionCreateWithInvalidMatchUrl() {
|
||||
String methodName = "testTransactionCreateWithInvalidMatchUrl";
|
||||
Bundle request = new Bundle();
|
||||
|
||||
Patient p;
|
||||
p = new Patient();
|
||||
p.addIdentifier().setSystem("urn:system").setValue(methodName);
|
||||
EntryRequest entry = request.addEntry().setResource(p).getRequest().setMethod(HTTPVerbEnum.POST);
|
||||
|
||||
try {
|
||||
entry.setIfNoneExist("Patient?identifier identifier" + methodName);
|
||||
mySystemDao.transaction(request);
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("Failed to parse match URL[Patient?identifier identifiertestTransactionCreateWithInvalidMatchUrl] - URL is invalid (must not contain spaces)", e.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
entry.setIfNoneExist("Patient?identifier=");
|
||||
mySystemDao.transaction(request);
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("Invalid match URL[Patient?identifier=] - URL has no search parameters", e.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
entry.setIfNoneExist("Patient?foo=bar");
|
||||
mySystemDao.transaction(request);
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("Failed to parse match URL[Patient?foo=bar] - Resource type Patient does not have a parameter with name: foo", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void testTransactionCreateWithDuplicateMatchUrl02() {
|
||||
String methodName = "testTransactionCreateWithDuplicateMatchUrl02";
|
||||
Bundle request = new Bundle();
|
||||
|
|
|
@ -1619,6 +1619,20 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransaction() throws Exception {
|
||||
String contents = loadClasspath("/update.xml");
|
||||
HttpPost post = new HttpPost(ourServerBase);
|
||||
post.setEntity(new StringEntity(contents, ContentType.create("application/xml+fhir", "UTF-8")));
|
||||
CloseableHttpResponse resp = ourHttpClient.execute(post);
|
||||
try {
|
||||
assertEquals(200, resp.getStatusLine().getStatusCode());
|
||||
String output= IOUtils.toString(resp.getEntity().getContent());
|
||||
ourLog.info(output);
|
||||
} finally {
|
||||
resp.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateRejectsInvalidTypes() throws InterruptedException {
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Bundle xmlns="http://hl7.org/fhir">
|
||||
<id value="bundle-transaction"/>
|
||||
<meta>
|
||||
<lastUpdated value="2014-08-18T01:43:30Z"/>
|
||||
</meta>
|
||||
<type value="transaction"/>
|
||||
<entry>
|
||||
<fullUrl value="urn:uuid:61ebe359-bfdc-4613-8bf2-c5e300945f0b"/>
|
||||
<resource>
|
||||
<Medication>
|
||||
<code>
|
||||
<coding>
|
||||
<system value="http://snomed.info/sct"/>
|
||||
<code value="408036003"/>
|
||||
<display value="Rosuvastatin 10mg tablet"/>
|
||||
</coding>
|
||||
</code>
|
||||
</Medication>
|
||||
</resource>
|
||||
<request>
|
||||
<method value="POST"/>
|
||||
<url value="Medication"/>
|
||||
<ifNoneExist value="code=408036003"/>
|
||||
</request>o
|
||||
</entry>
|
||||
</Bundle>
|
||||
|
|
@ -9,6 +9,24 @@
|
|||
</encoder>
|
||||
</appender>
|
||||
|
||||
<appender name="SUBSCRIPTION_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
|
||||
<level>DEBUG</level>
|
||||
</filter>
|
||||
<file>${fhir.logdir}/subscription.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
|
||||
<fileNamePattern>${fhir.logdir}/subscription.%i.log.zip</fileNamePattern>
|
||||
<minIndex>1</minIndex>
|
||||
<maxIndex>10</maxIndex>
|
||||
</rollingPolicy>
|
||||
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
|
||||
<maxFileSize>5MB</maxFileSize>
|
||||
</triggeringPolicy>
|
||||
<encoder>
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%X{req.remoteAddr}] [%X{req.userAgent}] %-5level %logger{36} %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
|
||||
<level>DEBUG</level>
|
||||
|
@ -39,19 +57,25 @@
|
|||
</encoder>
|
||||
</appender>
|
||||
|
||||
<logger name="fhirtest.access" level="INFO" additivity="false">
|
||||
<appender-ref ref="ACCESS" />
|
||||
</logger>
|
||||
<logger name="fhirtest.access" level="INFO" additivity="false">
|
||||
<appender-ref ref="ACCESS"/>
|
||||
</logger>
|
||||
|
||||
<logger name="ca.uhn.fhir.jpa.dao.FhirResourceDaoSubscriptionDstu2" additivity="true" level="debug">
|
||||
<appender-ref ref="SUBSCRIPTION_FILE"/>
|
||||
</logger>
|
||||
|
||||
<logger name="ca.uhn.fhir.jpa.subscription" additivity="true" level="debug">
|
||||
<appender-ref ref="SUBSCRIPTION_FILE"/>
|
||||
</logger>
|
||||
|
||||
<logger name="org.springframework.web.socket.handler.ExceptionWebSocketHandlerDecorator" additivity="false" level="debug">
|
||||
<appender-ref ref="STDOUT" />
|
||||
<appender-ref ref="FILE" />
|
||||
<appender-ref ref="FILE"/>
|
||||
<appender-ref ref="SUBSCRIPTION_FILE"/>
|
||||
</logger>
|
||||
|
||||
|
||||
</appender>
|
||||
|
||||
<root level="INFO">
|
||||
<appender-ref ref="FILE" />
|
||||
<appender-ref ref="FILE"/>
|
||||
</root>
|
||||
|
||||
</configuration>
|
|
@ -115,6 +115,11 @@
|
|||
Correct issues with Android library. Thanks to
|
||||
Thomas Andersen for the submission!
|
||||
</action>
|
||||
<action type="fix">
|
||||
JPA server incorrectly rejected match URLs
|
||||
if they did not contain a question mark. Thanks
|
||||
to Bill de Beaubien for reporting!
|
||||
</action>
|
||||
</release>
|
||||
<release version="1.2" date="2015-09-18">
|
||||
<action type="add">
|
||||
|
|
Loading…
Reference in New Issue