Allow bundle as param in transaction method
This commit is contained in:
parent
8078452f78
commit
fd41bafa82
|
@ -34,7 +34,7 @@ public class ServerMetadataExamples {
|
||||||
String linkAlternate = "Patient/7736";
|
String linkAlternate = "Patient/7736";
|
||||||
ResourceMetadataKeyEnum.LINK_ALTERNATE.put(patient, linkAlternate);
|
ResourceMetadataKeyEnum.LINK_ALTERNATE.put(patient, linkAlternate);
|
||||||
String linkSearch = "Patient?name=smith&name=john";
|
String linkSearch = "Patient?name=smith&name=john";
|
||||||
ResourceMetadataKeyEnum.LINK_ALTERNATE.put(patient, linkSearch);
|
ResourceMetadataKeyEnum.LINK_SEARCH.put(patient, linkSearch);
|
||||||
|
|
||||||
// Set the published and updated dates
|
// Set the published and updated dates
|
||||||
InstantDt pubDate = new InstantDt("2011-02-22");
|
InstantDt pubDate = new InstantDt("2011-02-22");
|
||||||
|
|
|
@ -83,6 +83,10 @@
|
||||||
<action type="fix">
|
<action type="fix">
|
||||||
Category header (for tags) is correctly read in client for "read" operation
|
Category header (for tags) is correctly read in client for "read" operation
|
||||||
</action>
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Transaction method in server can now have parameter type Bundle instead of
|
||||||
|
List<IResource>
|
||||||
|
</action>
|
||||||
</release>
|
</release>
|
||||||
<release version="0.5" date="2014-Jul-30">
|
<release version="0.5" date="2014-Jul-30">
|
||||||
<action type="add">
|
<action type="add">
|
||||||
|
|
|
@ -1,5 +1,25 @@
|
||||||
package ca.uhn.fhir.model.view;
|
package ca.uhn.fhir.model.view;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR - Core Library
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 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 java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
|
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
|
||||||
|
|
|
@ -24,7 +24,15 @@ import java.lang.annotation.ElementType;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.model.api.Bundle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parameter annotation for the "transaction" operation. The parameter annotated with this
|
||||||
|
* annotation must be either of type <code>{@link Bundle}</code> or of type
|
||||||
|
* <code>{@link List}<IResource></code>
|
||||||
|
*/
|
||||||
@Target(value=ElementType.PARAMETER)
|
@Target(value=ElementType.PARAMETER)
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
public @interface TransactionParam {
|
public @interface TransactionParam {
|
||||||
|
|
|
@ -52,9 +52,9 @@ public class TransactionMethodBinding extends BaseResourceReturningMethodBinding
|
||||||
|
|
||||||
public TransactionMethodBinding(Method theMethod, FhirContext theConetxt, Object theProvider) {
|
public TransactionMethodBinding(Method theMethod, FhirContext theConetxt, Object theProvider) {
|
||||||
super(null, theMethod, theConetxt, theProvider);
|
super(null, theMethod, theConetxt, theProvider);
|
||||||
|
|
||||||
myTransactionParamIndex = -1;
|
myTransactionParamIndex = -1;
|
||||||
int index=0;
|
int index = 0;
|
||||||
for (IParameter next : getParameters()) {
|
for (IParameter next : getParameters()) {
|
||||||
if (next instanceof TransactionParamBinder) {
|
if (next instanceof TransactionParamBinder) {
|
||||||
myTransactionParamIndex = index;
|
myTransactionParamIndex = index;
|
||||||
|
@ -62,7 +62,7 @@ public class TransactionMethodBinding extends BaseResourceReturningMethodBinding
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (myTransactionParamIndex==-1) {
|
if (myTransactionParamIndex == -1) {
|
||||||
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type " + theMethod.getDeclaringClass().getCanonicalName() + " does not have a parameter annotated with the @" + TransactionParam.class + " annotation");
|
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type " + theMethod.getDeclaringClass().getCanonicalName() + " does not have a parameter annotated with the @" + TransactionParam.class + " annotation");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -72,7 +72,6 @@ public class TransactionMethodBinding extends BaseResourceReturningMethodBinding
|
||||||
return RestfulOperationSystemEnum.TRANSACTION;
|
return RestfulOperationSystemEnum.TRANSACTION;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean incomingServerRequestMatchesMethod(Request theRequest) {
|
public boolean incomingServerRequestMatchesMethod(Request theRequest) {
|
||||||
if (theRequest.getRequestType() != RequestType.POST) {
|
if (theRequest.getRequestType() != RequestType.POST) {
|
||||||
|
@ -95,16 +94,23 @@ public class TransactionMethodBinding extends BaseResourceReturningMethodBinding
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@Override
|
@Override
|
||||||
public IBundleProvider invokeServer(Request theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
|
public IBundleProvider invokeServer(Request theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
|
||||||
List<IResource> resources = (List<IResource>) theMethodParams[myTransactionParamIndex];
|
|
||||||
|
// Grab the IDs of all of the resources in the transaction
|
||||||
List<IdDt> oldIds= new ArrayList<IdDt>();
|
List<IResource> resources;
|
||||||
|
if (theMethodParams[myTransactionParamIndex] instanceof Bundle) {
|
||||||
|
resources = ((Bundle) theMethodParams[myTransactionParamIndex]).toListOfResources();
|
||||||
|
} else {
|
||||||
|
resources = (List<IResource>) theMethodParams[myTransactionParamIndex];
|
||||||
|
}
|
||||||
|
List<IdDt> oldIds = new ArrayList<IdDt>();
|
||||||
for (IResource next : resources) {
|
for (IResource next : resources) {
|
||||||
oldIds.add(next.getId());
|
oldIds.add(next.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
Object response= invokeServerMethod(theMethodParams);
|
// Call the server implementation method
|
||||||
|
Object response = invokeServerMethod(theMethodParams);
|
||||||
IBundleProvider retVal = toResourceList(response);
|
IBundleProvider retVal = toResourceList(response);
|
||||||
|
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
if (retVal.size() != resources.size()) {
|
if (retVal.size() != resources.size()) {
|
||||||
if (retVal.size() > 0 && retVal.getResources(0, 1).get(0) instanceof OperationOutcome) {
|
if (retVal.size() > 0 && retVal.getResources(0, 1).get(0) instanceof OperationOutcome) {
|
||||||
|
@ -113,22 +119,22 @@ public class TransactionMethodBinding extends BaseResourceReturningMethodBinding
|
||||||
throw new InternalErrorException("Transaction bundle contained " + resources.size() + " entries, but server method response contained " + retVal.size() + " entries (must be the same)");
|
throw new InternalErrorException("Transaction bundle contained " + resources.size() + " entries, but server method response contained " + retVal.size() + " entries (must be the same)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<IResource> retResources = retVal.getResources(offset, retVal.size());
|
List<IResource> retResources = retVal.getResources(offset, retVal.size());
|
||||||
for (int i =0; i < resources.size(); i++) {
|
for (int i = 0; i < resources.size(); i++) {
|
||||||
IdDt oldId = oldIds.get(i);
|
IdDt oldId = oldIds.get(i);
|
||||||
IResource newRes = retResources.get(i);
|
IResource newRes = retResources.get(i);
|
||||||
if (newRes.getId() == null || newRes.getId().isEmpty()) {
|
if (newRes.getId() == null || newRes.getId().isEmpty()) {
|
||||||
throw new InternalErrorException("Transaction method returned resource at index " + i + " with no id specified - IResource#setId(IdDt)");
|
throw new InternalErrorException("Transaction method returned resource at index " + i + " with no id specified - IResource#setId(IdDt)");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oldId != null && !oldId.isEmpty()) {
|
if (oldId != null && !oldId.isEmpty()) {
|
||||||
if (!oldId.equals(newRes.getId())) {
|
if (!oldId.equals(newRes.getId())) {
|
||||||
newRes.getResourceMetadata().put(ResourceMetadataKeyEnum.PREVIOUS_ID, oldId);
|
newRes.getResourceMetadata().put(ResourceMetadataKeyEnum.PREVIOUS_ID, oldId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,11 +153,15 @@ public class TransactionMethodBinding extends BaseResourceReturningMethodBinding
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BaseHttpClientInvocation invokeClient(Object[] theArgs) throws InternalErrorException {
|
public BaseHttpClientInvocation invokeClient(Object[] theArgs) throws InternalErrorException {
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
List<IResource> resources = (List<IResource>) theArgs[myTransactionParamIndex];
|
|
||||||
FhirContext context = getContext();
|
FhirContext context = getContext();
|
||||||
|
if (theArgs[myTransactionParamIndex] instanceof Bundle) {
|
||||||
return createTransactionInvocation(resources, context);
|
Bundle bundle = (Bundle) theArgs[myTransactionParamIndex];
|
||||||
|
return createTransactionInvocation(bundle, context);
|
||||||
|
} else {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
List<IResource> resources = (List<IResource>) theArgs[myTransactionParamIndex];
|
||||||
|
return createTransactionInvocation(resources, context);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static BaseHttpClientInvocation createTransactionInvocation(List<IResource> theResources, FhirContext theContext) {
|
public static BaseHttpClientInvocation createTransactionInvocation(List<IResource> theResources, FhirContext theContext) {
|
||||||
|
|
|
@ -38,40 +38,56 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
|
|
||||||
class TransactionParamBinder implements IParameter {
|
class TransactionParamBinder implements IParameter {
|
||||||
|
|
||||||
|
private boolean myParamIsBundle;
|
||||||
|
|
||||||
public TransactionParamBinder() {
|
public TransactionParamBinder() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void translateClientArgumentIntoQueryArgument(FhirContext theContext, Object theSourceClientArgument, Map<String, List<String>> theTargetQueryArguments, BaseHttpClientInvocation theClientInvocation) throws InternalErrorException {
|
public void translateClientArgumentIntoQueryArgument(FhirContext theContext, Object theSourceClientArgument, Map<String, List<String>> theTargetQueryArguments, BaseHttpClientInvocation theClientInvocation) throws InternalErrorException {
|
||||||
// TODO Auto-generated method stub
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object translateQueryParametersIntoServerArgument(Request theRequest, Object theRequestContents) throws InternalErrorException, InvalidRequestException {
|
public Object translateQueryParametersIntoServerArgument(Request theRequest, Object theRequestContents) throws InternalErrorException, InvalidRequestException {
|
||||||
Bundle resource = (Bundle) theRequestContents;
|
Bundle resource = (Bundle) theRequestContents;
|
||||||
|
if (myParamIsBundle) {
|
||||||
|
return resource;
|
||||||
|
}
|
||||||
|
|
||||||
ArrayList<IResource> retVal = new ArrayList<IResource>();
|
ArrayList<IResource> retVal = new ArrayList<IResource>();
|
||||||
for (BundleEntry next : resource.getEntries()) {
|
for (BundleEntry next : resource.getEntries()) {
|
||||||
if (next.getResource() != null) {
|
if (next.getResource() != null) {
|
||||||
retVal.add(next.getResource());
|
retVal.add(next.getResource());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initializeTypes(Method theMethod, Class<? extends Collection<?>> theOuterCollectionType, Class<? extends Collection<?>> theInnerCollectionType, Class<?> theParameterType) {
|
public void initializeTypes(Method theMethod, Class<? extends Collection<?>> theOuterCollectionType, Class<? extends Collection<?>> theInnerCollectionType, Class<?> theParameterType) {
|
||||||
if (theOuterCollectionType != null) {
|
if (theOuterCollectionType != null) {
|
||||||
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type '" +theMethod.getDeclaringClass().getCanonicalName()+ "' is annotated with @" + TransactionParam.class.getName() + " but can not be a collection of collections");
|
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type '" + theMethod.getDeclaringClass().getCanonicalName() + "' is annotated with @" + TransactionParam.class.getName() + " but can not be a collection of collections");
|
||||||
}
|
}
|
||||||
if (theInnerCollectionType.equals(List.class)==false) {
|
if (theParameterType.equals(Bundle.class)) {
|
||||||
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type '" +theMethod.getDeclaringClass().getCanonicalName()+ "' is annotated with @" + TransactionParam.class.getName() + " but is not of type List<" + IResource.class.getCanonicalName()+">");
|
myParamIsBundle=true;
|
||||||
}
|
if (theInnerCollectionType!=null) {
|
||||||
if (theParameterType.equals(IResource.class)==false) {
|
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type '" + theMethod.getDeclaringClass().getCanonicalName() + "' is annotated with @" + TransactionParam.class.getName() + " but is not of type List<" + IResource.class.getCanonicalName()
|
||||||
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type '" +theMethod.getDeclaringClass().getCanonicalName()+ "' is annotated with @" + TransactionParam.class.getName() + " but is not of type List<" + IResource.class.getCanonicalName()+">");
|
+ "> or Bundle");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
myParamIsBundle=false;
|
||||||
|
if (theInnerCollectionType.equals(List.class) == false) {
|
||||||
|
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type '" + theMethod.getDeclaringClass().getCanonicalName() + "' is annotated with @" + TransactionParam.class.getName() + " but is not of type List<" + IResource.class.getCanonicalName()
|
||||||
|
+ "> or Bundle");
|
||||||
|
}
|
||||||
|
if (theParameterType.equals(IResource.class) == false) {
|
||||||
|
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type '" + theMethod.getDeclaringClass().getCanonicalName() + "' is annotated with @" + TransactionParam.class.getName() + " but is not of type List<" + IResource.class.getCanonicalName()
|
||||||
|
+ "> or Bundle");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,25 @@
|
||||||
package ca.uhn.fhir.rest.param;
|
package ca.uhn.fhir.rest.param;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR - Core Library
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 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 ca.uhn.fhir.model.api.IQueryParameterType;
|
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||||
import ca.uhn.fhir.rest.server.Constants;
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,25 @@
|
||||||
package ca.uhn.fhir.util;
|
package ca.uhn.fhir.util;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR - Core Library
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 University Health Network
|
||||||
|
* %%
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
|
||||||
public class UrlUtil {
|
public class UrlUtil {
|
||||||
|
|
||||||
public static boolean isAbsolute(String theValue) {
|
public static boolean isAbsolute(String theValue) {
|
||||||
|
|
|
@ -1201,6 +1201,11 @@
|
||||||
<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>
|
||||||
|
Transaction methods require one parameter annotated with @TransactionParam, and that
|
||||||
|
parameter may be of type List<IResource> or Bundle.
|
||||||
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Example URL to invoke this method:
|
Example URL to invoke this method:
|
||||||
<br />
|
<br />
|
||||||
|
|
|
@ -75,7 +75,7 @@ public class TransactionClientTest {
|
||||||
when(httpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_ATOM_XML + "; charset=UTF-8"));
|
when(httpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_ATOM_XML + "; charset=UTF-8"));
|
||||||
when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(createBundle()), Charset.forName("UTF-8")));
|
when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(createBundle()), Charset.forName("UTF-8")));
|
||||||
|
|
||||||
client.searchWithParam(resources);
|
client.transaction(resources);
|
||||||
|
|
||||||
assertEquals(HttpPost.class, capt.getValue().getClass());
|
assertEquals(HttpPost.class, capt.getValue().getClass());
|
||||||
HttpPost post = (HttpPost) capt.getValue();
|
HttpPost post = (HttpPost) capt.getValue();
|
||||||
|
@ -92,6 +92,47 @@ public class TransactionClientTest {
|
||||||
assertTrue(bundle.getEntries().get(1).getId().isEmpty());
|
assertTrue(bundle.getEntries().get(1).getId().isEmpty());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSimpleTransactionWithBundleParam() throws Exception {
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.setId(new IdDt("Patient/testPersistWithSimpleLinkP01"));
|
||||||
|
patient.addIdentifier("urn:system", "testPersistWithSimpleLinkP01");
|
||||||
|
patient.addName().addFamily("Tester").addGiven("Joe");
|
||||||
|
|
||||||
|
Observation obs = new Observation();
|
||||||
|
obs.getName().addCoding().setSystem("urn:system").setCode("testPersistWithSimpleLinkO01");
|
||||||
|
obs.setSubject(new ResourceReferenceDt("Patient/testPersistWithSimpleLinkP01"));
|
||||||
|
|
||||||
|
Bundle transactionBundle = Bundle.withResources(Arrays.asList((IResource)patient, obs), ctx, "http://foo");
|
||||||
|
|
||||||
|
IBundleClient client = ctx.newRestfulClient(IBundleClient.class, "http://foo");
|
||||||
|
|
||||||
|
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||||
|
when(httpClient.execute(capt.capture())).thenReturn(httpResponse);
|
||||||
|
when(httpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||||
|
when(httpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_ATOM_XML + "; charset=UTF-8"));
|
||||||
|
when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(createBundle()), Charset.forName("UTF-8")));
|
||||||
|
|
||||||
|
client.transaction(transactionBundle);
|
||||||
|
|
||||||
|
assertEquals(HttpPost.class, capt.getValue().getClass());
|
||||||
|
HttpPost post = (HttpPost) capt.getValue();
|
||||||
|
assertEquals("http://foo/", post.getURI().toString());
|
||||||
|
|
||||||
|
Bundle bundle = ctx.newXmlParser().parseBundle(new InputStreamReader(post.getEntity().getContent()));
|
||||||
|
ourLog.info(ctx.newXmlParser().setPrettyPrint(true).encodeBundleToString(bundle));
|
||||||
|
|
||||||
|
assertEquals(2, bundle.size());
|
||||||
|
assertEquals("http://foo/Patient/testPersistWithSimpleLinkP01", bundle.getEntries().get(0).getId().getValue());
|
||||||
|
assertEquals("http://foo/Patient/testPersistWithSimpleLinkP01", bundle.getEntries().get(0).getLinkSelf().getValue());
|
||||||
|
assertEquals(null, bundle.getEntries().get(0).getLinkAlternate().getValue());
|
||||||
|
|
||||||
|
assertTrue(bundle.getEntries().get(1).getId().isEmpty());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TransactionClientTest.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TransactionClientTest.class);
|
||||||
private String createBundle() {
|
private String createBundle() {
|
||||||
return ctx.newXmlParser().encodeBundleToString(new Bundle());
|
return ctx.newXmlParser().encodeBundleToString(new Bundle());
|
||||||
|
@ -100,7 +141,15 @@ public class TransactionClientTest {
|
||||||
private interface IClient extends IBasicClient {
|
private interface IClient extends IBasicClient {
|
||||||
|
|
||||||
@Transaction
|
@Transaction
|
||||||
public List<IResource> searchWithParam(@TransactionParam List<IResource> theResources);
|
public List<IResource> transaction(@TransactionParam List<IResource> theResources);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private interface IBundleClient extends IBasicClient {
|
||||||
|
|
||||||
|
@Transaction
|
||||||
|
public List<IResource> transaction(@TransactionParam Bundle theResources);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -232,7 +232,7 @@ public class SearchTest {
|
||||||
public static class DummyPatientResourceProvider implements IResourceProvider {
|
public static class DummyPatientResourceProvider implements IResourceProvider {
|
||||||
|
|
||||||
@Search
|
@Search
|
||||||
public List<Patient> findPatient(@OptionalParam(name = "_id") StringParam theParam) {
|
public List<Patient> findPatient(@RequiredParam(name = "_id") StringParam theParam) {
|
||||||
ArrayList<Patient> retVal = new ArrayList<Patient>();
|
ArrayList<Patient> retVal = new ArrayList<Patient>();
|
||||||
|
|
||||||
Patient patient = new Patient();
|
Patient patient = new Patient();
|
||||||
|
@ -246,7 +246,7 @@ public class SearchTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Search
|
@Search
|
||||||
public List<Patient> findPatientByAAA01(@OptionalParam(name = "AAA") StringParam theParam) {
|
public List<Patient> findPatientByAAA01(@RequiredParam(name = "AAA") StringParam theParam) {
|
||||||
ArrayList<Patient> retVal = new ArrayList<Patient>();
|
ArrayList<Patient> retVal = new ArrayList<Patient>();
|
||||||
|
|
||||||
Patient patient = new Patient();
|
Patient patient = new Patient();
|
||||||
|
|
|
@ -0,0 +1,234 @@
|
||||||
|
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.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.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.Bundle;
|
||||||
|
import ca.uhn.fhir.model.api.BundleEntry;
|
||||||
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
|
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
|
||||||
|
import ca.uhn.fhir.model.dstu.resource.OperationOutcome;
|
||||||
|
import ca.uhn.fhir.model.dstu.resource.Patient;
|
||||||
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
|
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Transaction;
|
||||||
|
import ca.uhn.fhir.rest.annotation.TransactionParam;
|
||||||
|
import ca.uhn.fhir.testutil.RandomServerPortProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by dsotnikov on 2/25/2014.
|
||||||
|
*/
|
||||||
|
public class TransactionWithBundleParamTest {
|
||||||
|
|
||||||
|
private static CloseableHttpClient ourClient;
|
||||||
|
private static FhirContext ourCtx = new FhirContext();
|
||||||
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TransactionWithBundleParamTest.class);
|
||||||
|
private static int ourPort;
|
||||||
|
private static boolean ourReturnOperationOutcome;
|
||||||
|
|
||||||
|
private static Server ourServer;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void before() {
|
||||||
|
ourReturnOperationOutcome = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTransaction() throws Exception {
|
||||||
|
Bundle b = new Bundle();
|
||||||
|
InstantDt nowInstant = InstantDt.withCurrentTime();
|
||||||
|
|
||||||
|
Patient p1 = new Patient();
|
||||||
|
p1.addName().addFamily("Family1");
|
||||||
|
BundleEntry entry = b.addEntry();
|
||||||
|
entry.getId().setValue("1");
|
||||||
|
entry.setResource(p1);
|
||||||
|
|
||||||
|
Patient p2 = new Patient();
|
||||||
|
p2.addName().addFamily("Family2");
|
||||||
|
entry = b.addEntry();
|
||||||
|
entry.getId().setValue("2");
|
||||||
|
entry.setResource(p2);
|
||||||
|
|
||||||
|
BundleEntry deletedEntry = b.addEntry();
|
||||||
|
deletedEntry.setId(new IdDt("Patient/3"));
|
||||||
|
deletedEntry.setDeleted(nowInstant);
|
||||||
|
|
||||||
|
String bundleString = ourCtx.newXmlParser().setPrettyPrint(true).encodeBundleToString(b);
|
||||||
|
ourLog.info(bundleString);
|
||||||
|
|
||||||
|
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/");
|
||||||
|
httpPost.addHeader("Accept", Constants.CT_ATOM_XML + "; pretty=true");
|
||||||
|
httpPost.setEntity(new StringEntity(bundleString, ContentType.create(Constants.CT_ATOM_XML, "UTF-8")));
|
||||||
|
HttpResponse status = ourClient.execute(httpPost);
|
||||||
|
String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
|
||||||
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
|
|
||||||
|
ourLog.info(responseContent);
|
||||||
|
|
||||||
|
Bundle bundle = new FhirContext().newXmlParser().parseBundle(responseContent);
|
||||||
|
assertEquals(3, bundle.size());
|
||||||
|
|
||||||
|
BundleEntry entry0 = bundle.getEntries().get(0);
|
||||||
|
assertEquals("http://localhost:" + ourPort + "/Patient/81", entry0.getId().getValue());
|
||||||
|
assertEquals("http://localhost:" + ourPort + "/Patient/81/_history/91", entry0.getLinkSelf().getValue());
|
||||||
|
assertEquals("http://localhost:" + ourPort + "/Patient/1", entry0.getLinkAlternate().getValue());
|
||||||
|
|
||||||
|
BundleEntry entry1 = bundle.getEntries().get(1);
|
||||||
|
assertEquals("http://localhost:" + ourPort + "/Patient/82", entry1.getId().getValue());
|
||||||
|
assertEquals("http://localhost:" + ourPort + "/Patient/82/_history/92", entry1.getLinkSelf().getValue());
|
||||||
|
assertEquals("http://localhost:" + ourPort + "/Patient/2", entry1.getLinkAlternate().getValue());
|
||||||
|
|
||||||
|
BundleEntry entry2 = bundle.getEntries().get(2);
|
||||||
|
assertEquals("http://localhost:" + ourPort + "/Patient/3", entry2.getId().getValue());
|
||||||
|
assertEquals("http://localhost:" + ourPort + "/Patient/3/_history/93", entry2.getLinkSelf().getValue());
|
||||||
|
assertEquals(nowInstant.getValueAsString(), entry2.getDeletedAt().getValueAsString());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTransactionWithOperationOutcome() throws Exception {
|
||||||
|
ourReturnOperationOutcome = true;
|
||||||
|
|
||||||
|
Bundle b = new Bundle();
|
||||||
|
InstantDt nowInstant = InstantDt.withCurrentTime();
|
||||||
|
|
||||||
|
Patient p1 = new Patient();
|
||||||
|
p1.addName().addFamily("Family1");
|
||||||
|
BundleEntry entry = b.addEntry();
|
||||||
|
entry.getId().setValue("1");
|
||||||
|
entry.setResource(p1);
|
||||||
|
|
||||||
|
Patient p2 = new Patient();
|
||||||
|
p2.addName().addFamily("Family2");
|
||||||
|
entry = b.addEntry();
|
||||||
|
entry.getId().setValue("2");
|
||||||
|
entry.setResource(p2);
|
||||||
|
|
||||||
|
BundleEntry deletedEntry = b.addEntry();
|
||||||
|
deletedEntry.setId(new IdDt("Patient/3"));
|
||||||
|
deletedEntry.setDeleted(nowInstant);
|
||||||
|
|
||||||
|
String bundleString = ourCtx.newXmlParser().setPrettyPrint(true).encodeBundleToString(b);
|
||||||
|
ourLog.info(bundleString);
|
||||||
|
|
||||||
|
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/");
|
||||||
|
httpPost.addHeader("Accept", Constants.CT_ATOM_XML + "; pretty=true");
|
||||||
|
httpPost.setEntity(new StringEntity(bundleString, ContentType.create(Constants.CT_ATOM_XML, "UTF-8")));
|
||||||
|
HttpResponse status = ourClient.execute(httpPost);
|
||||||
|
String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
|
||||||
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
|
|
||||||
|
ourLog.info(responseContent);
|
||||||
|
|
||||||
|
Bundle bundle = new FhirContext().newXmlParser().parseBundle(responseContent);
|
||||||
|
assertEquals(4, bundle.size());
|
||||||
|
|
||||||
|
assertEquals(OperationOutcome.class, bundle.getEntries().get(0).getResource().getClass());
|
||||||
|
assertEquals("OperationOutcome (no ID)", bundle.getEntries().get(0).getTitle().getValue());
|
||||||
|
|
||||||
|
BundleEntry entry0 = bundle.getEntries().get(1);
|
||||||
|
assertEquals("http://localhost:" + ourPort + "/Patient/81", entry0.getId().getValue());
|
||||||
|
assertEquals("http://localhost:" + ourPort + "/Patient/81/_history/91", entry0.getLinkSelf().getValue());
|
||||||
|
assertEquals("http://localhost:" + ourPort + "/Patient/1", entry0.getLinkAlternate().getValue());
|
||||||
|
|
||||||
|
BundleEntry entry1 = bundle.getEntries().get(2);
|
||||||
|
assertEquals("http://localhost:" + ourPort + "/Patient/82", entry1.getId().getValue());
|
||||||
|
assertEquals("http://localhost:" + ourPort + "/Patient/82/_history/92", entry1.getLinkSelf().getValue());
|
||||||
|
assertEquals("http://localhost:" + ourPort + "/Patient/2", entry1.getLinkAlternate().getValue());
|
||||||
|
|
||||||
|
BundleEntry entry2 = bundle.getEntries().get(3);
|
||||||
|
assertEquals("http://localhost:" + ourPort + "/Patient/3", entry2.getId().getValue());
|
||||||
|
assertEquals("http://localhost:" + ourPort + "/Patient/3/_history/93", entry2.getLinkSelf().getValue());
|
||||||
|
assertEquals(nowInstant.getValueAsString(), entry2.getDeletedAt().getValueAsString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void afterClass() throws Exception {
|
||||||
|
ourServer.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void beforeClass() throws Exception {
|
||||||
|
ourPort = RandomServerPortProvider.findFreePort();
|
||||||
|
ourServer = new Server(ourPort);
|
||||||
|
|
||||||
|
DummyProvider patientProvider = new DummyProvider();
|
||||||
|
RestfulServer server = new RestfulServer();
|
||||||
|
server.setProviders(patientProvider);
|
||||||
|
|
||||||
|
org.eclipse.jetty.servlet.ServletContextHandler proxyHandler = new org.eclipse.jetty.servlet.ServletContextHandler();
|
||||||
|
proxyHandler.setContextPath("/");
|
||||||
|
|
||||||
|
ServletHolder handler = new ServletHolder();
|
||||||
|
handler.setServlet(server);
|
||||||
|
proxyHandler.addServlet(handler, "/*");
|
||||||
|
|
||||||
|
ourServer.setHandler(proxyHandler);
|
||||||
|
ourServer.start();
|
||||||
|
|
||||||
|
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
|
||||||
|
HttpClientBuilder builder = HttpClientBuilder.create();
|
||||||
|
builder.setConnectionManager(connectionManager);
|
||||||
|
ourClient = builder.build();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by dsotnikov on 2/25/2014.
|
||||||
|
*/
|
||||||
|
public static class DummyProvider {
|
||||||
|
|
||||||
|
@Transaction
|
||||||
|
public List<IResource> transaction(@TransactionParam Bundle theResources) {
|
||||||
|
int index=1;
|
||||||
|
for (IResource next : theResources.toListOfResources()) {
|
||||||
|
String newId = "8"+Integer.toString(index);
|
||||||
|
if (next.getResourceMetadata().containsKey(ResourceMetadataKeyEnum.DELETED_AT)) {
|
||||||
|
newId = next.getId().getIdPart();
|
||||||
|
}
|
||||||
|
next.setId(new IdDt("Patient", newId, "9"+Integer.toString(index)));
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<IResource> retVal = theResources.toListOfResources();
|
||||||
|
if (ourReturnOperationOutcome) {
|
||||||
|
retVal = new ArrayList<IResource>();
|
||||||
|
OperationOutcome oo = new OperationOutcome();
|
||||||
|
oo.addIssue().setDetails("AAAAA");
|
||||||
|
retVal.add(oo);
|
||||||
|
retVal.addAll(theResources.toListOfResources());
|
||||||
|
}
|
||||||
|
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue