diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/AuthenticationException.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/AuthenticationException.java
index c817ea9c420..4f35d2e6a5e 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/AuthenticationException.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/AuthenticationException.java
@@ -24,7 +24,24 @@ import ca.uhn.fhir.util.CoverageIgnore;
*/
/**
- * Represents an HTTP 401 Client Unauthorized response, which means that the client needs to provide credentials, or has provided invalid credentials.
+ * Represents an HTTP 401 Client Unauthorized response, which
+ * means that the client needs to provide credentials, or has
+ * provided invalid credentials.
+ *
+ * For security failures, you should use
+ * {@link AuthenticationException} if you want to indicate that the
+ * user could not be authenticated (e.g. credential failures), also
+ * known as an authentication failure.
+ * You should use {@link ForbiddenOperationException} if you want to
+ * indicate that the authenticated user does not have permission to
+ * perform the requested operation, also known as an authorization
+ * failure.
+ *
+ *
+ * Note that a complete list of RESTful exceptions is available in the Package
+ * Summary.
+ *
+
*/
@CoverageIgnore
public class AuthenticationException extends BaseServerResponseException {
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/ForbiddenOperationException.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/ForbiddenOperationException.java
index 0cfca6bfd0e..782d9f33a36 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/ForbiddenOperationException.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/ForbiddenOperationException.java
@@ -33,6 +33,16 @@ import ca.uhn.fhir.util.CoverageIgnore;
*
*
*
+ * For security failures, you should use
+ * {@link AuthenticationException} if you want to indicate that the
+ * user could not be authenticated (e.g. credential failures), also
+ * known as an authentication failure.
+ * You should use {@link ForbiddenOperationException} if you want to
+ * indicate that the authenticated user does not have permission to
+ * perform the requested operation, also known as an authorization
+ * failure.
+ *
+ *
* Note that a complete list of RESTful exceptions is available in the Package
* Summary.
*
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java
index 504bec5f0e2..84e2358a42e 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java
@@ -903,12 +903,19 @@ public abstract class BaseHapiFhirResourceDao extends B
updateEntity(theResource, theEntity, null, true, false, theEntity.getUpdatedDate());
}
+ @Override
+ public void removeTag(IIdType theId, TagTypeEnum theTagType, String theScheme, String theTerm) {
+ removeTag(theId, theTagType, theScheme, theTerm, null);
+ }
+
@Override
public void removeTag(IIdType theId, TagTypeEnum theTagType, String theScheme, String theTerm, RequestDetails theRequestDetails) {
// Notify interceptors
- ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, getResourceName(), theId);
- notifyInterceptors(RestOperationTypeEnum.DELETE_TAGS, requestDetails);
-
+ if (theRequestDetails != null) {
+ ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, getResourceName(), theId);
+ notifyInterceptors(RestOperationTypeEnum.DELETE_TAGS, requestDetails);
+ }
+
StopWatch w = new StopWatch();
BaseHasResource entity = readEntity(theId);
if (entity == null) {
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDao.java
index 977ca4dbf7d..5c1101145ef 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDao.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDao.java
@@ -170,7 +170,9 @@ public interface IFhirResourceDao extends IDao {
*/
void reindex(T theResource, ResourceTable theEntity);
- void removeTag(IIdType theId, TagTypeEnum theTagType, String theScheme, String theTerm, RequestDetails theRequestDetails);
+ void removeTag(IIdType theId, TagTypeEnum theTagType, String theSystem, String theCode, RequestDetails theRequestDetails);
+
+ void removeTag(IIdType theId, TagTypeEnum theTagType, String theSystem, String theCode);
IBundleProvider search(Map theParams);
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDaoPatient.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDaoPatient.java
index d0c1420293c..ffae43164bd 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDaoPatient.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDaoPatient.java
@@ -26,6 +26,7 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
+import ca.uhn.fhir.jpa.entity.TagTypeEnum;
import ca.uhn.fhir.rest.api.SortSpec;
import ca.uhn.fhir.rest.method.RequestDetails;
import ca.uhn.fhir.rest.param.DateRangeParam;
@@ -38,4 +39,5 @@ public interface IFhirResourceDaoPatient extends IFhirR
IBundleProvider patientTypeEverything(HttpServletRequest theServletRequest, IPrimitiveType theCount, DateRangeParam theLastUpdated, SortSpec theSortSpec, StringAndListParam theContent, StringAndListParam theNarrative, RequestDetails theRequestDetails);
+
}
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3Test.java
index 527f2828d6f..f8a56dfb10e 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3Test.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3Test.java
@@ -2592,6 +2592,103 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
}
+ @Test
+ public void testRemoveTag() {
+
+ String methodName = "testResourceMetaOperation";
+ IIdType id1, id2;
+ {
+ Patient patient = new Patient();
+ patient.addIdentifier().setSystem("urn:system").setValue(methodName);
+ patient.addName().setFamily("Tester").addGiven("Joe");
+ patient.getMeta().addTag(null, "Dog", "Puppies");
+
+ patient.getMeta().addSecurity().setSystem("seclabel:sys:1").setCode("seclabel:code:1").setDisplay("seclabel:dis:1");
+
+ patient.getMeta().addProfile(("http://profile/1"));
+
+ id1 = myPatientDao.create(patient, mySrd).getId();
+ }
+ {
+ Patient patient = new Patient();
+ patient.addIdentifier().setSystem("urn:system").setValue(methodName);
+ patient.addName().setFamily("Tester").addGiven("Joe");
+
+ patient.getMeta().addTag("http://foo", "Cat", "Kittens");
+ patient.getMeta().addSecurity().setSystem("seclabel:sys:2").setCode("seclabel:code:2").setDisplay("seclabel:dis:2");
+ patient.getMeta().addProfile("http://profile/2");
+
+ id2 = myPatientDao.create(patient, mySrd).getId();
+ }
+ {
+ Device device = new Device();
+ device.addIdentifier().setSystem("urn:system").setValue(methodName);
+ device.getMeta().addTag("http://foo", "Foo", "Bars");
+ device.getMeta().addSecurity().setSystem("seclabel:sys:3").setCode("seclabel:code:3").setDisplay("seclabel:dis:3");
+ device.getMeta().addProfile("http://profile/3");
+ myDeviceDao.create(device, mySrd);
+ }
+
+ Meta meta;
+
+ meta = myPatientDao.metaGetOperation(Meta.class, mySrd);
+ List published = meta.getTag();
+ assertEquals(2, published.size());
+ assertEquals(null, published.get(0).getSystem());
+ assertEquals("Dog", published.get(0).getCode());
+ assertEquals("Puppies", published.get(0).getDisplay());
+ assertEquals("http://foo", published.get(1).getSystem());
+ assertEquals("Cat", published.get(1).getCode());
+ assertEquals("Kittens", published.get(1).getDisplay());
+ List secLabels = meta.getSecurity();
+ assertEquals(2, secLabels.size());
+ assertEquals("seclabel:sys:1", secLabels.get(0).getSystemElement().getValue());
+ assertEquals("seclabel:code:1", secLabels.get(0).getCodeElement().getValue());
+ assertEquals("seclabel:dis:1", secLabels.get(0).getDisplayElement().getValue());
+ assertEquals("seclabel:sys:2", secLabels.get(1).getSystemElement().getValue());
+ assertEquals("seclabel:code:2", secLabels.get(1).getCodeElement().getValue());
+ assertEquals("seclabel:dis:2", secLabels.get(1).getDisplayElement().getValue());
+ List profiles = meta.getProfile();
+ assertEquals(2, profiles.size());
+ assertEquals("http://profile/1", profiles.get(0).getValue());
+ assertEquals("http://profile/2", profiles.get(1).getValue());
+
+ meta = myPatientDao.metaGetOperation(Meta.class, id2, mySrd);
+ published = meta.getTag();
+ assertEquals(1, published.size());
+ assertEquals("http://foo", published.get(0).getSystem());
+ assertEquals("Cat", published.get(0).getCode());
+ assertEquals("Kittens", published.get(0).getDisplay());
+ secLabels = meta.getSecurity();
+ assertEquals(1, secLabels.size());
+ assertEquals("seclabel:sys:2", secLabels.get(0).getSystemElement().getValue());
+ assertEquals("seclabel:code:2", secLabels.get(0).getCodeElement().getValue());
+ assertEquals("seclabel:dis:2", secLabels.get(0).getDisplayElement().getValue());
+ profiles = meta.getProfile();
+ assertEquals(1, profiles.size());
+ assertEquals("http://profile/2", profiles.get(0).getValue());
+
+ myPatientDao.removeTag(id1, TagTypeEnum.TAG, null, "Dog");
+ myPatientDao.removeTag(id1, TagTypeEnum.SECURITY_LABEL, "seclabel:sys:1", "seclabel:code:1");
+ myPatientDao.removeTag(id1, TagTypeEnum.PROFILE, BaseHapiFhirDao.NS_JPA_PROFILE, "http://profile/1");
+
+ meta = myPatientDao.metaGetOperation(Meta.class, mySrd);
+ published = meta.getTag();
+ assertEquals(1, published.size());
+ assertEquals("http://foo", published.get(0).getSystem());
+ assertEquals("Cat", published.get(0).getCode());
+ assertEquals("Kittens", published.get(0).getDisplay());
+ secLabels = meta.getSecurity();
+ assertEquals(1, secLabels.size());
+ assertEquals("seclabel:sys:2", secLabels.get(0).getSystemElement().getValue());
+ assertEquals("seclabel:code:2", secLabels.get(0).getCodeElement().getValue());
+ assertEquals("seclabel:dis:2", secLabels.get(0).getDisplayElement().getValue());
+ profiles = meta.getProfile();
+ assertEquals(1, profiles.size());
+ assertEquals("http://profile/2", profiles.get(0).getValue());
+
+ }
+
@Test
public void testReverseIncludes() {
String methodName = "testReverseIncludes";
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index c167e33d94f..eb010dfba1e 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -63,6 +63,11 @@
of literal ones, meaning that the server will not try to resolve these
URLs. Thanks to Eeva Turkka for the suggestion!
+
+ Add a utility method to JPA server:
+ IFhirResourceDao#removeTag(IIdType, TagTypeEnum, String, String)]]>. This allows client code to remove tags
+ from a resource without having a servlet request object in context.
+