Tester now suports chained parameters

This commit is contained in:
jamesagnew 2014-07-17 08:32:01 -04:00
parent 41f160ed67
commit 84af486d51
21 changed files with 335 additions and 105 deletions

View File

@ -23,8 +23,18 @@ package ca.uhn.fhir.rest.annotation;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.rest.param.ReferenceParam;
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
public @interface OptionalParam { public @interface OptionalParam {
String name(); String name();
/**
* For resource reference parameters ({@link ReferenceParam}) this parameter may be
* used to indicate the resource type(s) which may be referenced by this param
*/
Class<? extends IResource>[] targetTypes() default {};
} }

View File

@ -23,8 +23,18 @@ package ca.uhn.fhir.rest.annotation;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.rest.param.ReferenceParam;
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
public @interface RequiredParam { public @interface RequiredParam {
String name(); String name();
/**
* For resource reference parameters ({@link ReferenceParam}) this parameter may be
* used to indicate the resource type(s) which may be referenced by this param
*/
Class<? extends IResource>[] targetTypes() default {};
} }

View File

@ -187,6 +187,7 @@ public class ParameterUtil {
SearchParameter parameter = new SearchParameter(); SearchParameter parameter = new SearchParameter();
parameter.setName(((RequiredParam) nextAnnotation).name()); parameter.setName(((RequiredParam) nextAnnotation).name());
parameter.setRequired(true); parameter.setRequired(true);
parameter.setDeclaredTypes(((RequiredParam) nextAnnotation).targetTypes());
parameter.setType(parameterType, innerCollectionType, outerCollectionType); parameter.setType(parameterType, innerCollectionType, outerCollectionType);
extractDescription(parameter, annotations); extractDescription(parameter, annotations);
param = parameter; param = parameter;
@ -194,6 +195,7 @@ public class ParameterUtil {
SearchParameter parameter = new SearchParameter(); SearchParameter parameter = new SearchParameter();
parameter.setName(((OptionalParam) nextAnnotation).name()); parameter.setName(((OptionalParam) nextAnnotation).name());
parameter.setRequired(false); parameter.setRequired(false);
parameter.setDeclaredTypes(((OptionalParam) nextAnnotation).targetTypes());
parameter.setType(parameterType, innerCollectionType, outerCollectionType); parameter.setType(parameterType, innerCollectionType, outerCollectionType);
extractDescription(parameter, annotations); extractDescription(parameter, annotations);
param = parameter; param = parameter;

View File

@ -25,13 +25,13 @@ import java.util.Collection;
import java.util.List; import java.util.List;
import org.apache.commons.lang3.builder.ToStringBuilder; 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.ConfigurationException;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.IQueryParameterAnd; import ca.uhn.fhir.model.api.IQueryParameterAnd;
import ca.uhn.fhir.model.api.IQueryParameterOr; import ca.uhn.fhir.model.api.IQueryParameterOr;
import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu.composite.IdentifierDt; import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
import ca.uhn.fhir.model.dstu.composite.QuantityDt; import ca.uhn.fhir.model.dstu.composite.QuantityDt;
import ca.uhn.fhir.model.dstu.valueset.SearchParamTypeEnum; import ca.uhn.fhir.model.dstu.valueset.SearchParamTypeEnum;
@ -45,6 +45,7 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
*/ */
public class SearchParameter extends BaseQueryParameter { public class SearchParameter extends BaseQueryParameter {
private Class<? extends IResource>[] myDeclaredTypes;
private String myDescription; private String myDescription;
private String myName; private String myName;
private IParamBinder myParamBinder; private IParamBinder myParamBinder;
@ -60,14 +61,6 @@ public class SearchParameter extends BaseQueryParameter {
this.myRequired = theRequired; 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) * (non-Javadoc)
* *
@ -85,6 +78,10 @@ public class SearchParameter extends BaseQueryParameter {
return retVal; return retVal;
} }
public Class<? extends IResource>[] getDeclaredTypes() {
return myDeclaredTypes;
}
public String getDescription() { public String getDescription() {
return myDescription; return myDescription;
} }
@ -128,6 +125,10 @@ public class SearchParameter extends BaseQueryParameter {
return myParamBinder.parse(theString); return myParamBinder.parse(theString);
} }
public void setDeclaredTypes(Class<? extends IResource>[] theTypes) {
myDeclaredTypes=theTypes;
}
public void setDescription(String theDescription) { public void setDescription(String theDescription) {
myDescription = theDescription; myDescription = theDescription;
} }
@ -189,4 +190,12 @@ public class SearchParameter extends BaseQueryParameter {
} }
@Override
public String toString() {
ToStringBuilder retVal = new ToStringBuilder(this);
retVal.append("name", myName);
retVal.append("required", myRequired);
return retVal.toString();
}
} }

View File

