Allow raw access to resource body in create/update/etc
This commit is contained in:
parent
064f113133
commit
e5b402cb14
|
@ -71,6 +71,7 @@ import ca.uhn.fhir.rest.param.StringOrListParam;
|
|||
import ca.uhn.fhir.rest.param.StringParam;
|
||||
import ca.uhn.fhir.rest.param.TokenOrListParam;
|
||||
import ca.uhn.fhir.rest.param.TokenParam;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
||||
|
@ -758,6 +759,23 @@ public MethodOutcome updatePatientConditional(
|
|||
}
|
||||
//END SNIPPET: updateConditional
|
||||
|
||||
//START SNIPPET: updateRaw
|
||||
@Update
|
||||
public MethodOutcome updatePatientWithRawValue (
|
||||
@ResourceParam Patient thePatient,
|
||||
@IdParam IdDt theId,
|
||||
@ResourceParam String theRawBody,
|
||||
@ResourceParam EncodingEnum theEncodingEnum) {
|
||||
|
||||
// Here, thePatient will have the parsed patient body, but
|
||||
// theRawBody will also have the raw text of the resource
|
||||
// being created, and theEncodingEnum will tell you which
|
||||
// encoding was used
|
||||
|
||||
return new MethodOutcome(); // populate this
|
||||
}
|
||||
//END SNIPPET: updateRaw
|
||||
|
||||
//START SNIPPET: update
|
||||
@Update
|
||||
public MethodOutcome updatePatient(@IdParam IdDt theId, @ResourceParam Patient thePatient) {
|
||||
|
|
|
@ -48,33 +48,33 @@ abstract class BaseOutcomeReturningMethodBindingWithResourceParam extends BaseOu
|
|||
int index = 0;
|
||||
for (IParameter next : getParameters()) {
|
||||
if (next instanceof ResourceParameter) {
|
||||
resourceParameter = (ResourceParameter) next;
|
||||
if (resourceParameter.getMode() != ResourceParameter.Mode.RESOURCE) {
|
||||
continue;
|
||||
}
|
||||
if (myResourceType != null) {
|
||||
throw new ConfigurationException("Method " + theMethod.getName() + " on type " + theMethod.getDeclaringClass() + " has more than one @ResourceParam. Only one is allowed.");
|
||||
}
|
||||
|
||||
resourceParameter = (ResourceParameter) next;
|
||||
Class<? extends IBaseResource> providerResourceType = resourceParameter.getResourceType();
|
||||
|
||||
if (theProvider instanceof IResourceProvider) {
|
||||
providerResourceType = ((IResourceProvider) theProvider).getResourceType();
|
||||
}
|
||||
|
||||
myResourceType = resourceParameter.getResourceType();
|
||||
if (Modifier.isAbstract(myResourceType.getModifiers())) {
|
||||
myResourceType = providerResourceType;
|
||||
}
|
||||
|
||||
myResourceName = theContext.getResourceDefinition(providerResourceType).getName();
|
||||
|
||||
myResourceParameterIndex = index;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
|
||||
if ((myResourceType == null || Modifier.isAbstract(myResourceType.getModifiers())) && (theProvider instanceof IResourceProvider)) {
|
||||
myResourceType = ((IResourceProvider) theProvider).getResourceType();
|
||||
}
|
||||
if (myResourceType == null) {
|
||||
throw new ConfigurationException("Unable to determine resource type for method: " + theMethod);
|
||||
}
|
||||
|
||||
myResourceName = theContext.getResourceDefinition(myResourceType).getName();
|
||||
myIdParamIndex = MethodUtil.findIdParameterIndex(theMethod);
|
||||
|
||||
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 resource parameter annotated with @" + ResourceParam.class.getSimpleName());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -73,6 +73,7 @@ import ca.uhn.fhir.rest.param.NumberAndListParam;
|
|||
import ca.uhn.fhir.rest.param.QuantityAndListParam;
|
||||
import ca.uhn.fhir.rest.param.ReferenceAndListParam;
|
||||
import ca.uhn.fhir.rest.param.ResourceParameter;
|
||||
import ca.uhn.fhir.rest.param.ResourceParameter.Mode;
|
||||
import ca.uhn.fhir.rest.param.StringAndListParam;
|
||||
import ca.uhn.fhir.rest.param.TokenAndListParam;
|
||||
import ca.uhn.fhir.rest.param.TransactionParameter;
|
||||
|
@ -408,10 +409,17 @@ public class MethodUtil {
|
|||
|
||||
param = new IncludeParameter((IncludeParam) nextAnnotation, instantiableCollectionType, specType);
|
||||
} else if (nextAnnotation instanceof ResourceParam) {
|
||||
if (!IResource.class.isAssignableFrom(parameterType)) {
|
||||
Mode mode;
|
||||
if (IBaseResource.class.isAssignableFrom(parameterType)) {
|
||||
mode = Mode.RESOURCE;
|
||||
} else if (String.class.equals(parameterType)) {
|
||||
mode = ResourceParameter.Mode.BODY;
|
||||
} else if (EncodingEnum.class.equals(parameterType)) {
|
||||
mode = Mode.ENCODING;
|
||||
} else {
|
||||
throw new ConfigurationException("Method '" + theMethod.getName() + "' is annotated with @" + ResourceParam.class.getSimpleName() + " but has a type that is not an implemtation of " + IResource.class.getCanonicalName());
|
||||
}
|
||||
param = new ResourceParameter((Class<? extends IResource>) parameterType, theProvider);
|
||||
param = new ResourceParameter((Class<? extends IResource>) parameterType, theProvider, mode);
|
||||
} else if (nextAnnotation instanceof IdParam || nextAnnotation instanceof VersionIdParam) {
|
||||
param = new NullParameter();
|
||||
} else if (nextAnnotation instanceof ServerBase) {
|
||||
|
|
|
@ -35,6 +35,7 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.hl7.fhir.instance.model.api.IBaseBinary;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
@ -49,6 +50,7 @@ import ca.uhn.fhir.rest.method.BaseMethodBinding;
|
|||
import ca.uhn.fhir.rest.method.IParameter;
|
||||
import ca.uhn.fhir.rest.method.MethodUtil;
|
||||
import ca.uhn.fhir.rest.method.Request;
|
||||
import ca.uhn.fhir.rest.param.ResourceParameter.Mode;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||
|
@ -59,11 +61,16 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
|||
public class ResourceParameter implements IParameter {
|
||||
|
||||
private boolean myBinary;
|
||||
private Mode myMode;
|
||||
private Class<? extends IBaseResource> myResourceType;
|
||||
|
||||
public ResourceParameter(Class<? extends IResource> theParameterType, Object theProvider) {
|
||||
public ResourceParameter(Class<? extends IResource> theParameterType, Object theProvider, Mode theMode) {
|
||||
Validate.notNull(theParameterType, "theParameterType can not be null");
|
||||
Validate.notNull(theMode, "theMode can not be null");
|
||||
|
||||
myResourceType = theParameterType;
|
||||
myBinary = IBaseBinary.class.isAssignableFrom(theParameterType);
|
||||
myMode = theMode;
|
||||
|
||||
Class<? extends IBaseResource> providerResourceType = null;
|
||||
if (theProvider instanceof IResourceProvider) {
|
||||
|
@ -76,6 +83,10 @@ public class ResourceParameter implements IParameter {
|
|||
|
||||
}
|
||||
|
||||
public Mode getMode() {
|
||||
return myMode;
|
||||
}
|
||||
|
||||
public Class<? extends IBaseResource> getResourceType() {
|
||||
return myResourceType;
|
||||
}
|
||||
|
@ -93,9 +104,21 @@ public class ResourceParameter implements IParameter {
|
|||
|
||||
@Override
|
||||
public Object translateQueryParametersIntoServerArgument(Request theRequest, byte[] theRequestContents, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
||||
switch (myMode) {
|
||||
case BODY:
|
||||
try {
|
||||
return IOUtils.toString(createRequestReader(theRequest, theRequestContents));
|
||||
} catch (IOException e) {
|
||||
// Shouldn't happen since we're reading from a byte array
|
||||
throw new InternalErrorException("Failed to load request");
|
||||
}
|
||||
case ENCODING:
|
||||
return RestfulServerUtils.determineRequestEncoding(theRequest);
|
||||
case RESOURCE:
|
||||
break;
|
||||
}
|
||||
|
||||
if (myBinary) {
|
||||
|
||||
FhirContext ctx = theRequest.getServer().getFhirContext();
|
||||
String ct = theRequest.getServletRequest().getHeader(Constants.HEADER_CONTENT_TYPE);
|
||||
IBaseBinary binary = (IBaseBinary) ctx.getResourceDefinition("Binary").newInstance();
|
||||
|
@ -110,6 +133,29 @@ public class ResourceParameter implements IParameter {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
static Reader createRequestReader(byte[] theRequestContents, Charset charset) {
|
||||
Reader requestReader = new InputStreamReader(new ByteArrayInputStream(theRequestContents), charset);
|
||||
return requestReader;
|
||||
}
|
||||
|
||||
static Reader createRequestReader(Request theRequest, byte[] theRequestContents) {
|
||||
return createRequestReader(theRequestContents, determineRequestCharset(theRequest));
|
||||
}
|
||||
|
||||
static Charset determineRequestCharset(Request theRequest) {
|
||||
String ct = theRequest.getServletRequest().getHeader(Constants.HEADER_CONTENT_TYPE);
|
||||
|
||||
Charset charset = null;
|
||||
if (isNotBlank(ct)) {
|
||||
ContentType parsedCt = ContentType.parse(ct);
|
||||
charset = parsedCt.getCharset();
|
||||
}
|
||||
if (charset == null) {
|
||||
charset = Charset.forName("UTF-8");
|
||||
}
|
||||
return charset;
|
||||
}
|
||||
|
||||
public static IBaseResource loadResourceFromRequest(Request theRequest, byte[] theRequestContents, BaseMethodBinding<?> theMethodBinding, Class<? extends IBaseResource> theResourceType) {
|
||||
FhirContext ctx = theRequest.getServer().getFhirContext();
|
||||
|
||||
|
@ -175,27 +221,8 @@ public class ResourceParameter implements IParameter {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
static Reader createRequestReader(byte[] theRequestContents, Charset charset) {
|
||||
Reader requestReader = new InputStreamReader(new ByteArrayInputStream(theRequestContents), charset);
|
||||
return requestReader;
|
||||
}
|
||||
|
||||
static Reader createRequestReader(Request theRequest, byte[] theRequestContents) {
|
||||
return createRequestReader(theRequestContents, determineRequestCharset(theRequest));
|
||||
}
|
||||
|
||||
static Charset determineRequestCharset(Request theRequest) {
|
||||
String ct = theRequest.getServletRequest().getHeader(Constants.HEADER_CONTENT_TYPE);
|
||||
|
||||
Charset charset = null;
|
||||
if (isNotBlank(ct)) {
|
||||
ContentType parsedCt = ContentType.parse(ct);
|
||||
charset = parsedCt.getCharset();
|
||||
}
|
||||
if (charset == null) {
|
||||
charset = Charset.forName("UTF-8");
|
||||
}
|
||||
return charset;
|
||||
public enum Mode{
|
||||
BODY, ENCODING, RESOURCE
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package ca.uhn.fhir.rest.server;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.StringReader;
|
||||
|
@ -20,6 +21,7 @@ import org.eclipse.jetty.servlet.ServletHandler;
|
|||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.hamcrest.core.StringContains;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
|
@ -33,6 +35,7 @@ import ca.uhn.fhir.model.dstu.resource.AdverseReaction;
|
|||
import ca.uhn.fhir.model.dstu.resource.DiagnosticReport;
|
||||
import ca.uhn.fhir.model.dstu.resource.Observation;
|
||||
import ca.uhn.fhir.model.dstu.resource.OperationOutcome;
|
||||
import ca.uhn.fhir.model.dstu.resource.Organization;
|
||||
import ca.uhn.fhir.model.dstu.resource.Patient;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.model.primitive.StringDt;
|
||||
|
@ -48,11 +51,21 @@ import ca.uhn.fhir.util.PortUtil;
|
|||
*/
|
||||
public class CreateTest {
|
||||
private static CloseableHttpClient ourClient;
|
||||
private static EncodingEnum ourLastEncoding;
|
||||
private static String ourLastResourceBody;
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CreateTest.class);
|
||||
private static int ourPort;
|
||||
|
||||
private static DiagnosticReportProvider ourReportProvider;
|
||||
|
||||
private static Server ourServer;
|
||||
|
||||
@Before()
|
||||
public void before() {
|
||||
ourLastResourceBody=null;
|
||||
ourLastEncoding=null;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreate() throws Exception {
|
||||
|
||||
|
@ -73,6 +86,34 @@ public class CreateTest {
|
|||
assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("location").getValue());
|
||||
assertThat(status.getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue(), StringContains.containsString("UTF-8"));
|
||||
|
||||
assertThat(ourLastResourceBody, stringContainsInOrder("<Patient ", "<identifier>","<value value=\"001"));
|
||||
assertEquals(EncodingEnum.XML, ourLastEncoding);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateWithNoParsed() throws Exception {
|
||||
|
||||
Organization org = new Organization();
|
||||
org.addIdentifier().setValue("001");
|
||||
org.addIdentifier().setValue("002");
|
||||
|
||||
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Organization");
|
||||
httpPost.setEntity(new StringEntity(new FhirContext().newJsonParser().encodeResourceToString(org), ContentType.create(Constants.CT_FHIR_JSON, "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 + "/Organization/001", status.getFirstHeader("location").getValue());
|
||||
assertThat(status.getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue(), StringContains.containsString("UTF-8"));
|
||||
|
||||
assertThat(ourLastResourceBody, stringContainsInOrder("\"resourceType\":\"Organization\"", "\"identifier\"","\"value\":\"001"));
|
||||
assertEquals(EncodingEnum.JSON, ourLastEncoding);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -96,6 +137,27 @@ public class CreateTest {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateCustomType() throws Exception {
|
||||
|
||||
Observation obs = new Observation();
|
||||
obs.getIdentifier().setValue("001");
|
||||
|
||||
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Observation");
|
||||
httpPost.setEntity(new StringEntity(new FhirContext().newJsonParser().encodeResourceToString(obs), ContentType.create(Constants.CT_FHIR_JSON, "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 + "/Observation/001/_history/002", status.getFirstHeader("location").getValue());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateJson() throws Exception {
|
||||
|
||||
|
@ -118,27 +180,6 @@ public class CreateTest {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateCustomType() throws Exception {
|
||||
|
||||
Observation obs = new Observation();
|
||||
obs.getIdentifier().setValue("001");
|
||||
|
||||
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Observation");
|
||||
httpPost.setEntity(new StringEntity(new FhirContext().newJsonParser().encodeResourceToString(obs), ContentType.create(Constants.CT_FHIR_JSON, "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 + "/Observation/001/_history/002", status.getFirstHeader("location").getValue());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateWithUnprocessableEntity() throws Exception {
|
||||
|
||||
|
@ -178,7 +219,9 @@ public class CreateTest {
|
|||
|
||||
ServletHandler proxyHandler = new ServletHandler();
|
||||
RestfulServer servlet = new RestfulServer();
|
||||
servlet.setResourceProviders(patientProvider, ourReportProvider, new DummyAdverseReactionResourceProvider(), new CustomObservationProvider());
|
||||
servlet.setResourceProviders(
|
||||
patientProvider, ourReportProvider, new DummyAdverseReactionResourceProvider(), new CustomObservationProvider(),
|
||||
new OrganizationProvider());
|
||||
ServletHolder servletHolder = new ServletHolder(servlet);
|
||||
proxyHandler.addServletWithMapping(servletHolder, "/*");
|
||||
ourServer.setHandler(proxyHandler);
|
||||
|
@ -190,10 +233,53 @@ public class CreateTest {
|
|||
ourClient = builder.build();
|
||||
|
||||
}
|
||||
@ResourceDef(name = "Observation")
|
||||
public static class CustomObservation extends Observation {
|
||||
|
||||
@Extension(url = "http://foo#string", definedLocally = false, isModifier = false)
|
||||
@Child(name = "string")
|
||||
private StringDt myString;
|
||||
|
||||
public StringDt getString() {
|
||||
if (myString == null) {
|
||||
myString = new StringDt();
|
||||
}
|
||||
return myString;
|
||||
}
|
||||
|
||||
public void setString(StringDt theString) {
|
||||
myString = theString;
|
||||
}
|
||||
}
|
||||
|
||||
public static class CustomObservationProvider implements IResourceProvider {
|
||||
|
||||
@Create()
|
||||
public MethodOutcome createPatient(@ResourceParam CustomObservation thePatient) {
|
||||
IdDt id = new IdDt(thePatient.getIdentifier().getValue().getValue());
|
||||
if (thePatient.getId().isEmpty() == false) {
|
||||
id = thePatient.getId();
|
||||
}
|
||||
return new MethodOutcome(id.withVersion("002"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends IResource> getResourceType() {
|
||||
return Observation.class;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class DiagnosticReportProvider implements IResourceProvider {
|
||||
private TagList myLastTags;
|
||||
|
||||
@Create()
|
||||
public MethodOutcome createDiagnosticReport(@ResourceParam DiagnosticReport thePatient) {
|
||||
OperationOutcome outcome = new OperationOutcome();
|
||||
outcome.addIssue().setDetails("FOOBAR");
|
||||
throw new UnprocessableEntityException(outcome);
|
||||
}
|
||||
|
||||
public TagList getLastTags() {
|
||||
return myLastTags;
|
||||
}
|
||||
|
@ -203,13 +289,6 @@ public class CreateTest {
|
|||
return DiagnosticReport.class;
|
||||
}
|
||||
|
||||
@Create()
|
||||
public MethodOutcome createDiagnosticReport(@ResourceParam DiagnosticReport thePatient) {
|
||||
OperationOutcome outcome = new OperationOutcome();
|
||||
outcome.addIssue().setDetails("FOOBAR");
|
||||
throw new UnprocessableEntityException(outcome);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -252,55 +331,39 @@ public class CreateTest {
|
|||
|
||||
public static class PatientProvider implements IResourceProvider {
|
||||
|
||||
@Create()
|
||||
public MethodOutcome createPatient(@ResourceParam Patient thePatient, @ResourceParam String theResourceBody, @ResourceParam EncodingEnum theEncoding) {
|
||||
IdDt id = new IdDt(thePatient.getIdentifier().get(0).getValue().getValue());
|
||||
if (thePatient.getId().isEmpty() == false) {
|
||||
id = thePatient.getId();
|
||||
}
|
||||
|
||||
ourLastResourceBody=theResourceBody;
|
||||
ourLastEncoding=theEncoding;
|
||||
|
||||
return new MethodOutcome(id.withVersion("002"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends IResource> getResourceType() {
|
||||
return Patient.class;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class OrganizationProvider implements IResourceProvider {
|
||||
|
||||
@Create()
|
||||
public MethodOutcome createPatient(@ResourceParam Patient thePatient) {
|
||||
IdDt id = new IdDt(thePatient.getIdentifier().get(0).getValue().getValue());
|
||||
if (thePatient.getId().isEmpty() == false) {
|
||||
id = thePatient.getId();
|
||||
}
|
||||
return new MethodOutcome(id.withVersion("002"));
|
||||
public MethodOutcome create(@ResourceParam String theResourceBody, @ResourceParam EncodingEnum theEncoding) {
|
||||
ourLastResourceBody=theResourceBody;
|
||||
ourLastEncoding=theEncoding;
|
||||
|
||||
return new MethodOutcome(new IdDt("001"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ResourceDef(name = "Observation")
|
||||
public static class CustomObservation extends Observation {
|
||||
|
||||
@Extension(url = "http://foo#string", definedLocally = false, isModifier = false)
|
||||
@Child(name = "string")
|
||||
private StringDt myString;
|
||||
|
||||
public StringDt getString() {
|
||||
if (myString == null) {
|
||||
myString = new StringDt();
|
||||
}
|
||||
return myString;
|
||||
}
|
||||
|
||||
public void setString(StringDt theString) {
|
||||
myString = theString;
|
||||
}
|
||||
}
|
||||
|
||||
public static class CustomObservationProvider implements IResourceProvider {
|
||||
|
||||
@Override
|
||||
public Class<? extends IResource> getResourceType() {
|
||||
return Observation.class;
|
||||
}
|
||||
|
||||
@Create()
|
||||
public MethodOutcome createPatient(@ResourceParam CustomObservation thePatient) {
|
||||
IdDt id = new IdDt(thePatient.getIdentifier().getValue().getValue());
|
||||
if (thePatient.getId().isEmpty() == false) {
|
||||
id = thePatient.getId();
|
||||
}
|
||||
return new MethodOutcome(id.withVersion("002"));
|
||||
return Organization.class;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,155 @@
|
|||
package ca.uhn.fhir.rest.server;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.http.HttpResponse;
|
||||
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.servlet.ServletHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.hamcrest.core.StringContains;
|
||||
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.dstu.resource.Organization;
|
||||
import ca.uhn.fhir.model.dstu.resource.Patient;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
||||
import ca.uhn.fhir.rest.annotation.Validate;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.util.PortUtil;
|
||||
|
||||
/**
|
||||
* Created by dsotnikov on 2/25/2014.
|
||||
*/
|
||||
public class ValidateTest {
|
||||
private static CloseableHttpClient ourClient;
|
||||
private static EncodingEnum ourLastEncoding;
|
||||
private static String ourLastResourceBody;
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ValidateTest.class);
|
||||
private static int ourPort;
|
||||
private static Server ourServer;
|
||||
|
||||
@Before()
|
||||
public void before() {
|
||||
ourLastResourceBody = null;
|
||||
ourLastEncoding = null;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidate() throws Exception {
|
||||
|
||||
Patient patient = new Patient();
|
||||
patient.addIdentifier().setValue("001");
|
||||
patient.addIdentifier().setValue("002");
|
||||
|
||||
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/_validate");
|
||||
httpPost.setEntity(new StringEntity(new FhirContext().newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
|
||||
|
||||
HttpResponse status = ourClient.execute(httpPost);
|
||||
|
||||
assertEquals(204, status.getStatusLine().getStatusCode());
|
||||
|
||||
assertThat(ourLastResourceBody, stringContainsInOrder("<Patient ", "<identifier>", "<value value=\"001"));
|
||||
assertEquals(EncodingEnum.XML, ourLastEncoding);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateWithNoParsed() throws Exception {
|
||||
|
||||
Organization org = new Organization();
|
||||
org.addIdentifier().setValue("001");
|
||||
org.addIdentifier().setValue("002");
|
||||
|
||||
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Organization/_validate");
|
||||
httpPost.setEntity(new StringEntity(new FhirContext().newJsonParser().encodeResourceToString(org), ContentType.create(Constants.CT_FHIR_JSON, "UTF-8")));
|
||||
|
||||
HttpResponse status = ourClient.execute(httpPost);
|
||||
assertEquals(204, status.getStatusLine().getStatusCode());
|
||||
|
||||
assertThat(ourLastResourceBody, stringContainsInOrder("\"resourceType\":\"Organization\"", "\"identifier\"", "\"value\":\"001"));
|
||||
assertEquals(EncodingEnum.JSON, ourLastEncoding);
|
||||
|
||||
}
|
||||
|
||||
@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, new OrganizationProvider());
|
||||
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 {
|
||||
|
||||
@Validate()
|
||||
public MethodOutcome validatePatient(@ResourceParam Patient thePatient, @ResourceParam String theResourceBody, @ResourceParam EncodingEnum theEncoding) {
|
||||
IdDt id = new IdDt(thePatient.getIdentifier().get(0).getValue().getValue());
|
||||
if (thePatient.getId().isEmpty() == false) {
|
||||
id = thePatient.getId();
|
||||
}
|
||||
|
||||
ourLastResourceBody = theResourceBody;
|
||||
ourLastEncoding = theEncoding;
|
||||
|
||||
return new MethodOutcome(id.withVersion("002"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends IResource> getResourceType() {
|
||||
return Patient.class;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class OrganizationProvider implements IResourceProvider {
|
||||
|
||||
@Validate()
|
||||
public MethodOutcome validate(@ResourceParam String theResourceBody, @ResourceParam EncodingEnum theEncoding) {
|
||||
ourLastResourceBody = theResourceBody;
|
||||
ourLastEncoding = theEncoding;
|
||||
|
||||
return new MethodOutcome(new IdDt("001"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends IResource> getResourceType() {
|
||||
return Organization.class;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
package ca.uhn.fhir.validation;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.hl7.fhir.instance.model.OperationOutcome.IssueSeverity;
|
||||
import org.hl7.fhir.instance.model.StructureDefinition;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.utils.WorkerContext;
|
||||
import org.hl7.fhir.instance.validation.ValidationMessage;
|
||||
import org.w3c.dom.Document;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
|
||||
public class InstanceValidator {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(InstanceValidator.class);
|
||||
|
||||
private FhirContext myCtx;
|
||||
private DocumentBuilderFactory myDocBuilderFactory;
|
||||
|
||||
InstanceValidator(FhirContext theContext) {
|
||||
myCtx = theContext;
|
||||
|
||||
myDocBuilderFactory = DocumentBuilderFactory.newInstance();
|
||||
myDocBuilderFactory.setNamespaceAware(true);
|
||||
}
|
||||
|
||||
public List<ValidationMessage> validate(String theInput, Class<? extends IBaseResource> theResourceType) {
|
||||
WorkerContext workerContext = new WorkerContext();
|
||||
org.hl7.fhir.instance.validation.InstanceValidator v;
|
||||
try {
|
||||
v = new org.hl7.fhir.instance.validation.InstanceValidator(workerContext);
|
||||
} catch (Exception e) {
|
||||
throw new ConfigurationException(e);
|
||||
}
|
||||
|
||||
Document document;
|
||||
try {
|
||||
DocumentBuilder builder = myDocBuilderFactory.newDocumentBuilder();
|
||||
InputSource src = new InputSource(new StringReader(theInput));
|
||||
document = builder.parse(src);
|
||||
} catch (Exception e2) {
|
||||
ourLog.error("Failure to parse XML input", e2);
|
||||
ValidationMessage m = new ValidationMessage();
|
||||
m.setLevel(IssueSeverity.FATAL);
|
||||
m.setMessage("Failed to parse input, it does not appear to be valid XML:" + e2.getMessage());
|
||||
return Collections.singletonList(m);
|
||||
}
|
||||
|
||||
String profileCpName = "/org/hl7/fhir/instance/model/profile/" + myCtx.getResourceDefinition(theResourceType).getName().toLowerCase() + ".profile.xml";
|
||||
String profileText;
|
||||
try {
|
||||
profileText = IOUtils.toString(InstanceValidator.class.getResourceAsStream(profileCpName), "UTF-8");
|
||||
} catch (IOException e1) {
|
||||
throw new ConfigurationException("Failed to load profile from classpath: " + profileCpName, e1);
|
||||
}
|
||||
StructureDefinition profile = myCtx.newXmlParser().parseResource(StructureDefinition.class, profileText);
|
||||
|
||||
try {
|
||||
List<ValidationMessage> results = v.validate(document, profile);
|
||||
return results;
|
||||
} catch (Exception e) {
|
||||
throw new InternalErrorException("Unexpected failure while validating resource", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
package ca.uhn.fhir.validation;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.hl7.fhir.instance.model.OperationOutcome.IssueSeverity;
|
||||
import org.hl7.fhir.instance.model.StructureDefinition;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.utils.WorkerContext;
|
||||
import org.hl7.fhir.instance.validation.ValidationMessage;
|
||||
import org.w3c.dom.Document;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
|
||||
public class StructureDefinitionValidator {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(StructureDefinitionValidator.class);
|
||||
|
||||
private FhirContext myCtx;
|
||||
private DocumentBuilderFactory myDocBuilderFactory;
|
||||
|
||||
StructureDefinitionValidator(FhirContext theContext) {
|
||||
myCtx = theContext;
|
||||
|
||||
myDocBuilderFactory = DocumentBuilderFactory.newInstance();
|
||||
myDocBuilderFactory.setNamespaceAware(true);
|
||||
}
|
||||
|
||||
public List<ValidationMessage> validate(String theInput, EncodingEnum theEncoding, Class<? extends IBaseResource> theResourceType) {
|
||||
WorkerContext workerContext = new WorkerContext();
|
||||
org.hl7.fhir.instance.validation.InstanceValidator v;
|
||||
try {
|
||||
v = new org.hl7.fhir.instance.validation.InstanceValidator(workerContext);
|
||||
} catch (Exception e) {
|
||||
throw new ConfigurationException(e);
|
||||
}
|
||||
|
||||
String profileCpName = "/org/hl7/fhir/instance/model/profile/" + myCtx.getResourceDefinition(theResourceType).getName().toLowerCase() + ".profile.xml";
|
||||
String profileText;
|
||||
try {
|
||||
profileText = IOUtils.toString(StructureDefinitionValidator.class.getResourceAsStream(profileCpName), "UTF-8");
|
||||
} catch (IOException e1) {
|
||||
throw new ConfigurationException("Failed to load profile from classpath: " + profileCpName, e1);
|
||||
}
|
||||
StructureDefinition profile = myCtx.newXmlParser().parseResource(StructureDefinition.class, profileText);
|
||||
|
||||
if (theEncoding == EncodingEnum.XML) {
|
||||
Document document;
|
||||
try {
|
||||
DocumentBuilder builder = myDocBuilderFactory.newDocumentBuilder();
|
||||
InputSource src = new InputSource(new StringReader(theInput));
|
||||
document = builder.parse(src);
|
||||
} catch (Exception e2) {
|
||||
ourLog.error("Failure to parse XML input", e2);
|
||||
ValidationMessage m = new ValidationMessage();
|
||||
m.setLevel(IssueSeverity.FATAL);
|
||||
m.setMessage("Failed to parse input, it does not appear to be valid XML:" + e2.getMessage());
|
||||
return Collections.singletonList(m);
|
||||
}
|
||||
try {
|
||||
List<ValidationMessage> results = v.validate(document, profile);
|
||||
return results;
|
||||
} catch (Exception e) {
|
||||
throw new InternalErrorException("Unexpected failure while validating resource", e);
|
||||
}
|
||||
} else if (theEncoding == EncodingEnum.JSON) {
|
||||
Gson gson = new GsonBuilder().create();
|
||||
JsonObject json = gson.fromJson(theInput, JsonObject.class);
|
||||
try {
|
||||
return v.validate(json, profile);
|
||||
} catch (Exception e) {
|
||||
throw new InternalErrorException("Unexpected failure while validating resource", e);
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unknown encoding: " + theEncoding);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
package ca.uhn.fhir.validation;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hl7.fhir.instance.model.Patient;
|
||||
import org.hl7.fhir.instance.validation.ValidationMessage;
|
||||
import org.junit.Test;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
|
||||
public class InstanceValidatorTest {
|
||||
|
||||
private static FhirContext ourCtx = FhirContext.forDstu2Hl7Org();
|
||||
|
||||
@Test
|
||||
public void testValidateXmlResource() {
|
||||
String input = "<Patient xmlns=\"http://hl7.org/fhir\">"
|
||||
+ "<id value=\"123\"/>"
|
||||
+ "</Patient>";
|
||||
|
||||
InstanceValidator val = new InstanceValidator(ourCtx);
|
||||
List<ValidationMessage> output = val.validate(input, Patient.class);
|
||||
assertEquals(output.toString(), 0, output.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateXmlResourceBadAttributes() {
|
||||
String input = "<Patient xmlns=\"http://hl7.org/fhir\">"
|
||||
+ "<id value=\"123\"/>"
|
||||
+ "<foo value=\"222\"/>"
|
||||
+ "</Patient>";
|
||||
|
||||
InstanceValidator val = new InstanceValidator(ourCtx);
|
||||
List<ValidationMessage> output = val.validate(input, Patient.class);
|
||||
assertEquals(output.toString(), 1, output.size());
|
||||
assertThat(output.toString(), containsString("/f:Patient/f:foo Element is unknown"));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
package ca.uhn.fhir.validation;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hl7.fhir.instance.model.Patient;
|
||||
import org.hl7.fhir.instance.validation.ValidationMessage;
|
||||
import org.junit.Test;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
|
||||
public class StructureDefinitionValidatorTest {
|
||||
|
||||
private static FhirContext ourCtx = FhirContext.forDstu2Hl7Org();
|
||||
|
||||
@Test
|
||||
public void testValidateJsonResource() {
|
||||
String input = "{"
|
||||
+ "\"resourceType\":\"Patient\","
|
||||
+ "\"id\":\"123\""
|
||||
+ "}";
|
||||
|
||||
StructureDefinitionValidator val = new StructureDefinitionValidator(ourCtx);
|
||||
List<ValidationMessage> output = val.validate(input, EncodingEnum.JSON, Patient.class);
|
||||
assertEquals(output.toString(), 0, output.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateJsonResourceBadAttributes() {
|
||||
String input = "{"
|
||||
+ "\"resourceType\":\"Patient\","
|
||||
+ "\"id\":\"123\","
|
||||
+ "\"foo\":\"123\""
|
||||
+ "}";
|
||||
|
||||
|
||||
StructureDefinitionValidator val = new StructureDefinitionValidator(ourCtx);
|
||||
List<ValidationMessage> output = val.validate(input, EncodingEnum.JSON, Patient.class);
|
||||
assertEquals(output.toString(), 1, output.size());
|
||||
assertThat(output.toString(), containsString("/foo Element is unknown"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateXmlResource() {
|
||||
String input = "<Patient xmlns=\"http://hl7.org/fhir\">"
|
||||
+ "<id value=\"123\"/>"
|
||||
+ "</Patient>";
|
||||
|
||||
StructureDefinitionValidator val = new StructureDefinitionValidator(ourCtx);
|
||||
List<ValidationMessage> output = val.validate(input, EncodingEnum.XML, Patient.class);
|
||||
assertEquals(output.toString(), 0, output.size());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testValidateXmlResourceBadAttributes() {
|
||||
String input = "<Patient xmlns=\"http://hl7.org/fhir\">"
|
||||
+ "<id value=\"123\"/>"
|
||||
+ "<foo value=\"222\"/>"
|
||||
+ "</Patient>";
|
||||
|
||||
StructureDefinitionValidator val = new StructureDefinitionValidator(ourCtx);
|
||||
List<ValidationMessage> output = val.validate(input, EncodingEnum.XML, Patient.class);
|
||||
assertEquals(output.toString(), 1, output.size());
|
||||
assertThat(output.toString(), containsString("/f:Patient/f:foo Element is unknown"));
|
||||
}
|
||||
}
|
|
@ -177,6 +177,24 @@
|
|||
<code>http://fhir.example.com/Patient?identifier=system%7C00001</code>
|
||||
</p>
|
||||
|
||||
<a name="raw_update_access"/>
|
||||
<h4>Accessing The Raw Resource Payload</h4>
|
||||
<p>
|
||||
If you wish to have access to the raw resource payload as well as the parsed value
|
||||
for any reason, you may also add parameters which have been annotated
|
||||
with the <code>@ResourceParam</code> of type
|
||||
<code>String</code> (to access the raw resource body) and/or
|
||||
<code>EncodingEnum</code> (to determine which encoding was used)
|
||||
</p>
|
||||
<p>
|
||||
The following example shows how to use these additonal data elements.
|
||||
</p>
|
||||
|
||||
<macro name="snippet">
|
||||
<param name="id" value="updateRaw" />
|
||||
<param name="file" value="examples/src/main/java/example/RestfulPatientResourceProviderMore.java" />
|
||||
</macro>
|
||||
|
||||
<a name="instance_delete" />
|
||||
</section>
|
||||
|
||||
|
@ -339,6 +357,13 @@
|
|||
<code>http://fhir.example.com/Patient<br/>If-None-Exist: Patient?identifier=system%7C0001</code>
|
||||
</p>
|
||||
|
||||
<h4>Accessing The Raw Resource Payload</h4>
|
||||
<p>
|
||||
The create operation also supports access to the raw payload,
|
||||
using the same semantics as raw payload access
|
||||
<a href="#raw_update_access">for the update operation</a>.
|
||||
</p>
|
||||
|
||||
<a name="type_search" />
|
||||
</section>
|
||||
|
||||
|
|
Loading…
Reference in New Issue