Move searches with required parameters to unnamed queries
This commit is contained in:
parent
add96f2124
commit
cb2b54cb09
|
@ -97,6 +97,9 @@ public class ExtensionDt extends BaseIdentifiableElement implements ICompositeDa
|
|||
* If the value of this extension is not a primitive datatype
|
||||
*/
|
||||
public IPrimitiveDatatype<?> getValueAsPrimitive() {
|
||||
if (!(getValue() instanceof IPrimitiveDatatype)) {
|
||||
throw new ClassCastException("Extension with URL["+myUrl+"] can not be cast to primitive type, type is: "+ getClass().getCanonicalName());
|
||||
}
|
||||
return (IPrimitiveDatatype<?>) getValue();
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ import java.lang.annotation.RetentionPolicy;
|
|||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(value= {ElementType.FIELD, ElementType.TYPE, ElementType.PARAMETER})
|
||||
@Target(value= {ElementType.FIELD, ElementType.TYPE, ElementType.PARAMETER, ElementType.METHOD})
|
||||
public @interface Description {
|
||||
|
||||
/**
|
||||
|
|
|
@ -20,6 +20,8 @@ package ca.uhn.fhir.rest.method;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
|
@ -32,6 +34,7 @@ import org.apache.commons.lang3.StringUtils;
|
|||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.api.annotation.Description;
|
||||
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationSystemEnum;
|
||||
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum;
|
||||
import ca.uhn.fhir.rest.param.BaseQueryParameter;
|
||||
|
@ -47,17 +50,34 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
|||
public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchMethodBinding.class);
|
||||
|
||||
private Class<?> myDeclaredResourceType;
|
||||
private Class<? extends IResource> myDeclaredResourceType;
|
||||
private String myQueryName;
|
||||
|
||||
private String myDescription;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public SearchMethodBinding(Class<? extends IResource> theReturnResourceType, Method theMethod, String theQueryName, FhirContext theContext, Object theProvider) {
|
||||
super(theReturnResourceType, theMethod, theContext, theProvider);
|
||||
this.myQueryName = StringUtils.defaultIfBlank(theQueryName, null);
|
||||
this.myDeclaredResourceType = theMethod.getReturnType();
|
||||
this.myDeclaredResourceType = (Class<? extends IResource>) theMethod.getReturnType();
|
||||
|
||||
Description desc = theMethod.getAnnotation(Description.class);
|
||||
if (desc != null) {
|
||||
if (isNotBlank(desc.formalDefinition())) {
|
||||
myDescription = StringUtils.defaultIfBlank(desc.formalDefinition(), null);
|
||||
} else {
|
||||
myDescription = StringUtils.defaultIfBlank(desc.shortDefinition(), null);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public Class<?> getDeclaredResourceType() {
|
||||
return myDeclaredResourceType.getClass();
|
||||
public String getDescription() {
|
||||
return myDescription;
|
||||
}
|
||||
|
||||
public Class<? extends IResource> getDeclaredResourceType() {
|
||||
return myDeclaredResourceType;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -136,10 +156,10 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
return false;
|
||||
}
|
||||
|
||||
// This is used to track all the parameters so we can reject queries that
|
||||
// This is used to track all the parameters so we can reject queries that
|
||||
// have additional params we don't understand
|
||||
Set<String> methodParamsTemp = new HashSet<String>();
|
||||
|
||||
|
||||
Set<String> unqualifiedNames = theRequest.getUnqualifiedToQualifiedNames().keySet();
|
||||
Set<String> qualifiedParamNames = theRequest.getParameters().keySet();
|
||||
for (int i = 0; i < this.getParameters().size(); i++) {
|
||||
|
@ -196,7 +216,7 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
return true;
|
||||
}
|
||||
|
||||
public void setResourceType(Class<?> resourceType) {
|
||||
public void setResourceType(Class<? extends IResource> resourceType) {
|
||||
this.myDeclaredResourceType = resourceType;
|
||||
}
|
||||
|
||||
|
|
|
@ -523,7 +523,15 @@ public class RestfulServer extends HttpServlet {
|
|||
resourceMethod = resourceBinding.getMethod(r);
|
||||
}
|
||||
if (null == resourceMethod) {
|
||||
throw new InvalidRequestException("No resource method available for the supplied parameters " + params);
|
||||
StringBuilder b = new StringBuilder();
|
||||
b.append("No resource method available for ");
|
||||
b.append(theRequestType.name());
|
||||
b.append(" operation[");
|
||||
b.append(requestPath);
|
||||
b.append("]");
|
||||
b.append(" with parameters ");
|
||||
b.append(params.keySet());
|
||||
throw new InvalidRequestException(b.toString());
|
||||
}
|
||||
|
||||
resourceMethod.invokeServer(this, r, theResponse);
|
||||
|
|
|
@ -35,8 +35,10 @@ import org.apache.commons.lang3.StringUtils;
|
|||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||
import ca.uhn.fhir.model.api.ExtensionDt;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
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.RestQuery;
|
||||
import ca.uhn.fhir.model.dstu.resource.Conformance.RestResource;
|
||||
import ca.uhn.fhir.model.dstu.resource.Conformance.RestResourceOperation;
|
||||
import ca.uhn.fhir.model.dstu.resource.Conformance.RestResourceSearchParam;
|
||||
|
@ -105,8 +107,9 @@ public class ServerConformanceProvider {
|
|||
resource.getProfile().setId(new IdDt(def.getResourceProfile()));
|
||||
|
||||
TreeSet<String> includes = new TreeSet<String>();
|
||||
|
||||
Map<String, Conformance.RestResourceSearchParam> nameToSearchParam = new HashMap<String, Conformance.RestResourceSearchParam>();
|
||||
|
||||
// Map<String, Conformance.RestResourceSearchParam> nameToSearchParam = new HashMap<String,
|
||||
// Conformance.RestResourceSearchParam>();
|
||||
for (BaseMethodBinding<?> nextMethodBinding : next.getMethodBindings()) {
|
||||
RestfulOperationTypeEnum resOp = nextMethodBinding.getResourceOperationType();
|
||||
if (resOp != null) {
|
||||
|
@ -127,7 +130,7 @@ public class ServerConformanceProvider {
|
|||
if (nextMethodBinding instanceof SearchMethodBinding) {
|
||||
SearchMethodBinding searchMethodBinding = (SearchMethodBinding) nextMethodBinding;
|
||||
includes.addAll(searchMethodBinding.getIncludes());
|
||||
|
||||
|
||||
List<IParameter> params = searchMethodBinding.getParameters();
|
||||
List<SearchParameter> searchParameters = new ArrayList<SearchParameter>();
|
||||
for (IParameter nextParameter : params) {
|
||||
|
@ -152,8 +155,13 @@ public class ServerConformanceProvider {
|
|||
}
|
||||
boolean allOptional = searchParameters.get(0).isRequired() == false;
|
||||
|
||||
RestResourceSearchParam searchParam = null;
|
||||
ExtensionDt searchParamChain = null;
|
||||
RestQuery query = null;
|
||||
if (!allOptional) {
|
||||
query = rest.addQuery();
|
||||
query.getDocumentation().setValue(searchMethodBinding.getDescription());
|
||||
query.addUndeclaredExtension(false, ExtensionConstants.QUERY_RETURN_TYPE, new CodeDt(resourceName));
|
||||
}
|
||||
|
||||
for (SearchParameter nextParameter : searchParameters) {
|
||||
|
||||
String nextParamName = nextParameter.getName();
|
||||
|
@ -175,56 +183,21 @@ public class ServerConformanceProvider {
|
|||
}
|
||||
}
|
||||
|
||||
if (searchParam == null || allOptional) {
|
||||
if (!nameToSearchParam.containsKey(nextParameter.getName())) {
|
||||
RestResourceSearchParam param = resource.addSearchParam();
|
||||
param.setName(nextParamName);
|
||||
if (StringUtils.isNotBlank(chain)) {
|
||||
param.addChain(chain);
|
||||
}
|
||||
param.setDocumentation(nextParamDescription);
|
||||
param.setType(nextParameter.getParamType());
|
||||
searchParam = param;
|
||||
} else {
|
||||
searchParam = nameToSearchParam.get(nextParameter.getName());
|
||||
}
|
||||
|
||||
if (searchParam != null) {
|
||||
searchParam.setType(nextParameter.getParamType());
|
||||
}
|
||||
|
||||
RestResourceSearchParam param;
|
||||
if (query == null) {
|
||||
param = resource.addSearchParam();
|
||||
} else {
|
||||
|
||||
if (searchParamChain == null) {
|
||||
searchParamChain = searchParam.addUndeclaredExtension(false, ExtensionConstants.CONF_ADDITIONAL_PARAM);
|
||||
} else {
|
||||
searchParamChain = searchParamChain.addUndeclaredExtension(false, ExtensionConstants.CONF_ADDITIONAL_PARAM);
|
||||
}
|
||||
|
||||
ExtensionDt ext = new ExtensionDt();
|
||||
ext.setUrl(ExtensionConstants.CONF_ADDITIONAL_PARAM_NAME);
|
||||
ext.setValue(new StringDt(nextParamName));
|
||||
searchParamChain.getUndeclaredExtensions().add(ext);
|
||||
|
||||
ext = new ExtensionDt();
|
||||
ext.setUrl(ExtensionConstants.CONF_ADDITIONAL_PARAM_DESCRIPTION);
|
||||
ext.setValue(new StringDt(nextParamDescription));
|
||||
searchParamChain.getUndeclaredExtensions().add(ext);
|
||||
|
||||
ext = new ExtensionDt();
|
||||
ext.setUrl(ExtensionConstants.CONF_ADDITIONAL_PARAM_TYPE);
|
||||
if (nextParameter.getParamType() != null) {
|
||||
ext.setValue(new CodeDt(nextParameter.getParamType().getCode()));
|
||||
}
|
||||
searchParamChain.getUndeclaredExtensions().add(ext);
|
||||
|
||||
ext = new ExtensionDt();
|
||||
ext.setUrl(ExtensionConstants.CONF_ADDITIONAL_PARAM_REQUIRED);
|
||||
ext.setValue(new BooleanDt(nextParameter.isRequired()));
|
||||
searchParamChain.getUndeclaredExtensions().add(ext);
|
||||
|
||||
param = query.addParameter();
|
||||
param.addUndeclaredExtension(false, ExtensionConstants.PARAM_IS_REQUIRED, new BooleanDt(nextParameter.isRequired()));
|
||||
}
|
||||
|
||||
param.setName(nextParamName);
|
||||
if (StringUtils.isNotBlank(chain)) {
|
||||
param.addChain(chain);
|
||||
}
|
||||
param.setDocumentation(nextParamDescription);
|
||||
param.setType(nextParameter.getParamType());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -251,7 +224,7 @@ public class ServerConformanceProvider {
|
|||
for (String nextInclude : includes) {
|
||||
resource.addSearchInclude(nextInclude);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
myConformance = retVal;
|
||||
|
@ -259,7 +232,8 @@ public class ServerConformanceProvider {
|
|||
}
|
||||
|
||||
/**
|
||||
* Sets the cache property (default is true). If set to true, the same response will be returned for each invocation.
|
||||
* Sets the cache property (default is true). If set to true, the same response will be returned for each
|
||||
* invocation.
|
||||
*/
|
||||
public void setCache(boolean theCache) {
|
||||
myCache = theCache;
|
||||
|
|
|
@ -21,6 +21,9 @@ package ca.uhn.fhir.util;
|
|||
*/
|
||||
|
||||
public class ExtensionConstants {
|
||||
public static final String PARAM_IS_REQUIRED = "http://hl7api.sourceforge.net/hapi-fhir/extensions.xml#paramIsRequired";
|
||||
|
||||
public static final String QUERY_RETURN_TYPE = "http://hl7api.sourceforge.net/hapi-fhir/extensions.xml#queryReturnType";
|
||||
|
||||
public static final String CONF_ADDITIONAL_PARAM = "http://hl7api.sourceforge.net/hapi-fhir/extensions.xml#additionalParam";
|
||||
|
||||
|
@ -31,5 +34,5 @@ public class ExtensionConstants {
|
|||
public static final String CONF_ADDITIONAL_PARAM_TYPE = "http://hl7api.sourceforge.net/hapi-fhir/extensions.xml#additionalParamType";
|
||||
|
||||
public static final String CONF_ADDITIONAL_PARAM_REQUIRED = "http://hl7api.sourceforge.net/hapi-fhir/extensions.xml#additionalParamRequired";
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package ca.uhn.fhir.rest.server;
|
|||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
@ -14,6 +15,8 @@ import ca.uhn.fhir.model.api.Include;
|
|||
import ca.uhn.fhir.model.api.annotation.Description;
|
||||
import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
|
||||
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.RestQuery;
|
||||
import ca.uhn.fhir.model.dstu.resource.Conformance.RestResource;
|
||||
import ca.uhn.fhir.model.dstu.resource.Conformance.RestResourceSearchParam;
|
||||
import ca.uhn.fhir.model.dstu.resource.DiagnosticReport;
|
||||
|
@ -110,14 +113,15 @@ public class ServerConformanceProviderTest {
|
|||
rs.init(null);
|
||||
|
||||
Conformance conformance = sc.getServerConformance();
|
||||
String conf = new FhirContext().newJsonParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
String conf = new FhirContext().newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
ourLog.info(conf);
|
||||
|
||||
RestResource res = conformance.getRestFirstRep().getResourceFirstRep();
|
||||
Rest rest = conformance.getRestFirstRep();
|
||||
RestResource res = rest.getResourceFirstRep();
|
||||
assertEquals("DiagnosticReport", res.getType().getValueAsString());
|
||||
|
||||
RestResourceSearchParam p0 = res.getSearchParam().get(0);
|
||||
assertEquals("subject", p0.getName().getValue());
|
||||
RestQuery p0 = rest.getQueryFirstRep();
|
||||
assertEquals("subject", p0.getParameterFirstRep().getName().getValue());
|
||||
|
||||
assertEquals(1,res.getSearchInclude().size());
|
||||
assertEquals("DiagnosticReport.result", res.getSearchIncludeFirstRep().getValue());
|
||||
|
@ -160,6 +164,7 @@ public class ServerConformanceProviderTest {
|
|||
|
||||
public static class ProviderWithRequiredAndOptional {
|
||||
|
||||
@Description(shortDefinition="This is a search for stuff!")
|
||||
@Search
|
||||
public List<DiagnosticReport> findDiagnosticReportsByPatient (
|
||||
@RequiredParam(name=DiagnosticReport.SP_SUBJECT + '.' + Patient.SP_IDENTIFIER) IdentifierDt thePatientId,
|
||||
|
|
|
@ -375,8 +375,8 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
|||
}
|
||||
|
||||
String likeExpression = normalizeString(string);
|
||||
likeExpression = likeExpression.replace("%", "[%]") + "%";
|
||||
|
||||
likeExpression = likeExpression.replace("%", "[%]") + "%";
|
||||
|
||||
Predicate singleCode = builder.like(from.get("myValueNormalized").as(String.class), likeExpression);
|
||||
if (params instanceof StringParam && ((StringParam) params).isExact()) {
|
||||
Predicate exactCode = builder.equal(from.get("myValueExact"), string);
|
||||
|
@ -461,6 +461,32 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
|||
return new HashSet<Long>(q.getResultList());
|
||||
}
|
||||
|
||||
private Set<Long> addPredicateId(String theParamName, Set<Long> theExistingPids, Set<Long> thePids) {
|
||||
if (thePids == null || thePids.isEmpty()) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<Long> cq = builder.createQuery(Long.class);
|
||||
Root<ResourceTable> from = cq.from(ResourceTable.class);
|
||||
cq.select(from.get("myId").as(Long.class));
|
||||
|
||||
Predicate typePredicate = builder.equal(from.get("myResourceType"), myResourceName);
|
||||
Predicate idPrecidate = from.get("myId").in(thePids);
|
||||
|
||||
cq.where(builder.and(typePredicate, idPrecidate));
|
||||
|
||||
TypedQuery<Long> q = myEntityManager.createQuery(cq);
|
||||
HashSet<Long> found = new HashSet<Long>(q.getResultList());
|
||||
if (!theExistingPids.isEmpty()) {
|
||||
theExistingPids.retainAll(found);
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void addTag(IdDt theId, String theScheme, String theTerm, String theLabel) {
|
||||
BaseHasResource entity = readEntity(theId);
|
||||
|
@ -781,7 +807,7 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
|||
if (theIncludePids.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<ResourceTable> cq = builder.createQuery(ResourceTable.class);
|
||||
Root<ResourceTable> from = cq.from(ResourceTable.class);
|
||||
|
@ -843,7 +869,8 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
|||
} else {
|
||||
for (IQueryParameterType next : nextValue) {
|
||||
String value = next.getValueAsQueryToken();
|
||||
long valueLong = Long.parseLong(value);
|
||||
IdDt valueId = new IdDt(value);
|
||||
long valueLong = valueId.getIdPartAsLong();
|
||||
joinPids.add(valueLong);
|
||||
}
|
||||
if (joinPids.isEmpty()) {
|
||||
|
@ -851,6 +878,11 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
|||
}
|
||||
}
|
||||
|
||||
pids = addPredicateId(nextParamName, pids, joinPids);
|
||||
if (pids.isEmpty()) {
|
||||
return new HashSet<Long>();
|
||||
}
|
||||
|
||||
if (pids.isEmpty()) {
|
||||
pids.addAll(joinPids);
|
||||
} else {
|
||||
|
@ -858,47 +890,48 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
|
|||
}
|
||||
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
RuntimeSearchParam nextParamDef = resourceDef.getSearchParam(nextParamName);
|
||||
if (nextParamDef != null) {
|
||||
if (nextParamDef.getParamType() == SearchParamTypeEnum.TOKEN) {
|
||||
for (List<IQueryParameterType> nextAnd : nextParamEntry.getValue()) {
|
||||
pids = addPredicateToken(nextParamName, pids, nextAnd);
|
||||
if (pids.isEmpty()) {
|
||||
return new HashSet<Long>();
|
||||
RuntimeSearchParam nextParamDef = resourceDef.getSearchParam(nextParamName);
|
||||
if (nextParamDef != null) {
|
||||
if (nextParamDef.getParamType() == SearchParamTypeEnum.TOKEN) {
|
||||
for (List<IQueryParameterType> nextAnd : nextParamEntry.getValue()) {
|
||||
pids = addPredicateToken(nextParamName, pids, nextAnd);
|
||||
if (pids.isEmpty()) {
|
||||
return new HashSet<Long>();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (nextParamDef.getParamType() == SearchParamTypeEnum.STRING) {
|
||||
for (List<IQueryParameterType> nextAnd : nextParamEntry.getValue()) {
|
||||
pids = addPredicateString(nextParamName, pids, nextAnd);
|
||||
if (pids.isEmpty()) {
|
||||
return new HashSet<Long>();
|
||||
} else if (nextParamDef.getParamType() == SearchParamTypeEnum.STRING) {
|
||||
for (List<IQueryParameterType> nextAnd : nextParamEntry.getValue()) {
|
||||
pids = addPredicateString(nextParamName, pids, nextAnd);
|
||||
if (pids.isEmpty()) {
|
||||
return new HashSet<Long>();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (nextParamDef.getParamType() == SearchParamTypeEnum.QUANTITY) {
|
||||
for (List<IQueryParameterType> nextAnd : nextParamEntry.getValue()) {
|
||||
pids = addPredicateQuantity(nextParamName, pids, nextAnd);
|
||||
if (pids.isEmpty()) {
|
||||
return new HashSet<Long>();
|
||||
} else if (nextParamDef.getParamType() == SearchParamTypeEnum.QUANTITY) {
|
||||
for (List<IQueryParameterType> nextAnd : nextParamEntry.getValue()) {
|
||||
pids = addPredicateQuantity(nextParamName, pids, nextAnd);
|
||||
if (pids.isEmpty()) {
|
||||
return new HashSet<Long>();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (nextParamDef.getParamType() == SearchParamTypeEnum.DATE) {
|
||||
for (List<IQueryParameterType> nextAnd : nextParamEntry.getValue()) {
|
||||
pids = addPredicateDate(nextParamName, pids, nextAnd);
|
||||
if (pids.isEmpty()) {
|
||||
return new HashSet<Long>();
|
||||
} else if (nextParamDef.getParamType() == SearchParamTypeEnum.DATE) {
|
||||
for (List<IQueryParameterType> nextAnd : nextParamEntry.getValue()) {
|
||||
pids = addPredicateDate(nextParamName, pids, nextAnd);
|
||||
if (pids.isEmpty()) {
|
||||
return new HashSet<Long>();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (nextParamDef.getParamType() == SearchParamTypeEnum.REFERENCE) {
|
||||
for (List<IQueryParameterType> nextAnd : nextParamEntry.getValue()) {
|
||||
pids = addPredicateReference(nextParamName, pids, nextAnd);
|
||||
if (pids.isEmpty()) {
|
||||
return new HashSet<Long>();
|
||||
} else if (nextParamDef.getParamType() == SearchParamTypeEnum.REFERENCE) {
|
||||
for (List<IQueryParameterType> nextAnd : nextParamEntry.getValue()) {
|
||||
pids = addPredicateReference(nextParamName, pids, nextAnd);
|
||||
if (pids.isEmpty()) {
|
||||
return new HashSet<Long>();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("Don't know how to handle parameter of type: " + nextParamDef.getParamType());
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("Don't know how to handle parameter of type: " + nextParamDef.getParamType());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -447,6 +447,37 @@ public class FhirResourceDaoTest {
|
|||
assertEquals(0, patients.size());
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testSearchByIdParam() {
|
||||
IdDt id1;
|
||||
{
|
||||
Patient patient = new Patient();
|
||||
patient.addIdentifier("urn:system", "001");
|
||||
id1 = ourPatientDao.create(patient).getId();
|
||||
}
|
||||
IdDt id2;
|
||||
{
|
||||
Organization patient = new Organization();
|
||||
patient.addIdentifier("urn:system", "001");
|
||||
id2 = ourOrganizationDao.create(patient).getId();
|
||||
}
|
||||
|
||||
|
||||
Map<String, IQueryParameterType> params = new HashMap<String, IQueryParameterType>();
|
||||
params.put("_id", new StringDt(id1.getIdPart()));
|
||||
assertEquals(1, toList(ourPatientDao.search(params)).size());
|
||||
|
||||
params.put("_id", new StringDt("9999999999999999"));
|
||||
assertEquals(0, toList(ourPatientDao.search(params)).size());
|
||||
|
||||
params.put("_id", new StringDt(id2.getIdPart()));
|
||||
assertEquals(0, toList(ourPatientDao.search(params)).size());
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSearchResourceLinkWithChain() {
|
||||
|
|
|
@ -30,7 +30,8 @@
|
|||
<!-- <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>
|
||||
<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="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="password" value="SA"/>
|
||||
|
|
|
@ -1,56 +0,0 @@
|
|||
<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>
|
|
@ -0,0 +1,67 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head th:include="tmpl-head :: head">
|
||||
<title>About This Server</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<form action="" method="get" id="outerForm">
|
||||
<div th:replace="tmpl-navbar-top :: top" ></div>
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div th:replace="tmpl-navbar-left :: left" ></div>
|
||||
|
||||
<div class="col-sm-9 col-sm-offset-3 col-md-9 col-md-offset-3 main">
|
||||
|
||||
<div th:replace="tmpl-banner :: banner"></div>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">About This Server</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="pull-right">
|
||||
<object data="img/fhirtest-architecture.svg" width="383" height="369" type="image/svg+xml" />
|
||||
</div>
|
||||
<p>
|
||||
This server provides a nearly complete implementation of the FHIR Specification
|
||||
using a 100% open source software stack. It is hosted by University Health Network.
|
||||
</p>
|
||||
<p>
|
||||
The architecture in use here is shown in the image on the right. This server is built
|
||||
from a number of modules of the
|
||||
<a href="https://github.com/jamesagnew/hapi-fhir/">HAPI FHIR</a>
|
||||
project, which is a 100% open-source (Apache 2.0 Licensed) Java based
|
||||
implementation of the FHIR specification.
|
||||
</p>
|
||||
<p>
|
||||
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">Data On This Server</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<p>
|
||||
This server is regularly loaded with a standard set of test data sourced
|
||||
from UHN's own testing environment. Do not use this server to store any data
|
||||
that you will need later, as we will be regularly resetting it.
|
||||
</p>
|
||||
<p>
|
||||
This is not a production server and it provides no privacy. Do not store any
|
||||
confidential data here.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div th:replace="tmpl-footer :: footer" ></div>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
|
@ -1,75 +0,0 @@
|
|||
<!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>
|
|
@ -0,0 +1,115 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="383px" height="369px" version="1.1">
|
||||
<defs />
|
||||
<g transform="translate(0.5,0.5)">
|
||||
<rect x="1" y="177" width="170" height="50" rx="8" ry="8" fill="#ffffff" stroke="#000000" pointer-events="none" />
|
||||
<g transform="translate(3,187)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="all" width="166" height="30" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml"
|
||||
style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 166px; white-space: normal; text-align: center;">
|
||||
<a href="https://github.com/jamesagnew/hapi-fhir/tree/master/hapi-fhir-base">hapi-fhir-base</a>
|
||||
<br />
|
||||
(RESTful Server)
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="83" y="21" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">[Not supported by viewer]</text>
|
||||
</switch>
|
||||
</g>
|
||||
<rect x="1" y="247" width="170" height="50" rx="8" ry="8" fill="#ffffff" stroke="#000000" pointer-events="none" />
|
||||
<g transform="translate(3,250)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="all" width="166" height="45" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml"
|
||||
style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 166px; white-space: normal; text-align: center;">
|
||||
<a href="https://github.com/jamesagnew/hapi-fhir/tree/master/hapi-fhir-jpaserver-base">hapi-fhir-jpaserver-base</a>
|
||||
<br />
|
||||
Hibernate + Spring
|
||||
<br />
|
||||
(Database Abstraction)
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="83" y="29" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">[Not supported by viewer]</text>
|
||||
</switch>
|
||||
</g>
|
||||
<rect x="1" y="317" width="170" height="50" rx="8" ry="8" fill="#ffffff" stroke="#000000" pointer-events="none" />
|
||||
<g transform="translate(3,327)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="all" width="166" height="30" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml"
|
||||
style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 166px; white-space: normal; text-align: center;">
|
||||
Apache Derby
|
||||
<br />
|
||||
(Database)
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="83" y="21" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">[Not supported by viewer]</text>
|
||||
</switch>
|
||||
</g>
|
||||
<rect x="211" y="177" width="170" height="50" rx="8" ry="8" fill="#ffffff" stroke="#000000" pointer-events="none" />
|
||||
<g transform="translate(213,179)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="all" width="166" height="45" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml"
|
||||
style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 166px; white-space: normal; text-align: center;">
|
||||
<a href="https://github.com/jamesagnew/hapi-fhir/tree/master/hapi-fhir-testpage-overlay">hapi-fhir-testpage-overlay</a>
|
||||
<br />
|
||||
Thymeleaf + Spring
|
||||
<br />
|
||||
(Testing Application)
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="83" y="21" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica">[Not supported by viewer]</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 86 247 Q 86 247 86 233" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none" />
|
||||
<path d="M 86 228 L 90 235 L 86 233 L 83 235 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none" />
|
||||
<path d="M 86 317 Q 86 317 86 303" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none" />
|
||||
<path d="M 86 298 L 90 305 L 86 303 L 83 305 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none" />
|
||||
<path d="M 171 202 Q 171 202 205 202" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none" />
|
||||
<path d="M 210 202 L 203 206 L 205 202 L 203 199 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none" />
|
||||
<image x="111" y="32" width="40" height="40" xlink:href="https://cdn1.iconfinder.com/data/icons/professional-toolbar-icons-2/64/Web_server_internet_vector.png"
|
||||
preserveAspectRatio="none" pointer-events="none" />
|
||||
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
|
||||
<text x="131" y="12">FHIR Clients and</text>
|
||||
<text x="131" y="26">Applications</text>
|
||||
</g>
|
||||
<image x="221" y="32" width="40" height="40" xlink:href="https://cdn2.iconfinder.com/data/icons/crystalproject/128x128/filesystems/Globe2.png" preserveAspectRatio="none"
|
||||
pointer-events="none" />
|
||||
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
|
||||
<text x="241" y="26">Browsers</text>
|
||||
</g>
|
||||
<path d="M 86 177 L 86 132 Q 86 122 96 122 L 111 122 Q 121 122 121 112 L 121 78" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none" />
|
||||
<path d="M 121 73 L 125 80 L 121 78 L 118 80 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none" />
|
||||
<path d="M 296 177 L 296 132 Q 296 122 286 122 L 251 122 Q 241 122 241 112 L 241 78" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none" />
|
||||
<path d="M 241 73 L 245 80 L 241 78 L 238 80 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none" />
|
||||
<rect x="71" y="97" width="240" height="50" rx="8" ry="8" fill-opacity="0.35" fill="#ababab" stroke="#000000" stroke-opacity="0.35" pointer-events="none" />
|
||||
<g transform="translate(73,107)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="all" width="236" height="30" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml"
|
||||
style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.26; vertical-align: top; width: 236px; white-space: normal; font-weight: bold; text-align: center;">
|
||||
Apache
|
||||
<br />
|
||||
HTTPd
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="118" y="21" fill="#000000" text-anchor="middle" font-size="12px" font-family="Helvetica" font-weight="bold">[Not supported by viewer]</text>
|
||||
</switch>
|
||||
</g>
|
||||
<image x="276" y="292" width="40" height="40" xlink:href="https://cdn2.iconfinder.com/data/icons/seo-web-optomization-ultimate-set/512/web_hosting-128.png"
|
||||
preserveAspectRatio="none" pointer-events="none" />
|
||||
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
|
||||
<text x="296" y="350">Other Public</text>
|
||||
<text x="296" y="364">FHIR Servers</text>
|
||||
</g>
|
||||
<path d="M 296 292 L 296 233" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none" />
|
||||
<path d="M 296 228 L 300 235 L 296 233 L 293 235 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none" />
|
||||
<image x="316" y="33" width="35" height="37" xlink:href="https://cdn2.iconfinder.com/data/icons/ios-7-icons/50/user_male4-128.png" preserveAspectRatio="none"
|
||||
pointer-events="none" />
|
||||
<g fill="#000000" font-family="Helvetica" text-anchor="middle" font-size="12px">
|
||||
<text x="334" y="27">(You are here)</text>
|
||||
</g>
|
||||
<path d="M 316 52 L 267 52" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="none" />
|
||||
<path d="M 262 52 L 269 48 L 267 52 L 269 55 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="none" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 7.6 KiB |
|
@ -22,10 +22,13 @@ public class UhnFhirTestApp {
|
|||
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
// new File("target/testdb").mkdirs();
|
||||
System.setProperty("fhir.db.location", "/target/testdb");
|
||||
|
||||
int myPort = 8888;
|
||||
String base = "http://localhost:" + myPort + "/base";
|
||||
|
||||
// new File("target/testdb").mkdirs();
|
||||
System.setProperty("fhir.db.location", "./target/testdb");
|
||||
System.setProperty("fhir.baseurl", base);
|
||||
|
||||
Server server = new Server(myPort);
|
||||
|
||||
WebAppContext root = new WebAppContext();
|
||||
|
@ -40,7 +43,6 @@ public class UhnFhirTestApp {
|
|||
|
||||
server.start();
|
||||
|
||||
String base = "http://localhost:" + myPort + "/base";
|
||||
// base = "http://fhir.healthintersections.com.au/open";
|
||||
// base = "http://spark.furore.com/fhir";
|
||||
|
||||
|
|
|
@ -36,6 +36,7 @@ import ca.uhn.fhir.model.api.IResource;
|
|||
import ca.uhn.fhir.model.api.Include;
|
||||
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.RestQuery;
|
||||
import ca.uhn.fhir.model.dstu.resource.Conformance.RestResource;
|
||||
import ca.uhn.fhir.model.primitive.DateTimeDt;
|
||||
import ca.uhn.fhir.model.primitive.DecimalDt;
|
||||
|
@ -51,6 +52,7 @@ import ca.uhn.fhir.rest.server.EncodingEnum;
|
|||
import ca.uhn.fhir.to.model.HomeRequest;
|
||||
import ca.uhn.fhir.to.model.ResourceRequest;
|
||||
import ca.uhn.fhir.to.model.TransactionRequest;
|
||||
import ca.uhn.fhir.util.ExtensionConstants;
|
||||
|
||||
@org.springframework.stereotype.Controller()
|
||||
public class Controller {
|
||||
|
@ -68,6 +70,18 @@ public class Controller {
|
|||
@Autowired
|
||||
private TemplateEngine myTemplateEngine;
|
||||
|
||||
@RequestMapping(value = { "/about" })
|
||||
public String about( final HomeRequest theRequest, final ModelMap theModel) {
|
||||
addCommonParams(theRequest, theModel);
|
||||
GenericClient client = theRequest.newClient(myCtx, myConfig);
|
||||
loadAndAddConformance(theRequest, theModel, client);
|
||||
|
||||
theModel.put("notHome", true);
|
||||
theModel.put("extraBreadcrumb", "About");
|
||||
|
||||
return "about";
|
||||
}
|
||||
|
||||
@RequestMapping(value = { "/transaction" })
|
||||
public String actionTransaction(final TransactionRequest theRequest, final BindingResult theBindingResult, final ModelMap theModel) {
|
||||
addCommonParams(theRequest, theModel);
|
||||
|
@ -170,36 +184,41 @@ public class Controller {
|
|||
loadAndAddConformance(theRequest, theModel, client);
|
||||
|
||||
Class<? extends IResource> resType = null;
|
||||
long start = System.currentTimeMillis();
|
||||
if (isNotBlank(theReq.getParameter(PARAM_RESOURCE))) {
|
||||
RuntimeResourceDefinition def;
|
||||
try {
|
||||
def = getResourceType(theReq);
|
||||
} catch (ServletException e) {
|
||||
theModel.put("errorMsg", e.toString());
|
||||
return "resource";
|
||||
}
|
||||
|
||||
resType = def.getImplementingClass();
|
||||
String id = theReq.getParameter("resource-tags-id");
|
||||
if (isNotBlank(id)) {
|
||||
String vid = theReq.getParameter("resource-tags-vid");
|
||||
if (isNotBlank(vid)) {
|
||||
client.getTags().forResource(resType, id, vid).execute();
|
||||
} else {
|
||||
client.getTags().forResource(resType, id).execute();
|
||||
}
|
||||
} else {
|
||||
client.getTags().forResource(resType).execute();
|
||||
}
|
||||
} else {
|
||||
client.getTags().execute();
|
||||
}
|
||||
long delay = System.currentTimeMillis() - start;
|
||||
|
||||
ResultType returnsResource = ResultType.TAGLIST;
|
||||
String outcomeDescription = "Tag List";
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
try {
|
||||
if (isNotBlank(theReq.getParameter(PARAM_RESOURCE))) {
|
||||
RuntimeResourceDefinition def;
|
||||
try {
|
||||
def = getResourceType(theReq);
|
||||
} catch (ServletException e) {
|
||||
theModel.put("errorMsg", e.toString());
|
||||
return "resource";
|
||||
}
|
||||
|
||||
resType = def.getImplementingClass();
|
||||
String id = theReq.getParameter("resource-tags-id");
|
||||
if (isNotBlank(id)) {
|
||||
String vid = theReq.getParameter("resource-tags-vid");
|
||||
if (isNotBlank(vid)) {
|
||||
client.getTags().forResource(resType, id, vid).execute();
|
||||
} else {
|
||||
client.getTags().forResource(resType, id).execute();
|
||||
}
|
||||
} else {
|
||||
client.getTags().forResource(resType).execute();
|
||||
}
|
||||
} else {
|
||||
client.getTags().execute();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
returnsResource = ResultType.NONE;
|
||||
ourLog.warn("Failed to invoke server", e);
|
||||
}
|
||||
long delay = System.currentTimeMillis() - start;
|
||||
|
||||
processAndAddLastClientInvocation(client, returnsResource, theModel, delay, outcomeDescription);
|
||||
|
||||
return "result";
|
||||
|
@ -311,6 +330,8 @@ public class Controller {
|
|||
RuntimeResourceDefinition def = myCtx.getResourceDefinition(theRequest.getResource());
|
||||
|
||||
TreeSet<String> includes = new TreeSet<String>();
|
||||
List<RestQuery> queries = new ArrayList<Conformance.RestQuery>();
|
||||
boolean haveSearchParams = false;
|
||||
for (Rest nextRest : conformance.getRest()) {
|
||||
for (RestResource nextRes : nextRest.getResource()) {
|
||||
if (nextRes.getType().getValue().equals(resourceName)) {
|
||||
|
@ -319,10 +340,25 @@ public class Controller {
|
|||
includes.add(next.getValue());
|
||||
}
|
||||
}
|
||||
if (nextRes.getSearchParam().size() > 0) {
|
||||
haveSearchParams = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (RestQuery nextQuery : nextRest.getQuery()) {
|
||||
List<ExtensionDt> returnTypeExt = nextQuery.getUndeclaredExtensionsByUrl(ExtensionConstants.QUERY_RETURN_TYPE);
|
||||
if (returnTypeExt != null) {
|
||||
for (ExtensionDt nextExt : returnTypeExt) {
|
||||
if (resourceName.equals(nextExt.getValueAsPrimitive().getValueAsString())) {
|
||||
queries.add(nextQuery);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
theModel.put("includes", includes);
|
||||
theModel.put("queries", queries);
|
||||
theModel.put("haveSearchParams", haveSearchParams);
|
||||
|
||||
if (isNotBlank(theRequest.getUpdateId())) {
|
||||
String updateId = theRequest.getUpdateId();
|
||||
|
@ -510,7 +546,7 @@ public class Controller {
|
|||
}
|
||||
|
||||
private String preProcessMessageBody(String theBody) {
|
||||
if(theBody==null) {
|
||||
if (theBody == null) {
|
||||
return "";
|
||||
}
|
||||
String retVal = theBody.trim();
|
||||
|
@ -774,7 +810,10 @@ public class Controller {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
theModel.put("conf", conformance);
|
||||
theModel.put("requiredParamExtension", ExtensionConstants.PARAM_IS_REQUIRED);
|
||||
|
||||
return conformance;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
<bean class="ca.uhn.fhir.to.TesterConfig">
|
||||
<property name="servers">
|
||||
<list>
|
||||
<value>test, TEST, http://uhnvesb01d.uhn.on.ca:25180/uhn-fhir-service-1.2/</value>
|
||||
<!-- <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>hi , Health Intersections , http://fhir.healthintersections.com.au/open</value>
|
||||
<value>furore , Spark - Furore Reference Server , http://spark.furore.com/fhir</value>
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head th:include="tmpl-head :: head">
|
||||
<title>About This Server</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div th:replace="tmpl-navbar-top :: top" ></div>
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div th:replace="tmpl-navbar-left :: left" ></div>
|
||||
|
||||
<div class="col-sm-9 col-sm-offset-3 col-md-9 col-md-offset-3 main">
|
||||
|
||||
<div th:replace="tmpl-banner :: banner"></div>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">About This Server</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
About...
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div th:replace="tmpl-footer :: footer" ></div>
|
||||
</body>
|
||||
</html>
|
|
@ -67,7 +67,7 @@
|
|||
Retrieve the server's <b>conformance</b> statement.
|
||||
</div>
|
||||
<div class="row-fluid">
|
||||
<div class="col-sm-2 form-group">
|
||||
<div class="col-sm-3 form-group">
|
||||
<button type="button" id="fetch-conformance-btn"
|
||||
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>
|
||||
|
@ -92,7 +92,7 @@
|
|||
the server.
|
||||
</div>
|
||||
<div class="row-fluid top-buffer">
|
||||
<div class="col-sm-2">
|
||||
<div class="col-sm-3">
|
||||
<button type="button" id="server-history-btn"
|
||||
data-loading-text="Loading <i class='fa fa-spinner fa-spin'/>" class="btn btn-primary btn-block">
|
||||
<i class="fa fa-calendar"></i>
|
||||
|
@ -152,14 +152,14 @@
|
|||
store all resources within a single atomic transaction.
|
||||
</div>
|
||||
<div class="row-fluid">
|
||||
<div class="col-sm-2">
|
||||
<div class="col-sm-3">
|
||||
<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='col-sm-9'>
|
||||
<div class="form-group">
|
||||
<div class='input-group'>
|
||||
<div class="input-group-addon">
|
||||
|
@ -206,7 +206,7 @@
|
|||
Show all of the tags currently in use on the server
|
||||
</div>
|
||||
<div class="row-fluid">
|
||||
<div class="col-sm-2 form-group">
|
||||
<div class="col-sm-3 form-group">
|
||||
<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">
|
||||
<i class="fa fa-tags"></i>
|
||||
|
|
|
@ -43,16 +43,56 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default" th:if="${resourceName.empty} == false">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">Search</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<!-- Nav tabs -->
|
||||
<ul class="nav nav-tabs" role="tablist" id="resource-nav-tabs">
|
||||
<!-- Search Tab -->
|
||||
<li th:if="${haveSearchParams}" >
|
||||
<a href="#tab-search" role="tab" data-toggle="tab">Search</a>
|
||||
</li>
|
||||
<li th:if="${!haveSearchParams}" class="disabled">
|
||||
<a href="#tab-search" role="tab">Search</a>
|
||||
</li>
|
||||
<!-- Queries Tab -->
|
||||
<li th:if="${!queries.empty}">
|
||||
<a href="#tab-queries" role="tab" data-toggle="tab">Queries</a>
|
||||
</li>
|
||||
<li th:if="${queries.empty}" class="disabled">
|
||||
<a href="#tab-queries" role="tab">Queries</a>
|
||||
</li>
|
||||
<!-- CRUD Tab -->
|
||||
<li th:class="(${!haveSearchParams} and ${queries.empty}) ? 'active'">
|
||||
<a href="#tab-otheractions" role="tab" data-toggle="tab">CRUD Operations</a>
|
||||
</li>
|
||||
<!-- Tags Tab -->
|
||||
<li>
|
||||
<a href="#tab-tags" role="tab" data-toggle="tab">Tags</a>
|
||||
</li>
|
||||
</ul>
|
||||
<script type="text/javascript">
|
||||
$( document ).ready(function() {
|
||||
for (var i = 0; i != 4; i++){
|
||||
if (!$('#resource-nav-tabs li:eq('+i+')').hasClass('disabled')) {
|
||||
$('#resource-nav-tabs li:eq('+i+') a').tab('show');
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- Tab panes -->
|
||||
<div class="tab-content">
|
||||
|
||||
<!-- *************************************************** -->
|
||||
<!-- Search Tab -->
|
||||
<!-- *************************************************** -->
|
||||
<div class="tab-pane active" id="tab-search">
|
||||
|
||||
<!-- Search contents -->
|
||||
<div class="container-fluid">
|
||||
<div class="row-fluid">
|
||||
<div class="col-sm-2">
|
||||
<div class="col-sm-2" style="padding-left: 0px;">
|
||||
<button type="button" id="search-btn" class="btn btn-primary btn-block"
|
||||
data-loading-text="Searching <i class='fa fa-spinner fa-spin'/>" >
|
||||
data-loading-text="Searching <i class='fa fa-spinner fa-spin'/>">
|
||||
<span class="glyphicon glyphicon-search"></span>
|
||||
Search
|
||||
</button>
|
||||
|
@ -62,6 +102,15 @@
|
|||
$('#search-btn').click(function() {
|
||||
var btn = $(this);
|
||||
btn.button('loading');
|
||||
var sd = $('#tab-search').find('input').each(function() {
|
||||
if (this.id) {
|
||||
if (this.id.substring(0,4) == 'inc_') {
|
||||
this.name = '_include';
|
||||
} else {
|
||||
this.name = this.id;
|
||||
}
|
||||
}
|
||||
});
|
||||
$("#outerForm").attr("action", "search").submit();
|
||||
});
|
||||
</script>
|
||||
|
@ -82,7 +131,7 @@
|
|||
<div class="row-fluid">
|
||||
<span th:each="include : ${includes}" class="includeCheckContainer">
|
||||
<span class="includeCheckCheck">
|
||||
<input type="checkbox" name="_include" th:value="${include}" th:id="'inc_' + ${include}" />
|
||||
<input type="checkbox" th:value="${include}" th:id="'inc_' + ${include}" />
|
||||
</span>
|
||||
<span class="includeCheckName" th:text="${include}"/>
|
||||
</span>
|
||||
|
@ -99,30 +148,30 @@
|
|||
<div class="input-group-addon">
|
||||
Limit
|
||||
</div>
|
||||
<input type="text" class="form-control" name="resource-search-limit" placeholder="max # returned"/>
|
||||
<input type="text" class="form-control" id="resource-search-limit" placeholder="max # returned"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--
|
||||
<div class="col-sm-1">
|
||||
<button type="button" class="btn btn-success btn-block">Add
|
||||
<span class="glyphicon glyphicon-plus"></span>
|
||||
</button>
|
||||
</div>
|
||||
-->
|
||||
<br clear="all"/>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default" th:if="${resourceName.empty} == false">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">Operations</h3>
|
||||
<!-- End of search Tab -->
|
||||
|
||||
<!-- *************************************************** -->
|
||||
<!-- Queries Tab -->
|
||||
<!-- *************************************************** -->
|
||||
<div class="tab-pane" id="tab-queries">
|
||||
<div th:replace="tmpl-queries :: queries-list" ></div>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<!-- End of queries tab -->
|
||||
|
||||
<!-- *************************************************** -->
|
||||
<!-- Other Operations Tab -->
|
||||
<!-- *************************************************** -->
|
||||
<div class="tab-pane" id="tab-otheractions">
|
||||
<div class="container-fluid">
|
||||
|
||||
<!-- Read/VRead -->
|
||||
|
@ -416,9 +465,18 @@
|
|||
</script>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<!-- End of other operations tab -->
|
||||
|
||||
<!-- *************************************************** -->
|
||||
<!-- Queries Tab -->
|
||||
<!-- *************************************************** -->
|
||||
<div class="tab-pane" id="tab-tags">
|
||||
<div class="container-fluid">
|
||||
|
||||
<!-- Get Tags -->
|
||||
|
||||
<br clear="all"/>
|
||||
|
||||
<div class="row-fluid">
|
||||
Show all of the tags currently in use on the server for this resource type. If an ID is specified,
|
||||
returns only tags posted to the given version. If an ID and a version are specified,
|
||||
|
@ -465,13 +523,14 @@
|
|||
});
|
||||
</script>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<!-- End of queries tab -->
|
||||
|
||||
</div>
|
||||
<!-- End of tab pane -->
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -76,7 +76,11 @@
|
|||
<span class="glyphicon glyphicon glyphicon-chevron-left"></span>
|
||||
Response
|
||||
</td>
|
||||
<td th:text="${resultStatus}">HTTP 200 OK</td>
|
||||
<td>
|
||||
<i th:if="${resultStatus.contains(' 2')}" class="fa fa-check" style="color:#4E4;"/>
|
||||
<i th:if="${!resultStatus.contains(' 2')}" class="fa fa-warning" style="color:#E44;"/>
|
||||
<th:block th:text="${resultStatus}"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Response Headers</td>
|
||||
|
@ -167,10 +171,14 @@
|
|||
<tbody>
|
||||
<tr th:each="entry : ${bundle.entries}">
|
||||
<td style="white-space: nowrap;">
|
||||
<button class="btn btn-primary btn-xs" th:onclick="'readFromEntriesTable(this, \'' + ${entry.resource.id.resourceType} + '\', \'' + ${entry.resource.id.idPart} + '\', \'' + ${#strings.defaultString(entry.resource.id.versionIdPart,'')} + '\');'" type="submit" name="action" value="read" data-loading-text="Loading <i class='fa fa-spinner fa-spin'/>" ><i class="fa fa-book"></i> Read</button>
|
||||
<button class="btn btn-primary btn-xs" th:onclick="'updateFromEntriesTable(this, \'' + ${entry.resource.id.resourceType} + '\', \'' + ${entry.resource.id.idPart} + '\', \'' + ${#strings.defaultString(entry.resource.id.versionIdPart, '')} + '\');'" type="submit" name="action" value="home"><i class="fa fa-pencil" data-loading-text="Loading <i class='fa fa-spinner fa-spin'/>" ></i> Update</button>
|
||||
<th:block th:if="${entry.resource} != null">
|
||||
<button class="btn btn-primary btn-xs" th:onclick="'readFromEntriesTable(this, \'' + ${entry.resource.id.resourceType} + '\', \'' + ${entry.resource.id.idPart} + '\', \'' + ${#strings.defaultString(entry.resource.id.versionIdPart,'')} + '\');'" type="submit" name="action" value="read" data-loading-text="Loading <i class='fa fa-spinner fa-spin'/>" ><i class="fa fa-book"></i> Read</button>
|
||||
<button class="btn btn-primary btn-xs" th:onclick="'updateFromEntriesTable(this, \'' + ${entry.resource.id.resourceType} + '\', \'' + ${entry.resource.id.idPart} + '\', \'' + ${#strings.defaultString(entry.resource.id.versionIdPart, '')} + '\');'" type="submit" name="action" value="home"><i class="fa fa-pencil" data-loading-text="Loading <i class='fa fa-spinner fa-spin'/>" ></i> Update</button>
|
||||
</th:block>
|
||||
</td>
|
||||
<td>
|
||||
<th:block th:if="${entry.resource} != null" th:text="${entry.resource.id.toUnqualified()}"/>
|
||||
</td>
|
||||
<td><th:block th:text="${entry.resource.id.toUnqualified()}"/></td>
|
||||
<td><small th:text="${entry.title}"/></td>
|
||||
<td th:if="${entry.updated.value} == null"></td>
|
||||
<td th:if="${entry.updated.value} != null and ${entry.updated.today} == true" th:text="${#dates.format(entry.updated.value, 'HH:mm:ss')}"></td>
|
||||
|
|
|
@ -63,7 +63,7 @@
|
|||
<h4>Server</h4>
|
||||
|
||||
<ul class="nav nav-sidebar">
|
||||
<li th:class="${resourceName.empty} ? 'active' : ''">
|
||||
<li th:class="(${notHome} == null and ${resourceName} != null and ${resourceName.empty}) ? 'active' : ''">
|
||||
<a href="#" onclick="doAction(this, 'home', null);">Server Home/Actions</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
<ul th:fragment="farright">
|
||||
<li><a href="https://github.com/jamesagnew/hapi-fhir"><i class="fa fa-github topbarIcon" /> Source Code</a></li>
|
||||
<li><a href="about"><i class="fa fa-question-circle topbarIcon" /> About This Server</a></li>
|
||||
</ul>
|
|
@ -11,26 +11,29 @@
|
|||
</button>
|
||||
<a class="navbar-brand"
|
||||
th:href="'home?encoding=' + ${encoding} + '&pretty=' + ${pretty}">
|
||||
<i class="fa fa-home" /> HAPI FHIR
|
||||
<i class="fa fa-home topbarIcon" /> HAPI FHIR
|
||||
</a>
|
||||
<a class="navbar-left navbarBreadcrumb hidden-xs hidden-sm"
|
||||
th:if="${resourceName.empty} == false"
|
||||
th:if="${resourceName} != null and ${resourceName.empty} == false"
|
||||
th:href="'resource?encoding=' + ${encoding} + '&pretty=' + ${pretty} + '&resource=' + ${resourceName}">
|
||||
<span class="glyphicon glyphicon-chevron-right"></span> <span
|
||||
th:text="'Resource[' + ${resourceName} + ']'"></span>
|
||||
th:text="${resourceName}"></span>
|
||||
</a>
|
||||
<div class="navbar-left navbarBreadcrumb hidden-xs hidden-sm"
|
||||
th:if="${outcomeDescription} != null">
|
||||
<span class="glyphicon glyphicon-chevron-right"></span> <span
|
||||
th:text="${outcomeDescription}"></span>
|
||||
</div>
|
||||
<div class="navbar-left navbarBreadcrumb hidden-xs hidden-sm"
|
||||
th:if="${extraBreadcrumb} != null">
|
||||
<span class="glyphicon glyphicon-chevron-right"></span> <span
|
||||
th:text="${extraBreadcrumb}"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li class="dropdown"><a href="#" class="dropdown-toggle"
|
||||
data-toggle="dropdown"> <span id="serverSelectorFhirIcon" class="glyphicon glyphicon-fire" style="color: #66AAFF" />
|
||||
<span id="serverSelectorName" th:text="'Server: ' + ${baseName}" /> <span class="caret" />
|
||||
</a>
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><span id="serverSelectorFhirIcon" class="glyphicon glyphicon-fire topbarIcon" /> <span id="serverSelectorName" th:text="'Server: ' + ${baseName}" /> <span class="caret" /></a>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
<li th:each="serverEntry : ${serverEntries}">
|
||||
<a th:href="'javascript:selectServer(\'' + ${serverEntry.key} + '\');'">
|
||||
|
@ -40,10 +43,9 @@
|
|||
<th:block th:text="${serverEntry.value}"/>
|
||||
</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li><a href="https://github.com/jamesagnew/hapi-fhir"><i
|
||||
class="fa fa-github" /> Source Code</a></li>
|
||||
<li><a href="#">About</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<th:block th:include="tmpl-navbar-top-farright :: farright"/>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<div th:fragment="queries-list" class="container-fluid">
|
||||
<div th:each="query,queryIterStat : ${queries}" class="panel panel-default" th:id="'search-div-' + ${queryIterStat.index}" >
|
||||
<div class="panel-heading">
|
||||
<div class="pull-right">
|
||||
<button type="button" th:id="'search-btn-' + ${queryIterStat.index}" class="btn btn-primary btn-block"
|
||||
data-loading-text="Searching <i class='fa fa-spinner fa-spin'/>">
|
||||
<span class="glyphicon glyphicon-search"></span>
|
||||
Execute
|
||||
|
||||
</button>
|
||||
<script type="text/javascript">
|
||||
var searchButtonName = '<th:block th:text="'search-btn-' + ${queryIterStat.index}"/>';
|
||||
$('#' + searchButtonName).click(function() {
|
||||
var btn = $(this);
|
||||
btn.button('loading');
|
||||
var searchDiv = '<th:block th:text="'search-div-' + ${queryIterStat.index}"/>';
|
||||
var sd = $('#' + searchDiv).find('input').each(function() {
|
||||
this.name = this.id;
|
||||
});
|
||||
$("#outerForm").attr("action", "search").submit();
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
<h4>
|
||||
Query
|
||||
<small th:if="${!query.name.empty}" th:text="${query.name.value}"/>
|
||||
</h4>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<p th:if="${!query.documentation.empty}" th:text="${query.documentation.value}"></p>
|
||||
<div th:each="param,paramIterStat : ${query.parameter}"
|
||||
th:id="'search-param-rowopts-' + ${queryIterStat.index} + '-' + ${paramIterStat.index}"
|
||||
th:class="'row queryParameter ' + (${paramIterStat.odd} ? 'queryParameterOdd')">
|
||||
<div class="col-sm-6">
|
||||
<div class="searchParamDescription">
|
||||
<div>
|
||||
<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}">
|
||||
(required)
|
||||
</span>
|
||||
<span th:if="${!param.getUndeclaredExtensionsByUrl(requiredParamExtension).get(0).value.value}">
|
||||
(optional)
|
||||
</span>
|
||||
</th:block>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
var type = '<th:block th:text="${param.type.value}"/>';
|
||||
var name = '<th:block th:text="${param.name}"/>';
|
||||
var chain = new Array();
|
||||
<th:block th:each="chain : ${param.chain}">
|
||||
chain.push('<th:block th:text="${chain}"/>');
|
||||
</th:block>
|
||||
var rowNum = <th:block th:text="${paramIterStat.index}"/>;
|
||||
var containerRowNum = <th:block th:text="${queryIterStat.index}"/> + '-' + rowNum;
|
||||
addSearchControls(type, name, chain, containerRowNum, rowNum);
|
||||
</script>
|
||||
<br clear="all"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</html>
|
|
@ -147,6 +147,7 @@ body .syntaxhighlighter .line {
|
|||
.sidebar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
|
@ -178,6 +179,10 @@ body .syntaxhighlighter .line {
|
|||
background-color: #428bca;
|
||||
}
|
||||
|
||||
.nav-tabs li.active A {
|
||||
background: #f5f5f5;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/*
|
||||
* Main content
|
||||
|
@ -220,6 +225,18 @@ body .syntaxhighlighter .line {
|
|||
border-radius: 50%;
|
||||
}
|
||||
|
||||
DIV.queryParameter {
|
||||
padding-top: 3px;
|
||||
padding-bottom: 3px;
|
||||
border-radius: 6px;
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
DIV.queryParameterOdd {
|
||||
background: #EEE;
|
||||
}
|
||||
|
||||
TABLE.resultTable TR TD:FIRST-CHILD,
|
||||
TABLE.requestTable TR TD:FIRST-CHILD {
|
||||
font-weight: bold;
|
||||
|
@ -272,6 +289,14 @@ DIV.searchParamSeparator {
|
|||
font-size: 0.9em !important;
|
||||
}
|
||||
|
||||
DIV.tab-pane DIV.container-fluid {
|
||||
padding-top: 15px;
|
||||
}
|
||||
|
||||
.topbarIcon {
|
||||
color: #66AAFF;
|
||||
}
|
||||
|
||||
DIV.top-buffer {
|
||||
margin-top: 2px;
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 14 KiB |
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
|
@ -1,23 +1,4 @@
|
|||
|
||||
function addChildParam(searchParam, nextExt) {
|
||||
var childParam = new Object();
|
||||
for (var extIdx = 0; extIdx < nextExt.extension.length; extIdx++) {
|
||||
var childExt = nextExt.extension[extIdx];
|
||||
if (childExt.url == "http://hl7api.sourceforge.net/hapi-fhir/extensions.xml#additionalParamName") {
|
||||
childParam.name = childExt.valueString;
|
||||
}
|
||||
if (childExt.url == "http://hl7api.sourceforge.net/hapi-fhir/extensions.xml#additionalParamDescription") {
|
||||
childParam.documentation = childExt.valueString;
|
||||
}
|
||||
if (childExt.url == "http://hl7api.sourceforge.net/hapi-fhir/extensions.xml#additionalParamType") {
|
||||
childParam.type = childExt.valueCode;
|
||||
}
|
||||
if (childExt.url == "http://hl7api.sourceforge.net/hapi-fhir/extensions.xml#additionalParam") {
|
||||
addChildParam(childParam, childExt);
|
||||
}
|
||||
}
|
||||
searchParam.childParam = childParam;
|
||||
}
|
||||
|
||||
var numRows = 0;
|
||||
function addSearchParamRow() {
|
||||
|
@ -51,17 +32,6 @@ function addSearchParamRow() {
|
|||
$('<option />', { value: nextName }).text(searchParam.name + ' - ' + searchParam.documentation)
|
||||
);
|
||||
|
||||
if (restResource._searchParam && restResource._searchParam[i] != null) {
|
||||
if (restResource._searchParam[i].extension) {
|
||||
for (var j = 0; j < restResource._searchParam[i].extension.length; j++) {
|
||||
var nextExt = restResource._searchParam[i].extension[j];
|
||||
if (nextExt.url == "http://hl7api.sourceforge.net/hapi-fhir/extensions.xml#additionalParam") {
|
||||
addChildParam(searchParam, nextExt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
restResource.searchParam.forEach(function(searchParam){
|
||||
|
@ -89,49 +59,38 @@ function updateSearchDateQualifier(qualifierBtn, qualifierInput, qualifier) {
|
|||
}
|
||||
}
|
||||
|
||||
function addSearchControls(searchParam, searchParamName, containerRowNum, rowNum, isChild) {
|
||||
if (searchParam.childParam || isChild) {
|
||||
$('#search-param-rowopts-' + containerRowNum).append(
|
||||
$('<br clear="all"/>'),
|
||||
$('<div class="searchParamSeparator"/>'),
|
||||
$('<div />', { 'class': 'col-sm-6' }).append(
|
||||
$('<div class="searchParamDescription"/>').append(
|
||||
$('<div />').text(searchParam.documentation)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
function addSearchControls(theSearchParamType, theSearchParamName, theSearchParamChain, theContainerRowNum, theRowNum) {
|
||||
|
||||
if (searchParam.chain && searchParam.chain.length > 0) {
|
||||
$('#search-param-rowopts-' + containerRowNum).append(
|
||||
$('<input />', { name: 'param.' + rowNum + '.qualifier', type: 'hidden', value: '.' + searchParam.chain[0] })
|
||||
if (theSearchParamChain && theSearchParamChain.length > 0) {
|
||||
$('#search-param-rowopts-' + theContainerRowNum).append(
|
||||
$('<input />', { id: 'param.' + theRowNum + '.qualifier', type: 'hidden', value: '.' + theSearchParamChain[0] })
|
||||
);
|
||||
}
|
||||
|
||||
$('#search-param-rowopts-' + containerRowNum).append(
|
||||
$('<input />', { name: 'param.' + rowNum + '.name', type: 'hidden', value: searchParam.name })
|
||||
$('#search-param-rowopts-' + theContainerRowNum).append(
|
||||
$('<input />', { id: 'param.' + theRowNum + '.name', type: 'hidden', value: theSearchParamName })
|
||||
);
|
||||
|
||||
if (searchParam.type == 'token') {
|
||||
$('#search-param-rowopts-' + containerRowNum).append(
|
||||
if (theSearchParamType == 'token') {
|
||||
$('#search-param-rowopts-' + theContainerRowNum).append(
|
||||
$('<div />', { 'class': 'col-sm-3' }).append(
|
||||
$('<input />', { name: 'param.' + rowNum + '.0', placeholder: 'system/namespace', type: 'text', 'class': 'form-control' })
|
||||
$('<input />', { id: 'param.' + theRowNum + '.0', placeholder: 'system/namespace', type: 'text', 'class': 'form-control' })
|
||||
),
|
||||
$('<div />', { 'class': 'col-sm-3' }).append(
|
||||
$('<input />', { name: 'param.' + rowNum + '.1', type: 'hidden', value: '|' }),
|
||||
$('<input />', { name: 'param.' + rowNum + '.2', placeholder: 'value', type: 'text', 'class': 'form-control' })
|
||||
$('<input />', { id: 'param.' + theRowNum + '.1', type: 'hidden', value: '|' }),
|
||||
$('<input />', { id: 'param.' + theRowNum + '.2', placeholder: 'value', type: 'text', 'class': 'form-control' })
|
||||
)
|
||||
);
|
||||
} else if (searchParam.type == 'string' || searchParam.type == 'number') {
|
||||
$('#search-param-rowopts-' + containerRowNum).append(
|
||||
} else if (theSearchParamType == 'string' || theSearchParamType == 'number') {
|
||||
$('#search-param-rowopts-' + theContainerRowNum).append(
|
||||
$('<div />', { 'class': 'col-sm-3' }).append(
|
||||
$('<input />', { name: 'param.' + rowNum + '.0', placeholder: 'value', type: 'text', 'class': 'form-control' })
|
||||
$('<input />', { id: 'param.' + theRowNum + '.0', placeholder: 'value', type: 'text', 'class': 'form-control' })
|
||||
)
|
||||
);
|
||||
} else if (searchParam.type == 'date') {
|
||||
var qualifier = $('<input />', {type:'hidden', name:'param.'+rowNum+'.0', id:'param.'+rowNum+'.0'});
|
||||
} else if (theSearchParamType == 'date') {
|
||||
var qualifier = $('<input />', {type:'hidden', id:'param.'+theRowNum+'.0', id:'param.'+theRowNum+'.0'});
|
||||
|
||||
if (/date$/.test(searchParam.name)) {
|
||||
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' });
|
||||
|
@ -139,7 +98,7 @@ function addSearchControls(searchParam, searchParamName, containerRowNum, rowNum
|
|||
var qualifierDiv = $('<div />');
|
||||
input.append(
|
||||
qualifierDiv,
|
||||
$('<input />', { type:'text', 'class':'form-control', name: 'param.' + rowNum + '.1' }),
|
||||
$('<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'})
|
||||
)
|
||||
|
@ -168,7 +127,7 @@ function addSearchControls(searchParam, searchParamName, containerRowNum, rowNum
|
|||
)
|
||||
);
|
||||
|
||||
$('#search-param-rowopts-' + containerRowNum).append(
|
||||
$('#search-param-rowopts-' + theContainerRowNum).append(
|
||||
qualifier,
|
||||
$('<div />', { 'class': 'col-sm-4' }).append(
|
||||
input
|
||||
|
@ -176,21 +135,6 @@ function addSearchControls(searchParam, searchParamName, containerRowNum, rowNum
|
|||
);
|
||||
}
|
||||
|
||||
if (searchParam.childParam) {
|
||||
$('#search-param-rowopts-' + containerRowNum).append(
|
||||
/*
|
||||
$('<br clear="all"/>'),
|
||||
$('<div style="height: 5px;"/>'),
|
||||
$('<div />', { 'class': 'col-sm-6' }).append(
|
||||
$('<span>' + searchParam.childParam.documentation + '</span>')
|
||||
),
|
||||
*/
|
||||
$('<input />', { name: 'param.' + rowNum + '.0.type', type: 'hidden', value: searchParam.childParam.type })
|
||||
);
|
||||
addSearchControls(searchParam.childParam, searchParamName, containerRowNum, rowNum + '.0', true);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
function handleSearchParamTypeChange(select, params, rowNum) {
|
||||
|
@ -205,7 +149,7 @@ function handleSearchParamTypeChange(select, params, rowNum) {
|
|||
$('<input />', { name: 'param.' + rowNum + '.type', type: 'hidden', value: searchParam.type })
|
||||
);
|
||||
|
||||
addSearchControls(searchParam, searchParam.name, rowNum, rowNum, false);
|
||||
addSearchControls(searchParam.type, searchParam.name, searchParam.chain, rowNum, rowNum);
|
||||
|
||||
select.prevVal = newVal;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package ca.uhn.fhir.jpa.test;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
|
@ -18,6 +19,7 @@ import ca.uhn.fhir.model.api.IResource;
|
|||
import ca.uhn.fhir.model.api.Include;
|
||||
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
|
||||
import ca.uhn.fhir.model.api.TagList;
|
||||
import ca.uhn.fhir.model.api.annotation.Description;
|
||||
import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
|
||||
import ca.uhn.fhir.model.dstu.resource.DiagnosticReport;
|
||||
import ca.uhn.fhir.model.dstu.resource.Organization;
|
||||
|
@ -164,6 +166,7 @@ public class OverlayTestApp {
|
|||
|
||||
public static class ProviderWithRequiredAndOptional implements IResourceProvider {
|
||||
|
||||
@Description(shortDefinition="This is a query by date!")
|
||||
@Search
|
||||
public List<DiagnosticReport> findDiagnosticReportsByPatient (
|
||||
@RequiredParam(name=DiagnosticReport.SP_SUBJECT + '.' + Patient.SP_IDENTIFIER) IdentifierDt thePatientId,
|
||||
|
@ -174,6 +177,7 @@ public class OverlayTestApp {
|
|||
return null;
|
||||
}
|
||||
|
||||
@Description(shortDefinition="This is a query by issued.. blah blah foo bar blah blah")
|
||||
@Search
|
||||
public List<DiagnosticReport> findDiagnosticReportsByPatientIssued (
|
||||
@RequiredParam(name=DiagnosticReport.SP_SUBJECT + '.' + Patient.SP_IDENTIFIER) IdentifierDt thePatientId,
|
||||
|
|
Loading…
Reference in New Issue