@ -32,15 +32,18 @@ import org.apache.commons.lang3.StringUtils;
import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam; import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu.resource.Conformance; import ca.uhn.fhir.model.dstu.resource.Conformance;
import ca.uhn.fhir.model.dstu.resource.Conformance.Rest; import ca.uhn.fhir.model.dstu.resource.Conformance.Rest;
import ca.uhn.fhir.model.dstu.resource.Conformance.RestQuery; 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.RestResource;
import ca.uhn.fhir.model.dstu.resource.Conformance.RestResourceOperation; import ca.uhn.fhir.model.dstu.resource.Conformance.RestResourceOperation;
import ca.uhn.fhir.model.dstu.resource.Conformance.RestResourceSearchParam; import ca.uhn.fhir.model.dstu.resource.Conformance.RestResourceSearchParam;
import ca.uhn.fhir.model.dstu.valueset.ResourceTypeEnum;
import ca.uhn.fhir.model.dstu.valueset.RestfulConformanceModeEnum; import ca.uhn.fhir.model.dstu.valueset.RestfulConformanceModeEnum;
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationSystemEnum; import ca.uhn.fhir.model.dstu.valueset.RestfulOperationSystemEnum;
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum; import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum;
import ca.uhn.fhir.model.dstu.valueset.SearchParamTypeEnum;
import ca.uhn.fhir.model.primitive.BooleanDt; import ca.uhn.fhir.model.primitive.BooleanDt;
import ca.uhn.fhir.model.primitive.CodeDt; import ca.uhn.fhir.model.primitive.CodeDt;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IdDt;
@ -164,10 +167,12 @@ public class ServerConformanceProvider {
for (SearchParameter nextParameter : searchParameters) { for (SearchParameter nextParameter : searchParameters) {
String nextParamName = nextParameter.getName(); String nextParamName = nextParameter.getName();
String chain = null;
// String chain = null;
String nextParamUnchainedName = nextParamName;
if (nextParamName.contains(".")) { if (nextParamName.contains(".")) {
chain = nextParamName.substring(nextParamName.indexOf('.') + 1); // chain = nextParamName.substring(nextParamName.indexOf('.') + 1);
nextParamName = nextParamName.substring(0, nextParamName.indexOf('.')); nextParamUnchainedName = nextParamName.substring(0, nextParamName.indexOf('.'));
} }
String nextParamDescription = nextParameter.getDescription(); String nextParamDescription = nextParameter.getDescription();
@ -176,7 +181,7 @@ public class ServerConformanceProvider {
* If the parameter has no description, default to the one from the resource * If the parameter has no description, default to the one from the resource
*/ */
if (StringUtils.isBlank(nextParamDescription)) { if (StringUtils.isBlank(nextParamDescription)) {
RuntimeSearchParam paramDef = def.getSearchParam(nextParamName); RuntimeSearchParam paramDef = def.getSearchParam(nextParamUnchainedName);
if (paramDef != null) { if (paramDef != null) {
nextParamDescription = paramDef.getDescription(); nextParamDescription = paramDef.getDescription();
} }
@ -191,12 +196,21 @@ public class ServerConformanceProvider {
} }
param.setName(nextParamName); param.setName(nextParamName);
if (StringUtils.isNotBlank(chain)) { // if (StringUtils.isNotBlank(chain)) {
param.addChain(chain); // param.addChain(chain);
} // }
param.setDocumentation(nextParamDescription); param.setDocumentation(nextParamDescription);
param.setType(nextParameter.getParamType()); param.setType(nextParameter.getParamType());
for (Class<? extends IResource> nextTarget : nextParameter.getDeclaredTypes()) {
RuntimeResourceDefinition targetDef = myRestfulServer.getFhirContext().getResourceDefinition(nextTarget);
if (targetDef != null) {
ResourceTypeEnum code = ResourceTypeEnum.VALUESET_BINDER.fromCodeString(targetDef.getName());
if (code != null) {
param.addTarget(code);
}
}
}
} }
} }

View File

@ -680,7 +680,7 @@
<p> <p>
Example URL to invoke this method: Example URL to invoke this method:
<br /> <br />
<code>http://fhir.example.com/Observation?value-quantity=%lt;=123.2||mg</code> <code>http://fhir.example.com/Observation?value-quantity=&lt;=123.2||mg</code>
</p> </p>
</subsection> </subsection>

View File

@ -23,8 +23,13 @@ import org.junit.Test;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.BundleEntry; import ca.uhn.fhir.model.api.BundleEntry;
import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu.resource.Conformance;
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.Organization;
import ca.uhn.fhir.model.dstu.resource.Patient; import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.rest.annotation.RequiredParam; import ca.uhn.fhir.model.dstu.valueset.ResourceTypeEnum;
import ca.uhn.fhir.rest.annotation.OptionalParam;
import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.param.ReferenceParam; import ca.uhn.fhir.rest.param.ReferenceParam;
import ca.uhn.fhir.testutil.RandomServerPortProvider; import ca.uhn.fhir.testutil.RandomServerPortProvider;
@ -111,7 +116,29 @@ public class ReferenceParameterTest {
assertEquals("2name", p.getName().get(2).getFamilyFirstRep().getValue()); assertEquals("2name", p.getName().get(2).getFamilyFirstRep().getValue());
} }
} }
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ReferenceParameterTest.class);
@Test
public void testParamTypesInConformanceStatement() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/metadata?_pretty=true");
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info(responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
Conformance conf = ourCtx.newXmlParser().parseResource(Conformance.class,responseContent);
RestResource res = conf.getRestFirstRep().getResourceFirstRep();
assertEquals("Patient", res.getType().getValue());
RestResourceSearchParam param = res.getSearchParamFirstRep();
assertEquals(Patient.SP_PROVIDER, param.getName().getValue());
assertEquals(1, param.getTarget().size());
assertEquals(ResourceTypeEnum.ORGANIZATION, param.getTarget().get(0).getValueAsEnum());
}
@AfterClass @AfterClass
public static void afterClass() throws Exception { public static void afterClass() throws Exception {
ourServer.stop(); ourServer.stop();
@ -147,7 +174,7 @@ public class ReferenceParameterTest {
public static class DummyPatientResourceProvider implements IResourceProvider { public static class DummyPatientResourceProvider implements IResourceProvider {
@Search @Search
public List<Patient> findPatient(@RequiredParam(name = Patient.SP_PROVIDER) ReferenceParam theParam) { public List<Patient> findPatient(@OptionalParam(name = Patient.SP_PROVIDER, targetTypes= {Organization.class}) ReferenceParam theParam) {
ArrayList<Patient> retVal = new ArrayList<Patient>(); ArrayList<Patient> retVal = new ArrayList<Patient>();
Patient p = new Patient(); Patient p = new Patient();

View File

@ -121,7 +121,7 @@ public class ServerConformanceProviderTest {
assertEquals("DiagnosticReport", res.getType().getValueAsString()); assertEquals("DiagnosticReport", res.getType().getValueAsString());
RestQuery p0 = rest.getQueryFirstRep(); RestQuery p0 = rest.getQueryFirstRep();
assertEquals("subject", p0.getParameterFirstRep().getName().getValue()); assertEquals("subject.identifier", p0.getParameterFirstRep().getName().getValue());
assertEquals(1,res.getSearchInclude().size()); assertEquals(1,res.getSearchInclude().size());
assertEquals("DiagnosticReport.result", res.getSearchIncludeFirstRep().getValue()); assertEquals("DiagnosticReport.result", res.getSearchIncludeFirstRep().getValue());

View File

@ -515,7 +515,7 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
} }
entity.setHasTags(true); entity.setHasTags(true);
TagDefinition def = getTag(theScheme, theTerm, theLabel); TagDefinition def = getTag(theScheme, theTerm, theLabel);
BaseTag newEntity = entity.addTag(def); BaseTag newEntity = entity.addTag(def);
@ -729,7 +729,7 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
StopWatch w = new StopWatch(); StopWatch w = new StopWatch();
BaseHasResource entity = readEntity(theId); BaseHasResource entity = readEntity(theId);
validateResourceType(entity); validateResourceType(entity);
T retVal = toResource(myResourceType, entity); T retVal = toResource(myResourceType, entity);
InstantDt deleted = ResourceMetadataKeyEnum.DELETED_AT.get(retVal); InstantDt deleted = ResourceMetadataKeyEnum.DELETED_AT.get(retVal);
@ -749,14 +749,14 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
private void validateResourceTypeAndThrowIllegalArgumentException(IdDt theId) { private void validateResourceTypeAndThrowIllegalArgumentException(IdDt theId) {
if (theId.hasResourceType() && !theId.getResourceType().equals(myResourceName)) { if (theId.hasResourceType() && !theId.getResourceType().equals(myResourceName)) {
throw new IllegalArgumentException("Incorrect resource type (" + theId.getResourceType()+ ") for this DAO, wanted: " + myResourceName); throw new IllegalArgumentException("Incorrect resource type (" + theId.getResourceType() + ") for this DAO, wanted: " + myResourceName);
} }
} }
@Override @Override
public BaseHasResource readEntity(IdDt theId) { public BaseHasResource readEntity(IdDt theId) {
validateResourceTypeAndThrowIllegalArgumentException(theId); validateResourceTypeAndThrowIllegalArgumentException(theId);
Long pid = translateForcedIdToPid(theId); Long pid = translateForcedIdToPid(theId);
BaseHasResource entity = myEntityManager.find(ResourceTable.class, pid); BaseHasResource entity = myEntityManager.find(ResourceTable.class, pid);
if (theId.hasVersionIdPart()) { if (theId.hasVersionIdPart()) {
@ -779,7 +779,7 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
} }
validateResourceType(entity); validateResourceType(entity);
return entity; return entity;
} }
@ -809,7 +809,7 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
if (entity.getTags().isEmpty()) { if (entity.getTags().isEmpty()) {
entity.setHasTags(false); entity.setHasTags(false);
} }
myEntityManager.merge(entity); myEntityManager.merge(entity);
ourLog.info("Processed remove tag {}/{} on {} in {}ms", new Object[] { theScheme, theTerm, theId.getValue(), w.getMillisAndRestart() }); ourLog.info("Processed remove tag {}/{} on {} in {}ms", new Object[] { theScheme, theTerm, theId.getValue(), w.getMillisAndRestart() });
@ -916,7 +916,7 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
} }
}; };
ourLog.info("Processed search for {} on {} in {}ms", new Object[] {myResourceName, theParams, w.getMillisAndRestart()}); ourLog.info("Processed search for {} on {} in {}ms", new Object[] { myResourceName, theParams, w.getMillisAndRestart() });
return retVal; return retVal;
} }
@ -968,8 +968,12 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
for (IQueryParameterType next : nextValue) { for (IQueryParameterType next : nextValue) {
String value = next.getValueAsQueryToken(); String value = next.getValueAsQueryToken();
IdDt valueId = new IdDt(value); IdDt valueId = new IdDt(value);
long valueLong = translateForcedIdToPid(valueId); try {
joinPids.add(valueLong); long valueLong = translateForcedIdToPid(valueId);
joinPids.add(valueLong);
} catch (ResourceNotFoundException e) {
// This isn't an error, just means no result found
}
} }
if (joinPids.isEmpty()) { if (joinPids.isEmpty()) {
continue; continue;

View File

@ -1,11 +1,19 @@
package ca.uhn.fhir.jpa.provider; package ca.uhn.fhir.jpa.provider;
import java.util.List;
import java.util.Map; import java.util.Map;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.jpa.dao.IFhirSystemDao; import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
import ca.uhn.fhir.model.dstu.resource.Conformance; import ca.uhn.fhir.model.dstu.resource.Conformance;
import ca.uhn.fhir.model.dstu.resource.Conformance.Rest; import ca.uhn.fhir.model.dstu.resource.Conformance.Rest;
import ca.uhn.fhir.model.dstu.resource.Conformance.RestResource; import ca.uhn.fhir.model.dstu.resource.Conformance.RestResource;
import ca.uhn.fhir.model.dstu.resource.Conformance.RestResourceSearchParam;
import ca.uhn.fhir.model.dstu.valueset.ResourceTypeEnum;
import ca.uhn.fhir.model.dstu.valueset.SearchParamTypeEnum;
import ca.uhn.fhir.model.primitive.BoundCodeDt;
import ca.uhn.fhir.model.primitive.DecimalDt; import ca.uhn.fhir.model.primitive.DecimalDt;
import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.provider.ServerConformanceProvider; import ca.uhn.fhir.rest.server.provider.ServerConformanceProvider;
@ -16,9 +24,11 @@ public class JpaConformanceProvider extends ServerConformanceProvider {
private String myImplementationDescription; private String myImplementationDescription;
private IFhirSystemDao mySystemDao; private IFhirSystemDao mySystemDao;
private volatile Conformance myCachedValue; private volatile Conformance myCachedValue;
private RestfulServer myRestfulServer;
public JpaConformanceProvider(RestfulServer theRestfulServer, IFhirSystemDao theSystemDao) { public JpaConformanceProvider(RestfulServer theRestfulServer, IFhirSystemDao theSystemDao) {
super(theRestfulServer); super(theRestfulServer);
myRestfulServer = theRestfulServer;
mySystemDao = theSystemDao; mySystemDao = theSystemDao;
super.setCache(false); super.setCache(false);
@ -41,13 +51,31 @@ public class JpaConformanceProvider extends ServerConformanceProvider {
Map<String, Long> counts = mySystemDao.getResourceCounts(); Map<String, Long> counts = mySystemDao.getResourceCounts();
FhirContext ctx = myRestfulServer.getFhirContext();
retVal = super.getServerConformance(); retVal = super.getServerConformance();
for (Rest nextRest : retVal.getRest()) { for (Rest nextRest : retVal.getRest()) {
for (RestResource nextResource : nextRest.getResource()) { for (RestResource nextResource : nextRest.getResource()) {
// Add resource counts
Long count = counts.get(nextResource.getType().getValueAsString()); Long count = counts.get(nextResource.getType().getValueAsString());
if (count != null) { if (count != null) {
nextResource.addUndeclaredExtension(false, ExtensionConstants.CONF_RESOURCE_COUNT, new DecimalDt(count)); nextResource.addUndeclaredExtension(false, ExtensionConstants.CONF_RESOURCE_COUNT, new DecimalDt(count));
} }
// Add chained params
for (RestResourceSearchParam nextParam : nextResource.getSearchParam()) {
if (nextParam.getType().getValueAsEnum() == SearchParamTypeEnum.REFERENCE) {
List<BoundCodeDt<ResourceTypeEnum>> targets = nextParam.getTarget();
for (BoundCodeDt<ResourceTypeEnum> next : targets) {
RuntimeResourceDefinition def = ctx.getResourceDefinition(next.getValue());
for (RuntimeSearchParam nextChainedParam : def.getSearchParams()) {
nextParam.addChain(nextChainedParam.getName());
}
}
}
}
} }
} }

View File

@ -192,6 +192,17 @@ public class FhirResourceDaoTest {
assertEquals(o1id.toUnqualifiedVersionless(), p1.getManagingOrganization().getReference().toUnqualifiedVersionless()); assertEquals(o1id.toUnqualifiedVersionless(), p1.getManagingOrganization().getReference().toUnqualifiedVersionless());
} }
@Test
public void testSearchForUnknownAlphanumericId() {
{
SearchParameterMap map = new SearchParameterMap();
map.add("_id", new StringParam("testSearchForUnknownAlphanumericId"));
IBundleProvider retrieved = ourPatientDao.search(map);
assertEquals(0, retrieved.size());
}
}
@Test @Test
public void testSearchTokenParam() { public void testSearchTokenParam() {
Patient patient = new Patient(); Patient patient = new Patient();

View File

@ -6,13 +6,7 @@
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src/main/resources"/> <wb-resource deploy-path="/WEB-INF/classes" source-path="/src/main/resources"/>
<wb-resource deploy-path="/" source-path="/target/m2e-wtp/web-resources"/> <wb-resource deploy-path="/" source-path="/target/m2e-wtp/web-resources"/>
<wb-resource deploy-path="/" source-path="/src/main/webapp" tag="defaultRootSource"/> <wb-resource deploy-path="/" source-path="/src/main/webapp" tag="defaultRootSource"/>
<dependent-module archiveName="hapi-fhir-jpaserver-base-0.5-SNAPSHOT.jar" deploy-path="/WEB-INF/lib" handle="module:/resource/hapi-fhir-jpaserver-base/hapi-fhir-jpaserver-base"> <dependent-module deploy-path="/" handle="module:/overlay/prj/hapi-fhir-tester-overlay?includes=**/**&amp;excludes=META-INF/MANIFEST.MF">
<dependency-type>uses</dependency-type>
</dependent-module>
<dependent-module archiveName="hapi-fhir-base-0.5-SNAPSHOT.jar" deploy-path="/WEB-INF/lib" handle="module:/resource/hapi-fhir-base/hapi-fhir-base">
<dependency-type>uses</dependency-type>
</dependent-module>
<dependent-module deploy-path="/" handle="module:/overlay/prj/hapi-fhir-testpage-overlay?includes=**/**&amp;excludes=META-INF/MANIFEST.MF">
<dependency-type>consumes</dependency-type> <dependency-type>consumes</dependency-type>
</dependent-module> </dependent-module>
<dependent-module deploy-path="/" handle="module:/overlay/slf/?includes=**/**&amp;excludes=META-INF/MANIFEST.MF"> <dependent-module deploy-path="/" handle="module:/overlay/slf/?includes=**/**&amp;excludes=META-INF/MANIFEST.MF">

View File

@ -64,9 +64,13 @@
<th:block th:each="chain : ${param.chain}"> <th:block th:each="chain : ${param.chain}">
chain.push('<th:block th:text="${chain}"/>'); chain.push('<th:block th:text="${chain}"/>');
</th:block> </th:block>
var target = new Array();
<th:block th:each="target : ${param.target}">
target.push('<th:block th:text="${target}"/>');
</th:block>
var rowNum = <th:block th:text="${paramIterStat.index}"/>; var rowNum = <th:block th:text="${paramIterStat.index}"/>;
var containerRowNum = <th:block th:text="${queryIterStat.index}"/> + '-' + rowNum; var containerRowNum = <th:block th:text="${queryIterStat.index}"/> + '-' + rowNum;
addSearchControls(type, name, chain, containerRowNum, rowNum); addSearchControls(conformance, type, name, chain, target, containerRowNum, rowNum);
</script> </script>
<br clear="all"/> <br clear="all"/>
</div> </div>

View File

@ -16,6 +16,7 @@ function addSearchParamRow() {
$("#search-param-rows").append(rowDiv); $("#search-param-rows").append(rowDiv);
plusBtn.click(function() { plusBtn.click(function() {
plusBtn.hide();
addSearchParamRow(); addSearchParamRow();
}); });
@ -46,8 +47,8 @@ function addSearchParamRow() {
}); });
}); });
select.select2(); select.select2();
handleSearchParamTypeChange(select, params, nextRow); handleSearchParamTypeChange(select, params, nextRow, nextRow);
select.change(function(){ handleSearchParamTypeChange(select, params, nextRow); }); select.change(function(){ handleSearchParamTypeChange(select, params, nextRow, nextRow); });
} }
function updateSearchDateQualifier(qualifierBtn, qualifierInput, qualifier) { function updateSearchDateQualifier(qualifierBtn, qualifierInput, qualifier) {
@ -59,19 +60,9 @@ function updateSearchDateQualifier(qualifierBtn, qualifierInput, qualifier) {
} }
} }
function addSearchControls(theSearchParamType, theSearchParamName, theSearchParamChain, theContainerRowNum, theRowNum) { function addSearchControls(theConformance, theSearchParamType, theSearchParamName, theSearchParamChain, theSearchParamTarget, theContainerRowNum, theRowNum) {
if (theSearchParamChain && theSearchParamChain.length > 0) { var addNameAndType = true;
$('#search-param-rowopts-' + theContainerRowNum).append(
$('<input />', { id: 'param.' + theRowNum + '.qualifier', type: 'hidden', value: '.' + theSearchParamChain[0] })
);
}
$('#search-param-rowopts-' + theContainerRowNum).append(
$('<input />', { id: 'param.' + theRowNum + '.name', type: 'hidden', value: theSearchParamName }),
$('<input />', { id: 'param.' + theRowNum + '.type', type: 'hidden', value: theSearchParamType })
);
if (theSearchParamType == 'token') { if (theSearchParamType == 'token') {
$('#search-param-rowopts-' + theContainerRowNum).append( $('#search-param-rowopts-' + theContainerRowNum).append(
$('<div />', { 'class': 'col-sm-3' }).append( $('<div />', { 'class': 'col-sm-3' }).append(
@ -122,7 +113,24 @@ function addSearchControls(theSearchParamType, theSearchParamName, theSearchPara
$('<input />', { id: 'param.' + theRowNum + '.0', placeholder: placeholderText, type: 'text', 'class': 'form-control' }) $('<input />', { id: 'param.' + theRowNum + '.0', placeholder: placeholderText, type: 'text', 'class': 'form-control' })
) )
); );
} else if (theSearchParamType == 'number' || theSearchParamType == 'reference') { } else if (theSearchParamType == 'number') {
var placeholderText = 'Number';
$('#search-param-rowopts-' + theContainerRowNum).append(
$('<input />', { id: 'param.' + theRowNum + '.0', placeholder: placeholderText, type: 'hidden' })
);
$('#search-param-rowopts-' + theContainerRowNum).append(
$('<div />', { 'class': 'col-sm-3' }).append(
$('<input />', { id: 'param.' + theRowNum + '.1', placeholder: placeholderText, type: 'text', 'class': 'form-control' })
)
);
} else if (theSearchParamType == 'date') {
addSearchControlDate(theSearchParamName, theContainerRowNum, theRowNum, true);
addSearchControlDate(theSearchParamName, theContainerRowNum, theRowNum, false);
} else if (theSearchParamType == 'reference' && theSearchParamChain.length == 0) {
/*
* This is a reference parameter with no chain options, so just display a simple
* text box for the ID of the referenced resource
*/
var placeholderText = 'value'; var placeholderText = 'value';
if (theSearchParamType == 'number') { if (theSearchParamType == 'number') {
placeholderText = 'Number'; placeholderText = 'Number';
@ -137,10 +145,72 @@ function addSearchControls(theSearchParamType, theSearchParamName, theSearchPara
$('<input />', { id: 'param.' + theRowNum + '.1', placeholder: placeholderText, type: 'text', 'class': 'form-control' }) $('<input />', { id: 'param.' + theRowNum + '.1', placeholder: placeholderText, type: 'text', 'class': 'form-control' })
) )
); );
} else if (theSearchParamType == 'date') { } else if (theSearchParamType == 'reference' && theSearchParamChain.length > 0) {
addSearchControlDate(theSearchParamName, theContainerRowNum, theRowNum, true); /*
addSearchControlDate(theSearchParamName, theContainerRowNum, theRowNum, false); * This is a reference parameter with possible chain options, so we need
} * to display a secondary combobox to let the user choose which chained
* parameter they are filling out
*/
var select = $('<select/>', {/*style:'margin-left:30px;'*/});
var newContainerRowNum = theContainerRowNum + "-0";
var newContainer = $('<div />', { id: 'search-param-rowopts-' + newContainerRowNum })
$('#search-param-rowopts-' + theContainerRowNum).append(
$('<br clear="all" />'),
$('<div />', { 'class': 'col-sm-1' }),
$('<div />', { 'class': 'col-sm-1' }).append(
$('<i class="glyphicon glyphicon-link" style="margin-left: 20px; margin-top: 10px;"/>')
),
$('<div />', { 'class': 'col-sm-4' }).append(
select
),
newContainer
);
// select.append(
// $('<option />', { value: '' }).text('Resource ID')
// );
var params = new Array();
for (var chainIdx = 0; chainIdx < theSearchParamChain.length; chainIdx++) {
var nextChain = theSearchParamChain[chainIdx];
var found = false;
for (var resIdx = 0; resIdx < theConformance.rest[0].resource.length && !found; resIdx++) {
var nextRes = theConformance.rest[0].resource[resIdx];
if (!(nextRes.searchParam)) {
continue;
}
for (var paramIdx = 0; paramIdx < nextRes.searchParam.length && !found; paramIdx++) {
var nextParam = nextRes.searchParam[paramIdx];
if (nextParam.name == nextChain) {
if (theSearchParamTarget.length == 0 || theSearchParamTarget.indexOf(nextRes.type) != -1) {
var nextName = nextParam.name + '_' + i;
nextParam = jQuery.extend({}, nextParam); // clone it so we can add the chain to the name
nextParam.name = theSearchParamName + '.' + nextParam.name;
params[nextName] = nextParam;
select.append(
$('<option />', { value: nextName }).text(nextParam.name + ' - ' + nextParam.documentation)
);
found = true;
}
}
}
}
}
select.select2();
handleSearchParamTypeChange(select, params, newContainerRowNum, theRowNum);
select.change(function(){ handleSearchParamTypeChange(select, params, newContainerRowNum, theRowNum); });
addNameAndType = false;
}
if (addNameAndType) {
$('#search-param-rowopts-' + theContainerRowNum).append(
$('<input />', { id: 'param.' + theRowNum + '.name', type: 'hidden', value: theSearchParamName }),
$('<input />', { id: 'param.' + theRowNum + '.type', type: 'hidden', value: theSearchParamType })
);
}
} }
@ -202,20 +272,20 @@ function addSearchControlDate(theSearchParamName, theContainerRowNum, theRowNum,
); );
} }
function handleSearchParamTypeChange(select, params, rowNum) { function handleSearchParamTypeChange(select, params, theContainerRowNum, theParamRowNum) {
var oldVal = select.prevVal; var oldVal = select.prevVal;
var newVal = select.val(); var newVal = select.val();
if (oldVal == newVal) { if (oldVal == newVal) {
return; return;
} }
$('#search-param-rowopts-' + rowNum).empty(); $('#search-param-rowopts-' + theContainerRowNum).empty();
var searchParam = params[newVal]; var searchParam = params[newVal];
/* /*
$('#search-param-rowopts-' + rowNum).append( $('#search-param-rowopts-' + rowNum).append(
$('<input />', { name: 'param.' + rowNum + '.type', type: 'hidden', value: searchParam.type }) $('<input />', { name: 'param.' + rowNum + '.type', type: 'hidden', value: searchParam.type })
); );
*/ */
addSearchControls(searchParam.type, searchParam.name, searchParam.chain, rowNum, rowNum); addSearchControls(conformance, searchParam.type, searchParam.name, searchParam.chain, searchParam.target, theContainerRowNum, theParamRowNum);
select.prevVal = newVal; select.prevVal = newVal;
} }

