More work on tester
This commit is contained in:
parent
0a7f7d0390
commit
a2a1035003
|
@ -71,6 +71,15 @@
|
|||
<action type="fix">
|
||||
Server now automatically compresses responses if the client indicates support
|
||||
</action>
|
||||
<action type="fix">
|
||||
Server failed to support optional parameters when type is String and :exact qualifier is used
|
||||
</action>
|
||||
<action type="fix">
|
||||
Read method in client correctly populated resource ID in returned object
|
||||
</action>
|
||||
<action type="add">
|
||||
Support added for deleted-entry by/name, by/email, and comment from Tombstones spec
|
||||
</action>
|
||||
</release>
|
||||
</body>
|
||||
</document>
|
||||
|
|
|
@ -58,13 +58,12 @@ public class Bundle extends BaseBundle /* implements IElement */{
|
|||
private IntegerDt myTotalResults;
|
||||
private InstantDt myUpdated;
|
||||
|
||||
/**
|
||||
* Returns true if this bundle contains zero entries
|
||||
*/
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
//@formatter:off
|
||||
return super.isEmpty() &&
|
||||
ElementUtil.isEmpty(myBundleId, myLinkBase, myLinkFirst, myLinkLast, myLinkNext, myLinkPrevious, myLinkSelf, myPublished, myTitle, myTotalResults) &&
|
||||
ElementUtil.isEmpty(myEntries);
|
||||
//@formatter:on
|
||||
return getEntries().isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -38,6 +38,9 @@ public class BundleEntry extends BaseBundle {
|
|||
//@formatter:on
|
||||
private TagList myCategories;
|
||||
private InstantDt myDeletedAt;
|
||||
private StringDt myDeletedByEmail;
|
||||
private StringDt myDeletedByName;
|
||||
private StringDt myDeletedComment;
|
||||
private StringDt myLinkAlternate;
|
||||
private StringDt myLinkSelf;
|
||||
private InstantDt myPublished;
|
||||
|
@ -52,18 +55,6 @@ public class BundleEntry extends BaseBundle {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
|
||||
if (getResource() != null) {
|
||||
b.append("type", getResource().getClass().getSimpleName());
|
||||
} else {
|
||||
b.append("No resource");
|
||||
}
|
||||
b.append("id", getId());
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
public void addCategory(Tag theTag) {
|
||||
getCategories().add(theTag);
|
||||
}
|
||||
|
@ -85,6 +76,27 @@ public class BundleEntry extends BaseBundle {
|
|||
return myDeletedAt;
|
||||
}
|
||||
|
||||
public StringDt getDeletedByEmail() {
|
||||
if (myDeletedByEmail == null) {
|
||||
myDeletedByEmail = new StringDt();
|
||||
}
|
||||
return myDeletedByEmail;
|
||||
}
|
||||
|
||||
public StringDt getDeletedByName() {
|
||||
if (myDeletedByName == null) {
|
||||
myDeletedByName = new StringDt();
|
||||
}
|
||||
return myDeletedByName;
|
||||
}
|
||||
|
||||
public StringDt getDeletedComment() {
|
||||
if (myDeletedComment == null) {
|
||||
myDeletedComment = new StringDt();
|
||||
}
|
||||
return myDeletedComment;
|
||||
}
|
||||
|
||||
public StringDt getLinkAlternate() {
|
||||
if (myLinkAlternate == null) {
|
||||
myLinkAlternate = new StringDt();
|
||||
|
@ -135,7 +147,7 @@ public class BundleEntry extends BaseBundle {
|
|||
public boolean isEmpty() {
|
||||
//@formatter:off
|
||||
return super.isEmpty() &&
|
||||
ElementUtil.isEmpty(myCategories, myDeletedAt, myLinkAlternate, myLinkSelf, myPublished, myResource, mySummary, myTitle, myUpdated);
|
||||
ElementUtil.isEmpty(myCategories, myDeletedAt, myLinkAlternate, myLinkSelf, myPublished, myResource, mySummary, myTitle, myUpdated, myDeletedByEmail, myDeletedByName, myDeletedComment);
|
||||
//@formatter:on
|
||||
}
|
||||
|
||||
|
@ -146,6 +158,21 @@ public class BundleEntry extends BaseBundle {
|
|||
myDeletedAt = theDeletedAt;
|
||||
}
|
||||
|
||||
public void setDeletedByEmail(StringDt theDeletedByEmail) {
|
||||
myDeletedByEmail = theDeletedByEmail;
|
||||
}
|
||||
|
||||
public void setDeletedByName(StringDt theDeletedByName) {
|
||||
if (myDeletedByName == null) {
|
||||
myDeletedByName = new StringDt();
|
||||
}
|
||||
myDeletedByName = theDeletedByName;
|
||||
}
|
||||
|
||||
public void setDeletedComment(StringDt theDeletedComment) {
|
||||
myDeletedComment = theDeletedComment;
|
||||
}
|
||||
|
||||
public void setLinkAlternate(StringDt theLinkAlternate) {
|
||||
myLinkAlternate = theLinkAlternate;
|
||||
}
|
||||
|
@ -171,4 +198,16 @@ public class BundleEntry extends BaseBundle {
|
|||
myUpdated = theUpdated;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
|
||||
if (getResource() != null) {
|
||||
b.append("type", getResource().getClass().getSimpleName());
|
||||
} else {
|
||||
b.append("No resource");
|
||||
}
|
||||
b.append("id", getId());
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -525,10 +525,14 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
|
|||
|
||||
final IStandardExpression expression = expressionParser.parseExpression(configuration, theArguments, attributeValue);
|
||||
final Object value = expression.execute(configuration, theArguments);
|
||||
|
||||
|
||||
theElement.removeAttribute(theAttributeName);
|
||||
theElement.clearChildren();
|
||||
|
||||
if (value == null) {
|
||||
return ProcessorResult.ok();
|
||||
}
|
||||
|
||||
Context context = new Context();
|
||||
context.setVariable("resource", value);
|
||||
|
||||
|
|
|
@ -157,8 +157,7 @@ class ParserState<T> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Invoked after any new XML event is individually processed, containing a copy of the XML event. This is basically
|
||||
* intended for embedded XHTML content
|
||||
* Invoked after any new XML event is individually processed, containing a copy of the XML event. This is basically intended for embedded XHTML content
|
||||
*/
|
||||
public void xmlEvent(XMLEvent theNextEvent) {
|
||||
myState.xmlEvent(theNextEvent);
|
||||
|
@ -238,8 +237,7 @@ class ParserState<T> {
|
|||
myInstance.setScheme(theValue);
|
||||
} else if ("value".equals(theName)) {
|
||||
/*
|
||||
* This handles XML parsing, which is odd for this quasi-resource type, since the tag has three values
|
||||
* instead of one like everything else.
|
||||
* This handles XML parsing, which is odd for this quasi-resource type, since the tag has three values instead of one like everything else.
|
||||
*/
|
||||
switch (myCatState) {
|
||||
case STATE_LABEL:
|
||||
|
@ -299,6 +297,23 @@ class ParserState<T> {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException {
|
||||
if ("by".equals(theLocalPart) && verifyNamespace(XmlParser.TOMBSTONES_NS, theNamespaceURI)) {
|
||||
push(new AtomDeletedEntryByState(getEntry()));
|
||||
} else if ("comment".equals(theLocalPart)) {
|
||||
push(new AtomPrimitiveState(getEntry().getDeletedComment()));
|
||||
} else if ("link".equals(theLocalPart)) {
|
||||
push(new AtomLinkState(getEntry()));
|
||||
} else {
|
||||
if (theNamespaceURI != null) {
|
||||
throw new DataFormatException("Unexpected element: {" + theNamespaceURI + "}" + theLocalPart);
|
||||
} else {
|
||||
throw new DataFormatException("Unexpected element: " + theLocalPart);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endingElement() throws DataFormatException {
|
||||
putPlacerResourceInDeletedEntry(getEntry());
|
||||
|
@ -307,6 +322,33 @@ class ParserState<T> {
|
|||
|
||||
}
|
||||
|
||||
public class AtomDeletedEntryByState extends BaseState {
|
||||
|
||||
private BundleEntry myEntry;
|
||||
|
||||
public AtomDeletedEntryByState(BundleEntry theEntry) {
|
||||
super(null);
|
||||
myEntry = theEntry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException {
|
||||
if ("name".equals(theLocalPart)) {
|
||||
push(new AtomPrimitiveState(myEntry.getDeletedByName()));
|
||||
} else if ("email".equals(theLocalPart)) {
|
||||
push(new AtomPrimitiveState(myEntry.getDeletedByEmail()));
|
||||
} else {
|
||||
throw new DataFormatException("Unexpected element in entry: " + theLocalPart);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endingElement() throws DataFormatException {
|
||||
pop();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class AtomDeletedJsonWhenState extends BaseState {
|
||||
|
||||
private String myData;
|
||||
|
|
|
@ -143,13 +143,32 @@ public class XmlParser extends BaseParser implements IParser {
|
|||
}
|
||||
|
||||
for (BundleEntry nextEntry : theBundle.getEntries()) {
|
||||
boolean deleted=false;
|
||||
if (nextEntry.getDeletedAt() != null && nextEntry.getDeletedAt().isEmpty()==false) {
|
||||
deleted=true;
|
||||
eventWriter.writeStartElement("at","deleted-entry",TOMBSTONES_NS);
|
||||
boolean deleted = false;
|
||||
if (nextEntry.getDeletedAt() != null && nextEntry.getDeletedAt().isEmpty() == false) {
|
||||
deleted = true;
|
||||
eventWriter.writeStartElement("at", "deleted-entry", TOMBSTONES_NS);
|
||||
eventWriter.writeNamespace("at", TOMBSTONES_NS);
|
||||
eventWriter.writeAttribute("ref", nextEntry.getId().getValueAsString());
|
||||
eventWriter.writeAttribute("when", nextEntry.getDeletedAt().getValueAsString());
|
||||
if (nextEntry.getDeletedByEmail().isEmpty() == false || nextEntry.getDeletedByName().isEmpty()) {
|
||||
eventWriter.writeStartElement(TOMBSTONES_NS, "by");
|
||||
if (nextEntry.getDeletedByName().isEmpty()==false) {
|
||||
eventWriter.writeStartElement(TOMBSTONES_NS, "name");
|
||||
eventWriter.writeCharacters(nextEntry.getDeletedByName().getValue());
|
||||
eventWriter.writeEndElement();
|
||||
}
|
||||
if (nextEntry.getDeletedByEmail().isEmpty() == false) {
|
||||
eventWriter.writeStartElement(TOMBSTONES_NS, "email");
|
||||
eventWriter.writeCharacters(nextEntry.getDeletedByEmail().getValue());
|
||||
eventWriter.writeEndElement();
|
||||
}
|
||||
eventWriter.writeEndElement();
|
||||
}
|
||||
if (nextEntry.getDeletedComment().isEmpty()==false) {
|
||||
eventWriter.writeStartElement(TOMBSTONES_NS, "comment");
|
||||
eventWriter.writeCharacters(nextEntry.getDeletedComment().getValue());
|
||||
eventWriter.writeEndElement();
|
||||
}
|
||||
} else {
|
||||
eventWriter.writeStartElement("entry");
|
||||
}
|
||||
|
@ -377,8 +396,8 @@ public class XmlParser extends BaseParser implements IParser {
|
|||
}
|
||||
}
|
||||
|
||||
private void encodeChildElementToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, XMLStreamWriter theEventWriter, IElement nextValue, String childName, BaseRuntimeElementDefinition<?> childDef, String theExtensionUrl, boolean theIncludedResource)
|
||||
throws XMLStreamException, DataFormatException {
|
||||
private void encodeChildElementToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, XMLStreamWriter theEventWriter, IElement nextValue, String childName,
|
||||
BaseRuntimeElementDefinition<?> childDef, String theExtensionUrl, boolean theIncludedResource) throws XMLStreamException, DataFormatException {
|
||||
if (nextValue.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
@ -442,8 +461,8 @@ public class XmlParser extends BaseParser implements IParser {
|
|||
|
||||
}
|
||||
|
||||
private void encodeCompositeElementChildrenToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, IElement theElement, XMLStreamWriter theEventWriter, List<? extends BaseRuntimeChildDefinition> children, boolean theIncludedResource)
|
||||
throws XMLStreamException, DataFormatException {
|
||||
private void encodeCompositeElementChildrenToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, IElement theElement, XMLStreamWriter theEventWriter,
|
||||
List<? extends BaseRuntimeChildDefinition> children, boolean theIncludedResource) throws XMLStreamException, DataFormatException {
|
||||
for (BaseRuntimeChildDefinition nextChild : children) {
|
||||
if (nextChild instanceof RuntimeChildNarrativeDefinition && !theIncludedResource) {
|
||||
INarrativeGenerator gen = myContext.getNarrativeGenerator();
|
||||
|
@ -475,13 +494,13 @@ public class XmlParser extends BaseParser implements IParser {
|
|||
if (childDef == null) {
|
||||
super.throwExceptionForUnknownChildType(nextChild, type);
|
||||
}
|
||||
|
||||
|
||||
if (nextValue instanceof ExtensionDt) {
|
||||
|
||||
|
||||
extensionUrl = ((ExtensionDt) nextValue).getUrlAsString();
|
||||
encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, nextValue, childName, childDef, extensionUrl, theIncludedResource);
|
||||
|
||||
} else if (extensionUrl != null && childName.equals("extension") == false) {
|
||||
|
||||
} else if (extensionUrl != null && childName.equals("extension") == false) {
|
||||
RuntimeChildDeclaredExtensionDefinition extDef = (RuntimeChildDeclaredExtensionDefinition) nextChild;
|
||||
if (extDef.isModifier()) {
|
||||
theEventWriter.writeStartElement("modifierExtension");
|
||||
|
@ -499,14 +518,15 @@ public class XmlParser extends BaseParser implements IParser {
|
|||
}
|
||||
}
|
||||
|
||||
private void encodeCompositeElementToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, IElement theElement, XMLStreamWriter theEventWriter, BaseRuntimeElementCompositeDefinition<?> resDef, boolean theIncludedResource) throws XMLStreamException,
|
||||
DataFormatException {
|
||||
private void encodeCompositeElementToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, IElement theElement, XMLStreamWriter theEventWriter,
|
||||
BaseRuntimeElementCompositeDefinition<?> resDef, boolean theIncludedResource) throws XMLStreamException, DataFormatException {
|
||||
encodeExtensionsIfPresent(theResDef, theResource, theEventWriter, theElement, theIncludedResource);
|
||||
encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theElement, theEventWriter, resDef.getExtensions(), theIncludedResource);
|
||||
encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theElement, theEventWriter, resDef.getChildren(), theIncludedResource);
|
||||
}
|
||||
|
||||
private void encodeExtensionsIfPresent(RuntimeResourceDefinition theResDef, IResource theResource, XMLStreamWriter theWriter, IElement theElement, boolean theIncludedResource) throws XMLStreamException, DataFormatException {
|
||||
private void encodeExtensionsIfPresent(RuntimeResourceDefinition theResDef, IResource theResource, XMLStreamWriter theWriter, IElement theElement, boolean theIncludedResource)
|
||||
throws XMLStreamException, DataFormatException {
|
||||
if (theElement instanceof ISupportsUndeclaredExtensions) {
|
||||
ISupportsUndeclaredExtensions res = (ISupportsUndeclaredExtensions) theElement;
|
||||
encodeUndeclaredExtensions(theResDef, theResource, theWriter, res.getUndeclaredExtensions(), "extension", theIncludedResource);
|
||||
|
@ -516,11 +536,11 @@ public class XmlParser extends BaseParser implements IParser {
|
|||
|
||||
private void encodeResourceReferenceToStreamWriter(XMLStreamWriter theEventWriter, ResourceReferenceDt theRef) throws XMLStreamException {
|
||||
String reference = theRef.getReference().getValue();
|
||||
// if (StringUtils.isBlank(reference)) {
|
||||
// if (theRef.getResourceType() != null && StringUtils.isNotBlank(theRef.getResourceId())) {
|
||||
// reference = myContext.getResourceDefinition(theRef.getResourceType()).getName() + '/' + theRef.getResourceId();
|
||||
// }
|
||||
// }
|
||||
// if (StringUtils.isBlank(reference)) {
|
||||
// if (theRef.getResourceType() != null && StringUtils.isNotBlank(theRef.getResourceId())) {
|
||||
// reference = myContext.getResourceDefinition(theRef.getResourceType()).getName() + '/' + theRef.getResourceId();
|
||||
// }
|
||||
// }
|
||||
|
||||
if (!(theRef.getDisplay().isEmpty())) {
|
||||
theEventWriter.writeStartElement("display");
|
||||
|
@ -536,9 +556,7 @@ public class XmlParser extends BaseParser implements IParser {
|
|||
|
||||
/**
|
||||
* @param theIncludedResource
|
||||
* Set to true only if this resource is an "included" resource,
|
||||
* as opposed to a "root level" resource by itself or in a bundle
|
||||
* entry
|
||||
* Set to true only if this resource is an "included" resource, as opposed to a "root level" resource by itself or in a bundle entry
|
||||
*
|
||||
*/
|
||||
private void encodeResourceToXmlStreamWriter(IResource theResource, XMLStreamWriter theEventWriter, boolean theIncludedResource) throws XMLStreamException, DataFormatException {
|
||||
|
@ -569,7 +587,8 @@ public class XmlParser extends BaseParser implements IParser {
|
|||
theEventWriter.writeEndElement();
|
||||
}
|
||||
|
||||
private void encodeUndeclaredExtensions(RuntimeResourceDefinition theResDef, IResource theResource, XMLStreamWriter theWriter, List<ExtensionDt> extensions, String tagName, boolean theIncludedResource) throws XMLStreamException, DataFormatException {
|
||||
private void encodeUndeclaredExtensions(RuntimeResourceDefinition theResDef, IResource theResource, XMLStreamWriter theWriter, List<ExtensionDt> extensions, String tagName,
|
||||
boolean theIncludedResource) throws XMLStreamException, DataFormatException {
|
||||
for (ExtensionDt next : extensions) {
|
||||
theWriter.writeStartElement(tagName);
|
||||
theWriter.writeAttribute("url", next.getUrl().getValue());
|
||||
|
@ -654,7 +673,7 @@ public class XmlParser extends BaseParser implements IParser {
|
|||
} else {
|
||||
if (StringUtils.isBlank(se.getName().getPrefix())) {
|
||||
theEventWriter.writeStartElement(se.getName().getLocalPart());
|
||||
// theEventWriter.writeDefaultNamespace(se.getName().getNamespaceURI());
|
||||
// theEventWriter.writeDefaultNamespace(se.getName().getNamespaceURI());
|
||||
} else {
|
||||
theEventWriter.writeStartElement(se.getName().getNamespaceURI(), se.getName().getLocalPart());
|
||||
}
|
||||
|
|
|
@ -63,7 +63,7 @@ public abstract class BaseClient {
|
|||
private boolean myKeepResponses = false;
|
||||
private HttpResponse myLastResponse;
|
||||
private String myLastResponseBody;
|
||||
private boolean myPrettyPrint = false;
|
||||
private Boolean myPrettyPrint = false;
|
||||
|
||||
private final String myUrlBase;
|
||||
|
||||
|
@ -271,9 +271,17 @@ public abstract class BaseClient {
|
|||
* HAPI based servers (and any other servers which might implement it).
|
||||
*/
|
||||
public boolean isPrettyPrint() {
|
||||
return myPrettyPrint;
|
||||
return Boolean.TRUE.equals(myPrettyPrint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the pretty print flag, which is a request to the server for it to return "pretty printed" responses. Note that this is currently a non-standard flag (_pretty) which is supported only by
|
||||
* HAPI based servers (and any other servers which might implement it).
|
||||
*/
|
||||
public Boolean getPrettyPrint() {
|
||||
return myPrettyPrint;
|
||||
}
|
||||
|
||||
private void keepResponseAndLogIt(boolean theLogRequestAndResponse, HttpResponse response, String responseString) {
|
||||
if (myKeepResponses) {
|
||||
myLastResponse = response;
|
||||
|
@ -330,7 +338,7 @@ public abstract class BaseClient {
|
|||
* Sets the pretty print flag, which is a request to the server for it to return "pretty printed" responses. Note that this is currently a non-standard flag (_pretty) which is supported only by
|
||||
* HAPI based servers (and any other servers which might implement it).
|
||||
*/
|
||||
public BaseClient setPrettyPrint(boolean thePrettyPrint) {
|
||||
public BaseClient setPrettyPrint(Boolean thePrettyPrint) {
|
||||
myPrettyPrint = thePrettyPrint;
|
||||
return this;
|
||||
}
|
||||
|
|
|
@ -101,7 +101,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
|||
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding());
|
||||
}
|
||||
|
||||
ResourceResponseHandler<Conformance> binding = new ResourceResponseHandler<Conformance>(Conformance.class);
|
||||
ResourceResponseHandler<Conformance> binding = new ResourceResponseHandler<Conformance>(Conformance.class, null);
|
||||
Conformance resp = invokeClient(binding, invocation, myLogRequestAndResponse);
|
||||
return resp;
|
||||
}
|
||||
|
@ -181,12 +181,16 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
|||
|
||||
@Override
|
||||
public <T extends IResource> T read(final Class<T> theType, IdDt theId) {
|
||||
if (theId == null || theId.hasIdPart() == false) {
|
||||
throw new IllegalArgumentException("theId does not contain a valid ID, is: " + theId);
|
||||
}
|
||||
|
||||
HttpGetClientInvocation invocation = ReadMethodBinding.createReadInvocation(theId, toResourceName(theType));
|
||||
if (isKeepResponses()) {
|
||||
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding());
|
||||
}
|
||||
|
||||
ResourceResponseHandler<T> binding = new ResourceResponseHandler<T>(theType);
|
||||
ResourceResponseHandler<T> binding = new ResourceResponseHandler<T>(theType, theId);
|
||||
T resp = invokeClient(binding, invocation, myLogRequestAndResponse);
|
||||
return resp;
|
||||
}
|
||||
|
@ -291,7 +295,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
|||
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding());
|
||||
}
|
||||
|
||||
ResourceResponseHandler<T> binding = new ResourceResponseHandler<T>(theType);
|
||||
ResourceResponseHandler<T> binding = new ResourceResponseHandler<T>(theType, theId);
|
||||
T resp = invokeClient(binding, invocation, myLogRequestAndResponse);
|
||||
return resp;
|
||||
}
|
||||
|
@ -594,9 +598,11 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
|||
private final class ResourceResponseHandler<T extends IResource> implements IClientResponseHandler<T> {
|
||||
|
||||
private Class<T> myType;
|
||||
private IdDt myId;
|
||||
|
||||
public ResourceResponseHandler(Class<T> theType) {
|
||||
public ResourceResponseHandler(Class<T> theType, IdDt theId) {
|
||||
myType = theType;
|
||||
myId=theId;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -606,7 +612,13 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
|||
throw NonFhirResponseException.newInstance(theResponseStatusCode, theResponseMimeType, theResponseReader);
|
||||
}
|
||||
IParser parser = respType.newParser(myContext);
|
||||
return parser.parseResource(myType, theResponseReader);
|
||||
T retVal = parser.parseResource(myType, theResponseReader);
|
||||
|
||||
if (myId != null) {
|
||||
retVal.setId(myId);
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ package ca.uhn.fhir.rest.gclient;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.model.primitive.StringDt;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
|
||||
public class StringParam implements IParam {
|
||||
|
@ -54,6 +55,8 @@ public class StringParam implements IParam {
|
|||
|
||||
ICriterion value(String theValue);
|
||||
|
||||
ICriterion value(StringDt theValue);
|
||||
|
||||
}
|
||||
|
||||
private class StringExactly implements IStringMatch {
|
||||
|
@ -61,6 +64,11 @@ public class StringParam implements IParam {
|
|||
public ICriterion value(String theValue) {
|
||||
return new StringCriterion(getParamName() + Constants.PARAMQUALIFIER_STRING_EXACT, theValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ICriterion value(StringDt theValue) {
|
||||
return new StringCriterion(getParamName() + Constants.PARAMQUALIFIER_STRING_EXACT, theValue.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
private class StringMatches implements IStringMatch {
|
||||
|
@ -68,6 +76,11 @@ public class StringParam implements IParam {
|
|||
public ICriterion value(String theValue) {
|
||||
return new StringCriterion(getParamName(), theValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ICriterion value(StringDt theValue) {
|
||||
return new StringCriterion(getParamName(), theValue.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -177,10 +177,16 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
} else {
|
||||
ourLog.trace("Method {} doesn't match param '{}' is not present", getMethod().getName(), name);
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
methodParamsTemp.add(name);
|
||||
if (qualifiedParamNames.contains(name)) {
|
||||
methodParamsTemp.add(name);
|
||||
} else if (unqualifiedNames.contains(name)) {
|
||||
methodParamsTemp.addAll(theRequest.getUnqualifiedToQualifiedNames().get(name));
|
||||
} else {
|
||||
methodParamsTemp.add(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (myQueryName != null) {
|
||||
|
|
|
@ -24,6 +24,9 @@ import java.util.ArrayList;
|
|||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterAnd;
|
||||
|
@ -57,6 +60,14 @@ public class SearchParameter extends BaseQueryParameter {
|
|||
this.myRequired = theRequired;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
ToStringBuilder retVal = new ToStringBuilder(this);
|
||||
retVal.append("name", myName);
|
||||
retVal.append("required", myRequired);
|
||||
return retVal.toString();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
|
|
|
@ -52,7 +52,7 @@
|
|||
<td>
|
||||
<th:block th:if="${not result.resource.name.text.empty}" th:text="${result.resource.name.text.value}"/>
|
||||
<th:block th:if="${result.resource.name.text.empty} and ${not #lists.isEmpty(result.resource.name.coding)} and ${not result.resource.name.coding[0].empty} and ${not result.resource.name.coding[0].display.empty}" th:text="${result.resource.name.coding[0].display}"/>
|
||||
<th:block th:if="${result.resource.name.text.empty} and ${not #lists.isEmpty(result.resource.name.coding)} and ${not result.resource.name.coding[0].empty} and ${result.resource.name.coding[0].display.empty}" th:text="?"/>
|
||||
<th:block th:if="${result.resource.name.text.empty} and ${not #lists.isEmpty(result.resource.name.coding)} and ${not result.resource.name.coding[0].empty} and ${result.resource.name.coding[0].display.empty}" th:text="'?'"/>
|
||||
<!--/*--> Hb <!--*/-->
|
||||
</td>
|
||||
<td th:narrative="${result.resource.value}">2.2 g/L</td>
|
||||
|
|
|
@ -12,6 +12,7 @@ import org.junit.Test;
|
|||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
|
||||
import ca.uhn.fhir.model.dstu.composite.CodeableConceptDt;
|
||||
import ca.uhn.fhir.model.dstu.composite.NarrativeDt;
|
||||
import ca.uhn.fhir.model.dstu.composite.PeriodDt;
|
||||
import ca.uhn.fhir.model.dstu.composite.QuantityDt;
|
||||
|
@ -154,6 +155,11 @@ public class DefaultThymeleafNarrativeGeneratorTest {
|
|||
obs.setValue(new StringDt("HELLO!"));
|
||||
value.addResult().setResource(obs);
|
||||
}
|
||||
{
|
||||
Observation obs = new Observation();
|
||||
obs.setName(new CodeableConceptDt("AA", "BB"));
|
||||
value.addResult().setResource(obs);
|
||||
}
|
||||
NarrativeDt generateNarrative = gen.generateNarrative("http://hl7.org/fhir/profiles/DiagnosticReport", value);
|
||||
String output = generateNarrative.getDiv().getValueAsString();
|
||||
|
||||
|
@ -161,8 +167,8 @@ public class DefaultThymeleafNarrativeGeneratorTest {
|
|||
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);
|
||||
// ourLog.info(title);
|
||||
assertEquals("Some & Diagnostic Report - final - 3 observations", title);
|
||||
|
||||
|
||||
// Now try it with the parser
|
||||
|
|
|
@ -878,7 +878,12 @@ public class XmlParserTest {
|
|||
"<link rel=\"self\" href=\"http://hl7.org/implement/standards/fhir/valuesets.xml\"/>" +
|
||||
"<updated>2014-02-10T04:11:24.435+00:00</updated>" +
|
||||
"<at:deleted-entry xmlns:at=\"http://purl.org/atompub/tombstones/1.0\" ref=\"http://foo/Patient/1\" when=\"2013-02-10T04:11:24.435+00:00\">" +
|
||||
"<link rel=\"self\" href=\"http://foo/Patient/1/_history/2\"/>" +
|
||||
"<at:by>" +
|
||||
"<at:name>John Doe</at:name>" +
|
||||
"<at:email>jdoe@example.org</at:email>" +
|
||||
"</at:by>" +
|
||||
"<at:comment>Removed comment spam</at:comment>" +
|
||||
"<link rel=\"self\" href=\"http://foo/Patient/1/_history/2\"/>" +
|
||||
"</at:deleted-entry>" +
|
||||
"</feed>";
|
||||
//@formatter:on
|
||||
|
@ -893,6 +898,9 @@ public class XmlParserTest {
|
|||
assertEquals("1", entry.getResource().getId().getIdPart());
|
||||
assertEquals("2", entry.getResource().getId().getVersionIdPart());
|
||||
assertEquals("2", ((IdDt)entry.getResource().getResourceMetadata().get(ResourceMetadataKeyEnum.VERSION_ID)).getVersionIdPart());
|
||||
assertEquals("John Doe", entry.getDeletedByName().getValue());
|
||||
assertEquals("jdoe@example.org", entry.getDeletedByEmail().getValue());
|
||||
assertEquals("Removed comment spam", entry.getDeletedComment().getValue());
|
||||
assertEquals(new InstantDt("2013-02-10T04:11:24.435+00:00"), entry.getResource().getResourceMetadata().get(ResourceMetadataKeyEnum.DELETED_AT));
|
||||
|
||||
ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeBundleToString(bundle));
|
||||
|
|
|
@ -32,6 +32,7 @@ import ca.uhn.fhir.model.dstu.resource.Encounter;
|
|||
import ca.uhn.fhir.model.dstu.resource.Observation;
|
||||
import ca.uhn.fhir.model.dstu.resource.Organization;
|
||||
import ca.uhn.fhir.model.dstu.resource.Patient;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.client.exceptions.NonFhirResponseException;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
|
@ -115,6 +116,23 @@ public class GenericClientTest {
|
|||
return msg;
|
||||
}
|
||||
|
||||
private String getResourceResult() {
|
||||
//@formatter:off
|
||||
String msg =
|
||||
"<Patient xmlns=\"http://hl7.org/fhir\">"
|
||||
+ "<text><status value=\"generated\" /><div xmlns=\"http://www.w3.org/1999/xhtml\">John Cardinal: 444333333 </div></text>"
|
||||
+ "<identifier><label value=\"SSN\" /><system value=\"http://orionhealth.com/mrn\" /><value value=\"PRP1660\" /></identifier>"
|
||||
+ "<name><use value=\"official\" /><family value=\"Cardinal\" /><given value=\"John\" /></name>"
|
||||
+ "<name><family value=\"Kramer\" /><given value=\"Doe\" /></name>"
|
||||
+ "<telecom><system value=\"phone\" /><value value=\"555-555-2004\" /><use value=\"work\" /></telecom>"
|
||||
+ "<gender><coding><system value=\"http://hl7.org/fhir/v3/AdministrativeGender\" /><code value=\"M\" /></coding></gender>"
|
||||
+ "<address><use value=\"home\" /><line value=\"2222 Home Street\" /></address><active value=\"true\" />"
|
||||
+ "</Patient>";
|
||||
//@formatter:on
|
||||
return msg;
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Test
|
||||
public void testSearchByString() throws Exception {
|
||||
|
@ -406,6 +424,32 @@ public class GenericClientTest {
|
|||
assertEquals("http://example.com/fhir/Patient?birthdate=%3C%3D2012-01-22&birthdate=%3E2011-01-01&_include=Patient.managingOrganization&_sort%3Aasc=birthdate&_sort%3Adesc=name&_count=123&_format=json", capt.getValue().getURI().toString());
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testRead() throws Exception {
|
||||
|
||||
String msg = getResourceResult();
|
||||
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
|
||||
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
|
||||
|
||||
IGenericClient client = myCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||
|
||||
//@formatter:off
|
||||
Patient response = client.read(Patient.class, new IdDt("Patient/1234"));
|
||||
//@formatter:on
|
||||
|
||||
assertThat(response.getNameFirstRep().getFamilyAsSingleString(), StringContains.containsString("Cardinal"));
|
||||
assertEquals("Patient/1234", response.getId().getValue());
|
||||
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Test
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package ca.uhn.fhir.rest.server;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
@ -22,6 +22,7 @@ import org.junit.Test;
|
|||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.dstu.resource.Patient;
|
||||
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.param.StringParam;
|
||||
|
@ -127,6 +128,19 @@ public class StringParameterTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchExactMatchOptional() throws Exception {
|
||||
{
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?ccc:exact=aaa");
|
||||
HttpResponse status = ourClient.execute(httpGet);
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
assertEquals(1, new FhirContext().newXmlParser().parseBundle(responseContent).getEntries().size());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@AfterClass
|
||||
public static void afterClass() throws Exception {
|
||||
ourServer.stop();
|
||||
|
@ -190,6 +204,25 @@ public class StringParameterTest {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
@Search
|
||||
public List<Patient> findPatientWithOptional(@OptionalParam(name = "ccc") StringParam theParam) {
|
||||
ArrayList<Patient> retVal = new ArrayList<Patient>();
|
||||
|
||||
if (theParam.isExact() && theParam.getValue().equals("aaa")) {
|
||||
Patient patient = new Patient();
|
||||
patient.setId("1");
|
||||
retVal.add(patient);
|
||||
}
|
||||
if (!theParam.isExact() && theParam.getValue().toLowerCase().equals("aaa")) {
|
||||
Patient patient = new Patient();
|
||||
patient.setId("2");
|
||||
retVal.add(patient);
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Class<? extends IResource> getResourceType() {
|
||||
return Patient.class;
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<value>furore , Spark - Furore Reference Server , http://spark.furore.com/fhir</value>
|
||||
<value>blaze , Blaze (Orion Health) , https://fhir.orionhealth.com/blaze/fhir</value>
|
||||
<value>oridashi , Oridashi , http://demo.oridashi.com.au:8190</value>
|
||||
<value>fhirbase , FHIRPlace (Health Samurai) , http://try-fhirplace.hospital-systems.com/ </value>
|
||||
<!-- <value>fhirbase , FHIRPlace (Health Samurai) , http://try-fhirplace.hospital-systems.com/ </value> -->
|
||||
</list>
|
||||
</property>
|
||||
</bean>
|
||||
|
|
|
@ -82,9 +82,15 @@ public class Controller {
|
|||
theModel.put("notHome", true);
|
||||
theModel.put("extraBreadcrumb", "About");
|
||||
|
||||
ourLog.info(logPrefix(theModel) + "Displayed about page");
|
||||
|
||||
return "about";
|
||||
}
|
||||
|
||||
private String logPrefix(ModelMap theModel) {
|
||||
return "[server=" + theModel.get("serverId") + "] - ";
|
||||
}
|
||||
|
||||
@RequestMapping(value = { "/conformance" })
|
||||
public String actionConformance(final HomeRequest theRequest, final BindingResult theBindingResult, final ModelMap theModel) {
|
||||
addCommonParams(theRequest, theModel);
|
||||
|
@ -102,6 +108,8 @@ public class Controller {
|
|||
|
||||
processAndAddLastClientInvocation(client, returnsResource, theModel, delay, "Loaded conformance");
|
||||
|
||||
ourLog.info(logPrefix(theModel) + "Displayed conformance profile");
|
||||
|
||||
return "result";
|
||||
}
|
||||
|
||||
|
@ -143,6 +151,8 @@ public class Controller {
|
|||
long delay = System.currentTimeMillis() - start;
|
||||
processAndAddLastClientInvocation(client, returnsResource, theModel, delay, outcomeDescription);
|
||||
|
||||
ourLog.info(logPrefix(theModel) + "Deleted resource of type " + def.getName());
|
||||
|
||||
return "result";
|
||||
}
|
||||
|
||||
|
@ -185,14 +195,18 @@ public class Controller {
|
|||
String vid = theReq.getParameter("resource-tags-vid");
|
||||
if (isNotBlank(vid)) {
|
||||
client.getTags().forResource(resType, id, vid).execute();
|
||||
ourLog.info(logPrefix(theModel) + "Got tags for type " + def.getName() + " ID " + id + " version" + vid);
|
||||
} else {
|
||||
client.getTags().forResource(resType, id).execute();
|
||||
ourLog.info(logPrefix(theModel) + "Got tags for type " + def.getName() + " ID " + id);
|
||||
}
|
||||
} else {
|
||||
client.getTags().forResource(resType).execute();
|
||||
ourLog.info(logPrefix(theModel) + "Got tags for type " + def.getName());
|
||||
}
|
||||
} else {
|
||||
client.getTags().execute();
|
||||
ourLog.info(logPrefix(theModel) + "Got tags for server");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
returnsResource = handleClientException(client, e, theModel);
|
||||
|
@ -230,6 +244,7 @@ public class Controller {
|
|||
|
||||
String url = defaultString(theReq.getParameter("page-url"));
|
||||
if (!url.startsWith(theModel.get("base").toString())) {
|
||||
ourLog.warn(logPrefix(theModel) + "Refusing to load page URL: {}", url);
|
||||
theModel.put("errorMsg", "Invalid page URL: " + url);
|
||||
return "result";
|
||||
}
|
||||
|
@ -240,6 +255,7 @@ public class Controller {
|
|||
|
||||
long start = System.currentTimeMillis();
|
||||
try {
|
||||
ourLog.info(logPrefix(theModel) + "Loading paging URL: {}", url);
|
||||
client.loadPage().url(url).execute();
|
||||
} catch (Exception e) {
|
||||
returnsResource = handleClientException(client, e, theModel);
|
||||
|
@ -284,7 +300,9 @@ public class Controller {
|
|||
|
||||
long start = System.currentTimeMillis();
|
||||
try {
|
||||
client.read(def.getImplementingClass(), new IdDt(def.getName(), id, versionId));
|
||||
IdDt resid = new IdDt(def.getName(), id, versionId);
|
||||
ourLog.info(logPrefix(theModel) + "Reading resource: {}", resid);
|
||||
client.read(def.getImplementingClass(), resid);
|
||||
} catch (Exception e) {
|
||||
returnsResource = handleClientException(client, e, theModel);
|
||||
}
|
||||
|
@ -360,6 +378,8 @@ public class Controller {
|
|||
theModel.put("updateResourceId", updateId);
|
||||
}
|
||||
|
||||
ourLog.info(logPrefix(theModel) + "Showing resource page: {}", resourceName);
|
||||
|
||||
return "resource";
|
||||
}
|
||||
|
||||
|
@ -371,8 +391,8 @@ public class Controller {
|
|||
JsonGenerator clientCodeJsonWriter = Json.createGenerator(clientCodeJsonStringWriter);
|
||||
clientCodeJsonWriter.writeStartObject();
|
||||
clientCodeJsonWriter.write("action", "search");
|
||||
clientCodeJsonWriter.write("base", (String)theModel.get("base"));
|
||||
|
||||
clientCodeJsonWriter.write("base", (String) theModel.get("base"));
|
||||
|
||||
GenericClient client = theRequest.newClient(myCtx, myConfig);
|
||||
|
||||
IUntypedQuery search = client.search();
|
||||
|
@ -389,29 +409,45 @@ public class Controller {
|
|||
query = search.forAllResources();
|
||||
clientCodeJsonWriter.writeNull("resource");
|
||||
}
|
||||
|
||||
|
||||
if (client.getPrettyPrint() != null) {
|
||||
clientCodeJsonWriter.write("pretty", client.getPrettyPrint().toString());
|
||||
} else {
|
||||
clientCodeJsonWriter.writeNull("pretty");
|
||||
}
|
||||
|
||||
if (client.getEncoding() != null) {
|
||||
clientCodeJsonWriter.write("format", client.getEncoding().getRequestContentType());
|
||||
} else {
|
||||
clientCodeJsonWriter.writeNull("format");
|
||||
}
|
||||
|
||||
String outcomeDescription = "Search for Resources";
|
||||
|
||||
clientCodeJsonWriter.writeStartArray("params");
|
||||
int paramIdx = -1;
|
||||
while (true) {
|
||||
paramIdx++;
|
||||
|
||||
String paramIdxString = Integer.toString(paramIdx);
|
||||
boolean shouldContinue = handleSearchParam(paramIdxString, theReq, query);
|
||||
boolean shouldContinue = handleSearchParam(paramIdxString, theReq, query, clientCodeJsonWriter);
|
||||
if (!shouldContinue) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
clientCodeJsonWriter.writeEnd();
|
||||
|
||||
clientCodeJsonWriter.writeStartArray("includes");
|
||||
String[] incValues = theReq.getParameterValues(Constants.PARAM_INCLUDE);
|
||||
if (incValues != null) {
|
||||
for (String next : incValues) {
|
||||
if (isNotBlank(next)) {
|
||||
query.include(new Include(next));
|
||||
clientCodeJsonWriter.write(next);
|
||||
}
|
||||
}
|
||||
}
|
||||
clientCodeJsonWriter.writeEnd();
|
||||
|
||||
String limit = theReq.getParameter("resource-search-limit");
|
||||
if (isNotBlank(limit)) {
|
||||
|
@ -419,12 +455,18 @@ public class Controller {
|
|||
theModel.put("errorMsg", "Search limit must be a numeric value.");
|
||||
return "resource";
|
||||
}
|
||||
query.limitTo(Integer.parseInt(limit));
|
||||
int limitInt = Integer.parseInt(limit);
|
||||
query.limitTo(limitInt);
|
||||
clientCodeJsonWriter.write("limit", limit);
|
||||
} else {
|
||||
clientCodeJsonWriter.writeNull("limit");
|
||||
}
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
ResultType returnsResource;
|
||||
try {
|
||||
ourLog.info(logPrefix(theModel) + "Executing a search");
|
||||
|
||||
query.execute();
|
||||
returnsResource = ResultType.BUNDLE;
|
||||
} catch (Exception e) {
|
||||
|
@ -438,7 +480,7 @@ public class Controller {
|
|||
clientCodeJsonWriter.close();
|
||||
String clientCodeJson = clientCodeJsonStringWriter.toString();
|
||||
theModel.put("clientCodeJson", clientCodeJson);
|
||||
|
||||
|
||||
return "result";
|
||||
}
|
||||
|
||||
|
@ -466,11 +508,17 @@ public class Controller {
|
|||
return "home";
|
||||
}
|
||||
|
||||
ResultType returnsResource = ResultType.BUNDLE;
|
||||
long start = System.currentTimeMillis();
|
||||
client.transaction().withBundle(bundle).execute();
|
||||
try {
|
||||
ourLog.info(logPrefix(theModel) + "Executing transaction with {} resources", bundle.size());
|
||||
client.transaction().withBundle(bundle).execute();
|
||||
} catch (Exception e) {
|
||||
returnsResource = handleClientException(client, e, theModel);
|
||||
}
|
||||
long delay = System.currentTimeMillis() - start;
|
||||
|
||||
processAndAddLastClientInvocation(client, ResultType.BUNDLE, theModel, delay, "Transaction");
|
||||
processAndAddLastClientInvocation(client, returnsResource, theModel, delay, "Transaction");
|
||||
|
||||
return "result";
|
||||
}
|
||||
|
@ -555,6 +603,7 @@ public class Controller {
|
|||
long start = System.currentTimeMillis();
|
||||
ResultType returnsResource = ResultType.RESOURCE;
|
||||
outcomeDescription = "";
|
||||
boolean update = false;
|
||||
try {
|
||||
if (validate) {
|
||||
outcomeDescription = "Validate Resource";
|
||||
|
@ -564,6 +613,7 @@ public class Controller {
|
|||
if (isNotBlank(id)) {
|
||||
outcomeDescription = "Update Resource";
|
||||
client.update(id, resource);
|
||||
update = true;
|
||||
} else {
|
||||
outcomeDescription = "Create Resource";
|
||||
client.create(resource);
|
||||
|
@ -576,6 +626,18 @@ public class Controller {
|
|||
|
||||
processAndAddLastClientInvocation(client, returnsResource, theModel, delay, outcomeDescription);
|
||||
|
||||
try {
|
||||
if (validate) {
|
||||
ourLog.info(logPrefix(theModel) + "Validated resource of type " + getResourceType(theReq).getName());
|
||||
} else if (update) {
|
||||
ourLog.info(logPrefix(theModel) + "Updated resource of type " + getResourceType(theReq).getName());
|
||||
} else {
|
||||
ourLog.info(logPrefix(theModel) + "Created resource of type " + getResourceType(theReq).getName());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
ourLog.warn("Failed to determine resource type from request", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void doActionHistory(HttpServletRequest theReq, HomeRequest theRequest, BindingResult theBindingResult, ModelMap theModel, String theMethod, String theMethodDescription) {
|
||||
|
@ -603,11 +665,18 @@ public class Controller {
|
|||
limit = Integer.parseInt(limitStr);
|
||||
}
|
||||
|
||||
ResultType returnsResource = ResultType.BUNDLE;
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
client.history(type, id, since, limit);
|
||||
try {
|
||||
ourLog.info(logPrefix(theModel) + "Retrieving history for type {} ID {} since {}", new Object[] { type, id, since });
|
||||
client.history(type, id, since, limit);
|
||||
} catch (Exception e) {
|
||||
returnsResource = handleClientException(client, e, theModel);
|
||||
}
|
||||
long delay = System.currentTimeMillis() - start;
|
||||
|
||||
processAndAddLastClientInvocation(client, ResultType.BUNDLE, theModel, delay, theMethodDescription);
|
||||
processAndAddLastClientInvocation(client, returnsResource, theModel, delay, theMethodDescription);
|
||||
|
||||
}
|
||||
|
||||
|
@ -770,42 +839,59 @@ public class Controller {
|
|||
return def;
|
||||
}
|
||||
|
||||
private boolean handleSearchParam(String paramIdxString, HttpServletRequest theReq, IQuery theQuery) {
|
||||
private boolean handleSearchParam(String paramIdxString, HttpServletRequest theReq, IQuery theQuery, JsonGenerator theClientCodeJsonWriter) {
|
||||
String nextName = theReq.getParameter("param." + paramIdxString + ".name");
|
||||
if (isBlank(nextName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String nextQualifier = StringUtils.defaultString(theReq.getParameter("param." + paramIdxString + ".qualifier"));
|
||||
|
||||
String nextType = theReq.getParameter("param." + paramIdxString + ".type");
|
||||
|
||||
StringBuilder b = new StringBuilder();
|
||||
for (int i = 0; i < 100; i++) {
|
||||
b.append(defaultString(theReq.getParameter("param." + paramIdxString + "." + i)));
|
||||
}
|
||||
|
||||
String paramValue = b.toString();
|
||||
if (isBlank(paramValue)) {
|
||||
return true;
|
||||
List<String> parts = new ArrayList<String>();
|
||||
for (int i = 0; i < 5; i++) {
|
||||
parts.add(defaultString(theReq.getParameter("param." + paramIdxString + "." + i)));
|
||||
}
|
||||
|
||||
List<String> values;
|
||||
if ("token".equals(nextType)) {
|
||||
if (paramValue.length() < 2) {
|
||||
if (isBlank(parts.get(2))) {
|
||||
return true;
|
||||
}
|
||||
values = Collections.singletonList(StringUtils.join(parts, ""));
|
||||
} else if ("date".equals(nextType)) {
|
||||
values = new ArrayList<String>();
|
||||
if (isNotBlank(parts.get(1))) {
|
||||
values.add(StringUtils.join(parts.get(0), parts.get(1)));
|
||||
}
|
||||
if (isNotBlank(parts.get(3))) {
|
||||
values.add(StringUtils.join(parts.get(2), parts.get(3)));
|
||||
}
|
||||
if (values.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
values = Collections.singletonList(StringUtils.join(parts, ""));
|
||||
if (isBlank(values.get(0))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// if ("xml".equals(theReq.getParameter("encoding"))) {
|
||||
// query.encodedXml();
|
||||
// }else if ("json".equals(theReq.getParameter("encoding"))) {
|
||||
// query.encodedJson();
|
||||
// }
|
||||
for (String nextValue : values) {
|
||||
|
||||
theQuery.where(new StringParam(nextName + nextQualifier).matches().value(paramValue));
|
||||
theClientCodeJsonWriter.writeStartObject();
|
||||
theClientCodeJsonWriter.write("type", nextType);
|
||||
theClientCodeJsonWriter.write("name", nextName);
|
||||
theClientCodeJsonWriter.write("qualifier", nextQualifier);
|
||||
theClientCodeJsonWriter.write("value", nextValue);
|
||||
theClientCodeJsonWriter.writeEnd();
|
||||
|
||||
theQuery.where(new StringParam(nextName + nextQualifier).matches().value(nextValue));
|
||||
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(theReq.getParameter("param." + paramIdxString + ".0.name"))) {
|
||||
handleSearchParam(paramIdxString + ".0", theReq, theQuery);
|
||||
handleSearchParam(paramIdxString + ".0", theReq, theQuery, theClientCodeJsonWriter);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -15,8 +15,9 @@
|
|||
<value>home , Localhost Server , http://localhost:8887/fhir/context </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>blaze , Blaze (Orion Health) , https://his-medicomp-gateway.orionhealth.com/blaze/fhir</value>
|
||||
<value>blaze , Blaze (Orion Health) , https://fhir.orionhealth.com/blaze/fhir</value>
|
||||
<value>oridashi , Oridashi , http://demo.oridashi.com.au:8190</value>
|
||||
<value>fhirbase , FHIRPlace (Health Samurai) , http://try-fhirplace.hospital-systems.com/ </value>
|
||||
</list>
|
||||
</property>
|
||||
</bean>
|
||||
|
|
|
@ -537,5 +537,11 @@
|
|||
|
||||
</form>
|
||||
<div th:replace="tmpl-footer :: footer" ></div>
|
||||
|
||||
<!--
|
||||
<script type="text/javascript">
|
||||
$(function () { $("[data-toggle='tooltip']").tooltip(); });
|
||||
</script>
|
||||
-->
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -27,7 +27,6 @@
|
|||
<th:block th:text="${latencyMs} + 'ms'"/>
|
||||
</div>
|
||||
|
||||
<!--
|
||||
<div th:if="${clientCodeJson} != null" class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
Client Code -
|
||||
|
@ -44,7 +43,6 @@
|
|||
generateHapi(jsonClientCode, $('#clientCodeBody'));
|
||||
</script>
|
||||
</div>
|
||||
-->
|
||||
|
||||
<div th:if="${requestUrl} != null">
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
<div class="col-sm-6">
|
||||
<div class="searchParamDescription">
|
||||
<div>
|
||||
<b th:if="${param.name} != null and ${!param.name.empty}" th:text="${param.name} + ' - '" />
|
||||
<th:block th:text="${param.documentation}" />
|
||||
<th:block th:if="${!param.getUndeclaredExtensionsByUrl(requiredParamExtension).empty}">
|
||||
<span style="color:#F88;" th:if="${param.getUndeclaredExtensionsByUrl(requiredParamExtension).get(0).value.value}">
|
||||
|
|
|
@ -22,11 +22,20 @@ body {
|
|||
line-height: 0.85em;
|
||||
}
|
||||
|
||||
.clientCodeComment {
|
||||
color: #4A4;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.clientCodePreamble {
|
||||
color: #888;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.clientCodeIndent {
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
label {
|
||||
font-size: 1.0em;
|
||||
color: #808080;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
|
||||
|
||||
function generateHapi(json, container) {
|
||||
if (json.action == 'search') {
|
||||
generateHapiSearch(json, container);
|
||||
|
@ -6,12 +6,17 @@ function generateHapi(json, container) {
|
|||
}
|
||||
|
||||
function generateHapiSearch(json, container) {
|
||||
container.append($('<span />', {'class': 'clientCodeComment'}).text("// Create a client (only needed once)"));
|
||||
container.append($('<br/>'));
|
||||
container.append($('<span />', {'class': 'clientCodePreamble'}).text("FhirContext ctx = new FhirContext();"));
|
||||
container.append($('<br/>'));
|
||||
container.append($('<span />', {'class': 'clientCodePreamble'}).text("IGenericClient client = ctx.newRestfulGenericClient(\"" + json.base + "\");"));
|
||||
container.append($('<br/>'));
|
||||
container.append($('<br/>'));
|
||||
container.append($('<span />', {'class': 'clientCodeComment'}).text("// Invoke the client"));
|
||||
container.append($('<br/>'));
|
||||
|
||||
var searchLine = 'client.search()';
|
||||
var searchLine = 'Bundle bundle = client.search()';
|
||||
if (json.resource != null) {
|
||||
searchLine = searchLine + '.forResource(' + json.resource + '.class)';
|
||||
} else {
|
||||
|
@ -19,4 +24,60 @@ function generateHapiSearch(json, container) {
|
|||
}
|
||||
container.append($('<span />', {'class': 'clientCodeMain'}).text(searchLine));
|
||||
|
||||
var indented = $('<div />', {'class': 'clientCodeIndent'});
|
||||
container.append(indented);
|
||||
|
||||
if (json.pretty) {
|
||||
indented.append($('<span />', {'class': 'clientCodeMain'}).text('.setPrettyPrint(' + json.pretty + ')'));
|
||||
indented.append($('<br/>'));
|
||||
}
|
||||
|
||||
if (json.format) {
|
||||
indented.append($('<span />', {'class': 'clientCodeMain'}).text('.setEncoding(EncodingEnum.' + json.format.toUpperCase() + ')'));
|
||||
indented.append($('<br/>'));
|
||||
}
|
||||
|
||||
for (var i = 0; i < json.params.length; i++) {
|
||||
var nextParam = json.params[i];
|
||||
var paramLine = null;
|
||||
if (nextParam.type == 'string') {
|
||||
paramLine = '.where(new StringParam("' + nextParam.name + '").matches().value("' + nextParam.value + '"))';
|
||||
} else if (nextParam.type == 'token') {
|
||||
var idx = nextParam.value.indexOf('|');
|
||||
if (idx == -1) {
|
||||
paramLine = '.where(new TokenParam("' + nextParam.name + '").exactly().code("' + nextParam.value + '"))';
|
||||
} else {
|
||||
paramLine = '.where(new TokenParam("' + nextParam.name + '").exactly().systemAndCode("' + nextParam.value.substring(0,idx) + '", "' + nextParam.value.substring(idx+1) + '"))';
|
||||
}
|
||||
} else if (nextParam.type == 'number') {
|
||||
paramLine = '.where(new NumberParam("' + nextParam.name + '").exactly().value("' + nextParam.value + '"))';
|
||||
} else if (nextParam.type == 'date') {
|
||||
if (nextParam.value.substring(0,2) == '>=') {
|
||||
paramLine = '.where(new DateParam("' + nextParam.name + '").afterOrEquals().value("' + nextParam.value.substring(2) + '"))';
|
||||
} else if (nextParam.value.substring(0,1) == '>') {
|
||||
paramLine = '.where(new DateParam("' + nextParam.name + '").after().value("' + nextParam.value.substring(1) + '"))';
|
||||
} else if (nextParam.value.substring(0,2) == '<=') {
|
||||
paramLine = '.where(new DateParam("' + nextParam.name + '").beforeOrEquals().value("' + nextParam.value.substring(2) + '"))';
|
||||
} else if (nextParam.value.substring(0,1) == '<') {
|
||||
paramLine = '.where(new DateParam("' + nextParam.name + '").before().value("' + nextParam.value.substring(1) + '"))';
|
||||
}
|
||||
}
|
||||
if (paramLine != null) {
|
||||
indented.append($('<span />', {'class': 'clientCodeMain'}).text(paramLine));
|
||||
indented.append($('<br/>'));
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < json.includes.length; i++) {
|
||||
indented.append($('<span />', {'class': 'clientCodeMain'}).text('.include(new Include("' + json.includes[i] + '"))'));
|
||||
indented.append($('<br/>'));
|
||||
}
|
||||
|
||||
if (json.limit) {
|
||||
indented.append($('<span />', {'class': 'clientCodeMain'}).text('.limitTo(' + json.limit + ')'));
|
||||
indented.append($('<br/>'));
|
||||
}
|
||||
|
||||
indented.append($('<span />', {'class': 'clientCodeMain'}).text('.execute();'));
|
||||
|
||||
}
|
||||
|
|
|
@ -95,55 +95,70 @@ function addSearchControls(theSearchParamType, theSearchParamName, theSearchPara
|
|||
)
|
||||
);
|
||||
} else if (theSearchParamType == 'date') {
|
||||
var qualifier = $('<input />', {type:'hidden', id:'param.'+theRowNum+'.0', id:'param.'+theRowNum+'.0'});
|
||||
|
||||
if (/date$/.test(theSearchParamName)) {
|
||||
var input = $('<div />', { 'class':'input-group date', 'data-date-format':'YYYY-MM-DD' });
|
||||
} else {
|
||||
var input = $('<div />', { 'class':'input-group date', 'data-date-format':'YYYY-MM-DDTHH:mm:ss' });
|
||||
}
|
||||
var qualifierDiv = $('<div />');
|
||||
input.append(
|
||||
qualifierDiv,
|
||||
$('<input />', { type:'text', 'class':'form-control', id: 'param.' + theRowNum + '.1' }),
|
||||
$('<div />', { 'class':'input-group-addon', 'style':'padding:6px;'} ).append(
|
||||
$('<i />', { 'class':'fa fa-chevron-circle-down'})
|
||||
)
|
||||
);
|
||||
input.datetimepicker({
|
||||
pickTime: false,
|
||||
showToday: true
|
||||
});
|
||||
// Set up the qualifier dropdown after we've initialized the datepicker, since it
|
||||
// overrides all addon buttons while it inits..
|
||||
qualifierDiv.addClass('input-group-btn');
|
||||
var qualifierBtn = $('<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">=</button>');
|
||||
var qualifierBtnEq = $('<a>=</a>').click(function() { updateSearchDateQualifier(qualifierBtn, qualifier, '='); });
|
||||
var qualifierBtnGt = $('<a>></a>').click(function() { updateSearchDateQualifier(qualifierBtn, qualifier, '>'); });
|
||||
var qualifierBtnGe = $('<a>>=</a>').click(function() { updateSearchDateQualifier(qualifierBtn, qualifier, '>='); });
|
||||
var qualifierBtnLt = $('<a><</a>').click(function() { updateSearchDateQualifier(qualifierBtn, qualifier, '<'); });
|
||||
var qualifierBtnLe = $('<a><=</a>').click(function() { updateSearchDateQualifier(qualifierBtn, qualifier, '<='); });
|
||||
qualifierDiv.append(
|
||||
qualifierBtn,
|
||||
$('<ul class="dropdown-menu" role="menu">').append(
|
||||
$('<li />').append(qualifierBtnEq),
|
||||
$('<li />').append(qualifierBtnGt),
|
||||
$('<li />').append(qualifierBtnGe),
|
||||
$('<li />').append(qualifierBtnLt),
|
||||
$('<li />').append(qualifierBtnLe)
|
||||
)
|
||||
);
|
||||
|
||||
$('#search-param-rowopts-' + theContainerRowNum).append(
|
||||
qualifier,
|
||||
$('<div />', { 'class': 'col-sm-4' }).append(
|
||||
input
|
||||
)
|
||||
);
|
||||
addSearchControlDate(theSearchParamName, theContainerRowNum, theRowNum, true);
|
||||
addSearchControlDate(theSearchParamName, theContainerRowNum, theRowNum, false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function addSearchControlDate(theSearchParamName, theContainerRowNum, theRowNum, theLower) {
|
||||
var inputId0 = theRowNum + '.' + (theLower ? 0 : 2);
|
||||
var inputId1 = theRowNum + '.' + (theLower ? 1 : 3);
|
||||
|
||||
var qualifier = $('<input />', {type:'hidden', id:'param.'+inputId0, id:'param.'+inputId0});
|
||||
|
||||
if (/date$/.test(theSearchParamName)) {
|
||||
var input = $('<div />', { 'class':'input-group date', 'data-date-format':'YYYY-MM-DD' });
|
||||
} else {
|
||||
var input = $('<div />', { 'class':'input-group date', 'data-date-format':'YYYY-MM-DDTHH:mm:ss' });
|
||||
}
|
||||
var qualifierDiv = $('<div />');
|
||||
|
||||
input.append(
|
||||
qualifierDiv,
|
||||
$('<input />', { type:'text', 'class':'form-control', id: 'param.' + inputId1 }),
|
||||
$('<div />', { 'class':'input-group-addon', 'style':'padding:6px;'} ).append(
|
||||
$('<i />', { 'class':'fa fa-chevron-circle-down'})
|
||||
)
|
||||
);
|
||||
input.datetimepicker({
|
||||
pickTime: false,
|
||||
showToday: true
|
||||
});
|
||||
// Set up the qualifier dropdown after we've initialized the datepicker, since it
|
||||
// overrides all addon buttons while it inits..
|
||||
qualifierDiv.addClass('input-group-btn');
|
||||
var qualifierTooltip = "Set a qualifier and a date to specify a boundary date. Set two qualifiers and dates to specify a range.";
|
||||
var qualifierBtn = $('<button />', {type:'button', 'class':'btn btn-default dropdown-toggle', 'data-toggle':'dropdown', 'data-placement':'top', 'title':qualifierTooltip}).text('=');
|
||||
qualifierBtn.tooltip({
|
||||
'selector': '',
|
||||
'placement': 'top',
|
||||
'container':'body'
|
||||
});
|
||||
var qualifierBtnEq = $('<a>=</a>').click(function() { updateSearchDateQualifier(qualifierBtn, qualifier, '='); });
|
||||
var qualifierBtnGt = $('<a>></a>').click(function() { updateSearchDateQualifier(qualifierBtn, qualifier, '>'); });
|
||||
var qualifierBtnGe = $('<a>>=</a>').click(function() { updateSearchDateQualifier(qualifierBtn, qualifier, '>='); });
|
||||
var qualifierBtnLt = $('<a><</a>').click(function() { updateSearchDateQualifier(qualifierBtn, qualifier, '<'); });
|
||||
var qualifierBtnLe = $('<a><=</a>').click(function() { updateSearchDateQualifier(qualifierBtn, qualifier, '<='); });
|
||||
qualifierDiv.append(
|
||||
qualifierBtn,
|
||||
$('<ul class="dropdown-menu" role="menu">').append(
|
||||
$('<li />').append(qualifierBtnEq),
|
||||
$('<li />').append(qualifierBtnGt),
|
||||
$('<li />').append(qualifierBtnGe),
|
||||
$('<li />').append(qualifierBtnLt),
|
||||
$('<li />').append(qualifierBtnLe)
|
||||
)
|
||||
);
|
||||
|
||||
$('#search-param-rowopts-' + theContainerRowNum).append(
|
||||
qualifier,
|
||||
$('<div />', { 'class': 'col-sm-3' }).append(
|
||||
input
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function handleSearchParamTypeChange(select, params, rowNum) {
|
||||
var oldVal = select.prevVal;
|
||||
var newVal = select.val();
|
||||
|
|
Loading…
Reference in New Issue