Correct issues uncovered during connectathon
This commit is contained in:
parent
552842e547
commit
589059256f
|
@ -52,6 +52,7 @@ import ca.uhn.fhir.rest.client.api.IBasicClient;
|
|||
import ca.uhn.fhir.rest.client.api.IRestfulClient;
|
||||
import ca.uhn.fhir.rest.server.IVersionSpecificBundleFactory;
|
||||
import ca.uhn.fhir.util.FhirTerser;
|
||||
import ca.uhn.fhir.util.VersionUtil;
|
||||
import ca.uhn.fhir.validation.FhirValidator;
|
||||
|
||||
/**
|
||||
|
@ -113,6 +114,8 @@ public class FhirContext {
|
|||
}
|
||||
|
||||
private FhirContext(FhirVersionEnum theVersion, Collection<Class<? extends IBaseResource>> theResourceTypes) {
|
||||
VersionUtil.getVersion();
|
||||
|
||||
if (theVersion != null) {
|
||||
if (!theVersion.isPresentOnClasspath()) {
|
||||
throw new IllegalStateException(getLocalizer().getMessage(FhirContext.class, "noStructuresForSpecifiedVersion", theVersion.name()));
|
||||
|
@ -465,9 +468,6 @@ public class FhirContext {
|
|||
}
|
||||
|
||||
public void setNarrativeGenerator(INarrativeGenerator theNarrativeGenerator) {
|
||||
if (theNarrativeGenerator != null) {
|
||||
theNarrativeGenerator.setFhirContext(this);
|
||||
}
|
||||
myNarrativeGenerator = theNarrativeGenerator;
|
||||
}
|
||||
|
||||
|
@ -509,6 +509,8 @@ public class FhirContext {
|
|||
|
||||
/**
|
||||
* Creates and returns a new FhirContext with version {@link FhirVersionEnum#DSTU2_1 DSTU 2.1}
|
||||
*
|
||||
* @since 1.4
|
||||
*/
|
||||
public static FhirContext forDstu2_1() {
|
||||
return new FhirContext(FhirVersionEnum.DSTU2_1);
|
||||
|
|
|
@ -30,8 +30,10 @@ import java.util.Set;
|
|||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.hl7.fhir.instance.model.api.IBase;
|
||||
import org.hl7.fhir.instance.model.api.IBaseDatatype;
|
||||
import org.hl7.fhir.instance.model.api.IBaseReference;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
|
||||
import ca.uhn.fhir.model.api.annotation.Child;
|
||||
import ca.uhn.fhir.model.api.annotation.Description;
|
||||
|
@ -108,6 +110,7 @@ public class RuntimeChildChoiceDefinition extends BaseRuntimeDeclaredChildDefini
|
|||
} else {
|
||||
nextDef = theClassToElementDefinitions.get(next);
|
||||
BaseRuntimeElementDefinition<?> nextDefForChoice = nextDef;
|
||||
|
||||
/*
|
||||
* In HAPI 1.3 the following applied:
|
||||
* Elements which are called foo[x] and have a choice which is a profiled datatype must use the
|
||||
|
@ -115,19 +118,19 @@ public class RuntimeChildChoiceDefinition extends BaseRuntimeDeclaredChildDefini
|
|||
* element fooString when encoded, because markdown is a profile of string. This is according to the
|
||||
* FHIR spec
|
||||
*
|
||||
* As of HAPI 1.4 this has been disabled after conversation with Grahame. It appears
|
||||
* that it is not correct behaviour.
|
||||
* Note that as of HAPI 1.4 this applies only to non-primitive datatypes after discussion
|
||||
* with Grahame.
|
||||
*/
|
||||
// if (nextDef instanceof IRuntimeDatatypeDefinition) {
|
||||
// IRuntimeDatatypeDefinition nextDefDatatype = (IRuntimeDatatypeDefinition) nextDef;
|
||||
// if (nextDefDatatype.getProfileOf() != null) {
|
||||
// nextDefForChoice = null;
|
||||
// nonPreferred = true;
|
||||
// Class<? extends IBaseDatatype> profileType = nextDefDatatype.getProfileOf();
|
||||
// BaseRuntimeElementDefinition<?> elementDef = theClassToElementDefinitions.get(profileType);
|
||||
// elementName = getElementName() + StringUtils.capitalize(elementDef.getName());
|
||||
// }
|
||||
// }
|
||||
if (nextDef instanceof IRuntimeDatatypeDefinition) {
|
||||
IRuntimeDatatypeDefinition nextDefDatatype = (IRuntimeDatatypeDefinition) nextDef;
|
||||
if (nextDefDatatype.getProfileOf() != null && !IPrimitiveType.class.isAssignableFrom(next)) {
|
||||
nextDefForChoice = null;
|
||||
nonPreferred = true;
|
||||
Class<? extends IBaseDatatype> profileType = nextDefDatatype.getProfileOf();
|
||||
BaseRuntimeElementDefinition<?> elementDef = theClassToElementDefinitions.get(profileType);
|
||||
elementName = getElementName() + StringUtils.capitalize(elementDef.getName());
|
||||
}
|
||||
}
|
||||
if (nextDefForChoice != null) {
|
||||
elementName = getElementName() + StringUtils.capitalize(nextDefForChoice.getName());
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package ca.uhn.fhir.model.base.composite;
|
||||
|
||||
import org.hl7.fhir.instance.model.api.INarrative;
|
||||
|
||||
import ca.uhn.fhir.model.api.BaseIdentifiableElement;
|
||||
import ca.uhn.fhir.model.api.ICompositeDatatype;
|
||||
import ca.uhn.fhir.model.primitive.BoundCodeDt;
|
||||
|
@ -28,10 +30,31 @@ import ca.uhn.fhir.model.primitive.XhtmlDt;
|
|||
/**
|
||||
* @param <T> The narrative status enum type
|
||||
*/
|
||||
public abstract class BaseNarrativeDt<T extends Enum<?>> extends BaseIdentifiableElement implements ICompositeDatatype {
|
||||
public abstract class BaseNarrativeDt<T extends Enum<?>> extends BaseIdentifiableElement implements ICompositeDatatype, INarrative {
|
||||
|
||||
public abstract BoundCodeDt<T> getStatus();
|
||||
|
||||
@Override
|
||||
public void setDivAsString(String theString) {
|
||||
getDiv().setValueAsString(theString);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDivAsString() {
|
||||
return getDiv().getValueAsString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public INarrative setStatusAsString(String theString) {
|
||||
getStatus().setValueAsString(theString);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStatusAsString() {
|
||||
return getStatus().getValueAsString();
|
||||
}
|
||||
|
||||
public abstract XhtmlDt getDiv();
|
||||
|
||||
}
|
||||
|
|
|
@ -35,7 +35,9 @@ import java.util.Properties;
|
|||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.io.input.ReaderInputStream;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.hl7.fhir.instance.model.api.IBaseDatatype;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.INarrative;
|
||||
import org.thymeleaf.Arguments;
|
||||
import org.thymeleaf.Configuration;
|
||||
import org.thymeleaf.TemplateEngine;
|
||||
|
@ -62,7 +64,7 @@ import org.thymeleaf.templateresolver.TemplateResolver;
|
|||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.IDatatype;
|
||||
import ca.uhn.fhir.model.base.composite.BaseNarrativeDt;
|
||||
import ca.uhn.fhir.model.api.annotation.ResourceDef;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
|
||||
public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGenerator {
|
||||
|
@ -77,10 +79,7 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
|
|||
private boolean myIgnoreMissingTemplates = true;
|
||||
private volatile boolean myInitialized;
|
||||
private HashMap<String, String> myNameToNarrativeTemplate;
|
||||
private HashMap<String, String> myNameToTitleTemplate;
|
||||
private TemplateEngine myProfileTemplateEngine;
|
||||
private HashMap<String, String> myProfileToName;
|
||||
private TemplateEngine myTitleTemplateEngine;
|
||||
|
||||
public BaseThymeleafNarrativeGenerator() {
|
||||
myThymeleafConfig = new Configuration();
|
||||
|
@ -90,44 +89,31 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
|
|||
myThymeleafConfig.initialize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateNarrative(IBaseResource theResource, BaseNarrativeDt<?> theNarrative) {
|
||||
generateNarrative(null, theResource, theNarrative);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setFhirContext(FhirContext theFhirContext) {
|
||||
if (theFhirContext == null) {
|
||||
throw new NullPointerException("Can not set theFhirContext to null");
|
||||
}
|
||||
if (myFhirContext != null && myFhirContext != theFhirContext) {
|
||||
throw new IllegalStateException("Narrative generators may not be reused/shared across multiple FhirContext instances");
|
||||
}
|
||||
myFhirContext = theFhirContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateNarrative(String theProfile, IBaseResource theResource, BaseNarrativeDt<?> theNarrative) {
|
||||
public void generateNarrative(FhirContext theContext, IBaseResource theResource, INarrative theNarrative) {
|
||||
if (!myInitialized) {
|
||||
initialize();
|
||||
initialize(theContext);
|
||||
}
|
||||
|
||||
String name = null;
|
||||
if (StringUtils.isNotBlank(theProfile)) {
|
||||
name = myProfileToName.get(theProfile);
|
||||
}
|
||||
if (name == null) {
|
||||
name = myClassToName.get(theResource.getClass());
|
||||
}
|
||||
if (name == null) {
|
||||
name = myFhirContext.getResourceDefinition(theResource).getName().toLowerCase();
|
||||
name = theContext.getResourceDefinition(theResource).getName().toLowerCase();
|
||||
}
|
||||
|
||||
if (name == null) {
|
||||
if (myIgnoreMissingTemplates) {
|
||||
ourLog.debug("No narrative template available for profile: {}", theProfile);
|
||||
theNarrative.getDiv().setValueAsString("<div>No narrative template available for resource profile: " + theProfile + "</div>");
|
||||
theNarrative.getStatus().setValueAsString("empty");
|
||||
ourLog.debug("No narrative template available for resorce: {}", name);
|
||||
try {
|
||||
theNarrative.setDivAsString("<div>No narrative template available for resource : " + name + "</div>");
|
||||
} catch (Exception e) {
|
||||
// last resort..
|
||||
}
|
||||
theNarrative.setStatusAsString("empty");
|
||||
return;
|
||||
} else {
|
||||
throw new DataFormatException("No narrative template for class " + theResource.getClass().getCanonicalName());
|
||||
|
@ -137,7 +123,7 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
|
|||
try {
|
||||
Context context = new Context();
|
||||
context.setVariable("resource", theResource);
|
||||
context.setVariable("fhirVersion", myFhirContext.getVersion().getVersion().name());
|
||||
context.setVariable("fhirVersion", theContext.getVersion().getVersion().name());
|
||||
|
||||
String result = myProfileTemplateEngine.process(name, context);
|
||||
|
||||
|
@ -151,14 +137,18 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
|
|||
return;
|
||||
}
|
||||
|
||||
theNarrative.getDiv().setValueAsString(result);
|
||||
theNarrative.getStatus().setValueAsString("generated");
|
||||
theNarrative.setDivAsString(result);
|
||||
theNarrative.setStatusAsString("generated");
|
||||
return;
|
||||
} catch (Exception e) {
|
||||
if (myIgnoreFailures) {
|
||||
ourLog.error("Failed to generate narrative", e);
|
||||
theNarrative.getDiv().setValueAsString("<div>No narrative available - Error: " + e.getMessage() + "</div>");
|
||||
theNarrative.getStatus().setValueAsString("empty");
|
||||
try {
|
||||
theNarrative.setDivAsString("<div>No narrative available - Error: " + e.getMessage() + "</div>");
|
||||
} catch (Exception e1) {
|
||||
// last resort..
|
||||
}
|
||||
theNarrative.setStatusAsString("empty");
|
||||
return;
|
||||
} else {
|
||||
throw new DataFormatException(e);
|
||||
|
@ -166,98 +156,7 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateTitle(IBaseResource theResource) {
|
||||
return generateTitle( null, theResource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateTitle(String theProfile, IBaseResource theResource) {
|
||||
if (!myInitialized) {
|
||||
initialize();
|
||||
}
|
||||
|
||||
ourLog.trace("Generating resource title {}", theResource);
|
||||
|
||||
String name = null;
|
||||
if (StringUtils.isNotBlank(theProfile)) {
|
||||
name = myProfileToName.get(theProfile);
|
||||
}
|
||||
if (name == null) {
|
||||
name = myClassToName.get(theResource.getClass());
|
||||
}
|
||||
if (name == null) {
|
||||
name = myFhirContext.getResourceDefinition(theResource).getName().toLowerCase();
|
||||
}
|
||||
|
||||
ourLog.trace("Template name is {}", name);
|
||||
|
||||
if (name == null) {
|
||||
if (myIgnoreMissingTemplates) {
|
||||
ourLog.debug("No title template available for profile: {}", theProfile);
|
||||
return null;
|
||||
} else {
|
||||
throw new DataFormatException("No title template for class " + theResource.getClass().getCanonicalName());
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
Context context = new Context();
|
||||
context.setVariable("resource", theResource);
|
||||
context.setVariable("fhirVersion", myFhirContext.getVersion().getVersion().name());
|
||||
|
||||
String result = myTitleTemplateEngine.process(name, context);
|
||||
|
||||
ourLog.trace("Produced {}", result);
|
||||
|
||||
StringBuilder b = new StringBuilder();
|
||||
boolean inTag = false;
|
||||
for (int i = 0; i < result.length(); i++) {
|
||||
char nextChar = result.charAt(i);
|
||||
char prevChar = i > 0 ? result.charAt(i - 1) : '\n';
|
||||
if (nextChar == '<') {
|
||||
inTag = true;
|
||||
continue;
|
||||
} else if (inTag) {
|
||||
if (nextChar == '>') {
|
||||
inTag = false;
|
||||
}
|
||||
continue;
|
||||
} else if (nextChar <= ' ') {
|
||||
if (prevChar <= ' ' || prevChar == '>') {
|
||||
continue;
|
||||
} else {
|
||||
b.append(' ');
|
||||
}
|
||||
} else {
|
||||
b.append(nextChar);
|
||||
}
|
||||
}
|
||||
|
||||
while (b.length() > 0 && b.charAt(b.length() - 1) == ' ') {
|
||||
b.setLength(b.length() - 1);
|
||||
}
|
||||
|
||||
result = b.toString();
|
||||
if (result.startsWith("<") && result.contains(">")) {
|
||||
result = result.substring(result.indexOf('>') + 1);
|
||||
}
|
||||
if (result.endsWith(">") && result.contains("<")) {
|
||||
result = result.substring(0, result.lastIndexOf('<'));
|
||||
}
|
||||
|
||||
result = result.replace(">", ">").replace("<", "<").replace("&", "&");
|
||||
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
if (myIgnoreFailures) {
|
||||
ourLog.error("Failed to generate narrative", e);
|
||||
return "No title available - Error: " + e.getMessage();
|
||||
} else {
|
||||
throw new DataFormatException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract List<String> getPropertyFile();
|
||||
|
||||
|
@ -270,17 +169,15 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
|
|||
}
|
||||
}
|
||||
|
||||
private synchronized void initialize() {
|
||||
private synchronized void initialize(FhirContext theContext) {
|
||||
if (myInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
ourLog.info("Initializing narrative generator");
|
||||
|
||||
myProfileToName = new HashMap<String, String>();
|
||||
myClassToName = new HashMap<Class<?>, String>();
|
||||
myNameToNarrativeTemplate = new HashMap<String, String>();
|
||||
myNameToTitleTemplate = new HashMap<String, String>();
|
||||
|
||||
List<String> propFileName = getPropertyFile();
|
||||
|
||||
|
@ -303,23 +200,11 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
|
|||
myProfileTemplateEngine.setTemplateResolver(resolver);
|
||||
StandardDialect dialect = new StandardDialect();
|
||||
HashSet<IProcessor> additionalProcessors = new HashSet<IProcessor>();
|
||||
additionalProcessors.add(new NarrativeAttributeProcessor());
|
||||
additionalProcessors.add(new NarrativeAttributeProcessor(theContext));
|
||||
dialect.setAdditionalProcessors(additionalProcessors);
|
||||
myProfileTemplateEngine.setDialect(dialect);
|
||||
myProfileTemplateEngine.initialize();
|
||||
}
|
||||
{
|
||||
myTitleTemplateEngine = new TemplateEngine();
|
||||
TemplateResolver resolver = new TemplateResolver();
|
||||
resolver.setResourceResolver(new TitleResourceResolver());
|
||||
myTitleTemplateEngine.setTemplateResolver(resolver);
|
||||
StandardDialect dialect = new StandardDialect();
|
||||
HashSet<IProcessor> additionalProcessors = new HashSet<IProcessor>();
|
||||
additionalProcessors.add(new NarrativeAttributeProcessor());
|
||||
dialect.setAdditionalProcessors(additionalProcessors);
|
||||
myTitleTemplateEngine.setDialect(dialect);
|
||||
myTitleTemplateEngine.initialize();
|
||||
}
|
||||
|
||||
myInitialized = true;
|
||||
}
|
||||
|
@ -369,22 +254,14 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
|
|||
|
||||
String narrativePropName = name + ".narrative";
|
||||
String narrativeName = file.getProperty(narrativePropName);
|
||||
String titlePropName = name + ".title";
|
||||
String titleName = file.getProperty(titlePropName);
|
||||
if (isBlank(narrativeName) && isBlank(titleName)) {
|
||||
throw new ConfigurationException("Found property '" + nextKey + "' but no corresponding property '" + narrativePropName + "' or '" + titlePropName + "' in file " + propFileName);
|
||||
if (isBlank(narrativeName)) {
|
||||
throw new ConfigurationException("Found property '" + nextKey + "' but no corresponding property '" + narrativePropName + "' in file " + propFileName);
|
||||
}
|
||||
|
||||
myProfileToName.put(file.getProperty(nextKey), name);
|
||||
|
||||
if (StringUtils.isNotBlank(narrativeName)) {
|
||||
String narrative = IOUtils.toString(loadResource(narrativeName));
|
||||
myNameToNarrativeTemplate.put(name, narrative);
|
||||
}
|
||||
if (StringUtils.isNotBlank(titleName)) {
|
||||
String title = IOUtils.toString(loadResource(titleName));
|
||||
myNameToTitleTemplate.put(name, title);
|
||||
}
|
||||
|
||||
} else if (nextKey.endsWith(".class")) {
|
||||
|
||||
|
@ -403,10 +280,6 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
|
|||
clazz = null;
|
||||
}
|
||||
|
||||
// if (isBlank(narrativeName) && isBlank(titleName)) {
|
||||
// throw new ConfigurationException("Found property '" + nextKey + "' but no corresponding property '" + narrativePropName + "' or '" + titlePropName + "' in file " + propFileName);
|
||||
// }
|
||||
|
||||
if (clazz != null) {
|
||||
myClassToName.put(clazz, name);
|
||||
}
|
||||
|
@ -424,18 +297,7 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
|
|||
}
|
||||
continue;
|
||||
} else if (nextKey.endsWith(".title")) {
|
||||
String name = nextKey.substring(0, nextKey.indexOf(".title"));
|
||||
if (isBlank(name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String titlePropName = name + ".title";
|
||||
String titleName = file.getProperty(titlePropName);
|
||||
if (StringUtils.isNotBlank(titleName)) {
|
||||
String title = IOUtils.toString(loadResource(titleName));
|
||||
myNameToTitleTemplate.put(name, title);
|
||||
}
|
||||
continue;
|
||||
ourLog.debug("Ignoring title property as narrative generator no longer generates titles: {}", nextKey);
|
||||
} else {
|
||||
throw new ConfigurationException("Invalid property name: " + nextKey);
|
||||
}
|
||||
|
@ -555,13 +417,13 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
|
|||
return b.toString();
|
||||
}
|
||||
|
||||
private FhirContext myFhirContext;
|
||||
|
||||
public class NarrativeAttributeProcessor extends AbstractAttrProcessor {
|
||||
|
||||
private FhirContext myContext;
|
||||
|
||||
protected NarrativeAttributeProcessor() {
|
||||
protected NarrativeAttributeProcessor(FhirContext theContext) {
|
||||
super("narrative");
|
||||
myContext = theContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -569,6 +431,7 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
|
|||
return 0;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected ProcessorResult processAttribute(Arguments theArguments, Element theElement, String theAttributeName) {
|
||||
final String attributeValue = theElement.getAttributeValue(theAttributeName);
|
||||
|
@ -587,6 +450,7 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
|
|||
}
|
||||
|
||||
Context context = new Context();
|
||||
context.setVariable("fhirVersion", myContext.getVersion().getVersion().name());
|
||||
context.setVariable("resource", value);
|
||||
|
||||
String name = null;
|
||||
|
@ -599,10 +463,15 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
|
|||
|
||||
if (name == null) {
|
||||
if (value instanceof IBaseResource) {
|
||||
name = myFhirContext.getResourceDefinition((IBaseResource)value).getName();
|
||||
name = myContext.getResourceDefinition((Class<? extends IBaseResource>) value).getName();
|
||||
} else if (value instanceof IDatatype) {
|
||||
name = value.getClass().getSimpleName();
|
||||
name = name.substring(0, name.length() - 2);
|
||||
} else if (value instanceof IBaseDatatype) {
|
||||
name = value.getClass().getSimpleName();
|
||||
if (name.endsWith("Type")) {
|
||||
name = name.substring(0, name.length() - 4);
|
||||
}
|
||||
} else {
|
||||
throw new DataFormatException("Don't know how to determine name for type: " + value.getClass());
|
||||
}
|
||||
|
@ -677,20 +546,4 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
|
|||
}
|
||||
}
|
||||
|
||||
private final class TitleResourceResolver implements IResourceResolver {
|
||||
@Override
|
||||
public String getName() {
|
||||
return getClass().getCanonicalName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getResourceAsStream(TemplateProcessingParameters theTemplateProcessingParameters, String theName) {
|
||||
String template = myNameToTitleTemplate.get(theName);
|
||||
if (template == null) {
|
||||
ourLog.info("No narative template for resource profile: {}", theName);
|
||||
return new ReaderInputStream(new StringReader(""));
|
||||
}
|
||||
return new ReaderInputStream(new StringReader(template));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,6 @@ import java.util.List;
|
|||
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
|
||||
|
||||
public class DefaultThymeleafNarrativeGenerator extends BaseThymeleafNarrativeGenerator implements INarrativeGenerator {
|
||||
|
||||
public static final String NARRATIVES_PROPERTIES = "classpath:ca/uhn/fhir/narrative/narratives.properties";
|
||||
|
@ -35,7 +34,7 @@ public class DefaultThymeleafNarrativeGenerator extends BaseThymeleafNarrativeGe
|
|||
|
||||
@Override
|
||||
protected List<String> getPropertyFile() {
|
||||
List<String> retVal=new ArrayList<String>();
|
||||
List<String> retVal = new ArrayList<String>();
|
||||
retVal.add(NARRATIVES_PROPERTIES);
|
||||
if (myUseHapiServerConformanceNarrative) {
|
||||
retVal.add(HAPISERVER_NARRATIVES_PROPERTIES);
|
||||
|
@ -44,25 +43,19 @@ public class DefaultThymeleafNarrativeGenerator extends BaseThymeleafNarrativeGe
|
|||
}
|
||||
|
||||
/**
|
||||
* If set to <code>true</code> (default is <code>false</code>) a special custom narrative for the
|
||||
* Conformance resource will be provided, which is designed to be used with
|
||||
* HAPI {@link RestfulServer} instances. This narrative provides a friendly search
|
||||
* page which can assist users of the service.
|
||||
* If set to <code>true</code> (default is <code>false</code>) a special custom narrative for the Conformance resource will be provided, which is designed to be used with HAPI {@link RestfulServer}
|
||||
* instances. This narrative provides a friendly search page which can assist users of the service.
|
||||
*/
|
||||
public void setUseHapiServerConformanceNarrative(boolean theValue) {
|
||||
myUseHapiServerConformanceNarrative=theValue;
|
||||
myUseHapiServerConformanceNarrative = theValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to <code>true</code> (default is <code>false</code>) a special custom narrative for the
|
||||
* Conformance resource will be provided, which is designed to be used with
|
||||
* HAPI {@link RestfulServer} instances. This narrative provides a friendly search
|
||||
* page which can assist users of the service.
|
||||
* If set to <code>true</code> (default is <code>false</code>) a special custom narrative for the Conformance resource will be provided, which is designed to be used with HAPI {@link RestfulServer}
|
||||
* instances. This narrative provides a friendly search page which can assist users of the service.
|
||||
*/
|
||||
public boolean isUseHapiServerConformanceNarrative() {
|
||||
return myUseHapiServerConformanceNarrative;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -21,24 +21,12 @@ package ca.uhn.fhir.narrative;
|
|||
*/
|
||||
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.INarrative;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.base.composite.BaseNarrativeDt;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
|
||||
public interface INarrativeGenerator {
|
||||
|
||||
void generateNarrative(String theProfile, IBaseResource theResource, BaseNarrativeDt<?> theNarrative) throws DataFormatException;
|
||||
|
||||
void generateNarrative(IBaseResource theResource, BaseNarrativeDt<?> theNarrative);
|
||||
|
||||
String generateTitle(IBaseResource theResource);
|
||||
|
||||
String generateTitle(String theProfile, IBaseResource theResource);
|
||||
|
||||
/**
|
||||
* This method is called automatically by the framework, you do not need to interact with this method.
|
||||
*/
|
||||
void setFhirContext(FhirContext theFhirContext);
|
||||
void generateNarrative(FhirContext theContext, IBaseResource theResource, INarrative theNarrative);
|
||||
|
||||
}
|
||||
|
|
|
@ -62,7 +62,9 @@ import org.hl7.fhir.instance.model.api.IBaseHasModifierExtensions;
|
|||
import org.hl7.fhir.instance.model.api.IBaseIntegerDatatype;
|
||||
import org.hl7.fhir.instance.model.api.IBaseReference;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IDomainResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.instance.model.api.INarrative;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
|
||||
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
|
||||
|
@ -504,7 +506,7 @@ public class JsonParser extends BaseParser implements IParser {
|
|||
}
|
||||
case PRIMITIVE_XHTML_HL7ORG:
|
||||
case PRIMITIVE_XHTML: {
|
||||
if (!getSuppressNarratives()) {
|
||||
if (!isSuppressNarratives()) {
|
||||
IPrimitiveType<?> dt = (IPrimitiveType<?>) theNextValue;
|
||||
if (theChildName != null) {
|
||||
theWriter.write(theChildName, dt.getValueAsString());
|
||||
|
@ -539,10 +541,17 @@ public class JsonParser extends BaseParser implements IParser {
|
|||
BaseRuntimeChildDefinition nextChild = nextChildElem.getDef();
|
||||
if (nextChild instanceof RuntimeChildNarrativeDefinition) {
|
||||
INarrativeGenerator gen = myContext.getNarrativeGenerator();
|
||||
if (gen != null && theResource instanceof IResource) {
|
||||
BaseNarrativeDt<?> narr = ((IResource) theResource).getText();
|
||||
if (narr.getDiv().isEmpty()) {
|
||||
gen.generateNarrative(theResDef.getResourceProfile(), theResource, narr);
|
||||
if (gen != null) {
|
||||
INarrative narr;
|
||||
if (theResource instanceof IResource) {
|
||||
narr = ((IResource) theResource).getText();
|
||||
} else if (theResource instanceof IDomainResource) {
|
||||
narr = ((IDomainResource)theResource).getText();
|
||||
} else {
|
||||
narr = null;
|
||||
}
|
||||
if (narr != null && narr.isEmpty()) {
|
||||
gen.generateNarrative(myContext, theResource, narr);
|
||||
if (narr != null && !narr.isEmpty()) {
|
||||
RuntimeChildNarrativeDefinition child = (RuntimeChildNarrativeDefinition) nextChild;
|
||||
String childName = nextChild.getChildNameByDatatype(child.getDatatype());
|
||||
|
|
|
@ -56,7 +56,9 @@ import org.hl7.fhir.instance.model.api.IBaseHasModifierExtensions;
|
|||
import org.hl7.fhir.instance.model.api.IBaseReference;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IBaseXhtml;
|
||||
import org.hl7.fhir.instance.model.api.IDomainResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.instance.model.api.INarrative;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
|
||||
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
|
||||
|
@ -576,21 +578,23 @@ public class XmlParser extends BaseParser implements IParser {
|
|||
|
||||
if (nextChild instanceof RuntimeChildNarrativeDefinition) {
|
||||
INarrativeGenerator gen = myContext.getNarrativeGenerator();
|
||||
INarrative narr;
|
||||
if (theResource instanceof IResource) {
|
||||
BaseNarrativeDt<?> narr = ((IResource) theResource).getText();
|
||||
if (gen != null && narr.isEmpty()) {
|
||||
String resourceProfile = myContext.getResourceDefinition(theResource).getResourceProfile();
|
||||
gen.generateNarrative(resourceProfile, theResource, narr);
|
||||
}
|
||||
if (narr.isEmpty() == false) {
|
||||
RuntimeChildNarrativeDefinition child = (RuntimeChildNarrativeDefinition) nextChild;
|
||||
String childName = nextChild.getChildNameByDatatype(child.getDatatype());
|
||||
BaseRuntimeElementDefinition<?> type = child.getChildByName(childName);
|
||||
encodeChildElementToStreamWriter(theResource, theEventWriter, narr, childName, type, null, theContainedResource, nextChildElem);
|
||||
continue;
|
||||
}
|
||||
narr = ((IResource) theResource).getText();
|
||||
} else if (theResource instanceof IDomainResource) {
|
||||
narr = ((IDomainResource)theResource).getText();
|
||||
} else {
|
||||
// Narrative generation not currently supported for HL7org structures
|
||||
narr = null;
|
||||
}
|
||||
if (gen != null && narr.isEmpty()) {
|
||||
gen.generateNarrative(myContext, theResource, narr);
|
||||
}
|
||||
if (narr != null && narr.isEmpty() == false) {
|
||||
RuntimeChildNarrativeDefinition child = (RuntimeChildNarrativeDefinition) nextChild;
|
||||
String childName = nextChild.getChildNameByDatatype(child.getDatatype());
|
||||
BaseRuntimeElementDefinition<?> type = child.getChildByName(childName);
|
||||
encodeChildElementToStreamWriter(theResource, theEventWriter, narr, childName, type, null, theContainedResource, nextChildElem);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -27,14 +27,14 @@ import ca.uhn.fhir.parser.IParser;
|
|||
|
||||
public enum EncodingEnum {
|
||||
|
||||
XML(Constants.CT_FHIR_XML, Constants.CT_ATOM_XML, "application/xml", Constants.FORMAT_XML) {
|
||||
XML(Constants.CT_FHIR_XML, Constants.CT_ATOM_XML, Constants.FORMAT_XML) {
|
||||
@Override
|
||||
public IParser newParser(FhirContext theContext) {
|
||||
return theContext.newXmlParser();
|
||||
}
|
||||
},
|
||||
|
||||
JSON(Constants.CT_FHIR_JSON, Constants.CT_FHIR_JSON, Constants.CT_JSON, Constants.FORMAT_JSON) {
|
||||
JSON(Constants.CT_FHIR_JSON, Constants.CT_FHIR_JSON, Constants.FORMAT_JSON) {
|
||||
@Override
|
||||
public IParser newParser(FhirContext theContext) {
|
||||
return theContext.newJsonParser();
|
||||
|
@ -50,7 +50,6 @@ public enum EncodingEnum {
|
|||
for (EncodingEnum next : values()) {
|
||||
ourContentTypeToEncoding.put(next.getBundleContentType(), next);
|
||||
ourContentTypeToEncoding.put(next.getResourceContentType(), next);
|
||||
ourContentTypeToEncoding.put(next.getBrowserFriendlyBundleContentType(), next);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -67,13 +66,11 @@ public enum EncodingEnum {
|
|||
|
||||
private String myResourceContentType;
|
||||
private String myBundleContentType;
|
||||
private String myBrowserFriendlyContentType;
|
||||
private String myFormatContentType;
|
||||
|
||||
EncodingEnum(String theResourceContentType, String theBundleContentType, String theBrowserFriendlyContentType, String theFormatContentType) {
|
||||
EncodingEnum(String theResourceContentType, String theBundleContentType, String theFormatContentType) {
|
||||
myResourceContentType = theResourceContentType;
|
||||
myBundleContentType = theBundleContentType;
|
||||
myBrowserFriendlyContentType = theBrowserFriendlyContentType;
|
||||
myFormatContentType = theFormatContentType;
|
||||
}
|
||||
|
||||
|
@ -91,10 +88,6 @@ public enum EncodingEnum {
|
|||
return myResourceContentType;
|
||||
}
|
||||
|
||||
public String getBrowserFriendlyBundleContentType() {
|
||||
return myBrowserFriendlyContentType;
|
||||
}
|
||||
|
||||
public static EncodingEnum forContentType(String theContentType) {
|
||||
return ourContentTypeToEncoding.get(theContentType);
|
||||
}
|
||||
|
|
|
@ -43,15 +43,14 @@ public abstract class RestfulResponse<T extends RequestDetails> implements IRest
|
|||
public final Object streamResponseAsResource(IBaseResource resource, boolean prettyPrint, Set<SummaryEnum> summaryMode,
|
||||
int statusCode, boolean respondGzip, boolean addContentLocationHeader)
|
||||
throws IOException {
|
||||
return RestfulServerUtils.streamResponseAsResource(theRequestDetails.getServer(), resource, prettyPrint, summaryMode, statusCode, respondGzip, addContentLocationHeader,
|
||||
respondGzip, getRequestDetails());
|
||||
return RestfulServerUtils.streamResponseAsResource(theRequestDetails.getServer(), resource, summaryMode, statusCode, addContentLocationHeader, respondGzip, getRequestDetails());
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object streamResponseAsBundle(Bundle bundle, Set<SummaryEnum> summaryMode, boolean respondGzip, boolean requestIsBrowser)
|
||||
throws IOException {
|
||||
return RestfulServerUtils.streamResponseAsBundle(theRequestDetails.getServer(), bundle, summaryMode, requestIsBrowser, respondGzip, getRequestDetails());
|
||||
return RestfulServerUtils.streamResponseAsBundle(theRequestDetails.getServer(), bundle, summaryMode, respondGzip, getRequestDetails());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -72,6 +72,7 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
|||
import ca.uhn.fhir.rest.server.exceptions.NotModifiedException;
|
||||
import ca.uhn.fhir.rest.server.interceptor.ExceptionHandlingInterceptor;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||
import ca.uhn.fhir.rest.server.interceptor.ResponseHighlighterInterceptor;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
import ca.uhn.fhir.util.ReflectionUtil;
|
||||
import ca.uhn.fhir.util.UrlUtil;
|
||||
|
@ -112,9 +113,8 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
|||
private boolean myUseBrowserFriendlyContentTypes;
|
||||
|
||||
/**
|
||||
* Constructor. Note that if no {@link FhirContext} is passed in to the server (either through the constructor, or
|
||||
* through {@link #setFhirContext(FhirContext)}) the server will determine which version of FHIR to support through
|
||||
* classpath scanning. This is brittle, and it is highly recommended to explicitly specify a FHIR version.
|
||||
* Constructor. Note that if no {@link FhirContext} is passed in to the server (either through the constructor, or through {@link #setFhirContext(FhirContext)}) the server will determine which
|
||||
* version of FHIR to support through classpath scanning. This is brittle, and it is highly recommended to explicitly specify a FHIR version.
|
||||
*/
|
||||
public RestfulServer() {
|
||||
this(null);
|
||||
|
@ -137,8 +137,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
|||
/**
|
||||
* This method is called prior to sending a response to incoming requests. It is used to add custom headers.
|
||||
* <p>
|
||||
* Use caution if overriding this method: it is recommended to call <code>super.addHeadersToResponse</code> to avoid
|
||||
* inadvertantly disabling functionality.
|
||||
* Use caution if overriding this method: it is recommended to call <code>super.addHeadersToResponse</code> to avoid inadvertantly disabling functionality.
|
||||
* </p>
|
||||
*/
|
||||
public void addHeadersToResponse(HttpServletResponse theHttpResponse) {
|
||||
|
@ -375,9 +374,8 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the default encoding to return (XML/JSON) if an incoming request does not specify a preference (either
|
||||
* with the <code>_format</code> URL parameter, or with an <code>Accept</code> header in the request. The default is
|
||||
* {@link EncodingEnum#XML}. Will not return null.
|
||||
* Returns the default encoding to return (XML/JSON) if an incoming request does not specify a preference (either with the <code>_format</code> URL parameter, or with an <code>Accept</code> header
|
||||
* in the request. The default is {@link EncodingEnum#XML}. Will not return null.
|
||||
*/
|
||||
@Override
|
||||
public EncodingEnum getDefaultResponseEncoding() {
|
||||
|
@ -390,8 +388,8 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
|||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link FhirContext} associated with this server. For efficient processing, resource providers and plain
|
||||
* providers should generally use this context if one is needed, as opposed to creating their own.
|
||||
* Gets the {@link FhirContext} associated with this server. For efficient processing, resource providers and plain providers should generally use this context if one is needed, as opposed to
|
||||
* creating their own.
|
||||
*/
|
||||
@Override
|
||||
public FhirContext getFhirContext() {
|
||||
|
@ -428,8 +426,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
|||
}
|
||||
|
||||
/**
|
||||
* Allows users of RestfulServer to override the getRequestPath method to let them build their custom request path
|
||||
* implementation
|
||||
* Allows users of RestfulServer to override the getRequestPath method to let them build their custom request path implementation
|
||||
*
|
||||
* @param requestFullPath
|
||||
* the full request path
|
||||
|
@ -455,8 +452,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
|||
}
|
||||
|
||||
/**
|
||||
* Get the server address strategy, which is used to determine what base URL to provide clients to refer to this
|
||||
* server. Defaults to an instance of {@link IncomingRequestAddressStrategy}
|
||||
* Get the server address strategy, which is used to determine what base URL to provide clients to refer to this server. Defaults to an instance of {@link IncomingRequestAddressStrategy}
|
||||
*/
|
||||
public IServerAddressStrategy getServerAddressStrategy() {
|
||||
return myServerAddressStrategy;
|
||||
|
@ -476,19 +472,17 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the method bindings for this server which are not specific to any particular resource type. This method is
|
||||
* internal to HAPI and developers generally do not need to interact with it. Use with caution, as it may change.
|
||||
* Returns the method bindings for this server which are not specific to any particular resource type. This method is internal to HAPI and developers generally do not need to interact with it. Use
|
||||
* with caution, as it may change.
|
||||
*/
|
||||
public List<BaseMethodBinding<?>> getServerBindings() {
|
||||
return myServerBinding.getMethodBindings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the server conformance provider, which is the provider that is used to generate the server's conformance
|
||||
* (metadata) statement if one has been explicitly defined.
|
||||
* Returns the server conformance provider, which is the provider that is used to generate the server's conformance (metadata) statement if one has been explicitly defined.
|
||||
* <p>
|
||||
* By default, the ServerConformanceProvider for the declared version of FHIR is used, but this can be changed, or
|
||||
* set to <code>null</code> to use the appropriate one for the given FHIR version.
|
||||
* By default, the ServerConformanceProvider for the declared version of FHIR is used, but this can be changed, or set to <code>null</code> to use the appropriate one for the given FHIR version.
|
||||
* </p>
|
||||
*/
|
||||
public Object getServerConformanceProvider() {
|
||||
|
@ -496,8 +490,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
|||
}
|
||||
|
||||
/**
|
||||
* Gets the server's name, as exported in conformance profiles exported by the server. This is informational only,
|
||||
* but can be helpful to set with something appropriate.
|
||||
* Gets the server's name, as exported in conformance profiles exported by the server. This is informational only, but can be helpful to set with something appropriate.
|
||||
*
|
||||
* @see RestfulServer#setServerName(String)
|
||||
*/
|
||||
|
@ -510,8 +503,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
|||
}
|
||||
|
||||
/**
|
||||
* Gets the server's version, as exported in conformance profiles exported by the server. This is informational only,
|
||||
* but can be helpful to set with something appropriate.
|
||||
* Gets the server's version, as exported in conformance profiles exported by the server. This is informational only, but can be helpful to set with something appropriate.
|
||||
*/
|
||||
public String getServerVersion() {
|
||||
return myServerVersion;
|
||||
|
@ -621,12 +613,10 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
|||
}
|
||||
|
||||
/*
|
||||
* Actualy invoke the server method. This call is to a HAPI method binding, which is an object that wraps a
|
||||
* specific implementing (user-supplied) method, but handles its input and provides its output back to the
|
||||
* client.
|
||||
* Actualy invoke the server method. This call is to a HAPI method binding, which is an object that wraps a specific implementing (user-supplied) method, but handles its input and provides
|
||||
* its output back to the client.
|
||||
*
|
||||
* This is basically the end of processing for a successful request, since the method binding replies to the
|
||||
* client and closes the response.
|
||||
* This is basically the end of processing for a successful request, since the method binding replies to the client and closes the response.
|
||||
*/
|
||||
resourceMethod.invokeServer(this, requestDetails);
|
||||
|
||||
|
@ -660,12 +650,10 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
|||
} catch (Throwable e) {
|
||||
|
||||
/*
|
||||
* We have caught an exception during request processing. This might be because a handling method threw
|
||||
* something they wanted to throw (e.g. UnprocessableEntityException because the request had business
|
||||
* requirement problems) or it could be due to bugs (e.g. NullPointerException).
|
||||
* We have caught an exception during request processing. This might be because a handling method threw something they wanted to throw (e.g. UnprocessableEntityException because the request
|
||||
* had business requirement problems) or it could be due to bugs (e.g. NullPointerException).
|
||||
*
|
||||
* First we let the interceptors have a crack at converting the exception into something HAPI can use
|
||||
* (BaseServerResponseException)
|
||||
* First we let the interceptors have a crack at converting the exception into something HAPI can use (BaseServerResponseException)
|
||||
*/
|
||||
BaseServerResponseException exception = null;
|
||||
for (int i = getInterceptors().size() - 1; i >= 0; i--) {
|
||||
|
@ -678,8 +666,8 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
|||
}
|
||||
|
||||
/*
|
||||
* If none of the interceptors converted the exception, default behaviour is to keep the exception as-is if it
|
||||
* extends BaseServerResponseException, otherwise wrap it in an InternalErrorException.
|
||||
* If none of the interceptors converted the exception, default behaviour is to keep the exception as-is if it extends BaseServerResponseException, otherwise wrap it in an
|
||||
* InternalErrorException.
|
||||
*/
|
||||
if (exception == null) {
|
||||
exception = DEFAULT_EXCEPTION_HANDLER.preProcessOutgoingException(requestDetails, e, theRequest);
|
||||
|
@ -711,9 +699,8 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
|||
}
|
||||
|
||||
/**
|
||||
* Initializes the server. Note that this method is final to avoid accidentally introducing bugs in implementations,
|
||||
* but subclasses may put initialization code in {@link #initialize()}, which is called immediately before beginning
|
||||
* initialization of the restful server's internal init.
|
||||
* Initializes the server. Note that this method is final to avoid accidentally introducing bugs in implementations, but subclasses may put initialization code in {@link #initialize()}, which is
|
||||
* called immediately before beginning initialization of the restful server's internal init.
|
||||
*/
|
||||
@Override
|
||||
public final void init() throws ServletException {
|
||||
|
@ -782,13 +769,11 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
|||
}
|
||||
|
||||
/**
|
||||
* This method may be overridden by subclasses to do perform initialization that needs to be performed prior to the
|
||||
* server being used.
|
||||
* This method may be overridden by subclasses to do perform initialization that needs to be performed prior to the server being used.
|
||||
*
|
||||
* @throws ServletException
|
||||
* If the initialization failed. Note that you should consider throwing {@link UnavailableException}
|
||||
* (which extends {@link ServletException}), as this is a flag to the servlet container that the servlet
|
||||
* is not usable.
|
||||
* If the initialization failed. Note that you should consider throwing {@link UnavailableException} (which extends {@link ServletException}), as this is a flag to the servlet container
|
||||
* that the servlet is not usable.
|
||||
*/
|
||||
protected void initialize() throws ServletException {
|
||||
// nothing by default
|
||||
|
@ -845,8 +830,8 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
|||
}
|
||||
|
||||
/**
|
||||
* Should the server "pretty print" responses by default (requesting clients can always override this default by
|
||||
* supplying an <code>Accept</code> header in the request, or a <code>_pretty</code> parameter in the request URL.
|
||||
* Should the server "pretty print" responses by default (requesting clients can always override this default by supplying an <code>Accept</code> header in the request, or a <code>_pretty</code>
|
||||
* parameter in the request URL.
|
||||
* <p>
|
||||
* The default is <code>false</code>
|
||||
* </p>
|
||||
|
@ -859,14 +844,18 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
|||
}
|
||||
|
||||
/**
|
||||
* Should the server attempt to decompress incoming request contents (default is <code>true</code>). Typically this
|
||||
* should be set to <code>true</code> unless the server has other configuration to deal with decompressing request
|
||||
* bodies (e.g. a filter applied to the whole server).
|
||||
* Should the server attempt to decompress incoming request contents (default is <code>true</code>). Typically this should be set to <code>true</code> unless the server has other configuration to
|
||||
* deal with decompressing request bodies (e.g. a filter applied to the whole server).
|
||||
*/
|
||||
public boolean isUncompressIncomingContents() {
|
||||
return myUncompressIncomingContents;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated This feature did not work well, and will be removed. Use {@link ResponseHighlighterInterceptor} instead as an interceptor on your server and it will provide more useful syntax
|
||||
* highlighting. Deprocated in 1.4
|
||||
*/
|
||||
@Deprecated
|
||||
@Override
|
||||
public boolean isUseBrowserFriendlyContentTypes() {
|
||||
return myUseBrowserFriendlyContentTypes;
|
||||
|
@ -946,8 +935,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
|||
}
|
||||
|
||||
/**
|
||||
* Register a single provider. This could be a Resource Provider or a "plain" provider not associated with any
|
||||
* resource.
|
||||
* Register a single provider. This could be a Resource Provider or a "plain" provider not associated with any resource.
|
||||
*
|
||||
* @param provider
|
||||
* @throws Exception
|
||||
|
@ -1005,7 +993,8 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
|||
}
|
||||
String resourceName = getFhirContext().getResourceDefinition(resourceType).getName();
|
||||
if (myTypeToProvider.containsKey(resourceName)) {
|
||||
throw new ServletException("Multiple resource providers return resource type[" + resourceName + "]: First[" + myTypeToProvider.get(resourceName).getClass().getCanonicalName() + "] and Second[" + rsrcProvider.getClass().getCanonicalName() + "]");
|
||||
throw new ServletException("Multiple resource providers return resource type[" + resourceName + "]: First[" + myTypeToProvider.get(resourceName).getClass().getCanonicalName()
|
||||
+ "] and Second[" + rsrcProvider.getClass().getCanonicalName() + "]");
|
||||
}
|
||||
if (!inInit) {
|
||||
myResourceProviders.add(rsrcProvider);
|
||||
|
@ -1118,9 +1107,8 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
|||
}
|
||||
|
||||
/**
|
||||
* Sets the profile tagging behaviour for the server. When set to a value other than {@link AddProfileTagEnum#NEVER}
|
||||
* (which is the default), the server will automatically add a profile tag based on the class of the resource(s)
|
||||
* being returned.
|
||||
* Sets the profile tagging behaviour for the server. When set to a value other than {@link AddProfileTagEnum#NEVER} (which is the default), the server will automatically add a profile tag based on
|
||||
* the class of the resource(s) being returned.
|
||||
*
|
||||
* @param theAddProfileTag
|
||||
* The behaviour enum (must not be null)
|
||||
|
@ -1141,8 +1129,8 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
|||
}
|
||||
|
||||
/**
|
||||
* Should the server "pretty print" responses by default (requesting clients can always override this default by
|
||||
* supplying an <code>Accept</code> header in the request, or a <code>_pretty</code> parameter in the request URL.
|
||||
* Should the server "pretty print" responses by default (requesting clients can always override this default by supplying an <code>Accept</code> header in the request, or a <code>_pretty</code>
|
||||
* parameter in the request URL.
|
||||
* <p>
|
||||
* The default is <code>false</code>
|
||||
* </p>
|
||||
|
@ -1155,12 +1143,10 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
|||
}
|
||||
|
||||
/**
|
||||
* Sets the default encoding to return (XML/JSON) if an incoming request does not specify a preference (either with
|
||||
* the <code>_format</code> URL parameter, or with an <code>Accept</code> header in the request. The default is
|
||||
* {@link EncodingEnum#XML}.
|
||||
* Sets the default encoding to return (XML/JSON) if an incoming request does not specify a preference (either with the <code>_format</code> URL parameter, or with an <code>Accept</code> header in
|
||||
* the request. The default is {@link EncodingEnum#XML}.
|
||||
* <p>
|
||||
* Note when testing this feature: Some browsers will include "application/xml" in their Accept header, which means
|
||||
* that the
|
||||
* Note when testing this feature: Some browsers will include "application/xml" in their Accept header, which means that the
|
||||
* </p>
|
||||
*/
|
||||
public void setDefaultResponseEncoding(EncodingEnum theDefaultResponseEncoding) {
|
||||
|
@ -1169,8 +1155,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
|||
}
|
||||
|
||||
/**
|
||||
* Sets (enables/disables) the server support for ETags. Must not be <code>null</code>. Default is
|
||||
* {@link #DEFAULT_ETAG_SUPPORT}
|
||||
* Sets (enables/disables) the server support for ETags. Must not be <code>null</code>. Default is {@link #DEFAULT_ETAG_SUPPORT}
|
||||
*
|
||||
* @param theETagSupport
|
||||
* The ETag support mode
|
||||
|
@ -1278,8 +1263,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
|||
}
|
||||
|
||||
/**
|
||||
* Provide a server address strategy, which is used to determine what base URL to provide clients to refer to this
|
||||
* server. Defaults to an instance of {@link IncomingRequestAddressStrategy}
|
||||
* Provide a server address strategy, which is used to determine what base URL to provide clients to refer to this server. Defaults to an instance of {@link IncomingRequestAddressStrategy}
|
||||
*/
|
||||
public void setServerAddressStrategy(IServerAddressStrategy theServerAddressStrategy) {
|
||||
Validate.notNull(theServerAddressStrategy, "Server address strategy can not be null");
|
||||
|
@ -1287,17 +1271,15 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the server conformance provider, which is the provider that is used to generate the server's conformance
|
||||
* (metadata) statement.
|
||||
* Returns the server conformance provider, which is the provider that is used to generate the server's conformance (metadata) statement.
|
||||
* <p>
|
||||
* By default, the ServerConformanceProvider implementation for the declared version of FHIR is used, but this can be
|
||||
* changed, or set to <code>null</code> if you do not wish to export a conformance statement.
|
||||
* By default, the ServerConformanceProvider implementation for the declared version of FHIR is used, but this can be changed, or set to <code>null</code> if you do not wish to export a conformance
|
||||
* statement.
|
||||
* </p>
|
||||
* Note that this method can only be called before the server is initialized.
|
||||
*
|
||||
* @throws IllegalStateException
|
||||
* Note that this method can only be called prior to {@link #init() initialization} and will throw an
|
||||
* {@link IllegalStateException} if called after that.
|
||||
* Note that this method can only be called prior to {@link #init() initialization} and will throw an {@link IllegalStateException} if called after that.
|
||||
*/
|
||||
public void setServerConformanceProvider(Object theServerConformanceProvider) {
|
||||
if (myStarted) {
|
||||
|
@ -1320,34 +1302,32 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
|||
}
|
||||
|
||||
/**
|
||||
* Sets the server's name, as exported in conformance profiles exported by the server. This is informational only,
|
||||
* but can be helpful to set with something appropriate.
|
||||
* Sets the server's name, as exported in conformance profiles exported by the server. This is informational only, but can be helpful to set with something appropriate.
|
||||
*/
|
||||
public void setServerName(String theServerName) {
|
||||
myServerName = theServerName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the server's version, as exported in conformance profiles exported by the server. This is informational only,
|
||||
* but can be helpful to set with something appropriate.
|
||||
* Gets the server's version, as exported in conformance profiles exported by the server. This is informational only, but can be helpful to set with something appropriate.
|
||||
*/
|
||||
public void setServerVersion(String theServerVersion) {
|
||||
myServerVersion = theServerVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should the server attempt to decompress incoming request contents (default is <code>true</code>). Typically this
|
||||
* should be set to <code>true</code> unless the server has other configuration to deal with decompressing request
|
||||
* bodies (e.g. a filter applied to the whole server).
|
||||
* Should the server attempt to decompress incoming request contents (default is <code>true</code>). Typically this should be set to <code>true</code> unless the server has other configuration to
|
||||
* deal with decompressing request bodies (e.g. a filter applied to the whole server).
|
||||
*/
|
||||
public void setUncompressIncomingContents(boolean theUncompressIncomingContents) {
|
||||
myUncompressIncomingContents = theUncompressIncomingContents;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to <code>true</code> (default is false), the server will use browser friendly content-types (instead of
|
||||
* standard FHIR ones) when it detects that the request is coming from a browser instead of a FHIR
|
||||
* @deprecated This feature did not work well, and will be removed. Use {@link ResponseHighlighterInterceptor} instead as an interceptor on your server and it will provide more useful syntax
|
||||
* highlighting. Deprocated in 1.4
|
||||
*/
|
||||
@Deprecated
|
||||
public void setUseBrowserFriendlyContentTypes(boolean theUseBrowserFriendlyContentTypes) {
|
||||
myUseBrowserFriendlyContentTypes = theUseBrowserFriendlyContentTypes;
|
||||
}
|
||||
|
|
|
@ -545,8 +545,7 @@ public class RestfulServerUtils {
|
|||
return prettyPrint;
|
||||
}
|
||||
|
||||
public static Object streamResponseAsBundle(IRestfulServerDefaults theServer, Bundle bundle, Set<SummaryEnum> theSummaryMode,
|
||||
boolean theRequestIsBrowser, boolean respondGzip, RequestDetails theRequestDetails)
|
||||
public static Object streamResponseAsBundle(IRestfulServerDefaults theServer, Bundle bundle, Set<SummaryEnum> theSummaryMode, boolean respondGzip, RequestDetails theRequestDetails)
|
||||
throws IOException {
|
||||
|
||||
int status = 200;
|
||||
|
@ -554,12 +553,7 @@ public class RestfulServerUtils {
|
|||
// Determine response encoding
|
||||
EncodingEnum responseEncoding = RestfulServerUtils.determineResponseEncodingWithDefault(theRequestDetails);
|
||||
|
||||
String contentType;
|
||||
if (theRequestIsBrowser && theServer.isUseBrowserFriendlyContentTypes()) {
|
||||
contentType = responseEncoding.getBrowserFriendlyBundleContentType();
|
||||
} else {
|
||||
contentType = responseEncoding.getBundleContentType();
|
||||
}
|
||||
String contentType = responseEncoding.getBundleContentType();
|
||||
|
||||
String charset = Constants.CHARSET_NAME_UTF8;
|
||||
Writer writer = theRequestDetails.getResponse().getResponseWriter(status, contentType, charset, respondGzip);
|
||||
|
@ -577,8 +571,8 @@ public class RestfulServerUtils {
|
|||
return theRequestDetails.getResponse().sendWriterResponse(status, contentType, charset, writer);
|
||||
}
|
||||
|
||||
public static Object streamResponseAsResource(IRestfulServerDefaults theServer, IBaseResource theResource, boolean theRequestIsBrowser, Set<SummaryEnum> theSummaryMode,
|
||||
int stausCode, boolean theRespondGzip, boolean theAddContentLocationHeader, boolean respondGzip,
|
||||
public static Object streamResponseAsResource(IRestfulServerDefaults theServer, IBaseResource theResource, Set<SummaryEnum> theSummaryMode,
|
||||
int stausCode, boolean theAddContentLocationHeader, boolean respondGzip,
|
||||
RequestDetails theRequestDetails)
|
||||
throws IOException {
|
||||
IRestfulResponse restUtil = theRequestDetails.getResponse();
|
||||
|
@ -637,9 +631,7 @@ public class RestfulServerUtils {
|
|||
}
|
||||
}
|
||||
|
||||
if (theRequestIsBrowser && theServer.isUseBrowserFriendlyContentTypes()) {
|
||||
contentType = responseEncoding.getBrowserFriendlyBundleContentType();
|
||||
} else if (encodingDomainResourceAsText) {
|
||||
if (encodingDomainResourceAsText) {
|
||||
contentType = Constants.CT_HTML;
|
||||
} else {
|
||||
contentType = responseEncoding.getResourceContentType();
|
||||
|
@ -668,18 +660,15 @@ public class RestfulServerUtils {
|
|||
}
|
||||
|
||||
Writer writer = restUtil.getResponseWriter(stausCode, contentType, charset, respondGzip);
|
||||
try {
|
||||
if (encodingDomainResourceAsText && theResource instanceof IResource) {
|
||||
writer.append(((IResource) theResource).getText().getDiv().getValueAsString());
|
||||
} else {
|
||||
IParser parser = getNewParser(theServer.getFhirContext(), theRequestDetails);
|
||||
parser.encodeResourceToWriter(theResource, writer);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
//always send a response, even if the parsing went wrong
|
||||
}
|
||||
return restUtil.sendWriterResponse(stausCode, contentType, charset, writer);
|
||||
|
||||
if (encodingDomainResourceAsText && theResource instanceof IResource) {
|
||||
writer.append(((IResource) theResource).getText().getDiv().getValueAsString());
|
||||
} else {
|
||||
IParser parser = getNewParser(theServer.getFhirContext(), theRequestDetails);
|
||||
parser.encodeResourceToWriter(theResource, writer);
|
||||
}
|
||||
|
||||
return restUtil.sendWriterResponse(stausCode, contentType, charset, writer);
|
||||
}
|
||||
|
||||
// static Integer tryToExtractNamedParameter(HttpServletRequest theRequest, String name) {
|
||||
|
|
|
@ -61,12 +61,12 @@ abstract class BaseValidatingInterceptor<T> extends InterceptorAdapter {
|
|||
private Integer myAddResponseIssueHeaderOnSeverity = null;
|
||||
private Integer myAddResponseOutcomeHeaderOnSeverity = null;
|
||||
private Integer myFailOnSeverity = ResultSeverityEnum.ERROR.ordinal();
|
||||
private int myMaximumHeaderLength = 200;
|
||||
private String myResponseIssueHeaderName = provideDefaultResponseHeaderName();
|
||||
private String myResponseIssueHeaderValue = DEFAULT_RESPONSE_HEADER_VALUE;
|
||||
private String myResponseIssueHeaderValueNoIssues = null;
|
||||
private String myResponseOutcomeHeaderName = provideDefaultResponseHeaderName();
|
||||
private List<IValidatorModule> myValidatorModules;
|
||||
|
||||
private void addResponseIssueHeader(RequestDetails theRequestDetails, SingleValidationMessage theNext) {
|
||||
// Perform any string substitutions from the message format
|
||||
StrLookup<?> lookup = new MyLookup(theNext);
|
||||
|
@ -78,7 +78,6 @@ abstract class BaseValidatingInterceptor<T> extends InterceptorAdapter {
|
|||
|
||||
theRequestDetails.getResponse().addHeader(myResponseIssueHeaderName, headerValue);
|
||||
}
|
||||
|
||||
public BaseValidatingInterceptor<T> addValidatorModule(IValidatorModule theModule) {
|
||||
Validate.notNull(theModule, "theModule must not be null");
|
||||
if (getValidatorModules() == null) {
|
||||
|
@ -98,6 +97,23 @@ abstract class BaseValidatingInterceptor<T> extends InterceptorAdapter {
|
|||
throw new UnprocessableEntityException(theRequestDetails.getServer().getFhirContext(), theValidationResult.toOperationOutcome());
|
||||
}
|
||||
|
||||
/**
|
||||
* If the validation produces a result with at least the given severity, a header with the name
|
||||
* specified by {@link #setResponseOutcomeHeaderName(String)} will be added containing a JSON encoded
|
||||
* OperationOutcome resource containing the validation results.
|
||||
*/
|
||||
public ResultSeverityEnum getAddResponseOutcomeHeaderOnSeverity() {
|
||||
return myAddResponseOutcomeHeaderOnSeverity != null ? ResultSeverityEnum.values()[myAddResponseOutcomeHeaderOnSeverity] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The maximum length for an individual header. If an individual header would be written exceeding this length,
|
||||
* the header value will be truncated.
|
||||
*/
|
||||
public int getMaximumHeaderLength() {
|
||||
return myMaximumHeaderLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the header specified by {@link #setAddResponseOutcomeHeaderOnSeverity(ResultSeverityEnum)}
|
||||
*/
|
||||
|
@ -109,15 +125,6 @@ abstract class BaseValidatingInterceptor<T> extends InterceptorAdapter {
|
|||
return myValidatorModules;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the validation produces a result with at least the given severity, a header with the name
|
||||
* specified by {@link #setResponseOutcomeHeaderName(String)} will be added containing a JSON encoded
|
||||
* OperationOutcome resource containing the validation results.
|
||||
*/
|
||||
public ResultSeverityEnum getAddResponseOutcomeHeaderOnSeverity() {
|
||||
return myAddResponseOutcomeHeaderOnSeverity != null ? ResultSeverityEnum.values()[myAddResponseOutcomeHeaderOnSeverity] : null;
|
||||
}
|
||||
|
||||
abstract String provideDefaultResponseHeaderName();
|
||||
|
||||
/**
|
||||
|
@ -148,6 +155,15 @@ abstract class BaseValidatingInterceptor<T> extends InterceptorAdapter {
|
|||
myFailOnSeverity = theSeverity != null ? theSeverity.ordinal() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The maximum length for an individual header. If an individual header would be written exceeding this length,
|
||||
* the header value will be truncated. Value must be greater than 100.
|
||||
*/
|
||||
public void setMaximumHeaderLength(int theMaximumHeaderLength) {
|
||||
Validate.isTrue(theMaximumHeaderLength >= 100, "theMaximumHeadeerLength must be >= 100");
|
||||
myMaximumHeaderLength = theMaximumHeaderLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the name of the response header to add validation failures to
|
||||
*
|
||||
|
@ -271,6 +287,9 @@ abstract class BaseValidatingInterceptor<T> extends InterceptorAdapter {
|
|||
if (outcome != null) {
|
||||
IParser parser = theRequestDetails.getServer().getFhirContext().newJsonParser().setPrettyPrint(false);
|
||||
String encoded = parser.encodeResourceToString(outcome);
|
||||
if (encoded.length() > getMaximumHeaderLength()) {
|
||||
encoded = encoded.substring(0, getMaximumHeaderLength() - 3) + "...";
|
||||
}
|
||||
theRequestDetails.getResponse().addHeader(myResponseOutcomeHeaderName, encoded);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
package ca.uhn.fhir.util;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.defaultIfBlank;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLDecoder;
|
||||
import java.net.URLEncoder;
|
||||
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
|
||||
|
@ -37,8 +40,7 @@ public class UrlUtil {
|
|||
}
|
||||
|
||||
/**
|
||||
* Resolve a relative URL - THIS METHOD WILL NOT FAIL but will log a warning and return theEndpoint if the input is
|
||||
* invalid.
|
||||
* Resolve a relative URL - THIS METHOD WILL NOT FAIL but will log a warning and return theEndpoint if the input is invalid.
|
||||
*/
|
||||
public static String constructAbsoluteUrl(String theBase, String theEndpoint) {
|
||||
if (theEndpoint == null) {
|
||||
|
@ -174,45 +176,61 @@ public class UrlUtil {
|
|||
//@formatter:on
|
||||
public static UrlParts parseUrl(String theUrl) {
|
||||
String url = theUrl;
|
||||
if (url.matches("\\/[a-zA-Z]+\\?.*")) {
|
||||
url = url.substring(1);
|
||||
}
|
||||
|
||||
UrlParts retVal = new UrlParts();
|
||||
|
||||
int nextStart = 0;
|
||||
boolean nextIsHistory = false;
|
||||
|
||||
for (int idx = 0; idx < url.length(); idx++) {
|
||||
char nextChar = url.charAt(idx);
|
||||
boolean atEnd = (idx + 1) == url.length();
|
||||
if (nextChar == '?' || nextChar == '/' || atEnd) {
|
||||
int endIdx = (atEnd && nextChar != '?') ? idx + 1 : idx;
|
||||
String nextSubstring = url.substring(nextStart, endIdx);
|
||||
if (retVal.getResourceType() == null) {
|
||||
retVal.setResourceType(nextSubstring);
|
||||
} else if (retVal.getResourceId() == null) {
|
||||
retVal.setResourceId(nextSubstring);
|
||||
} else if (nextIsHistory) {
|
||||
retVal.setVersionId(nextSubstring);
|
||||
} else {
|
||||
if (nextSubstring.equals(Constants.URL_TOKEN_HISTORY)) {
|
||||
nextIsHistory = true;
|
||||
} else {
|
||||
throw new InvalidRequestException("Invalid FHIR resource URL: " + url);
|
||||
}
|
||||
}
|
||||
if (nextChar == '?') {
|
||||
if (url.length() > idx + 1) {
|
||||
retVal.setParams(url.substring(idx + 1, url.length()));
|
||||
}
|
||||
break;
|
||||
}
|
||||
nextStart = idx + 1;
|
||||
if (url.startsWith("http")) {
|
||||
if (url.startsWith("/")) {
|
||||
url = url.substring(1);
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
int qmIdx = url.indexOf('?');
|
||||
if (qmIdx != -1) {
|
||||
retVal.setParams(defaultIfBlank(url.substring(qmIdx + 1), null));
|
||||
url = url.substring(0, qmIdx);
|
||||
}
|
||||
|
||||
IdDt id = new IdDt(url);
|
||||
retVal.setResourceType(id.getResourceType());
|
||||
retVal.setResourceId(id.getIdPart());
|
||||
retVal.setVersionId(id.getVersionIdPart());
|
||||
return retVal;
|
||||
} else {
|
||||
if (url.matches("\\/[a-zA-Z]+\\?.*")) {
|
||||
url = url.substring(1);
|
||||
}
|
||||
int nextStart = 0;
|
||||
boolean nextIsHistory = false;
|
||||
|
||||
for (int idx = 0; idx < url.length(); idx++) {
|
||||
char nextChar = url.charAt(idx);
|
||||
boolean atEnd = (idx + 1) == url.length();
|
||||
if (nextChar == '?' || nextChar == '/' || atEnd) {
|
||||
int endIdx = (atEnd && nextChar != '?') ? idx + 1 : idx;
|
||||
String nextSubstring = url.substring(nextStart, endIdx);
|
||||
if (retVal.getResourceType() == null) {
|
||||
retVal.setResourceType(nextSubstring);
|
||||
} else if (retVal.getResourceId() == null) {
|
||||
retVal.setResourceId(nextSubstring);
|
||||
} else if (nextIsHistory) {
|
||||
retVal.setVersionId(nextSubstring);
|
||||
} else {
|
||||
if (nextSubstring.equals(Constants.URL_TOKEN_HISTORY)) {
|
||||
nextIsHistory = true;
|
||||
} else {
|
||||
throw new InvalidRequestException("Invalid FHIR resource URL: " + url);
|
||||
}
|
||||
}
|
||||
if (nextChar == '?') {
|
||||
if (url.length() > idx + 1) {
|
||||
retVal.setParams(url.substring(idx + 1, url.length()));
|
||||
}
|
||||
break;
|
||||
}
|
||||
nextStart = idx + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
}
|
||||
|
||||
public static class UrlParts {
|
||||
|
|
|
@ -61,4 +61,8 @@ public class VersionUtil {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
getVersion();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,4 +31,8 @@ public interface INarrative extends ICompositeType {
|
|||
// TODO: use less broad exception type here
|
||||
public String getDivAsString() throws Exception;
|
||||
|
||||
public INarrative setStatusAsString(String theString);
|
||||
|
||||
public String getStatusAsString();
|
||||
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ ca.uhn.fhir.jpa.dao.BaseHapiFhirDao.transactionOperationFailedNoId=Failed to {0}
|
|||
ca.uhn.fhir.jpa.dao.BaseHapiFhirDao.transactionOperationFailedUnknownId=Failed to {0} resource in transaction because no resource could be found with ID {1}
|
||||
|
||||
ca.uhn.fhir.jpa.dao.BaseHapiFhirSystemDao.transactionContainsMultipleWithDuplicateId=Transaction bundle contains multiple resources with ID: {0}
|
||||
ca.uhn.fhir.jpa.dao.BaseHapiFhirSystemDao.transactionEntryHasInvalidVerb=Transaction bundle entry has missing or invalid HTTP Verb specified in Bundle.entry.request.method. Found value: "{0}"
|
||||
ca.uhn.fhir.jpa.dao.BaseHapiFhirSystemDao.transactionEntryHasInvalidVerb=Transaction bundle entry has missing or invalid HTTP Verb specified in Bundle.entry({1}).request.method. Found value: "{0}"
|
||||
ca.uhn.fhir.jpa.dao.BaseHapiFhirSystemDao.transactionMissingUrl=Unable to perform {0}, no URL provided.
|
||||
ca.uhn.fhir.jpa.dao.BaseHapiFhirSystemDao.transactionInvalidUrl=Unable to perform {0}, URL provided is invalid: {1}
|
||||
|
||||
|
|
|
@ -16,13 +16,13 @@
|
|||
<th:block th:switch="${fhirVersion}">
|
||||
<th:block th:case="'DSTU1'">
|
||||
<th:block th:if="${not resource.name.textElement.empty}" th:text="${resource.name.textElement.value}"/>
|
||||
<th:block th:if=" ${resource.name.textElement.empty} and ${not resource.name.codingFirstRep.displayElement.empty}" th:text="${resource.name.codingFirstRep.display}"/>
|
||||
<th:block th:if= "${resource.name.textElement.empty} and ${resource.name.codingFirstRep.displayElement.empty}" th:text="Untitled Diagnostic Report"/>
|
||||
<th:block th:if=" ${resource.name.textElement.empty} and ${not resource.name.coding[0].displayElement.empty}" th:text="${resource.name.coding[0].display}"/>
|
||||
<th:block th:if= "${resource.name.textElement.empty} and ${resource.name.coding[0].displayElement.empty}" th:text="Untitled Diagnostic Report"/>
|
||||
</th:block>
|
||||
<th:block th:case="*">
|
||||
<th:block th:if="${not resource.code.textElement.empty}" th:text="${resource.code.textElement.value}"/>
|
||||
<th:block th:if=" ${resource.code.textElement.empty} and ${not resource.code.codingFirstRep.displayElement.empty}" th:text="${resource.code.codingFirstRep.display}"/>
|
||||
<th:block th:if= "${resource.code.textElement.empty} and ${resource.code.codingFirstRep.displayElement.empty}" th:text="Untitled Diagnostic Report"/>
|
||||
<th:block th:if="${not resource.code.textElement.empty} or ${resource.code.coding.empty}" th:text="${resource.code.textElement.value}"/>
|
||||
<th:block th:if="${not resource.code.coding.empty} and ${resource.code.textElement.empty} and ${not resource.code.coding[0].displayElement.empty}" th:text="${resource.code.coding[0].display}"/>
|
||||
<th:block th:if="${not resource.code.coding.empty} and ${resource.code.textElement.empty} and ${resource.code.coding[0].displayElement.empty}" th:text="Untitled Diagnostic Report"/>
|
||||
</th:block>
|
||||
</th:block>
|
||||
<!--/*--> Complete Blood Count <!--*/-->
|
||||
|
@ -76,7 +76,7 @@
|
|||
<td th:narrative="${result.resource.value}">2.2 g/L</td>
|
||||
<td>
|
||||
<th:block th:if="${not result.resource.interpretation.textElement.empty}" th:text="${result.resource.interpretation.text}"/>
|
||||
<th:block th:if="${result.resource.interpretation.textElement.empty} and ${not result.resource.interpretation.codingFirstRep.displayElement.empty}" th:text="${result.resource.interpretation.codingFirstRep.display}"/>
|
||||
<th:block th:if="${result.resource.interpretation.textElement.empty} and ${not result.resource.interpretation.coding.empty} and ${not result.resource.interpretation.coding[0].displayElement.empty}" th:text="${result.resource.interpretation.coding[0].display}"/>
|
||||
<!--/*--> N <!--*/-->
|
||||
</td>
|
||||
<td>
|
||||
|
|
|
@ -11,16 +11,18 @@ a browser.
|
|||
<!--*/-->
|
||||
|
||||
<div>
|
||||
<div class="hapiHeaderText" th:if="${not resource.nameFirstRep.empty}" th:narrative="${resource.nameFirstRep}"/>
|
||||
<th:block th:unless="${#lists.isEmpty(resource.name)}">
|
||||
<div class="hapiHeaderText" th:narrative="${resource.name[0]}"/>
|
||||
</th:block>
|
||||
<table class="hapiPropertyTable">
|
||||
<tbody>
|
||||
<tr th:if="${not resource.identifierFirstRep.empty}">
|
||||
<tr th:if="${not resource.identifier.empty}">
|
||||
<td>Identifier</td>
|
||||
<td th:narrative="${resource.identifierFirstRep}"></td>
|
||||
<td th:narrative="${resource.identifier[0]}"></td>
|
||||
</tr>
|
||||
<tr th:if="${not resource.addressFirstRep.empty}">
|
||||
<tr th:if="${not resource.address.empty}">
|
||||
<td>Address</td>
|
||||
<td th:narrative="${resource.addressFirstRep}"></td>
|
||||
<td th:narrative="${resource.address[0]}"></td>
|
||||
</tr>
|
||||
<tr th:if="${not resource.birthDateElement.empty}">
|
||||
<td>Date of birth</td>
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
<div>
|
||||
<th:block th:if="${not resource.textElement.empty}" th:text="${resource.textElement.value}"/>
|
||||
<th:block th:if="${resource.textElement.empty}">
|
||||
<th:block th:if="${!resource.codingFirstRep.empty}">
|
||||
<th:block th:if="${!resource.codingFirstRep.displayElement.empty}" th:text="${!resource.codingFirstRep.displayElement.value}"/>
|
||||
<th:block th:if="${resource.codingFirstRep.displayElement.empty}">
|
||||
<th:block th:if="${!resource.codingFirstRep.codeElement.empty}">
|
||||
<th:block th:text="${resource.codingFirstRep.codeElement.value}"/>
|
||||
<th:block th:if="${!resource.coding.empty}">
|
||||
<th:block th:if="${!resource.coding[0].displayElement.empty}" th:text="${!resource.coding[0].displayElement.value}"/>
|
||||
<th:block th:if="${resource.coding[0].displayElement.empty}">
|
||||
<th:block th:if="${!resource.coding[0].codeElement.empty}">
|
||||
<th:block th:text="${resource.coding[0].codeElement.value}"/>
|
||||
</th:block>
|
||||
</th:block>
|
||||
</th:block>
|
||||
|
|
|
@ -1,5 +1,12 @@
|
|||
<div>
|
||||
<th:block th:if="${not resource.comparatorElement.empty}" th:text="${resource.comparatorElement.value}"/>
|
||||
<th:block th:if="${not resource.valueElement.empty}" th:text="${resource.valueElement.valueAsString}"/>
|
||||
<th:block th:if="${not resource.unitsElement.empty}" th:text="${resource.unitsElement.value}"/>
|
||||
<th:block th:switch="${fhirVersion}">
|
||||
<th:block th:case="'DSTU1'">
|
||||
<th:block th:if="${not resource.unitsElement.empty}" th:text="${resource.unitsElement.value}"/>
|
||||
</th:block>
|
||||
<th:block th:case="*">
|
||||
<th:block th:if="${not resource.unitElement.empty}" th:text="${resource.unitElement.value}"/>
|
||||
</th:block>
|
||||
</th:block>
|
||||
</div>
|
||||
|
|
|
@ -1,4 +1,11 @@
|
|||
<div>
|
||||
<th:block th:if="${not resource.valueElement.empty}" th:text="${resource.valueElement.valueAsString}"/>
|
||||
<th:block th:if="${not resource.unitsElement.empty}" th:text="${resource.unitsElement.value}"/>
|
||||
<th:block th:switch="${fhirVersion}">
|
||||
<th:block th:case="'DSTU1'">
|
||||
<th:block th:if="${not resource.unitsElement.empty}" th:text="${resource.unitsElement.value}"/>
|
||||
</th:block>
|
||||
<th:block th:case="*">
|
||||
<th:block th:if="${not resource.unitElement.empty}" th:text="${resource.unitElement.value}"/>
|
||||
</th:block>
|
||||
</th:block>
|
||||
</div>
|
||||
|
|
|
@ -48,25 +48,19 @@ simplequantity.narrative=classpath:ca/uhn/fhir/narrative/datatype/SimpleQuantity
|
|||
|
||||
diagnosticreport.class=ca.uhn.fhir.model.dstu.resource.DiagnosticReport
|
||||
diagnosticreport.narrative=classpath:ca/uhn/fhir/narrative/DiagnosticReport.html
|
||||
diagnosticreport.title=classpath:ca/uhn/fhir/narrative/title/DiagnosticReport.html
|
||||
|
||||
encounter.class=ca.uhn.fhir.model.dstu.resource.Encounter
|
||||
encounter.title=classpath:ca/uhn/fhir/narrative/title/Encounter.html
|
||||
|
||||
operationoutcome.class=ca.uhn.fhir.model.dstu.resource.OperationOutcome
|
||||
operationoutcome.title=classpath:ca/uhn/fhir/narrative/title/OperationOutcome.html
|
||||
operationoutcome.narrative=classpath:ca/uhn/fhir/narrative/OperationOutcome.html
|
||||
|
||||
organization.class=ca.uhn.fhir.model.dstu.resource.Organization
|
||||
organization.title=classpath:ca/uhn/fhir/narrative/title/Organization.html
|
||||
|
||||
patient.class=ca.uhn.fhir.model.dstu.resource.Patient
|
||||
patient.narrative=classpath:ca/uhn/fhir/narrative/Patient.html
|
||||
patient.title=classpath:ca/uhn/fhir/narrative/title/Patient.html
|
||||
|
||||
medicationprescription.class=ca.uhn.fhir.model.dstu.resource.MedicationPrescription
|
||||
medicationprescription.narrative=classpath:ca/uhn/fhir/narrative/MedicationPrescription.html
|
||||
medicationprescription.title=classpath:ca/uhn/fhir/narrative/title/MedicationPrescription.html
|
||||
|
||||
medication.class=ca.uhn.fhir.model.dstu.resource.Medication
|
||||
medication.narrative=classpath:ca/uhn/fhir/narrative/Medication.html
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
<div>
|
||||
<th:block th:switch="${fhirVersion}">
|
||||
<th:block th:case="'DSTU1'">
|
||||
<th:block th:if="${not resource.name.textElement.empty}" th:text="${resource.name.textElement.value}"/>
|
||||
<th:block th:if=" ${resource.name.textElement.empty} and ${not resource.name.codingFirstRep.displayElement.empty}" th:text="${resource.name.codingFirstRep.display}"/>
|
||||
<th:block th:if= "${resource.name.textElement.empty} and ${resource.name.codingFirstRep.displayElement.empty}" th:text="Untitled Diagnostic Report"/>
|
||||
</th:block>
|
||||
<th:block th:case="*">
|
||||
<th:block th:if="${not resource.code.textElement.empty}" th:text="${resource.code.textElement.value}"/>
|
||||
<th:block th:if=" ${resource.code.textElement.empty} and ${not resource.code.codingFirstRep.displayElement.empty}" th:text="${resource.code.codingFirstRep.display}"/>
|
||||
<th:block th:if= "${resource.code.textElement.empty} and ${resource.code.codingFirstRep.displayElement.empty}" th:text="Untitled Diagnostic Report"/>
|
||||
</th:block>
|
||||
</th:block>
|
||||
<th:block th:if="${not resource.statusElement.empty}" th:text="' - ' + ${resource.statusElement.value}"/>
|
||||
<th:block th:text="' - ' + ${resource.result.size} + ' observations'"/>
|
||||
|
||||
</div>
|
|
@ -1,15 +0,0 @@
|
|||
<div>
|
||||
<th:block th:if="${!resource.identifierFirstRep.empty}" th:narrative="${resource.identifierFirstRep}" />
|
||||
<th:block th:if="${not resource.statusElement.empty}">
|
||||
/ <th:block th:narrative="${resource.statusElement}"/>
|
||||
</th:block>
|
||||
<th:block th:if="${not resource.typeFirstRep.empty}">
|
||||
/ <th:block th:narrative="${resource.typeFirstRep}"/>
|
||||
</th:block>
|
||||
<th:block th:if="${not resource.classElementElement.empty}">
|
||||
/ <th:block th:narrative="${resource.classElementElement}"/>
|
||||
</th:block>
|
||||
<th:block th:if="${not resource.period.empty}">
|
||||
/ <th:block th:narrative="${resource.period}"/>
|
||||
</th:block>
|
||||
</div>
|
|
@ -1,3 +0,0 @@
|
|||
<div>
|
||||
<div class="hapiHeaderText" th:if="${not resource.medication.resource.nameElement.empty}" th:narrative="${resource.medication.resource.nameElement}"></div>
|
||||
</div>
|
|
@ -1,5 +0,0 @@
|
|||
<div>
|
||||
Operation Outcome
|
||||
<th:block th:if="${resource.issue.size} == 1" th:text="'(' + ${resource.issueFirstRep.severityElement.value} + ')'"/>
|
||||
<th:block th:if="${resource.issue.size} != 1" th:text="'(' + ${resource.issue.size} + ' issues)'"/>
|
||||
</div>
|
|
@ -1,4 +0,0 @@
|
|||
<div>
|
||||
<th:block th:if="${not resource.nameElement.empty}" th:text="${resource.nameElement.value}"/>
|
||||
<th:block th:if="${resource.nameElement.empty}">Unknown Organization</th:block>
|
||||
</div>
|
|
@ -1,6 +0,0 @@
|
|||
<div>
|
||||
<th:block th:narrative="${resource.nameFirstRep}" />
|
||||
<th:block th:if="${not resource.identifierFirstRep.empty}">
|
||||
(<th:block th:narrative="${resource.identifierFirstRep}">8708660</th:block>)
|
||||
</th:block>
|
||||
</div>
|
|
@ -4,6 +4,8 @@ import static org.junit.Assert.*;
|
|||
|
||||
import org.junit.Test;
|
||||
|
||||
import ca.uhn.fhir.util.UrlUtil.UrlParts;
|
||||
|
||||
public class UrlUtilTest {
|
||||
|
||||
@Test
|
||||
|
@ -23,6 +25,18 @@ public class UrlUtilTest {
|
|||
assertFalse(UrlUtil.isValid(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseUrl() {
|
||||
assertEquals("ConceptMap", UrlUtil.parseUrl("http://hl7.org/fhir/ConceptMap/ussgfht-loincde").getResourceType());
|
||||
assertEquals("ConceptMap", UrlUtil.parseUrl("http://hl7.org/fhir/ConceptMap/ussgfht-loincde").getResourceType());
|
||||
assertEquals("ussgfht-loincde", UrlUtil.parseUrl("http://hl7.org/fhir/ConceptMap/ussgfht-loincde").getResourceId());
|
||||
assertEquals(null, UrlUtil.parseUrl("http://hl7.org/fhir/ConceptMap/ussgfht-loincde?").getParams());
|
||||
assertEquals("a=b", UrlUtil.parseUrl("http://hl7.org/fhir/ConceptMap/ussgfht-loincde?a=b").getParams());
|
||||
|
||||
assertEquals("a=b", UrlUtil.parseUrl("ConceptMap/ussgfht-loincde?a=b").getParams());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConstructAbsoluteUrl() {
|
||||
assertEquals("http://foo/bar/baz", UrlUtil.constructAbsoluteUrl(null, "http://foo/bar/baz"));
|
||||
|
|
|
@ -18,7 +18,6 @@ import ca.uhn.fhir.model.dstu2.resource.Bundle;
|
|||
import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry;
|
||||
import ca.uhn.fhir.model.dstu2.resource.StructureDefinition;
|
||||
import ca.uhn.fhir.model.dstu2.resource.ValueSet;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
import ca.uhn.fhir.rest.client.IGenericClient;
|
||||
|
||||
public class ValidationDataUploader extends BaseCommand {
|
||||
|
@ -64,7 +63,8 @@ public class ValidationDataUploader extends BaseCommand {
|
|||
|
||||
String vsContents;
|
||||
try {
|
||||
vsContents = IOUtils.toString(ValidationDataUploader.class.getResourceAsStream("/org/hl7/fhir/instance/model/valueset/valuesets.xml"), "UTF-8");
|
||||
ctx.getVersion().getPathToSchemaDefinitions();
|
||||
vsContents = IOUtils.toString(ValidationDataUploader.class.getResourceAsStream("/org/hl7/fhir/instance/model/valueset/"+"valuesets.xml"), "UTF-8");
|
||||
} catch (IOException e) {
|
||||
throw new CommandFailureException(e.toString());
|
||||
}
|
||||
|
@ -83,7 +83,7 @@ public class ValidationDataUploader extends BaseCommand {
|
|||
}
|
||||
|
||||
try {
|
||||
vsContents = IOUtils.toString(ValidationDataUploader.class.getResourceAsStream("/org/hl7/fhir/instance/model/valueset/v3-codesystems.xml"), "UTF-8");
|
||||
vsContents = IOUtils.toString(ValidationDataUploader.class.getResourceAsStream("/org/hl7/fhir/instance/model/valueset/"+"v3-codesystems.xml"), "UTF-8");
|
||||
} catch (IOException e) {
|
||||
throw new CommandFailureException(e.toString());
|
||||
}
|
||||
|
@ -102,7 +102,7 @@ public class ValidationDataUploader extends BaseCommand {
|
|||
}
|
||||
|
||||
try {
|
||||
vsContents = IOUtils.toString(ValidationDataUploader.class.getResourceAsStream("/org/hl7/fhir/instance/model/valueset/v2-tables.xml"), "UTF-8");
|
||||
vsContents = IOUtils.toString(ValidationDataUploader.class.getResourceAsStream("/org/hl7/fhir/instance/model/valueset/"+"v2-tables.xml"), "UTF-8");
|
||||
} catch (IOException e) {
|
||||
throw new CommandFailureException(e.toString());
|
||||
}
|
||||
|
@ -123,7 +123,7 @@ public class ValidationDataUploader extends BaseCommand {
|
|||
ResourcePatternResolver patternResolver = new PathMatchingResourcePatternResolver();
|
||||
Resource[] mappingLocations;
|
||||
try {
|
||||
mappingLocations = patternResolver.getResources("classpath*:org/hl7/fhir/instance/model/profile/*.profile.xml");
|
||||
mappingLocations = patternResolver.getResources("classpath*:org/hl7/fhir/instance/model/profile/"+"*.profile.xml");
|
||||
} catch (IOException e) {
|
||||
throw new CommandFailureException(e.toString());
|
||||
}
|
||||
|
|
|
@ -45,24 +45,13 @@ public class JaxRsResponseTest {
|
|||
boolean theRequestIsBrowser = false;
|
||||
boolean respondGzip = false;
|
||||
Set<SummaryEnum> theSummaryMode = Collections.<SummaryEnum>emptySet();
|
||||
Response result = (Response) RestfulServerUtils.streamResponseAsBundle(request.getServer(), bundle, theSummaryMode, theRequestIsBrowser, respondGzip, request);
|
||||
Response result = (Response) RestfulServerUtils.streamResponseAsBundle(request.getServer(), bundle, theSummaryMode, respondGzip, request);
|
||||
assertEquals(200, result.getStatus());
|
||||
assertEquals(Constants.CT_FHIR_JSON+Constants.CHARSET_UTF8_CTSUFFIX, result.getHeaderString(Constants.HEADER_CONTENT_TYPE));
|
||||
assertTrue(result.getEntity().toString().contains("Patient"));
|
||||
assertTrue(result.getEntity().toString().contains("15"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetResponseWriterBrowserNoZip() throws IOException {
|
||||
boolean theRequestIsBrowser = true;
|
||||
boolean respondGzip = false;
|
||||
Response result = (Response) RestfulServerUtils.streamResponseAsBundle(request.getServer(), bundle, theSummaryMode, theRequestIsBrowser, respondGzip, request);
|
||||
assertEquals(200, result.getStatus());
|
||||
assertEquals(Constants.CT_JSON+Constants.CHARSET_UTF8_CTSUFFIX, result.getHeaderString(Constants.HEADER_CONTENT_TYPE));
|
||||
assertTrue(result.getEntity().toString().contains("Patient"));
|
||||
assertTrue(result.getEntity().toString().contains("15"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSendAttachmentResponse() throws IOException {
|
||||
boolean theRequestIsBrowser = true;
|
||||
|
@ -73,7 +62,7 @@ public class JaxRsResponseTest {
|
|||
binary.setContentType(contentType);
|
||||
binary.setContent(content);
|
||||
boolean theAddContentLocationHeader = false;
|
||||
Response result = (Response) RestfulServerUtils.streamResponseAsResource(request.getServer(), binary, theRequestIsBrowser, theSummaryMode, 200, respondGzip, theAddContentLocationHeader, respondGzip, this.request);
|
||||
Response result = (Response) RestfulServerUtils.streamResponseAsResource(request.getServer(), binary, theSummaryMode, 200, theAddContentLocationHeader, respondGzip, this.request);
|
||||
assertEquals(200, result.getStatus());
|
||||
assertEquals(contentType, result.getHeaderString(Constants.HEADER_CONTENT_TYPE));
|
||||
assertEquals(content, result.getEntity());
|
||||
|
@ -86,7 +75,7 @@ public class JaxRsResponseTest {
|
|||
IBaseBinary binary = new Binary();
|
||||
binary.setContent(new byte[]{});
|
||||
boolean theAddContentLocationHeader = false;
|
||||
Response result = (Response) RestfulServerUtils.streamResponseAsResource(request.getServer(), binary, theRequestIsBrowser, theSummaryMode, 200, respondGzip, theAddContentLocationHeader, respondGzip, this.request);
|
||||
Response result = (Response) RestfulServerUtils.streamResponseAsResource(request.getServer(), binary, theSummaryMode, 200, theAddContentLocationHeader, respondGzip, this.request);
|
||||
assertEquals(200, result.getStatus());
|
||||
assertEquals(null, result.getHeaderString(Constants.HEADER_CONTENT_TYPE));
|
||||
assertEquals(null, result.getEntity());
|
||||
|
@ -98,7 +87,7 @@ public class JaxRsResponseTest {
|
|||
boolean respondGzip = true;
|
||||
IBaseBinary binary = new Binary();
|
||||
boolean theAddContentLocationHeader = false;
|
||||
Response result = (Response) RestfulServerUtils.streamResponseAsResource(request.getServer(), binary, theRequestIsBrowser, theSummaryMode, 200, respondGzip, theAddContentLocationHeader, respondGzip, this.request);
|
||||
Response result = (Response) RestfulServerUtils.streamResponseAsResource(request.getServer(), binary, theSummaryMode, 200, theAddContentLocationHeader, respondGzip, this.request);
|
||||
assertEquals(200, result.getStatus());
|
||||
assertEquals(null, result.getHeaderString(Constants.HEADER_CONTENT_TYPE));
|
||||
assertEquals(null, result.getEntity());
|
||||
|
|
|
@ -225,7 +225,7 @@
|
|||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.transaction</groupId>
|
||||
<artifactId>jta</artifactId>
|
||||
<artifactId>javax.transaction-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.mail</groupId>
|
||||
|
|
|
@ -91,7 +91,7 @@ public class BaseDstu21Config extends BaseConfig {
|
|||
return module;
|
||||
}
|
||||
|
||||
@Bean(autowire=Autowire.BY_NAME)
|
||||
@Bean(autowire=Autowire.BY_NAME, name="myJpaValidationSupportChainDstu21")
|
||||
public IValidationSupport validationSupportChainDstu21() {
|
||||
return new JpaValidationSupportChainDstu21();
|
||||
}
|
||||
|
|
|
@ -355,6 +355,9 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
|
|||
nextEntity = new ResourceLink(nextPath, theEntity, target);
|
||||
} else {
|
||||
if (!multiType) {
|
||||
if (nextSpDef.getName().equals("sourceuri")) {
|
||||
continue; // TODO: disable this eventually - ConceptMap:sourceuri is of type reference but points to a URI
|
||||
}
|
||||
throw new ConfigurationException("Search param " + nextSpDef.getName() + " is of unexpected datatype: " + nextObject.getClass());
|
||||
} else {
|
||||
continue;
|
||||
|
|
|
@ -292,6 +292,15 @@ public class FhirSystemDaoDstu21 extends BaseHapiFhirSystemDao<Bundle, Meta> {
|
|||
Map<IdType, IdType> idSubstitutions = new HashMap<IdType, IdType>();
|
||||
Map<IdType, DaoMethodOutcome> idToPersistedOutcome = new HashMap<IdType, DaoMethodOutcome>();
|
||||
|
||||
// Do all entries have a verb?
|
||||
for (int i = 0; i < theRequest.getEntry().size(); i++) {
|
||||
BundleEntryComponent nextReqEntry = theRequest.getEntry().get(i);
|
||||
HTTPVerb verb = nextReqEntry.getRequest().getMethodElement().getValue();
|
||||
if (verb == null) {
|
||||
throw new InvalidRequestException(getContext().getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionEntryHasInvalidVerb", nextReqEntry.getRequest().getMethod(), i));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We want to execute the transaction request bundle elements in the order
|
||||
* specified by the FHIR specification (see TransactionSorter) so we save the
|
||||
|
@ -366,9 +375,6 @@ public class FhirSystemDaoDstu21 extends BaseHapiFhirSystemDao<Bundle, Meta> {
|
|||
}
|
||||
|
||||
HTTPVerb verb = nextReqEntry.getRequest().getMethodElement().getValue();
|
||||
if (verb == null) {
|
||||
throw new InvalidRequestException(getContext().getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionEntryHasInvalidVerb", nextReqEntry.getRequest().getMethod()));
|
||||
}
|
||||
|
||||
String resourceType = res != null ? getContext().getResourceDefinition(res).getName() : null;
|
||||
BundleEntryComponent nextRespEntry = response.getEntry().get(originalRequestOrder.get(nextReqEntry));
|
||||
|
|
|
@ -9,6 +9,8 @@ import org.springframework.beans.factory.annotation.Qualifier;
|
|||
|
||||
public class JpaValidationSupportChainDstu21 extends ValidationSupportChain {
|
||||
|
||||
private DefaultProfileValidationSupport myDefaultProfileValidationSupport = new DefaultProfileValidationSupport();
|
||||
|
||||
@Autowired
|
||||
@Qualifier("myJpaValidationSupportDstu21")
|
||||
public ca.uhn.fhir.jpa.dao.IJpaValidationSupportDstu21 myJpaValidationSupportDstu21;
|
||||
|
@ -17,15 +19,13 @@ public class JpaValidationSupportChainDstu21 extends ValidationSupportChain {
|
|||
super();
|
||||
}
|
||||
|
||||
private DefaultProfileValidationSupport myDefaultProfileValidationSupport = new DefaultProfileValidationSupport();
|
||||
public void flush() {
|
||||
myDefaultProfileValidationSupport.flush();
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void postConstruct() {
|
||||
addValidationSupport(myDefaultProfileValidationSupport);
|
||||
addValidationSupport(myJpaValidationSupportDstu21);
|
||||
}
|
||||
|
||||
public void flush() {
|
||||
myDefaultProfileValidationSupport.flush();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import javax.persistence.EntityManager;
|
|||
import org.apache.commons.io.IOUtils;
|
||||
import org.hibernate.search.jpa.FullTextEntityManager;
|
||||
import org.hibernate.search.jpa.Search;
|
||||
import org.hl7.fhir.dstu21.hapi.validation.IValidationSupport;
|
||||
import org.hl7.fhir.dstu21.model.Bundle;
|
||||
import org.hl7.fhir.dstu21.model.CodeableConcept;
|
||||
import org.hl7.fhir.dstu21.model.Coding;
|
||||
|
@ -86,7 +87,9 @@ import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
|||
@ContextConfiguration(classes= {TestDstu21Config.class})
|
||||
//@formatter:on
|
||||
public abstract class BaseJpaDstu21Test extends BaseJpaTest {
|
||||
|
||||
@Autowired
|
||||
@Qualifier("myJpaValidationSupportChainDstu21")
|
||||
protected IValidationSupport myValidationSupport;
|
||||
@Autowired
|
||||
protected ApplicationContext myAppCtx;
|
||||
@Autowired
|
||||
|
|
|
@ -875,6 +875,43 @@ public class FhirSystemDaoDstu21Test extends BaseJpaDstu21SystemTest {
|
|||
assertEquals("Patient/temp6789", p.getLink().get(0).getOther().getReference());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransactionWithBundledValidationSourceAndTarget() throws Exception {
|
||||
|
||||
InputStream bundleRes = SystemProviderDstu2Test.class.getResourceAsStream("/questionnaire-sdc-profile-example-ussg-fht.xml");
|
||||
String bundleStr = IOUtils.toString(bundleRes);
|
||||
Bundle bundle = myFhirCtx.newXmlParser().parseResource(Bundle.class, bundleStr);
|
||||
|
||||
Bundle resp = mySystemDao.transaction(myRequestDetails, bundle);
|
||||
|
||||
String encoded = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(resp);
|
||||
ourLog.info(encoded);
|
||||
|
||||
encoded = myFhirCtx.newJsonParser().setPrettyPrint(false).encodeResourceToString(resp);
|
||||
//@formatter:off
|
||||
assertThat(encoded, containsString("\"response\":{" +
|
||||
"\"status\":\"201 Created\"," +
|
||||
"\"location\":\"Questionnaire/54127-6/_history/1\","));
|
||||
//@formatter:on
|
||||
|
||||
/*
|
||||
* Upload again to update
|
||||
*/
|
||||
|
||||
resp = mySystemDao.transaction(myRequestDetails, bundle);
|
||||
|
||||
encoded = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(resp);
|
||||
ourLog.info(encoded);
|
||||
|
||||
encoded = myFhirCtx.newJsonParser().setPrettyPrint(false).encodeResourceToString(resp);
|
||||
//@formatter:off
|
||||
assertThat(encoded, containsString("\"response\":{" +
|
||||
"\"status\":\"200 OK\"," +
|
||||
"\"location\":\"Questionnaire/54127-6/_history/2\","));
|
||||
//@formatter:on
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransactionFromBundle6() throws Exception {
|
||||
InputStream bundleRes = SystemProviderDstu2Test.class.getResourceAsStream("/simone_bundle3.xml");
|
||||
|
|
|
@ -13,18 +13,24 @@ import java.io.InputStream;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
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.ServletContextHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.hl7.fhir.dstu21.hapi.validation.FhirInstanceValidator;
|
||||
import org.hl7.fhir.dstu21.model.Bundle;
|
||||
import org.hl7.fhir.dstu21.model.Bundle.BundleType;
|
||||
import org.hl7.fhir.dstu21.model.Bundle.HTTPVerb;
|
||||
import org.hl7.fhir.dstu21.model.DecimalType;
|
||||
import org.hl7.fhir.dstu21.model.Enumerations.AdministrativeGender;
|
||||
import org.hl7.fhir.dstu21.model.IdType;
|
||||
import org.hl7.fhir.dstu21.model.Observation;
|
||||
import org.hl7.fhir.dstu21.model.OperationDefinition;
|
||||
|
@ -33,6 +39,7 @@ import org.hl7.fhir.dstu21.model.Parameters;
|
|||
import org.hl7.fhir.dstu21.model.Patient;
|
||||
import org.hl7.fhir.dstu21.model.StringType;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
@ -41,16 +48,20 @@ import org.springframework.transaction.annotation.Transactional;
|
|||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.jpa.dao.dstu21.BaseJpaDstu21Test;
|
||||
import ca.uhn.fhir.jpa.provider.SystemProviderDstu2Test;
|
||||
import ca.uhn.fhir.jpa.rp.dstu21.ObservationResourceProvider;
|
||||
import ca.uhn.fhir.jpa.rp.dstu21.OrganizationResourceProvider;
|
||||
import ca.uhn.fhir.jpa.rp.dstu21.PatientResourceProvider;
|
||||
import ca.uhn.fhir.jpa.testutil.RandomServerPortProvider;
|
||||
import ca.uhn.fhir.rest.client.IGenericClient;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
|
||||
import ca.uhn.fhir.rest.server.interceptor.ResponseHighlighterInterceptor;
|
||||
import ca.uhn.fhir.validation.ResultSeverityEnum;
|
||||
|
||||
public class SystemProviderDstu21Test extends BaseJpaDstu21Test {
|
||||
|
||||
|
@ -113,6 +124,62 @@ public class SystemProviderDstu21Test extends BaseJpaDstu21Test {
|
|||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@After
|
||||
public void after() {
|
||||
myRestServer.setUseBrowserFriendlyContentTypes(true);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Test
|
||||
public void testResponseUsesCorrectContentType() throws Exception {
|
||||
myRestServer.setUseBrowserFriendlyContentTypes(true);
|
||||
myRestServer.setDefaultResponseEncoding(EncodingEnum.JSON);
|
||||
|
||||
HttpGet get = new HttpGet(ourServerBase);
|
||||
// get.addHeader("Accept", "application/xml, text/html");
|
||||
CloseableHttpResponse http = ourHttpClient.execute(get);
|
||||
assertThat(http.getFirstHeader("Content-Type").getValue(), containsString("application/json+fhir"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testValidateUsingIncomingResources() throws Exception {
|
||||
FhirInstanceValidator val = new FhirInstanceValidator(myValidationSupport);
|
||||
RequestValidatingInterceptor interceptor = new RequestValidatingInterceptor();
|
||||
interceptor.addValidatorModule(val);
|
||||
interceptor.setFailOnSeverity(ResultSeverityEnum.ERROR);
|
||||
interceptor.setAddResponseHeaderOnSeverity(ResultSeverityEnum.INFORMATION);
|
||||
myRestServer.registerInterceptor(interceptor);
|
||||
try {
|
||||
|
||||
InputStream bundleRes = SystemProviderDstu2Test.class.getResourceAsStream("/questionnaire-sdc-profile-example-ussg-fht.xml");
|
||||
String bundleStr = IOUtils.toString(bundleRes);
|
||||
|
||||
HttpPost req = new HttpPost(ourServerBase);
|
||||
req.setEntity(new StringEntity(bundleStr, ContentType.parse(Constants.CT_FHIR_XML + "; charset=utf-8")));
|
||||
|
||||
CloseableHttpResponse resp = ourHttpClient.execute(req);
|
||||
|
||||
String encoded = IOUtils.toString(resp.getEntity().getContent());
|
||||
IOUtils.closeQuietly(resp.getEntity().getContent());
|
||||
ourLog.info(encoded);
|
||||
|
||||
//@formatter:off
|
||||
assertThat(encoded, containsString("Questionnaire/54127-6/_history/"));
|
||||
//@formatter:on
|
||||
|
||||
for (Header next : resp.getHeaders(RequestValidatingInterceptor.DEFAULT_RESPONSE_HEADER_NAME)) {
|
||||
ourLog.info(next.toString());
|
||||
}
|
||||
|
||||
} finally {
|
||||
myRestServer.unregisterInterceptor(interceptor);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testEverythingReturnsCorrectFormatInPagingLink() throws Exception {
|
||||
myRestServer.setDefaultResponseEncoding(EncodingEnum.JSON);
|
||||
|
@ -129,6 +196,7 @@ public class SystemProviderDstu21Test extends BaseJpaDstu21Test {
|
|||
HttpGet get = new HttpGet(ourServerBase + "/Patient/$everything");
|
||||
get.addHeader("Accept", "application/xml, text/html");
|
||||
CloseableHttpResponse http = ourHttpClient.execute(get);
|
||||
|
||||
try {
|
||||
String response = IOUtils.toString(http.getEntity().getContent());
|
||||
ourLog.info(response);
|
||||
|
@ -282,6 +350,23 @@ public class SystemProviderDstu21Test extends BaseJpaDstu21Test {
|
|||
ourLog.info(response);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransactionWithIncompleteBundle() throws Exception {
|
||||
Patient patient = new Patient();
|
||||
patient.setGender(AdministrativeGender.MALE);
|
||||
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.setType(BundleType.TRANSACTION);
|
||||
bundle.addEntry().setResource(patient);
|
||||
|
||||
try {
|
||||
ourClient.transaction().withBundle(bundle).prettyPrint().execute();
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertThat(e.toString(), containsString(""));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransactionFromBundle2() throws Exception {
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -126,20 +126,6 @@ public class JpaServerDemo extends RestfulServer {
|
|||
FhirContext ctx = getFhirContext();
|
||||
ctx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator());
|
||||
|
||||
/*
|
||||
* This tells the server to use "browser friendly" MIME types if it
|
||||
* detects that the request is coming from a browser, in the hopes that the
|
||||
* browser won't just treat the content as a binary payload and try
|
||||
* to download it (which is what generally happens if you load a
|
||||
* FHIR URL in a browser).
|
||||
*
|
||||
* This means that the server isn't technically complying with the
|
||||
* FHIR specification for direct browser requests, but this mode
|
||||
* is very helpful for testing and troubleshooting since it means
|
||||
* you can look at FHIR URLs directly in a browser.
|
||||
*/
|
||||
setUseBrowserFriendlyContentTypes(true);
|
||||
|
||||
/*
|
||||
* Default to XML and pretty printing
|
||||
*/
|
||||
|
|
|
@ -61,7 +61,6 @@ public class Dstu1BundleFactory implements IVersionSpecificBundleFactory {
|
|||
myContext = theContext;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void addResourcesToBundle(List<IBaseResource> theResult, BundleTypeEnum theBundleType, String theServerBase, BundleInclusionRule theBundleInclusionRule, Set<Include> theIncludes) {
|
||||
if (myBundle == null) {
|
||||
|
@ -78,7 +77,7 @@ public class Dstu1BundleFactory implements IVersionSpecificBundleFactory {
|
|||
}
|
||||
|
||||
for (IBaseResource nextBaseRes : theResult) {
|
||||
IResource next = (IResource)nextBaseRes;
|
||||
IResource next = (IResource) nextBaseRes;
|
||||
|
||||
Set<String> containedIds = new HashSet<String>();
|
||||
for (IResource nextContained : next.getContained().getContainedResources()) {
|
||||
|
@ -87,23 +86,13 @@ public class Dstu1BundleFactory implements IVersionSpecificBundleFactory {
|
|||
}
|
||||
}
|
||||
|
||||
if (myContext.getNarrativeGenerator() != null) {
|
||||
String title = myContext.getNarrativeGenerator().generateTitle(next);
|
||||
ourLog.trace("Narrative generator created title: {}", title);
|
||||
if (StringUtils.isNotBlank(title)) {
|
||||
ResourceMetadataKeyEnum.TITLE.put(next, title);
|
||||
}
|
||||
} else {
|
||||
ourLog.trace("No narrative generator specified");
|
||||
}
|
||||
|
||||
List<ResourceReferenceInfo> references = myContext.newTerser().getAllResourceReferences(next);
|
||||
List<ResourceReferenceInfo> references = myContext.newTerser().getAllResourceReferences(next);
|
||||
do {
|
||||
List<IResource> addedResourcesThisPass = new ArrayList<IResource>();
|
||||
|
||||
for (ResourceReferenceInfo nextRefInfo : references) {
|
||||
if (!theBundleInclusionRule.shouldIncludeReferencedResource(nextRefInfo, theIncludes))
|
||||
continue;
|
||||
for (ResourceReferenceInfo nextRefInfo : references) {
|
||||
if (!theBundleInclusionRule.shouldIncludeReferencedResource(nextRefInfo, theIncludes))
|
||||
continue;
|
||||
|
||||
IResource nextRes = (IResource) nextRefInfo.getResourceReference().getResource();
|
||||
if (nextRes != null) {
|
||||
|
@ -128,7 +117,7 @@ public class Dstu1BundleFactory implements IVersionSpecificBundleFactory {
|
|||
}
|
||||
}
|
||||
|
||||
includedResources.addAll(addedResourcesThisPass);
|
||||
includedResources.addAll(addedResourcesThisPass);
|
||||
|
||||
// Linked resources may themselves have linked resources
|
||||
references = new ArrayList<ResourceReferenceInfo>();
|
||||
|
@ -155,7 +144,7 @@ public class Dstu1BundleFactory implements IVersionSpecificBundleFactory {
|
|||
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override
|
||||
public void addRootPropertiesToBundle(String theAuthor, String theServerBase, String theCompleteUrl, Integer theTotalResults, BundleTypeEnum theBundleType, IPrimitiveType<Date> theLastUpdated) {
|
||||
if (myBundle.getAuthorName().isEmpty()) {
|
||||
myBundle.getAuthorName().setValue(theAuthor);
|
||||
|
@ -197,7 +186,8 @@ public class Dstu1BundleFactory implements IVersionSpecificBundleFactory {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void initializeBundleFromBundleProvider(IRestfulServer theServer, IBundleProvider theResult, EncodingEnum theResponseEncoding, String theServerBase, String theCompleteUrl, boolean thePrettyPrint, int theOffset, Integer theLimit, String theSearchId, BundleTypeEnum theBundleType, Set<Include> theIncludes) {
|
||||
public void initializeBundleFromBundleProvider(IRestfulServer theServer, IBundleProvider theResult, EncodingEnum theResponseEncoding, String theServerBase, String theCompleteUrl,
|
||||
boolean thePrettyPrint, int theOffset, Integer theLimit, String theSearchId, BundleTypeEnum theBundleType, Set<Include> theIncludes) {
|
||||
int numToReturn;
|
||||
String searchId = null;
|
||||
List<IBaseResource> resourceList;
|
||||
|
@ -255,7 +245,8 @@ public class Dstu1BundleFactory implements IVersionSpecificBundleFactory {
|
|||
|
||||
if (searchId != null) {
|
||||
if (theOffset + numToReturn < theResult.size()) {
|
||||
myBundle.getLinkNext().setValue(RestfulServerUtils.createPagingLink(theIncludes, theServerBase, searchId, theOffset + numToReturn, numToReturn, theResponseEncoding, thePrettyPrint, theBundleType));
|
||||
myBundle.getLinkNext()
|
||||
.setValue(RestfulServerUtils.createPagingLink(theIncludes, theServerBase, searchId, theOffset + numToReturn, numToReturn, theResponseEncoding, thePrettyPrint, theBundleType));
|
||||
}
|
||||
if (theOffset > 0) {
|
||||
int start = Math.max(0, theOffset - limit);
|
||||
|
@ -266,7 +257,8 @@ public class Dstu1BundleFactory implements IVersionSpecificBundleFactory {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void initializeBundleFromResourceList(String theAuthor, List<? extends IBaseResource> theResult, String theServerBase, String theCompleteUrl, int theTotalResults, BundleTypeEnum theBundleType) {
|
||||
public void initializeBundleFromResourceList(String theAuthor, List<? extends IBaseResource> theResult, String theServerBase, String theCompleteUrl, int theTotalResults,
|
||||
BundleTypeEnum theBundleType) {
|
||||
myBundle = new Bundle();
|
||||
|
||||
myBundle.getAuthorName().setValue(theAuthor);
|
||||
|
@ -294,16 +286,6 @@ public class Dstu1BundleFactory implements IVersionSpecificBundleFactory {
|
|||
}
|
||||
}
|
||||
|
||||
if (myContext.getNarrativeGenerator() != null) {
|
||||
String title = myContext.getNarrativeGenerator().generateTitle(next);
|
||||
ourLog.trace("Narrative generator created title: {}", title);
|
||||
if (StringUtils.isNotBlank(title)) {
|
||||
ResourceMetadataKeyEnum.TITLE.put(next, title);
|
||||
}
|
||||
} else {
|
||||
ourLog.trace("No narrative generator specified");
|
||||
}
|
||||
|
||||
List<BaseResourceReferenceDt> references = myContext.newTerser().getAllPopulatedChildElementsOfType(next, BaseResourceReferenceDt.class);
|
||||
do {
|
||||
List<IBaseResource> addedResourcesThisPass = new ArrayList<IBaseResource>();
|
||||
|
@ -367,10 +349,9 @@ public class Dstu1BundleFactory implements IVersionSpecificBundleFactory {
|
|||
throw new UnsupportedOperationException("DSTU1 server doesn't support resource style bundles");
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<IBaseResource> toListOfResources() {
|
||||
return new ArrayList<IBaseResource>( myBundle.toListOfResources());
|
||||
return new ArrayList<IBaseResource>(myBundle.toListOfResources());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -19,7 +19,6 @@ public class CustomThymeleafNarrativeGeneratorTest {
|
|||
public void testGenerator() {
|
||||
|
||||
CustomThymeleafNarrativeGenerator gen = new CustomThymeleafNarrativeGenerator("classpath:narrative/customnarrative.properties");
|
||||
gen.setFhirContext(ourCtx);
|
||||
|
||||
Practitioner p = new Practitioner();
|
||||
p.addIdentifier("sys", "val1");
|
||||
|
@ -28,7 +27,7 @@ public class CustomThymeleafNarrativeGeneratorTest {
|
|||
p.getName().addFamily("fam1").addGiven("given");
|
||||
|
||||
NarrativeDt narrative = new NarrativeDt();
|
||||
gen.generateNarrative(p, narrative);
|
||||
gen.generateNarrative(ourCtx, p, narrative);
|
||||
|
||||
String actual = narrative.getDiv().getValueAsString();
|
||||
ourLog.info(actual);
|
||||
|
|
|
@ -62,14 +62,10 @@ public class DefaultThymeleafNarrativeGeneratorTest {
|
|||
value.setBirthDate(new Date(), TemporalPrecisionEnum.DAY);
|
||||
|
||||
NarrativeDt narrative = new NarrativeDt();
|
||||
gen.generateNarrative(value, narrative);
|
||||
gen.generateNarrative(myCtx, value, narrative);
|
||||
String output = narrative.getDiv().getValueAsString();
|
||||
assertThat(output, StringContains.containsString("<div class=\"hapiHeaderText\"> joe john <b>BLOW </b></div>"));
|
||||
|
||||
String title = gen.generateTitle(value);
|
||||
assertEquals("joe john BLOW (123456)", title);
|
||||
ourLog.info(title);
|
||||
|
||||
// Removed because label is gone in DSTU2
|
||||
// value.getIdentifierFirstRep().setLabel("FOO MRN 123");
|
||||
// title = gen.generateTitle(value);
|
||||
|
@ -78,6 +74,24 @@ public class DefaultThymeleafNarrativeGeneratorTest {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGeneratePatientWithoutData() throws DataFormatException {
|
||||
Patient value = new Patient();
|
||||
|
||||
NarrativeDt narrative = new NarrativeDt();
|
||||
gen.generateNarrative(myCtx, value, narrative);
|
||||
String output = narrative.getDiv().getValueAsString();
|
||||
assertThat(output, StringContains.containsString("<div>"));
|
||||
|
||||
// Removed because label is gone in DSTU2
|
||||
// value.getIdentifierFirstRep().setLabel("FOO MRN 123");
|
||||
// title = gen.generateTitle(value);
|
||||
// assertEquals("joe john BLOW (FOO MRN 123)", title);
|
||||
// ourLog.info(title);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testGenerateEncounter() throws DataFormatException {
|
||||
Encounter enc = new Encounter();
|
||||
|
@ -87,11 +101,6 @@ public class DefaultThymeleafNarrativeGeneratorTest {
|
|||
enc.setPeriod(new PeriodDt().setStart(new DateTimeDt("2001-01-02T11:11:00")));
|
||||
enc.setType(EncounterTypeEnum.ANNUAL_DIABETES_MELLITUS_SCREENING);
|
||||
|
||||
String title = gen.generateTitle(enc);
|
||||
title = title.replaceAll("00 [A-Z]+ 2001", "00 TZ 2001"); // account for whatever time zone
|
||||
assertEquals("1234567 / ADMS / ambulatory / Tue Jan 02 11:11:00 TZ 2001 - ?", title);
|
||||
ourLog.info(title);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -102,10 +111,6 @@ public class DefaultThymeleafNarrativeGeneratorTest {
|
|||
enc.setName("Some Test Org");
|
||||
enc.addAddress().addLine("123 Fake St").setCity("Toronto").setState("ON").setCountry("Canada").setZip("12345");
|
||||
|
||||
String title = gen.generateTitle(enc);
|
||||
assertEquals("Some Test Org", title);
|
||||
ourLog.info(title);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -113,7 +118,7 @@ public class DefaultThymeleafNarrativeGeneratorTest {
|
|||
Conformance value = myCtx.newXmlParser().parseResource(Conformance.class, new InputStreamReader(getClass().getResourceAsStream("/server-conformance-statement.xml")));
|
||||
|
||||
NarrativeDt narrative = new NarrativeDt();
|
||||
gen.generateNarrative(value, narrative);
|
||||
gen.generateNarrative(myCtx, value, narrative);
|
||||
String output =narrative.getDiv().getValueAsString();
|
||||
|
||||
ourLog.info(output);
|
||||
|
@ -129,7 +134,7 @@ public class DefaultThymeleafNarrativeGeneratorTest {
|
|||
value.addResult().setReference("Observation/3");
|
||||
|
||||
NarrativeDt narrative = new NarrativeDt();
|
||||
gen.generateNarrative("http://hl7.org/fhir/profiles/DiagnosticReport", value, narrative);
|
||||
gen.generateNarrative(myCtx, value, narrative);
|
||||
String output = narrative.getDiv().getValueAsString();
|
||||
|
||||
ourLog.info(output);
|
||||
|
@ -158,7 +163,7 @@ public class DefaultThymeleafNarrativeGeneratorTest {
|
|||
// assertEquals("Operation Outcome (2 issues)", output);
|
||||
|
||||
NarrativeDt narrative = new NarrativeDt();
|
||||
gen.generateNarrative(null, oo, narrative);
|
||||
gen.generateNarrative(myCtx, oo, narrative);
|
||||
String nar = narrative.getDiv().getValueAsString();
|
||||
ourLog.info(nar);
|
||||
|
||||
|
@ -201,16 +206,12 @@ public class DefaultThymeleafNarrativeGeneratorTest {
|
|||
}
|
||||
|
||||
NarrativeDt narrative = new NarrativeDt();
|
||||
gen.generateNarrative("http://hl7.org/fhir/profiles/DiagnosticReport", value, narrative);
|
||||
gen.generateNarrative(myCtx, value, narrative);
|
||||
String output = narrative.getDiv().getValueAsString();
|
||||
|
||||
ourLog.info(output);
|
||||
assertThat(output, StringContains.containsString("<div class=\"hapiHeaderText\"> Some & Diagnostic Report </div>"));
|
||||
|
||||
String title = gen.generateTitle(value);
|
||||
// ourLog.info(title);
|
||||
assertEquals("Some & Diagnostic Report - final - 3 observations", title);
|
||||
|
||||
// Now try it with the parser
|
||||
|
||||
output = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(value);
|
||||
|
@ -231,13 +232,10 @@ public class DefaultThymeleafNarrativeGeneratorTest {
|
|||
mp.setDateWritten(new DateTimeDt("2014-09-01"));
|
||||
|
||||
NarrativeDt narrative = new NarrativeDt();
|
||||
gen.generateNarrative(mp, narrative);
|
||||
gen.generateNarrative(myCtx, mp, narrative);
|
||||
assertTrue("Expected medication name of ciprofloaxin within narrative: " + narrative.getDiv().toString(), narrative.getDiv().toString().indexOf("ciprofloaxin") > -1);
|
||||
assertTrue("Expected string status of ACTIVE within narrative: " + narrative.getDiv().toString(), narrative.getDiv().toString().indexOf("ACTIVE") > -1);
|
||||
|
||||
String title = gen.generateTitle(mp);
|
||||
assertEquals("ciprofloaxin", title);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ import org.hamcrest.core.IsNot;
|
|||
import org.hamcrest.core.StringContains;
|
||||
import org.hamcrest.text.StringContainsInOrder;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.INarrative;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
@ -940,30 +941,15 @@ public class JsonParserTest {
|
|||
INarrativeGenerator gen = new INarrativeGenerator() {
|
||||
|
||||
@Override
|
||||
public void generateNarrative(IBaseResource theResource, BaseNarrativeDt<?> theNarrative) {
|
||||
throw new UnsupportedOperationException();
|
||||
public void generateNarrative(FhirContext theContext, IBaseResource theResource, INarrative theNarrative) {
|
||||
try {
|
||||
theNarrative.setDivAsString("<div>help</div>");
|
||||
} catch (Exception e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
theNarrative.setStatusAsString("generated");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateNarrative(String theProfile, IBaseResource theResource, BaseNarrativeDt<?> theNarrative) throws DataFormatException {
|
||||
theNarrative.getDiv().setValueAsString("<div>help</div>");
|
||||
theNarrative.getStatus().setValueAsString("generated");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateTitle(IBaseResource theResource) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateTitle(String theProfile, IBaseResource theResource) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFhirContext(FhirContext theFhirContext) {
|
||||
// nothing
|
||||
}
|
||||
};
|
||||
|
||||
FhirContext context = ourCtx;
|
||||
|
|
|
@ -21,6 +21,7 @@ import org.hamcrest.core.IsNot;
|
|||
import org.hamcrest.core.StringContains;
|
||||
import org.hamcrest.text.StringContainsInOrder;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.INarrative;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.xml.sax.SAXException;
|
||||
|
@ -1246,30 +1247,15 @@ public class XmlParserTest {
|
|||
INarrativeGenerator gen = new INarrativeGenerator() {
|
||||
|
||||
@Override
|
||||
public void generateNarrative(IBaseResource theResource, BaseNarrativeDt<?> theNarrative) {
|
||||
throw new UnsupportedOperationException();
|
||||
public void generateNarrative(FhirContext theContext, IBaseResource theResource, INarrative theNarrative) {
|
||||
try {
|
||||
theNarrative.setDivAsString("<div>help</div>");
|
||||
} catch (Exception e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
theNarrative.setStatusAsString("generated");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateNarrative(String theProfile, IBaseResource theResource, BaseNarrativeDt<?> theNarrative) throws DataFormatException {
|
||||
theNarrative.getDiv().setValueAsString("<div>help</div>");
|
||||
theNarrative.getStatus().setValueAsString("generated");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateTitle(IBaseResource theResource) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateTitle(String theProfile, IBaseResource theResource) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFhirContext(FhirContext theFhirContext) {
|
||||
// nothing
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
|
|
|
@ -298,7 +298,6 @@ public class SearchSearchServerDstu1Test {
|
|||
|
||||
Patient p = bundle.getResources(Patient.class).get(0);
|
||||
assertEquals("idaaa", p.getNameFirstRep().getFamilyAsSingleString());
|
||||
assertEquals("IDAAA (identifier123)", bundle.getEntries().get(0).getTitle().getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -310,7 +309,6 @@ public class SearchSearchServerDstu1Test {
|
|||
|
||||
Patient p = bundle.getResources(Patient.class).get(0);
|
||||
assertEquals("idaaa", p.getNameFirstRep().getFamilyAsSingleString());
|
||||
assertEquals("IDAAA (identifier123)", bundle.getEntries().get(0).getTitle().getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -364,7 +362,6 @@ public class SearchSearchServerDstu1Test {
|
|||
|
||||
Patient p = bundle.getResources(Patient.class).get(0);
|
||||
assertEquals("idaaa", p.getNameFirstRep().getFamilyAsSingleString());
|
||||
assertEquals("IDAAA (identifier123)", bundle.getEntries().get(0).getTitle().getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -129,6 +129,7 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
|
|||
Bundle bundle = theContext.newXmlParser().parseResource(Bundle.class, reader);
|
||||
for (BundleEntryComponent next : bundle.getEntry()) {
|
||||
ValueSet nextValueSet = (ValueSet) next.getResource();
|
||||
nextValueSet.getText().setDivAsString("");
|
||||
String system = nextValueSet.getCodeSystem().getSystem();
|
||||
if (isNotBlank(system)) {
|
||||
theCodeSystems.put(system, nextValueSet);
|
||||
|
|
|
@ -36,4 +36,17 @@ public abstract class BaseNarrative extends Type implements INarrative {
|
|||
|
||||
protected abstract XhtmlNode getDiv();
|
||||
|
||||
public abstract Enumeration<?> getStatusElement();
|
||||
|
||||
@Override
|
||||
public INarrative setStatusAsString(String theString) {
|
||||
getStatusElement().setValueAsString(theString);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStatusAsString() {
|
||||
return getStatusElement().getValueAsString();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1527,7 +1527,14 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
|
||||
List<WrapperElement> answers = new ArrayList<InstanceValidator.WrapperElement>();
|
||||
element.getNamedChildren("answer", answers);
|
||||
rule(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), (answers.size() > 0) || !qItem.getRequired(), "No response answer found for required item "+qItem.getLinkId());
|
||||
switch (qItem.getType()) {
|
||||
case NULL:
|
||||
case GROUP:
|
||||
case DISPLAY:
|
||||
break;
|
||||
default:
|
||||
rule(errors, IssueType.REQUIRED, element.line(), element.col(), stack.getLiteralPath(), (answers.size() > 0) || !qItem.getRequired(), "No response answer found for required item "+qItem.getLinkId());
|
||||
}
|
||||
if (answers.size() > 1)
|
||||
rule(errors, IssueType.INVALID, answers.get(1).line(), answers.get(1).col(), stack.getLiteralPath(), qItem.getRepeats(), "Only one response answer item with this linkId allowed");
|
||||
|
||||
|
@ -1564,7 +1571,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
validateQuestionnaireResponseItemType(errors, answer, ns, "string");
|
||||
break;
|
||||
case TEXT:
|
||||
validateQuestionnaireResponseItemType(errors, answer, ns, "text");
|
||||
validateQuestionnaireResponseItemType(errors, answer, ns, "string");
|
||||
break;
|
||||
case URL:
|
||||
validateQuestionnaireResponseItemType(errors, answer, ns, "uri");
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -38,6 +38,7 @@ import org.hl7.fhir.dstu21.model.Medication;
|
|||
import org.hl7.fhir.dstu21.model.MedicationOrder;
|
||||
import org.hl7.fhir.dstu21.model.Observation;
|
||||
import org.hl7.fhir.dstu21.model.Observation.ObservationStatus;
|
||||
import org.hl7.fhir.dstu21.model.Parameters;
|
||||
import org.hl7.fhir.dstu21.model.Patient;
|
||||
import org.hl7.fhir.dstu21.model.Quantity;
|
||||
import org.hl7.fhir.dstu21.model.QuestionnaireResponse;
|
||||
|
@ -45,11 +46,14 @@ import org.hl7.fhir.dstu21.model.Reference;
|
|||
import org.hl7.fhir.dstu21.model.StringType;
|
||||
import org.hl7.fhir.dstu21.model.UriType;
|
||||
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import net.sf.json.JSON;
|
||||
import net.sf.json.JSONSerializer;
|
||||
|
@ -59,6 +63,39 @@ public class JsonParserDstu21Test {
|
|||
private static final FhirContext ourCtx = FhirContext.forDstu2_1();
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(JsonParserDstu21Test.class);
|
||||
|
||||
@Test
|
||||
public void testEncodeParametersWithId() {
|
||||
Parameters reqParms = new Parameters();
|
||||
IdType patient = new IdType(1);
|
||||
reqParms.addParameter().setName("patient").setValue(patient);
|
||||
|
||||
String enc = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(reqParms);
|
||||
ourLog.info(enc);
|
||||
|
||||
assertThat(enc, containsString("\"valueId\":\"1\""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeWithNarrative() {
|
||||
Patient p = new Patient();
|
||||
p.addName().addFamily("Smith").addGiven("John");
|
||||
|
||||
ourCtx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator());
|
||||
|
||||
String output = ourCtx.newJsonParser().encodeResourceToString(p);
|
||||
ourLog.info(output);
|
||||
|
||||
assertThat(output, containsString("\"text\":{\"status\":\"generated\",\"div\":\"<div><div class=\\\"hapiHeaderText\\\"> John <b>SMITH </b></div>"));
|
||||
}
|
||||
|
||||
@After
|
||||
public void after() {
|
||||
ourCtx.setNarrativeGenerator(null);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// FIXME: this should pass
|
||||
@Test
|
||||
@Ignore
|
||||
|
|
|
@ -32,7 +32,6 @@ import org.hamcrest.text.StringContainsInOrder;
|
|||
import org.hl7.fhir.dstu21.model.Address.AddressUse;
|
||||
import org.hl7.fhir.dstu21.model.AllergyIntolerance;
|
||||
import org.hl7.fhir.dstu21.model.Annotation;
|
||||
import org.hl7.fhir.dstu21.model.Attachment;
|
||||
import org.hl7.fhir.dstu21.model.Binary;
|
||||
import org.hl7.fhir.dstu21.model.Bundle;
|
||||
import org.hl7.fhir.dstu21.model.Bundle.BundleEntryComponent;
|
||||
|
@ -77,13 +76,14 @@ import org.hl7.fhir.dstu21.model.StringType;
|
|||
import org.hl7.fhir.dstu21.model.UriType;
|
||||
import org.hl7.fhir.dstu21.model.ValueSet;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.junit.After;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.annotation.Child;
|
||||
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
||||
import ca.uhn.fhir.parser.IParserErrorHandler.IParseLocation;
|
||||
import ca.uhn.fhir.rest.client.IGenericClient;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
|
@ -124,6 +124,24 @@ public class XmlParserDstu21Test {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeWithNarrative() {
|
||||
Patient p = new Patient();
|
||||
p.addName().addFamily("Smith").addGiven("John");
|
||||
|
||||
ourCtx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator());
|
||||
|
||||
String output = ourCtx.newXmlParser().encodeResourceToString(p);
|
||||
ourLog.info(output);
|
||||
|
||||
assertThat(output, containsString("<text><status value=\"generated\"/><div xmlns=\"http://www.w3.org/1999/xhtml\"><div class=\"hapiHeaderText\"> John <b>SMITH </b>"));
|
||||
}
|
||||
|
||||
@After
|
||||
public void after() {
|
||||
ourCtx.setNarrativeGenerator(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeEmptyBinary() {
|
||||
String output = ourCtx.newXmlParser().encodeResourceToString(new Binary());
|
||||
|
@ -541,7 +559,7 @@ public class XmlParserDstu21Test {
|
|||
mo.addDosageInstruction().getTiming().getRepeat().setBounds(new Duration().setCode("code"));
|
||||
String out = ourCtx.newXmlParser().encodeResourceToString(mo);
|
||||
ourLog.info(out);
|
||||
assertThat(out, containsString("</boundsDuration>"));
|
||||
assertThat(out, containsString("</boundsQuantity>"));
|
||||
|
||||
mo = ourCtx.newXmlParser().parseResource(MedicationOrder.class, out);
|
||||
Duration duration = (Duration) mo.getDosageInstruction().get(0).getTiming().getRepeat().getBounds();
|
||||
|
@ -551,17 +569,15 @@ public class XmlParserDstu21Test {
|
|||
/**
|
||||
* See #216 - Profiled datatypes should use their unprofiled parent type as the choice[x] name
|
||||
*/
|
||||
@Test @Ignore
|
||||
@Test
|
||||
public void testEncodeAndParseProfiledDatatypeChoice() throws Exception {
|
||||
IParser xmlParser = ourCtx.newXmlParser();
|
||||
|
||||
String input = IOUtils.toString(XmlParser.class.getResourceAsStream("/medicationstatement_invalidelement.xml"));
|
||||
MedicationStatement ms = xmlParser.parseResource(MedicationStatement.class, input);
|
||||
SimpleQuantity q = (SimpleQuantity) ms.getDosage().get(0).getQuantity();
|
||||
assertEquals("1", q.getValueElement().getValueAsString());
|
||||
MedicationStatement ms = new MedicationStatement();
|
||||
ms.addDosage().setQuantity(new SimpleQuantity().setValue(123));
|
||||
|
||||
String output = xmlParser.encodeResourceToString(ms);
|
||||
assertThat(output, containsString("<quantityQuantity><value value=\"1\"/></quantityQuantity>"));
|
||||
assertThat(output, containsString("<quantityQuantity><value value=\"123\"/></quantityQuantity>"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1588,7 +1604,7 @@ public class XmlParserDstu21Test {
|
|||
/**
|
||||
* See #191
|
||||
*/
|
||||
@Test @Ignore
|
||||
@Test
|
||||
public void testParseBundleWithLinksOfUnknownRelation() throws Exception {
|
||||
String input = IOUtils.toString(XmlParserDstu21Test.class.getResourceAsStream("/bundle_orion.xml"));
|
||||
Bundle parsed = ourCtx.newXmlParser().parseResource(Bundle.class, input);
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
package ca.uhn.fhir.rest.server;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.endsWith;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
|
@ -19,6 +21,7 @@ import org.eclipse.jetty.servlet.ServletHandler;
|
|||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.hl7.fhir.dstu21.hapi.validation.FhirInstanceValidator;
|
||||
import org.hl7.fhir.dstu21.model.Enumerations.AdministrativeGender;
|
||||
import org.hl7.fhir.dstu21.model.Identifier.IdentifierUse;
|
||||
import org.hl7.fhir.dstu21.model.Patient;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.junit.AfterClass;
|
||||
|
@ -56,10 +59,10 @@ public class ResponseValidatingInterceptorDstu21Test {
|
|||
}
|
||||
|
||||
myInterceptor = new ResponseValidatingInterceptor();
|
||||
// myInterceptor.setFailOnSeverity(ResultSeverityEnum.ERROR);
|
||||
// myInterceptor.setAddResponseHeaderOnSeverity(ResultSeverityEnum.INFORMATION);
|
||||
// myInterceptor.setResponseHeaderName("X-RESP");
|
||||
// myInterceptor.setResponseHeaderValue(RequestValidatingInterceptor.DEFAULT_RESPONSE_HEADER_VALUE);
|
||||
// myInterceptor.setFailOnSeverity(ResultSeverityEnum.ERROR);
|
||||
// myInterceptor.setAddResponseHeaderOnSeverity(ResultSeverityEnum.INFORMATION);
|
||||
// myInterceptor.setResponseHeaderName("X-RESP");
|
||||
// myInterceptor.setResponseHeaderValue(RequestValidatingInterceptor.DEFAULT_RESPONSE_HEADER_VALUE);
|
||||
|
||||
ourServlet.registerInterceptor(myInterceptor);
|
||||
}
|
||||
|
@ -91,7 +94,6 @@ public class ResponseValidatingInterceptorDstu21Test {
|
|||
assertThat(responseContent, containsString("<severity value=\"error\"/>"));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSearchJsonValidNoValidatorsSpecified() throws Exception {
|
||||
Patient patient = new Patient();
|
||||
|
@ -113,7 +115,6 @@ public class ResponseValidatingInterceptorDstu21Test {
|
|||
assertThat(status.toString(), not(containsString("X-FHIR-Response-Validation")));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSearchJsonValidNoValidatorsSpecifiedDefaultMessage() throws Exception {
|
||||
myInterceptor.setResponseHeaderValueNoIssues("NO ISSUES");
|
||||
|
@ -270,9 +271,54 @@ public class ResponseValidatingInterceptorDstu21Test {
|
|||
ourLog.trace("Response was:\n{}", responseContent);
|
||||
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
assertThat(status.toString(), (containsString("X-FHIR-Response-Validation: {\"resourceType\":\"OperationOutcome\",\"issue\":[{\"severity\":\"information\",\"code\":\"informational\",\"diagnostics\":\"No issues detected\"}]}")));
|
||||
assertThat(status.toString(), (containsString(
|
||||
"X-FHIR-Response-Validation: {\"resourceType\":\"OperationOutcome\",\"issue\":[{\"severity\":\"information\",\"code\":\"informational\",\"diagnostics\":\"No issues detected\"}]}")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLongHeaderTruncated() throws Exception {
|
||||
IValidatorModule module = new FhirInstanceValidator();
|
||||
myInterceptor.addValidatorModule(module);
|
||||
myInterceptor.setAddResponseOutcomeHeaderOnSeverity(ResultSeverityEnum.INFORMATION);
|
||||
myInterceptor.setFailOnSeverity(null);
|
||||
|
||||
Patient patient = new Patient();
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
patient.addContact().setGender(AdministrativeGender.MALE);
|
||||
}
|
||||
patient.setGender(AdministrativeGender.MALE);
|
||||
myReturnResource = patient;
|
||||
|
||||
HttpGet httpPost = new HttpGet("http://localhost:" + ourPort + "/Patient?foo=bar");
|
||||
|
||||
{
|
||||
HttpResponse status = ourClient.execute(httpPost);
|
||||
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
|
||||
ourLog.info("Response was:\n{}", status);
|
||||
ourLog.trace("Response was:\n{}", responseContent);
|
||||
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
assertThat(status.getFirstHeader("X-FHIR-Response-Validation").getValue(), endsWith("..."));
|
||||
assertThat(status.getFirstHeader("X-FHIR-Response-Validation").getValue(), startsWith("{\"resourceType\":\"OperationOutcome\""));
|
||||
}
|
||||
{
|
||||
myInterceptor.setMaximumHeaderLength(100);
|
||||
HttpResponse status = ourClient.execute(httpPost);
|
||||
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
|
||||
ourLog.info("Response was:\n{}", status);
|
||||
ourLog.trace("Response was:\n{}", responseContent);
|
||||
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
assertThat(status.getFirstHeader("X-FHIR-Response-Validation").getValue(), endsWith("..."));
|
||||
assertThat(status.getFirstHeader("X-FHIR-Response-Validation").getValue(), startsWith("{\"resourceType\":\"OperationOutcome\""));
|
||||
}
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterClass() throws Exception {
|
||||
|
@ -309,7 +355,7 @@ public class ResponseValidatingInterceptorDstu21Test {
|
|||
}
|
||||
|
||||
@Search
|
||||
public ArrayList<IBaseResource> search(@OptionalParam(name="foo") StringParam theString) {
|
||||
public ArrayList<IBaseResource> search(@OptionalParam(name = "foo") StringParam theString) {
|
||||
ArrayList<IBaseResource> retVal = new ArrayList<IBaseResource>();
|
||||
myReturnResource.setId("1");
|
||||
retVal.add(myReturnResource);
|
||||
|
|
|
@ -37,6 +37,7 @@ import org.hl7.fhir.dstu21.model.ValueSet.ValueSetExpansionComponent;
|
|||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TestRule;
|
||||
|
@ -170,7 +171,7 @@ public class FhirInstanceValidatorDstu21Test {
|
|||
|
||||
int index = 0;
|
||||
for (SingleValidationMessage next : theOutput.getMessages()) {
|
||||
ourLog.info("Result {}: {} - {} - {}", new Object[] { index, next.getSeverity(), next.getLocationString(), next.getMessage() });
|
||||
ourLog.info("Result {}: {} - {}:{} {} - {}", new Object[] { index, next.getSeverity(), defaultString(next.getLocationLine()), defaultString(next.getLocationCol()), next.getLocationString(), next.getMessage() });
|
||||
index++;
|
||||
|
||||
retVal.add(next);
|
||||
|
@ -179,6 +180,10 @@ public class FhirInstanceValidatorDstu21Test {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
private Object defaultString(Integer theLocationLine) {
|
||||
return theLocationLine != null ? theLocationLine.toString() : "";
|
||||
}
|
||||
|
||||
@Rule
|
||||
public TestRule watcher = new TestWatcher() {
|
||||
protected void starting(Description description) {
|
||||
|
@ -262,7 +267,22 @@ public class FhirInstanceValidatorDstu21Test {
|
|||
String input = IOUtils.toString(FhirInstanceValidatorDstu21Test.class.getResourceAsStream("/qr_jon.xml"));
|
||||
|
||||
ValidationResult output = myVal.validateWithResult(input);
|
||||
assertEquals(output.toString(), 12, output.getMessages().size());
|
||||
logResultsAndReturnAll(output);
|
||||
|
||||
assertEquals(output.toString(), 3, output.getMessages().size());
|
||||
ourLog.info(output.getMessages().get(0).getLocationString());
|
||||
ourLog.info(output.getMessages().get(0).getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void testValidateStructureDefinition() throws IOException {
|
||||
String input = IOUtils.toString(FhirInstanceValidatorDstu21Test.class.getResourceAsStream("/sdc-questionnaire.profile.xml"));
|
||||
|
||||
ValidationResult output = myVal.validateWithResult(input);
|
||||
logResultsAndReturnAll(output);
|
||||
|
||||
assertEquals(output.toString(), 3, output.getMessages().size());
|
||||
ourLog.info(output.getMessages().get(0).getLocationString());
|
||||
ourLog.info(output.getMessages().get(0).getMessage());
|
||||
}
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
<Bundle
|
||||
xmlns="http://hl7.org/fhir">
|
||||
<id value="487d3669-0e1c-4867-b124-400d1849548d"></id>
|
||||
<type value="searchset"></type>
|
||||
<base value="http://localhost:19080/fhir/dstu1"></base>
|
||||
<link>
|
||||
<relation value="just trying add link"></relation>
|
||||
<url value="blarion"></url>
|
||||
</link>
|
||||
<link>
|
||||
<relation value="self"></relation>
|
||||
<url value="http://localhost:19080/fhir/dstu1/Observation?subject.identifier=puppet|CLONE-AA102"></url>
|
||||
</link>
|
||||
<entry>
|
||||
<link>
|
||||
<relation value="orionhealth.edit"></relation>
|
||||
<url value="Observation"></url>
|
||||
</link>
|
||||
<resource>
|
||||
<Observation
|
||||
xmlns="http://hl7.org/fhir">
|
||||
<id value="0d87f02c-da2c-4551-9ead-2956f0165a4f"></id>
|
||||
<extension url="http://orionhealth.com/fhir/extensions#created-by"></extension>
|
||||
<extension url="http://orionhealth.com/fhir/extensions#last-modified-by"></extension>
|
||||
<extension url="http://orionhealth.com/fhir/extensions#received-instant">
|
||||
<valueInstant value="2015-06-25T17:08:47.190+12:00"></valueInstant>
|
||||
</extension>
|
||||
<code>
|
||||
<coding>
|
||||
<system value="http://loinc.org"></system>
|
||||
<code value="8867-4"></code>
|
||||
</coding>
|
||||
</code>
|
||||
<valueString value="observationValue"></valueString>
|
||||
<status value="final"></status>
|
||||
<reliability value="ok"></reliability>
|
||||
<subject>
|
||||
<reference value="Patient/INGE6TSFFVAUCMJQGJAHA5LQOBSXI"></reference>
|
||||
</subject>
|
||||
</Observation>
|
||||
</resource>
|
||||
</entry>
|
||||
<entry>
|
||||
<link>
|
||||
<relation value="orionhealth.edit"></relation>
|
||||
<url value="Observation"></url>
|
||||
</link>
|
||||
<resource>
|
||||
<Observation
|
||||
xmlns="http://hl7.org/fhir">
|
||||
<id value="c54ac0cc-a99f-40aa-9541-c5aa853a2e88"></id>
|
||||
<extension url="http://orionhealth.com/fhir/extensions#created-by"></extension>
|
||||
<extension url="http://orionhealth.com/fhir/extensions#last-modified-by"></extension>
|
||||
<extension url="http://orionhealth.com/fhir/extensions#received-instant">
|
||||
<valueInstant value="2015-06-25T17:08:47.190+12:00"></valueInstant>
|
||||
</extension>
|
||||
<code>
|
||||
<coding>
|
||||
<system value="http://loinc.org"></system>
|
||||
<code value="3141-9"></code>
|
||||
</coding>
|
||||
</code>
|
||||
<valueString value="observationValue"></valueString>
|
||||
<status value="final"></status>
|
||||
<reliability value="ok"></reliability>
|
||||
<subject>
|
||||
<reference value="Patient/INGE6TSFFVAUCMJQGJAHA5LQOBSXI"></reference>
|
||||
</subject>
|
||||
</Observation>
|
||||
</resource>
|
||||
</entry>
|
||||
</Bundle>
|
File diff suppressed because it is too large
Load Diff
|
@ -29,7 +29,6 @@ import java.util.List;
|
|||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
|
@ -94,16 +93,6 @@ public class Dstu2BundleFactory implements IVersionSpecificBundleFactory {
|
|||
}
|
||||
}
|
||||
|
||||
if (myContext.getNarrativeGenerator() != null) {
|
||||
String title = myContext.getNarrativeGenerator().generateTitle(next);
|
||||
ourLog.trace("Narrative generator created title: {}", title);
|
||||
if (StringUtils.isNotBlank(title)) {
|
||||
ResourceMetadataKeyEnum.TITLE.put(next, title);
|
||||
}
|
||||
} else {
|
||||
ourLog.trace("No narrative generator specified");
|
||||
}
|
||||
|
||||
List<BaseResourceReferenceDt> references = myContext.newTerser().getAllPopulatedChildElementsOfType(next, BaseResourceReferenceDt.class);
|
||||
do {
|
||||
List<IResource> addedResourcesThisPass = new ArrayList<IResource>();
|
||||
|
@ -190,16 +179,6 @@ public class Dstu2BundleFactory implements IVersionSpecificBundleFactory {
|
|||
}
|
||||
}
|
||||
|
||||
if (myContext.getNarrativeGenerator() != null) {
|
||||
String title = myContext.getNarrativeGenerator().generateTitle(next);
|
||||
ourLog.trace("Narrative generator created title: {}", title);
|
||||
if (StringUtils.isNotBlank(title)) {
|
||||
ResourceMetadataKeyEnum.TITLE.put(next, title);
|
||||
}
|
||||
} else {
|
||||
ourLog.trace("No narrative generator specified");
|
||||
}
|
||||
|
||||
List<ResourceReferenceInfo> references = myContext.newTerser().getAllResourceReferences(next);
|
||||
do {
|
||||
List<IResource> addedResourcesThisPass = new ArrayList<IResource>();
|
||||
|
|
|
@ -29,7 +29,7 @@ public class CustomThymeleafNarrativeGeneratorDstu2Test {
|
|||
p.getName().addFamily("fam1").addGiven("given");
|
||||
|
||||
NarrativeDt narrative = new NarrativeDt();
|
||||
gen.generateNarrative(p, narrative);
|
||||
gen.generateNarrative(ourCtx, p, narrative);
|
||||
|
||||
String actual = narrative.getDiv().getValueAsString();
|
||||
ourLog.info(actual);
|
||||
|
|
|
@ -62,23 +62,15 @@ public class DefaultThymeleafNarrativeGeneratorTestDstu2 {
|
|||
value.setBirthDate(new Date(), TemporalPrecisionEnum.DAY);
|
||||
|
||||
NarrativeDt narrative = new NarrativeDt();
|
||||
myGen.generateNarrative(value, narrative);
|
||||
myGen.generateNarrative(ourCtx, value, narrative);
|
||||
String output = narrative.getDiv().getValueAsString();
|
||||
ourLog.info(output);
|
||||
assertThat(output, StringContains.containsString("<div class=\"hapiHeaderText\"> joe john <b>BLOW </b></div>"));
|
||||
|
||||
String title = myGen.generateTitle(value);
|
||||
assertEquals("joe john BLOW (123456)", title);
|
||||
// ourLog.info(title);
|
||||
|
||||
value.getIdentifierFirstRep().setValue("FOO MRN 123");
|
||||
title = myGen.generateTitle(value);
|
||||
assertEquals("joe john BLOW (FOO MRN 123)", title);
|
||||
// ourLog.info(title);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void testGenerateEncounter() throws DataFormatException {
|
||||
Encounter enc = new Encounter();
|
||||
|
||||
|
@ -87,30 +79,13 @@ public class DefaultThymeleafNarrativeGeneratorTestDstu2 {
|
|||
enc.setPeriod(new PeriodDt().setStart(new DateTimeDt("2001-01-02T11:11:00")));
|
||||
enc.setType(ca.uhn.fhir.model.dstu2.valueset.EncounterTypeEnum.ANNUAL_DIABETES_MELLITUS_SCREENING);
|
||||
|
||||
String title = myGen.generateTitle(enc);
|
||||
title = title.replaceAll("00 [A-Z0-9:+-]+ 2001", "00 TZ 2001"); // account for whatever time zone
|
||||
assertEquals("1234567 / ADMS / ambulatory / Tue Jan 02 11:11:00 TZ 2001 - ?", title);
|
||||
ourLog.info(title);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenerateDiagnosticReport() throws DataFormatException {
|
||||
DiagnosticReport value = new DiagnosticReport();
|
||||
value.getCode().setText("Some Diagnostic Report");
|
||||
|
||||
value.addResult().setReference("Observation/1");
|
||||
value.addResult().setReference("Observation/2");
|
||||
value.addResult().setReference("Observation/3");
|
||||
|
||||
NarrativeDt narrative = new NarrativeDt();
|
||||
myGen.generateNarrative("http://hl7.org/fhir/profiles/DiagnosticReport", value, narrative);
|
||||
String output = narrative.getDiv().getValueAsString();
|
||||
myGen.generateNarrative(ourCtx, enc, narrative);
|
||||
|
||||
ourLog.info(output);
|
||||
assertThat(output, StringContains.containsString(value.getCode().getTextElement().getValue()));
|
||||
assertEquals("", narrative.getDivAsString());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testGenerateOperationOutcome() {
|
||||
//@formatter:off
|
||||
|
@ -133,17 +108,12 @@ public class DefaultThymeleafNarrativeGeneratorTestDstu2 {
|
|||
// assertEquals("Operation Outcome (2 issues)", output);
|
||||
|
||||
NarrativeDt narrative = new NarrativeDt();
|
||||
myGen.generateNarrative(null, oo, narrative);
|
||||
myGen.generateNarrative(ourCtx, oo, narrative);
|
||||
String output = narrative.getDiv().getValueAsString();
|
||||
|
||||
ourLog.info(output);
|
||||
|
||||
// oo = new OperationOutcome();
|
||||
// oo.addIssue().setSeverity(IssueSeverityEnum.FATAL).setDetails("AA");
|
||||
// output = gen.generateTitle(oo);
|
||||
// ourLog.info(output);
|
||||
// assertEquals("Operation Outcome (fatal)", output);
|
||||
|
||||
assertThat(output, containsString("<td><pre>YThis is a warning</pre></td>"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -177,22 +147,17 @@ public class DefaultThymeleafNarrativeGeneratorTestDstu2 {
|
|||
}
|
||||
|
||||
NarrativeDt narrative = new NarrativeDt();
|
||||
myGen.generateNarrative("http://hl7.org/fhir/profiles/DiagnosticReport", value, narrative);
|
||||
myGen.generateNarrative(ourCtx, value, narrative);
|
||||
String output = narrative.getDiv().getValueAsString();
|
||||
|
||||
ourLog.info(output);
|
||||
assertThat(output, StringContains.containsString("<div class=\"hapiHeaderText\"> Some & Diagnostic Report </div>"));
|
||||
|
||||
String title = myGen.generateTitle(value);
|
||||
// ourLog.info(title);
|
||||
assertEquals("Some & Diagnostic Report - final - 3 observations", title);
|
||||
|
||||
// Now try it with the parser
|
||||
|
||||
output = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(value);
|
||||
ourLog.info(output);
|
||||
assertThat(output, StringContains.containsString("<div class=\"hapiHeaderText\"> Some & Diagnostic Report </div>"));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -208,14 +173,11 @@ public class DefaultThymeleafNarrativeGeneratorTestDstu2 {
|
|||
mp.setDateWritten(new DateTimeDt("2014-09-01"));
|
||||
|
||||
NarrativeDt narrative = new NarrativeDt();
|
||||
myGen.generateNarrative(mp, narrative);
|
||||
myGen.generateNarrative(ourCtx, mp, narrative);
|
||||
|
||||
assertTrue("Expected medication name of ciprofloaxin within narrative: " + narrative.getDiv().toString(), narrative.getDiv().toString().indexOf("ciprofloaxin") > -1);
|
||||
assertTrue("Expected string status of ACTIVE within narrative: " + narrative.getDiv().toString(), narrative.getDiv().toString().indexOf("ACTIVE") > -1);
|
||||
|
||||
String title = myGen.generateTitle(mp);
|
||||
assertEquals("ciprofloaxin", title);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -224,14 +186,11 @@ public class DefaultThymeleafNarrativeGeneratorTestDstu2 {
|
|||
med.getCode().setText("ciproflaxin");
|
||||
|
||||
NarrativeDt narrative = new NarrativeDt();
|
||||
myGen.generateNarrative(med, narrative);
|
||||
myGen.generateNarrative(ourCtx, med, narrative);
|
||||
|
||||
String string = narrative.getDiv().toString();
|
||||
assertThat(string, containsString("ciproflaxin"));
|
||||
|
||||
String title = myGen.generateTitle(med);
|
||||
assertEquals("ciproflaxin", title);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -110,18 +110,17 @@ public class XmlParserDstu2Test {
|
|||
assertEquals(1, parsed.getUndeclaredExtensions().size());
|
||||
ExtensionDt ext = parsed.getUndeclaredExtensions().get(0);
|
||||
assertEquals("http://example.com", ext.getUrl());
|
||||
assertEquals("THIS IS MARKDOWN", ((MarkdownDt)ext.getValue()).getValue());
|
||||
assertEquals("THIS IS MARKDOWN", ((MarkdownDt) ext.getValue()).getValue());
|
||||
|
||||
String encoded = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(parsed);
|
||||
assertThat(encoded, containsString("<valueMarkdown value=\"THIS IS MARKDOWN\"/>"));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testChoiceTypeWithProfiledType2() {
|
||||
Parameters par = new Parameters();
|
||||
par.addParameter().setValue((StringDt)new StringDt().setValue("ST"));
|
||||
par.addParameter().setValue((MarkdownDt)new MarkdownDt().setValue("MD"));
|
||||
par.addParameter().setValue((StringDt) new StringDt().setValue("ST"));
|
||||
par.addParameter().setValue((MarkdownDt) new MarkdownDt().setValue("MD"));
|
||||
|
||||
String str = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(par);
|
||||
ourLog.info(str);
|
||||
|
@ -134,8 +133,6 @@ public class XmlParserDstu2Test {
|
|||
assertEquals(MarkdownDt.class, par.getParameter().get(1).getValue().getClass());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testBundleWithBinary() {
|
||||
//@formatter:off
|
||||
|
@ -184,7 +181,6 @@ public class XmlParserDstu2Test {
|
|||
ourCtx.newJsonParser().parseResource(encoded);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testEncodeDoesntIncludeUuidId() {
|
||||
Patient p = new Patient();
|
||||
|
@ -195,7 +191,6 @@ public class XmlParserDstu2Test {
|
|||
assertThat(actual, not(containsString("78ef6f64c2f2")));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testContainedResourceInExtensionUndeclared() {
|
||||
Patient p = new Patient();
|
||||
|
@ -340,7 +335,6 @@ public class XmlParserDstu2Test {
|
|||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testEncodeAndParseExtensions() throws Exception {
|
||||
|
||||
|
@ -566,7 +560,7 @@ public class XmlParserDstu2Test {
|
|||
}
|
||||
|
||||
/**
|
||||
* Test for #233 - This was reversed after a conversation with Grahame
|
||||
* Test for #233
|
||||
*/
|
||||
@Test
|
||||
public void testEncodeAndParseProfiledDatatype() {
|
||||
|
@ -574,7 +568,7 @@ public class XmlParserDstu2Test {
|
|||
mo.addDosageInstruction().getTiming().getRepeat().setBounds(new DurationDt().setCode("code"));
|
||||
String out = ourCtx.newXmlParser().encodeResourceToString(mo);
|
||||
ourLog.info(out);
|
||||
assertThat(out, containsString("</boundsDuration>"));
|
||||
assertThat(out, containsString("</boundsQuantity>"));
|
||||
|
||||
mo = ourCtx.newXmlParser().parseResource(MedicationOrder.class, out);
|
||||
DurationDt duration = (DurationDt) mo.getDosageInstruction().get(0).getTiming().getRepeat().getBounds();
|
||||
|
@ -587,7 +581,6 @@ public class XmlParserDstu2Test {
|
|||
* Disabled because we reverted this change after a conversation with Grahame
|
||||
*/
|
||||
@Test
|
||||
@Ignore
|
||||
public void testEncodeAndParseProfiledDatatypeChoice() throws Exception {
|
||||
IParser xmlParser = ourCtx.newXmlParser();
|
||||
|
||||
|
@ -597,7 +590,7 @@ public class XmlParserDstu2Test {
|
|||
assertEquals("1", q.getValueElement().getValueAsString());
|
||||
|
||||
String output = xmlParser.encodeResourceToString(ms);
|
||||
assertThat(output, containsString("<quantitySimpleQuantity><value value=\"1\"/></quantitySimpleQuantity>"));
|
||||
assertThat(output, containsString("<quantityQuantity><value value=\"1\"/></quantityQuantity>"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -54,7 +54,6 @@ import ca.uhn.fhir.rest.annotation.Read;
|
|||
import ca.uhn.fhir.rest.annotation.RequiredParam;
|
||||
import ca.uhn.fhir.rest.annotation.Search;
|
||||
import ca.uhn.fhir.rest.api.RequestTypeEnum;
|
||||
import ca.uhn.fhir.rest.method.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.BundleInclusionRule;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
|
@ -89,6 +88,22 @@ public class ResponseHighlightingInterceptorTest {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetInvalidResourceNoAcceptHeader() throws Exception {
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Foobar/123");
|
||||
CloseableHttpResponse status = ourClient.execute(httpGet);
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
|
||||
ourLog.info("Resp: {}", responseContent);
|
||||
assertEquals(400, status.getStatusLine().getStatusCode());
|
||||
|
||||
assertThat(responseContent, not(stringContainsInOrder("<span class='hlTagName'>OperationOutcome</span>", "Unknown resource type 'Foobar' - Server knows how to handle")));
|
||||
assertThat(responseContent, (stringContainsInOrder("Unknown resource type 'Foobar'")));
|
||||
assertThat(status.getFirstHeader("Content-Type").getValue(), containsString("application/xml+fhir"));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetRoot() throws Exception {
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/");
|
||||
|
|
|
@ -1,12 +1,24 @@
|
|||
package org.hl7.fhir.instance.model;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.hl7.fhir.instance.model.Narrative.NarrativeStatus;
|
||||
import org.hl7.fhir.instance.model.api.INarrative;
|
||||
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
|
||||
|
||||
public abstract class BaseNarrative extends Type implements INarrative {
|
||||
|
||||
/**
|
||||
@Override
|
||||
public INarrative setStatusAsString(String theString) {
|
||||
getStatusElement().setValueAsString(theString);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStatusAsString() {
|
||||
return getStatusElement().getValueAsString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of
|
||||
*
|
||||
* @param theString
|
||||
|
@ -36,4 +48,6 @@ public abstract class BaseNarrative extends Type implements INarrative {
|
|||
|
||||
protected abstract XhtmlNode getDiv();
|
||||
|
||||
public abstract Enumeration<?> getStatusElement();
|
||||
|
||||
}
|
||||
|
|
|
@ -53,6 +53,7 @@ import org.hl7.fhir.instance.model.ValueSet;
|
|||
import org.hl7.fhir.instance.model.ValueSet.ConceptDefinitionComponent;
|
||||
import org.hl7.fhir.instance.model.ValueSet.ValueSetCodeSystemComponent;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.INarrative;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
|
||||
import org.junit.After;
|
||||
|
@ -972,34 +973,19 @@ public class JsonParserHl7OrgTest {
|
|||
Organization org = new Organization();
|
||||
patient.getManagingOrganization().setResource(org);
|
||||
|
||||
INarrativeGenerator gen = new INarrativeGenerator() {
|
||||
INarrativeGenerator gen = new INarrativeGenerator() {
|
||||
|
||||
@Override
|
||||
public void generateNarrative(IBaseResource theResource, BaseNarrativeDt<?> theNarrative) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
@Override
|
||||
public void generateNarrative(FhirContext theContext, IBaseResource theResource, INarrative theNarrative) {
|
||||
try {
|
||||
theNarrative.setDivAsString("<div>help</div>");
|
||||
} catch (Exception e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
theNarrative.setStatusAsString("generated");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateNarrative(String theProfile, IBaseResource theResource, BaseNarrativeDt<?> theNarrative) throws DataFormatException {
|
||||
theNarrative.getDiv().setValueAsString("<div>help</div>");
|
||||
theNarrative.getStatus().setValueAsString("generated");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateTitle(IBaseResource theResource) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateTitle(String theProfile, IBaseResource theResource) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFhirContext(FhirContext theFhirContext) {
|
||||
// nothing
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
FhirContext context = ourCtx;
|
||||
context.setNarrativeGenerator(gen);
|
||||
|
|
|
@ -58,6 +58,7 @@ import org.hl7.fhir.instance.model.SimpleQuantity;
|
|||
import org.hl7.fhir.instance.model.Specimen;
|
||||
import org.hl7.fhir.instance.model.StringType;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.INarrative;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Ignore;
|
||||
|
@ -1382,34 +1383,19 @@ public class XmlParserHl7OrgDstu2Test {
|
|||
Organization org = new Organization();
|
||||
patient.getManagingOrganization().setResource(org);
|
||||
|
||||
INarrativeGenerator gen = new INarrativeGenerator() {
|
||||
INarrativeGenerator gen = new INarrativeGenerator() {
|
||||
|
||||
@Override
|
||||
public void generateNarrative(IBaseResource theResource, BaseNarrativeDt<?> theNarrative) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
@Override
|
||||
public void generateNarrative(FhirContext theContext, IBaseResource theResource, INarrative theNarrative) {
|
||||
try {
|
||||
theNarrative.setDivAsString("<div>help</div>");
|
||||
} catch (Exception e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
theNarrative.setStatusAsString("generated");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateNarrative(String theProfile, IBaseResource theResource, BaseNarrativeDt<?> theNarrative) throws DataFormatException {
|
||||
theNarrative.getDiv().setValueAsString("<div>help</div>");
|
||||
theNarrative.getStatus().setValueAsString("generated");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateTitle(IBaseResource theResource) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateTitle(String theProfile, IBaseResource theResource) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFhirContext(FhirContext theFhirContext) {
|
||||
// nothing
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
FhirContext context = ourCtx;
|
||||
context.setNarrativeGenerator(gen);
|
||||
|
|
|
@ -600,21 +600,6 @@ public class BaseController {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* DSTU2 no longer has a title in the bundle format, but it's still useful here..
|
||||
*/
|
||||
if (bundle != null) {
|
||||
INarrativeGenerator gen = context.getNarrativeGenerator();
|
||||
if (gen != null) {
|
||||
for (BundleEntry next : bundle.getEntries()) {
|
||||
if (next.getTitle().isEmpty() && next.getResource() != null) {
|
||||
String title = gen.generateTitle(next.getResource());
|
||||
next.getTitle().setValue(title);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resultDescription.append(" (").append(resultBody.length() + " bytes)");
|
||||
|
||||
Header[] requestHeaders = lastRequest != null ? applyHeaderFilters(lastRequest.getAllHeaders()) : new Header[0];
|
||||
|
|
8
pom.xml
8
pom.xml
|
@ -335,8 +335,8 @@
|
|||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.transaction</groupId>
|
||||
<artifactId>jta</artifactId>
|
||||
<version>1.1</version>
|
||||
<artifactId>javax.transaction-api</artifactId>
|
||||
<version>1.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.validation</groupId>
|
||||
|
@ -556,7 +556,7 @@
|
|||
<dependency>
|
||||
<groupId>org.hibernate</groupId>
|
||||
<artifactId>hibernate-search-orm</artifactId>
|
||||
<version>5.5.1.Final</version>
|
||||
<version>5.5.2.Final</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.javassist</groupId>
|
||||
|
@ -941,7 +941,7 @@
|
|||
<dependency>
|
||||
<groupId>com.puppycrawl.tools</groupId>
|
||||
<artifactId>checkstyle</artifactId>
|
||||
<version>6.13</version>
|
||||
<version>6.14.1</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<configuration>
|
||||
|
|
|
@ -122,6 +122,23 @@
|
|||
Add DSTU2.1 example to hpia-fhir-jpaserver-example. Thanks to Karl
|
||||
Davis for the Pull Request!
|
||||
</action>
|
||||
<action type="add">
|
||||
RestfulServer#setUseBrowserFriendlyContentTypes has been deprecated and its
|
||||
functionality removed. The intention of this feature was that if it
|
||||
detected a request coming in from a browser, it would serve up JSON/XML
|
||||
using content types that caused the browsers to pretty print. But
|
||||
each browser has different rules for when to pretty print, and
|
||||
after we wrote that feature both Chrome and FF changed their rules to break it anyhow.
|
||||
ResponseHighlightingInterceptor provides a better implementation of
|
||||
this functionality and should be used instead.
|
||||
</action>
|
||||
<action type="remove">
|
||||
Narrative generator framework has removed the
|
||||
ability to generate resource titles. This
|
||||
functionality was only useful for DSTU1
|
||||
implementations and wasn't compatible
|
||||
with coming changes to that API.
|
||||
</action>
|
||||
</release>
|
||||
<release version="1.3" date="2015-11-14">
|
||||
<action type="add">
|
||||
|
|
Loading…
Reference in New Issue