Merge branch 'master' of github.com:jamesagnew/hapi-fhir
This commit is contained in:
commit
65598c200e
|
@ -87,4 +87,17 @@ public @interface Search {
|
|||
*/
|
||||
boolean dynamic() default false;
|
||||
|
||||
/**
|
||||
* In a REST server, should this method be invoked even if it does not have method parameters
|
||||
* which correspond to all of the URL parameters passed in by the client (default is <code>false</code>).
|
||||
* <p>
|
||||
* Use this method with caution: Methods marked with a value of <code>true</code> will
|
||||
* be greedy, meaning they may handle invocations you had intended to be handled by other
|
||||
* search methods. Such a method may be invoked as long as any method parameters
|
||||
* marked as {@link RequiredParam required} have been satisfied. If there are other methods
|
||||
* which have parameters marked as {@link OptionalParam optional} which would technically be
|
||||
* a better match, either the this method or the other method might be called.
|
||||
* </p>
|
||||
*/
|
||||
boolean allowUnknownParams() default false;
|
||||
}
|
||||
|
|
|
@ -37,7 +37,6 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
|
|||
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.api.annotation.Description;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
|
||||
|
@ -56,19 +55,18 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchMethodBinding.class);
|
||||
|
||||
private String myCompartmentName;
|
||||
private Class<? extends IBaseResource> myDeclaredResourceType;
|
||||
private String myDescription;
|
||||
private Integer myIdParamIndex;
|
||||
private String myQueryName;
|
||||
private boolean myAllowUnknownParams;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public SearchMethodBinding(Class<? extends IBaseResource> theReturnResourceType, Method theMethod, FhirContext theContext, Object theProvider) {
|
||||
super(theReturnResourceType, theMethod, theContext, theProvider);
|
||||
Search search = theMethod.getAnnotation(Search.class);
|
||||
this.myQueryName = StringUtils.defaultIfBlank(search.queryName(), null);
|
||||
this.myCompartmentName = StringUtils.defaultIfBlank(search.compartmentName(), null);
|
||||
this.myDeclaredResourceType = (Class<? extends IBaseResource>) theMethod.getReturnType();
|
||||
this.myIdParamIndex = MethodUtil.findIdParameterIndex(theMethod);
|
||||
this.myAllowUnknownParams = search.allowUnknownParams();
|
||||
|
||||
Description desc = theMethod.getAnnotation(Description.class);
|
||||
if (desc != null) {
|
||||
|
@ -232,17 +230,13 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
}
|
||||
}
|
||||
Set<String> keySet = theRequest.getParameters().keySet();
|
||||
for (String next : keySet) {
|
||||
// if (next.startsWith("_")) {
|
||||
// if (!SPECIAL_PARAM_NAMES.contains(next)) {
|
||||
// continue;
|
||||
// }
|
||||
// }
|
||||
if (!methodParamsTemp.contains(next)) {
|
||||
return false;
|
||||
if (myAllowUnknownParams == false) {
|
||||
for (String next : keySet) {
|
||||
if (!methodParamsTemp.contains(next)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -303,10 +297,6 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
public void setResourceType(Class<? extends IResource> resourceType) {
|
||||
this.myDeclaredResourceType = resourceType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getMethod().toString();
|
||||
|
|
|
@ -154,7 +154,7 @@ public class FhirTerser {
|
|||
}
|
||||
}
|
||||
|
||||
public BaseRuntimeChildDefinition getDefinition(Class<? extends IResource> theResourceType, String thePath) {
|
||||
public BaseRuntimeChildDefinition getDefinition(Class<? extends IBaseResource> theResourceType, String thePath) {
|
||||
RuntimeResourceDefinition def = myContext.getResourceDefinition(theResourceType);
|
||||
|
||||
BaseRuntimeElementCompositeDefinition<?> currentDef = def;
|
||||
|
|
|
@ -49,7 +49,6 @@ import javax.xml.stream.events.Characters;
|
|||
import javax.xml.stream.events.XMLEvent;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.apache.http.NameValuePair;
|
||||
import org.apache.http.client.utils.URLEncodedUtils;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
@ -175,7 +174,6 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
@PersistenceContext(type = PersistenceContextType.TRANSACTION)
|
||||
private EntityManager myEntityManager;
|
||||
|
||||
private List<IDaoListener> myListeners = new ArrayList<IDaoListener>();
|
||||
@Autowired
|
||||
private PlatformTransactionManager myPlatformTransactionManager;
|
||||
|
||||
|
@ -647,12 +645,6 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
return true;
|
||||
}
|
||||
|
||||
protected void notifyWriteCompleted() {
|
||||
for (IDaoListener next : myListeners) {
|
||||
next.writeCompleted();
|
||||
}
|
||||
}
|
||||
|
||||
protected void populateResourceIntoEntity(IResource theResource, ResourceTable theEntity) {
|
||||
theEntity.setResourceType(toResourceName(theResource));
|
||||
|
||||
|
@ -895,12 +887,6 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
return parameters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerDaoListener(IDaoListener theListener) {
|
||||
Validate.notNull(theListener, "theListener");
|
||||
myListeners.add(theListener);
|
||||
}
|
||||
|
||||
private void searchHistoryCurrentVersion(List<HistoryTuple> theTuples, List<BaseHasResource> theRetVal) {
|
||||
Collection<HistoryTuple> tuples = Collections2.filter(theTuples, new com.google.common.base.Predicate<HistoryTuple>() {
|
||||
@Override
|
||||
|
|
|
@ -95,6 +95,7 @@ import ca.uhn.fhir.jpa.entity.Search;
|
|||
import ca.uhn.fhir.jpa.entity.SearchResult;
|
||||
import ca.uhn.fhir.jpa.entity.TagDefinition;
|
||||
import ca.uhn.fhir.jpa.entity.TagTypeEnum;
|
||||
import ca.uhn.fhir.jpa.interceptor.IJpaServerInterceptor;
|
||||
import ca.uhn.fhir.jpa.util.StopWatch;
|
||||
import ca.uhn.fhir.model.api.IPrimitiveDatatype;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
|
@ -136,6 +137,7 @@ import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
|
|||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
|
||||
import ca.uhn.fhir.util.FhirTerser;
|
||||
import ca.uhn.fhir.util.ObjectUtil;
|
||||
|
@ -194,7 +196,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
|
|||
|
||||
myEntityManager.persist(newEntity);
|
||||
myEntityManager.merge(entity);
|
||||
notifyWriteCompleted();
|
||||
|
||||
ourLog.info("Processed addTag {}/{} on {} in {}ms", new Object[] { theScheme, theTerm, theId, w.getMillisAndRestart() });
|
||||
}
|
||||
|
||||
|
@ -258,7 +260,12 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
|
|||
Date updateTime = new Date();
|
||||
ResourceTable savedEntity = updateEntity(null, entity, true, updateTime, updateTime);
|
||||
|
||||
notifyWriteCompleted();
|
||||
// Notify JPA interceptors
|
||||
for (IServerInterceptor next : getConfig().getInterceptors()) {
|
||||
if (next instanceof IJpaServerInterceptor) {
|
||||
((IJpaServerInterceptor) next).resourceDeleted(requestDetails, entity);
|
||||
}
|
||||
}
|
||||
|
||||
ourLog.info("Processed delete on {} in {}ms", theId.getValue(), w.getMillisAndRestart());
|
||||
return toMethodOutcome(savedEntity, null);
|
||||
|
@ -299,7 +306,13 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
|
|||
// Perform delete
|
||||
Date updateTime = new Date();
|
||||
updateEntity(null, entity, true, updateTime, updateTime);
|
||||
notifyWriteCompleted();
|
||||
|
||||
// Notify JPA interceptors
|
||||
for (IServerInterceptor next : getConfig().getInterceptors()) {
|
||||
if (next instanceof IJpaServerInterceptor) {
|
||||
((IJpaServerInterceptor) next).resourceDeleted(requestDetails, entity);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -350,12 +363,18 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
|
|||
ActionRequestDetails requestDetails = new ActionRequestDetails(theResource.getId(), toResourceName(theResource), theResource);
|
||||
notifyInterceptors(RestOperationTypeEnum.CREATE, requestDetails);
|
||||
|
||||
// Perform actual DB update
|
||||
updateEntity(theResource, entity, false, null, thePerformIndexing, true, theUpdateTime);
|
||||
|
||||
// Notify JPA interceptors
|
||||
for (IServerInterceptor next : getConfig().getInterceptors()) {
|
||||
if (next instanceof IJpaServerInterceptor) {
|
||||
((IJpaServerInterceptor) next).resourceCreated(requestDetails, entity);
|
||||
}
|
||||
}
|
||||
|
||||
DaoMethodOutcome outcome = toMethodOutcome(entity, theResource).setCreated(true);
|
||||
|
||||
notifyWriteCompleted();
|
||||
|
||||
String msg = getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "successfulCreate", outcome.getId(), w.getMillisAndRestart());
|
||||
outcome.setOperationOutcome(createInfoOperationOutcome(msg));
|
||||
|
||||
|
@ -804,7 +823,6 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
|
|||
//@formatter:on
|
||||
|
||||
myEntityManager.merge(entity);
|
||||
notifyWriteCompleted();
|
||||
ourLog.info("Processed metaAddOperation on {} in {}ms", new Object[] { theResourceId, w.getMillisAndRestart() });
|
||||
|
||||
return metaGetOperation(theResourceId);
|
||||
|
@ -1563,7 +1581,12 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
|
|||
// Perform update
|
||||
ResourceTable savedEntity = updateEntity(theResource, entity, true, null, thePerformIndexing, true, new Date());
|
||||
|
||||
notifyWriteCompleted();
|
||||
// Notify JPA interceptors
|
||||
for (IServerInterceptor next : getConfig().getInterceptors()) {
|
||||
if (next instanceof IJpaServerInterceptor) {
|
||||
((IJpaServerInterceptor) next).resourceUpdated(requestDetails, entity);
|
||||
}
|
||||
}
|
||||
|
||||
DaoMethodOutcome outcome = toMethodOutcome(savedEntity, theResource).setCreated(false);
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ package ca.uhn.fhir.jpa.dao;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
@ -65,6 +66,9 @@ public class DaoConfig {
|
|||
* @see #setInterceptors(List)
|
||||
*/
|
||||
public List<IServerInterceptor> getInterceptors() {
|
||||
if (myInterceptors == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return myInterceptors;
|
||||
}
|
||||
|
||||
|
|
|
@ -40,6 +40,7 @@ import ca.uhn.fhir.util.FhirTerser;
|
|||
|
||||
public class FhirResourceDaoDstu1<T extends IResource> extends BaseHapiFhirResourceDao<T> {
|
||||
|
||||
@Override
|
||||
protected List<Object> getIncludeValues(FhirTerser t, Include next, IBaseResource nextResource, RuntimeResourceDefinition def) {
|
||||
List<Object> values;
|
||||
if ("*".equals(next.getValue())) {
|
||||
|
|
|
@ -278,8 +278,6 @@ public class FhirSystemDaoDstu1 extends BaseHapiFhirSystemDao<List<IResource>> {
|
|||
|
||||
oo.addIssue().setSeverity(IssueSeverityEnum.INFORMATION).setDetails("Transaction completed in " + delay + "ms with " + creations + " creations and " + updates + " updates");
|
||||
|
||||
notifyWriteCompleted();
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
|
|
@ -446,8 +446,6 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
|||
long delay = System.currentTimeMillis() - start;
|
||||
ourLog.info(theActionName + " completed in {}ms", new Object[] { delay });
|
||||
|
||||
notifyWriteCompleted();
|
||||
|
||||
response.setType(BundleTypeEnum.TRANSACTION_RESPONSE);
|
||||
return response;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
package ca.uhn.fhir.jpa.dao;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
|
||||
|
||||
|
@ -42,8 +40,4 @@ public interface IDao {
|
|||
}
|
||||
};
|
||||
|
||||
void registerDaoListener(IDaoListener theListener);
|
||||
|
||||
// void setResourceDaos(List<IFhirResourceDao<?>> theResourceDaos);
|
||||
|
||||
}
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
package ca.uhn.fhir.jpa.dao;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2015 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
public interface IDaoListener {
|
||||
|
||||
void writeCompleted();
|
||||
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
package ca.uhn.fhir.jpa.interceptor;
|
||||
|
||||
import ca.uhn.fhir.jpa.entity.ResourceTable;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2015 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
/**
|
||||
* Server interceptor for JPA DAOs which adds methods that will be called at certain points
|
||||
* in the operation lifecycle for JPA operations.
|
||||
*/
|
||||
public interface IJpaServerInterceptor extends IServerInterceptor {
|
||||
|
||||
/**
|
||||
* This method is invoked by the JPA DAOs when a resource has been newly created in the database.
|
||||
* It will be invoked within the current transaction scope.
|
||||
* <p>
|
||||
* This method is called after the
|
||||
* entity has been persisted and flushed to the database, so it is probably not a good
|
||||
* candidate for security decisions.
|
||||
* </p>
|
||||
*
|
||||
* @param theDetails The request details
|
||||
* @param theResourceTable The actual created entity
|
||||
*/
|
||||
void resourceCreated(ActionRequestDetails theDetails, ResourceTable theResourceTable);
|
||||
|
||||
/**
|
||||
* This method is invoked by the JPA DAOs when a resource has been updated in the database.
|
||||
* It will be invoked within the current transaction scope.
|
||||
* <p>
|
||||
* This method is called after the
|
||||
* entity has been persisted and flushed to the database, so it is probably not a good
|
||||
* candidate for security decisions.
|
||||
* </p>
|
||||
*
|
||||
* @param theDetails The request details
|
||||
* @param theResourceTable The actual updated entity
|
||||
*/
|
||||
void resourceUpdated(ActionRequestDetails theDetails, ResourceTable theResourceTable);
|
||||
|
||||
/**
|
||||
* This method is invoked by the JPA DAOs when a resource has been updated in the database.
|
||||
* It will be invoked within the current transaction scope.
|
||||
* <p>
|
||||
* This method is called after the
|
||||
* entity has been persisted and flushed to the database, so it is probably not a good
|
||||
* candidate for security decisions.
|
||||
* </p>
|
||||
*
|
||||
* @param theDetails The request details
|
||||
* @param theResourceTable The actual updated entity
|
||||
*/
|
||||
void resourceDeleted(ActionRequestDetails theDetails, ResourceTable theResourceTable);
|
||||
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package ca.uhn.fhir.jpa.interceptor;
|
||||
|
||||
import ca.uhn.fhir.jpa.entity.ResourceTable;
|
||||
import ca.uhn.fhir.rest.server.interceptor.InterceptorAdapter;
|
||||
|
||||
public class JpaServerInterceptorAdapter extends InterceptorAdapter implements IJpaServerInterceptor {
|
||||
|
||||
@Override
|
||||
public void resourceCreated(ActionRequestDetails theDetails, ResourceTable theResourceTable) {
|
||||
// nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resourceUpdated(ActionRequestDetails theDetails, ResourceTable theResourceTable) {
|
||||
// nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resourceDeleted(ActionRequestDetails theDetails, ResourceTable theResourceTable) {
|
||||
// nothing
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,156 @@
|
|||
package ca.uhn.fhir.jpa.dao;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
||||
import ca.uhn.fhir.jpa.entity.ResourceTable;
|
||||
import ca.uhn.fhir.jpa.interceptor.IJpaServerInterceptor;
|
||||
import ca.uhn.fhir.jpa.interceptor.JpaServerInterceptorAdapter;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
|
||||
|
||||
public class FhirResourceDaoDstu2InterceptorTest extends BaseJpaDstu2Test {
|
||||
|
||||
private IJpaServerInterceptor myJpaInterceptor;
|
||||
private JpaServerInterceptorAdapter myJpaInterceptorAdapter = new JpaServerInterceptorAdapter();
|
||||
|
||||
@After
|
||||
public void after() {
|
||||
myDaoConfig.getInterceptors().remove(myJpaInterceptor);
|
||||
myDaoConfig.getInterceptors().remove(myJpaInterceptorAdapter);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
myJpaInterceptor = mock(IJpaServerInterceptor.class);
|
||||
myDaoConfig.getInterceptors().add(myJpaInterceptor);
|
||||
myDaoConfig.getInterceptors().add(myJpaInterceptorAdapter);
|
||||
}
|
||||
|
||||
/*
|
||||
* *****************************************************
|
||||
* Note that non JPA specific operations get tested in individual
|
||||
* operation test methods too
|
||||
* *****************************************************
|
||||
*/
|
||||
|
||||
@Test
|
||||
public void testJpaCreate() {
|
||||
Patient p = new Patient();
|
||||
p.addName().addFamily("PATIENT");
|
||||
Long id = myPatientDao.create(p).getId().getIdPartAsLong();
|
||||
|
||||
ArgumentCaptor<ActionRequestDetails> detailsCapt;
|
||||
ArgumentCaptor<ResourceTable> tableCapt;
|
||||
|
||||
detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
|
||||
tableCapt = ArgumentCaptor.forClass(ResourceTable.class);
|
||||
verify(myJpaInterceptor, times(1)).resourceCreated(detailsCapt.capture(), tableCapt.capture());
|
||||
assertNotNull(tableCapt.getValue().getId());
|
||||
assertEquals(id, tableCapt.getValue().getId());
|
||||
|
||||
detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
|
||||
tableCapt = ArgumentCaptor.forClass(ResourceTable.class);
|
||||
verify(myJpaInterceptor, times(0)).resourceUpdated(detailsCapt.capture(), tableCapt.capture());
|
||||
|
||||
/*
|
||||
* Not do a conditional create
|
||||
*/
|
||||
p = new Patient();
|
||||
p.addName().addFamily("PATIENT1");
|
||||
Long id2 = myPatientDao.create(p, "Patient?family=PATIENT").getId().getIdPartAsLong();
|
||||
assertEquals(id, id2);
|
||||
|
||||
detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
|
||||
tableCapt = ArgumentCaptor.forClass(ResourceTable.class);
|
||||
verify(myJpaInterceptor, times(1)).resourceCreated(detailsCapt.capture(), tableCapt.capture());
|
||||
verify(myJpaInterceptor, times(0)).resourceUpdated(detailsCapt.capture(), tableCapt.capture());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJpaDelete() {
|
||||
Patient p = new Patient();
|
||||
p.addName().addFamily("PATIENT");
|
||||
Long id = myPatientDao.create(p).getId().getIdPartAsLong();
|
||||
|
||||
myPatientDao.delete(new IdDt("Patient", id));
|
||||
|
||||
ArgumentCaptor<ActionRequestDetails> detailsCapt;
|
||||
ArgumentCaptor<ResourceTable> tableCapt;
|
||||
|
||||
detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
|
||||
tableCapt = ArgumentCaptor.forClass(ResourceTable.class);
|
||||
verify(myJpaInterceptor, times(1)).resourceDeleted(detailsCapt.capture(), tableCapt.capture());
|
||||
assertNotNull(tableCapt.getValue().getId());
|
||||
assertEquals(id, tableCapt.getValue().getId());
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testJpaUpdate() {
|
||||
Patient p = new Patient();
|
||||
p.addName().addFamily("PATIENT");
|
||||
Long id = myPatientDao.create(p).getId().getIdPartAsLong();
|
||||
|
||||
p = new Patient();
|
||||
p.setId(new IdDt(id));
|
||||
p.addName().addFamily("PATIENT1");
|
||||
Long id2 = myPatientDao.update(p).getId().getIdPartAsLong();
|
||||
assertEquals(id, id2);
|
||||
|
||||
ArgumentCaptor<ActionRequestDetails> detailsCapt;
|
||||
ArgumentCaptor<ResourceTable> tableCapt;
|
||||
|
||||
detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
|
||||
tableCapt = ArgumentCaptor.forClass(ResourceTable.class);
|
||||
verify(myJpaInterceptor, times(1)).resourceUpdated(detailsCapt.capture(), tableCapt.capture());
|
||||
assertNotNull(tableCapt.getValue().getId());
|
||||
assertEquals(id, tableCapt.getValue().getId());
|
||||
|
||||
/*
|
||||
* Now do a conditional update
|
||||
*/
|
||||
|
||||
p = new Patient();
|
||||
p.setId(new IdDt(id));
|
||||
p.addName().addFamily("PATIENT2");
|
||||
id2 = myPatientDao.update(p, "Patient?family=PATIENT1").getId().getIdPartAsLong();
|
||||
assertEquals(id, id2);
|
||||
|
||||
detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
|
||||
tableCapt = ArgumentCaptor.forClass(ResourceTable.class);
|
||||
verify(myJpaInterceptor, times(1)).resourceCreated(detailsCapt.capture(), tableCapt.capture());
|
||||
verify(myJpaInterceptor, times(2)).resourceUpdated(detailsCapt.capture(), tableCapt.capture());
|
||||
assertEquals(id, tableCapt.getAllValues().get(2).getId());
|
||||
|
||||
/*
|
||||
* Now do a conditional update where none will match (so this is actually a create)
|
||||
*/
|
||||
|
||||
p = new Patient();
|
||||
p.addName().addFamily("PATIENT3");
|
||||
id2 = myPatientDao.update(p, "Patient?family=ZZZ").getId().getIdPartAsLong();
|
||||
assertNotEquals(id, id2);
|
||||
|
||||
detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
|
||||
tableCapt = ArgumentCaptor.forClass(ResourceTable.class);
|
||||
verify(myJpaInterceptor, times(2)).resourceUpdated(detailsCapt.capture(), tableCapt.capture());
|
||||
verify(myJpaInterceptor, times(2)).resourceCreated(detailsCapt.capture(), tableCapt.capture());
|
||||
assertEquals(id2, tableCapt.getAllValues().get(3).getId());
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -68,6 +68,7 @@ public class FhirServerConfig extends BaseJavaConfigDstu2 {
|
|||
|
||||
private Properties jpaProperties() {
|
||||
Properties extraProperties = new Properties();
|
||||
extraProperties.put("hibernate.dialect", org.hibernate.dialect.DerbyTenSevenDialect.class.getName());
|
||||
extraProperties.put("hibernate.format_sql", "true");
|
||||
extraProperties.put("hibernate.show_sql", "false");
|
||||
extraProperties.put("hibernate.hbm2ddl.auto", "update");
|
||||
|
|
4
pom.xml
4
pom.xml
|
@ -194,6 +194,10 @@
|
|||
<id>samlanfranchi</id>
|
||||
<name>Sam Lanfranchi</name>
|
||||
</developer>
|
||||
<developer>
|
||||
<id>jkiddo</id>
|
||||
<name>Jens Kristian Villadsen</name>
|
||||
</developer>
|
||||
</developers>
|
||||
|
||||
<licenses>
|
||||
|
|
|
@ -204,9 +204,14 @@
|
|||
@am202 for reporting!
|
||||
</action>
|
||||
<action type="fix" issue="245">
|
||||
FIx issue in testpage-overlay's new Java configuration where only the first
|
||||
Fix issue in testpage-overlay's new Java configuration where only the first
|
||||
configured server actually gets used.
|
||||
</action>
|
||||
<action type="add">
|
||||
Introduce
|
||||
<![CDATA[<a href="./apidocs-jpaserver/ca/uhn/fhir/jpa/dao/IJpaServerInterceptor.html">IJpaServerInterceptor</a>]]>
|
||||
interceptors for JPA server which can be used for more fine grained operations.
|
||||
</action>
|
||||
<action type="fix">
|
||||
Parser (XML and JSON) shouldn't encode an ID tag in resources
|
||||
which are part of a bundle when the resource has a UUID/OID
|
||||
|
|
|
@ -275,6 +275,33 @@
|
|||
</macro>
|
||||
|
||||
</section>
|
||||
|
||||
<section name="JPA Server Interceptors">
|
||||
|
||||
<p>
|
||||
The HAPI <a href="./doc_jpa.html">JPA Server</a> is an added layer on top of the HAPI
|
||||
REST server framework. If you are using it, you may wish to also register interceptors
|
||||
against the <a href="./apidocs-jpaserver/ca/uhn/fhir/jpa/dao/DaoConfig.html">DaoConfig</a>
|
||||
bean that you create using Spring configuration.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
By registering an interceptor against the DaoConfig, the server will invoke
|
||||
interceptor methods for operations such as <b>create</b>, <b>update</b>, etc even
|
||||
when these operations are found nested within a transaction. This is useful
|
||||
if you are using interceptors to make access control decisions because
|
||||
it avoids clients using transactions as a means of bypassing these controls.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
You may also choose to create interceptors which implement the
|
||||
more specialized
|
||||
<a href="./apidocs-jpaserver/ca/uhn/fhir/jpa/dao/IJpaServerInterceptor.html">IJpaServerInterceptor</a>
|
||||
interface, as this interceptor adds additional methods which are called during the JPA
|
||||
lifecycle.
|
||||
</p>
|
||||
|
||||
</section>
|
||||
|
||||
</body>
|
||||
|
||||
|
|
Loading…
Reference in New Issue