View File

@ -150,7 +150,7 @@ public class TinderJpaRestServerMojo extends AbstractMojo {
TinderJpaRestServerMojo mojo = new TinderJpaRestServerMojo(); TinderJpaRestServerMojo mojo = new TinderJpaRestServerMojo();
mojo.packageBase = "ca.uhn.test"; mojo.packageBase = "ca.uhn.test";
mojo.baseResourceNames = java.util.Collections.singletonList("patient"); mojo.baseResourceNames = java.util.Collections.singletonList("observation");
mojo.targetDirectory = new File("target/gen"); mojo.targetDirectory = new File("target/gen");
mojo.execute(); mojo.execute();
} }

View File

@ -3,6 +3,8 @@ package ca.uhn.fhir.tinder.model;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import edu.emory.mathcs.backport.java.util.Collections;
public abstract class BaseRootType extends BaseElement { public abstract class BaseRootType extends BaseElement {
private String myId; private String myId;
@ -22,7 +24,7 @@ public abstract class BaseRootType extends BaseElement {
if (mySearchParameters == null) { if (mySearchParameters == null) {
mySearchParameters = new ArrayList<SearchParameter>(); mySearchParameters = new ArrayList<SearchParameter>();
} }
return mySearchParameters; return java.util.Collections.unmodifiableList(mySearchParameters);
} }
public List<SearchParameter> getSearchParametersWithoutComposite() { public List<SearchParameter> getSearchParametersWithoutComposite() {
@ -57,5 +59,9 @@ public abstract class BaseRootType extends BaseElement {
} }
return retVal; return retVal;
} }
public void addSearchParameter(SearchParameter theParam) {
getSearchParameters();
mySearchParameters.add(theParam);
}
} }

