More work on getting conditional updates working
This commit is contained in:
parent
ecd3620e27
commit
0f2eb230e7
|
@ -694,6 +694,29 @@ public MethodOutcome createPatient(@ResourceParam Patient thePatient) {
|
||||||
//END SNIPPET: create
|
//END SNIPPET: create
|
||||||
|
|
||||||
|
|
||||||
|
//START SNIPPET: createConditional
|
||||||
|
@Create
|
||||||
|
public MethodOutcome createPatientConditional(
|
||||||
|
@ResourceParam Patient thePatient,
|
||||||
|
@ConditionalOperationParam String theConditionalUrl) {
|
||||||
|
|
||||||
|
if (theConditionalUrl != null) {
|
||||||
|
// We are doing a conditional create
|
||||||
|
|
||||||
|
// populate this with the ID of the existing resource which
|
||||||
|
// matches the conditional URL
|
||||||
|
return new MethodOutcome();
|
||||||
|
} else {
|
||||||
|
// We are doing a normal create
|
||||||
|
|
||||||
|
// populate this with the ID of the newly created resource
|
||||||
|
return new MethodOutcome();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
//END SNIPPET: createConditional
|
||||||
|
|
||||||
|
|
||||||
//START SNIPPET: createClient
|
//START SNIPPET: createClient
|
||||||
@Create
|
@Create
|
||||||
public abstract MethodOutcome createNewPatient(@ResourceParam Patient thePatient);
|
public abstract MethodOutcome createNewPatient(@ResourceParam Patient thePatient);
|
||||||
|
|
|
@ -92,7 +92,7 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
|
||||||
myMethod = theMethod;
|
myMethod = theMethod;
|
||||||
myContext = theContext;
|
myContext = theContext;
|
||||||
myProvider = theProvider;
|
myProvider = theProvider;
|
||||||
myParameters = MethodUtil.getResourceParameters(theContext, theMethod, theProvider);
|
myParameters = MethodUtil.getResourceParameters(theContext, theMethod, theProvider, getResourceOperationType());
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Class<?>> getAllowableParamAnnotations() {
|
public List<Class<?>> getAllowableParamAnnotations() {
|
||||||
|
|
|
@ -43,6 +43,7 @@ abstract class BaseOutcomeReturningMethodBindingWithResourceParam extends BaseOu
|
||||||
private String myResourceName;
|
private String myResourceName;
|
||||||
private boolean myBinary;
|
private boolean myBinary;
|
||||||
private Class<? extends IBaseResource> myResourceType;
|
private Class<? extends IBaseResource> myResourceType;
|
||||||
|
private Integer myIdParamIndex;
|
||||||
|
|
||||||
public BaseOutcomeReturningMethodBindingWithResourceParam(Method theMethod, FhirContext theContext, Class<?> theMethodAnnotation, Object theProvider) {
|
public BaseOutcomeReturningMethodBindingWithResourceParam(Method theMethod, FhirContext theContext, Class<?> theMethodAnnotation, Object theProvider) {
|
||||||
super(theMethod, theContext, theMethodAnnotation, theProvider);
|
super(theMethod, theContext, theMethodAnnotation, theProvider);
|
||||||
|
@ -78,6 +79,8 @@ abstract class BaseOutcomeReturningMethodBindingWithResourceParam extends BaseOu
|
||||||
}
|
}
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
myIdParamIndex = MethodUtil.findIdParameterIndex(theMethod);
|
||||||
|
|
||||||
if (resourceParameter == null) {
|
if (resourceParameter == null) {
|
||||||
throw new ConfigurationException("Method " + theMethod.getName() + " in type " + theMethod.getDeclaringClass().getCanonicalName() + " does not have a parameter annotated with @" + ResourceParam.class.getSimpleName());
|
throw new ConfigurationException("Method " + theMethod.getName() + " in type " + theMethod.getDeclaringClass().getCanonicalName() + " does not have a parameter annotated with @" + ResourceParam.class.getSimpleName());
|
||||||
|
@ -106,12 +109,11 @@ abstract class BaseOutcomeReturningMethodBindingWithResourceParam extends BaseOu
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* For subclasses to override
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
protected void addParametersForServerRequest(Request theRequest, Object[] theParams) {
|
protected void addParametersForServerRequest(Request theRequest, Object[] theParams) {
|
||||||
// nothing
|
if (myIdParamIndex != null) {
|
||||||
|
theParams[myIdParamIndex] = theRequest.getId();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -20,19 +20,28 @@ package ca.uhn.fhir.rest.method;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum;
|
||||||
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
|
|
||||||
class ConditionalParamBinder implements IParameter {
|
class ConditionalParamBinder implements IParameter {
|
||||||
|
|
||||||
ConditionalParamBinder() {
|
private RestfulOperationTypeEnum myOperationType;
|
||||||
super();
|
|
||||||
|
ConditionalParamBinder(RestfulOperationTypeEnum theOperationType) {
|
||||||
|
Validate.notNull(theOperationType, "theOperationType can not be null");
|
||||||
|
myOperationType = theOperationType;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -42,9 +51,34 @@ class ConditionalParamBinder implements IParameter {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object translateQueryParametersIntoServerArgument(Request theRequest, Object theRequestContents) throws InternalErrorException, InvalidRequestException {
|
public Object translateQueryParametersIntoServerArgument(Request theRequest, Object theRequestContents) throws InternalErrorException, InvalidRequestException {
|
||||||
|
|
||||||
|
if (myOperationType == RestfulOperationTypeEnum.CREATE) {
|
||||||
|
String retVal = theRequest.getServletRequest().getHeader(Constants.HEADER_IF_NONE_EXIST);
|
||||||
|
if (isBlank(retVal)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (retVal.startsWith(theRequest.getFhirServerBase())) {
|
||||||
|
retVal = retVal.substring(theRequest.getFhirServerBase().length());
|
||||||
|
}
|
||||||
|
return retVal;
|
||||||
|
} else if (myOperationType != RestfulOperationTypeEnum.DELETE && myOperationType != RestfulOperationTypeEnum.UPDATE) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
if (theRequest.getId() != null && theRequest.getId().hasIdPart()) {
|
if (theRequest.getId() != null && theRequest.getId().hasIdPart()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
boolean haveParam = false;
|
||||||
|
for (String next : theRequest.getParameters().keySet()) {
|
||||||
|
if (!next.startsWith("_")) {
|
||||||
|
haveParam=true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!haveParam) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
int questionMarkIndex = theRequest.getCompleteUrl().indexOf('?');
|
int questionMarkIndex = theRequest.getCompleteUrl().indexOf('?');
|
||||||
return theRequest.getResourceName() + theRequest.getCompleteUrl().substring(questionMarkIndex);
|
return theRequest.getResourceName() + theRequest.getCompleteUrl().substring(questionMarkIndex);
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,7 @@ import ca.uhn.fhir.model.api.Tag;
|
||||||
import ca.uhn.fhir.model.api.TagList;
|
import ca.uhn.fhir.model.api.TagList;
|
||||||
import ca.uhn.fhir.model.api.annotation.Description;
|
import ca.uhn.fhir.model.api.annotation.Description;
|
||||||
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
|
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
|
||||||
|
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum;
|
||||||
import ca.uhn.fhir.model.primitive.IdDt;
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||||
import ca.uhn.fhir.parser.IParser;
|
import ca.uhn.fhir.parser.IParser;
|
||||||
|
@ -300,7 +301,7 @@ public class MethodUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public static List<IParameter> getResourceParameters(FhirContext theContext, Method theMethod, Object theProvider) {
|
public static List<IParameter> getResourceParameters(FhirContext theContext, Method theMethod, Object theProvider, RestfulOperationTypeEnum theRestfulOperationTypeEnum) {
|
||||||
List<IParameter> parameters = new ArrayList<IParameter>();
|
List<IParameter> parameters = new ArrayList<IParameter>();
|
||||||
|
|
||||||
Class<?>[] parameterTypes = theMethod.getParameterTypes();
|
Class<?>[] parameterTypes = theMethod.getParameterTypes();
|
||||||
|
@ -396,7 +397,7 @@ public class MethodUtil {
|
||||||
} else if (nextAnnotation instanceof TransactionParam) {
|
} else if (nextAnnotation instanceof TransactionParam) {
|
||||||
param = new TransactionParamBinder(theContext);
|
param = new TransactionParamBinder(theContext);
|
||||||
} else if (nextAnnotation instanceof ConditionalOperationParam) {
|
} else if (nextAnnotation instanceof ConditionalOperationParam) {
|
||||||
param = new ConditionalParamBinder();
|
param = new ConditionalParamBinder(theRestfulOperationTypeEnum);
|
||||||
} else {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,7 @@ import java.util.Map;
|
||||||
import java.util.StringTokenizer;
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.UnavailableException;
|
||||||
import javax.servlet.http.HttpServlet;
|
import javax.servlet.http.HttpServlet;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
@ -814,6 +815,10 @@ public class RestfulServer extends HttpServlet {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method may be overridden by subclasses to do perform initialization that needs to be performed prior to the server being used.
|
* This method may be overridden by subclasses to do perform initialization that needs to be performed prior to the server being used.
|
||||||
|
*
|
||||||
|
* @throws ServletException If the initialization failed. Note that you should consider throwing
|
||||||
|
* {@link UnavailableException} (which extends {@link ServletException}), as this is a flag
|
||||||
|
* to the servlet container that the servlet is not usable.
|
||||||
*/
|
*/
|
||||||
protected void initialize() throws ServletException {
|
protected void initialize() throws ServletException {
|
||||||
// nothing by default
|
// nothing by default
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
package ca.uhn.fhir.jpa.entity;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* #%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%
|
|
||||||
*/
|
|
||||||
|
|
||||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
|
||||||
|
|
||||||
public class FhirPersister {
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
|
||||||
|
|
||||||
new ClassPathXmlApplicationContext("fhir-spring-config.xml");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -50,42 +50,22 @@ import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||||
|
|
||||||
public class JpaResourceProvider<T extends IResource> extends BaseJpaProvider implements IResourceProvider {
|
public abstract class BaseJpaResourceProvider<T extends IResource> extends BaseJpaProvider implements IResourceProvider {
|
||||||
|
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(JpaResourceProvider.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseJpaResourceProvider.class);
|
||||||
|
|
||||||
private FhirContext myContext;
|
private FhirContext myContext;
|
||||||
|
|
||||||
private IFhirResourceDao<T> myDao;
|
private IFhirResourceDao<T> myDao;
|
||||||
|
|
||||||
public JpaResourceProvider() {
|
public BaseJpaResourceProvider() {
|
||||||
// nothing
|
// nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
public JpaResourceProvider(IFhirResourceDao<T> theDao) {
|
public BaseJpaResourceProvider(IFhirResourceDao<T> theDao) {
|
||||||
myDao = theDao;
|
myDao = theDao;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Create
|
|
||||||
public MethodOutcome create(HttpServletRequest theRequest, @ResourceParam T theResource) {
|
|
||||||
startRequest(theRequest);
|
|
||||||
try {
|
|
||||||
return myDao.create(theResource);
|
|
||||||
} finally {
|
|
||||||
endRequest(theRequest);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Delete
|
|
||||||
public MethodOutcome delete(HttpServletRequest theRequest, @IdParam IdDt theResource) {
|
|
||||||
startRequest(theRequest);
|
|
||||||
try {
|
|
||||||
return myDao.delete(theResource);
|
|
||||||
} finally {
|
|
||||||
endRequest(theRequest);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public FhirContext getContext() {
|
public FhirContext getContext() {
|
||||||
return myContext;
|
return myContext;
|
||||||
}
|
}
|
||||||
|
@ -158,21 +138,6 @@ public class JpaResourceProvider<T extends IResource> extends BaseJpaProvider im
|
||||||
myDao = theDao;
|
myDao = theDao;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Update
|
|
||||||
public MethodOutcome update(HttpServletRequest theRequest, @ResourceParam T theResource, @IdParam IdDt theId) {
|
|
||||||
startRequest(theRequest);
|
|
||||||
try {
|
|
||||||
theResource.setId(theId);
|
|
||||||
return myDao.update(theResource);
|
|
||||||
} catch (ResourceNotFoundException e) {
|
|
||||||
ourLog.info("Can't update resource with ID[" + theId.getValue() + "] because it doesn't exist, going to create it instead");
|
|
||||||
theResource.setId(theId);
|
|
||||||
return myDao.create(theResource);
|
|
||||||
} finally {
|
|
||||||
endRequest(theRequest);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Validate
|
@Validate
|
||||||
public MethodOutcome validate(HttpServletRequest theRequest, @ResourceParam T theResource) {
|
public MethodOutcome validate(HttpServletRequest theRequest, @ResourceParam T theResource) {
|
||||||
startRequest(theRequest);
|
startRequest(theRequest);
|
|
@ -0,0 +1,83 @@
|
||||||
|
package ca.uhn.fhir.jpa.provider;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* #%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%
|
||||||
|
*/
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
|
||||||
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Create;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Delete;
|
||||||
|
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||||
|
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Update;
|
||||||
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||||
|
|
||||||
|
public class JpaResourceProviderDstu1<T extends IResource> extends BaseJpaResourceProvider<T> {
|
||||||
|
|
||||||
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(JpaResourceProviderDstu1.class);
|
||||||
|
|
||||||
|
public JpaResourceProviderDstu1() {
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
public JpaResourceProviderDstu1(IFhirResourceDao<T> theDao) {
|
||||||
|
super(theDao);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Create
|
||||||
|
public MethodOutcome create(HttpServletRequest theRequest, @ResourceParam T theResource) {
|
||||||
|
startRequest(theRequest);
|
||||||
|
try {
|
||||||
|
return getDao().create(theResource);
|
||||||
|
} finally {
|
||||||
|
endRequest(theRequest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Delete
|
||||||
|
public MethodOutcome delete(HttpServletRequest theRequest, @IdParam IdDt theResource) {
|
||||||
|
startRequest(theRequest);
|
||||||
|
try {
|
||||||
|
return getDao().delete(theResource);
|
||||||
|
} finally {
|
||||||
|
endRequest(theRequest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Update
|
||||||
|
public MethodOutcome update(HttpServletRequest theRequest, @ResourceParam T theResource, @IdParam IdDt theId) {
|
||||||
|
startRequest(theRequest);
|
||||||
|
try {
|
||||||
|
theResource.setId(theId);
|
||||||
|
return getDao().update(theResource);
|
||||||
|
} catch (ResourceNotFoundException e) {
|
||||||
|
ourLog.info("Can't update resource with ID[" + theId.getValue() + "] because it doesn't exist, going to create it instead");
|
||||||
|
theResource.setId(theId);
|
||||||
|
return getDao().create(theResource);
|
||||||
|
} finally {
|
||||||
|
endRequest(theRequest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
package ca.uhn.fhir.jpa.provider;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* #%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%
|
||||||
|
*/
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
|
||||||
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
|
import ca.uhn.fhir.rest.annotation.ConditionalOperationParam;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Create;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Delete;
|
||||||
|
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||||
|
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Update;
|
||||||
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||||
|
|
||||||
|
public class JpaResourceProviderDstu2<T extends IResource> extends BaseJpaResourceProvider<T> {
|
||||||
|
|
||||||
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(JpaResourceProviderDstu2.class);
|
||||||
|
|
||||||
|
public JpaResourceProviderDstu2() {
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
public JpaResourceProviderDstu2(IFhirResourceDao<T> theDao) {
|
||||||
|
super(theDao);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Create
|
||||||
|
public MethodOutcome create(HttpServletRequest theRequest, @ResourceParam T theResource, @ConditionalOperationParam String theConditional) {
|
||||||
|
startRequest(theRequest);
|
||||||
|
try {
|
||||||
|
if (theConditional != null) {
|
||||||
|
return getDao().create(theResource, theConditional);
|
||||||
|
} else {
|
||||||
|
return getDao().create(theResource);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
endRequest(theRequest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Delete
|
||||||
|
public MethodOutcome delete(HttpServletRequest theRequest, @IdParam IdDt theResource, @ConditionalOperationParam String theConditional) {
|
||||||
|
startRequest(theRequest);
|
||||||
|
try {
|
||||||
|
if (theConditional != null) {
|
||||||
|
return getDao().deleteByUrl(theConditional);
|
||||||
|
} else {
|
||||||
|
return getDao().delete(theResource);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
endRequest(theRequest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Update
|
||||||
|
public MethodOutcome update(HttpServletRequest theRequest, @ResourceParam T theResource, @IdParam IdDt theId, @ConditionalOperationParam String theConditional) {
|
||||||
|
startRequest(theRequest);
|
||||||
|
try {
|
||||||
|
if (theConditional != null) {
|
||||||
|
return getDao().update(theResource, theConditional);
|
||||||
|
} else {
|
||||||
|
theResource.setId(theId);
|
||||||
|
return getDao().update(theResource);
|
||||||
|
}
|
||||||
|
} catch (ResourceNotFoundException e) {
|
||||||
|
ourLog.info("Can't update resource with ID[" + theId.getValue() + "] because it doesn't exist, going to create it instead");
|
||||||
|
theResource.setId(theId);
|
||||||
|
return getDao().create(theResource);
|
||||||
|
} finally {
|
||||||
|
endRequest(theRequest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,10 +1,9 @@
|
||||||
package ca.uhn.fhir.jpa.provider;
|
package ca.uhn.fhir.jpa.provider;
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.provider.JpaResourceProvider;
|
|
||||||
import ca.uhn.fhir.model.api.IResource;
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
import ca.uhn.fhir.model.dstu.resource.Questionnaire;
|
import ca.uhn.fhir.model.dstu.resource.Questionnaire;
|
||||||
|
|
||||||
public class QuestionnaireResourceProvider extends JpaResourceProvider<Questionnaire> {
|
public class QuestionnaireResourceProvider extends JpaResourceProviderDstu1<Questionnaire> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Class<? extends IResource> getResourceType() {
|
public Class<? extends IResource> getResourceType() {
|
||||||
|
|
|
@ -1,13 +1,29 @@
|
||||||
package ca.uhn.fhir.jpa.provider;
|
package ca.uhn.fhir.jpa.provider;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.*;
|
import static org.hamcrest.Matchers.containsString;
|
||||||
import static org.junit.Assert.*;
|
import static org.hamcrest.Matchers.endsWith;
|
||||||
|
import static org.hamcrest.Matchers.greaterThan;
|
||||||
|
import static org.hamcrest.Matchers.not;
|
||||||
|
import static org.hamcrest.Matchers.startsWith;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||||
|
import org.apache.http.client.methods.HttpPost;
|
||||||
|
import org.apache.http.entity.ContentType;
|
||||||
|
import org.apache.http.entity.StringEntity;
|
||||||
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
|
import org.apache.http.impl.client.HttpClientBuilder;
|
||||||
|
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
||||||
import org.eclipse.jetty.server.Server;
|
import org.eclipse.jetty.server.Server;
|
||||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||||
import org.eclipse.jetty.servlet.ServletHolder;
|
import org.eclipse.jetty.servlet.ServletHolder;
|
||||||
|
@ -45,6 +61,7 @@ import ca.uhn.fhir.rest.client.IGenericClient;
|
||||||
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
|
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
|
||||||
import ca.uhn.fhir.rest.gclient.StringClientParam;
|
import ca.uhn.fhir.rest.gclient.StringClientParam;
|
||||||
import ca.uhn.fhir.rest.gclient.TokenClientParam;
|
import ca.uhn.fhir.rest.gclient.TokenClientParam;
|
||||||
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider;
|
import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider;
|
||||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||||
|
@ -63,6 +80,8 @@ public class ResourceProviderDstu2Test {
|
||||||
private static Server ourServer;
|
private static Server ourServer;
|
||||||
private static IFhirResourceDao<Organization> ourOrganizationDao;
|
private static IFhirResourceDao<Organization> ourOrganizationDao;
|
||||||
private static DaoConfig ourDaoConfig;
|
private static DaoConfig ourDaoConfig;
|
||||||
|
private static CloseableHttpClient ourHttpClient;
|
||||||
|
private static String ourServerBase;
|
||||||
|
|
||||||
// private static JpaConformanceProvider ourConfProvider;
|
// private static JpaConformanceProvider ourConfProvider;
|
||||||
|
|
||||||
|
@ -92,9 +111,24 @@ public class ResourceProviderDstu2Test {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testTryToCreateResourceWithNumericId() {
|
@Test
|
||||||
|
public void testCreateResourceWithNumericId() throws IOException {
|
||||||
String resource = "<Patient xmlns=\"http://hl7.org/fhir\"><id value=\"1777\"/><meta><versionId value=\"1\"/><lastUpdated value=\"2015-02-25T15:47:48Z\"/></meta></Patient>";
|
String resource = "<Patient xmlns=\"http://hl7.org/fhir\"><id value=\"1777\"/><meta><versionId value=\"1\"/><lastUpdated value=\"2015-02-25T15:47:48Z\"/></meta></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());
|
||||||
|
assertThat(response.getFirstHeader(Constants.HEADER_LOCATION_LC).getValue(), startsWith(ourServerBase + "/Patient/"));
|
||||||
|
assertThat(response.getFirstHeader(Constants.HEADER_LOCATION_LC).getValue(), endsWith("/_history/1"));
|
||||||
|
assertThat(response.getFirstHeader(Constants.HEADER_LOCATION_LC).getValue(), not(containsString("1777")));
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
response.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -523,6 +557,7 @@ public class ResourceProviderDstu2Test {
|
||||||
public static void afterClass() throws Exception {
|
public static void afterClass() throws Exception {
|
||||||
ourServer.stop();
|
ourServer.stop();
|
||||||
ourAppCtx.stop();
|
ourAppCtx.stop();
|
||||||
|
ourHttpClient.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
|
@ -534,7 +569,7 @@ public class ResourceProviderDstu2Test {
|
||||||
ourFhirCtx = FhirContext.forDstu2();
|
ourFhirCtx = FhirContext.forDstu2();
|
||||||
restServer.setFhirContext(ourFhirCtx);
|
restServer.setFhirContext(ourFhirCtx);
|
||||||
|
|
||||||
String serverBase = "http://localhost:" + port + "/fhir/context";
|
ourServerBase = "http://localhost:" + port + "/fhir/context";
|
||||||
|
|
||||||
ourAppCtx = new ClassPathXmlApplicationContext("hapi-fhir-server-resourceproviders-dstu2.xml", "fhir-jpabase-spring-test-config.xml");
|
ourAppCtx = new ClassPathXmlApplicationContext("hapi-fhir-server-resourceproviders-dstu2.xml", "fhir-jpabase-spring-test-config.xml");
|
||||||
|
|
||||||
|
@ -565,9 +600,13 @@ public class ResourceProviderDstu2Test {
|
||||||
ourServer.start();
|
ourServer.start();
|
||||||
|
|
||||||
ourFhirCtx.getRestfulClientFactory().setSocketTimeout(600 * 1000);
|
ourFhirCtx.getRestfulClientFactory().setSocketTimeout(600 * 1000);
|
||||||
ourClient = ourFhirCtx.newRestfulGenericClient(serverBase);
|
ourClient = ourFhirCtx.newRestfulGenericClient(ourServerBase);
|
||||||
ourClient.registerInterceptor(new LoggingInterceptor(true));
|
ourClient.registerInterceptor(new LoggingInterceptor(true));
|
||||||
|
|
||||||
|
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
|
||||||
|
HttpClientBuilder builder = HttpClientBuilder.create();
|
||||||
|
builder.setConnectionManager(connectionManager);
|
||||||
|
ourHttpClient = builder.build();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -145,9 +145,9 @@ public class PagingTest {
|
||||||
assertEquals(2, bundle.getEntries().size());
|
assertEquals(2, bundle.getEntries().size());
|
||||||
assertEquals("2", bundle.getEntries().get(0).getId().getIdPart());
|
assertEquals("2", bundle.getEntries().get(0).getId().getIdPart());
|
||||||
assertEquals("3", bundle.getEntries().get(1).getId().getIdPart());
|
assertEquals("3", bundle.getEntries().get(1).getId().getIdPart());
|
||||||
assertEquals(base + '?' + Constants.PARAM_PAGINGACTION + "=ABCD&" + Constants.PARAM_PAGINGOFFSET + "=4&" + Constants.PARAM_COUNT + "=2", bundle.getLinkNext().getValue());
|
assertEquals(base + '?' + Constants.PARAM_PAGINGACTION + "=ABCD&" + Constants.PARAM_PAGINGOFFSET + "=4&" + Constants.PARAM_COUNT + "=2&_format=xml", bundle.getLinkNext().getValue());
|
||||||
assertEquals(base + '/' + '?' + Constants.PARAM_PAGINGACTION + "=ABCD&" + Constants.PARAM_PAGINGOFFSET + "=2&" + Constants.PARAM_COUNT + "=2", bundle.getLinkSelf().getValue());
|
assertEquals(base + '/' + '?' + Constants.PARAM_PAGINGACTION + "=ABCD&" + Constants.PARAM_PAGINGOFFSET + "=2&" + Constants.PARAM_COUNT + "=2&_format=xml", bundle.getLinkSelf().getValue());
|
||||||
assertEquals(base + '?' + Constants.PARAM_PAGINGACTION + "=ABCD&" + Constants.PARAM_PAGINGOFFSET + "=0&" + Constants.PARAM_COUNT + "=2", bundle.getLinkPrevious().getValue());
|
assertEquals(base + '?' + Constants.PARAM_PAGINGACTION + "=ABCD&" + Constants.PARAM_PAGINGOFFSET + "=0&" + Constants.PARAM_COUNT + "=2&_format=xml", bundle.getLinkPrevious().getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,200 @@
|
||||||
|
package ca.uhn.fhir.rest.server;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.http.HttpResponse;
|
||||||
|
import org.apache.http.client.methods.HttpGet;
|
||||||
|
import org.apache.http.client.methods.HttpPost;
|
||||||
|
import org.apache.http.client.methods.HttpPut;
|
||||||
|
import org.apache.http.entity.ContentType;
|
||||||
|
import org.apache.http.entity.StringEntity;
|
||||||
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
|
import org.apache.http.impl.client.HttpClientBuilder;
|
||||||
|
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
||||||
|
import org.eclipse.jetty.server.Server;
|
||||||
|
import org.eclipse.jetty.servlet.ServletHandler;
|
||||||
|
import org.eclipse.jetty.servlet.ServletHolder;
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
|
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
|
||||||
|
import ca.uhn.fhir.model.api.TagList;
|
||||||
|
import ca.uhn.fhir.model.dstu2.resource.DiagnosticOrder;
|
||||||
|
import ca.uhn.fhir.model.dstu2.resource.DiagnosticReport;
|
||||||
|
import ca.uhn.fhir.model.dstu2.resource.Observation;
|
||||||
|
import ca.uhn.fhir.model.dstu2.resource.OperationOutcome;
|
||||||
|
import ca.uhn.fhir.model.dstu2.resource.Organization;
|
||||||
|
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||||
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
|
import ca.uhn.fhir.model.primitive.StringDt;
|
||||||
|
import ca.uhn.fhir.rest.annotation.ConditionalOperationParam;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Create;
|
||||||
|
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||||
|
import ca.uhn.fhir.rest.annotation.OptionalParam;
|
||||||
|
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Search;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Update;
|
||||||
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
|
import ca.uhn.fhir.util.PortUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by dsotnikov on 2/25/2014.
|
||||||
|
*/
|
||||||
|
public class CreateConditionalTest {
|
||||||
|
private static CloseableHttpClient ourClient;
|
||||||
|
private static String ourLastConditionalUrl;
|
||||||
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CreateConditionalTest.class);
|
||||||
|
private static int ourPort;
|
||||||
|
|
||||||
|
private static Server ourServer;
|
||||||
|
private static IdDt ourLastId;
|
||||||
|
private static IdDt ourLastIdParam;
|
||||||
|
private static boolean ourLastRequestWasSearch;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void before() {
|
||||||
|
ourLastId = null;
|
||||||
|
ourLastConditionalUrl = null;
|
||||||
|
ourLastIdParam = null;
|
||||||
|
ourLastRequestWasSearch = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreateWithConditionalUrl() throws Exception {
|
||||||
|
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.addIdentifier().setValue("002");
|
||||||
|
|
||||||
|
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient");
|
||||||
|
httpPost.addHeader(Constants.HEADER_IF_NONE_EXIST, "Patient?identifier=system%7C001");
|
||||||
|
httpPost.setEntity(new StringEntity(new FhirContext().newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
|
||||||
|
|
||||||
|
HttpResponse status = ourClient.execute(httpPost);
|
||||||
|
|
||||||
|
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
|
||||||
|
ourLog.info("Response was:\n{}", responseContent);
|
||||||
|
|
||||||
|
assertEquals(201, status.getStatusLine().getStatusCode());
|
||||||
|
assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("location").getValue());
|
||||||
|
assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue());
|
||||||
|
|
||||||
|
assertNull(ourLastId.getValue());
|
||||||
|
assertNull(ourLastIdParam);
|
||||||
|
assertEquals("Patient?identifier=system%7C001", ourLastConditionalUrl);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreateWithoutConditionalUrl() throws Exception {
|
||||||
|
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.addIdentifier().setValue("002");
|
||||||
|
|
||||||
|
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/2?_format=true&_pretty=true");
|
||||||
|
httpPost.setEntity(new StringEntity(new FhirContext().newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
|
||||||
|
|
||||||
|
HttpResponse status = ourClient.execute(httpPost);
|
||||||
|
|
||||||
|
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
|
||||||
|
ourLog.info("Response was:\n{}", responseContent);
|
||||||
|
|
||||||
|
assertEquals(201, status.getStatusLine().getStatusCode());
|
||||||
|
assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("location").getValue());
|
||||||
|
assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue());
|
||||||
|
|
||||||
|
assertEquals("Patient/2", ourLastId.toUnqualified().getValue());
|
||||||
|
assertEquals("Patient/2", ourLastIdParam.toUnqualified().getValue());
|
||||||
|
assertNull(ourLastConditionalUrl);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSearchStillWorks() throws Exception {
|
||||||
|
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.addIdentifier().setValue("002");
|
||||||
|
|
||||||
|
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_pretty=true");
|
||||||
|
|
||||||
|
HttpResponse status = ourClient.execute(httpGet);
|
||||||
|
|
||||||
|
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
|
||||||
|
ourLog.info("Response was:\n{}", responseContent);
|
||||||
|
|
||||||
|
assertTrue(ourLastRequestWasSearch);
|
||||||
|
assertNull(ourLastId);
|
||||||
|
assertNull(ourLastIdParam);
|
||||||
|
assertNull(ourLastConditionalUrl);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void afterClass() throws Exception {
|
||||||
|
ourServer.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void beforeClass() throws Exception {
|
||||||
|
ourPort = PortUtil.findFreePort();
|
||||||
|
ourServer = new Server(ourPort);
|
||||||
|
|
||||||
|
PatientProvider patientProvider = new PatientProvider();
|
||||||
|
|
||||||
|
ServletHandler proxyHandler = new ServletHandler();
|
||||||
|
RestfulServer servlet = new RestfulServer();
|
||||||
|
servlet.setResourceProviders(patientProvider);
|
||||||
|
ServletHolder servletHolder = new ServletHolder(servlet);
|
||||||
|
proxyHandler.addServletWithMapping(servletHolder, "/*");
|
||||||
|
ourServer.setHandler(proxyHandler);
|
||||||
|
ourServer.start();
|
||||||
|
|
||||||
|
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
|
||||||
|
HttpClientBuilder builder = HttpClientBuilder.create();
|
||||||
|
builder.setConnectionManager(connectionManager);
|
||||||
|
ourClient = builder.build();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PatientProvider implements IResourceProvider {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<? extends IResource> getResourceType() {
|
||||||
|
return Patient.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Search
|
||||||
|
public List<IResource> search(@OptionalParam(name="foo") StringDt theString) {
|
||||||
|
ourLastRequestWasSearch = true;
|
||||||
|
return new ArrayList<IResource>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Create()
|
||||||
|
public MethodOutcome createPatient(@ResourceParam Patient thePatient, @ConditionalOperationParam String theConditional, @IdParam IdDt theIdParam) {
|
||||||
|
ourLastConditionalUrl = theConditional;
|
||||||
|
ourLastId = thePatient.getId();
|
||||||
|
ourLastIdParam = theIdParam;
|
||||||
|
return new MethodOutcome(new IdDt("Patient/001/_history/002"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -2,10 +2,13 @@ package ca.uhn.fhir.rest.server;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.apache.http.HttpResponse;
|
import org.apache.http.HttpResponse;
|
||||||
|
import org.apache.http.client.methods.HttpGet;
|
||||||
import org.apache.http.client.methods.HttpPut;
|
import org.apache.http.client.methods.HttpPut;
|
||||||
import org.apache.http.entity.ContentType;
|
import org.apache.http.entity.ContentType;
|
||||||
import org.apache.http.entity.StringEntity;
|
import org.apache.http.entity.StringEntity;
|
||||||
|
@ -31,9 +34,12 @@ import ca.uhn.fhir.model.dstu2.resource.OperationOutcome;
|
||||||
import ca.uhn.fhir.model.dstu2.resource.Organization;
|
import ca.uhn.fhir.model.dstu2.resource.Organization;
|
||||||
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||||
import ca.uhn.fhir.model.primitive.IdDt;
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
|
import ca.uhn.fhir.model.primitive.StringDt;
|
||||||
import ca.uhn.fhir.rest.annotation.ConditionalOperationParam;
|
import ca.uhn.fhir.rest.annotation.ConditionalOperationParam;
|
||||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||||
|
import ca.uhn.fhir.rest.annotation.OptionalParam;
|
||||||
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Search;
|
||||||
import ca.uhn.fhir.rest.annotation.Update;
|
import ca.uhn.fhir.rest.annotation.Update;
|
||||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
import ca.uhn.fhir.util.PortUtil;
|
import ca.uhn.fhir.util.PortUtil;
|
||||||
|
@ -50,6 +56,7 @@ public class UpdateConditionalTest {
|
||||||
private static Server ourServer;
|
private static Server ourServer;
|
||||||
private static IdDt ourLastId;
|
private static IdDt ourLastId;
|
||||||
private static IdDt ourLastIdParam;
|
private static IdDt ourLastIdParam;
|
||||||
|
private static boolean ourLastRequestWasSearch;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -58,6 +65,7 @@ public class UpdateConditionalTest {
|
||||||
ourLastId = null;
|
ourLastId = null;
|
||||||
ourLastConditionalUrl = null;
|
ourLastConditionalUrl = null;
|
||||||
ourLastIdParam = null;
|
ourLastIdParam = null;
|
||||||
|
ourLastRequestWasSearch = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -112,6 +120,29 @@ public class UpdateConditionalTest {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSearchStillWorks() throws Exception {
|
||||||
|
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.addIdentifier().setValue("002");
|
||||||
|
|
||||||
|
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_pretty=true");
|
||||||
|
|
||||||
|
HttpResponse status = ourClient.execute(httpGet);
|
||||||
|
|
||||||
|
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
|
||||||
|
ourLog.info("Response was:\n{}", responseContent);
|
||||||
|
|
||||||
|
assertTrue(ourLastRequestWasSearch);
|
||||||
|
assertNull(ourLastId);
|
||||||
|
assertNull(ourLastIdParam);
|
||||||
|
assertNull(ourLastConditionalUrl);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@AfterClass
|
@AfterClass
|
||||||
public static void afterClass() throws Exception {
|
public static void afterClass() throws Exception {
|
||||||
ourServer.stop();
|
ourServer.stop();
|
||||||
|
@ -147,6 +178,11 @@ public class UpdateConditionalTest {
|
||||||
return Patient.class;
|
return Patient.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Search
|
||||||
|
public List<IResource> search(@OptionalParam(name="foo") StringDt theString) {
|
||||||
|
ourLastRequestWasSearch = true;
|
||||||
|
return new ArrayList<IResource>();
|
||||||
|
}
|
||||||
|
|
||||||
@Update()
|
@Update()
|
||||||
public MethodOutcome updatePatient(@ResourceParam Patient thePatient, @ConditionalOperationParam String theConditional, @IdParam IdDt theIdParam) {
|
public MethodOutcome updatePatient(@ResourceParam Patient thePatient, @ConditionalOperationParam String theConditional, @IdParam IdDt theIdParam) {
|
||||||
|
|
|
@ -19,6 +19,7 @@ import java.util.Set;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
|
|
||||||
|
import org.apache.commons.lang.WordUtils;
|
||||||
import org.apache.commons.lang3.ObjectUtils;
|
import org.apache.commons.lang3.ObjectUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.maven.plugin.MojoFailureException;
|
import org.apache.maven.plugin.MojoFailureException;
|
||||||
|
@ -444,6 +445,12 @@ public abstract class BaseStructureParser {
|
||||||
ctx.put("includes", (theResource.getIncludes()));
|
ctx.put("includes", (theResource.getIncludes()));
|
||||||
ctx.put("esc", new EscapeTool());
|
ctx.put("esc", new EscapeTool());
|
||||||
|
|
||||||
|
String capitalize = WordUtils.capitalize(myVersion);
|
||||||
|
if ("Dstu".equals(capitalize)) {
|
||||||
|
capitalize="Dstu1";
|
||||||
|
}
|
||||||
|
ctx.put("versionCapitalized", capitalize);
|
||||||
|
|
||||||
VelocityEngine v = new VelocityEngine();
|
VelocityEngine v = new VelocityEngine();
|
||||||
v.setProperty("resource.loader", "cp");
|
v.setProperty("resource.loader", "cp");
|
||||||
v.setProperty("cp.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
|
v.setProperty("cp.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
|
||||||
|
@ -525,6 +532,13 @@ public abstract class BaseStructureParser {
|
||||||
ctx.put("version", myVersion);
|
ctx.put("version", myVersion);
|
||||||
ctx.put("versionEnumName", determineVersionEnum().name());
|
ctx.put("versionEnumName", determineVersionEnum().name());
|
||||||
ctx.put("esc", new EscapeTool());
|
ctx.put("esc", new EscapeTool());
|
||||||
|
|
||||||
|
String capitalize = WordUtils.capitalize(myVersion);
|
||||||
|
if ("Dstu".equals(capitalize)) {
|
||||||
|
capitalize="Dstu1";
|
||||||
|
}
|
||||||
|
ctx.put("versionCapitalized", capitalize);
|
||||||
|
|
||||||
|
|
||||||
VelocityEngine v = new VelocityEngine();
|
VelocityEngine v = new VelocityEngine();
|
||||||
v.setProperty("resource.loader", "cp");
|
v.setProperty("resource.loader", "cp");
|
||||||
|
|
|
@ -5,7 +5,7 @@ import java.util.*;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.provider.JpaResourceProvider;
|
import ca.uhn.fhir.jpa.provider.JpaResourceProvider${versionCapitalized};
|
||||||
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
|
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
|
||||||
import ca.uhn.fhir.model.api.*;
|
import ca.uhn.fhir.model.api.*;
|
||||||
import ca.uhn.fhir.model.api.annotation.*;
|
import ca.uhn.fhir.model.api.annotation.*;
|
||||||
|
@ -18,7 +18,7 @@ import ca.uhn.fhir.model.dstu.resource.Binary;
|
||||||
// import ca.uhn.fhir.model.dstu2.resource.Bundle;
|
// import ca.uhn.fhir.model.dstu2.resource.Bundle;
|
||||||
import ca.uhn.fhir.model.api.Bundle;
|
import ca.uhn.fhir.model.api.Bundle;
|
||||||
|
|
||||||
public class ${className}ResourceProvider extends JpaResourceProvider<${className}> {
|
public class ${className}ResourceProvider extends JpaResourceProvider${versionCapitalized}<${className}> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Class<? extends IResource> getResourceType() {
|
public Class<? extends IResource> getResourceType() {
|
||||||
|
|
|
@ -474,6 +474,35 @@
|
||||||
<param name="file" value="examples/src/main/java/example/RestfulPatientResourceProviderMore.java" />
|
<param name="file" value="examples/src/main/java/example/RestfulPatientResourceProviderMore.java" />
|
||||||
</macro>
|
</macro>
|
||||||
|
|
||||||
|
<h4>Conditional Creates</h4>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
The FHIR specification also allows "conditional creates". A conditional
|
||||||
|
create has an additional header called <code>If-None-Exist</code>
|
||||||
|
which the client will supply on the HTTP request. The client will
|
||||||
|
populate this header with a search URL such as <code>Patient?identifier=foo</code>.
|
||||||
|
See the FHIR specification for details on the semantics for correctly
|
||||||
|
implementing conditional create.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
When a conditional create is detected (i.e. when the create request contains
|
||||||
|
a populated <code>If-None-Exist</code> header), if a method parameter annotated
|
||||||
|
with the
|
||||||
|
<a href="./apidocs/ca/uhn/fhir/rest/annotation/ConditionalOperationParam.html">@ConditionalOperationParam</a>
|
||||||
|
is detected, it will be populated with the value of this header.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<macro name="snippet">
|
||||||
|
<param name="id" value="createConditional" />
|
||||||
|
<param name="file" value="examples/src/main/java/example/RestfulPatientResourceProviderMore.java" />
|
||||||
|
</macro>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Example URL and HTTP header to perform a conditional create:
|
||||||
|
<br />
|
||||||
|
<code>http://fhir.example.com/Patient<br/>If-None-Exist: Patient?identifier=system%7C0001</code>
|
||||||
|
</p>
|
||||||
|
|
||||||
<a name="type_search" />
|
<a name="type_search" />
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
@ -535,9 +564,9 @@
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Parameters which take a string as their format should use the
|
Parameters which take a string as their format should use the
|
||||||
<a href="./apidocs/ca/uhn/fhir/rest/param/StringParam.html">StringParam</a>
|
<code><a href="./apidocs/ca/uhn/fhir/rest/param/StringParam.html">StringParam</a></code>
|
||||||
type. They may also use normal java Strings, although it is
|
type. They may also use normal java <code>String</code>, although it is
|
||||||
not possible to use the ":exact" qualifier in that case.
|
not possible to use the <code>:exact</code> qualifier in that case.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<macro name="snippet">
|
<macro name="snippet">
|
||||||
|
@ -971,7 +1000,7 @@
|
||||||
<p>
|
<p>
|
||||||
To accept a composite parameter, use a parameter type which implements the
|
To accept a composite parameter, use a parameter type which implements the
|
||||||
<a href="./apidocs/ca/uhn/fhir/model/api/IQueryParameterAnd.html">IQueryParameterAnd</a>
|
<a href="./apidocs/ca/uhn/fhir/model/api/IQueryParameterAnd.html">IQueryParameterAnd</a>
|
||||||
interface.
|
interface (which in turn encapsulates the corresponding IQueryParameterOr types).
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
An example follows which shows a search for Patients by address, where multiple string
|
An example follows which shows a search for Patients by address, where multiple string
|
||||||
|
@ -991,6 +1020,17 @@
|
||||||
<param name="id" value="searchMultipleAnd" />
|
<param name="id" value="searchMultipleAnd" />
|
||||||
<param name="file" value="examples/src/main/java/example/RestfulPatientResourceProviderMore.java" />
|
<param name="file" value="examples/src/main/java/example/RestfulPatientResourceProviderMore.java" />
|
||||||
</macro>
|
</macro>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Note that AND parameters join multiple OR parameters together, but
|
||||||
|
the inverse is not true. In other words, it is possible in FHIR
|
||||||
|
to use AND search parameters to specify a search criteria of
|
||||||
|
<code>(A=1 OR A=2) AND (B=1 OR B=2)</code>
|
||||||
|
but it is not possible to specify
|
||||||
|
<code>(A=1 AND B=1) OR (A=2 AND B=2)</code> (aside from
|
||||||
|
in very specific cases where a composite parameter has been
|
||||||
|
specifically defined).
|
||||||
|
</p>
|
||||||
|
|
||||||
<h4>AND Relationship Query Parameters for Dates</h4>
|
<h4>AND Relationship Query Parameters for Dates</h4>
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue