Narrative generator now working

This commit is contained in:
jamesagnew 2014-03-26 17:37:08 -04:00
parent 4953c5629c
commit 082ebb64c1
28 changed files with 457 additions and 112 deletions

View File

@ -35,6 +35,9 @@
--------- ---------
Issues: Issues:
* Submit _narrative parameter as enhancement suggestion
* Need to be able to bind Identifier.system to a set of allowable values in a profile. Graeme * Need to be able to bind Identifier.system to a set of allowable values in a profile. Graeme
has suggested making value[x] repeatable in the profile definition has suggested making value[x] repeatable in the profile definition

View File

@ -1,6 +1,7 @@
package ca.uhn.fhir.model.api; package ca.uhn.fhir.model.api;
import ca.uhn.fhir.model.dstu.composite.ContainedDt; import ca.uhn.fhir.model.dstu.composite.ContainedDt;
import ca.uhn.fhir.model.dstu.composite.NarrativeDt;
public interface IResource extends ICompositeElement { public interface IResource extends ICompositeElement {
@ -15,6 +16,8 @@ public interface IResource extends ICompositeElement {
* </p> * </p>
* TODO: document contained resources and link there * TODO: document contained resources and link there
*/ */
public ContainedDt getContained(); ContainedDt getContained();
NarrativeDt getText();
} }

View File

@ -7,6 +7,7 @@ import ca.uhn.fhir.model.api.IElement;
import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.annotation.ResourceDef; import ca.uhn.fhir.model.api.annotation.ResourceDef;
import ca.uhn.fhir.model.dstu.composite.ContainedDt; import ca.uhn.fhir.model.dstu.composite.ContainedDt;
import ca.uhn.fhir.model.dstu.composite.NarrativeDt;
@ResourceDef(name="Binary", profile="http://hl7.org/fhir/profiles/Binary", id="binary") @ResourceDef(name="Binary", profile="http://hl7.org/fhir/profiles/Binary", id="binary")
public class Binary extends BaseElement implements IResource { public class Binary extends BaseElement implements IResource {
@ -36,4 +37,9 @@ public class Binary extends BaseElement implements IResource {
return null; return null;
} }
@Override
public NarrativeDt getText() {
throw new IllegalStateException();
}
} }

View File

@ -227,8 +227,11 @@ public class DefaultThymeleafNarrativeGenerator extends BaseThymeleafNarrativeGe
} }
@Override @Override
public InputStream getResourceAsStream(TemplateProcessingParameters theTemplateProcessingParameters, String theResourceName) { public InputStream getResourceAsStream(TemplateProcessingParameters theTemplateProcessingParameters, String theClassName) {
String template = myDatatypeClassNameToNarrativeTemplate.get(theResourceName); String template = myDatatypeClassNameToNarrativeTemplate.get(theClassName);
if (template==null) {
throw new NullPointerException("No narrative template exists for datatype: " +theClassName);
}
return new ReaderInputStream(new StringReader(template)); return new ReaderInputStream(new StringReader(template));
} }
} }
@ -255,10 +258,9 @@ public class DefaultThymeleafNarrativeGenerator extends BaseThymeleafNarrativeGe
final Object value = expression.execute(configuration, theArguments); final Object value = expression.execute(configuration, theArguments);
Context context = new Context(); Context context = new Context();
QuantityDt datatype = new QuantityDt();
context.setVariable("resource", value); context.setVariable("resource", value);
String result = myDatatypeTemplateEngine.process(datatype.getClass().getCanonicalName(), context); String result = myDatatypeTemplateEngine.process(value.getClass().getCanonicalName(), context);
Document dom = DOMUtils.getXhtmlDOMFor(new StringReader(result)); Document dom = DOMUtils.getXhtmlDOMFor(new StringReader(result));
theElement.removeAttribute(theAttributeName); theElement.removeAttribute(theAttributeName);

View File

@ -19,6 +19,8 @@ import ca.uhn.fhir.model.primitive.IdDt;
public abstract class BaseParser implements IParser { public abstract class BaseParser implements IParser {
private boolean mySuppressNarratives;
@SuppressWarnings("cast") @SuppressWarnings("cast")
@Override @Override
public <T extends IResource> T parseResource(Class<T> theResourceType, String theMessageString) { public <T extends IResource> T parseResource(Class<T> theResourceType, String theMessageString) {
@ -81,4 +83,19 @@ public abstract class BaseParser implements IParser {
throw new DataFormatException(nextChild + " has no child of type " + type); throw new DataFormatException(nextChild + " has no child of type " + type);
} }
@Override
public IParser setSuppressNarratives(boolean theSuppressNarratives) {
mySuppressNarratives=theSuppressNarratives;
return this;
}
/**
* If set to <code>true</code> (default is <code>false</code>), narratives will not be included in the
* encoded values.
*/
public boolean getSuppressNarratives() {
return mySuppressNarratives;
}
} }

View File

@ -32,4 +32,10 @@ public interface IParser {
IParser setPrettyPrint(boolean thePrettyPrint); IParser setPrettyPrint(boolean thePrettyPrint);
/**
* If set to <code>true</code> (default is <code>false</code>), narratives will not be included in the
* encoded values.
*/
IParser setSuppressNarratives(boolean theSuppressNarratives);
} }

View File

@ -252,7 +252,7 @@ public class JsonParser extends BaseParser implements IParser {
hashSet.add("id"); hashSet.add("id");
hashSet.add("updated"); hashSet.add("updated");
hashSet.add("published"); hashSet.add("published");
BUNDLE_TEXTNODE_CHILDREN=Collections.unmodifiableSet(hashSet); BUNDLE_TEXTNODE_CHILDREN = Collections.unmodifiableSet(hashSet);
} }
private void parseBundleChildren(JsonObject theObject, ParserState<?> theState) { private void parseBundleChildren(JsonObject theObject, ParserState<?> theState) {
@ -439,7 +439,8 @@ public class JsonParser extends BaseParser implements IParser {
return eventWriter; return eventWriter;
} }
private void encodeChildElementToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, JsonGenerator theWriter, IElement theValue, BaseRuntimeElementDefinition<?> theChildDef, String theChildName) throws IOException { private void encodeChildElementToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, JsonGenerator theWriter, IElement theValue, BaseRuntimeElementDefinition<?> theChildDef,
String theChildName) throws IOException {
switch (theChildDef.getChildType()) { switch (theChildDef.getChildType()) {
case PRIMITIVE_DATATYPE: { case PRIMITIVE_DATATYPE: {
@ -518,11 +519,19 @@ public class JsonParser extends BaseParser implements IParser {
break; break;
} }
case PRIMITIVE_XHTML: { case PRIMITIVE_XHTML: {
XhtmlDt dt = (XhtmlDt) theValue; if (!getSuppressNarratives()) {
if (theChildName != null) { XhtmlDt dt = (XhtmlDt) theValue;
theWriter.write(theChildName, dt.getValueAsString()); if (theChildName != null) {
theWriter.write(theChildName, dt.getValueAsString());
} else {
theWriter.write(dt.getValueAsString());
}
} else { } else {
theWriter.write(dt.getValueAsString()); if (theChildName != null) {
// do nothing
} else {
theWriter.writeNull();
}
} }
break; break;
} }
@ -533,13 +542,14 @@ public class JsonParser extends BaseParser implements IParser {
} }
private void encodeCompositeElementChildrenToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, IElement theElement, JsonGenerator theEventWriter, List<? extends BaseRuntimeChildDefinition> theChildren) throws IOException { private void encodeCompositeElementChildrenToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, IElement theElement, JsonGenerator theEventWriter,
List<? extends BaseRuntimeChildDefinition> theChildren) throws IOException {
for (BaseRuntimeChildDefinition nextChild : theChildren) { for (BaseRuntimeChildDefinition nextChild : theChildren) {
if (nextChild instanceof RuntimeChildNarrativeDefinition) { if (nextChild instanceof RuntimeChildNarrativeDefinition) {
INarrativeGenerator gen = myContext.getNarrativeGenerator(); INarrativeGenerator gen = myContext.getNarrativeGenerator();
if (gen != null) { if (gen != null) {
NarrativeDt narr = gen.generateNarrative(theResDef.getResourceProfile(), theResource); NarrativeDt narr = gen.generateNarrative(theResDef.getResourceProfile(), theResource);
if (narr!=null) { if (narr != null) {
RuntimeChildNarrativeDefinition child = (RuntimeChildNarrativeDefinition) nextChild; RuntimeChildNarrativeDefinition child = (RuntimeChildNarrativeDefinition) nextChild;
String childName = nextChild.getChildNameByDatatype(child.getDatatype()); String childName = nextChild.getChildNameByDatatype(child.getDatatype());
BaseRuntimeElementDefinition<?> type = child.getChildByName(childName); BaseRuntimeElementDefinition<?> type = child.getChildByName(childName);
@ -672,7 +682,8 @@ public class JsonParser extends BaseParser implements IParser {
} }
} }
private void encodeCompositeElementToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, IElement theElement, JsonGenerator theEventWriter, BaseRuntimeElementCompositeDefinition<?> resDef) throws IOException, DataFormatException { private void encodeCompositeElementToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, IElement theElement, JsonGenerator theEventWriter,
BaseRuntimeElementCompositeDefinition<?> resDef) throws IOException, DataFormatException {
encodeExtensionsIfPresent(theResDef, theResource, theEventWriter, theElement); encodeExtensionsIfPresent(theResDef, theResource, theEventWriter, theElement);
encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theElement, theEventWriter, resDef.getExtensions()); encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theElement, theEventWriter, resDef.getExtensions());
encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theElement, theEventWriter, resDef.getChildren()); encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theElement, theEventWriter, resDef.getChildren());
@ -707,7 +718,8 @@ public class JsonParser extends BaseParser implements IParser {
theEventWriter.writeEnd(); theEventWriter.writeEnd();
} }
private void encodeUndeclaredExtensions(RuntimeResourceDefinition theResDef, IResource theResource, JsonGenerator theWriter, List<UndeclaredExtension> extensions, String theTagName) throws IOException { private void encodeUndeclaredExtensions(RuntimeResourceDefinition theResDef, IResource theResource, JsonGenerator theWriter, List<UndeclaredExtension> extensions, String theTagName)
throws IOException {
if (extensions.isEmpty()) { if (extensions.isEmpty()) {
return; return;
} }

View File

@ -473,7 +473,7 @@ public class XmlParser extends BaseParser implements IParser {
} }
private void encodeXhtml(XhtmlDt theDt, XMLStreamWriter theEventWriter) throws XMLStreamException { private void encodeXhtml(XhtmlDt theDt, XMLStreamWriter theEventWriter) throws XMLStreamException {
if (theDt == null || theDt.getValue() == null) { if (theDt == null || theDt.getValue() == null || getSuppressNarratives()) {
return; return;
} }

View File

@ -18,6 +18,14 @@ import ca.uhn.fhir.model.api.IResource;
@Target(value=ElementType.METHOD) @Target(value=ElementType.METHOD)
public @interface Search { public @interface Search {
/**
* If specified, this the name for the Named Query
*
* @see See the FHIR specification section on
* <a href="http://www.hl7.org/implement/standards/fhir/search.html#advanced">named queries</a>
*/
String queryName() default "";
/** /**
* The return type for this search method. This generally does not need * The return type for this search method. This generally does not need
* to be populated for a server implementation, since servers will return * to be populated for a server implementation, since servers will return

View File

@ -88,7 +88,8 @@ public abstract class BaseMethodBinding {
if (read != null) { if (read != null) {
return new ReadMethodBinding(methodReturnTypeEnum, returnType, theMethod); return new ReadMethodBinding(methodReturnTypeEnum, returnType, theMethod);
} else if (search != null) { } else if (search != null) {
return new SearchMethodBinding(methodReturnTypeEnum, returnType, theMethod); String queryName = search.queryName();
return new SearchMethodBinding(methodReturnTypeEnum, returnType, theMethod, queryName);
} else if (conformance != null) { } else if (conformance != null) {
return new ConformanceMethodBinding(methodReturnTypeEnum, returnType, theMethod); return new ConformanceMethodBinding(methodReturnTypeEnum, returnType, theMethod);
} else { } else {

View File

@ -1,5 +1,7 @@
package ca.uhn.fhir.rest.method; package ca.uhn.fhir.rest.method;
import java.util.HashMap;
import java.util.Map;
import java.util.Set; import java.util.Set;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IdDt;
@ -9,7 +11,7 @@ public class Request {
private IdDt myId; private IdDt myId;
private String myOperation; private String myOperation;
private Set<String> myParameterNames; private Map<String, String[]> myParameterNames;
private RequestType myRequestType; private RequestType myRequestType;
private String myResourceName; private String myResourceName;
private IdDt myVersion; private IdDt myVersion;
@ -22,7 +24,7 @@ public class Request {
return myOperation; return myOperation;
} }
public Set<String> getParameterNames() { public Map<String, String[]> getParameterNames() {
return myParameterNames; return myParameterNames;
} }
@ -46,8 +48,8 @@ public class Request {
myOperation = theOperation; myOperation = theOperation;
} }
public void setParameterNames(Set<String> theParameterNames) { public void setParameterNames(Map<String, String[]> theParams) {
myParameterNames = theParameterNames; myParameterNames = theParams;
} }
public void setRequestType(RequestType theRequestType) { public void setRequestType(RequestType theRequestType) {
@ -62,11 +64,15 @@ public class Request {
myVersion = theVersion; myVersion = theVersion;
} }
public static Request withResourceAndParams(String theResourceName, RequestType theRequestType, Set<String> theParamNamess) { public static Request withResourceAndParams(String theResourceName, RequestType theRequestType, Set<String> theParamNames) {
Request retVal = new Request(); Request retVal = new Request();
retVal.setResourceName(theResourceName); retVal.setResourceName(theResourceName);
retVal.setParameterNames(theParamNamess);
retVal.setRequestType(theRequestType); retVal.setRequestType(theRequestType);
Map<String, String[]> paramNames = new HashMap<String, String[]>();
for (String next : theParamNames) {
paramNames.put(next, new String[0]);
}
retVal.setParameterNames(paramNames);
return retVal; return retVal;
} }

View File

@ -10,12 +10,15 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationSystemEnum; import ca.uhn.fhir.model.dstu.valueset.RestfulOperationSystemEnum;
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum; import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.client.GetClientInvocation; import ca.uhn.fhir.rest.client.GetClientInvocation;
import ca.uhn.fhir.rest.param.IParameter; import ca.uhn.fhir.rest.param.IParameter;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
@ -27,16 +30,16 @@ import ca.uhn.fhir.util.QueryUtil;
public class SearchMethodBinding extends BaseMethodBinding { public class SearchMethodBinding extends BaseMethodBinding {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchMethodBinding.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchMethodBinding.class);
private Method method; private Method myMethod;
private Class<?> myDeclaredResourceType; private Class<?> myDeclaredResourceType;
private List<IParameter> myParameters; private List<IParameter> myParameters;
private String myQueryName;
public SearchMethodBinding(MethodReturnTypeEnum theMethodReturnTypeEnum, Class<? extends IResource> theReturnResourceType, Method theMethod) { public SearchMethodBinding(MethodReturnTypeEnum theMethodReturnTypeEnum, Class<? extends IResource> theReturnResourceType, Method theMethod, String theQueryName) {
super(theMethodReturnTypeEnum, theReturnResourceType); super(theMethodReturnTypeEnum, theReturnResourceType);
this.method = theMethod; this.myMethod = theMethod;
this.myParameters = Util.getResourceParameters(theMethod); this.myParameters = Util.getResourceParameters(theMethod);
this.myQueryName = StringUtils.defaultIfBlank(theQueryName, null);
this.myDeclaredResourceType = theMethod.getReturnType(); this.myDeclaredResourceType = theMethod.getReturnType();
} }
@ -45,7 +48,7 @@ public class SearchMethodBinding extends BaseMethodBinding {
} }
public Method getMethod() { public Method getMethod() {
return method; return myMethod;
} }
public List<IParameter> getParameters() { public List<IParameter> getParameters() {
@ -59,43 +62,50 @@ public class SearchMethodBinding extends BaseMethodBinding {
@Override @Override
public GetClientInvocation invokeClient(Object[] theArgs) throws InternalErrorException { public GetClientInvocation invokeClient(Object[] theArgs) throws InternalErrorException {
assert theArgs.length == myParameters.size() : "Wrong number of arguments: " + theArgs.length; assert (myQueryName == null || ((theArgs != null ? theArgs.length : 0) == myParameters.size())) : "Wrong number of arguments: " + theArgs;
Map<String, List<String>> args = new LinkedHashMap<String, List<String>>(); Map<String, List<String>> args = new LinkedHashMap<String, List<String>>();
for (int idx = 0; idx < theArgs.length; idx++) { if (myQueryName != null) {
Object object = theArgs[idx]; args.put(Constants.PARAM_QUERY, Collections.singletonList(myQueryName));
IParameter nextParam = myParameters.get(idx); }
if (object == null) { if (theArgs != null) {
if (nextParam.isRequired()) { for (int idx = 0; idx < theArgs.length; idx++) {
throw new NullPointerException("SearchParameter '" + nextParam.getName() + "' is required and may not be null"); Object object = theArgs[idx];
} IParameter nextParam = myParameters.get(idx);
} else {
List<List<String>> value = nextParam.encode(object);
ArrayList<String> paramValues = new ArrayList<String>(value.size());
args.put(nextParam.getName(), paramValues);
for (List<String> nextParamEntry : value) { if (object == null) {
StringBuilder b = new StringBuilder(); if (nextParam.isRequired()) {
for (String str : nextParamEntry) { throw new NullPointerException("SearchParameter '" + nextParam.getName() + "' is required and may not be null");
if (b.length() > 0) {
b.append(",");
}
b.append(str.replace(",", "\\,"));
} }
paramValues.add(b.toString()); } else {
List<List<String>> value = nextParam.encode(object);
ArrayList<String> paramValues = new ArrayList<String>(value.size());
args.put(nextParam.getName(), paramValues);
for (List<String> nextParamEntry : value) {
StringBuilder b = new StringBuilder();
for (String str : nextParamEntry) {
if (b.length() > 0) {
b.append(",");
}
b.append(str.replace(",", "\\,"));
}
paramValues.add(b.toString());
}
} }
} }
} }
return new GetClientInvocation(args, getResourceName()); return new GetClientInvocation(args, getResourceName());
} }
@Override @Override
public List<IResource> invokeServer(IResourceProvider theResourceProvider, IdDt theId, IdDt theVersionId, Map<String, String[]> parameterValues) throws InvalidRequestException, InternalErrorException { public List<IResource> invokeServer(IResourceProvider theResourceProvider, IdDt theId, IdDt theVersionId, Map<String, String[]> parameterValues) throws InvalidRequestException,
InternalErrorException {
assert theId == null; assert theId == null;
assert theVersionId == null; assert theVersionId == null;
@ -110,11 +120,11 @@ public class SearchMethodBinding extends BaseMethodBinding {
continue; continue;
} }
List<List<String>> paramList=new ArrayList<List<String>>(value.length); List<List<String>> paramList = new ArrayList<List<String>>(value.length);
for (String nextParam : value) { for (String nextParam : value) {
if (nextParam.contains(",")==false) { if (nextParam.contains(",") == false) {
paramList.add(Collections.singletonList(nextParam)); paramList.add(Collections.singletonList(nextParam));
}else { } else {
paramList.add(QueryUtil.splitQueryStringByCommasIgnoreEscape(nextParam)); paramList.add(QueryUtil.splitQueryStringByCommasIgnoreEscape(nextParam));
} }
} }
@ -125,7 +135,7 @@ public class SearchMethodBinding extends BaseMethodBinding {
Object response; Object response;
try { try {
response = this.method.invoke(theResourceProvider, params); response = this.myMethod.invoke(theResourceProvider, params);
} catch (IllegalAccessException e) { } catch (IllegalAccessException e) {
throw new InternalErrorException(e); throw new InternalErrorException(e);
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
@ -141,7 +151,7 @@ public class SearchMethodBinding extends BaseMethodBinding {
@Override @Override
public boolean matches(Request theRequest) { public boolean matches(Request theRequest) {
if (!theRequest.getResourceName().equals(getResourceName())) { if (!theRequest.getResourceName().equals(getResourceName())) {
ourLog.trace("Method {} doesn't match because resource name {} != {}", method.getName(), theRequest.getResourceName(), getResourceName()); ourLog.trace("Method {} doesn't match because resource name {} != {}", myMethod.getName(), theRequest.getResourceName(), getResourceName());
return false; return false;
} }
if (theRequest.getId() != null || theRequest.getVersion() != null) { if (theRequest.getId() != null || theRequest.getVersion() != null) {
@ -161,20 +171,35 @@ public class SearchMethodBinding extends BaseMethodBinding {
for (int i = 0; i < this.myParameters.size(); i++) { for (int i = 0; i < this.myParameters.size(); i++) {
IParameter temp = this.myParameters.get(i); IParameter temp = this.myParameters.get(i);
methodParamsTemp.add(temp.getName()); methodParamsTemp.add(temp.getName());
if (temp.isRequired() && !theRequest.getParameterNames().contains(temp.getName())) { if (temp.isRequired() && !theRequest.getParameterNames().containsKey(temp.getName())) {
ourLog.trace("Method {} doesn't match param '{}' is not present", method.getName(), temp.getName()); ourLog.trace("Method {} doesn't match param '{}' is not present", myMethod.getName(), temp.getName());
return false; return false;
} }
} }
boolean retVal = methodParamsTemp.containsAll(theRequest.getParameterNames()); if (myQueryName != null) {
String[] queryNameValues = theRequest.getParameterNames().get(Constants.PARAM_QUERY);
if (queryNameValues != null && StringUtils.isNotBlank(queryNameValues[0])) {
String queryName = queryNameValues[0];
if (!myQueryName.equals(queryName)) {
ourLog.trace("Query name does not match {}", myQueryName);
return false;
} else {
methodParamsTemp.add(Constants.PARAM_QUERY);
}
} else {
ourLog.trace("Query name does not match {}", myQueryName);
return false;
}
}
boolean retVal = methodParamsTemp.containsAll(theRequest.getParameterNames().keySet());
ourLog.trace("Method {} matches: {}", method.getName(), retVal); ourLog.trace("Method {} matches: {}", myMethod.getName(), retVal);
return retVal; return retVal;
} }
public void setMethod(Method method) { public void setMethod(Method method) {
this.method = method; this.myMethod = method;
} }
public void setParameters(List<IParameter> parameters) { public void setParameters(List<IParameter> parameters) {

View File

@ -18,6 +18,11 @@ public class Constants {
public static final Map<String, EncodingUtil> FORMAT_VAL_TO_ENCODING; public static final Map<String, EncodingUtil> FORMAT_VAL_TO_ENCODING;
public static final String CT_XML = "application/xml"; public static final String CT_XML = "application/xml";
public static final String CT_JSON = "application/json"; public static final String CT_JSON = "application/json";
public static final String CT_HTML = "text/html";
public static final String PARAM_NARRATIVE = "_narrative";
public static final String PARAM_HISTORY = "_history";
public static final String PARAM_PRETTY = "_pretty";
public static final String PARAM_QUERY = "_query";
static { static {
Map<String, EncodingUtil> valToEncoding = new HashMap<String, EncodingUtil>(); Map<String, EncodingUtil> valToEncoding = new HashMap<String, EncodingUtil>();

View File

@ -41,8 +41,6 @@ import ca.uhn.fhir.rest.server.provider.ServerProfileProvider;
public abstract class RestfulServer extends HttpServlet { public abstract class RestfulServer extends HttpServlet {
private static final String PARAM_HISTORY = "_history";
private static final String PARAM_PRETTY = "_pretty";
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RestfulServer.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RestfulServer.class);
@ -164,7 +162,7 @@ public abstract class RestfulServer extends HttpServlet {
return myFhirContext; return myFhirContext;
} }
private IParser getNewParser(EncodingUtil theResponseEncoding, boolean thePrettyPrint) { private IParser getNewParser(EncodingUtil theResponseEncoding, boolean thePrettyPrint, NarrativeModeEnum theNarrativeMode) {
IParser parser; IParser parser;
switch (theResponseEncoding) { switch (theResponseEncoding) {
case JSON: case JSON:
@ -175,7 +173,7 @@ public abstract class RestfulServer extends HttpServlet {
parser = myFhirContext.newXmlParser(); parser = myFhirContext.newXmlParser();
break; break;
} }
return parser.setPrettyPrint(thePrettyPrint); return parser.setPrettyPrint(thePrettyPrint).setSuppressNarratives(theNarrativeMode == NarrativeModeEnum.SUPPRESS);
} }
public Collection<ResourceBinding> getResourceBindings() { public Collection<ResourceBinding> getResourceBindings() {
@ -202,6 +200,16 @@ public abstract class RestfulServer extends HttpServlet {
return new ServerProfileProvider(getFhirContext()); return new ServerProfileProvider(getFhirContext());
} }
public enum NarrativeModeEnum {
NORMAL,
SUPPRESS,
ONLY;
public static NarrativeModeEnum valueOfCaseInsensitive(String theCode) {
return valueOf(NarrativeModeEnum.class, theCode.toUpperCase());
}
}
protected void handleRequest(SearchMethodBinding.RequestType requestType, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { protected void handleRequest(SearchMethodBinding.RequestType requestType, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try { try {
@ -260,7 +268,7 @@ public abstract class RestfulServer extends HttpServlet {
Map<String, String[]> params = new HashMap<String, String[]>(request.getParameterMap()); Map<String, String[]> params = new HashMap<String, String[]>(request.getParameterMap());
EncodingUtil responseEncoding = determineResponseEncoding(request, params); EncodingUtil responseEncoding = determineResponseEncoding(request, params);
String[] pretty = params.remove(PARAM_PRETTY); String[] pretty = params.remove(Constants.PARAM_PRETTY);
boolean prettyPrint = false; boolean prettyPrint = false;
if (pretty != null && pretty.length > 0) { if (pretty != null && pretty.length > 0) {
if ("true".equals(pretty[0])) { if ("true".equals(pretty[0])) {
@ -268,6 +276,15 @@ public abstract class RestfulServer extends HttpServlet {
} }
} }
String[] narrative = params.remove(Constants.PARAM_NARRATIVE);
NarrativeModeEnum narrativeMode = null;
if (narrative != null && narrative.length > 0) {
narrativeMode = NarrativeModeEnum.valueOfCaseInsensitive(narrative[0]);
}
if (narrativeMode==null) {
narrativeMode = NarrativeModeEnum.NORMAL;
}
StringTokenizer tok = new StringTokenizer(requestPath, "/"); StringTokenizer tok = new StringTokenizer(requestPath, "/");
if (!tok.hasMoreTokens()) { if (!tok.hasMoreTokens()) {
throw new MethodNotFoundException("No resource name specified"); throw new MethodNotFoundException("No resource name specified");
@ -297,7 +314,7 @@ public abstract class RestfulServer extends HttpServlet {
if (tok.hasMoreTokens()) { if (tok.hasMoreTokens()) {
String nextString = tok.nextToken(); String nextString = tok.nextToken();
if (nextString.startsWith(PARAM_HISTORY)) { if (nextString.startsWith(Constants.PARAM_HISTORY)) {
if (tok.hasMoreTokens()) { if (tok.hasMoreTokens()) {
versionId = new IdDt(tok.nextToken()); versionId = new IdDt(tok.nextToken());
} else { } else {
@ -313,7 +330,7 @@ public abstract class RestfulServer extends HttpServlet {
r.setId(id); r.setId(id);
r.setVersion(versionId); r.setVersion(versionId);
r.setOperation(operation); r.setOperation(operation);
r.setParameterNames(params.keySet()); r.setParameterNames(params);
r.setRequestType(requestType); r.setRequestType(requestType);
BaseMethodBinding resourceMethod = resourceBinding.getMethod(r); BaseMethodBinding resourceMethod = resourceBinding.getMethod(r);
@ -324,7 +341,7 @@ public abstract class RestfulServer extends HttpServlet {
List<IResource> result = resourceMethod.invokeServer(resourceBinding.getResourceProvider(), id, versionId, params); List<IResource> result = resourceMethod.invokeServer(resourceBinding.getResourceProvider(), id, versionId, params);
switch (resourceMethod.getReturnType()) { switch (resourceMethod.getReturnType()) {
case BUNDLE: case BUNDLE:
streamResponseAsBundle(response, result, responseEncoding, fhirServerBase, completeUrl, prettyPrint, requestIsBrowser); streamResponseAsBundle(response, result, responseEncoding, fhirServerBase, completeUrl, prettyPrint, requestIsBrowser, narrativeMode);
break; break;
case RESOURCE: case RESOURCE:
if (result.size() == 0) { if (result.size() == 0) {
@ -332,7 +349,7 @@ public abstract class RestfulServer extends HttpServlet {
} else if (result.size() > 1) { } else if (result.size() > 1) {
throw new InternalErrorException("Method returned multiple resources"); throw new InternalErrorException("Method returned multiple resources");
} }
streamResponseAsResource(response, result.get(0), responseEncoding, prettyPrint, requestIsBrowser); streamResponseAsResource(response, result.get(0), responseEncoding, prettyPrint, requestIsBrowser, narrativeMode);
break; break;
} }
// resourceMethod.get // resourceMethod.get
@ -409,13 +426,15 @@ public abstract class RestfulServer extends HttpServlet {
} }
private void streamResponseAsBundle(HttpServletResponse theHttpResponse, List<IResource> theResult, EncodingUtil theResponseEncoding, String theServerBase, String theCompleteUrl, private void streamResponseAsBundle(HttpServletResponse theHttpResponse, List<IResource> theResult, EncodingUtil theResponseEncoding, String theServerBase, String theCompleteUrl,
boolean thePrettyPrint, boolean theRequestIsBrowser) throws IOException { boolean thePrettyPrint, boolean theRequestIsBrowser, NarrativeModeEnum theNarrativeMode) throws IOException {
assert !theServerBase.endsWith("/"); assert !theServerBase.endsWith("/");
theHttpResponse.setStatus(200); theHttpResponse.setStatus(200);
if (theRequestIsBrowser && myUseBrowserFriendlyContentTypes) { if (theRequestIsBrowser && myUseBrowserFriendlyContentTypes) {
theHttpResponse.setContentType(theResponseEncoding.getBrowserFriendlyBundleContentType()); theHttpResponse.setContentType(theResponseEncoding.getBrowserFriendlyBundleContentType());
} else if (theNarrativeMode == NarrativeModeEnum.ONLY) {
theHttpResponse.setContentType(Constants.CT_HTML);
} else { } else {
theHttpResponse.setContentType(theResponseEncoding.getBundleContentType()); theHttpResponse.setContentType(theResponseEncoding.getBundleContentType());
} }
@ -449,7 +468,7 @@ public abstract class RestfulServer extends HttpServlet {
b.append(next.getId().getValue()); b.append(next.getId().getValue());
boolean haveQ = false; boolean haveQ = false;
if (thePrettyPrint) { if (thePrettyPrint) {
b.append('?').append(PARAM_PRETTY).append("=true"); b.append('?').append(Constants.PARAM_PRETTY).append("=true");
haveQ = true; haveQ = true;
} }
if (theResponseEncoding == EncodingUtil.JSON) { if (theResponseEncoding == EncodingUtil.JSON) {
@ -461,6 +480,9 @@ public abstract class RestfulServer extends HttpServlet {
} }
b.append(Constants.PARAM_FORMAT).append("=json"); b.append(Constants.PARAM_FORMAT).append("=json");
} }
if (theNarrativeMode != NarrativeModeEnum.NORMAL) {
b.append(Constants.PARAM_NARRATIVE).append("=").append(theNarrativeMode.name().toLowerCase());
}
entry.getLinkSelf().setValue(b.toString()); entry.getLinkSelf().setValue(b.toString());
} }
} }
@ -469,26 +491,41 @@ public abstract class RestfulServer extends HttpServlet {
PrintWriter writer = theHttpResponse.getWriter(); PrintWriter writer = theHttpResponse.getWriter();
try { try {
getNewParser(theResponseEncoding, thePrettyPrint).encodeBundleToWriter(bundle, writer); if (theNarrativeMode == NarrativeModeEnum.ONLY) {
} finally { for (IResource next : theResult) {
writer.append(next.getText().getDiv().getValueAsString());
writer.append("<hr/>");
}
} else {
getNewParser(theResponseEncoding, thePrettyPrint, theNarrativeMode).encodeBundleToWriter(bundle, writer);
} } finally {
writer.close(); writer.close();
} }
} }
private void streamResponseAsResource(HttpServletResponse theHttpResponse, IResource theResource, EncodingUtil theResponseEncoding, boolean thePrettyPrint, boolean theRequestIsBrowser) private void streamResponseAsResource(HttpServletResponse theHttpResponse, IResource theResource, EncodingUtil theResponseEncoding, boolean thePrettyPrint, boolean theRequestIsBrowser, NarrativeModeEnum theNarrativeMode)
throws IOException { throws IOException {
theHttpResponse.setStatus(200); theHttpResponse.setStatus(200);
if (theRequestIsBrowser && myUseBrowserFriendlyContentTypes) { if (theRequestIsBrowser && myUseBrowserFriendlyContentTypes) {
theHttpResponse.setContentType(theResponseEncoding.getBrowserFriendlyBundleContentType()); theHttpResponse.setContentType(theResponseEncoding.getBrowserFriendlyBundleContentType());
} else if (theNarrativeMode == NarrativeModeEnum.ONLY) {
theHttpResponse.setContentType(Constants.CT_HTML);
} else { } else {
theHttpResponse.setContentType(theResponseEncoding.getBundleContentType()); theHttpResponse.setContentType(theResponseEncoding.getResourceContentType());
} }
theHttpResponse.setCharacterEncoding("UTF-8"); theHttpResponse.setCharacterEncoding("UTF-8");
PrintWriter writer = theHttpResponse.getWriter(); PrintWriter writer = theHttpResponse.getWriter();
getNewParser(theResponseEncoding, thePrettyPrint).encodeResourceToWriter(theResource, writer); try {
if (theNarrativeMode == NarrativeModeEnum.ONLY) {
writer.append(theResource.getText().getDiv().getValueAsString());
} else {
getNewParser(theResponseEncoding, thePrettyPrint, theNarrativeMode).encodeResourceToWriter(theResource, writer);
}
} finally {
writer.close(); writer.close();
}
} }

View File

@ -0,0 +1,7 @@
<div th:switch="${resource.precision.name()}">
<th:block th:case="YEAR" th:text="${#dates.format(resource.value,'yyyy')}">22 March 2012</th:block>
<th:block th:case="MONTH" th:text="${#dates.format(resource.value,'MMMM yyyy')}"></th:block>
<th:block th:case="DAY" th:text="${#dates.format(resource.value,'dd MMMM yyyy')}"></th:block>
<th:block th:case="SECOND" th:text="${#dates.format(resource.value,'dd MMMM yyyy HH:mm:ss')}"></th:block>
<th:block th:case="MILLI" th:text="${#dates.format(resource.value,'dd MMMM yyyy HH:mm:ss')}"></th:block>
</div>

View File

@ -19,6 +19,23 @@
<!--/*--> Complete Blood Count <!--*/--> <!--/*--> Complete Blood Count <!--*/-->
</div> </div>
<table class="hapiPropertyTable">
<tbody>
<tr th:if="${not resource.status.empty}">
<td>Status</td>
<td th:text="${resource.status.value}">Final</td>
</tr>
<tr th:if="${not resource.issued.empty}">
<td>Issued</td>
<td th:narrative="${resource.issued}">22 March 2012</td>
</tr>
<tr th:if="${not resource.conclusion.empty}">
<td>Conclusion</td>
<td th:narrative="${resource.conclusion}">This is the conclusion text</td>
</tr>
</tbody>
</table>
<table class="hapiTableOfValues" th:if="${not #lists.isEmpty(resource.result)} and ${resource.result[0].resource != null}"> <table class="hapiTableOfValues" th:if="${not #lists.isEmpty(resource.result)} and ${resource.result[0].resource != null}">
<thead> <thead>
<tr> <tr>
@ -53,13 +70,15 @@
</td> </td>
<td th:text="${result.resource.status.value}">final</td> <td th:text="${result.resource.status.value}">final</td>
</tr> </tr>
<tr th:class="${rowStat.odd}? 'hapiTableOfValuesRowOdd' : 'hapiTableOfValuesRowEven'" class="hapiTableOfValuesRowOdd"> <tr th:if="${not result.resource.comments.empty}" th:class="${rowStat.odd}? 'hapiTableOfValuesRowOdd' : 'hapiTableOfValuesRowEven'" class="hapiTableOfValuesRowOdd">
<td th:text="${result.resource.comments.value}" colspan="5">This is a comment</td> <td th:text="${result.resource.comments.value}" colspan="5">This is a comment</td>
</tr> </tr>
<!--/*-->
<!--/* The following are just examples that don't appear in the generated narrative -->
<tr class="hapiTableOfValuesRowEven"><td>Lkc</td><td>198 g/L</td><td>H</td><td>78 g/L - 99 g/L</td><td>final</td></tr> <tr class="hapiTableOfValuesRowEven"><td>Lkc</td><td>198 g/L</td><td>H</td><td>78 g/L - 99 g/L</td><td>final</td></tr>
<tr class="hapiTableOfValuesRowEven"><td colspan="5">Yet another comment</td></tr> <tr class="hapiTableOfValuesRowEven"><td colspan="5">Yet another comment</td></tr>
<!--*/--> <!--*/-->
</th:block> </th:block>
</tbody> </tbody>
</table> </table>

View File

@ -1,9 +1,19 @@
datetime.dtclass=ca.uhn.fhir.model.primitive.DateTimeDt
datetime.dtnarrative=classpath:ca/uhn/fhir/narrative/DateTimeDt.html
diagnosticreport.profile=http://hl7.org/fhir/profiles/DiagnosticReport diagnosticreport.profile=http://hl7.org/fhir/profiles/DiagnosticReport
diagnosticreport.narrative=classpath:ca/uhn/fhir/narrative/DiagnosticReport.html diagnosticreport.narrative=classpath:ca/uhn/fhir/narrative/DiagnosticReport.html
# Instant uses DateTime narrative
instant.dtclass=ca.uhn.fhir.model.primitive.InstantDt
instant.dtnarrative=classpath:ca/uhn/fhir/narrative/DateTimeDt.html
patient.profile=http://hl7.org/fhir/profiles/Patient patient.profile=http://hl7.org/fhir/profiles/Patient
patient.narrative=classpath:ca/uhn/fhir/narrative/Patient.html patient.narrative=classpath:ca/uhn/fhir/narrative/Patient.html
quantity.dtclass=ca.uhn.fhir.model.dstu.composite.QuantityDt quantity.dtclass=ca.uhn.fhir.model.dstu.composite.QuantityDt
quantity.dtnarrative=classpath:ca/uhn/fhir/narrative/QuantityDt.html quantity.dtnarrative=classpath:ca/uhn/fhir/narrative/QuantityDt.html
string.dtclass=ca.uhn.fhir.model.primitive.StringDt
string.dtnarrative=classpath:ca/uhn/fhir/narrative/StringDt.html

View File

@ -11,11 +11,13 @@ public class ExampleRestfulClient {
//START SNIPPET: client //START SNIPPET: client
public static void main(String[] args) { public static void main(String[] args) {
FhirContext ctx = new FhirContext(Patient.class); FhirContext ctx = new FhirContext();
String serverBase = "http://foo.com/fhirServerBase"; String serverBase = "http://foo.com/fhirServerBase";
// Create the client
RestfulClientImpl client = ctx.newRestfulClient(RestfulClientImpl.class, serverBase); RestfulClientImpl client = ctx.newRestfulClient(RestfulClientImpl.class, serverBase);
// The client is now ready for use! // Try the client out! This method will invoke the server
List<Patient> patients = client.getPatient(new StringDt("SMITH")); List<Patient> patients = client.getPatient(new StringDt("SMITH"));
} }

View File

@ -54,8 +54,14 @@ public class MyPatient extends Patient {
/******** /********
* Accessors and mutators follow * Accessors and mutators follow
*
* IMPORTANT:
* Each extension is required to have an getter/accessor and a stter/mutator.
* You are highly recommended to create getters which create instances if they
* do not already exist, since this is how the rest of the HAPI FHIR API works.
********/ ********/
/** Getter for important dates */
public List<DateTimeDt> getImportantDates() { public List<DateTimeDt> getImportantDates() {
if (myImportantDates==null) { if (myImportantDates==null) {
myImportantDates = new ArrayList<DateTimeDt>(); myImportantDates = new ArrayList<DateTimeDt>();
@ -63,14 +69,20 @@ public class MyPatient extends Patient {
return myImportantDates; return myImportantDates;
} }
/** Getter for pet name */
public StringDt getPetName() { public StringDt getPetName() {
if (myPetName == null) {
myPetName = new StringDt();
}
return myPetName; return myPetName;
} }
/** Setter for important dates */
public void setImportantDates(List<DateTimeDt> theImportantDates) { public void setImportantDates(List<DateTimeDt> theImportantDates) {
myImportantDates = theImportantDates; myImportantDates = theImportantDates;
} }
/** Setter for pet name */
public void setPetName(StringDt thePetName) { public void setPetName(StringDt thePetName) {
myPetName = thePetName; myPetName = thePetName;
} }

View File

@ -67,6 +67,15 @@ public List<Patient> searchByLastName(@Required(name=Patient.SP_FAMILY) StringDt
} }
//END SNIPPET: searchStringParam //END SNIPPET: searchStringParam
//START SNIPPET: searchNamedQuery
@Search(queryName="namedQuery1")
public List<Patient> searchByNamedQuery(@Required(name="someparam") StringDt theSomeParam) {
List<Patient> retVal = new ArrayList<Patient>();
// ...populate...
return retVal;
}
//END SNIPPET: searchNamedQuery
//START SNIPPET: searchIdentifierParam //START SNIPPET: searchIdentifierParam
@Search() @Search()
public List<Patient> searchByIdentifier(@Required(name=Patient.SP_IDENTIFIER) IdentifierDt theId) { public List<Patient> searchByIdentifier(@Required(name=Patient.SP_IDENTIFIER) IdentifierDt theId) {

View File

@ -228,6 +228,19 @@
<code>http://fhir.example.com/Observation?name=urn:fakenames|123,urn:fakenames|456</code> <code>http://fhir.example.com/Observation?name=urn:fakenames|123,urn:fakenames|456</code>
</p> </p>
<h4>AND Relationship Query Parameters</h4>
<p>
To accept a composite parameter, use a parameter type which implements the
<a href="./apidocs/ca/uhn/fhir/model/api/IQueryParameterAnd.html">IQueryParameterAnd</a>
interface.
</p>
<p>
See the section below on <a href="#DATE_RANGES">date ranges</a> for
one example of an AND parameter.
</p>
</subsection> </subsection>
<subsection name="Date Parameters - Simple"> <subsection name="Date Parameters - Simple">
@ -270,6 +283,7 @@
</subsection> </subsection>
<a name="DATE_RANGES"/>
<subsection name="Date Parameters - Ranges"> <subsection name="Date Parameters - Ranges">
<p> <p>
@ -335,6 +349,31 @@
</subsection> </subsection>
<subsection name="Named Queries (_query)">
<p>
FHIR supports <a href="http://www.hl7.org/implement/standards/fhir/search.html#advanced">named queries</a>,
which may have specific behaviour defined. The following example shows how to create a Search
operation with a name.
</p>
<p>
This operation can only be invoked by explicitly specifying the given query name
in the request URL. Note that the query does not need to take any parameters.
</p>
<macro name="snippet">
<param name="id" value="searchNamedQuery" />
<param name="file" value="src/site/example/java/example/RestfulPatientResourceProviderMore.java" />
</macro>
<p>
Example URL to invoke this method:<br/>
<code>http://fhir.example.com/Patient?_query=namedQuery1&amp;someparam=value</code>
</p>
</subsection>
</section> </section>

View File

@ -2,6 +2,8 @@ package ca.uhn.fhir.context;
import java.util.List; import java.util.List;
import javax.naming.OperationNotSupportedException;
import ca.uhn.fhir.model.api.IElement; import ca.uhn.fhir.model.api.IElement;
import ca.uhn.fhir.model.api.IExtension; import ca.uhn.fhir.model.api.IExtension;
import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.IResource;
@ -11,6 +13,7 @@ import ca.uhn.fhir.model.api.annotation.Extension;
import ca.uhn.fhir.model.api.annotation.ResourceDef; import ca.uhn.fhir.model.api.annotation.ResourceDef;
import ca.uhn.fhir.model.dstu.composite.ContainedDt; import ca.uhn.fhir.model.dstu.composite.ContainedDt;
import ca.uhn.fhir.model.dstu.composite.IdentifierDt; import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
import ca.uhn.fhir.model.dstu.composite.NarrativeDt;
import ca.uhn.fhir.model.primitive.DateDt; import ca.uhn.fhir.model.primitive.DateDt;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.model.primitive.StringDt;
@ -232,8 +235,12 @@ public class ResourceWithExtensionsA implements IResource {
@Override @Override
public ContainedDt getContained() { public ContainedDt getContained() {
// TODO Auto-generated method stub throw new IllegalStateException();
return null; }
@Override
public NarrativeDt getText() {
throw new IllegalStateException();
} }
} }

View File

@ -17,7 +17,9 @@ import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
import ca.uhn.fhir.model.dstu.resource.DiagnosticReport; import ca.uhn.fhir.model.dstu.resource.DiagnosticReport;
import ca.uhn.fhir.model.dstu.resource.Observation; import ca.uhn.fhir.model.dstu.resource.Observation;
import ca.uhn.fhir.model.dstu.resource.Patient; import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.model.dstu.valueset.DiagnosticReportStatusEnum;
import ca.uhn.fhir.model.dstu.valueset.ObservationStatusEnum; import ca.uhn.fhir.model.dstu.valueset.ObservationStatusEnum;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.parser.DataFormatException; import ca.uhn.fhir.parser.DataFormatException;
public class ThymeleafNarrativeGeneratorTest { public class ThymeleafNarrativeGeneratorTest {
@ -64,18 +66,27 @@ public class ThymeleafNarrativeGeneratorTest {
@Test @Test
public void testGenerateDiagnosticReportWithObservations() throws DataFormatException, IOException { public void testGenerateDiagnosticReportWithObservations() throws DataFormatException, IOException {
DiagnosticReport value = new DiagnosticReport(); DiagnosticReport value = new DiagnosticReport();
value.getIssued().setValueAsString("2011-02-22T11:13:00");
value.setStatus(DiagnosticReportStatusEnum.FINAL);
value.getName().setText("Some Diagnostic Report"); value.getName().setText("Some Diagnostic Report");
{
Observation obs = new Observation();
obs.getName().addCoding().setCode("1938HB").setDisplay("Hemoglobin");
obs.setValue(new QuantityDt(null, 2.223, "mg/L"));
obs.addReferenceRange().setLow(new QuantityDt(2.20)).setHigh(new QuantityDt(2.99));
obs.setStatus(ObservationStatusEnum.FINAL);
obs.setComments("This is a result comment");
Observation obs = new Observation(); ResourceReferenceDt result = value.addResult();
obs.getName().addCoding().setCode("1938HB").setDisplay("Hemoglobin"); result.setResource(obs);
obs.setValue(new QuantityDt(null, 2.223, "mg/L")); }
obs.addReferenceRange().setLow(new QuantityDt(2.20)).setHigh(new QuantityDt(2.99)); {
obs.setStatus(ObservationStatusEnum.FINAL); Observation obs = new Observation();
obs.setComments("This is a result comment"); obs.setValue(new StringDt("HELLO!"));
value.addResult().setResource(obs);
ResourceReferenceDt result = value.addResult(); }
result.setResource(obs);
NarrativeDt generateNarrative = gen.generateNarrative("http://hl7.org/fhir/profiles/DiagnosticReport", value); NarrativeDt generateNarrative = gen.generateNarrative("http://hl7.org/fhir/profiles/DiagnosticReport", value);
String output = generateNarrative.getDiv().getValueAsString(); String output = generateNarrative.getDiv().getValueAsString();

View File

@ -64,8 +64,9 @@ public class JsonParserTest {
public void testNarrativeGeneration() throws DataFormatException, IOException { public void testNarrativeGeneration() throws DataFormatException, IOException {
Patient patient = new Patient(); Patient patient = new Patient();
patient.addName().addFamily("Smith"); patient.addName().addFamily("Smith");
Organization org = new Organization();
patient.getManagingOrganization().setResource(org);
INarrativeGenerator gen = mock(INarrativeGenerator.class); INarrativeGenerator gen = mock(INarrativeGenerator.class);
XhtmlDt xhtmlDt = new XhtmlDt("<div>help</div>"); XhtmlDt xhtmlDt = new XhtmlDt("<div>help</div>");

View File

@ -268,6 +268,42 @@ public class ClientTest {
} }
@Test
public void testSearchNamedQueryOneParam() throws Exception {
String msg = getPatientFeedWithOneResult();
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_FHIR_XML + "; charset=UTF-8"));
when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
ITestClient client = ctx.newRestfulClient(ITestClient.class, "http://foo");
client.getPatientOneParam(new StringDt("BB"));
assertEquals("http://foo/Patient?_query=someQueryOneParam&param1=BB", capt.getValue().getURI().toString());
}
@Test
public void testSearchNamedQueryNoParams() throws Exception {
String msg = getPatientFeedWithOneResult();
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_FHIR_XML + "; charset=UTF-8"));
when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
ITestClient client = ctx.newRestfulClient(ITestClient.class, "http://foo");
client.getPatientNoParams();
assertEquals("http://foo/Patient?_query=someQueryNoParams", capt.getValue().getURI().toString());
}
private String getPatientFeedWithOneResult() { private String getPatientFeedWithOneResult() {
//@formatter:off //@formatter:off
String msg = "<feed xmlns=\"http://www.w3.org/2005/Atom\">\n" + String msg = "<feed xmlns=\"http://www.w3.org/2005/Atom\">\n" +

View File

@ -44,4 +44,10 @@ public interface ITestClient extends IBasicClient {
@Search() @Search()
public Patient getPatientWithIncludes(@Required(name = "withIncludes") StringDt theString, @Include List<PathSpecification> theIncludes); public Patient getPatientWithIncludes(@Required(name = "withIncludes") StringDt theString, @Include List<PathSpecification> theIncludes);
@Search(queryName="someQueryNoParams")
public Patient getPatientNoParams();
@Search(queryName="someQueryOneParam")
public Patient getPatientOneParam(@Required(name="param1") StringDt theParam);
} }

View File

@ -546,6 +546,47 @@ public class ResfulServerMethodTest {
} }
@Test
public void testSearchNamedNoParams() throws Exception {
// HttpPost httpPost = new HttpPost("http://localhost:" + ourPort +
// "/Patient/1");
// httpPost.setEntity(new StringEntity("test",
// ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/?_query=someQueryNoParams");
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
ourLog.info("Response was:\n{}", responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
Patient patient = (Patient) ourCtx.newXmlParser().parseBundle(responseContent).getEntries().get(0).getResource();
assertEquals("someQueryNoParams", patient.getName().get(1).getFamilyAsSingleString());
}
@Test
public void testSearchNamedOneParam() throws Exception {
// HttpPost httpPost = new HttpPost("http://localhost:" + ourPort +
// "/Patient/1");
// httpPost.setEntity(new StringEntity("test",
// ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/?_query=someQueryOneParam&param1=AAAA");
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
ourLog.info("Response was:\n{}", responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
Patient patient = (Patient) ourCtx.newXmlParser().parseBundle(responseContent).getEntries().get(0).getResource();
assertEquals("AAAA", patient.getName().get(1).getFamilyAsSingleString());
}
public static class DummyRestfulServer extends RestfulServer { public static class DummyRestfulServer extends RestfulServer {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@ -604,6 +645,20 @@ public class ResfulServerMethodTest {
return idToPatient; return idToPatient;
} }
@Search(queryName="someQueryNoParams")
public Patient getPatientNoParams() {
Patient next = getIdToPatient().get("1");
next.addName().addFamily("someQueryNoParams");
return next;
}
@Search(queryName="someQueryOneParam")
public Patient getPatientOneParam(@Required(name="param1") StringDt theParam) {
Patient next = getIdToPatient().get("1");
next.addName().addFamily(theParam.getValue());
return next;
}
@Search() @Search()
public Patient getPatientWithIncludes(@Required(name = "withIncludes") StringDt theString, public Patient getPatientWithIncludes(@Required(name = "withIncludes") StringDt theString,
@Include(allow= {"include1","include2", "include3"}) List<PathSpecification> theIncludes) { @Include(allow= {"include1","include2", "include3"}) List<PathSpecification> theIncludes) {

View File

@ -24,7 +24,7 @@ public class ResourceMethodTest {
@Before @Before
public void before() throws NoSuchMethodException, SecurityException { public void before() throws NoSuchMethodException, SecurityException {
rm = new SearchMethodBinding(MethodReturnTypeEnum.RESOURCE, Patient.class, ResourceMethodTest.class.getMethod("before")); rm = new SearchMethodBinding(MethodReturnTypeEnum.RESOURCE, Patient.class, ResourceMethodTest.class.getMethod("before"), null);
} }
@Test @Test