View File

@ -1,6 +1,7 @@
package ca.uhn.fhir.tinder.model; package ca.uhn.fhir.tinder.model;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import org.apache.commons.lang.WordUtils; import org.apache.commons.lang.WordUtils;
@ -11,49 +12,19 @@ public class SearchParameter {
private String myDescription; private String myDescription;
private String myName; private String myName;
private String myPath; private String myPath;
private List<String> myTargetTypes;
private String myType; private String myType;
public SearchParameter() { public SearchParameter() {
} }
public String getDescription() {
return StringUtils.defaultString(myDescription);
}
public String getConstantName() { public String getConstantName() {
return "SP_" + myName.toUpperCase().replace("_[X]", "_X").replace("-[X]", "_X").replace('-', '_').replace("!", ""); return "SP_" + myName.toUpperCase().replace("_[X]", "_X").replace("-[X]", "_X").replace('-', '_').replace("!", "");
} }
public List<Include> getPaths() {
ArrayList<Include> retVal = new ArrayList<Include>();
for (String next : getPath().split("\\s*\\|\\s*")) {
retVal.add(new Include(next));
}
return retVal;
}
public static class Include
{
private String myPath;
public Include(String thePath) {
myPath=thePath;
}
public String getPath() {
// String retVal = StringUtils.defaultString(myPath);
// retVal = retVal.substring(retVal.indexOf('.')+1);
return myPath;
}
public String getIncludeName() {
String retVal = myPath;
retVal = retVal.substring(retVal.indexOf('.')+1);
retVal = retVal.toUpperCase().replace('.', '_').replace("[X]", "");
return retVal;
}
public String getDescription() {
return StringUtils.defaultString(myDescription);
} }
public String getFluentConstantName() { public String getFluentConstantName() {
@ -63,27 +34,42 @@ public class SearchParameter {
// return myPath.toUpperCase().replace("_[X]", "_X").replace("-[X]", "_X").replace('-', '_').replace("!", ""); // return myPath.toUpperCase().replace("_[X]", "_X").replace("-[X]", "_X").replace('-', '_').replace("!", "");
return myName.toUpperCase().replace("_[X]", "_X").replace("-[X]", "_X").replace('-', '_').replace("!", ""); return myName.toUpperCase().replace("_[X]", "_X").replace("-[X]", "_X").replace('-', '_').replace("!", "");
} }
public String getTypeCapitalized() {
return WordUtils.capitalize(myType);
}
public String getName() { public String getName() {
return myName; return myName;
} }
public String getNameCapitalized() { public String getNameCapitalized() {
return WordUtils.capitalize(myName).replace("_[x]", "").replace("-[x]", "").replace("_[X]", "").replace("-[X]", "").replace('-', '_').replace("!", ""); return WordUtils.capitalize(myName).replace("_[x]", "").replace("-[x]", "").replace("_[X]", "").replace("-[X]", "").replace('-', '_').replace("!", "");
} }
public String getPath() { public String getPath() {
return StringUtils.defaultString(myPath); return StringUtils.defaultString(myPath);
} }
public List<Include> getPaths() {
ArrayList<Include> retVal = new ArrayList<Include>();
for (String next : getPath().split("\\s*\\|\\s*")) {
retVal.add(new Include(next));
}
return retVal;
}
public List<String> getTargetTypes() {
if (myTargetTypes==null) {
return Collections.emptyList();
}
return myTargetTypes;
}
public String getType() { public String getType() {
return StringUtils.defaultString(myType); return StringUtils.defaultString(myType);
} }
public String getTypeCapitalized() {
return WordUtils.capitalize(myType);
}
public void setDescription(String theDescription) { public void setDescription(String theDescription) {
myDescription = theDescription; myDescription = theDescription;
} }
@ -96,8 +82,35 @@ public class SearchParameter {
myPath = thePath; myPath = thePath;
} }
public void setTargetTypes(List<String> theTargetTypes) {
myTargetTypes = theTargetTypes;
}
public void setType(String theType) { public void setType(String theType) {
myType = theType; myType = theType;
} }
public static class Include
{
private String myPath;
public Include(String thePath) {
myPath=thePath;
}
public String getIncludeName() {
String retVal = myPath;
retVal = retVal.substring(retVal.indexOf('.')+1);
retVal = retVal.toUpperCase().replace('.', '_').replace("[X]", "");
return retVal;
}
public String getPath() {
// String retVal = StringUtils.defaultString(myPath);
// retVal = retVal.substring(retVal.indexOf('.')+1);
return myPath;
}
}
} }

