Merge branch 'master' of github.com:jamesagnew/hapi-fhir
Conflicts: hapi-fhir-jpaserver-uhnfhirtest/src/main/webapp/WEB-INF/hapi-fhir-server-database-config.xml hapi-fhir-jpaserver-uhnfhirtest/src/main/webapp/WEB-INF/templates/tmpl-home-welcome.html
This commit is contained in:
commit
ee53c40ff8
|
@ -60,9 +60,11 @@ public class RuntimeChildAny extends RuntimeChildChoiceDefinition {
|
||||||
if (o1res && o2res) {
|
if (o1res && o2res) {
|
||||||
return theO1.getSimpleName().compareTo(theO2.getSimpleName());
|
return theO1.getSimpleName().compareTo(theO2.getSimpleName());
|
||||||
} else if (o1res) {
|
} else if (o1res) {
|
||||||
return 1;
|
|
||||||
}else {
|
|
||||||
return -1;
|
return -1;
|
||||||
|
} else if (o1res == false && o2res == false) {
|
||||||
|
return 0;
|
||||||
|
}else {
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
}});
|
}});
|
||||||
|
|
||||||
|
|
|
@ -221,7 +221,12 @@ public class Bundle extends BaseBundle /* implements IElement */{
|
||||||
RuntimeResourceDefinition def = theContext.getResourceDefinition(theResource);
|
RuntimeResourceDefinition def = theContext.getResourceDefinition(theResource);
|
||||||
|
|
||||||
if (theResource.getId() != null && StringUtils.isNotBlank(theResource.getId().getValue())) {
|
if (theResource.getId() != null && StringUtils.isNotBlank(theResource.getId().getValue())) {
|
||||||
entry.getTitle().setValue(def.getName() + " " + theResource.getId().getValue());
|
String title = ResourceMetadataKeyEnum.TITLE.get(theResource);
|
||||||
|
if (title != null) {
|
||||||
|
entry.getTitle().setValue(title);
|
||||||
|
} else {
|
||||||
|
entry.getTitle().setValue(def.getName() + " " + theResource.getId().getValue());
|
||||||
|
}
|
||||||
|
|
||||||
StringBuilder b = new StringBuilder();
|
StringBuilder b = new StringBuilder();
|
||||||
b.append(theServerBase);
|
b.append(theServerBase);
|
||||||
|
|
|
@ -25,6 +25,8 @@ import static org.apache.commons.lang3.StringUtils.*;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import ca.uhn.fhir.model.primitive.IdDt;
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
|
@ -71,7 +73,7 @@ public abstract class ResourceMetadataKeyEnum<T> {
|
||||||
theResource.getResourceMetadata().put(PREVIOUS_ID, theObject);
|
theResource.getResourceMetadata().put(PREVIOUS_ID, theObject);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The value for this key is the bundle entry <b>Published</b> time. This is
|
* The value for this key is the bundle entry <b>Published</b> time. This is
|
||||||
* defined by FHIR as "Time resource copied into the feed", which is
|
* defined by FHIR as "Time resource copied into the feed", which is
|
||||||
|
@ -128,6 +130,26 @@ public abstract class ResourceMetadataKeyEnum<T> {
|
||||||
theResource.getResourceMetadata().put(TAG_LIST, theObject);
|
theResource.getResourceMetadata().put(TAG_LIST, theObject);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If present and populated with a string (as an instance of {@link String}),
|
||||||
|
* this value contains the title for this resource, as supplied in any bundles containing the
|
||||||
|
* resource.
|
||||||
|
* <p>
|
||||||
|
* Values for this key are of type <b>{@link String}</b>
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public static final ResourceMetadataKeyEnum<String> TITLE = new ResourceMetadataKeyEnum<String>("TITLE") {
|
||||||
|
@Override
|
||||||
|
public String get(IResource theResource) {
|
||||||
|
return getStringFromMetadataOrNullIfNone(theResource.getResourceMetadata(), TITLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void put(IResource theResource, String theObject) {
|
||||||
|
theResource.getResourceMetadata().put(TITLE, theObject);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -182,7 +204,6 @@ public abstract class ResourceMetadataKeyEnum<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
if (this == obj)
|
if (this == obj)
|
||||||
|
@ -200,6 +221,8 @@ public abstract class ResourceMetadataKeyEnum<T> {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public abstract T get(IResource theResource);
|
public abstract T get(IResource theResource);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -210,6 +233,10 @@ public abstract class ResourceMetadataKeyEnum<T> {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String name() {
|
||||||
|
return myValue;
|
||||||
|
}
|
||||||
|
|
||||||
public abstract void put(IResource theResource, T theObject);
|
public abstract void put(IResource theResource, T theObject);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -217,32 +244,28 @@ public abstract class ResourceMetadataKeyEnum<T> {
|
||||||
return myValue;
|
return myValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String name() {
|
private static IdDt getIdFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum<?>, Object> theResourceMetadata, ResourceMetadataKeyEnum<?> theKey) {
|
||||||
return myValue;
|
Object retValObj = theResourceMetadata.get(theKey);
|
||||||
|
if (retValObj == null) {
|
||||||
|
return null;
|
||||||
|
} else if (retValObj instanceof String) {
|
||||||
|
if (isNotBlank((String) retValObj)) {
|
||||||
|
return new IdDt((String) retValObj);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else if (retValObj instanceof IdDt) {
|
||||||
|
if (((IdDt) retValObj).isEmpty()) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return (IdDt) retValObj;
|
||||||
|
}
|
||||||
|
} else if (retValObj instanceof Number) {
|
||||||
|
return new IdDt(((Number)retValObj).toString());
|
||||||
|
}
|
||||||
|
throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected " + IdDt.class.getCanonicalName());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IdDt getIdFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum<?>, Object> theResourceMetadata, ResourceMetadataKeyEnum<?> theKey) {
|
|
||||||
Object retValObj = theResourceMetadata.get(theKey);
|
|
||||||
if (retValObj == null) {
|
|
||||||
return null;
|
|
||||||
} else if (retValObj instanceof String) {
|
|
||||||
if (isNotBlank((String) retValObj)) {
|
|
||||||
return new IdDt((String) retValObj);
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
} else if (retValObj instanceof IdDt) {
|
|
||||||
if (((IdDt) retValObj).isEmpty()) {
|
|
||||||
return null;
|
|
||||||
} else {
|
|
||||||
return (IdDt) retValObj;
|
|
||||||
}
|
|
||||||
} else if (retValObj instanceof Number) {
|
|
||||||
return new IdDt(((Number)retValObj).toString());
|
|
||||||
}
|
|
||||||
throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected " + IdDt.class.getCanonicalName());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static InstantDt getInstantFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum<?>, Object> theResourceMetadata, ResourceMetadataKeyEnum<InstantDt> theKey) {
|
private static InstantDt getInstantFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum<?>, Object> theResourceMetadata, ResourceMetadataKeyEnum<InstantDt> theKey) {
|
||||||
Object retValObj = theResourceMetadata.get(theKey);
|
Object retValObj = theResourceMetadata.get(theKey);
|
||||||
if (retValObj == null) {
|
if (retValObj == null) {
|
||||||
|
@ -259,4 +282,18 @@ public abstract class ResourceMetadataKeyEnum<T> {
|
||||||
throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected " + InstantDt.class.getCanonicalName());
|
throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected " + InstantDt.class.getCanonicalName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String getStringFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum<?>, Object> theResourceMetadata, ResourceMetadataKeyEnum<String> theKey) {
|
||||||
|
Object retValObj = theResourceMetadata.get(theKey);
|
||||||
|
if (retValObj == null) {
|
||||||
|
return null;
|
||||||
|
} else if (retValObj instanceof String) {
|
||||||
|
if (StringUtils.isBlank(((String) retValObj))) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return (String) retValObj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected " + String.class.getCanonicalName());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -407,9 +407,11 @@ public class CodingDt
|
||||||
@Override
|
@Override
|
||||||
public String getValueAsQueryToken() {
|
public String getValueAsQueryToken() {
|
||||||
if (org.apache.commons.lang3.StringUtils.isNotBlank(getSystem().getValueAsString())) {
|
if (org.apache.commons.lang3.StringUtils.isNotBlank(getSystem().getValueAsString())) {
|
||||||
return getSystem().getValueAsString() + '|' + getCode().getValueAsString();
|
return getSystem().getValueAsString() + '|' + getCode().getValueAsString();
|
||||||
} else {
|
} else if (getSystem().getValue()==null) {
|
||||||
return getCode().getValueAsString();
|
return getCode().getValueAsString();
|
||||||
|
} else {
|
||||||
|
return '|' + getCode().getValueAsString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -413,7 +413,9 @@ public class IdentifierDt
|
||||||
@Override
|
@Override
|
||||||
public String getValueAsQueryToken() {
|
public String getValueAsQueryToken() {
|
||||||
if (org.apache.commons.lang3.StringUtils.isNotBlank(getSystem().getValueAsString())) {
|
if (org.apache.commons.lang3.StringUtils.isNotBlank(getSystem().getValueAsString())) {
|
||||||
return getSystem().getValueAsString() + '|' + getValue().getValueAsString();
|
return getSystem().getValueAsString() + '|' + getValue().getValueAsString();
|
||||||
|
} else if (getSystem().getValue() == null) {
|
||||||
|
return getValue().getValueAsString();
|
||||||
} else {
|
} else {
|
||||||
return '|' + getValue().getValueAsString();
|
return '|' + getValue().getValueAsString();
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,17 +69,100 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
|
||||||
private boolean myIgnoreMissingTemplates = true;
|
private boolean myIgnoreMissingTemplates = true;
|
||||||
|
|
||||||
private TemplateEngine myProfileTemplateEngine;
|
private TemplateEngine myProfileTemplateEngine;
|
||||||
private HashMap<String, String> myProfileToNarrativeName;
|
private TemplateEngine myTitleTemplateEngine;
|
||||||
private HashMap<Class<?>, String> myClassToNarrativeName;
|
private HashMap<String, String> myProfileToName;
|
||||||
|
private HashMap<Class<?>, String> myClassToName;
|
||||||
private HashMap<String, String> myNameToNarrativeTemplate;
|
private HashMap<String, String> myNameToNarrativeTemplate;
|
||||||
private boolean myApplyDefaultDatatypeTemplates=true;
|
private boolean myApplyDefaultDatatypeTemplates = true;
|
||||||
private volatile boolean myInitialized;
|
private volatile boolean myInitialized;
|
||||||
|
|
||||||
|
private HashMap<String, String> myNameToTitleTemplate;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NarrativeDt generateNarrative(IResource theResource) {
|
public NarrativeDt generateNarrative(IResource theResource) {
|
||||||
return generateNarrative(null, theResource);
|
return generateNarrative(null, theResource);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String generateTitle(IResource theResource) {
|
||||||
|
return generateTitle(null, theResource);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String generateTitle(String theProfile, IResource theResource) {
|
||||||
|
if (!myInitialized) {
|
||||||
|
initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
String name = null;
|
||||||
|
if (StringUtils.isNotBlank(theProfile)) {
|
||||||
|
name = myProfileToName.get(theProfile);
|
||||||
|
}
|
||||||
|
if (name == null) {
|
||||||
|
name = myClassToName.get(theResource.getClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
String result = myTitleTemplateEngine.process(name, context);
|
||||||
|
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('<'));
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NarrativeDt generateNarrative(String theProfile, IResource theResource) {
|
public NarrativeDt generateNarrative(String theProfile, IResource theResource) {
|
||||||
if (!myInitialized) {
|
if (!myInitialized) {
|
||||||
|
@ -88,10 +171,10 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
|
||||||
|
|
||||||
String name = null;
|
String name = null;
|
||||||
if (StringUtils.isNotBlank(theProfile)) {
|
if (StringUtils.isNotBlank(theProfile)) {
|
||||||
name = myProfileToNarrativeName.get(theProfile);
|
name = myProfileToName.get(theProfile);
|
||||||
}
|
}
|
||||||
if (name == null) {
|
if (name == null) {
|
||||||
name = myClassToNarrativeName.get(theResource.getClass());
|
name = myClassToName.get(theResource.getClass());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name == null) {
|
if (name == null) {
|
||||||
|
@ -106,7 +189,7 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
|
||||||
try {
|
try {
|
||||||
Context context = new Context();
|
Context context = new Context();
|
||||||
context.setVariable("resource", theResource);
|
context.setVariable("resource", theResource);
|
||||||
|
|
||||||
String result = myProfileTemplateEngine.process(name, context);
|
String result = myProfileTemplateEngine.process(name, context);
|
||||||
|
|
||||||
if (myCleanWhitespace) {
|
if (myCleanWhitespace) {
|
||||||
|
@ -131,9 +214,10 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
|
||||||
if (myInitialized) {
|
if (myInitialized) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
myProfileToNarrativeName = new HashMap<String, String>();
|
myProfileToName = new HashMap<String, String>();
|
||||||
myClassToNarrativeName = new HashMap<Class<?>, String>();
|
myClassToName = new HashMap<Class<?>, String>();
|
||||||
myNameToNarrativeTemplate = new HashMap<String, String>();
|
myNameToNarrativeTemplate = new HashMap<String, String>();
|
||||||
|
myNameToTitleTemplate = new HashMap<String, String>();
|
||||||
|
|
||||||
List<String> propFileName = getPropertyFile();
|
List<String> propFileName = getPropertyFile();
|
||||||
|
|
||||||
|
@ -160,6 +244,15 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
|
||||||
myProfileTemplateEngine.setDialect(dialect);
|
myProfileTemplateEngine.setDialect(dialect);
|
||||||
myProfileTemplateEngine.initialize();
|
myProfileTemplateEngine.initialize();
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
myTitleTemplateEngine = new TemplateEngine();
|
||||||
|
TemplateResolver resolver = new TemplateResolver();
|
||||||
|
resolver.setResourceResolver(new TitleResourceResolver());
|
||||||
|
myTitleTemplateEngine.setTemplateResolver(resolver);
|
||||||
|
StandardDialect dialect = new StandardDialect();
|
||||||
|
myTitleTemplateEngine.setDialect(dialect);
|
||||||
|
myTitleTemplateEngine.initialize();
|
||||||
|
}
|
||||||
|
|
||||||
myInitialized = true;
|
myInitialized = true;
|
||||||
}
|
}
|
||||||
|
@ -167,12 +260,9 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
|
||||||
protected abstract List<String> getPropertyFile();
|
protected abstract List<String> getPropertyFile();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If set to <code>true</code> (which is the default), most whitespace will
|
* If set to <code>true</code> (which is the default), most whitespace will be trimmed from the generated narrative before it is returned.
|
||||||
* be trimmed from the generated narrative before it is returned.
|
|
||||||
* <p>
|
* <p>
|
||||||
* Note that in order to preserve formatting, not all whitespace is trimmed.
|
* Note that in order to preserve formatting, not all whitespace is trimmed. Repeated whitespace characters (e.g. "\n \n ") will be trimmed to a single space.
|
||||||
* Repeated whitespace characters (e.g. "\n \n ") will be
|
|
||||||
* trimmed to a single space.
|
|
||||||
* </p>
|
* </p>
|
||||||
*/
|
*/
|
||||||
public boolean isCleanWhitespace() {
|
public boolean isCleanWhitespace() {
|
||||||
|
@ -180,30 +270,24 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If set to <code>true</code>, which is the default, if any failure occurs
|
* If set to <code>true</code>, which is the default, if any failure occurs during narrative generation the generator will suppress any generated exceptions, and simply return a default narrative
|
||||||
* during narrative generation the generator will suppress any generated
|
* indicating that no narrative is available.
|
||||||
* exceptions, and simply return a default narrative indicating that no
|
|
||||||
* narrative is available.
|
|
||||||
*/
|
*/
|
||||||
public boolean isIgnoreFailures() {
|
public boolean isIgnoreFailures() {
|
||||||
return myIgnoreFailures;
|
return myIgnoreFailures;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If set to true, will return an empty narrative block for any profiles
|
* If set to true, will return an empty narrative block for any profiles where no template is available
|
||||||
* where no template is available
|
|
||||||
*/
|
*/
|
||||||
public boolean isIgnoreMissingTemplates() {
|
public boolean isIgnoreMissingTemplates() {
|
||||||
return myIgnoreMissingTemplates;
|
return myIgnoreMissingTemplates;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If set to <code>true</code> (which is the default), most whitespace will
|
* If set to <code>true</code> (which is the default), most whitespace will be trimmed from the generated narrative before it is returned.
|
||||||
* be trimmed from the generated narrative before it is returned.
|
|
||||||
* <p>
|
* <p>
|
||||||
* Note that in order to preserve formatting, not all whitespace is trimmed.
|
* Note that in order to preserve formatting, not all whitespace is trimmed. Repeated whitespace characters (e.g. "\n \n ") will be trimmed to a single space.
|
||||||
* Repeated whitespace characters (e.g. "\n \n ") will be
|
|
||||||
* trimmed to a single space.
|
|
||||||
* </p>
|
* </p>
|
||||||
*/
|
*/
|
||||||
public void setCleanWhitespace(boolean theCleanWhitespace) {
|
public void setCleanWhitespace(boolean theCleanWhitespace) {
|
||||||
|
@ -211,18 +295,15 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If set to <code>true</code>, which is the default, if any failure occurs
|
* If set to <code>true</code>, which is the default, if any failure occurs during narrative generation the generator will suppress any generated exceptions, and simply return a default narrative
|
||||||
* during narrative generation the generator will suppress any generated
|
* indicating that no narrative is available.
|
||||||
* exceptions, and simply return a default narrative indicating that no
|
|
||||||
* narrative is available.
|
|
||||||
*/
|
*/
|
||||||
public void setIgnoreFailures(boolean theIgnoreFailures) {
|
public void setIgnoreFailures(boolean theIgnoreFailures) {
|
||||||
myIgnoreFailures = theIgnoreFailures;
|
myIgnoreFailures = theIgnoreFailures;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If set to true, will return an empty narrative block for any profiles
|
* If set to true, will return an empty narrative block for any profiles where no template is available
|
||||||
* where no template is available
|
|
||||||
*/
|
*/
|
||||||
public void setIgnoreMissingTemplates(boolean theIgnoreMissingTemplates) {
|
public void setIgnoreMissingTemplates(boolean theIgnoreMissingTemplates) {
|
||||||
myIgnoreMissingTemplates = theIgnoreMissingTemplates;
|
myIgnoreMissingTemplates = theIgnoreMissingTemplates;
|
||||||
|
@ -230,7 +311,7 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
|
||||||
|
|
||||||
private void loadProperties(String propFileName) throws IOException {
|
private void loadProperties(String propFileName) throws IOException {
|
||||||
ourLog.debug("Loading narrative properties file: {}", propFileName);
|
ourLog.debug("Loading narrative properties file: {}", propFileName);
|
||||||
|
|
||||||
Properties file = new Properties();
|
Properties file = new Properties();
|
||||||
|
|
||||||
InputStream resource = loadResource(propFileName);
|
InputStream resource = loadResource(propFileName);
|
||||||
|
@ -245,13 +326,22 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
|
||||||
|
|
||||||
String narrativePropName = name + ".narrative";
|
String narrativePropName = name + ".narrative";
|
||||||
String narrativeName = file.getProperty(narrativePropName);
|
String narrativeName = file.getProperty(narrativePropName);
|
||||||
if (isBlank(narrativeName)) {
|
String titlePropName = name + ".title";
|
||||||
throw new ConfigurationException("Found property '" + nextKey + "' but no corresponding property '" + narrativePropName + "' in file " + propFileName);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
String narrative = IOUtils.toString(loadResource(narrativeName));
|
myProfileToName.put(file.getProperty(nextKey), name);
|
||||||
myProfileToNarrativeName.put(file.getProperty(nextKey), name);
|
|
||||||
myNameToNarrativeTemplate.put(name, narrative);
|
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")) {
|
} else if (nextKey.endsWith(".class")) {
|
||||||
|
|
||||||
|
@ -262,9 +352,9 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
|
||||||
|
|
||||||
String className = file.getProperty(nextKey);
|
String className = file.getProperty(nextKey);
|
||||||
|
|
||||||
Class<?> dtClass;
|
Class<?> clazz;
|
||||||
try {
|
try {
|
||||||
dtClass = Class.forName(className);
|
clazz = Class.forName(className);
|
||||||
} catch (ClassNotFoundException e) {
|
} catch (ClassNotFoundException e) {
|
||||||
ourLog.warn("Unknown datatype class '{}' identified in narrative file {}", name, propFileName);
|
ourLog.warn("Unknown datatype class '{}' identified in narrative file {}", name, propFileName);
|
||||||
continue;
|
continue;
|
||||||
|
@ -272,16 +362,27 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
|
||||||
|
|
||||||
String narrativePropName = name + ".narrative";
|
String narrativePropName = name + ".narrative";
|
||||||
String narrativeName = file.getProperty(narrativePropName);
|
String narrativeName = file.getProperty(narrativePropName);
|
||||||
if (isBlank(narrativeName)) {
|
String titlePropName = name + ".title";
|
||||||
throw new ConfigurationException("Found property '" + nextKey + "' but no corresponding property '" + narrativePropName + "' in file " + propFileName);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
String narrative = IOUtils.toString(loadResource(narrativeName));
|
myClassToName.put(clazz, name);
|
||||||
myClassToNarrativeName.put(dtClass, name);
|
|
||||||
myNameToNarrativeTemplate.put(name, narrative);
|
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(".narrative")) {
|
} else if (nextKey.endsWith(".narrative")) {
|
||||||
continue;
|
continue;
|
||||||
|
} else if (nextKey.endsWith(".title")) {
|
||||||
|
continue;
|
||||||
} else {
|
} else {
|
||||||
throw new ConfigurationException("Invalid property name: " + nextKey);
|
throw new ConfigurationException("Invalid property name: " + nextKey);
|
||||||
}
|
}
|
||||||
|
@ -379,14 +480,13 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
|
||||||
final IStandardExpression expression = expressionParser.parseExpression(configuration, theArguments, attributeValue);
|
final IStandardExpression expression = expressionParser.parseExpression(configuration, theArguments, attributeValue);
|
||||||
final Object value = expression.execute(configuration, theArguments);
|
final Object value = expression.execute(configuration, theArguments);
|
||||||
|
|
||||||
|
|
||||||
theElement.removeAttribute(theAttributeName);
|
theElement.removeAttribute(theAttributeName);
|
||||||
theElement.clearChildren();
|
theElement.clearChildren();
|
||||||
|
|
||||||
Context context = new Context();
|
Context context = new Context();
|
||||||
context.setVariable("resource", value);
|
context.setVariable("resource", value);
|
||||||
|
|
||||||
String name = myClassToNarrativeName.get(value.getClass());
|
String name = myClassToName.get(value.getClass());
|
||||||
if (name == null) {
|
if (name == null) {
|
||||||
if (myIgnoreMissingTemplates) {
|
if (myIgnoreMissingTemplates) {
|
||||||
ourLog.debug("No narrative template available for type: {}", value.getClass());
|
ourLog.debug("No narrative template available for type: {}", value.getClass());
|
||||||
|
@ -438,4 +538,21 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
|
||||||
return new ReaderInputStream(new StringReader(template));
|
return new ReaderInputStream(new StringReader(template));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,4 +64,6 @@ public class DefaultThymeleafNarrativeGenerator extends BaseThymeleafNarrativeGe
|
||||||
return myUseHapiServerConformanceNarrative;
|
return myUseHapiServerConformanceNarrative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,4 +30,8 @@ public interface INarrativeGenerator {
|
||||||
|
|
||||||
NarrativeDt generateNarrative(IResource theResource);
|
NarrativeDt generateNarrative(IResource theResource);
|
||||||
|
|
||||||
|
String generateTitle(IResource theResource);
|
||||||
|
|
||||||
|
String generateTitle(String theProfile, IResource theResource);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -222,7 +222,7 @@ public class JsonParser extends BaseParser implements IParser {
|
||||||
eventWriter.writeEnd(); // entry array
|
eventWriter.writeEnd(); // entry array
|
||||||
|
|
||||||
eventWriter.writeEnd();
|
eventWriter.writeEnd();
|
||||||
eventWriter.close();
|
eventWriter.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void encodeChildElementToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, JsonGenerator theWriter, IElement theValue, BaseRuntimeElementDefinition<?> theChildDef, String theChildName) throws IOException {
|
private void encodeChildElementToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, JsonGenerator theWriter, IElement theValue, BaseRuntimeElementDefinition<?> theChildDef, String theChildName) throws IOException {
|
||||||
|
|
|
@ -417,6 +417,9 @@ class ParserState<T> {
|
||||||
if (myEntry.getUpdated().isEmpty() == false) {
|
if (myEntry.getUpdated().isEmpty() == false) {
|
||||||
ResourceMetadataKeyEnum.UPDATED.put(myEntry.getResource(), myEntry.getUpdated());
|
ResourceMetadataKeyEnum.UPDATED.put(myEntry.getResource(), myEntry.getUpdated());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ResourceMetadataKeyEnum.TITLE.put(myEntry.getResource(), myEntry.getTitle().getValue());
|
||||||
|
|
||||||
if (myEntry.getCategories().isEmpty() == false) {
|
if (myEntry.getCategories().isEmpty() == false) {
|
||||||
TagList tagList = new TagList();
|
TagList tagList = new TagList();
|
||||||
for (Tag next : myEntry.getCategories()) {
|
for (Tag next : myEntry.getCategories()) {
|
||||||
|
@ -608,7 +611,6 @@ class ParserState<T> {
|
||||||
myPreResourceState = thePreResourceState;
|
myPreResourceState = thePreResourceState;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
public void attributeValue(String theName, String theValue) throws DataFormatException {
|
public void attributeValue(String theName, String theValue) throws DataFormatException {
|
||||||
// ignore by default
|
// ignore by default
|
||||||
}
|
}
|
||||||
|
@ -617,7 +619,6 @@ class ParserState<T> {
|
||||||
// ignore by default
|
// ignore by default
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException {
|
public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException {
|
||||||
// ignore by default
|
// ignore by default
|
||||||
}
|
}
|
||||||
|
@ -657,7 +658,7 @@ class ParserState<T> {
|
||||||
myStack = theState;
|
myStack = theState;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void string(@SuppressWarnings("unused") String theData) {
|
public void string(String theData) {
|
||||||
// ignore by default
|
// ignore by default
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -665,7 +666,7 @@ class ParserState<T> {
|
||||||
// allow an implementor to override
|
// allow an implementor to override
|
||||||
}
|
}
|
||||||
|
|
||||||
public void xmlEvent(@SuppressWarnings("unused") XMLEvent theNextEvent) {
|
public void xmlEvent(XMLEvent theNextEvent) {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ import ca.uhn.fhir.rest.method.QualifiedParamList;
|
||||||
public class IdentifierListParam implements IQueryParameterOr {
|
public class IdentifierListParam implements IQueryParameterOr {
|
||||||
|
|
||||||
private List<IdentifierDt> myIdentifiers = new ArrayList<IdentifierDt>();
|
private List<IdentifierDt> myIdentifiers = new ArrayList<IdentifierDt>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns all identifiers associated with this list
|
* Returns all identifiers associated with this list
|
||||||
*/
|
*/
|
||||||
|
@ -64,4 +64,10 @@ public class IdentifierListParam implements IQueryParameterOr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addIdentifier(IdentifierDt theIdentifierDt) {
|
||||||
|
if (theIdentifierDt != null && theIdentifierDt.isEmpty() == false) {
|
||||||
|
getIdentifiers().add(theIdentifierDt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -160,6 +160,8 @@ public class SearchParameter extends BaseQueryParameter {
|
||||||
myParamType = SearchParamTypeEnum.QUANTITY;
|
myParamType = SearchParamTypeEnum.QUANTITY;
|
||||||
} else if (ReferenceParam.class.isAssignableFrom(type)) {
|
} else if (ReferenceParam.class.isAssignableFrom(type)) {
|
||||||
myParamType = SearchParamTypeEnum.REFERENCE;
|
myParamType = SearchParamTypeEnum.REFERENCE;
|
||||||
|
} else if (IdentifierListParam.class.isAssignableFrom(type)) {
|
||||||
|
myParamType = SearchParamTypeEnum.TOKEN;
|
||||||
} else {
|
} else {
|
||||||
throw new ConfigurationException("Unknown search parameter type: " + type);
|
throw new ConfigurationException("Unknown search parameter type: " + type);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,11 +20,10 @@ package ca.uhn.fhir.rest.server;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import static org.apache.commons.lang3.StringUtils.*;
|
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStreamWriter;
|
import java.io.OutputStreamWriter;
|
||||||
import java.io.PrintWriter;
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.io.Writer;
|
import java.io.Writer;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
@ -46,8 +45,9 @@ import javax.servlet.http.HttpServlet;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
import org.apache.commons.lang3.Validate;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
@ -542,22 +542,26 @@ public class RestfulServer extends HttpServlet {
|
||||||
theResponse.setContentType("text/plain");
|
theResponse.setContentType("text/plain");
|
||||||
theResponse.setCharacterEncoding("UTF-8");
|
theResponse.setCharacterEncoding("UTF-8");
|
||||||
theResponse.getWriter().write(e.getMessage());
|
theResponse.getWriter().write(e.getMessage());
|
||||||
|
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
|
|
||||||
int statusCode = 500;
|
|
||||||
if (e instanceof InternalErrorException) {
|
|
||||||
ourLog.error("Failure during REST processing", e);
|
|
||||||
} else if (e instanceof BaseServerResponseException) {
|
|
||||||
ourLog.warn("Failure during REST processing: {}", e.toString());
|
|
||||||
statusCode=((BaseServerResponseException) e).getStatusCode();
|
|
||||||
} else {
|
|
||||||
ourLog.warn("Failure during REST processing: {}", e.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
OperationOutcome oo = new OperationOutcome();
|
OperationOutcome oo = new OperationOutcome();
|
||||||
Issue issue = oo.addIssue();
|
Issue issue = oo.addIssue();
|
||||||
issue.getSeverity().setValueAsEnum(IssueSeverityEnum.ERROR);
|
issue.getSeverity().setValueAsEnum(IssueSeverityEnum.ERROR);
|
||||||
issue.getDetails().setValue(e.toString() + "\n\n" + ExceptionUtils.getStackTrace(e));
|
|
||||||
|
int statusCode = 500;
|
||||||
|
if (e instanceof InternalErrorException) {
|
||||||
|
ourLog.error("Failure during REST processing", e);
|
||||||
|
issue.getDetails().setValue(e.toString() + "\n\n" + ExceptionUtils.getStackTrace(e));
|
||||||
|
} else if (e instanceof BaseServerResponseException) {
|
||||||
|
ourLog.warn("Failure during REST processing: {}", e.toString());
|
||||||
|
statusCode=((BaseServerResponseException) e).getStatusCode();
|
||||||
|
issue.getDetails().setValue(e.getMessage());
|
||||||
|
} else {
|
||||||
|
ourLog.error("Failure during REST processing: {}"+ e.toString(), e);
|
||||||
|
issue.getDetails().setValue(e.toString() + "\n\n" + ExceptionUtils.getStackTrace(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
streamResponseAsResource(this, theResponse, oo, determineResponseEncoding(theRequest), true, false, NarrativeModeEnum.NORMAL, statusCode,false);
|
streamResponseAsResource(this, theResponse, oo, determineResponseEncoding(theRequest), true, false, NarrativeModeEnum.NORMAL, statusCode,false);
|
||||||
|
|
||||||
|
@ -776,6 +780,14 @@ public class RestfulServer extends HttpServlet {
|
||||||
bundle.getLinkSelf().setValue(theCompleteUrl);
|
bundle.getLinkSelf().setValue(theCompleteUrl);
|
||||||
|
|
||||||
for (IResource next : theResult) {
|
for (IResource next : theResult) {
|
||||||
|
|
||||||
|
if (theContext.getNarrativeGenerator() != null) {
|
||||||
|
String title = theContext.getNarrativeGenerator().generateTitle(next);
|
||||||
|
if (StringUtils.isNotBlank(title)) {
|
||||||
|
ResourceMetadataKeyEnum.TITLE.put(next, title);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bundle.addResource(next, theContext, theServerBase);
|
bundle.addResource(next, theContext, theServerBase);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,9 @@ quantity.narrative=classpath:ca/uhn/fhir/narrative/QuantityDt.html
|
||||||
|
|
||||||
patient.class=ca.uhn.fhir.model.dstu.resource.Patient
|
patient.class=ca.uhn.fhir.model.dstu.resource.Patient
|
||||||
patient.narrative=classpath:ca/uhn/fhir/narrative/Patient.html
|
patient.narrative=classpath:ca/uhn/fhir/narrative/Patient.html
|
||||||
|
patient.title=classpath:ca/uhn/fhir/narrative/title/Patient.html
|
||||||
|
|
||||||
|
|
||||||
diagnosticreport.class=ca.uhn.fhir.model.dstu.resource.DiagnosticReport
|
diagnosticreport.class=ca.uhn.fhir.model.dstu.resource.DiagnosticReport
|
||||||
diagnosticreport.narrative=classpath:ca/uhn/fhir/narrative/DiagnosticReport.html
|
diagnosticreport.narrative=classpath:ca/uhn/fhir/narrative/DiagnosticReport.html
|
||||||
|
diagnosticreport.title=classpath:ca/uhn/fhir/narrative/title/DiagnosticReport.html
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
<div>
|
||||||
|
<th:block th:if="${not resource.name.text.empty}" th:text="${resource.name.text.value}"/>
|
||||||
|
<th:block th:if=" ${resource.name.text.empty} and ${not resource.name.codingFirstRep.display.empty}" th:text="${resource.name.codingFirstRep.display}"/>
|
||||||
|
<th:block th:if= "${resource.name.text.empty} and ${resource.name.codingFirstRep.display.empty}" th:text="Untitled Diagnostic Report"/>
|
||||||
|
|
||||||
|
<th:block th:if="${not resource.status.empty}" th:text="' - ' + ${resource.status.value}"/>
|
||||||
|
<th:block th:text="' - ' + ${resource.result.size} + ' observations'"/>
|
||||||
|
|
||||||
|
</div>
|
|
@ -0,0 +1,9 @@
|
||||||
|
<div>
|
||||||
|
<th:block th:each="prefix : ${resource.nameFirstRep.prefix}" th:text="${prefix.value} + ' '">Dr</th:block>
|
||||||
|
<th:block th:each="givenName : ${resource.nameFirstRep.given}" th:text="${givenName.value} + ' '">John</th:block>
|
||||||
|
<th:block th:each="familyName : ${resource.nameFirstRep.family}" th:text="${#strings.toUpperCase(familyName.value)} + ' '">SMITH</th:block>
|
||||||
|
<th:block th:each="suffix : ${resource.nameFirstRep.suffix}" th:text="${suffix.value} + ' '">Jr</th:block>
|
||||||
|
<th:block th:if="${not resource.identifierFirstRep.empty}">
|
||||||
|
(<th:block th:text="${resource.identifierFirstRep.value.value}">8708660</th:block>)
|
||||||
|
</th:block>
|
||||||
|
</div>
|
|
@ -0,0 +1,41 @@
|
||||||
|
package ca.uhn.fhir.model.primitive;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.model.dstu.composite.CodingDt;
|
||||||
|
|
||||||
|
public class CodingDtTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTokenWithPipeInValue() {
|
||||||
|
CodingDt dt = new CodingDt();
|
||||||
|
dt.setValueAsQueryToken(null, "a|b|c");
|
||||||
|
|
||||||
|
assertEquals("a", dt.getSystem().getValueAsString());
|
||||||
|
assertEquals("b|c", dt.getCode().getValue());
|
||||||
|
assertEquals("a|b|c", dt.getValueAsQueryToken());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTokenWithPipeInValueAndNoSystem() {
|
||||||
|
CodingDt dt = new CodingDt();
|
||||||
|
dt.setValueAsQueryToken(null, "|b|c");
|
||||||
|
|
||||||
|
assertEquals("", dt.getSystem().getValueAsString());
|
||||||
|
assertEquals("b|c", dt.getCode().getValue());
|
||||||
|
assertEquals("|b|c", dt.getValueAsQueryToken());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTokenNoSystem() {
|
||||||
|
CodingDt dt = new CodingDt();
|
||||||
|
dt.setValueAsQueryToken(null, "c");
|
||||||
|
|
||||||
|
assertEquals(null, dt.getSystem().getValueAsString());
|
||||||
|
assertEquals("c", dt.getCode().getValue());
|
||||||
|
assertEquals("c", dt.getValueAsQueryToken());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -28,4 +28,14 @@ public class IdentifierDtTest {
|
||||||
assertEquals("|b|c", dt.getValueAsQueryToken());
|
assertEquals("|b|c", dt.getValueAsQueryToken());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTokenNoSystem() {
|
||||||
|
IdentifierDt dt = new IdentifierDt();
|
||||||
|
dt.setValueAsQueryToken(null, "c");
|
||||||
|
|
||||||
|
assertEquals(null, dt.getSystem().getValueAsString());
|
||||||
|
assertEquals("c", dt.getValue().getValue());
|
||||||
|
assertEquals("c", dt.getValueAsQueryToken());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,9 +46,12 @@ public class DefaultThymeleafNarrativeGeneratorTest {
|
||||||
|
|
||||||
value.setBirthDate(new Date(), TemporalPrecisionEnum.DAY);
|
value.setBirthDate(new Date(), TemporalPrecisionEnum.DAY);
|
||||||
|
|
||||||
String output = gen.generateNarrative("http://hl7.org/fhir/profiles/Patient", value).getDiv().getValueAsString();
|
String output = gen.generateNarrative(value).getDiv().getValueAsString();
|
||||||
|
assertThat(output, StringContains.containsString("<div class=\"hapiHeaderText\"> joe john <b>BLOW </b></div>"));
|
||||||
ourLog.info(output);
|
|
||||||
|
String title = gen.generateTitle(value);
|
||||||
|
assertEquals("joe john BLOW (123456)", title);
|
||||||
|
ourLog.info(title);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -105,6 +108,11 @@ public class DefaultThymeleafNarrativeGeneratorTest {
|
||||||
ourLog.info(output);
|
ourLog.info(output);
|
||||||
assertThat(output, StringContains.containsString("<div class=\"hapiHeaderText\"> Some Diagnostic Report </div>"));
|
assertThat(output, StringContains.containsString("<div class=\"hapiHeaderText\"> Some Diagnostic Report </div>"));
|
||||||
|
|
||||||
|
String title = gen.generateTitle(value);
|
||||||
|
ourLog.info(title);
|
||||||
|
assertEquals("Some Diagnostic Report - final - 2 observations", title);
|
||||||
|
|
||||||
|
|
||||||
// Now try it with the parser
|
// Now try it with the parser
|
||||||
|
|
||||||
FhirContext context = new FhirContext();
|
FhirContext context = new FhirContext();
|
||||||
|
|
|
@ -43,28 +43,44 @@ public class ExceptionTest {
|
||||||
private static RestfulServer servlet;
|
private static RestfulServer servlet;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSearchNormalMatch() throws Exception {
|
public void testInternalError() throws Exception {
|
||||||
{
|
{
|
||||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?throwInternalError=aaa");
|
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?throwInternalError=aaa");
|
||||||
HttpResponse status = ourClient.execute(httpGet);
|
HttpResponse status = ourClient.execute(httpGet);
|
||||||
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
ourLog.info(responseContent);
|
ourLog.info(responseContent);
|
||||||
assertEquals(500, status.getStatusLine().getStatusCode());
|
assertEquals(500, status.getStatusLine().getStatusCode());
|
||||||
OperationOutcome oo = (OperationOutcome) servlet.getFhirContext().newXmlParser().parseResource(responseContent);
|
OperationOutcome oo = (OperationOutcome) servlet.getFhirContext().newXmlParser().parseResource(responseContent);
|
||||||
assertThat(oo.getIssueFirstRep().getDetails().getValue(), StringContains.containsString("InternalErrorException: Exception Text"));
|
assertThat(oo.getIssueFirstRep().getDetails().getValue(), StringContains.containsString("InternalErrorException: Exception Text"));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInternalErrorFormatted() throws Exception {
|
||||||
{
|
{
|
||||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?throwInternalError=aaa&_format=json");
|
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?throwInternalError=aaa&_format=true");
|
||||||
HttpResponse status = ourClient.execute(httpGet);
|
HttpResponse status = ourClient.execute(httpGet);
|
||||||
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
ourLog.info(responseContent);
|
ourLog.info(responseContent);
|
||||||
assertEquals(500, status.getStatusLine().getStatusCode());
|
assertEquals(500, status.getStatusLine().getStatusCode());
|
||||||
OperationOutcome oo = (OperationOutcome) servlet.getFhirContext().newJsonParser().parseResource(responseContent);
|
OperationOutcome oo = (OperationOutcome) servlet.getFhirContext().newXmlParser().parseResource(responseContent);
|
||||||
assertThat(oo.getIssueFirstRep().getDetails().getValue(), StringContains.containsString("InternalErrorException: Exception Text"));
|
assertThat(oo.getIssueFirstRep().getDetails().getValue(), StringContains.containsString("InternalErrorException: Exception Text"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInternalErrorJson() throws Exception {
|
||||||
|
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?throwInternalError=aaa&_format=json");
|
||||||
|
HttpResponse status = ourClient.execute(httpGet);
|
||||||
|
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
ourLog.info(responseContent);
|
||||||
|
assertEquals(500, status.getStatusLine().getStatusCode());
|
||||||
|
OperationOutcome oo = (OperationOutcome) servlet.getFhirContext().newJsonParser().parseResource(responseContent);
|
||||||
|
assertThat(oo.getIssueFirstRep().getDetails().getValue(), StringContains.containsString("InternalErrorException: Exception Text"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@AfterClass
|
@AfterClass
|
||||||
|
|
|
@ -23,9 +23,8 @@ import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.model.api.Bundle;
|
import ca.uhn.fhir.model.api.Bundle;
|
||||||
import ca.uhn.fhir.model.api.IResource;
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
import ca.uhn.fhir.model.dstu.resource.Patient;
|
import ca.uhn.fhir.model.dstu.resource.Patient;
|
||||||
import ca.uhn.fhir.model.primitive.IdDt;
|
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
||||||
import ca.uhn.fhir.rest.annotation.OptionalParam;
|
import ca.uhn.fhir.rest.annotation.OptionalParam;
|
||||||
import ca.uhn.fhir.rest.annotation.RequiredParam;
|
|
||||||
import ca.uhn.fhir.rest.annotation.Search;
|
import ca.uhn.fhir.rest.annotation.Search;
|
||||||
import ca.uhn.fhir.rest.param.StringParam;
|
import ca.uhn.fhir.rest.param.StringParam;
|
||||||
import ca.uhn.fhir.testutil.RandomServerPortProvider;
|
import ca.uhn.fhir.testutil.RandomServerPortProvider;
|
||||||
|
@ -36,7 +35,6 @@ import ca.uhn.fhir.testutil.RandomServerPortProvider;
|
||||||
public class SearchTest {
|
public class SearchTest {
|
||||||
|
|
||||||
private static CloseableHttpClient ourClient;
|
private static CloseableHttpClient ourClient;
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchTest.class);
|
|
||||||
private static int ourPort;
|
private static int ourPort;
|
||||||
private static Server ourServer;
|
private static Server ourServer;
|
||||||
private static FhirContext ourCtx = new FhirContext();
|
private static FhirContext ourCtx = new FhirContext();
|
||||||
|
@ -46,12 +44,14 @@ public class SearchTest {
|
||||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_id=aaa");
|
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_id=aaa");
|
||||||
HttpResponse status = ourClient.execute(httpGet);
|
HttpResponse status = ourClient.execute(httpGet);
|
||||||
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
Bundle bundle = ourCtx.newXmlParser().parseBundle(responseContent);
|
Bundle bundle = ourCtx.newXmlParser().parseBundle(responseContent);
|
||||||
assertEquals(1, bundle.getEntries().size());
|
assertEquals(1, bundle.getEntries().size());
|
||||||
|
|
||||||
Patient p = bundle.getResources(Patient.class).get(0);
|
Patient p = bundle.getResources(Patient.class).get(0);
|
||||||
assertEquals("idaaa", p.getNameFirstRep().getFamilyAsSingleString());
|
assertEquals("idaaa", p.getNameFirstRep().getFamilyAsSingleString());
|
||||||
|
assertEquals("IDAAA (identifier123)", bundle.getEntries().get(0).getTitle().getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterClass
|
@AfterClass
|
||||||
|
@ -68,6 +68,8 @@ public class SearchTest {
|
||||||
|
|
||||||
ServletHandler proxyHandler = new ServletHandler();
|
ServletHandler proxyHandler = new ServletHandler();
|
||||||
RestfulServer servlet = new RestfulServer();
|
RestfulServer servlet = new RestfulServer();
|
||||||
|
servlet.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator());
|
||||||
|
|
||||||
servlet.setResourceProviders(patientProvider);
|
servlet.setResourceProviders(patientProvider);
|
||||||
ServletHolder servletHolder = new ServletHolder(servlet);
|
ServletHolder servletHolder = new ServletHolder(servlet);
|
||||||
proxyHandler.addServletWithMapping(servletHolder, "/*");
|
proxyHandler.addServletWithMapping(servletHolder, "/*");
|
||||||
|
@ -92,6 +94,7 @@ public class SearchTest {
|
||||||
|
|
||||||
Patient patient = new Patient();
|
Patient patient = new Patient();
|
||||||
patient.setId("1");
|
patient.setId("1");
|
||||||
|
patient.addIdentifier("system", "identifier123");
|
||||||
patient.addName().addFamily("id"+theParam.getValue());
|
patient.addName().addFamily("id"+theParam.getValue());
|
||||||
retVal.add(patient);
|
retVal.add(patient);
|
||||||
return retVal;
|
return retVal;
|
||||||
|
|
|
@ -747,6 +747,9 @@ public abstract class BaseFhirDao {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String title = ResourceMetadataKeyEnum.TITLE.get(theResource);
|
||||||
|
theEntity.setTitle(title);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ResourceTable toEntity(IResource theResource) {
|
protected ResourceTable toEntity(IResource theResource) {
|
||||||
|
@ -784,6 +787,10 @@ public abstract class BaseFhirDao {
|
||||||
retVal.getResourceMetadata().put(ResourceMetadataKeyEnum.PUBLISHED, theEntity.getPublished());
|
retVal.getResourceMetadata().put(ResourceMetadataKeyEnum.PUBLISHED, theEntity.getPublished());
|
||||||
retVal.getResourceMetadata().put(ResourceMetadataKeyEnum.UPDATED, theEntity.getUpdated());
|
retVal.getResourceMetadata().put(ResourceMetadataKeyEnum.UPDATED, theEntity.getUpdated());
|
||||||
|
|
||||||
|
if (theEntity.getTitle()!=null) {
|
||||||
|
ResourceMetadataKeyEnum.TITLE.put(retVal, theEntity.getTitle());
|
||||||
|
}
|
||||||
|
|
||||||
if (theEntity.getDeleted()!=null) {
|
if (theEntity.getDeleted()!=null) {
|
||||||
ResourceMetadataKeyEnum.DELETED_AT.put(retVal, new InstantDt(theEntity.getDeleted()));
|
ResourceMetadataKeyEnum.DELETED_AT.put(retVal, new InstantDt(theEntity.getDeleted()));
|
||||||
}
|
}
|
||||||
|
|
|
@ -431,7 +431,10 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
||||||
ArrayList<Predicate> singleCodePredicates = (new ArrayList<Predicate>());
|
ArrayList<Predicate> singleCodePredicates = (new ArrayList<Predicate>());
|
||||||
if (StringUtils.isNotBlank(system)) {
|
if (StringUtils.isNotBlank(system)) {
|
||||||
singleCodePredicates.add(builder.equal(from.get("mySystem"), system));
|
singleCodePredicates.add(builder.equal(from.get("mySystem"), system));
|
||||||
|
} else if (system == null) {
|
||||||
|
// don't check the system
|
||||||
} else {
|
} else {
|
||||||
|
// If the system is "", we only match on null systems
|
||||||
singleCodePredicates.add(builder.isNull(from.get("mySystem")));
|
singleCodePredicates.add(builder.isNull(from.get("mySystem")));
|
||||||
}
|
}
|
||||||
if (StringUtils.isNotBlank(code)) {
|
if (StringUtils.isNotBlank(code)) {
|
||||||
|
|
|
@ -35,6 +35,17 @@ public class SearchParameterMap extends HashMap<String, List<List<IQueryParamete
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void add(String theName, IQueryParameterOr theOr) {
|
||||||
|
if (theOr == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!containsKey(theName)) {
|
||||||
|
put(theName, new ArrayList<List<IQueryParameterType>>());
|
||||||
|
}
|
||||||
|
|
||||||
|
get(theName).add(theOr.getValuesAsQueryTokens());
|
||||||
|
}
|
||||||
|
|
||||||
public void add(String theName, IQueryParameterType theParam) {
|
public void add(String theName, IQueryParameterType theParam) {
|
||||||
if (theParam == null) {
|
if (theParam == null) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -17,6 +17,8 @@ import ca.uhn.fhir.model.primitive.InstantDt;
|
||||||
@MappedSuperclass
|
@MappedSuperclass
|
||||||
public abstract class BaseHasResource {
|
public abstract class BaseHasResource {
|
||||||
|
|
||||||
|
private static final int MAX_TITLE_LENGTH = 100;
|
||||||
|
|
||||||
@Column(name = "RES_DELETED_AT", nullable = true)
|
@Column(name = "RES_DELETED_AT", nullable = true)
|
||||||
@Temporal(TemporalType.TIMESTAMP)
|
@Temporal(TemporalType.TIMESTAMP)
|
||||||
private Date myDeleted;
|
private Date myDeleted;
|
||||||
|
@ -33,6 +35,9 @@ public abstract class BaseHasResource {
|
||||||
@Lob()
|
@Lob()
|
||||||
private byte[] myResource;
|
private byte[] myResource;
|
||||||
|
|
||||||
|
@Column(name = "RES_TITLE", nullable = true, length = MAX_TITLE_LENGTH)
|
||||||
|
private String myTitle;
|
||||||
|
|
||||||
@Temporal(TemporalType.TIMESTAMP)
|
@Temporal(TemporalType.TIMESTAMP)
|
||||||
@Column(name = "RES_UPDATED", nullable = false)
|
@Column(name = "RES_UPDATED", nullable = false)
|
||||||
private Date myUpdated;
|
private Date myUpdated;
|
||||||
|
@ -61,6 +66,10 @@ public abstract class BaseHasResource {
|
||||||
|
|
||||||
public abstract Collection<? extends BaseTag> getTags();
|
public abstract Collection<? extends BaseTag> getTags();
|
||||||
|
|
||||||
|
public String getTitle() {
|
||||||
|
return myTitle;
|
||||||
|
}
|
||||||
|
|
||||||
public InstantDt getUpdated() {
|
public InstantDt getUpdated() {
|
||||||
return new InstantDt(myUpdated);
|
return new InstantDt(myUpdated);
|
||||||
}
|
}
|
||||||
|
@ -87,6 +96,10 @@ public abstract class BaseHasResource {
|
||||||
myResource = theResource;
|
myResource = theResource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setTitle(String theTitle) {
|
||||||
|
myTitle = theTitle;
|
||||||
|
}
|
||||||
|
|
||||||
public void setUpdated(Date theUpdated) {
|
public void setUpdated(Date theUpdated) {
|
||||||
myUpdated = theUpdated;
|
myUpdated = theUpdated;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,10 @@ import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "HFJ_SPIDX_TOKEN" /* , indexes = { @Index(name = "IDX_SP_TOKEN", columnList = "SP_SYSTEM,SP_VALUE") } */)
|
@Table(name = "HFJ_SPIDX_TOKEN" /* , indexes = { @Index(name = "IDX_SP_TOKEN", columnList = "SP_SYSTEM,SP_VALUE") } */)
|
||||||
@org.hibernate.annotations.Table(appliesTo = "HFJ_SPIDX_TOKEN", indexes = { @org.hibernate.annotations.Index(name = "IDX_SP_TOKEN", columnNames = { "RES_TYPE", "SP_NAME", "SP_SYSTEM", "SP_VALUE" }) })
|
@org.hibernate.annotations.Table(appliesTo = "HFJ_SPIDX_TOKEN", indexes = {
|
||||||
|
@org.hibernate.annotations.Index(name = "IDX_SP_TOKEN", columnNames = { "RES_TYPE", "SP_NAME", "SP_SYSTEM", "SP_VALUE" }),
|
||||||
|
@org.hibernate.annotations.Index(name = "IDX_SP_TOKEN_UNQUAL", columnNames = { "RES_TYPE", "SP_NAME", "SP_VALUE" })
|
||||||
|
})
|
||||||
public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchParam {
|
public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchParam {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
|
@ -252,6 +252,7 @@ public class ResourceTable extends BaseHasResource implements Serializable {
|
||||||
retVal.setResourceType(myResourceType);
|
retVal.setResourceType(myResourceType);
|
||||||
retVal.setVersion(myVersion);
|
retVal.setVersion(myVersion);
|
||||||
|
|
||||||
|
retVal.setTitle(getTitle());
|
||||||
retVal.setPublished(getPublished());
|
retVal.setPublished(getPublished());
|
||||||
retVal.setUpdated(getUpdated());
|
retVal.setUpdated(getUpdated());
|
||||||
retVal.setEncoding(getEncoding());
|
retVal.setEncoding(getEncoding());
|
||||||
|
|
|
@ -7,11 +7,13 @@ import ca.uhn.fhir.model.dstu.resource.Conformance;
|
||||||
import ca.uhn.fhir.model.dstu.resource.Conformance.Rest;
|
import ca.uhn.fhir.model.dstu.resource.Conformance.Rest;
|
||||||
import ca.uhn.fhir.model.dstu.resource.Conformance.RestResource;
|
import ca.uhn.fhir.model.dstu.resource.Conformance.RestResource;
|
||||||
import ca.uhn.fhir.model.primitive.DecimalDt;
|
import ca.uhn.fhir.model.primitive.DecimalDt;
|
||||||
|
import ca.uhn.fhir.model.primitive.StringDt;
|
||||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||||
import ca.uhn.fhir.rest.server.provider.ServerConformanceProvider;
|
import ca.uhn.fhir.rest.server.provider.ServerConformanceProvider;
|
||||||
|
|
||||||
public class JpaConformanceProvider extends ServerConformanceProvider {
|
public class JpaConformanceProvider extends ServerConformanceProvider {
|
||||||
|
|
||||||
|
private String myImplementationDescription;
|
||||||
private IFhirSystemDao mySystemDao;
|
private IFhirSystemDao mySystemDao;
|
||||||
|
|
||||||
public JpaConformanceProvider(RestfulServer theRestfulServer, IFhirSystemDao theSystemDao) {
|
public JpaConformanceProvider(RestfulServer theRestfulServer, IFhirSystemDao theSystemDao) {
|
||||||
|
@ -19,24 +21,29 @@ public class JpaConformanceProvider extends ServerConformanceProvider {
|
||||||
mySystemDao = theSystemDao;
|
mySystemDao = theSystemDao;
|
||||||
super.setCache(false);
|
super.setCache(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Conformance getServerConformance() {
|
public Conformance getServerConformance() {
|
||||||
|
|
||||||
Map<String, Long> counts = mySystemDao.getResourceCounts();
|
Map<String, Long> counts = mySystemDao.getResourceCounts();
|
||||||
|
|
||||||
Conformance retVal = super.getServerConformance();
|
Conformance retVal = super.getServerConformance();
|
||||||
for (Rest nextRest : retVal.getRest()) {
|
for (Rest nextRest : retVal.getRest()) {
|
||||||
for (RestResource nextResource : nextRest.getResource()) {
|
for (RestResource nextResource : nextRest.getResource()) {
|
||||||
Long count = counts.get(nextResource.getType().getValueAsString());
|
Long count = counts.get(nextResource.getType().getValueAsString());
|
||||||
if (count!=null) {
|
if (count != null) {
|
||||||
nextResource.addUndeclaredExtension(false, "http://hl7api.sourceforge.net/hapi-fhir/res/extdefs.html#resourceCount", new DecimalDt(count));
|
nextResource.addUndeclaredExtension(false, "http://hl7api.sourceforge.net/hapi-fhir/res/extdefs.html#resourceCount", new DecimalDt(count));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
retVal.getImplementation().setDescription(myImplementationDescription);
|
||||||
|
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setImplementationDescription(String theImplDesc) {
|
||||||
|
myImplementationDescription = theImplDesc;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,9 @@ import ca.uhn.fhir.model.primitive.InstantDt;
|
||||||
import ca.uhn.fhir.model.primitive.StringDt;
|
import ca.uhn.fhir.model.primitive.StringDt;
|
||||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
import ca.uhn.fhir.rest.client.exceptions.FhirClientConnectionException;
|
import ca.uhn.fhir.rest.client.exceptions.FhirClientConnectionException;
|
||||||
|
import ca.uhn.fhir.rest.gclient.IQuery;
|
||||||
import ca.uhn.fhir.rest.param.DateRangeParam;
|
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||||
|
import ca.uhn.fhir.rest.param.IdentifierListParam;
|
||||||
import ca.uhn.fhir.rest.param.QualifiedDateParam;
|
import ca.uhn.fhir.rest.param.QualifiedDateParam;
|
||||||
import ca.uhn.fhir.rest.param.ReferenceParam;
|
import ca.uhn.fhir.rest.param.ReferenceParam;
|
||||||
import ca.uhn.fhir.rest.param.StringParam;
|
import ca.uhn.fhir.rest.param.StringParam;
|
||||||
|
@ -122,6 +124,50 @@ public class FhirResourceDaoTest {
|
||||||
assertEquals(o1id.toUnqualifiedVersionless(), p1.getManagingOrganization().getReference().toUnqualifiedVersionless());
|
assertEquals(o1id.toUnqualifiedVersionless(), p1.getManagingOrganization().getReference().toUnqualifiedVersionless());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSearchTokenParam() {
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.addIdentifier("urn:system", "testSearchTokenParam001");
|
||||||
|
patient.addName().addFamily("Tester").addGiven("testSearchTokenParam1");
|
||||||
|
ourPatientDao.create(patient);
|
||||||
|
|
||||||
|
patient = new Patient();
|
||||||
|
patient.addIdentifier("urn:system", "testSearchTokenParam002");
|
||||||
|
patient.addName().addFamily("Tester").addGiven("testSearchTokenParam2");
|
||||||
|
ourPatientDao.create(patient);
|
||||||
|
|
||||||
|
{
|
||||||
|
SearchParameterMap map = new SearchParameterMap();
|
||||||
|
map.add(Patient.SP_IDENTIFIER, new IdentifierDt("urn:system", "testSearchTokenParam001"));
|
||||||
|
IBundleProvider retrieved = ourPatientDao.search(map);
|
||||||
|
assertEquals(1, retrieved.size());
|
||||||
|
}
|
||||||
|
{
|
||||||
|
SearchParameterMap map = new SearchParameterMap();
|
||||||
|
map.add(Patient.SP_IDENTIFIER, new IdentifierDt(null, "testSearchTokenParam001"));
|
||||||
|
IBundleProvider retrieved = ourPatientDao.search(map);
|
||||||
|
assertEquals(1, retrieved.size());
|
||||||
|
}
|
||||||
|
{
|
||||||
|
SearchParameterMap map = new SearchParameterMap();
|
||||||
|
IdentifierListParam listParam = new IdentifierListParam();
|
||||||
|
listParam.addIdentifier(new IdentifierDt("urn:system", "testSearchTokenParam001"));
|
||||||
|
listParam.addIdentifier(new IdentifierDt("urn:system", "testSearchTokenParam002"));
|
||||||
|
map.add(Patient.SP_IDENTIFIER, listParam);
|
||||||
|
IBundleProvider retrieved = ourPatientDao.search(map);
|
||||||
|
assertEquals(2, retrieved.size());
|
||||||
|
}
|
||||||
|
{
|
||||||
|
SearchParameterMap map = new SearchParameterMap();
|
||||||
|
IdentifierListParam listParam = new IdentifierListParam();
|
||||||
|
listParam.addIdentifier(new IdentifierDt(null, "testSearchTokenParam001"));
|
||||||
|
listParam.addIdentifier(new IdentifierDt("urn:system", "testSearchTokenParam002"));
|
||||||
|
map.add(Patient.SP_IDENTIFIER, listParam);
|
||||||
|
IBundleProvider retrieved = ourPatientDao.search(map);
|
||||||
|
assertEquals(2, retrieved.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testIdParam() {
|
public void testIdParam() {
|
||||||
Patient patient = new Patient();
|
Patient patient = new Patient();
|
||||||
|
@ -253,13 +299,12 @@ public class FhirResourceDaoTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSearchWithNoResults() {
|
public void testSearchWithNoResults() {
|
||||||
IBundleProvider value = ourObservationDao.search(new SearchParameterMap());
|
IBundleProvider value = ourDeviceDao.search(new SearchParameterMap());
|
||||||
|
for (IResource next : value.getResources(0, value.size())) {
|
||||||
|
ourDeviceDao.delete(next.getId());
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
value = ourDeviceDao.search(new SearchParameterMap());
|
||||||
* This may fail at some point, which means another test has probably added a device
|
|
||||||
* resource. This test depends on there being none, so if that happens this test
|
|
||||||
* should be refactored to use another resource type
|
|
||||||
*/
|
|
||||||
assertEquals(0, value.size());
|
assertEquals(0, value.size());
|
||||||
|
|
||||||
List<IResource> res = value.getResources(0, 0);
|
List<IResource> res = value.getResources(0, 0);
|
||||||
|
@ -361,6 +406,7 @@ public class FhirResourceDaoTest {
|
||||||
Patient patient = new Patient();
|
Patient patient = new Patient();
|
||||||
patient.addIdentifier("urn:system", "001");
|
patient.addIdentifier("urn:system", "001");
|
||||||
patient.addName().addFamily("testSearchNameParam01Fam").addGiven("testSearchNameParam01Giv");
|
patient.addName().addFamily("testSearchNameParam01Fam").addGiven("testSearchNameParam01Giv");
|
||||||
|
ResourceMetadataKeyEnum.TITLE.put(patient, "P1TITLE");
|
||||||
id1 = ourPatientDao.create(patient).getId();
|
id1 = ourPatientDao.create(patient).getId();
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
|
@ -375,6 +421,7 @@ public class FhirResourceDaoTest {
|
||||||
List<Patient> patients = toList(ourPatientDao.search(params));
|
List<Patient> patients = toList(ourPatientDao.search(params));
|
||||||
assertEquals(1, patients.size());
|
assertEquals(1, patients.size());
|
||||||
assertEquals(id1.getIdPart(), patients.get(0).getId().getIdPart());
|
assertEquals(id1.getIdPart(), patients.get(0).getId().getIdPart());
|
||||||
|
assertEquals("P1TITLE", ResourceMetadataKeyEnum.TITLE.get(patients.get(0)));
|
||||||
|
|
||||||
// Given name shouldn't return for family param
|
// Given name shouldn't return for family param
|
||||||
params = new HashMap<String, IQueryParameterType>();
|
params = new HashMap<String, IQueryParameterType>();
|
||||||
|
|
|
@ -148,6 +148,31 @@
|
||||||
<target>1.7</target>
|
<target>1.7</target>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
<!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself.-->
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.eclipse.m2e</groupId>
|
||||||
|
<artifactId>lifecycle-mapping</artifactId>
|
||||||
|
<version>1.0.0</version>
|
||||||
|
<configuration>
|
||||||
|
<lifecycleMappingMetadata>
|
||||||
|
<pluginExecutions>
|
||||||
|
<pluginExecution>
|
||||||
|
<pluginExecutionFilter>
|
||||||
|
<groupId></groupId>
|
||||||
|
<artifactId></artifactId>
|
||||||
|
<versionRange>[0.4,)</versionRange>
|
||||||
|
<goals>
|
||||||
|
<goal></goal>
|
||||||
|
</goals>
|
||||||
|
</pluginExecutionFilter>
|
||||||
|
<action>
|
||||||
|
<ignore></ignore>
|
||||||
|
</action>
|
||||||
|
</pluginExecution>
|
||||||
|
</pluginExecutions>
|
||||||
|
</lifecycleMappingMetadata>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</pluginManagement>
|
</pluginManagement>
|
||||||
<plugins>
|
<plugins>
|
||||||
|
|
|
@ -55,7 +55,10 @@ public class TestRestfulServer extends RestfulServer {
|
||||||
JpaSystemProvider sp = new JpaSystemProvider(systemDao);
|
JpaSystemProvider sp = new JpaSystemProvider(systemDao);
|
||||||
setPlainProviders(sp);
|
setPlainProviders(sp);
|
||||||
|
|
||||||
|
String implDesc = getInitParameter("ImplementationDescription");
|
||||||
|
|
||||||
JpaConformanceProvider confProvider = new JpaConformanceProvider(this, systemDao);
|
JpaConformanceProvider confProvider = new JpaConformanceProvider(this, systemDao);
|
||||||
|
confProvider.setImplementationDescription(implDesc);
|
||||||
setServerConformanceProvider(confProvider);
|
setServerConformanceProvider(confProvider);
|
||||||
|
|
||||||
setUseBrowserFriendlyContentTypes(true);
|
setUseBrowserFriendlyContentTypes(true);
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
<!-- <property name="url" value="jdbc:derby:directory:#{systemproperties['fhir.db.location']};create=true" /> -->
|
<!-- <property name="url" value="jdbc:derby:directory:#{systemproperties['fhir.db.location']};create=true" /> -->
|
||||||
<property name="driverClassName" value="org.apache.derby.jdbc.ClientDriver"></property>
|
<property name="driverClassName" value="org.apache.derby.jdbc.ClientDriver"></property>
|
||||||
<property name="url" value="jdbc:derby://localhost:1527//opt/glassfish/fhirtest/fhirtest;create=true" />
|
<property name="url" value="jdbc:derby://localhost:1527//opt/glassfish/fhirtest/fhirtest;create=true" />
|
||||||
|
<!--<property name="url" value="jdbc:derby://localhost:1527#{systemProperties['fhir.db.location']};create=true" />-->
|
||||||
<property name="username" value="SA"/>
|
<property name="username" value="SA"/>
|
||||||
<property name="password" value="SA"/>
|
<property name="password" value="SA"/>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xmlns:security="http://www.springframework.org/schema/security"
|
||||||
|
xmlns:oauth="http://www.springframework.org/schema/security/oauth2"
|
||||||
|
xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-2.0.xsd
|
||||||
|
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
|
||||||
|
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd
|
||||||
|
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
|
||||||
|
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
|
||||||
|
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd">
|
||||||
|
|
||||||
|
<!--
|
||||||
|
<bean class="ca.uhn.fhirtest.HsqldbServer" id="dbServer" init-method="start">
|
||||||
|
<constructor-arg>
|
||||||
|
<value>
|
||||||
|
server.database.0=file:#{systemProperties['fhir.db.location']}/hsql-fhir-db
|
||||||
|
server.dbname.0=uhnfhirdb
|
||||||
|
server.remote_open=true
|
||||||
|
hsqldb.reconfig_logging=false
|
||||||
|
hsqldb.default_table_type=cached
|
||||||
|
</value>
|
||||||
|
</constructor-arg>
|
||||||
|
</bean>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<bean id="dbServer" class="ca.uhn.fhirtest.DerbyNetworkServer">
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean depends-on="dbServer" id="myPersistenceDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
|
||||||
|
<!-- ;create=true /opt/glassfish/glassfish4/glassfish/nodes/localhost-domain1/fhirtest/fhirdb -->
|
||||||
|
<!-- <property name="url" value="jdbc:hsqldb:hsql://localhost/uhnfhirdb"/>-->
|
||||||
|
<!-- <property name="url" value="jdbc:derby:directory:#{systemproperties['fhir.db.location']};create=true" /> -->
|
||||||
|
<property name="driverClassName" value="org.apache.derby.jdbc.ClientDriver"></property>
|
||||||
|
<<<<<<< HEAD
|
||||||
|
<property name="url" value="jdbc:derby://localhost:1527//opt/glassfish/fhirtest/fhirtest;create=true" />
|
||||||
|
=======
|
||||||
|
<property name="url" value="jdbc:derby://localhost:1527#{systemProperties['fhir.db.location']};create=true" />
|
||||||
|
>>>>>>> ca0929df07b8d435a3b756f89998f68e2aae79fc
|
||||||
|
<property name="username" value="SA"/>
|
||||||
|
<property name="password" value="SA"/>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean depends-on="dbServer" id="myEntityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
|
||||||
|
<property name="dataSource" ref="myPersistenceDataSource" />
|
||||||
|
<property name="persistenceXmlLocation" value="classpath:META-INF/fhirtest_persistence.xml" />
|
||||||
|
<property name="persistenceUnitName" value="FHIR_UT" />
|
||||||
|
<property name="jpaVendorAdapter">
|
||||||
|
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
|
||||||
|
<property name="showSql" value="false" />
|
||||||
|
<property name="generateDdl" value="true" />
|
||||||
|
<property name="databasePlatform" value="org.hibernate.dialect.DerbyTenSevenDialect" />
|
||||||
|
<!-- <property name="databasePlatform" value="org.hibernate.dialect.HSQLDialect" />-->
|
||||||
|
</bean>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
</beans>
|
|
@ -1,14 +1,31 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<div th:fragment="banner" class="well">
|
<div th:fragment="banner" class="well">
|
||||||
|
<th:block th:if="${serverId} == 'home'">
|
||||||
|
<p>
|
||||||
|
This is the home for the FHIR test server operated by
|
||||||
|
<a href="http://uhn.ca">University Health Network</a>. This server
|
||||||
|
(and the testing application you are currently using to access it)
|
||||||
|
is entirely built using
|
||||||
|
<a href="https://github.com/jamesagnew/hapi-fhir">HAPI-FHIR</a>,
|
||||||
|
a 100% open-source Java implementation of the
|
||||||
|
<a href="http://hl7.org/implement/standards/fhir/">FHIR specification</a>.
|
||||||
|
</p>
|
||||||
|
</th:block>
|
||||||
|
<th:block th:if="${serverId} != 'home'">
|
||||||
|
<p>
|
||||||
|
You are accessing the public FHIR server
|
||||||
|
<b th:text="${baseName}"/>. This server is hosted elsewhere on the internet
|
||||||
|
but is being accessed using the HAPI client implementation.
|
||||||
|
</p>
|
||||||
|
</th:block>
|
||||||
<p>
|
<p>
|
||||||
This is the home for the FHIR test server operated by
|
<b style="color: red;">
|
||||||
<a href="http://uhn.ca">University Health Network</a>.
|
<span class="glyphicon glyphicon-warning-sign"/>
|
||||||
</p>
|
This is not a production server!
|
||||||
<p>
|
</b>
|
||||||
<b style="color: red;">This is not a production server!</b>
|
|
||||||
Do not store any information here that contains personal health information
|
Do not store any information here that contains personal health information
|
||||||
or otherwise confidential information. This server will be regularly purged
|
or any other confidential information. This server will be regularly purged
|
||||||
and reloaded with fixed test data.
|
and reloaded with fixed test data.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<div th:fragment="banner" class="well">
|
||||||
|
<th:block th:if="${serverEntry.key} == 'home'">
|
||||||
|
<p>
|
||||||
|
This is the home for the FHIR test server operated by
|
||||||
|
<a href="http://uhn.ca">University Health Network</a>. This server
|
||||||
|
(and the testing application you are currently using to access it)
|
||||||
|
is entirely built using
|
||||||
|
<a href="https://github.com/jamesagnew/hapi-fhir">HAPI-FHIR</a>,
|
||||||
|
a 100% open-source Java implementation of the
|
||||||
|
<a href="http://hl7.org/implement/standards/fhir/">FHIR specification</a>.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Here are some things you might wish to try:
|
||||||
|
</p>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
View a
|
||||||
|
<a href="http://fhirtest.uhn.ca/search?serverId=home&encoding=json&pretty=true&resource=Patient&param.0.type=string&param.0.name=_id&param.0.0=&resource-search-limit=">list of patients</a>
|
||||||
|
on this server.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Construct a
|
||||||
|
<a href="http://fhirtest.uhn.ca/resource?serverId=home&encoding=json&pretty=true&resource=Patient">search query</a>
|
||||||
|
on this server.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Access a
|
||||||
|
<a href="http://fhirtest.uhn.ca/home?serverId=furore">different server</a>
|
||||||
|
(use the <b>Server</b> menu at the top of the page to see a list of public FHIR servers)
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</th:block>
|
||||||
|
<th:block th:if="${serverEntry.key} != 'home'">
|
||||||
|
<p>
|
||||||
|
You are accessing the public FHIR server
|
||||||
|
<b th:text="${baseName}"/>. This server is hosted elsewhere on the internet
|
||||||
|
but is being accessed using
|
||||||
|
</p>
|
||||||
|
</th:block>
|
||||||
|
<p>
|
||||||
|
<b style="color: red;">
|
||||||
|
<span class="glyphicon glyphicon-warning-sign/>
|
||||||
|
This is not a production server!
|
||||||
|
</b>
|
||||||
|
Do not store any information here that contains personal health information
|
||||||
|
or any other confidential information. This server will be regularly purged
|
||||||
|
and reloaded with fixed test data.
|
||||||
|
</p>
|
||||||
|
<<<<<<< HEAD
|
||||||
|
<p>
|
||||||
|
Here are some things you might wish to try:
|
||||||
|
</p>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
View a
|
||||||
|
<a href="http://fhirtest.uhn.ca/search?serverId=home&encoding=json&pretty=true&resource=Patient&param.0.type=string&param.0.name=_id&param.0.0=&resource-search-limit=">list of patients</a>
|
||||||
|
on this server.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Construct a
|
||||||
|
<a href="http://fhirtest.uhn.ca/resource?serverId=home&encoding=json&pretty=true&resource=Patient">search query</a>
|
||||||
|
on this server.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Access a
|
||||||
|
<a href="http://fhirtest.uhn.ca/home?serverId=furore">different server</a>
|
||||||
|
(use the <b>Server</b> menu at the top of the page to see a list of public FHIR servers)
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
=======
|
||||||
|
>>>>>>> ca0929df07b8d435a3b756f89998f68e2aae79fc
|
||||||
|
</div>
|
||||||
|
</html>
|
|
@ -35,6 +35,10 @@
|
||||||
<servlet>
|
<servlet>
|
||||||
<servlet-name>fhirServlet</servlet-name>
|
<servlet-name>fhirServlet</servlet-name>
|
||||||
<servlet-class>ca.uhn.fhirtest.TestRestfulServer</servlet-class>
|
<servlet-class>ca.uhn.fhirtest.TestRestfulServer</servlet-class>
|
||||||
|
<init-param>
|
||||||
|
<param-name>ImplementationDescription</param-name>
|
||||||
|
<param-value>UHN Test Server</param-value>
|
||||||
|
</init-param>
|
||||||
<load-on-startup>1</load-on-startup>
|
<load-on-startup>1</load-on-startup>
|
||||||
</servlet>
|
</servlet>
|
||||||
|
|
||||||
|
|
|
@ -75,8 +75,6 @@
|
||||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"
|
||||||
schemaLocation="xml.xsd"/>
|
schemaLocation="xml.xsd"/>
|
||||||
|
|
||||||
<xsd:include schemaLocation="javaee_web_services_client_1_3.xsd"/>
|
|
||||||
|
|
||||||
<xsd:group name="descriptionGroup">
|
<xsd:group name="descriptionGroup">
|
||||||
<xsd:annotation>
|
<xsd:annotation>
|
||||||
<xsd:documentation>
|
<xsd:documentation>
|
||||||
|
|
|
@ -23,7 +23,7 @@ public class UhnFhirTestApp {
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
|
|
||||||
// new File("target/testdb").mkdirs();
|
// new File("target/testdb").mkdirs();
|
||||||
System.setProperty("fhir.db.location", "target/testdb");
|
System.setProperty("fhir.db.location", "/target/testdb");
|
||||||
|
|
||||||
int myPort = 8888;
|
int myPort = 8888;
|
||||||
Server server = new Server(myPort);
|
Server server = new Server(myPort);
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
<wb-resource deploy-path="/" source-path="/target/m2e-wtp/web-resources"/>
|
<wb-resource deploy-path="/" source-path="/target/m2e-wtp/web-resources"/>
|
||||||
<wb-resource deploy-path="/" source-path="/src/main/webapp" tag="defaultRootSource"/>
|
<wb-resource deploy-path="/" source-path="/src/main/webapp" tag="defaultRootSource"/>
|
||||||
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src/main/java"/>
|
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src/main/java"/>
|
||||||
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src/main/resources"/>
|
|
||||||
<dependent-module archiveName="hapi-fhir-base-0.4-SNAPSHOT.jar" deploy-path="/WEB-INF/lib" handle="module:/resource/hapi-fhir-base/hapi-fhir-base">
|
<dependent-module archiveName="hapi-fhir-base-0.4-SNAPSHOT.jar" deploy-path="/WEB-INF/lib" handle="module:/resource/hapi-fhir-base/hapi-fhir-base">
|
||||||
<dependency-type>uses</dependency-type>
|
<dependency-type>uses</dependency-type>
|
||||||
</dependent-module>
|
</dependent-module>
|
||||||
|
|
|
@ -31,10 +31,10 @@
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>javax.servlet</groupId>
|
<groupId>javax.servlet</groupId>
|
||||||
<artifactId>javax.servlet-api</artifactId>
|
<artifactId>javax.servlet-api</artifactId>
|
||||||
<version>3.0.1</version>
|
<version>3.1.0</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|
|
@ -50,6 +50,7 @@ import ca.uhn.fhir.rest.server.Constants;
|
||||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||||
import ca.uhn.fhir.to.model.HomeRequest;
|
import ca.uhn.fhir.to.model.HomeRequest;
|
||||||
import ca.uhn.fhir.to.model.ResourceRequest;
|
import ca.uhn.fhir.to.model.ResourceRequest;
|
||||||
|
import ca.uhn.fhir.to.model.TransactionRequest;
|
||||||
|
|
||||||
@org.springframework.stereotype.Controller()
|
@org.springframework.stereotype.Controller()
|
||||||
public class Controller {
|
public class Controller {
|
||||||
|
@ -67,6 +68,39 @@ public class Controller {
|
||||||
@Autowired
|
@Autowired
|
||||||
private TemplateEngine myTemplateEngine;
|
private TemplateEngine myTemplateEngine;
|
||||||
|
|
||||||
|
@RequestMapping(value = { "/transaction" })
|
||||||
|
public String actionTransaction(final TransactionRequest theRequest, final BindingResult theBindingResult, final ModelMap theModel) {
|
||||||
|
addCommonParams(theRequest, theModel);
|
||||||
|
|
||||||
|
GenericClient client = theRequest.newClient(myCtx, myConfig);
|
||||||
|
|
||||||
|
String body = preProcessMessageBody(theRequest.getTransactionBody());
|
||||||
|
|
||||||
|
Bundle bundle;
|
||||||
|
try {
|
||||||
|
if (body.startsWith("{")) {
|
||||||
|
bundle = myCtx.newJsonParser().parseBundle(body);
|
||||||
|
} else if (body.startsWith("<")) {
|
||||||
|
bundle = myCtx.newXmlParser().parseBundle(body);
|
||||||
|
} else {
|
||||||
|
theModel.put("errorMsg", "Message body does not appear to be a valid FHIR resource instance document. Body should start with '<' (for XML encoding) or '{' (for JSON encoding).");
|
||||||
|
return "home";
|
||||||
|
}
|
||||||
|
} catch (DataFormatException e) {
|
||||||
|
ourLog.warn("Failed to parse bundle", e);
|
||||||
|
theModel.put("errorMsg", "Failed to parse transaction bundle body. Error was: " + e.getMessage());
|
||||||
|
return "home";
|
||||||
|
}
|
||||||
|
|
||||||
|
long start = System.currentTimeMillis();
|
||||||
|
// client.tr
|
||||||
|
long delay = System.currentTimeMillis() - start;
|
||||||
|
|
||||||
|
processAndAddLastClientInvocation(client, ResultType.RESOURCE, theModel, delay, "Loaded conformance");
|
||||||
|
|
||||||
|
return "result";
|
||||||
|
}
|
||||||
|
|
||||||
@RequestMapping(value = { "/conformance" })
|
@RequestMapping(value = { "/conformance" })
|
||||||
public String actionConformance(final HomeRequest theRequest, final BindingResult theBindingResult, final ModelMap theModel) {
|
public String actionConformance(final HomeRequest theRequest, final BindingResult theBindingResult, final ModelMap theModel) {
|
||||||
addCommonParams(theRequest, theModel);
|
addCommonParams(theRequest, theModel);
|
||||||
|
@ -427,18 +461,7 @@ public class Controller {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
body = body.trim();
|
body = preProcessMessageBody(body);
|
||||||
|
|
||||||
StringBuilder b = new StringBuilder();
|
|
||||||
for (int i = 0; i < body.length(); i++) {
|
|
||||||
char nextChar = body.charAt(i);
|
|
||||||
int nextCharI = nextChar;
|
|
||||||
if (nextCharI == 65533) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
b.append(nextChar);
|
|
||||||
}
|
|
||||||
body = b.toString();
|
|
||||||
|
|
||||||
IResource resource;
|
IResource resource;
|
||||||
try {
|
try {
|
||||||
|
@ -485,6 +508,25 @@ public class Controller {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String preProcessMessageBody(String theBody) {
|
||||||
|
if(theBody==null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
String retVal = theBody.trim();
|
||||||
|
|
||||||
|
StringBuilder b = new StringBuilder();
|
||||||
|
for (int i = 0; i < retVal.length(); i++) {
|
||||||
|
char nextChar = retVal.charAt(i);
|
||||||
|
int nextCharI = nextChar;
|
||||||
|
if (nextCharI == 65533) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
b.append(nextChar);
|
||||||
|
}
|
||||||
|
retVal = b.toString();
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
private void doActionHistory(HttpServletRequest theReq, HomeRequest theRequest, BindingResult theBindingResult, ModelMap theModel, String theMethod, String theMethodDescription) {
|
private void doActionHistory(HttpServletRequest theReq, HomeRequest theRequest, BindingResult theBindingResult, ModelMap theModel, String theMethod, String theMethodDescription) {
|
||||||
addCommonParams(theRequest, theModel);
|
addCommonParams(theRequest, theModel);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
package ca.uhn.fhir.to.model;
|
||||||
|
|
||||||
|
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||||
|
|
||||||
|
public class TransactionRequest extends HomeRequest {
|
||||||
|
|
||||||
|
private String myTransactionBody;
|
||||||
|
|
||||||
|
@ModelAttribute("transactionBody")
|
||||||
|
public String getTransactionBody() {
|
||||||
|
return myTransactionBody;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTransactionBody(String theTransactionBody) {
|
||||||
|
myTransactionBody = theTransactionBody;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -11,6 +11,7 @@
|
||||||
<bean class="ca.uhn.fhir.to.TesterConfig">
|
<bean class="ca.uhn.fhir.to.TesterConfig">
|
||||||
<property name="servers">
|
<property name="servers">
|
||||||
<list>
|
<list>
|
||||||
|
<value>test, TEST, http://uhnvesb01d.uhn.on.ca:25180/uhn-fhir-service-1.2/</value>
|
||||||
<value>home , Localhost Server , http://localhost:8887/fhir/context </value>
|
<value>home , Localhost Server , http://localhost:8887/fhir/context </value>
|
||||||
<value>hi , Health Intersections , http://fhir.healthintersections.com.au/open</value>
|
<value>hi , Health Intersections , http://fhir.healthintersections.com.au/open</value>
|
||||||
<value>furore , Spark - Furore Reference Server , http://spark.furore.com/fhir</value>
|
<value>furore , Spark - Furore Reference Server , http://spark.furore.com/fhir</value>
|
||||||
|
@ -21,12 +22,5 @@
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<bean id="fhirContext" class="ca.uhn.fhir.context.FhirContext">
|
<bean id="fhirContext" class="ca.uhn.fhir.context.FhirContext">
|
||||||
<property name="restfulClientFactory" ref="restfulClientFactory"/>
|
|
||||||
</bean>
|
|
||||||
|
|
||||||
<bean id="restfulClientFactory" class="ca.uhn.fhir.rest.client.RestfulClientFactory">
|
|
||||||
<property name="fhirContext" ref="fhirContext"/>
|
|
||||||
<property name="connectTimeout" value="4000"/>
|
|
||||||
<property name="socketTimeout" value="10000"/>
|
|
||||||
</bean>
|
</bean>
|
||||||
</beans>
|
</beans>
|
Binary file not shown.
|
@ -31,22 +31,22 @@
|
||||||
<col class="col-xs-7" />
|
<col class="col-xs-7" />
|
||||||
</colgroup>
|
</colgroup>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr th:if="${conf.implementation.description} != null and #{!string.isEmpty(conf.implementation.description)}">
|
<tr th:if="#{!strings.isEmpty(conf.implementation.description.value)}">
|
||||||
<td>Server</td>
|
<td>Server</td>
|
||||||
<td th:utext="'' + ${conf.implementation.description}">HAPI Restful Server</td>
|
<td th:utext="'' + ${conf.implementation.description}">HAPI Restful Server</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr th:if="#{!strings.isEmpty(conf.software.name.value)} or #{!strings.isEmpty(conf.software.version.value)}">
|
||||||
|
<td>Software</td>
|
||||||
|
<td>
|
||||||
|
<th:block th:utext="'' + ${conf.software.name}"/> - <th:block th:utext="'' + ${conf.software.version}"/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>FHIR Base</td>
|
<td>FHIR Base</td>
|
||||||
<td>
|
<td>
|
||||||
<a th:href="${base}" th:text="${base}"></a>
|
<a th:href="${base}" th:text="${base}"></a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr th:if="#{string.isEmpty(conf.software.name)} == false and #{string.isEmpty(conf.software.version)} == false">
|
|
||||||
<td>Software</td>
|
|
||||||
<td>
|
|
||||||
<th:block th:utext="'' + ${conf.software.name}"/> - <th:block th:utext="'' + ${conf.software.version}"/>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@
|
||||||
Retrieve the server's <b>conformance</b> statement.
|
Retrieve the server's <b>conformance</b> statement.
|
||||||
</div>
|
</div>
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="col-sm-2">
|
<div class="col-sm-2 form-group">
|
||||||
<button type="button" id="fetch-conformance-btn"
|
<button type="button" id="fetch-conformance-btn"
|
||||||
data-loading-text="Loading <i class='fa fa-spinner fa-spin'/>" class="btn btn-primary btn-block">
|
data-loading-text="Loading <i class='fa fa-spinner fa-spin'/>" class="btn btn-primary btn-block">
|
||||||
<i class="fa fa-dot-circle-o"></i>
|
<i class="fa fa-dot-circle-o"></i>
|
||||||
|
@ -144,6 +144,61 @@
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Transaction -->
|
||||||
|
|
||||||
|
<br clear="all"/>
|
||||||
|
<div class="row-fluid">
|
||||||
|
Post a bundle containing multiple resources to the server and
|
||||||
|
store all resources within a single atomic transaction.
|
||||||
|
</div>
|
||||||
|
<div class="row-fluid">
|
||||||
|
<div class="col-sm-2">
|
||||||
|
<button type="button" id="transaction-btn"
|
||||||
|
data-loading-text="Processing <i class='fa fa-spinner fa-spin'/>" class="btn btn-primary btn-block">
|
||||||
|
<i class="fa fa-files-o"></i>
|
||||||
|
Transaction
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class='col-sm-10'>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class='input-group'>
|
||||||
|
<div class="input-group-addon">
|
||||||
|
Bundle
|
||||||
|
<span class="loadingStar">*</span>
|
||||||
|
</div>
|
||||||
|
<textarea class="form-control" id="transaction-body" style="white-space: nowrap; overflow: auto;" placeholder="(place transaction bundle body here)" rows="1">
|
||||||
|
<th:block th:if="${transactionBundle} != null" th:text="${transactionBundle}"/>
|
||||||
|
</textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script type="text/javascript">
|
||||||
|
var textAreaChanger = function() {
|
||||||
|
createBodyOriginalHeight = $('#transaction-body').height();
|
||||||
|
$('#transaction-body').animate({height: "200px"}, 500);
|
||||||
|
}
|
||||||
|
$('#transaction-body').focus(textAreaChanger);
|
||||||
|
$('#transaction-btn').click(
|
||||||
|
function() {
|
||||||
|
var btn = $(this);
|
||||||
|
btn.button('loading');
|
||||||
|
var id = $('#transaction-id').val();
|
||||||
|
if (id != null) btn.append($('<input />', { type: 'hidden', name: 'resource-create-id', value: id }));
|
||||||
|
var body = $('#transaction-body').val();
|
||||||
|
btn.append($('<input />', { type: 'hidden', name: 'transactionBody', value: body }));
|
||||||
|
$("#outerForm").attr("method", "post");
|
||||||
|
$("#outerForm").attr("action", "create").submit();
|
||||||
|
});
|
||||||
|
$( document ).ready(function() {
|
||||||
|
/* if ($('#resource-create-id').val() != "") {
|
||||||
|
buttonChanger();
|
||||||
|
textAreaChanger();
|
||||||
|
$('#transaction-body').focus();
|
||||||
|
}
|
||||||
|
*/ });
|
||||||
|
</script>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Get Tags -->
|
<!-- Get Tags -->
|
||||||
|
|
||||||
<br clear="all"/>
|
<br clear="all"/>
|
||||||
|
@ -151,7 +206,7 @@
|
||||||
Show all of the tags currently in use on the server
|
Show all of the tags currently in use on the server
|
||||||
</div>
|
</div>
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="col-sm-2">
|
<div class="col-sm-2 form-group">
|
||||||
<button type="button" id="get-server-tags-btn"
|
<button type="button" id="get-server-tags-btn"
|
||||||
data-loading-text="Loading <i class='fa fa-spinner fa-spin'/>" class="btn btn-primary btn-block">
|
data-loading-text="Loading <i class='fa fa-spinner fa-spin'/>" class="btn btn-primary btn-block">
|
||||||
<i class="fa fa-tags"></i>
|
<i class="fa fa-tags"></i>
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<div th:fragment="banner" class="well">
|
<div th:fragment="banner" class="well">
|
||||||
<div>
|
<div>
|
||||||
This is a RESTful server tester, which can be used to send
|
This is a RESTful server tester, which can be used to send
|
||||||
requests to, and receive responses from the server.
|
requests to, and receive responses from the server.
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -59,7 +59,7 @@
|
||||||
|
|
||||||
<ul class="nav nav-sidebar">
|
<ul class="nav nav-sidebar">
|
||||||
<li th:class="${resourceName.empty} ? 'active' : ''">
|
<li th:class="${resourceName.empty} ? 'active' : ''">
|
||||||
<a href="#" onclick="doAction(this, 'home', null);">Server Home</a>
|
<a href="#" onclick="doAction(this, 'home', null);">Server Home/Actions</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@ public class ${className}ResourceProvider extends JpaResourceProvider<${classNam
|
||||||
#if (${param.type} == 'string' )
|
#if (${param.type} == 'string' )
|
||||||
StringParam the${param.nameCapitalized},
|
StringParam the${param.nameCapitalized},
|
||||||
#elseif (${param.type} == 'token' )
|
#elseif (${param.type} == 'token' )
|
||||||
IdentifierDt the${param.nameCapitalized},
|
IdentifierListParam the${param.nameCapitalized},
|
||||||
#elseif (${param.type} == 'date' )
|
#elseif (${param.type} == 'date' )
|
||||||
DateRangeParam the${param.nameCapitalized},
|
DateRangeParam the${param.nameCapitalized},
|
||||||
#elseif (${param.type} == 'quantity' )
|
#elseif (${param.type} == 'quantity' )
|
||||||
|
|
|
@ -102,6 +102,35 @@
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
|
<pluginManagement>
|
||||||
|
<plugins>
|
||||||
|
<!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself.-->
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.eclipse.m2e</groupId>
|
||||||
|
<artifactId>lifecycle-mapping</artifactId>
|
||||||
|
<version>1.0.0</version>
|
||||||
|
<configuration>
|
||||||
|
<lifecycleMappingMetadata>
|
||||||
|
<pluginExecutions>
|
||||||
|
<pluginExecution>
|
||||||
|
<pluginExecutionFilter>
|
||||||
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
|
<artifactId>hapi-tinder-plugin</artifactId>
|
||||||
|
<versionRange>[0.4-SNAPSHOT,)</versionRange>
|
||||||
|
<goals>
|
||||||
|
<goal>generate-structures</goal>
|
||||||
|
</goals>
|
||||||
|
</pluginExecutionFilter>
|
||||||
|
<action>
|
||||||
|
<ignore></ignore>
|
||||||
|
</action>
|
||||||
|
</pluginExecution>
|
||||||
|
</pluginExecutions>
|
||||||
|
</lifecycleMappingMetadata>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</pluginManagement>
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
|
Loading…
Reference in New Issue