Allow interceptors to modify request contents
This commit is contained in:
parent
0369b7fd70
commit
377bae8c16
|
@ -27,9 +27,6 @@ import ca.uhn.fhir.rest.param.StringParam;
|
|||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
|
||||
// @RunWith(SpringJUnit4ClassRunner.class)
|
||||
// @ContextConfiguration(classes= {TestR4WithoutLuceneConfig.class})
|
||||
// @SuppressWarnings("unchecked")
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(classes = { TestR4WithoutLuceneConfig.class })
|
||||
public class FhirResourceDaoR4SearchWithLuceneDisabledTest extends BaseJpaTest {
|
||||
|
@ -79,62 +76,8 @@ public class FhirResourceDaoR4SearchWithLuceneDisabledTest extends BaseJpaTest {
|
|||
@Autowired
|
||||
private FhirContext myFhirCtx;
|
||||
@Autowired
|
||||
@Qualifier("myImmunizationDaoR4")
|
||||
private IFhirResourceDao<Immunization> myImmunizationDao;
|
||||
@Autowired
|
||||
@Qualifier("myLocationDaoR4")
|
||||
private IFhirResourceDao<Location> myLocationDao;
|
||||
@Autowired
|
||||
@Qualifier("myMediaDaoR4")
|
||||
private IFhirResourceDao<Media> myMediaDao;
|
||||
@Autowired
|
||||
@Qualifier("myMedicationDaoR4")
|
||||
private IFhirResourceDao<Medication> myMedicationDao;
|
||||
@Autowired
|
||||
@Qualifier("myMedicationRequestDaoR4")
|
||||
private IFhirResourceDao<MedicationRequest> myMedicationRequestDao;
|
||||
@Autowired
|
||||
@Qualifier("myNamingSystemDaoR4")
|
||||
private IFhirResourceDao<NamingSystem> myNamingSystemDao;
|
||||
@Autowired
|
||||
@Qualifier("myObservationDaoR4")
|
||||
private IFhirResourceDao<Observation> myObservationDao;
|
||||
@Autowired
|
||||
@Qualifier("myOperationDefinitionDaoR4")
|
||||
private IFhirResourceDao<OperationDefinition> myOperationDefinitionDao;
|
||||
@Autowired
|
||||
@Qualifier("myOrganizationDaoR4")
|
||||
private IFhirResourceDao<Organization> myOrganizationDao;
|
||||
@Autowired
|
||||
@Qualifier("myPatientDaoR4")
|
||||
private IFhirResourceDaoPatient<Patient> myPatientDao;
|
||||
@Autowired
|
||||
@Qualifier("myPractitionerDaoR4")
|
||||
private IFhirResourceDao<Practitioner> myPractitionerDao;
|
||||
@Autowired
|
||||
@Qualifier("myQuestionnaireDaoR4")
|
||||
private IFhirResourceDao<Questionnaire> myQuestionnaireDao;
|
||||
@Autowired
|
||||
@Qualifier("myQuestionnaireResponseDaoR4")
|
||||
private IFhirResourceDao<QuestionnaireResponse> myQuestionnaireResponseDao;
|
||||
@Autowired
|
||||
@Qualifier("myResourceProvidersR4")
|
||||
private Object myResourceProviders;
|
||||
@Autowired
|
||||
@Qualifier("myStructureDefinitionDaoR4")
|
||||
private IFhirResourceDao<StructureDefinition> myStructureDefinitionDao;
|
||||
@Autowired
|
||||
@Qualifier("mySubscriptionDaoR4")
|
||||
private IFhirResourceDaoSubscription<Subscription> mySubscriptionDao;
|
||||
@Autowired
|
||||
@Qualifier("mySubstanceDaoR4")
|
||||
private IFhirResourceDao<Substance> mySubstanceDao;
|
||||
@Autowired
|
||||
@Qualifier("mySystemDaoR4")
|
||||
private IFhirSystemDao<Bundle, Meta> mySystemDao;
|
||||
@Autowired
|
||||
@Qualifier("mySystemProviderR4")
|
||||
private JpaSystemProviderR4 mySystemProvider;
|
||||
|
||||
@Autowired
|
||||
protected PlatformTransactionManager myTxManager;
|
||||
|
@ -169,7 +112,7 @@ public class FhirResourceDaoR4SearchWithLuceneDisabledTest extends BaseJpaTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testSearchWithRegularParam() throws Exception {
|
||||
public void testSearchWithRegularParam() {
|
||||
String methodName = "testEverythingIncludesBackReferences";
|
||||
|
||||
Organization org = new Organization();
|
||||
|
@ -183,7 +126,7 @@ public class FhirResourceDaoR4SearchWithLuceneDisabledTest extends BaseJpaTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testSearchWithContent() throws Exception {
|
||||
public void testSearchWithContent() {
|
||||
String methodName = "testEverythingIncludesBackReferences";
|
||||
|
||||
Organization org = new Organization();
|
||||
|
@ -201,7 +144,7 @@ public class FhirResourceDaoR4SearchWithLuceneDisabledTest extends BaseJpaTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testSearchWithText() throws Exception {
|
||||
public void testSearchWithText() {
|
||||
String methodName = "testEverythingIncludesBackReferences";
|
||||
|
||||
Organization org = new Organization();
|
||||
|
|
|
@ -10,6 +10,8 @@ import ca.uhn.fhir.rest.server.interceptor.IServerOperationInterceptor;
|
|||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
|
@ -348,6 +350,21 @@ public abstract class RequestDetails {
|
|||
return myRequestContents;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method may be used to modify the contents of the incoming
|
||||
* request by hardcoding a value which will be used instead of the
|
||||
* value received by the client.
|
||||
* <p>
|
||||
* This method is useful for modifying the request body prior
|
||||
* to parsing within interceptors. It generally only has an
|
||||
* impact when called in the {@link IServerInterceptor#incomingRequestPostProcessed(RequestDetails, HttpServletRequest, HttpServletResponse)}
|
||||
* method
|
||||
* </p>
|
||||
*/
|
||||
public void setRequestContents(byte[] theRequestContents) {
|
||||
myRequestContents = theRequestContents;
|
||||
}
|
||||
|
||||
private class RequestOperationCallback implements IRequestOperationCallback {
|
||||
|
||||
private List<IServerInterceptor> getInterceptors() {
|
||||
|
|
|
@ -19,32 +19,40 @@ package ca.uhn.fhir.rest.server.method;
|
|||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
|
||||
import java.io.*;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
|
||||
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.Include;
|
||||
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.rest.annotation.*;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.api.server.IRestfulServer;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.client.exceptions.NonFhirResponseException;
|
||||
import ca.uhn.fhir.rest.server.BundleProviders;
|
||||
import ca.uhn.fhir.rest.server.IDynamicSearchResourceProvider;
|
||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||
import ca.uhn.fhir.rest.server.exceptions.*;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
|
||||
import ca.uhn.fhir.util.ReflectionUtil;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||
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.*;
|
||||
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.rest.annotation.*;
|
||||
import ca.uhn.fhir.rest.api.*;
|
||||
import ca.uhn.fhir.rest.api.server.*;
|
||||
import ca.uhn.fhir.rest.client.exceptions.NonFhirResponseException;
|
||||
import ca.uhn.fhir.rest.server.*;
|
||||
import ca.uhn.fhir.rest.server.exceptions.*;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
import ca.uhn.fhir.util.ReflectionUtil;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
|
||||
public abstract class BaseMethodBinding<T> {
|
||||
|
||||
|
@ -460,14 +468,7 @@ public abstract class BaseMethodBinding<T> {
|
|||
}
|
||||
returnType = returnTypeFromAnnotation;
|
||||
} else {
|
||||
// if (IRestfulClient.class.isAssignableFrom(theMethod.getDeclaringClass())) {
|
||||
// Clients don't define their methods in resource specific types, so they can
|
||||
// infer their resource type from the method return type.
|
||||
returnType = (Class<? extends IBaseResource>) returnTypeFromMethod;
|
||||
// } else {
|
||||
// This is a plain provider method returning a resource, so it should be
|
||||
// an operation or global search presumably
|
||||
// returnType = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -505,26 +506,6 @@ public abstract class BaseMethodBinding<T> {
|
|||
throw new ConfigurationException("Did not detect any FHIR annotations on method '" + theMethod.getName() + "' on type: " + theMethod.getDeclaringClass().getCanonicalName());
|
||||
}
|
||||
|
||||
// // each operation name must have a request type annotation and be
|
||||
// unique
|
||||
// if (null != read) {
|
||||
// return rm;
|
||||
// }
|
||||
//
|
||||
// SearchMethodBinding sm = new SearchMethodBinding();
|
||||
// if (null != search) {
|
||||
// sm.setRequestType(SearchMethodBinding.RequestType.GET);
|
||||
// } else if (null != theMethod.getAnnotation(PUT.class)) {
|
||||
// sm.setRequestType(SearchMethodBinding.RequestType.PUT);
|
||||
// } else if (null != theMethod.getAnnotation(POST.class)) {
|
||||
// sm.setRequestType(SearchMethodBinding.RequestType.POST);
|
||||
// } else if (null != theMethod.getAnnotation(DELETE.class)) {
|
||||
// sm.setRequestType(SearchMethodBinding.RequestType.DELETE);
|
||||
// } else {
|
||||
// return null;
|
||||
// }
|
||||
//
|
||||
// return sm;
|
||||
}
|
||||
|
||||
private static boolean isResourceInterface(Class<?> theReturnTypeFromMethod) {
|
||||
|
@ -555,8 +536,6 @@ public abstract class BaseMethodBinding<T> {
|
|||
return false;
|
||||
}
|
||||
return true;
|
||||
// boolean retVal = Modifier.isAbstract(theReturnType.getModifiers()) == false;
|
||||
// return retVal;
|
||||
}
|
||||
|
||||
public static boolean verifyMethodHasZeroOrOneOperationAnnotation(Method theNextMethod, Object... theAnnotations) {
|
||||
|
@ -574,39 +553,8 @@ public abstract class BaseMethodBinding<T> {
|
|||
}
|
||||
if (obj1 == null) {
|
||||
return false;
|
||||
// throw new ConfigurationException("Method '" +
|
||||
// theNextMethod.getName() + "' on type '" +
|
||||
// theNextMethod.getDeclaringClass().getSimpleName() +
|
||||
// " has no FHIR method annotations.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ServletRequestDetails#getByteStreamRequestContents()
|
||||
*/
|
||||
public static class ActiveRequestReader implements IRequestReader {
|
||||
@Override
|
||||
public InputStream getInputStream(RequestDetails theRequestDetails) throws IOException {
|
||||
return theRequestDetails.getInputStream();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ServletRequestDetails#getByteStreamRequestContents()
|
||||
*/
|
||||
public static class InactiveRequestReader implements IRequestReader {
|
||||
@Override
|
||||
public InputStream getInputStream(RequestDetails theRequestDetails) {
|
||||
throw new IllegalStateException("The servlet-api JAR is not found on the classpath. Please check that this library is available.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ServletRequestDetails#getByteStreamRequestContents()
|
||||
*/
|
||||
public static interface IRequestReader {
|
||||
InputStream getInputStream(RequestDetails theRequestDetails) throws IOException;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -19,38 +19,35 @@ package ca.uhn.fhir.rest.server.servlet;
|
|||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.*;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.method.BaseMethodBinding;
|
||||
import ca.uhn.fhir.rest.server.method.BaseMethodBinding.IRequestReader;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
public class ServletRequestDetails extends RequestDetails {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServletRequestDetails.class);
|
||||
/**
|
||||
* @see BaseMethodBinding#loadRequestContents(RequestDetails)
|
||||
*/
|
||||
private static volatile IRequestReader ourRequestReader;
|
||||
|
||||
private RestfulServer myServer;
|
||||
private HttpServletRequest myServletRequest;
|
||||
private HttpServletResponse myServletResponse;
|
||||
private byte[] requestContents;
|
||||
|
||||
public ServletRequestDetails() {
|
||||
super();
|
||||
|
@ -59,36 +56,9 @@ public class ServletRequestDetails extends RequestDetails {
|
|||
|
||||
@Override
|
||||
protected byte[] getByteStreamRequestContents() {
|
||||
/*
|
||||
* This is weird, but this class is used both in clients and in servers, and we want to avoid needing to depend on
|
||||
* servlet-api in clients since there is no point. So we dynamically load a class that does the servlet processing
|
||||
* in servers. Down the road it may make sense to just split the method binding classes into server and client
|
||||
* versions, but this isn't actually a huge deal I don't think.
|
||||
*/
|
||||
IRequestReader reader = ourRequestReader;
|
||||
if (reader == null) {
|
||||
try {
|
||||
Class.forName("javax.servlet.ServletInputStream");
|
||||
String className = BaseMethodBinding.class.getName() + "$" + "ActiveRequestReader";
|
||||
try {
|
||||
reader = (IRequestReader) Class.forName(className).newInstance();
|
||||
} catch (Exception e1) {
|
||||
throw new ConfigurationException("Failed to instantiate class " + className, e1);
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
String className = BaseMethodBinding.class.getName() + "$" + "InactiveRequestReader";
|
||||
try {
|
||||
reader = (IRequestReader) Class.forName(className).newInstance();
|
||||
} catch (Exception e1) {
|
||||
throw new ConfigurationException("Failed to instantiate class " + className, e1);
|
||||
}
|
||||
}
|
||||
ourRequestReader = reader;
|
||||
}
|
||||
|
||||
try {
|
||||
InputStream inputStream = reader.getInputStream(this);
|
||||
requestContents = IOUtils.toByteArray(inputStream);
|
||||
InputStream inputStream = getInputStream();
|
||||
byte[] requestContents = IOUtils.toByteArray(inputStream);
|
||||
|
||||
if (myServer.isUncompressIncomingContents()) {
|
||||
String contentEncoding = myServletRequest.getHeader(Constants.HEADER_CONTENT_ENCODING);
|
||||
|
@ -100,7 +70,6 @@ public class ServletRequestDetails extends RequestDetails {
|
|||
}
|
||||
}
|
||||
}
|
||||
// FIXME resource leak
|
||||
return requestContents;
|
||||
} catch (IOException e) {
|
||||
ourLog.error("Could not load request resource", e);
|
||||
|
|
|
@ -1,20 +1,22 @@
|
|||
package ca.uhn.fhir.rest.server;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.rest.annotation.Create;
|
||||
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
||||
import ca.uhn.fhir.rest.annotation.Validate;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
|
||||
import ca.uhn.fhir.rest.server.interceptor.InterceptorAdapter;
|
||||
import ca.uhn.fhir.rest.server.interceptor.ServerOperationInterceptorAdapter;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
import ca.uhn.fhir.util.PortUtil;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
|
@ -26,6 +28,7 @@ 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.hamcrest.core.StringContains;
|
||||
import org.hl7.fhir.dstu3.model.OperationOutcome;
|
||||
import org.hl7.fhir.dstu3.model.Patient;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
@ -33,18 +36,14 @@ import org.junit.*;
|
|||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.InOrder;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
||||
import ca.uhn.fhir.rest.annotation.Validate;
|
||||
import ca.uhn.fhir.rest.api.*;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
|
||||
import ca.uhn.fhir.rest.server.interceptor.ServerOperationInterceptorAdapter;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
import ca.uhn.fhir.util.PortUtil;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
public class InterceptorDstu3Test {
|
||||
|
||||
|
@ -53,6 +52,7 @@ public class InterceptorDstu3Test {
|
|||
private static int ourPort;
|
||||
private static Server ourServer;
|
||||
private static RestfulServer ourServlet;
|
||||
private static Patient ourLastPatient;
|
||||
private IServerInterceptor myInterceptor1;
|
||||
private IServerInterceptor myInterceptor2;
|
||||
|
||||
|
@ -69,79 +69,18 @@ public class InterceptorDstu3Test {
|
|||
myInterceptor2 = mock(IServerInterceptor.class);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Test
|
||||
public void testServerOperationInterceptorAdapterMethods() {
|
||||
ServerOperationInterceptorAdapter i = new ServerOperationInterceptorAdapter();
|
||||
i.resourceCreated(null, null);
|
||||
i.resourceDeleted(null, null);
|
||||
i.resourceUpdated(null, null);
|
||||
i.resourceUpdated(null, null, null);
|
||||
private String createInput() {
|
||||
return "{\n" +
|
||||
" \"resourceType\":\"Patient\",\n" +
|
||||
" \"id\":\"1855669\",\n" +
|
||||
" \"meta\":{\n" +
|
||||
" \"versionId\":\"1\",\n" +
|
||||
" \"lastUpdated\":\"2016-02-18T07:41:35.953-05:00\"\n" +
|
||||
" },\n" +
|
||||
" \"active\":true\n" +
|
||||
"}";
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResponseWithOperationOutcome() throws Exception {
|
||||
ourServlet.setInterceptors(myInterceptor1);
|
||||
|
||||
when(myInterceptor1.incomingRequestPreProcessed(any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
when(myInterceptor1.incomingRequestPostProcessed(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
when(myInterceptor1.outgoingResponse(any(RequestDetails.class), any(IResource.class))).thenReturn(true);
|
||||
|
||||
String input = createInput();
|
||||
|
||||
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$validate");
|
||||
httpPost.setEntity(new StringEntity(input, ContentType.create(Constants.CT_FHIR_JSON, "UTF-8")));
|
||||
HttpResponse status = ourClient.execute(httpPost);
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
|
||||
InOrder order = inOrder(myInterceptor1);
|
||||
order.verify(myInterceptor1, times(1)).incomingRequestPreProcessed(any(HttpServletRequest.class), any(HttpServletResponse.class));
|
||||
order.verify(myInterceptor1, times(1)).incomingRequestPostProcessed(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class));
|
||||
ArgumentCaptor<RestOperationTypeEnum> opTypeCapt = ArgumentCaptor.forClass(RestOperationTypeEnum.class);
|
||||
ArgumentCaptor<ActionRequestDetails> arTypeCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
|
||||
ArgumentCaptor<IBaseResource> resourceCapt = ArgumentCaptor.forClass(IBaseResource.class);
|
||||
order.verify(myInterceptor1, times(1)).incomingRequestPreHandled(opTypeCapt.capture(), arTypeCapt.capture());
|
||||
order.verify(myInterceptor1, times(1)).outgoingResponse(any(RequestDetails.class), resourceCapt.capture());
|
||||
|
||||
assertEquals(1, resourceCapt.getAllValues().size());
|
||||
assertEquals(OperationOutcome.class, resourceCapt.getAllValues().get(0).getClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResponseWithNothing() throws Exception {
|
||||
ourServlet.setInterceptors(myInterceptor1);
|
||||
|
||||
when(myInterceptor1.incomingRequestPreProcessed(any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
when(myInterceptor1.incomingRequestPostProcessed(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
when(myInterceptor1.outgoingResponse(any(RequestDetails.class), any(IResource.class))).thenReturn(true);
|
||||
|
||||
String input = createInput();
|
||||
|
||||
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient");
|
||||
httpPost.setEntity(new StringEntity(input, ContentType.create(Constants.CT_FHIR_JSON, "UTF-8")));
|
||||
HttpResponse status = ourClient.execute(httpPost);
|
||||
try {
|
||||
assertEquals(201, status.getStatusLine().getStatusCode());
|
||||
} finally {
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
}
|
||||
|
||||
InOrder order = inOrder(myInterceptor1);
|
||||
verify(myInterceptor1, times(1)).incomingRequestPreProcessed(any(HttpServletRequest.class), any(HttpServletResponse.class));
|
||||
verify(myInterceptor1, times(1)).incomingRequestPostProcessed(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class));
|
||||
ArgumentCaptor<RestOperationTypeEnum> opTypeCapt = ArgumentCaptor.forClass(RestOperationTypeEnum.class);
|
||||
ArgumentCaptor<ActionRequestDetails> arTypeCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
|
||||
ArgumentCaptor<RequestDetails> rdCapt = ArgumentCaptor.forClass(RequestDetails.class);
|
||||
ArgumentCaptor<IBaseResource> resourceCapt = ArgumentCaptor.forClass(IBaseResource.class);
|
||||
verify(myInterceptor1, times(1)).incomingRequestPreHandled(opTypeCapt.capture(), arTypeCapt.capture());
|
||||
verify(myInterceptor1, times(1)).outgoingResponse(any(RequestDetails.class), resourceCapt.capture());
|
||||
|
||||
assertEquals(1, resourceCapt.getAllValues().size());
|
||||
assertEquals(null, resourceCapt.getAllValues().get(0));
|
||||
// assertEquals("", rdCapt.getAllValues().get(0).get)
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testResourceResponseIncluded() throws Exception {
|
||||
ourServlet.setInterceptors(myInterceptor1, myInterceptor2);
|
||||
|
@ -184,16 +123,91 @@ public class InterceptorDstu3Test {
|
|||
assertNotNull(arTypeCapt.getValue().getResource());
|
||||
}
|
||||
|
||||
private String createInput() {
|
||||
return "{\n" +
|
||||
" \"resourceType\":\"Patient\",\n" +
|
||||
" \"id\":\"1855669\",\n" +
|
||||
" \"meta\":{\n" +
|
||||
" \"versionId\":\"1\",\n" +
|
||||
" \"lastUpdated\":\"2016-02-18T07:41:35.953-05:00\"\n" +
|
||||
" },\n" +
|
||||
" \"active\":true\n" +
|
||||
"}";
|
||||
public void testModifyResponse() {
|
||||
InterceptorAdapter interceptor = new InterceptorAdapter(){
|
||||
@Override
|
||||
public boolean incomingRequestPostProcessed(RequestDetails theRequestDetails, HttpServletRequest theRequest, HttpServletResponse theResponse) throws AuthenticationException {
|
||||
ServletRequestDetails srd = (ServletRequestDetails)theRequestDetails;
|
||||
String input = new String(srd.loadRequestContents(), Constants.CHARSET_UTF8);
|
||||
assertThat(input, StringContains.containsString("\"active\":true"));
|
||||
|
||||
String newInput = createInput().replace("true", "false");
|
||||
srd.setRequestContents(newInput.getBytes(Constants.CHARSET_UTF8));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResponseWithNothing() throws Exception {
|
||||
ourServlet.setInterceptors(myInterceptor1);
|
||||
|
||||
when(myInterceptor1.incomingRequestPreProcessed(any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
when(myInterceptor1.incomingRequestPostProcessed(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
when(myInterceptor1.outgoingResponse(any(RequestDetails.class), any(IResource.class))).thenReturn(true);
|
||||
|
||||
String input = createInput();
|
||||
|
||||
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient");
|
||||
httpPost.setEntity(new StringEntity(input, ContentType.create(Constants.CT_FHIR_JSON, "UTF-8")));
|
||||
HttpResponse status = ourClient.execute(httpPost);
|
||||
try {
|
||||
assertEquals(201, status.getStatusLine().getStatusCode());
|
||||
} finally {
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
}
|
||||
|
||||
InOrder order = inOrder(myInterceptor1);
|
||||
verify(myInterceptor1, times(1)).incomingRequestPreProcessed(any(HttpServletRequest.class), any(HttpServletResponse.class));
|
||||
verify(myInterceptor1, times(1)).incomingRequestPostProcessed(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class));
|
||||
ArgumentCaptor<RestOperationTypeEnum> opTypeCapt = ArgumentCaptor.forClass(RestOperationTypeEnum.class);
|
||||
ArgumentCaptor<ActionRequestDetails> arTypeCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
|
||||
ArgumentCaptor<RequestDetails> rdCapt = ArgumentCaptor.forClass(RequestDetails.class);
|
||||
ArgumentCaptor<IBaseResource> resourceCapt = ArgumentCaptor.forClass(IBaseResource.class);
|
||||
verify(myInterceptor1, times(1)).incomingRequestPreHandled(opTypeCapt.capture(), arTypeCapt.capture());
|
||||
verify(myInterceptor1, times(1)).outgoingResponse(any(RequestDetails.class), resourceCapt.capture());
|
||||
|
||||
assertEquals(1, resourceCapt.getAllValues().size());
|
||||
assertEquals(null, resourceCapt.getAllValues().get(0));
|
||||
// assertEquals("", rdCapt.getAllValues().get(0).get)
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResponseWithOperationOutcome() throws Exception {
|
||||
ourServlet.setInterceptors(myInterceptor1);
|
||||
|
||||
when(myInterceptor1.incomingRequestPreProcessed(any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
when(myInterceptor1.incomingRequestPostProcessed(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
when(myInterceptor1.outgoingResponse(any(RequestDetails.class), any(IResource.class))).thenReturn(true);
|
||||
|
||||
String input = createInput();
|
||||
|
||||
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$validate");
|
||||
httpPost.setEntity(new StringEntity(input, ContentType.create(Constants.CT_FHIR_JSON, "UTF-8")));
|
||||
HttpResponse status = ourClient.execute(httpPost);
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
|
||||
InOrder order = inOrder(myInterceptor1);
|
||||
order.verify(myInterceptor1, times(1)).incomingRequestPreProcessed(any(HttpServletRequest.class), any(HttpServletResponse.class));
|
||||
order.verify(myInterceptor1, times(1)).incomingRequestPostProcessed(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class));
|
||||
ArgumentCaptor<RestOperationTypeEnum> opTypeCapt = ArgumentCaptor.forClass(RestOperationTypeEnum.class);
|
||||
ArgumentCaptor<ActionRequestDetails> arTypeCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
|
||||
ArgumentCaptor<IBaseResource> resourceCapt = ArgumentCaptor.forClass(IBaseResource.class);
|
||||
order.verify(myInterceptor1, times(1)).incomingRequestPreHandled(opTypeCapt.capture(), arTypeCapt.capture());
|
||||
order.verify(myInterceptor1, times(1)).outgoingResponse(any(RequestDetails.class), resourceCapt.capture());
|
||||
|
||||
assertEquals(1, resourceCapt.getAllValues().size());
|
||||
assertEquals(OperationOutcome.class, resourceCapt.getAllValues().get(0).getClass());
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Test
|
||||
public void testServerOperationInterceptorAdapterMethods() {
|
||||
ServerOperationInterceptorAdapter i = new ServerOperationInterceptorAdapter();
|
||||
i.resourceCreated(null, null);
|
||||
i.resourceDeleted(null, null);
|
||||
i.resourceUpdated(null, null);
|
||||
i.resourceUpdated(null, null, null);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
|
@ -226,6 +240,13 @@ public class InterceptorDstu3Test {
|
|||
|
||||
public static class DummyPatientResourceProvider implements IResourceProvider {
|
||||
|
||||
|
||||
@Create()
|
||||
public MethodOutcome create(@ResourceParam Patient theResource) {
|
||||
ourLastPatient = theResource;
|
||||
return new MethodOutcome();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<Patient> getResourceType() {
|
||||
return Patient.class;
|
||||
|
@ -236,11 +257,6 @@ public class InterceptorDstu3Test {
|
|||
return new MethodOutcome();
|
||||
}
|
||||
|
||||
@Create()
|
||||
public MethodOutcome create(@ResourceParam Patient theResource) {
|
||||
return new MethodOutcome();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,16 +1,11 @@
|
|||
package ca.uhn.fhir.rest.server;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.hamcrest.Matchers.stringContainsInOrder;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.rest.annotation.*;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.client.MyPatientWithExtensions;
|
||||
import ca.uhn.fhir.util.PortUtil;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
|
@ -25,31 +20,57 @@ import org.eclipse.jetty.servlet.ServletHandler;
|
|||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.r4.model.*;
|
||||
import org.hl7.fhir.r4.model.DateType;
|
||||
import org.hl7.fhir.r4.model.IdType;
|
||||
import org.hl7.fhir.r4.model.OperationOutcome;
|
||||
import org.hl7.fhir.r4.model.OperationOutcome.OperationOutcomeIssueComponent;
|
||||
import org.junit.*;
|
||||
import org.hl7.fhir.r4.model.Patient;
|
||||
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.rest.annotation.*;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.client.MyPatientWithExtensions;
|
||||
import ca.uhn.fhir.util.PortUtil;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class CreateR4Test {
|
||||
private static CloseableHttpClient ourClient;
|
||||
|
||||
private static FhirContext ourCtx = FhirContext.forR4();
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CreateR4Test.class);
|
||||
public static IBaseOperationOutcome ourReturnOo;
|
||||
private static CloseableHttpClient ourClient;
|
||||
private static FhirContext ourCtx = FhirContext.forR4();
|
||||
private static int ourPort;
|
||||
private static Server ourServer;
|
||||
public static IBaseOperationOutcome ourReturnOo;
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
ourReturnOo = null;
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testCreateIgnoresIdInResourceBody() throws Exception {
|
||||
|
||||
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient");
|
||||
httpPost.setEntity(new StringEntity("{\"resourceType\":\"Patient\", \"id\":\"999\", \"status\":\"active\"}", ContentType.parse("application/fhir+json; charset=utf-8")));
|
||||
HttpResponse status = ourClient.execute(httpPost);
|
||||
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
|
||||
ourLog.info("Response was:\n{}", responseContent);
|
||||
|
||||
assertEquals(201, status.getStatusLine().getStatusCode());
|
||||
|
||||
assertEquals(1, status.getHeaders("Location").length);
|
||||
assertEquals(0, status.getHeaders("Content-Location").length);
|
||||
assertEquals("http://localhost:" + ourPort + "/Patient/1", status.getFirstHeader("Location").getValue());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* #472
|
||||
*/
|
||||
|
@ -76,7 +97,7 @@ public class CreateR4Test {
|
|||
@Test
|
||||
public void testCreateReturnsOperationOutcome() throws Exception {
|
||||
ourReturnOo = new OperationOutcome().addIssue(new OperationOutcomeIssueComponent().setDiagnostics("DIAG"));
|
||||
|
||||
|
||||
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient");
|
||||
httpPost.setEntity(new StringEntity("{\"resourceType\":\"Patient\", \"status\":\"active\"}", ContentType.parse("application/fhir+json; charset=utf-8")));
|
||||
HttpResponse status = ourClient.execute(httpPost);
|
||||
|
@ -91,28 +112,6 @@ public class CreateR4Test {
|
|||
assertThat(responseContent, containsString("DIAG"));
|
||||
}
|
||||
|
||||
/**
|
||||
* #342
|
||||
*/
|
||||
@Test
|
||||
public void testCreateWithInvalidContent() throws Exception {
|
||||
|
||||
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient");
|
||||
httpPost.setEntity(new StringEntity("FOO", ContentType.parse("application/xml+fhir; charset=utf-8")));
|
||||
HttpResponse status = ourClient.execute(httpPost);
|
||||
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
|
||||
ourLog.info("Response was:\n{}", responseContent);
|
||||
|
||||
assertEquals(400, status.getStatusLine().getStatusCode());
|
||||
|
||||
assertThat(responseContent, containsString("<OperationOutcome xmlns=\"http://hl7.org/fhir\"><issue><severity value=\"error\"/><code value=\"processing\"/><diagnostics value=\""));
|
||||
assertThat(responseContent, containsString("Failed to parse request body as XML resource. Error was: com.ctc.wstx.exc.WstxUnexpectedCharException: Unexpected character 'F'"));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateWithIncorrectContent1() throws Exception {
|
||||
|
||||
|
@ -169,6 +168,28 @@ public class CreateR4Test {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* #342
|
||||
*/
|
||||
@Test
|
||||
public void testCreateWithInvalidContent() throws Exception {
|
||||
|
||||
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient");
|
||||
httpPost.setEntity(new StringEntity("FOO", ContentType.parse("application/xml+fhir; charset=utf-8")));
|
||||
HttpResponse status = ourClient.execute(httpPost);
|
||||
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
|
||||
ourLog.info("Response was:\n{}", responseContent);
|
||||
|
||||
assertEquals(400, status.getStatusLine().getStatusCode());
|
||||
|
||||
assertThat(responseContent, containsString("<OperationOutcome xmlns=\"http://hl7.org/fhir\"><issue><severity value=\"error\"/><code value=\"processing\"/><diagnostics value=\""));
|
||||
assertThat(responseContent, containsString("Failed to parse request body as XML resource. Error was: com.ctc.wstx.exc.WstxUnexpectedCharException: Unexpected character 'F'"));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearch() throws Exception {
|
||||
|
||||
|
@ -184,14 +205,14 @@ public class CreateR4Test {
|
|||
|
||||
//@formatter:off
|
||||
assertThat(responseContent, stringContainsInOrder(
|
||||
"<Patient xmlns=\"http://hl7.org/fhir\">",
|
||||
"<id value=\"0\"/>",
|
||||
"<meta>",
|
||||
"<profile value=\"http://example.com/StructureDefinition/patient_with_extensions\"/>",
|
||||
"</meta>",
|
||||
"<modifierExtension url=\"http://example.com/ext/date\">",
|
||||
"<valueDate value=\"2011-01-01\"/>",
|
||||
"</modifierExtension>",
|
||||
"<Patient xmlns=\"http://hl7.org/fhir\">",
|
||||
"<id value=\"0\"/>",
|
||||
"<meta>",
|
||||
"<profile value=\"http://example.com/StructureDefinition/patient_with_extensions\"/>",
|
||||
"</meta>",
|
||||
"<modifierExtension url=\"http://example.com/ext/date\">",
|
||||
"<valueDate value=\"2011-01-01\"/>",
|
||||
"</modifierExtension>",
|
||||
"</Patient>"));
|
||||
//@formatter:on
|
||||
|
||||
|
@ -231,6 +252,7 @@ public class CreateR4Test {
|
|||
|
||||
@Create()
|
||||
public MethodOutcome create(@ResourceParam Patient theIdParam) {
|
||||
assertNull(theIdParam.getIdElement().getIdPart());
|
||||
return new MethodOutcome(new IdType("Patient", "1"), true).setOperationOutcome(ourReturnOo);
|
||||
}
|
||||
|
||||
|
|
|
@ -62,6 +62,12 @@
|
|||
can be useful in order to allow interceptors to
|
||||
change payload contents being saved.
|
||||
</action>
|
||||
<action type="add">
|
||||
A new method has been added to RequestDetails called
|
||||
<![CDATA[<code>setRequestContents()]]> which can be used
|
||||
by interceptors to modify the request body before it
|
||||
is parsed by the server.
|
||||
</action>
|
||||
</release>
|
||||
<release version="3.1.0" date="2017-11-23">
|
||||
<action type="add">
|
||||
|
|
Loading…
Reference in New Issue