View File

@ -1,8 +1,10 @@
package ca.uhn.fhir.tinder.parser; package ca.uhn.fhir.tinder.parser;
import java.io.InputStream; import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -75,6 +77,7 @@ public abstract class BaseStructureSpreadsheetParser extends BaseStructureParser
// Map<String,String> blockFullNameToShortName = new // Map<String,String> blockFullNameToShortName = new
// HashMap<String,String>(); // HashMap<String,String>();
Map<String, List<String>> pathToResourceTypes = new HashMap<String, List<String>>();
for (int i = 2; i < rows.getLength(); i++) { for (int i = 2; i < rows.getLength(); i++) {
Element nextRow = (Element) rows.item(i); Element nextRow = (Element) rows.item(i);
String name = cellValue(nextRow, 0); String name = cellValue(nextRow, 0);
@ -112,11 +115,30 @@ public abstract class BaseStructureSpreadsheetParser extends BaseStructureParser
scanForSimpleSetters(elem); scanForSimpleSetters(elem);
} }
pathToResourceTypes.put(name, elem.getType());
}
for (SearchParameter nextParam : resource.getSearchParameters()) {
if (nextParam.getType().equals("reference")) {
String path = nextParam.getPath();
List<String> targetTypes = pathToResourceTypes.get(path);
if (targetTypes != null) {
targetTypes = new ArrayList<String>(targetTypes);
for (Iterator<String> iter = targetTypes.iterator();iter.hasNext();) {
String next = iter.next();
if (next.equals("Any") || next.endsWith("Dt")) {
iter.remove();
}
}
}
nextParam.setTargetTypes(targetTypes);
}
} }
index++; index++;
} }
ourLog.info("Parsed {} spreadsheet structures", getResources().size()); ourLog.info("Parsed {} spreadsheet structures", getResources().size());
} }
@ -166,7 +188,7 @@ public abstract class BaseStructureSpreadsheetParser extends BaseStructureParser
sp.setPath(cellValue(nextRow, colPath)); sp.setPath(cellValue(nextRow, colPath));
if (StringUtils.isNotBlank(sp.getName()) && !sp.getName().startsWith("!")) { if (StringUtils.isNotBlank(sp.getName()) && !sp.getName().startsWith("!")) {
theResource.getSearchParameters().add(sp); theResource.addSearchParameter(sp);
} }
} }

View File

@ -150,7 +150,7 @@ public class ProfileParser extends BaseStructureParser {
param.setType(nextParam.getType().getValue()); param.setType(nextParam.getType().getValue());
param.setDescription(nextParam.getDocumentation().getValue()); param.setDescription(nextParam.getDocumentation().getValue());
retVal.getSearchParameters().add(param); retVal.addSearchParameter(param);
} }
addResource(retVal); addResource(retVal);

View File

@ -32,20 +32,26 @@ public class ${className}ResourceProvider extends JpaResourceProvider<${classNam
#foreach ( $param in $searchParamsWithoutComposite ) #{if}(true) #{end} #foreach ( $param in $searchParamsWithoutComposite ) #{if}(true) #{end}
@Description(shortDefinition="${param.description}") @Description(shortDefinition="${param.description}")
@OptionalParam(name="${param.name}")
#if (${param.type} == 'string' ) #if (${param.type} == 'string' )
@OptionalParam(name="${param.name}")
StringParam the${param.nameCapitalized}, StringParam the${param.nameCapitalized},
#elseif (${param.type} == 'token' ) #elseif (${param.type} == 'token' )
@OptionalParam(name="${param.name}")
IdentifierListParam the${param.nameCapitalized}, IdentifierListParam the${param.nameCapitalized},
#elseif (${param.type} == 'date' ) #elseif (${param.type} == 'date' )
@OptionalParam(name="${param.name}")
DateRangeParam the${param.nameCapitalized}, DateRangeParam the${param.nameCapitalized},
#elseif (${param.type} == 'quantity' ) #elseif (${param.type} == 'quantity' )
@OptionalParam(name="${param.name}")
QuantityDt the${param.nameCapitalized}, QuantityDt the${param.nameCapitalized},
#elseif (${param.type} == 'number' ) #elseif (${param.type} == 'number' )
@OptionalParam(name="${param.name}")
QuantityDt the${param.nameCapitalized}, QuantityDt the${param.nameCapitalized},
#elseif (${param.type} == 'reference' ) #elseif (${param.type} == 'reference' )
@OptionalParam(name="${param.name}", targetTypes={ #{foreach}($nextType in ${param.targetTypes}) ${nextType}.class #{if}($foreach.hasNext), #{end} #{end} } )
ReferenceParam the${param.nameCapitalized}, ReferenceParam the${param.nameCapitalized},
#elseif (${param.type} == 'composite' ) #elseif (${param.type} == 'composite' )
@OptionalParam(name="${param.name}")
ReferenceParam the${param.nameCapitalized}, ReferenceParam the${param.nameCapitalized},
#end #end
#end #end

View File

@ -3,7 +3,7 @@
<wb-resource deploy-path="/" source-path="/target/m2e-wtp/web-resources"/> <wb-resource deploy-path="/" source-path="/target/m2e-wtp/web-resources"/>
<wb-resource deploy-path="/" source-path="/src/main/webapp" tag="defaultRootSource"/> <wb-resource deploy-path="/" source-path="/src/main/webapp" tag="defaultRootSource"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src/main/java"/> <wb-resource deploy-path="/WEB-INF/classes" source-path="/src/main/java"/>
<dependent-module archiveName="hapi-fhir-base-0.4.jar" deploy-path="/WEB-INF/lib" handle="module:/resource/hapi-fhir-base/hapi-fhir-base"> <dependent-module archiveName="hapi-fhir-base-0.5-SNAPSHOT.jar" deploy-path="/WEB-INF/lib" handle="module:/resource/hapi-fhir-base/hapi-fhir-base">
<dependency-type>uses</dependency-type> <dependency-type>uses</dependency-type>
</dependent-module> </dependent-module>
<property name="context-root" value="restful-server-example"/> <property name="context-root" value="restful-server-example"/>